]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserdefault.c
8e5a45c627de28dd64b15bcd3ec062045182637f
[~andy/gtk] / gtk / gtkfilechooserdefault.c
1 /* GTK - The GIMP Toolkit
2  * gtkfilechooserdefault.c: Default implementation of GtkFileChooser
3  * Copyright (C) 2003, Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include <config.h>
22 #include "gdk/gdkkeysyms.h"
23 #include "gtkalignment.h"
24 #include "gtkbindings.h"
25 #include "gtkbutton.h"
26 #include "gtkcelllayout.h"
27 #include "gtkcellrendererpixbuf.h"
28 #include "gtkcellrenderertext.h"
29 #include "gtkcellrenderertext.h"
30 #include "gtkcheckmenuitem.h"
31 #include "gtkcombobox.h"
32 #include "gtkentry.h"
33 #include "gtkeventbox.h"
34 #include "gtkexpander.h"
35 #include "gtkfilechooserdefault.h"
36 #include "gtkfilechooserembed.h"
37 #include "gtkfilechooserentry.h"
38 #include "gtkfilechooserutils.h"
39 #include "gtkfilechooser.h"
40 #include "gtkfilesystemmodel.h"
41 #include "gtkframe.h"
42 #include "gtkhbox.h"
43 #include "gtkhpaned.h"
44 #include "gtkiconfactory.h"
45 #include "gtkicontheme.h"
46 #include "gtkimage.h"
47 #include "gtkimagemenuitem.h"
48 #include "gtkintl.h"
49 #include "gtklabel.h"
50 #include "gtkmarshalers.h"
51 #include "gtkmenuitem.h"
52 #include "gtkmessagedialog.h"
53 #include "gtkpathbar.h"
54 #include "gtkprivate.h"
55 #include "gtkscrolledwindow.h"
56 #include "gtkseparatormenuitem.h"
57 #include "gtksizegroup.h"
58 #include "gtkstock.h"
59 #include "gtktable.h"
60 #include "gtktreednd.h"
61 #include "gtktreeprivate.h"
62 #include "gtktreeview.h"
63 #include "gtktreemodelsort.h"
64 #include "gtktreeselection.h"
65 #include "gtktreestore.h"
66 #include "gtktooltips.h"
67 #include "gtktypebuiltins.h"
68 #include "gtkvbox.h"
69
70 #if defined (G_OS_UNIX)
71 #include "gtkfilesystemunix.h"
72 #elif defined (G_OS_WIN32)
73 #include "gtkfilesystemwin32.h"
74 #endif
75
76 #include "gtkalias.h"
77
78 #include <errno.h>
79 #include <string.h>
80 #include <time.h>
81
82 typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
83
84 #define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
85 #define GTK_IS_FILE_CHOOSER_DEFAULT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT))
86 #define GTK_FILE_CHOOSER_DEFAULT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
87
88 typedef enum {
89   LOAD_EMPTY,                   /* There is no model */
90   LOAD_PRELOAD,                 /* Model is loading and a timer is running; model isn't inserted into the tree yet */
91   LOAD_LOADING,                 /* Timeout expired, model is inserted into the tree, but not fully loaded yet */
92   LOAD_FINISHED                 /* Model is fully loaded and inserted into the tree */
93 } LoadState;
94
95 #define MAX_LOADING_TIME 500
96
97 struct _GtkFileChooserDefaultClass
98 {
99   GtkVBoxClass parent_class;
100 };
101
102 struct _GtkFileChooserDefault
103 {
104   GtkVBox parent_instance;
105
106   GtkFileChooserAction action;
107
108   GtkFileSystem *file_system;
109
110   /* Save mode widgets */
111   GtkWidget *save_widgets;
112
113   GtkWidget *save_file_name_entry;
114   GtkWidget *save_folder_label;
115   GtkWidget *save_folder_combo;
116   GtkWidget *save_expander;
117
118   /* The file browsing widgets */
119   GtkWidget *browse_widgets;
120   GtkWidget *browse_shortcuts_tree_view;
121   GtkWidget *browse_shortcuts_add_button;
122   GtkWidget *browse_shortcuts_remove_button;
123   GtkWidget *browse_files_tree_view;
124   GtkWidget *browse_files_popup_menu;
125   GtkWidget *browse_files_popup_menu_add_shortcut_item;
126   GtkWidget *browse_files_popup_menu_hidden_files_item;
127   GtkWidget *browse_new_folder_button;
128   GtkWidget *browse_path_bar;
129
130   GtkFileSystemModel *browse_files_model;
131
132   GtkWidget *filter_combo_hbox;
133   GtkWidget *filter_combo;
134   GtkWidget *preview_box;
135   GtkWidget *preview_label;
136   GtkWidget *preview_widget;
137   GtkWidget *extra_align;
138   GtkWidget *extra_widget;
139
140   GtkListStore *shortcuts_model;
141   GtkTreeModel *shortcuts_filter_model;
142
143   GtkTreeModelSort *sort_model;
144
145   LoadState load_state;
146   guint load_timeout_id;
147
148   GSList *pending_select_paths;
149
150   GtkFileFilter *current_filter;
151   GSList *filters;
152
153   GtkTooltips *tooltips;
154
155   gboolean has_home;
156   gboolean has_desktop;
157
158   int num_volumes;
159   int num_shortcuts;
160   int num_bookmarks;
161
162   gulong volumes_changed_id;
163   gulong bookmarks_changed_id;
164
165   GtkFilePath *current_volume_path;
166   GtkFilePath *current_folder;
167   GtkFilePath *preview_path;
168   char *preview_display_name;
169
170   GtkTreeViewColumn *list_name_column;
171   GtkCellRenderer *list_name_renderer;
172
173   GSource *edited_idle;
174   char *edited_new_text;
175
176   gulong settings_signal_id;
177   int icon_size;
178
179   gulong toplevel_set_focus_id;
180   GtkWidget *toplevel_last_focus_widget;
181
182 #if 0
183   GdkDragContext *shortcuts_drag_context;
184   GSource *shortcuts_drag_outside_idle;
185 #endif
186
187   /* Flags */
188
189   guint local_only : 1;
190   guint preview_widget_active : 1;
191   guint use_preview_label : 1;
192   guint select_multiple : 1;
193   guint show_hidden : 1;
194   guint list_sort_ascending : 1;
195   guint changing_folder : 1;
196   guint shortcuts_current_folder_active : 1;
197
198 #if 0
199   guint shortcuts_drag_outside : 1;
200 #endif
201 };
202
203 /* Signal IDs */
204 enum {
205   LOCATION_POPUP,
206   UP_FOLDER,
207   DOWN_FOLDER,
208   HOME_FOLDER,
209   LAST_SIGNAL
210 };
211
212 static guint signals[LAST_SIGNAL] = { 0 };
213
214 /* Column numbers for the shortcuts tree.  Keep these in sync with shortcuts_model_create() */
215 enum {
216   SHORTCUTS_COL_PIXBUF,
217   SHORTCUTS_COL_NAME,
218   SHORTCUTS_COL_DATA,
219   SHORTCUTS_COL_IS_VOLUME,
220   SHORTCUTS_COL_REMOVABLE,
221   SHORTCUTS_COL_PIXBUF_VISIBLE,
222   SHORTCUTS_COL_NUM_COLUMNS
223 };
224
225 /* Column numbers for the file list */
226 enum {
227   FILE_LIST_COL_NAME,
228   FILE_LIST_COL_SIZE,
229   FILE_LIST_COL_MTIME,
230   FILE_LIST_COL_NUM_COLUMNS
231 };
232
233 /* Identifiers for target types */
234 enum {
235   GTK_TREE_MODEL_ROW,
236   TEXT_URI_LIST
237 };
238
239 /* Target types for dragging from the shortcuts list */
240 static const GtkTargetEntry shortcuts_source_targets[] = {
241   { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }
242 };
243
244 static const int num_shortcuts_source_targets = (sizeof (shortcuts_source_targets)
245                                                  / sizeof (shortcuts_source_targets[0]));
246
247 /* Target types for dropping into the shortcuts list */
248 static const GtkTargetEntry shortcuts_dest_targets[] = {
249   { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW },
250   { "text/uri-list", 0, TEXT_URI_LIST }
251 };
252
253 static const int num_shortcuts_dest_targets = (sizeof (shortcuts_dest_targets)
254                                                / sizeof (shortcuts_dest_targets[0]));
255
256 /* Target types for DnD from the file list */
257 static const GtkTargetEntry file_list_source_targets[] = {
258   { "text/uri-list", 0, TEXT_URI_LIST }
259 };
260
261 static const int num_file_list_source_targets = (sizeof (file_list_source_targets)
262                                                  / sizeof (file_list_source_targets[0]));
263
264 /* Interesting places in the shortcuts bar */
265 typedef enum {
266   SHORTCUTS_HOME,
267   SHORTCUTS_DESKTOP,
268   SHORTCUTS_VOLUMES,
269   SHORTCUTS_SHORTCUTS,
270   SHORTCUTS_BOOKMARKS_SEPARATOR,
271   SHORTCUTS_BOOKMARKS,
272   SHORTCUTS_CURRENT_FOLDER_SEPARATOR,
273   SHORTCUTS_CURRENT_FOLDER
274 } ShortcutsIndex;
275
276 /* Icon size for if we can't get it from the theme */
277 #define FALLBACK_ICON_SIZE 16
278
279 #define PREVIEW_HBOX_SPACING 12
280 #define NUM_LINES 40
281 #define NUM_CHARS 60
282
283 static void gtk_file_chooser_default_class_init       (GtkFileChooserDefaultClass *class);
284 static void gtk_file_chooser_default_iface_init       (GtkFileChooserIface        *iface);
285 static void gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface   *iface);
286 static void gtk_file_chooser_default_init             (GtkFileChooserDefault      *impl);
287
288 static GObject* gtk_file_chooser_default_constructor  (GType                  type,
289                                                        guint                  n_construct_properties,
290                                                        GObjectConstructParam *construct_params);
291 static void     gtk_file_chooser_default_finalize     (GObject               *object);
292 static void     gtk_file_chooser_default_set_property (GObject               *object,
293                                                        guint                  prop_id,
294                                                        const GValue          *value,
295                                                        GParamSpec            *pspec);
296 static void     gtk_file_chooser_default_get_property (GObject               *object,
297                                                        guint                  prop_id,
298                                                        GValue                *value,
299                                                        GParamSpec            *pspec);
300 static void     gtk_file_chooser_default_dispose      (GObject               *object);
301 static void     gtk_file_chooser_default_show_all       (GtkWidget             *widget);
302 static void     gtk_file_chooser_default_map            (GtkWidget             *widget);
303 static void     gtk_file_chooser_default_hierarchy_changed (GtkWidget          *widget,
304                                                             GtkWidget          *previous_toplevel);
305 static void     gtk_file_chooser_default_style_set      (GtkWidget             *widget,
306                                                          GtkStyle              *previous_style);
307 static void     gtk_file_chooser_default_screen_changed (GtkWidget             *widget,
308                                                          GdkScreen             *previous_screen);
309
310 static gboolean       gtk_file_chooser_default_set_current_folder          (GtkFileChooser    *chooser,
311                                                                             const GtkFilePath *path,
312                                                                             GError           **error);
313 static GtkFilePath *  gtk_file_chooser_default_get_current_folder          (GtkFileChooser    *chooser);
314 static void           gtk_file_chooser_default_set_current_name            (GtkFileChooser    *chooser,
315                                                                             const gchar       *name);
316 static gboolean       gtk_file_chooser_default_select_path                 (GtkFileChooser    *chooser,
317                                                                             const GtkFilePath *path,
318                                                                             GError           **error);
319 static void           gtk_file_chooser_default_unselect_path               (GtkFileChooser    *chooser,
320                                                                             const GtkFilePath *path);
321 static void           gtk_file_chooser_default_select_all                  (GtkFileChooser    *chooser);
322 static void           gtk_file_chooser_default_unselect_all                (GtkFileChooser    *chooser);
323 static GSList *       gtk_file_chooser_default_get_paths                   (GtkFileChooser    *chooser);
324 static GtkFilePath *  gtk_file_chooser_default_get_preview_path            (GtkFileChooser    *chooser);
325 static GtkFileSystem *gtk_file_chooser_default_get_file_system             (GtkFileChooser    *chooser);
326 static void           gtk_file_chooser_default_add_filter                  (GtkFileChooser    *chooser,
327                                                                             GtkFileFilter     *filter);
328 static void           gtk_file_chooser_default_remove_filter               (GtkFileChooser    *chooser,
329                                                                             GtkFileFilter     *filter);
330 static GSList *       gtk_file_chooser_default_list_filters                (GtkFileChooser    *chooser);
331 static gboolean       gtk_file_chooser_default_add_shortcut_folder    (GtkFileChooser    *chooser,
332                                                                        const GtkFilePath *path,
333                                                                        GError           **error);
334 static gboolean       gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
335                                                                        const GtkFilePath *path,
336                                                                        GError           **error);
337 static GSList *       gtk_file_chooser_default_list_shortcut_folders  (GtkFileChooser    *chooser);
338
339 static void           gtk_file_chooser_default_get_default_size       (GtkFileChooserEmbed *chooser_embed,
340                                                                        gint                *default_width,
341                                                                        gint                *default_height);
342 static void           gtk_file_chooser_default_get_resizable_hints    (GtkFileChooserEmbed *chooser_embed,
343                                                                        gboolean            *resize_horizontally,
344                                                                        gboolean            *resize_vertically);
345 static gboolean       gtk_file_chooser_default_should_respond         (GtkFileChooserEmbed *chooser_embed);
346 static void           gtk_file_chooser_default_initial_focus          (GtkFileChooserEmbed *chooser_embed);
347
348 static void location_popup_handler (GtkFileChooserDefault *impl,
349                                     const gchar           *path);
350 static void up_folder_handler      (GtkFileChooserDefault *impl);
351 static void down_folder_handler    (GtkFileChooserDefault *impl);
352 static void home_folder_handler    (GtkFileChooserDefault *impl);
353 static void update_appearance      (GtkFileChooserDefault *impl);
354
355 static void set_current_filter   (GtkFileChooserDefault *impl,
356                                   GtkFileFilter         *filter);
357 static void check_preview_change (GtkFileChooserDefault *impl);
358
359 static void filter_combo_changed       (GtkComboBox           *combo_box,
360                                         GtkFileChooserDefault *impl);
361 static void     shortcuts_row_activated_cb (GtkTreeView           *tree_view,
362                                             GtkTreePath           *path,
363                                             GtkTreeViewColumn     *column,
364                                             GtkFileChooserDefault *impl);
365
366 static gboolean shortcuts_key_press_event_cb (GtkWidget             *widget,
367                                               GdkEventKey           *event,
368                                               GtkFileChooserDefault *impl);
369
370 static gboolean shortcuts_select_func   (GtkTreeSelection      *selection,
371                                          GtkTreeModel          *model,
372                                          GtkTreePath           *path,
373                                          gboolean               path_currently_selected,
374                                          gpointer               data);
375 static gboolean shortcuts_get_selected  (GtkFileChooserDefault *impl,
376                                          GtkTreeIter           *iter);
377 static void shortcuts_activate_iter (GtkFileChooserDefault *impl,
378                                      GtkTreeIter           *iter);
379 static int shortcuts_get_index (GtkFileChooserDefault *impl,
380                                 ShortcutsIndex         where);
381 static int shortcut_find_position (GtkFileChooserDefault *impl,
382                                    const GtkFilePath     *path);
383
384 static void bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl);
385
386 static gboolean list_select_func   (GtkTreeSelection      *selection,
387                                     GtkTreeModel          *model,
388                                     GtkTreePath           *path,
389                                     gboolean               path_currently_selected,
390                                     gpointer               data);
391
392 static void list_selection_changed     (GtkTreeSelection      *tree_selection,
393                                         GtkFileChooserDefault *impl);
394 static void list_row_activated         (GtkTreeView           *tree_view,
395                                         GtkTreePath           *path,
396                                         GtkTreeViewColumn     *column,
397                                         GtkFileChooserDefault *impl);
398
399 static void select_func (GtkFileSystemModel *model,
400                          GtkTreePath        *path,
401                          GtkTreeIter        *iter,
402                          gpointer            user_data);
403
404 static void path_bar_clicked           (GtkPathBar            *path_bar,
405                                         GtkFilePath           *file_path,
406                                         gboolean               child_is_hidden,
407                                         GtkFileChooserDefault *impl);
408
409 static void add_bookmark_button_clicked_cb    (GtkButton             *button,
410                                                GtkFileChooserDefault *impl);
411 static void remove_bookmark_button_clicked_cb (GtkButton             *button,
412                                                GtkFileChooserDefault *impl);
413
414 static void list_icon_data_func (GtkTreeViewColumn *tree_column,
415                                  GtkCellRenderer   *cell,
416                                  GtkTreeModel      *tree_model,
417                                  GtkTreeIter       *iter,
418                                  gpointer           data);
419 static void list_name_data_func (GtkTreeViewColumn *tree_column,
420                                  GtkCellRenderer   *cell,
421                                  GtkTreeModel      *tree_model,
422                                  GtkTreeIter       *iter,
423                                  gpointer           data);
424 #if 0
425 static void list_size_data_func (GtkTreeViewColumn *tree_column,
426                                  GtkCellRenderer   *cell,
427                                  GtkTreeModel      *tree_model,
428                                  GtkTreeIter       *iter,
429                                  gpointer           data);
430 #endif
431 static void list_mtime_data_func (GtkTreeViewColumn *tree_column,
432                                   GtkCellRenderer   *cell,
433                                   GtkTreeModel      *tree_model,
434                                   GtkTreeIter       *iter,
435                                   gpointer           data);
436
437 static const GtkFileInfo *get_list_file_info (GtkFileChooserDefault *impl,
438                                               GtkTreeIter           *iter);
439
440 static void load_remove_timer (GtkFileChooserDefault *impl);
441
442 static GObjectClass *parent_class;
443
444 \f
445
446 /* Drag and drop interface declarations */
447
448 typedef struct {
449   GtkTreeModelFilter parent;
450
451   GtkFileChooserDefault *impl;
452 } ShortcutsModelFilter;
453
454 typedef struct {
455   GtkTreeModelFilterClass parent_class;
456 } ShortcutsModelFilterClass;
457
458 #define SHORTCUTS_MODEL_FILTER_TYPE (_shortcuts_model_filter_get_type ())
459 #define SHORTCUTS_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_MODEL_FILTER_TYPE, ShortcutsModelFilter))
460
461 static void shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface);
462
463 G_DEFINE_TYPE_WITH_CODE (ShortcutsModelFilter,
464                          _shortcuts_model_filter,
465                          GTK_TYPE_TREE_MODEL_FILTER,
466                          G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
467                                                 shortcuts_model_filter_drag_source_iface_init));
468
469 static GtkTreeModel *shortcuts_model_filter_new (GtkFileChooserDefault *impl,
470                                                  GtkTreeModel          *child_model,
471                                                  GtkTreePath           *root);
472
473 \f
474
475 GType
476 _gtk_file_chooser_default_get_type (void)
477 {
478   static GType file_chooser_default_type = 0;
479
480   if (!file_chooser_default_type)
481     {
482       static const GTypeInfo file_chooser_default_info =
483       {
484         sizeof (GtkFileChooserDefaultClass),
485         NULL,           /* base_init */
486         NULL,           /* base_finalize */
487         (GClassInitFunc) gtk_file_chooser_default_class_init,
488         NULL,           /* class_finalize */
489         NULL,           /* class_data */
490         sizeof (GtkFileChooserDefault),
491         0,              /* n_preallocs */
492         (GInstanceInitFunc) gtk_file_chooser_default_init,
493       };
494
495       static const GInterfaceInfo file_chooser_info =
496       {
497         (GInterfaceInitFunc) gtk_file_chooser_default_iface_init, /* interface_init */
498         NULL,                                                          /* interface_finalize */
499         NULL                                                           /* interface_data */
500       };
501
502       static const GInterfaceInfo file_chooser_embed_info =
503       {
504         (GInterfaceInitFunc) gtk_file_chooser_embed_default_iface_init, /* interface_init */
505         NULL,                                                          /* interface_finalize */
506         NULL                                                           /* interface_data */
507       };
508
509       file_chooser_default_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserDefault",
510                                                          &file_chooser_default_info, 0);
511
512       g_type_add_interface_static (file_chooser_default_type,
513                                    GTK_TYPE_FILE_CHOOSER,
514                                    &file_chooser_info);
515       g_type_add_interface_static (file_chooser_default_type,
516                                    GTK_TYPE_FILE_CHOOSER_EMBED,
517                                    &file_chooser_embed_info);
518     }
519
520   return file_chooser_default_type;
521 }
522
523 static void
524 gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
525 {
526   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
527   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
528   GtkBindingSet *binding_set;
529
530   parent_class = g_type_class_peek_parent (class);
531
532   gobject_class->finalize = gtk_file_chooser_default_finalize;
533   gobject_class->constructor = gtk_file_chooser_default_constructor;
534   gobject_class->set_property = gtk_file_chooser_default_set_property;
535   gobject_class->get_property = gtk_file_chooser_default_get_property;
536   gobject_class->dispose = gtk_file_chooser_default_dispose;
537
538   widget_class->show_all = gtk_file_chooser_default_show_all;
539   widget_class->map = gtk_file_chooser_default_map;
540   widget_class->hierarchy_changed = gtk_file_chooser_default_hierarchy_changed;
541   widget_class->style_set = gtk_file_chooser_default_style_set;
542   widget_class->screen_changed = gtk_file_chooser_default_screen_changed;
543
544   signals[LOCATION_POPUP] =
545     _gtk_binding_signal_new ("location-popup",
546                              G_OBJECT_CLASS_TYPE (class),
547                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
548                              G_CALLBACK (location_popup_handler),
549                              NULL, NULL,
550                              _gtk_marshal_VOID__STRING,
551                              G_TYPE_NONE, 1, G_TYPE_STRING);
552   signals[UP_FOLDER] =
553     _gtk_binding_signal_new ("up-folder",
554                              G_OBJECT_CLASS_TYPE (class),
555                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
556                              G_CALLBACK (up_folder_handler),
557                              NULL, NULL,
558                              _gtk_marshal_VOID__VOID,
559                              G_TYPE_NONE, 0);
560   signals[DOWN_FOLDER] =
561     _gtk_binding_signal_new ("down-folder",
562                              G_OBJECT_CLASS_TYPE (class),
563                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
564                              G_CALLBACK (down_folder_handler),
565                              NULL, NULL,
566                              _gtk_marshal_VOID__VOID,
567                              G_TYPE_NONE, 0);
568   signals[HOME_FOLDER] =
569     _gtk_binding_signal_new ("home-folder",
570                              G_OBJECT_CLASS_TYPE (class),
571                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
572                              G_CALLBACK (home_folder_handler),
573                              NULL, NULL,
574                              _gtk_marshal_VOID__VOID,
575                              G_TYPE_NONE, 0);
576
577   binding_set = gtk_binding_set_by_class (class);
578
579   gtk_binding_entry_add_signal (binding_set,
580                                 GDK_l, GDK_CONTROL_MASK,
581                                 "location-popup",
582                                 1, G_TYPE_STRING, "");
583
584   gtk_binding_entry_add_signal (binding_set,
585                                 GDK_slash, 0,
586                                 "location-popup",
587                                 1, G_TYPE_STRING, "/");
588
589   gtk_binding_entry_add_signal (binding_set,
590                                 GDK_Up, GDK_MOD1_MASK,
591                                 "up-folder",
592                                 0);
593   gtk_binding_entry_add_signal (binding_set,
594                                 GDK_BackSpace, 0,
595                                 "up-folder",
596                                 0);
597   gtk_binding_entry_add_signal (binding_set,
598                                 GDK_KP_Up, GDK_MOD1_MASK,
599                                 "up-folder",
600                                 0);
601
602   gtk_binding_entry_add_signal (binding_set,
603                                 GDK_Down, GDK_MOD1_MASK,
604                                 "down-folder",
605                                 0);
606   gtk_binding_entry_add_signal (binding_set,
607                                 GDK_KP_Down, GDK_MOD1_MASK,
608                                 "down-folder",
609                                 0);
610
611   gtk_binding_entry_add_signal (binding_set,
612                                 GDK_Home, GDK_MOD1_MASK,
613                                 "home-folder",
614                                 0);
615   gtk_binding_entry_add_signal (binding_set,
616                                 GDK_KP_Home, GDK_MOD1_MASK,
617                                 "home-folder",
618                                 0);
619
620   _gtk_file_chooser_install_properties (gobject_class);
621
622   gtk_settings_install_property (g_param_spec_string ("gtk-file-chooser-backend",
623                                                       P_("Default file chooser backend"),
624                                                       P_("Name of the GtkFileChooser backend to use by default"),
625                                                       NULL,
626                                                       G_PARAM_READWRITE));
627 }
628
629 static void
630 gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
631 {
632   iface->select_path = gtk_file_chooser_default_select_path;
633   iface->unselect_path = gtk_file_chooser_default_unselect_path;
634   iface->select_all = gtk_file_chooser_default_select_all;
635   iface->unselect_all = gtk_file_chooser_default_unselect_all;
636   iface->get_paths = gtk_file_chooser_default_get_paths;
637   iface->get_preview_path = gtk_file_chooser_default_get_preview_path;
638   iface->get_file_system = gtk_file_chooser_default_get_file_system;
639   iface->set_current_folder = gtk_file_chooser_default_set_current_folder;
640   iface->get_current_folder = gtk_file_chooser_default_get_current_folder;
641   iface->set_current_name = gtk_file_chooser_default_set_current_name;
642   iface->add_filter = gtk_file_chooser_default_add_filter;
643   iface->remove_filter = gtk_file_chooser_default_remove_filter;
644   iface->list_filters = gtk_file_chooser_default_list_filters;
645   iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder;
646   iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder;
647   iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders;
648 }
649
650 static void
651 gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
652 {
653   iface->get_default_size = gtk_file_chooser_default_get_default_size;
654   iface->get_resizable_hints = gtk_file_chooser_default_get_resizable_hints;
655   iface->should_respond = gtk_file_chooser_default_should_respond;
656   iface->initial_focus = gtk_file_chooser_default_initial_focus;
657 }
658 static void
659 gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
660 {
661   impl->local_only = TRUE;
662   impl->preview_widget_active = TRUE;
663   impl->use_preview_label = TRUE;
664   impl->select_multiple = FALSE;
665   impl->show_hidden = FALSE;
666   impl->icon_size = FALLBACK_ICON_SIZE;
667   impl->load_state = LOAD_EMPTY;
668   impl->pending_select_paths = NULL;
669
670   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (impl), TRUE);
671   gtk_box_set_spacing (GTK_BOX (impl), 12);
672
673   impl->tooltips = gtk_tooltips_new ();
674   g_object_ref (impl->tooltips);
675   gtk_object_sink (GTK_OBJECT (impl->tooltips));
676 }
677
678 /* Frees the data columns for the specified iter in the shortcuts model*/
679 static void
680 shortcuts_free_row_data (GtkFileChooserDefault *impl,
681                          GtkTreeIter           *iter)
682 {
683   gpointer col_data;
684   gboolean is_volume;
685
686   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
687                       SHORTCUTS_COL_DATA, &col_data,
688                       SHORTCUTS_COL_IS_VOLUME, &is_volume,
689                       -1);
690   if (!col_data)
691     return;
692
693   if (is_volume)
694     {
695       GtkFileSystemVolume *volume;
696
697       volume = col_data;
698       gtk_file_system_volume_free (impl->file_system, volume);
699     }
700   else
701     {
702       GtkFilePath *path;
703
704       path = col_data;
705       gtk_file_path_free (path);
706     }
707 }
708
709 /* Frees all the data columns in the shortcuts model */
710 static void
711 shortcuts_free (GtkFileChooserDefault *impl)
712 {
713   GtkTreeIter iter;
714
715   if (!impl->shortcuts_model)
716     return;
717
718   if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
719     do
720       {
721         shortcuts_free_row_data (impl, &iter);
722       }
723     while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter));
724
725   g_object_unref (impl->shortcuts_model);
726   impl->shortcuts_model = NULL;
727 }
728
729 static void
730 pending_select_paths_free (GtkFileChooserDefault *impl)
731 {
732   GSList *l;
733
734   for (l = impl->pending_select_paths; l; l = l->next)
735     {
736       GtkFilePath *path;
737
738       path = l->data;
739       gtk_file_path_free (path);
740     }
741
742   g_slist_free (impl->pending_select_paths);
743   impl->pending_select_paths = NULL;
744 }
745
746 static void
747 pending_select_paths_add (GtkFileChooserDefault *impl,
748                           const GtkFilePath     *path)
749 {
750   impl->pending_select_paths = g_slist_prepend (impl->pending_select_paths, gtk_file_path_copy (path));
751 }
752
753 /* Used from gtk_tree_selection_selected_foreach() */
754 static void
755 store_selection_foreach (GtkTreeModel *model,
756                          GtkTreePath  *path,
757                          GtkTreeIter  *iter,
758                          gpointer      data)
759 {
760   GtkFileChooserDefault *impl;
761   GtkTreeIter child_iter;
762   const GtkFilePath *file_path;
763
764   impl = GTK_FILE_CHOOSER_DEFAULT (data);
765
766   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter);
767
768   file_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
769   pending_select_paths_add (impl, file_path);
770 }
771
772 /* Stores the current selection in the list of paths to select; this is used to
773  * preserve the selection when reloading the current folder.
774  */
775 static void
776 pending_select_paths_store_selection (GtkFileChooserDefault *impl)
777 {
778   GtkTreeSelection *selection;
779
780   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
781   gtk_tree_selection_selected_foreach (selection, store_selection_foreach, impl);
782 }
783
784 static void
785 gtk_file_chooser_default_finalize (GObject *object)
786 {
787   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
788   GSList *l;
789
790   if (impl->shortcuts_filter_model)
791     g_object_unref (impl->shortcuts_filter_model);
792
793   shortcuts_free (impl);
794
795   g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
796   impl->volumes_changed_id = 0;
797   g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
798   impl->bookmarks_changed_id = 0;
799   g_object_unref (impl->file_system);
800
801   for (l = impl->filters; l; l = l->next)
802     {
803       GtkFileFilter *filter;
804
805       filter = GTK_FILE_FILTER (l->data);
806       g_object_unref (filter);
807     }
808   g_slist_free (impl->filters);
809
810   if (impl->current_filter)
811     g_object_unref (impl->current_filter);
812
813   if (impl->current_volume_path)
814     gtk_file_path_free (impl->current_volume_path);
815
816   if (impl->current_folder)
817     gtk_file_path_free (impl->current_folder);
818
819   if (impl->preview_path)
820     gtk_file_path_free (impl->preview_path);
821
822   pending_select_paths_free (impl);
823
824   load_remove_timer (impl);
825
826   /* Free all the Models we have */
827   if (impl->browse_files_model)
828     g_object_unref (impl->browse_files_model);
829
830   if (impl->sort_model)
831     g_object_unref (impl->sort_model);
832
833   g_free (impl->preview_display_name);
834
835   g_free (impl->edited_new_text);
836
837   g_object_unref (impl->tooltips);
838
839   G_OBJECT_CLASS (parent_class)->finalize (object);
840 }
841
842 /* Shows an error dialog set as transient for the specified window */
843 static void
844 error_message_with_parent (GtkWindow  *parent,
845                            const char *msg,
846                            const char *detail)
847 {
848   GtkWidget *dialog;
849
850   dialog = gtk_message_dialog_new (parent,
851                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
852                                    GTK_MESSAGE_ERROR,
853                                    GTK_BUTTONS_OK,
854                                    "%s",
855                                    msg);
856   gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
857                                             "%s", detail);
858   gtk_dialog_run (GTK_DIALOG (dialog));
859   gtk_widget_destroy (dialog);
860 }
861
862 /* Returns a toplevel GtkWindow, or NULL if none */
863 static GtkWindow *
864 get_toplevel (GtkWidget *widget)
865 {
866   GtkWidget *toplevel;
867
868   toplevel = gtk_widget_get_toplevel (widget);
869   if (!GTK_WIDGET_TOPLEVEL (toplevel))
870     return NULL;
871   else
872     return GTK_WINDOW (toplevel);
873 }
874
875 /* Shows an error dialog for the file chooser */
876 static void
877 error_message (GtkFileChooserDefault *impl,
878                const char            *msg,
879                const char            *detail)
880 {
881   error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail);
882 }
883
884 /* Shows a simple error dialog relative to a path.  Frees the GError as well. */
885 static void
886 error_dialog (GtkFileChooserDefault *impl,
887               const char            *msg,
888               const GtkFilePath     *path,
889               GError                *error)
890 {
891   if (error)
892     {
893       char *uri = NULL;
894       char *text;
895
896       if (path)
897         uri = gtk_file_system_path_to_uri (impl->file_system, path);
898       text = g_strdup_printf (msg, uri);
899       error_message (impl, text, error->message);
900       g_free (text);
901       g_free (uri);
902       g_error_free (error);
903     }
904 }
905
906 /* Displays an error message about not being able to get information for a file.
907  * Frees the GError as well.
908  */
909 static void
910 error_getting_info_dialog (GtkFileChooserDefault *impl,
911                            const GtkFilePath     *path,
912                            GError                *error)
913 {
914   error_dialog (impl,
915                 _("Could not retrieve information about the file"),
916                 path, error);
917 }
918
919 /* Shows an error dialog about not being able to add a bookmark */
920 static void
921 error_adding_bookmark_dialog (GtkFileChooserDefault *impl,
922                               const GtkFilePath     *path,
923                               GError                *error)
924 {
925   error_dialog (impl,
926                 _("Could not add a bookmark"),
927                 path, error);
928 }
929
930 /* Shows an error dialog about not being able to remove a bookmark */
931 static void
932 error_removing_bookmark_dialog (GtkFileChooserDefault *impl,
933                                 const GtkFilePath     *path,
934                                 GError                *error)
935 {
936   error_dialog (impl,
937                 _("Could not remove bookmark"),
938                 path, error);
939 }
940
941 /* Shows an error dialog about not being able to create a folder */
942 static void
943 error_creating_folder_dialog (GtkFileChooserDefault *impl,
944                               const GtkFilePath     *path,
945                               GError                *error)
946 {
947   error_dialog (impl, 
948                 _("The folder could not be created"), 
949                 path, error);
950 }
951
952 /* Shows an error dialog about not being able to create a filename */
953 static void
954 error_building_filename_dialog (GtkFileChooserDefault *impl,
955                                 const GtkFilePath     *folder_part,
956                                 const char            *file_part,
957                                 GError                *error)
958 {
959   error_dialog (impl, _("Invalid file name"), 
960                 NULL, error);
961 }
962
963 /* Shows an error dialog when we cannot switch to a folder */
964 static void
965 error_changing_folder_dialog (GtkFileChooserDefault *impl,
966                               const GtkFilePath     *path,
967                               GError                *error)
968 {
969   error_dialog (impl, _("The folder contents could not be displayed"),
970                 path, error);
971 }
972
973 /* Changes folders, displaying an error dialog if this fails */
974 static gboolean
975 change_folder_and_display_error (GtkFileChooserDefault *impl,
976                                  const GtkFilePath     *path)
977 {
978   GError *error;
979   gboolean result;
980   GtkFilePath *path_copy;
981
982   /* We copy the path because of this case:
983    *
984    * list_row_activated()
985    *   fetches path from model; path belongs to the model (*)
986    *   calls change_folder_and_display_error()
987    *     calls _gtk_file_chooser_set_current_folder_path()
988    *       changing folders fails, sets model to NULL, thus freeing the path in (*)
989    */
990
991   path_copy = gtk_file_path_copy (path);
992
993   error = NULL;
994   result = _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), path_copy, &error);
995
996   if (!result)
997     error_changing_folder_dialog (impl, path_copy, error);
998
999   gtk_file_path_free (path_copy);
1000
1001   return result;
1002 }
1003
1004 static void
1005 update_preview_widget_visibility (GtkFileChooserDefault *impl)
1006 {
1007   if (impl->use_preview_label)
1008     {
1009       if (!impl->preview_label)
1010         {
1011           impl->preview_label = gtk_label_new (impl->preview_display_name);
1012           gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_label, FALSE, FALSE, 0);
1013           gtk_box_reorder_child (GTK_BOX (impl->preview_box), impl->preview_label, 0);
1014           gtk_widget_show (impl->preview_label);
1015         }
1016     }
1017   else
1018     {
1019       if (impl->preview_label)
1020         {
1021           gtk_widget_destroy (impl->preview_label);
1022           impl->preview_label = NULL;
1023         }
1024     }
1025
1026   if (impl->preview_widget_active && impl->preview_widget)
1027     gtk_widget_show (impl->preview_box);
1028   else
1029     gtk_widget_hide (impl->preview_box);
1030
1031   g_signal_emit_by_name (impl, "default-size-changed");
1032 }
1033
1034 static void
1035 set_preview_widget (GtkFileChooserDefault *impl,
1036                     GtkWidget             *preview_widget)
1037 {
1038   if (preview_widget == impl->preview_widget)
1039     return;
1040
1041   if (impl->preview_widget)
1042     gtk_container_remove (GTK_CONTAINER (impl->preview_box),
1043                           impl->preview_widget);
1044
1045   impl->preview_widget = preview_widget;
1046   if (impl->preview_widget)
1047     {
1048       gtk_widget_show (impl->preview_widget);
1049       gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_widget, TRUE, TRUE, 0);
1050       gtk_box_reorder_child (GTK_BOX (impl->preview_box),
1051                              impl->preview_widget,
1052                              (impl->use_preview_label && impl->preview_label) ? 1 : 0);
1053     }
1054
1055   update_preview_widget_visibility (impl);
1056 }
1057
1058 /* Re-reads all the icons for the shortcuts, used when the theme changes */
1059 static void
1060 shortcuts_reload_icons (GtkFileChooserDefault *impl)
1061 {
1062   GtkTreeIter iter;
1063
1064   if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
1065     return;
1066
1067   do {
1068     gpointer data;
1069     gboolean is_volume;
1070     gboolean pixbuf_visible;
1071     GdkPixbuf *pixbuf;
1072
1073     gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
1074                         SHORTCUTS_COL_DATA, &data,
1075                         SHORTCUTS_COL_IS_VOLUME, &is_volume,
1076                         SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible,
1077                         -1);
1078
1079     if (pixbuf_visible && data)
1080       {
1081         if (is_volume)
1082           {
1083             GtkFileSystemVolume *volume;
1084
1085             volume = data;
1086             pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl),
1087                                                          impl->icon_size, NULL);
1088           }
1089         else
1090           {
1091             const GtkFilePath *path;
1092
1093             path = data;
1094             pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
1095                                                   impl->icon_size, NULL);
1096           }
1097
1098         gtk_list_store_set (impl->shortcuts_model, &iter,
1099                             SHORTCUTS_COL_PIXBUF, pixbuf,
1100                             -1);
1101         if (pixbuf)
1102           g_object_unref (pixbuf);
1103       }
1104   } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
1105 }
1106
1107 static void 
1108 shortcuts_find_folder (GtkFileChooserDefault *impl,
1109                        GtkFilePath           *folder)
1110 {
1111   GtkTreeSelection *selection;
1112   int pos;
1113   GtkTreePath *path;
1114
1115   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1116
1117   g_assert (folder != NULL);
1118   pos = shortcut_find_position (impl, folder);
1119   if (pos == -1)
1120     {
1121       gtk_tree_selection_unselect_all (selection);
1122       return;
1123     }
1124
1125   path = gtk_tree_path_new_from_indices (pos, -1);
1126   gtk_tree_selection_select_path (selection, path);
1127   gtk_tree_path_free (path);
1128 }
1129
1130 /* If a shortcut corresponds to the current folder, selects it */
1131 static void
1132 shortcuts_find_current_folder (GtkFileChooserDefault *impl)
1133 {
1134   shortcuts_find_folder (impl, impl->current_folder);
1135 }
1136
1137 /* Convenience function to get the display name and icon info for a path */
1138 static GtkFileInfo *
1139 get_file_info (GtkFileSystem      *file_system, 
1140                const GtkFilePath  *path, 
1141                gboolean            name_only, 
1142                GError            **error)
1143 {
1144   GtkFilePath *parent_path;
1145   GtkFileFolder *parent_folder;
1146   GtkFileInfo *info;
1147   GError *tmp = NULL;
1148
1149   parent_path = NULL;
1150   info = NULL;
1151
1152   if (!gtk_file_system_get_parent (file_system, path, &parent_path, &tmp))
1153     goto out;
1154
1155   parent_folder = gtk_file_system_get_folder (file_system, parent_path ? parent_path : path,
1156                                               GTK_FILE_INFO_DISPLAY_NAME
1157                                               | (name_only ? 0 : GTK_FILE_INFO_IS_FOLDER),
1158                                               &tmp);
1159   if (!parent_folder)
1160     goto out;
1161
1162   info = gtk_file_folder_get_info (parent_folder, parent_path ? path : NULL, &tmp);
1163   g_object_unref (parent_folder);
1164
1165  out:
1166   if (parent_path)
1167     gtk_file_path_free (parent_path);
1168
1169   if (tmp)
1170     {
1171       g_set_error (error,
1172                    GTK_FILE_CHOOSER_ERROR,
1173                    GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
1174                    _("Could not get information about '%s': %s"), 
1175                    gtk_file_path_get_string (path),
1176                    tmp->message);
1177       g_error_free (tmp);
1178     }
1179
1180   return info;
1181 }
1182
1183 /* Returns whether a path is a folder */
1184 static gboolean
1185 check_is_folder (GtkFileSystem      *file_system, 
1186                  const GtkFilePath  *path, 
1187                  GError            **error)
1188 {
1189   GtkFileFolder *folder;
1190
1191   folder = gtk_file_system_get_folder (file_system, path, 0, error);
1192   if (!folder)
1193     return FALSE;
1194
1195   g_object_unref (folder);
1196   return TRUE;
1197 }
1198
1199 /* Inserts a path in the shortcuts tree, making a copy of it; alternatively,
1200  * inserts a volume.  A position of -1 indicates the end of the tree.
1201  */
1202 static gboolean
1203 shortcuts_insert_path (GtkFileChooserDefault *impl,
1204                        int                    pos,
1205                        gboolean               is_volume,
1206                        GtkFileSystemVolume   *volume,
1207                        const GtkFilePath     *path,
1208                        const char            *label,
1209                        gboolean               removable,
1210                        GError               **error)
1211 {
1212   char *label_copy;
1213   GdkPixbuf *pixbuf;
1214   gpointer data;
1215   GtkTreeIter iter;
1216
1217   if (is_volume)
1218     {
1219       data = volume;
1220       label_copy = gtk_file_system_volume_get_display_name (impl->file_system, volume);
1221       pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl),
1222                                                    impl->icon_size, NULL);
1223     }
1224   else
1225     {
1226       if (!check_is_folder (impl->file_system, path, error))
1227         return FALSE;
1228
1229       if (label)
1230         label_copy = g_strdup (label);
1231       else
1232         {
1233           GtkFileInfo *info = get_file_info (impl->file_system, path, TRUE, error);
1234
1235           if (!info)
1236             return FALSE;
1237
1238           label_copy = g_strdup (gtk_file_info_get_display_name (info));
1239           gtk_file_info_free (info);
1240         }
1241
1242       data = gtk_file_path_copy (path);
1243       pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
1244                                             impl->icon_size, NULL);
1245     }
1246
1247   if (pos == -1)
1248     gtk_list_store_append (impl->shortcuts_model, &iter);
1249   else
1250     gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
1251
1252   gtk_list_store_set (impl->shortcuts_model, &iter,
1253                       SHORTCUTS_COL_PIXBUF, pixbuf,
1254                       SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1255                       SHORTCUTS_COL_NAME, label_copy,
1256                       SHORTCUTS_COL_DATA, data,
1257                       SHORTCUTS_COL_IS_VOLUME, is_volume,
1258                       SHORTCUTS_COL_REMOVABLE, removable,
1259                       -1);
1260
1261   g_free (label_copy);
1262
1263   if (pixbuf)
1264     g_object_unref (pixbuf);
1265
1266   return TRUE;
1267 }
1268
1269 /* Appends an item for the user's home directory to the shortcuts model */
1270 static void
1271 shortcuts_append_home (GtkFileChooserDefault *impl)
1272 {
1273   const char *home;
1274   GtkFilePath *home_path;
1275   GError *error;
1276
1277   home = g_get_home_dir ();
1278   if (home == NULL)
1279     return;
1280
1281   home_path = gtk_file_system_filename_to_path (impl->file_system, home);
1282
1283   error = NULL;
1284   impl->has_home = shortcuts_insert_path (impl, -1, FALSE, NULL, home_path, _("Home"), FALSE, &error);
1285   if (!impl->has_home)
1286     error_getting_info_dialog (impl, home_path, error);
1287
1288   gtk_file_path_free (home_path);
1289 }
1290
1291 /* Appends the ~/Desktop directory to the shortcuts model */
1292 static void
1293 shortcuts_append_desktop (GtkFileChooserDefault *impl)
1294 {
1295   char *name;
1296   GtkFilePath *path;
1297
1298 #ifdef G_OS_WIN32
1299   name = _gtk_file_system_win32_get_desktop ();
1300 #else
1301   const char *home = g_get_home_dir ();
1302   if (home == NULL)
1303     return;
1304
1305   name = g_build_filename (home, "Desktop", NULL);
1306 #endif
1307
1308   path = gtk_file_system_filename_to_path (impl->file_system, name);
1309   g_free (name);
1310
1311   impl->has_desktop = shortcuts_insert_path (impl, -1, FALSE, NULL, path, _("Desktop"), FALSE, NULL);
1312   /* We do not actually pop up an error dialog if there is no desktop directory
1313    * because some people may really not want to have one.
1314    */
1315
1316   gtk_file_path_free (path);
1317 }
1318
1319 /* Appends a list of GtkFilePath to the shortcuts model; returns how many were inserted */
1320 static int
1321 shortcuts_append_paths (GtkFileChooserDefault *impl,
1322                         GSList                *paths)
1323 {
1324   int start_row;
1325   int num_inserted;
1326
1327   /* As there is no separator now, we want to start there.
1328    */
1329   start_row = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
1330   num_inserted = 0;
1331
1332   for (; paths; paths = paths->next)
1333     {
1334       GtkFilePath *path;
1335       GError *error;
1336
1337       path = paths->data;
1338       error = NULL;
1339
1340       if (impl->local_only &&
1341           !gtk_file_system_path_is_local (impl->file_system, path))
1342         continue;
1343
1344       /* NULL GError, but we don't really want to show error boxes here */
1345       if (shortcuts_insert_path (impl, start_row + num_inserted, FALSE, NULL, path, NULL, TRUE, NULL))
1346         num_inserted++;
1347     }
1348
1349   return num_inserted;
1350 }
1351
1352 /* Returns the index for the corresponding item in the shortcuts bar */
1353 static int
1354 shortcuts_get_index (GtkFileChooserDefault *impl,
1355                      ShortcutsIndex         where)
1356 {
1357   int n;
1358
1359   n = 0;
1360
1361   if (where == SHORTCUTS_HOME)
1362     goto out;
1363
1364   n += impl->has_home ? 1 : 0;
1365
1366   if (where == SHORTCUTS_DESKTOP)
1367     goto out;
1368
1369   n += impl->has_desktop ? 1 : 0;
1370
1371   if (where == SHORTCUTS_VOLUMES)
1372     goto out;
1373
1374   n += impl->num_volumes;
1375
1376   if (where == SHORTCUTS_SHORTCUTS)
1377     goto out;
1378
1379   n += impl->num_shortcuts;
1380
1381   if (where == SHORTCUTS_BOOKMARKS_SEPARATOR)
1382     goto out;
1383
1384   /* If there are no bookmarks there won't be a separator */
1385   n += (impl->num_bookmarks > 0) ? 1 : 0;
1386
1387   if (where == SHORTCUTS_BOOKMARKS)
1388     goto out;
1389
1390   n += impl->num_bookmarks;
1391
1392   if (where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR)
1393     goto out;
1394
1395   n += 1;
1396
1397   if (where == SHORTCUTS_CURRENT_FOLDER)
1398     goto out;
1399
1400   g_assert_not_reached ();
1401
1402  out:
1403
1404   return n;
1405 }
1406
1407 /* Removes the specified number of rows from the shortcuts list */
1408 static void
1409 shortcuts_remove_rows (GtkFileChooserDefault *impl,
1410                        int                    start_row,
1411                        int                    n_rows)
1412 {
1413   GtkTreePath *path;
1414
1415   path = gtk_tree_path_new_from_indices (start_row, -1);
1416
1417   for (; n_rows; n_rows--)
1418     {
1419       GtkTreeIter iter;
1420
1421       if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
1422         g_assert_not_reached ();
1423
1424       shortcuts_free_row_data (impl, &iter);
1425       gtk_list_store_remove (impl->shortcuts_model, &iter);
1426     }
1427
1428   gtk_tree_path_free (path);
1429 }
1430
1431 /* Adds all the file system volumes to the shortcuts model */
1432 static void
1433 shortcuts_add_volumes (GtkFileChooserDefault *impl)
1434 {
1435   int start_row;
1436   GSList *list, *l;
1437   int n;
1438   gboolean old_changing_folders;
1439
1440   old_changing_folders = impl->changing_folder;
1441   impl->changing_folder = TRUE;
1442
1443   start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
1444   shortcuts_remove_rows (impl, start_row, impl->num_volumes);
1445   impl->num_volumes = 0;
1446
1447   list = gtk_file_system_list_volumes (impl->file_system);
1448
1449   n = 0;
1450
1451   for (l = list; l; l = l->next)
1452     {
1453       GtkFileSystemVolume *volume;
1454
1455       volume = l->data;
1456
1457       if (impl->local_only)
1458         {
1459           GtkFilePath *base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
1460           gboolean is_local = gtk_file_system_path_is_local (impl->file_system, base_path);
1461           gtk_file_path_free (base_path);
1462
1463           if (!is_local)
1464             {
1465               gtk_file_system_volume_free (impl->file_system, volume);
1466               continue;
1467             }
1468         }
1469
1470       if (shortcuts_insert_path (impl, start_row + n, TRUE, volume, NULL, NULL, FALSE, NULL))
1471         n++;
1472       else
1473         gtk_file_system_volume_free (impl->file_system, volume);
1474     }
1475
1476   impl->num_volumes = n;
1477   g_slist_free (list);
1478
1479   if (impl->shortcuts_filter_model)
1480     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
1481
1482   impl->changing_folder = old_changing_folders;
1483 }
1484
1485 /* Inserts a separator node in the shortcuts list */
1486 static void
1487 shortcuts_insert_separator (GtkFileChooserDefault *impl,
1488                             ShortcutsIndex where)
1489 {
1490   GtkTreeIter iter;
1491
1492   g_assert (where == SHORTCUTS_BOOKMARKS_SEPARATOR || where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1493
1494   gtk_list_store_insert (impl->shortcuts_model, &iter,
1495                          shortcuts_get_index (impl, where));
1496   gtk_list_store_set (impl->shortcuts_model, &iter,
1497                       SHORTCUTS_COL_PIXBUF, NULL,
1498                       SHORTCUTS_COL_PIXBUF_VISIBLE, FALSE,
1499                       SHORTCUTS_COL_NAME, NULL,
1500                       SHORTCUTS_COL_DATA, NULL,
1501                       -1);
1502 }
1503
1504 /* Updates the list of bookmarks */
1505 static void
1506 shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
1507 {
1508   GSList *bookmarks;
1509   gboolean old_changing_folders;
1510   GtkTreeIter iter;
1511   GtkFilePath *list_selected = NULL;
1512   GtkFilePath *combo_selected = NULL;
1513   gboolean is_volume;
1514   gpointer col_data;
1515         
1516   old_changing_folders = impl->changing_folder;
1517   impl->changing_folder = TRUE;
1518
1519   if (shortcuts_get_selected (impl, &iter))
1520     {
1521       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), 
1522                           &iter, 
1523                           SHORTCUTS_COL_DATA, &col_data,
1524                           SHORTCUTS_COL_IS_VOLUME, &is_volume,
1525                           -1);
1526
1527       if (col_data && !is_volume)
1528         list_selected = gtk_file_path_copy (col_data);
1529     }
1530
1531   if (impl->save_folder_combo &&
1532       gtk_combo_box_get_active_iter (GTK_COMBO_BOX (impl->save_folder_combo), 
1533                                      &iter))
1534     {
1535       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), 
1536                           &iter, 
1537                           SHORTCUTS_COL_DATA, &col_data,
1538                           SHORTCUTS_COL_IS_VOLUME, &is_volume,
1539                           -1);
1540       
1541       if (col_data && !is_volume)
1542         combo_selected = gtk_file_path_copy (col_data);
1543     }
1544
1545   if (impl->num_bookmarks > 0)
1546     shortcuts_remove_rows (impl,
1547                            shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR),
1548                            impl->num_bookmarks + 1);
1549
1550   bookmarks = gtk_file_system_list_bookmarks (impl->file_system);
1551   impl->num_bookmarks = shortcuts_append_paths (impl, bookmarks);
1552   gtk_file_paths_free (bookmarks);
1553
1554   if (impl->num_bookmarks > 0)
1555     shortcuts_insert_separator (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
1556
1557   if (impl->shortcuts_filter_model)
1558     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
1559
1560   if (list_selected)
1561     {
1562       shortcuts_find_folder (impl, list_selected);
1563       gtk_file_path_free (list_selected);
1564     }
1565
1566   if (combo_selected)
1567     {
1568       gint pos;
1569
1570       pos = shortcut_find_position (impl, combo_selected);
1571       if (pos != -1)
1572         gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), 
1573                                   pos);
1574       gtk_file_path_free (combo_selected);
1575     }
1576   
1577   impl->changing_folder = old_changing_folders;
1578 }
1579
1580 /* Appends a separator and a row to the shortcuts list for the current folder */
1581 static void
1582 shortcuts_add_current_folder (GtkFileChooserDefault *impl)
1583 {
1584   int pos;
1585   gboolean success;
1586
1587   g_assert (!impl->shortcuts_current_folder_active);
1588
1589   success = TRUE;
1590
1591   g_assert (impl->current_folder != NULL);
1592
1593   pos = shortcut_find_position (impl, impl->current_folder);
1594   if (pos == -1)
1595     {
1596       GtkFileSystemVolume *volume;
1597       GtkFilePath *base_path;
1598
1599       /* Separator */
1600
1601       shortcuts_insert_separator (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1602
1603       /* Item */
1604
1605       pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER);
1606
1607       volume = gtk_file_system_get_volume_for_path (impl->file_system, impl->current_folder);
1608       if (volume)
1609         base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
1610       else
1611         base_path = NULL;
1612
1613       if (base_path &&
1614           strcmp (gtk_file_path_get_string (base_path), gtk_file_path_get_string (impl->current_folder)) == 0)
1615         {
1616           success = shortcuts_insert_path (impl, pos, TRUE, volume, NULL, NULL, FALSE, NULL);
1617           if (success)
1618             volume = NULL;
1619         }
1620       else
1621         success = shortcuts_insert_path (impl, pos, FALSE, NULL, impl->current_folder, NULL, FALSE, NULL);
1622
1623       if (volume)
1624         gtk_file_system_volume_free (impl->file_system, volume);
1625
1626       if (base_path)
1627         gtk_file_path_free (base_path);
1628
1629       if (!success)
1630         shortcuts_remove_rows (impl, pos - 1, 1); /* remove the separator */
1631
1632       impl->shortcuts_current_folder_active = success;
1633     }
1634
1635   if (success)
1636     gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), pos);
1637 }
1638
1639 /* Updates the current folder row in the shortcuts model */
1640 static void
1641 shortcuts_update_current_folder (GtkFileChooserDefault *impl)
1642 {
1643   int pos;
1644
1645   pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1646
1647   if (impl->shortcuts_current_folder_active)
1648     {
1649       shortcuts_remove_rows (impl, pos, 2);
1650       impl->shortcuts_current_folder_active = FALSE;
1651     }
1652
1653   shortcuts_add_current_folder (impl);
1654 }
1655
1656 /* Filter function used for the shortcuts filter model */
1657 static gboolean
1658 shortcuts_filter_cb (GtkTreeModel          *model,
1659                      GtkTreeIter           *iter,
1660                      gpointer               data)
1661 {
1662   GtkFileChooserDefault *impl;
1663   GtkTreePath *path;
1664   int pos;
1665
1666   impl = GTK_FILE_CHOOSER_DEFAULT (data);
1667
1668   path = gtk_tree_model_get_path (model, iter);
1669   if (!path)
1670     return FALSE;
1671
1672   pos = *gtk_tree_path_get_indices (path);
1673   gtk_tree_path_free (path);
1674
1675   return (pos < shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR));
1676 }
1677
1678 /* Creates the list model for shortcuts */
1679 static void
1680 shortcuts_model_create (GtkFileChooserDefault *impl)
1681 {
1682   /* Keep this order in sync with the SHORCUTS_COL_* enum values */
1683   impl->shortcuts_model = gtk_list_store_new (SHORTCUTS_COL_NUM_COLUMNS,
1684                                               GDK_TYPE_PIXBUF,  /* pixbuf */
1685                                               G_TYPE_STRING,    /* name */
1686                                               G_TYPE_POINTER,   /* path or volume */
1687                                               G_TYPE_BOOLEAN,   /* is the previous column a volume? */
1688                                               G_TYPE_BOOLEAN,   /* removable */
1689                                               G_TYPE_BOOLEAN);  /* pixbuf cell visibility */
1690
1691   if (impl->file_system)
1692     {
1693       shortcuts_append_home (impl);
1694       shortcuts_append_desktop (impl);
1695       shortcuts_add_volumes (impl);
1696       shortcuts_add_bookmarks (impl);
1697     }
1698
1699   impl->shortcuts_filter_model = shortcuts_model_filter_new (impl,
1700                                                              GTK_TREE_MODEL (impl->shortcuts_model),
1701                                                              NULL);
1702
1703   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
1704                                           shortcuts_filter_cb,
1705                                           impl,
1706                                           NULL);
1707 }
1708
1709 /* Callback used when the "New Folder" button is clicked */
1710 static void
1711 new_folder_button_clicked (GtkButton             *button,
1712                            GtkFileChooserDefault *impl)
1713 {
1714   GtkTreeIter iter;
1715   GtkTreePath *path;
1716
1717   if (!impl->browse_files_model)
1718     return; /* FIXME: this sucks.  Disable the New Folder button or something. */
1719
1720   /* Prevent button from being clicked twice */
1721   gtk_widget_set_sensitive (impl->browse_new_folder_button, FALSE);
1722
1723   _gtk_file_system_model_add_editable (impl->browse_files_model, &iter);
1724
1725   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->browse_files_model), &iter);
1726   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_files_tree_view),
1727                                 path, impl->list_name_column,
1728                                 FALSE, 0.0, 0.0);
1729
1730   g_object_set (impl->list_name_renderer, "editable", TRUE, NULL);
1731   gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view),
1732                             path,
1733                             impl->list_name_column,
1734                             TRUE);
1735
1736   gtk_tree_path_free (path);
1737 }
1738
1739 /* Idle handler for creating a new folder after editing its name cell, or for
1740  * canceling the editing.
1741  */
1742 static gboolean
1743 edited_idle_cb (GtkFileChooserDefault *impl)
1744 {
1745   GDK_THREADS_ENTER ();
1746   
1747   g_source_destroy (impl->edited_idle);
1748   impl->edited_idle = NULL;
1749
1750   _gtk_file_system_model_remove_editable (impl->browse_files_model);
1751   g_object_set (impl->list_name_renderer, "editable", FALSE, NULL);
1752
1753   gtk_widget_set_sensitive (impl->browse_new_folder_button, TRUE);
1754
1755   if (impl->edited_new_text) /* not cancelled? */
1756     {
1757       GError *error;
1758       GtkFilePath *file_path;
1759
1760       error = NULL;
1761       file_path = gtk_file_system_make_path (impl->file_system, impl->current_folder, impl->edited_new_text,
1762                                              &error);
1763       if (file_path)
1764         {
1765           error = NULL;
1766           if (gtk_file_system_create_folder (impl->file_system, file_path, &error))
1767             change_folder_and_display_error (impl, file_path);
1768           else
1769             error_creating_folder_dialog (impl, file_path, error);
1770
1771           gtk_file_path_free (file_path);
1772         }
1773       else
1774         error_creating_folder_dialog (impl, file_path, error);
1775
1776       g_free (impl->edited_new_text);
1777       impl->edited_new_text = NULL;
1778     }
1779
1780   GDK_THREADS_LEAVE ();
1781
1782   return FALSE;
1783 }
1784
1785 static void
1786 queue_edited_idle (GtkFileChooserDefault *impl,
1787                    const gchar           *new_text)
1788 {
1789   /* We create the folder in an idle handler so that we don't modify the tree
1790    * just now.
1791    */
1792
1793   g_assert (!impl->edited_idle);
1794   g_assert (!impl->edited_new_text);
1795
1796   impl->edited_idle = g_idle_source_new ();
1797   g_source_set_closure (impl->edited_idle,
1798                         g_cclosure_new_object (G_CALLBACK (edited_idle_cb),
1799                                                G_OBJECT (impl)));
1800   g_source_attach (impl->edited_idle, NULL);
1801
1802   if (new_text)
1803     impl->edited_new_text = g_strdup (new_text);
1804 }
1805
1806 /* Callback used from the text cell renderer when the new folder is named */
1807 static void
1808 renderer_edited_cb (GtkCellRendererText   *cell_renderer_text,
1809                     const gchar           *path,
1810                     const gchar           *new_text,
1811                     GtkFileChooserDefault *impl)
1812 {
1813  /* work around bug #154921 */
1814   g_object_set (cell_renderer_text, 
1815                 "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
1816    queue_edited_idle (impl, new_text);
1817 }
1818
1819 /* Callback used from the text cell renderer when the new folder edition gets
1820  * canceled.
1821  */
1822 static void
1823 renderer_editing_canceled_cb (GtkCellRendererText   *cell_renderer_text,
1824                               GtkFileChooserDefault *impl)
1825 {
1826  /* work around bug #154921 */
1827   g_object_set (cell_renderer_text, 
1828                 "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
1829    queue_edited_idle (impl, NULL);
1830 }
1831
1832 /* Creates the widgets for the filter combo box */
1833 static GtkWidget *
1834 filter_create (GtkFileChooserDefault *impl)
1835 {
1836   impl->filter_combo = gtk_combo_box_new_text ();
1837   g_signal_connect (impl->filter_combo, "changed",
1838                     G_CALLBACK (filter_combo_changed), impl);
1839
1840   return impl->filter_combo;
1841 }
1842
1843 static GtkWidget *
1844 button_new (GtkFileChooserDefault *impl,
1845             const char *text,
1846             const char *stock_id,
1847             gboolean    sensitive,
1848             gboolean    show,
1849             GCallback   callback)
1850 {
1851   GtkWidget *button;
1852   GtkWidget *hbox;
1853   GtkWidget *widget;
1854   GtkWidget *align;
1855
1856   button = gtk_button_new ();
1857   hbox = gtk_hbox_new (FALSE, 2);
1858   align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1859
1860   gtk_container_add (GTK_CONTAINER (button), align);
1861   gtk_container_add (GTK_CONTAINER (align), hbox);
1862   widget = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
1863
1864   gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1865
1866   widget = gtk_label_new_with_mnemonic (text);
1867   gtk_label_set_mnemonic_widget (GTK_LABEL (widget), GTK_WIDGET (button));
1868   gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1869
1870   gtk_widget_set_sensitive (button, sensitive);
1871   g_signal_connect (button, "clicked", callback, impl);
1872
1873   gtk_widget_show_all (align);
1874
1875   if (show)
1876     gtk_widget_show (button);
1877
1878   return button;
1879 }
1880
1881 /* Looks for a path among the shortcuts; returns its index or -1 if it doesn't exist */
1882 static int
1883 shortcut_find_position (GtkFileChooserDefault *impl,
1884                         const GtkFilePath     *path)
1885 {
1886   GtkTreeIter iter;
1887   int i;
1888   int current_folder_separator_idx;
1889
1890   if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
1891     return -1;
1892
1893   current_folder_separator_idx = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1894
1895   for (i = 0; i < current_folder_separator_idx; i++)
1896     {
1897       gpointer col_data;
1898       gboolean is_volume;
1899
1900       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
1901                           SHORTCUTS_COL_DATA, &col_data,
1902                           SHORTCUTS_COL_IS_VOLUME, &is_volume,
1903                           -1);
1904
1905       if (col_data)
1906         {
1907           if (is_volume)
1908             {
1909               GtkFileSystemVolume *volume;
1910               GtkFilePath *base_path;
1911               gboolean exists;
1912
1913               volume = col_data;
1914               base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
1915
1916               exists = strcmp (gtk_file_path_get_string (path),
1917                                gtk_file_path_get_string (base_path)) == 0;
1918               g_free (base_path);
1919
1920               if (exists)
1921                 return i;
1922             }
1923           else
1924             {
1925               GtkFilePath *model_path;
1926
1927               model_path = col_data;
1928
1929               if (model_path && gtk_file_path_compare (model_path, path) == 0)
1930                 return i;
1931             }
1932         }
1933
1934       gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
1935     }
1936
1937   return -1;
1938 }
1939
1940 /* Tries to add a bookmark from a path name */
1941 static gboolean
1942 shortcuts_add_bookmark_from_path (GtkFileChooserDefault *impl,
1943                                   const GtkFilePath     *path,
1944                                   int                    pos)
1945 {
1946   GError *error;
1947
1948   if (shortcut_find_position (impl, path) != -1)
1949     return FALSE;
1950
1951   /* FIXME: this check really belongs in gtk_file_system_insert_bookmark.  */
1952   error = NULL;
1953   if (!check_is_folder (impl->file_system, path, &error))
1954     {
1955       error_adding_bookmark_dialog (impl, path, error);
1956       return FALSE;
1957     }
1958
1959   error = NULL;
1960   if (!gtk_file_system_insert_bookmark (impl->file_system, path, pos, &error))
1961     {
1962       error_adding_bookmark_dialog (impl, path, error);
1963       return FALSE;
1964     }
1965
1966   return TRUE;
1967 }
1968
1969 static void
1970 add_bookmark_foreach_cb (GtkTreeModel *model,
1971                          GtkTreePath  *path,
1972                          GtkTreeIter  *iter,
1973                          gpointer      data)
1974 {
1975   GtkFileChooserDefault *impl;
1976   GtkFileSystemModel *fs_model;
1977   GtkTreeIter child_iter;
1978   const GtkFilePath *file_path;
1979
1980   impl = (GtkFileChooserDefault *) data;
1981
1982   fs_model = impl->browse_files_model;
1983   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter);
1984
1985   file_path = _gtk_file_system_model_get_path (fs_model, &child_iter);
1986   shortcuts_add_bookmark_from_path (impl, file_path, -1);
1987 }
1988
1989 /* Adds a bookmark from the currently selected item in the file list */
1990 static void
1991 bookmarks_add_selected_folder (GtkFileChooserDefault *impl)
1992 {
1993   GtkTreeSelection *selection;
1994
1995   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
1996
1997   if (gtk_tree_selection_count_selected_rows (selection) == 0)
1998     shortcuts_add_bookmark_from_path (impl, impl->current_folder, -1);
1999   else
2000     gtk_tree_selection_selected_foreach (selection,
2001                                          add_bookmark_foreach_cb,
2002                                          impl);
2003 }
2004
2005 /* Callback used when the "Add bookmark" button is clicked */
2006 static void
2007 add_bookmark_button_clicked_cb (GtkButton *button,
2008                                 GtkFileChooserDefault *impl)
2009 {
2010   bookmarks_add_selected_folder (impl);
2011 }
2012
2013 /* Returns TRUE plus an iter in the shortcuts_model if a row is selected;
2014  * returns FALSE if no shortcut is selected.
2015  */
2016 static gboolean
2017 shortcuts_get_selected (GtkFileChooserDefault *impl,
2018                         GtkTreeIter           *iter)
2019 {
2020   GtkTreeSelection *selection;
2021   GtkTreeIter parent_iter;
2022
2023   if (!impl->browse_shortcuts_tree_view)
2024     return FALSE;
2025
2026   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
2027
2028   if (!gtk_tree_selection_get_selected (selection, NULL, &parent_iter))
2029     return FALSE;
2030
2031   gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
2032                                                     iter,
2033                                                     &parent_iter);
2034   return TRUE;
2035 }
2036
2037 /* Removes the selected bookmarks */
2038 static void
2039 remove_selected_bookmarks (GtkFileChooserDefault *impl)
2040 {
2041   GtkTreeIter iter;
2042   gpointer col_data;
2043   gboolean is_volume;
2044   GtkFilePath *path;
2045   gboolean removable;
2046   GError *error;
2047
2048   if (!shortcuts_get_selected (impl, &iter))
2049     return;
2050
2051   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2052                       SHORTCUTS_COL_DATA, &col_data,
2053                       SHORTCUTS_COL_IS_VOLUME, &is_volume,
2054                       SHORTCUTS_COL_REMOVABLE, &removable,
2055                       -1);
2056   g_assert (col_data != NULL);
2057   g_assert (!is_volume);
2058
2059   if (!removable)
2060     return;
2061
2062   path = col_data;
2063
2064   error = NULL;
2065   if (!gtk_file_system_remove_bookmark (impl->file_system, path, &error))
2066     error_removing_bookmark_dialog (impl, path, error);
2067 }
2068
2069 /* Callback used when the "Remove bookmark" button is clicked */
2070 static void
2071 remove_bookmark_button_clicked_cb (GtkButton *button,
2072                                    GtkFileChooserDefault *impl)
2073 {
2074   remove_selected_bookmarks (impl);
2075 }
2076
2077 struct selection_check_closure {
2078   GtkFileChooserDefault *impl;
2079   int num_selected;
2080   gboolean all_files;
2081   gboolean all_folders;
2082 };
2083
2084 /* Used from gtk_tree_selection_selected_foreach() */
2085 static void
2086 selection_check_foreach_cb (GtkTreeModel *model,
2087                             GtkTreePath  *path,
2088                             GtkTreeIter  *iter,
2089                             gpointer      data)
2090 {
2091   struct selection_check_closure *closure;
2092   GtkTreeIter child_iter;
2093   const GtkFileInfo *info;
2094   gboolean is_folder;
2095
2096   closure = data;
2097   closure->num_selected++;
2098
2099   gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
2100
2101   info = _gtk_file_system_model_get_info (closure->impl->browse_files_model, &child_iter);
2102   is_folder = info ? gtk_file_info_get_is_folder (info) : FALSE;
2103
2104   closure->all_folders = closure->all_folders && is_folder;
2105   closure->all_files = closure->all_files && !is_folder;
2106 }
2107
2108 /* Checks whether the selected items in the file list are all files or all folders */
2109 static void
2110 selection_check (GtkFileChooserDefault *impl,
2111                  gint                  *num_selected,
2112                  gboolean              *all_files,
2113                  gboolean              *all_folders)
2114 {
2115   struct selection_check_closure closure;
2116   GtkTreeSelection *selection;
2117
2118   closure.impl = impl;
2119   closure.num_selected = 0;
2120   closure.all_files = TRUE;
2121   closure.all_folders = TRUE;
2122
2123   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2124   gtk_tree_selection_selected_foreach (selection,
2125                                        selection_check_foreach_cb,
2126                                        &closure);
2127
2128   g_assert (closure.num_selected == 0 || !(closure.all_files && closure.all_folders));
2129
2130   if (num_selected)
2131     *num_selected = closure.num_selected;
2132
2133   if (all_files)
2134     *all_files = closure.all_files;
2135
2136   if (all_folders)
2137     *all_folders = closure.all_folders;
2138 }
2139
2140 struct get_selected_path_closure {
2141   GtkFileChooserDefault *impl;
2142   const GtkFilePath *path;
2143 };
2144
2145 static void
2146 get_selected_path_foreach_cb (GtkTreeModel *model,
2147                               GtkTreePath  *path,
2148                               GtkTreeIter  *iter,
2149                               gpointer      data)
2150 {
2151   struct get_selected_path_closure *closure;
2152   GtkTreeIter child_iter;
2153
2154   closure = data;
2155
2156   gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
2157   closure->path = _gtk_file_system_model_get_path (closure->impl->browse_files_model, &child_iter);
2158 }
2159
2160 /* Returns a selected path from the file list */
2161 static const GtkFilePath *
2162 get_selected_path (GtkFileChooserDefault *impl)
2163 {
2164   struct get_selected_path_closure closure;
2165   GtkTreeSelection *selection;
2166
2167   closure.impl = impl;
2168   closure.path = NULL;
2169
2170   selection =  gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2171   gtk_tree_selection_selected_foreach (selection,
2172                                        get_selected_path_foreach_cb,
2173                                        &closure);
2174
2175   return closure.path;
2176 }
2177
2178 typedef struct {
2179   GtkFileChooserDefault *impl;
2180   gchar *tip;
2181 } UpdateTooltipData;
2182
2183 static void 
2184 update_tooltip (GtkTreeModel      *model,
2185                 GtkTreePath       *path,
2186                 GtkTreeIter       *iter,
2187                 gpointer           data)
2188 {
2189   UpdateTooltipData *udata = data;
2190   GtkTreeIter child_iter;
2191   const GtkFileInfo *info;
2192
2193   if (udata->tip == NULL)
2194     {
2195       gtk_tree_model_sort_convert_iter_to_child_iter (udata->impl->sort_model,
2196                                                       &child_iter,
2197                                                       iter);
2198   
2199       info = _gtk_file_system_model_get_info (udata->impl->browse_files_model, &child_iter);
2200       udata->tip = g_strdup_printf (_("Add the folder '%s' to the bookmarks"),
2201                                     gtk_file_info_get_display_name (info));
2202     }
2203 }
2204
2205
2206 /* Sensitize the "add bookmark" button if all the selected items are folders, or
2207  * if there are no selected items *and* the current folder is not in the
2208  * bookmarks list.  De-sensitize the button otherwise.
2209  */
2210 static void
2211 bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl)
2212 {
2213   gint num_selected;
2214   gboolean all_folders;
2215   gboolean active;
2216   gchar *tip;
2217
2218   selection_check (impl, &num_selected, NULL, &all_folders);
2219
2220   if (num_selected == 0)
2221     active = (impl->current_folder != NULL) && (shortcut_find_position (impl, impl->current_folder) == -1);
2222   else if (num_selected == 1)
2223     {
2224       const GtkFilePath *path;
2225
2226       path = get_selected_path (impl);
2227       active = all_folders && (shortcut_find_position (impl, path) == -1);
2228     }
2229   else
2230     active = all_folders;
2231
2232   gtk_widget_set_sensitive (impl->browse_shortcuts_add_button, active);
2233
2234   if (impl->browse_files_popup_menu_add_shortcut_item)
2235     gtk_widget_set_sensitive (impl->browse_files_popup_menu_add_shortcut_item,
2236                               (num_selected == 0) ? FALSE : active);
2237
2238   if (active)
2239     {
2240       if (num_selected == 0)
2241         tip = g_strdup_printf (_("Add the current folder to the bookmarks"));    
2242       else if (num_selected > 1)
2243         tip = g_strdup_printf (_("Add the selected folders to the bookmarks"));
2244       else
2245         {
2246           GtkTreeSelection *selection;
2247           UpdateTooltipData data;
2248           
2249           selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2250           data.impl = impl;
2251           data.tip = NULL;
2252           gtk_tree_selection_selected_foreach (selection, update_tooltip, &data);
2253           tip = data.tip;
2254           
2255         }
2256       gtk_tooltips_set_tip (impl->tooltips, impl->browse_shortcuts_add_button, tip, NULL);
2257       g_free (tip);
2258     }
2259 }
2260
2261 /* Sets the sensitivity of the "remove bookmark" button depending on whether a
2262  * bookmark row is selected in the shortcuts tree.
2263  */
2264 static void
2265 bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl)
2266 {
2267   GtkTreeIter iter;
2268   gboolean removable = FALSE;
2269   gchar *name = NULL;
2270   
2271   if (shortcuts_get_selected (impl, &iter))
2272     gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2273                         SHORTCUTS_COL_REMOVABLE, &removable,
2274                         SHORTCUTS_COL_NAME, &name,
2275                         -1);
2276
2277   gtk_widget_set_sensitive (impl->browse_shortcuts_remove_button, removable);
2278
2279   if (removable)
2280     {
2281       gchar *tip;
2282
2283       tip = g_strdup_printf (_("Remove the bookmark '%s'"), name);
2284       gtk_tooltips_set_tip (impl->tooltips, impl->browse_shortcuts_remove_button,
2285                             tip, NULL);
2286       g_free (tip);
2287     }
2288
2289   g_free (name);
2290 }
2291
2292 /* GtkWidget::drag-begin handler for the shortcuts list. */
2293 static void
2294 shortcuts_drag_begin_cb (GtkWidget             *widget,
2295                          GdkDragContext        *context,
2296                          GtkFileChooserDefault *impl)
2297 {
2298 #if 0
2299   impl->shortcuts_drag_context = g_object_ref (context);
2300 #endif
2301 }
2302
2303 #if 0
2304 /* Removes the idle handler for outside drags */
2305 static void
2306 shortcuts_cancel_drag_outside_idle (GtkFileChooserDefault *impl)
2307 {
2308   if (!impl->shortcuts_drag_outside_idle)
2309     return;
2310
2311   g_source_destroy (impl->shortcuts_drag_outside_idle);
2312   impl->shortcuts_drag_outside_idle = NULL;
2313 }
2314 #endif
2315
2316 /* GtkWidget::drag-end handler for the shortcuts list. */
2317 static void
2318 shortcuts_drag_end_cb (GtkWidget             *widget,
2319                        GdkDragContext        *context,
2320                        GtkFileChooserDefault *impl)
2321 {
2322 #if 0
2323   g_object_unref (impl->shortcuts_drag_context);
2324
2325   shortcuts_cancel_drag_outside_idle (impl);
2326
2327   if (!impl->shortcuts_drag_outside)
2328     return;
2329
2330   gtk_button_clicked (GTK_BUTTON (impl->browse_shortcuts_remove_button));
2331
2332   impl->shortcuts_drag_outside = FALSE;
2333 #endif
2334 }
2335
2336 /* GtkWidget::drag-data-delete handler for the shortcuts list. */
2337 static void
2338 shortcuts_drag_data_delete_cb (GtkWidget             *widget,
2339                                GdkDragContext        *context,
2340                                GtkFileChooserDefault *impl)
2341 {
2342   g_signal_stop_emission_by_name (widget, "drag-data-delete");
2343 }
2344
2345 #if 0
2346 /* Creates a suitable drag cursor to indicate that the selected bookmark will be
2347  * deleted or not.
2348  */
2349 static void
2350 shortcuts_drag_set_delete_cursor (GtkFileChooserDefault *impl,
2351                                   gboolean               delete)
2352 {
2353   GtkTreeView *tree_view;
2354   GtkTreeIter iter;
2355   GtkTreePath *path;
2356   GdkPixmap *row_pixmap;
2357   GdkBitmap *mask;
2358   int row_pixmap_y;
2359   int cell_y;
2360
2361   tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
2362
2363   /* Find the selected path and get its drag pixmap */
2364
2365   if (!shortcuts_get_selected (impl, &iter))
2366     g_assert_not_reached ();
2367
2368   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
2369
2370   row_pixmap = gtk_tree_view_create_row_drag_icon (tree_view, path);
2371   gtk_tree_path_free (path);
2372
2373   mask = NULL;
2374   row_pixmap_y = 0;
2375
2376   if (delete)
2377     {
2378       GdkPixbuf *pixbuf;
2379
2380       pixbuf = gtk_widget_render_icon (impl->browse_shortcuts_tree_view,
2381                                        GTK_STOCK_DELETE,
2382                                        GTK_ICON_SIZE_DND,
2383                                        NULL);
2384       if (pixbuf)
2385         {
2386           GdkPixmap *composite;
2387           int row_pixmap_width, row_pixmap_height;
2388           int pixbuf_width, pixbuf_height;
2389           int composite_width, composite_height;
2390           int pixbuf_x, pixbuf_y;
2391           GdkGC *gc, *mask_gc;
2392           GdkColor color;
2393           GdkBitmap *pixbuf_mask;
2394
2395           /* Create pixmap and mask for composite image */
2396
2397           gdk_drawable_get_size (row_pixmap, &row_pixmap_width, &row_pixmap_height);
2398           pixbuf_width = gdk_pixbuf_get_width (pixbuf);
2399           pixbuf_height = gdk_pixbuf_get_height (pixbuf);
2400
2401           composite_width = MAX (row_pixmap_width, pixbuf_width);
2402           composite_height = MAX (row_pixmap_height, pixbuf_height);
2403
2404           row_pixmap_y = (composite_height - row_pixmap_height) / 2;
2405
2406           if (gtk_widget_get_direction (impl->browse_shortcuts_tree_view) == GTK_TEXT_DIR_RTL)
2407             pixbuf_x = 0;
2408           else
2409             pixbuf_x = composite_width - pixbuf_width;
2410
2411           pixbuf_y = (composite_height - pixbuf_height) / 2;
2412
2413           composite = gdk_pixmap_new (row_pixmap, composite_width, composite_height, -1);
2414           gc = gdk_gc_new (composite);
2415
2416           mask = gdk_pixmap_new (row_pixmap, composite_width, composite_height, 1);
2417           mask_gc = gdk_gc_new (mask);
2418           color.pixel = 0;
2419           gdk_gc_set_foreground (mask_gc, &color);
2420           gdk_draw_rectangle (mask, mask_gc, TRUE, 0, 0, composite_width, composite_height);
2421
2422           color.red = 0xffff;
2423           color.green = 0xffff;
2424           color.blue = 0xffff;
2425           gdk_gc_set_rgb_fg_color (gc, &color);
2426           gdk_draw_rectangle (composite, gc, TRUE, 0, 0, composite_width, composite_height);
2427
2428           /* Composite the row pixmap and the pixbuf */
2429
2430           gdk_pixbuf_render_pixmap_and_mask_for_colormap
2431             (pixbuf,
2432              gtk_widget_get_colormap (impl->browse_shortcuts_tree_view),
2433              NULL, &pixbuf_mask, 128);
2434           gdk_draw_drawable (mask, mask_gc, pixbuf_mask,
2435                              0, 0,
2436                              pixbuf_x, pixbuf_y,
2437                              pixbuf_width, pixbuf_height);
2438           g_object_unref (pixbuf_mask);
2439
2440           gdk_draw_drawable (composite, gc, row_pixmap,
2441                              0, 0,
2442                              0, row_pixmap_y,
2443                              row_pixmap_width, row_pixmap_height);
2444           color.pixel = 1;
2445           gdk_gc_set_foreground (mask_gc, &color);
2446           gdk_draw_rectangle (mask, mask_gc, TRUE, 0, row_pixmap_y, row_pixmap_width, row_pixmap_height);
2447
2448           gdk_draw_pixbuf (composite, gc, pixbuf,
2449                            0, 0,
2450                            pixbuf_x, pixbuf_y,
2451                            pixbuf_width, pixbuf_height,
2452                            GDK_RGB_DITHER_MAX,
2453                            0, 0);
2454
2455           g_object_unref (pixbuf);
2456           g_object_unref (row_pixmap);
2457
2458           row_pixmap = composite;
2459         }
2460     }
2461
2462   /* The hotspot offsets here are copied from gtk_tree_view_drag_begin(), ugh */
2463
2464   gtk_tree_view_get_path_at_pos (tree_view,
2465                                  tree_view->priv->press_start_x,
2466                                  tree_view->priv->press_start_y,
2467                                  NULL,
2468                                  NULL,
2469                                  NULL,
2470                                  &cell_y);
2471
2472   gtk_drag_set_icon_pixmap (impl->shortcuts_drag_context,
2473                             gdk_drawable_get_colormap (row_pixmap),
2474                             row_pixmap,
2475                             mask,
2476                             tree_view->priv->press_start_x + 1,
2477                             row_pixmap_y + cell_y + 1);
2478
2479   g_object_unref (row_pixmap);
2480   if (mask)
2481     g_object_unref (mask);
2482 }
2483
2484 /* We set the delete cursor and the shortcuts_drag_outside flag in an idle
2485  * handler so that we can tell apart the drag_leave event that comes right
2486  * before a drag_drop, from a normal drag_leave.  We don't want to set the
2487  * cursor nor the flag in the latter case.
2488  */
2489 static gboolean
2490 shortcuts_drag_outside_idle_cb (GtkFileChooserDefault *impl)
2491 {
2492   GDK_THREADS_ENTER ();
2493   
2494   shortcuts_drag_set_delete_cursor (impl, TRUE);
2495   impl->shortcuts_drag_outside = TRUE;
2496
2497   shortcuts_cancel_drag_outside_idle (impl);
2498
2499   GDK_THREADS_LEAVE ();
2500
2501   return FALSE;
2502 }
2503 #endif
2504
2505 /* GtkWidget::drag-leave handler for the shortcuts list.  We unhighlight the
2506  * drop position.
2507  */
2508 static void
2509 shortcuts_drag_leave_cb (GtkWidget             *widget,
2510                          GdkDragContext        *context,
2511                          guint                  time_,
2512                          GtkFileChooserDefault *impl)
2513 {
2514 #if 0
2515   if (gtk_drag_get_source_widget (context) == widget && !impl->shortcuts_drag_outside_idle)
2516     {
2517       impl->shortcuts_drag_outside_idle = g_idle_source_new ();
2518       g_source_set_closure (impl->shortcuts_drag_outside_idle,
2519                             g_cclosure_new_object (G_CALLBACK (shortcuts_drag_outside_idle_cb),
2520                                                    G_OBJECT (impl)));
2521       g_source_attach (impl->shortcuts_drag_outside_idle, NULL);
2522     }
2523 #endif
2524
2525   gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
2526                                    NULL,
2527                                    GTK_TREE_VIEW_DROP_BEFORE);
2528
2529   g_signal_stop_emission_by_name (widget, "drag-leave");
2530 }
2531
2532 /* Computes the appropriate row and position for dropping */
2533 static void
2534 shortcuts_compute_drop_position (GtkFileChooserDefault   *impl,
2535                                  int                      x,
2536                                  int                      y,
2537                                  GtkTreePath            **path,
2538                                  GtkTreeViewDropPosition *pos)
2539 {
2540   GtkTreeView *tree_view;
2541   GtkTreeViewColumn *column;
2542   int cell_y;
2543   GdkRectangle cell;
2544   int row;
2545   int bookmarks_index;
2546
2547   tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
2548
2549   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2550
2551   if (!gtk_tree_view_get_path_at_pos (tree_view,
2552                                       x,
2553                                       y - TREE_VIEW_HEADER_HEIGHT (tree_view),
2554                                       path,
2555                                       &column,
2556                                       NULL,
2557                                       &cell_y))
2558     {
2559       row = bookmarks_index + impl->num_bookmarks - 1;
2560       *path = gtk_tree_path_new_from_indices (row, -1);
2561       *pos = GTK_TREE_VIEW_DROP_AFTER;
2562       return;
2563     }
2564
2565   row = *gtk_tree_path_get_indices (*path);
2566   gtk_tree_view_get_background_area (tree_view, *path, column, &cell);
2567   gtk_tree_path_free (*path);
2568
2569   if (row < bookmarks_index)
2570     {
2571       row = bookmarks_index;
2572       *pos = GTK_TREE_VIEW_DROP_BEFORE;
2573     }
2574   else if (row > bookmarks_index + impl->num_bookmarks - 1)
2575     {
2576       row = bookmarks_index + impl->num_bookmarks - 1;
2577       *pos = GTK_TREE_VIEW_DROP_AFTER;
2578     }
2579   else
2580     {
2581       if (cell_y < cell.height / 2)
2582         *pos = GTK_TREE_VIEW_DROP_BEFORE;
2583       else
2584         *pos = GTK_TREE_VIEW_DROP_AFTER;
2585     }
2586
2587   *path = gtk_tree_path_new_from_indices (row, -1);
2588 }
2589
2590 /* GtkWidget::drag-motion handler for the shortcuts list.  We basically
2591  * implement the destination side of DnD by hand, due to limitations in
2592  * GtkTreeView's DnD API.
2593  */
2594 static gboolean
2595 shortcuts_drag_motion_cb (GtkWidget             *widget,
2596                           GdkDragContext        *context,
2597                           gint                   x,
2598                           gint                   y,
2599                           guint                  time_,
2600                           GtkFileChooserDefault *impl)
2601 {
2602   GtkTreePath *path;
2603   GtkTreeViewDropPosition pos;
2604   GdkDragAction action;
2605
2606 #if 0
2607   if (gtk_drag_get_source_widget (context) == widget)
2608     {
2609       shortcuts_cancel_drag_outside_idle (impl);
2610
2611       if (impl->shortcuts_drag_outside)
2612         {
2613           shortcuts_drag_set_delete_cursor (impl, FALSE);
2614           impl->shortcuts_drag_outside = FALSE;
2615         }
2616     }
2617 #endif
2618
2619   if (context->suggested_action == GDK_ACTION_COPY || (context->actions & GDK_ACTION_COPY) != 0)
2620     action = GDK_ACTION_COPY;
2621   else if (context->suggested_action == GDK_ACTION_MOVE || (context->actions & GDK_ACTION_MOVE) != 0)
2622     action = GDK_ACTION_MOVE;
2623   else
2624     {
2625       action = 0;
2626       goto out;
2627     }
2628
2629   shortcuts_compute_drop_position (impl, x, y, &path, &pos);
2630   gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), path, pos);
2631   gtk_tree_path_free (path);
2632
2633  out:
2634
2635   g_signal_stop_emission_by_name (widget, "drag-motion");
2636
2637   if (action != 0)
2638     {
2639       gdk_drag_status (context, action, time_);
2640       return TRUE;
2641     }
2642   else
2643     return FALSE;
2644 }
2645
2646 /* GtkWidget::drag-drop handler for the shortcuts list. */
2647 static gboolean
2648 shortcuts_drag_drop_cb (GtkWidget             *widget,
2649                         GdkDragContext        *context,
2650                         gint                   x,
2651                         gint                   y,
2652                         guint                  time_,
2653                         GtkFileChooserDefault *impl)
2654 {
2655 #if 0
2656   shortcuts_cancel_drag_outside_idle (impl);
2657 #endif
2658
2659   g_signal_stop_emission_by_name (widget, "drag-drop");
2660   return TRUE;
2661 }
2662
2663 /* Parses a "text/uri-list" string and inserts its URIs as bookmarks */
2664 static void
2665 shortcuts_drop_uris (GtkFileChooserDefault *impl,
2666                      const char            *data,
2667                      int                    position)
2668 {
2669   gchar **uris;
2670   gint i;
2671
2672   uris = g_uri_list_extract_uris (data);
2673
2674   for (i = 0; uris[i]; i++)
2675     {
2676       char *uri;
2677       GtkFilePath *path;
2678
2679       uri = uris[i];
2680       path = gtk_file_system_uri_to_path (impl->file_system, uri);
2681
2682       if (path)
2683         {
2684           if (shortcuts_add_bookmark_from_path (impl, path, position))
2685             position++;
2686
2687           gtk_file_path_free (path);
2688         }
2689       else
2690         {
2691           GError *error;
2692
2693           g_set_error (&error,
2694                        GTK_FILE_CHOOSER_ERROR,
2695                        GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
2696                        _("Could not add a bookmark for '%s' "
2697                          "because it is an invalid path name."),
2698                        uri);
2699           error_adding_bookmark_dialog (impl, path, error);
2700         }
2701     }
2702
2703   g_strfreev (uris);
2704 }
2705
2706 /* Reorders the selected bookmark to the specified position */
2707 static void
2708 shortcuts_reorder (GtkFileChooserDefault *impl,
2709                    int                    new_position)
2710 {
2711   GtkTreeIter iter;
2712   gpointer col_data;
2713   gboolean is_volume;
2714   GtkTreePath *path;
2715   int old_position;
2716   int bookmarks_index;
2717   const GtkFilePath *file_path;
2718   GtkFilePath *file_path_copy;
2719   GError *error;
2720
2721   /* Get the selected path */
2722
2723   if (!shortcuts_get_selected (impl, &iter))
2724     g_assert_not_reached ();
2725
2726   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
2727   old_position = *gtk_tree_path_get_indices (path);
2728   gtk_tree_path_free (path);
2729
2730   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2731   old_position -= bookmarks_index;
2732   g_assert (old_position >= 0 && old_position < impl->num_bookmarks);
2733
2734   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2735                       SHORTCUTS_COL_DATA, &col_data,
2736                       SHORTCUTS_COL_IS_VOLUME, &is_volume,
2737                       -1);
2738   g_assert (col_data != NULL);
2739   g_assert (!is_volume);
2740
2741   file_path = col_data;
2742   file_path_copy = gtk_file_path_copy (file_path); /* removal below will free file_path, so we need a copy */
2743
2744   /* Remove the path from the old position and insert it in the new one */
2745
2746   if (new_position > old_position)
2747     new_position--;
2748
2749   if (old_position == new_position)
2750     goto out;
2751
2752   error = NULL;
2753   if (gtk_file_system_remove_bookmark (impl->file_system, file_path_copy, &error))
2754     shortcuts_add_bookmark_from_path (impl, file_path_copy, new_position);
2755   else
2756     error_adding_bookmark_dialog (impl, file_path_copy, error);
2757
2758  out:
2759
2760   gtk_file_path_free (file_path_copy);
2761 }
2762
2763 /* Callback used when we get the drag data for the bookmarks list.  We add the
2764  * received URIs as bookmarks if they are folders.
2765  */
2766 static void
2767 shortcuts_drag_data_received_cb (GtkWidget          *widget,
2768                                  GdkDragContext     *context,
2769                                  gint                x,
2770                                  gint                y,
2771                                  GtkSelectionData   *selection_data,
2772                                  guint               info,
2773                                  guint               time_,
2774                                  gpointer            data)
2775 {
2776   GtkFileChooserDefault *impl;
2777   GtkTreePath *tree_path;
2778   GtkTreeViewDropPosition tree_pos;
2779   int position;
2780   int bookmarks_index;
2781
2782   impl = GTK_FILE_CHOOSER_DEFAULT (data);
2783
2784   /* Compute position */
2785
2786   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2787
2788   shortcuts_compute_drop_position (impl, x, y, &tree_path, &tree_pos);
2789   position = *gtk_tree_path_get_indices (tree_path);
2790   gtk_tree_path_free (tree_path);
2791
2792   if (tree_pos == GTK_TREE_VIEW_DROP_AFTER)
2793     position++;
2794
2795   g_assert (position >= bookmarks_index);
2796   position -= bookmarks_index;
2797
2798   if (selection_data->target == gdk_atom_intern ("text/uri-list", FALSE))
2799     shortcuts_drop_uris (impl, selection_data->data, position);
2800   else if (selection_data->target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE))
2801     shortcuts_reorder (impl, position);
2802
2803   g_signal_stop_emission_by_name (widget, "drag-data-received");
2804 }
2805
2806 /* Callback used when the selection in the shortcuts tree changes */
2807 static void
2808 shortcuts_selection_changed_cb (GtkTreeSelection      *selection,
2809                                 GtkFileChooserDefault *impl)
2810 {
2811   bookmarks_check_remove_sensitivity (impl);
2812 }
2813
2814 static gboolean
2815 shortcuts_row_separator_func (GtkTreeModel *model,
2816                               GtkTreeIter  *iter,
2817                               gpointer      data)
2818 {
2819   gint column = GPOINTER_TO_INT (data);
2820   gchar *text;
2821
2822   gtk_tree_model_get (model, iter, column, &text, -1);
2823   
2824   if (!text)
2825     return TRUE;
2826
2827   g_free (text);
2828
2829   return FALSE;
2830 }
2831
2832 /* Since GtkTreeView has a keybinding attached to '/', we need to catch
2833  * keypresses before the TreeView gets them.
2834  */
2835 static gboolean
2836 tree_view_keybinding_cb (GtkWidget             *tree_view,
2837                          GdkEventKey           *event,
2838                          GtkFileChooserDefault *impl)
2839 {
2840   if (event->keyval == GDK_slash &&
2841       ! (event->state & (~GDK_SHIFT_MASK & gtk_accelerator_get_default_mod_mask ())))
2842     {
2843       location_popup_handler (impl, "/");
2844       return TRUE;
2845     }
2846   
2847   return FALSE;
2848 }
2849
2850
2851 /* Creates the widgets for the shortcuts and bookmarks tree */
2852 static GtkWidget *
2853 shortcuts_list_create (GtkFileChooserDefault *impl)
2854 {
2855   GtkWidget *swin;
2856   GtkTreeSelection *selection;
2857   GtkTreeViewColumn *column;
2858   GtkCellRenderer *renderer;
2859
2860   /* Scrolled window */
2861
2862   swin = gtk_scrolled_window_new (NULL, NULL);
2863   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
2864                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2865   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
2866                                        GTK_SHADOW_IN);
2867   gtk_widget_show (swin);
2868
2869   /* Tree */
2870
2871   impl->browse_shortcuts_tree_view = gtk_tree_view_new ();
2872   g_signal_connect (impl->browse_shortcuts_tree_view, "key-press-event",
2873                     G_CALLBACK (tree_view_keybinding_cb), impl);
2874   atk_object_set_name (gtk_widget_get_accessible (impl->browse_shortcuts_tree_view), _("Shortcuts"));
2875   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), FALSE);
2876
2877   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), impl->shortcuts_filter_model);
2878
2879   gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
2880                                           GDK_BUTTON1_MASK,
2881                                           shortcuts_source_targets,
2882                                           num_shortcuts_source_targets,
2883                                           GDK_ACTION_MOVE);
2884
2885   gtk_drag_dest_set (impl->browse_shortcuts_tree_view,
2886                      GTK_DEST_DEFAULT_ALL,
2887                      shortcuts_dest_targets,
2888                      num_shortcuts_dest_targets,
2889                      GDK_ACTION_COPY | GDK_ACTION_MOVE);
2890
2891   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
2892   gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
2893   gtk_tree_selection_set_select_function (selection,
2894                                           shortcuts_select_func,
2895                                           impl, NULL);
2896
2897   g_signal_connect (selection, "changed",
2898                     G_CALLBACK (shortcuts_selection_changed_cb), impl);
2899
2900   g_signal_connect (impl->browse_shortcuts_tree_view, "row-activated",
2901                     G_CALLBACK (shortcuts_row_activated_cb), impl);
2902
2903   g_signal_connect (impl->browse_shortcuts_tree_view, "key-press-event",
2904                     G_CALLBACK (shortcuts_key_press_event_cb), impl);
2905
2906   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-begin",
2907                     G_CALLBACK (shortcuts_drag_begin_cb), impl);
2908   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-end",
2909                     G_CALLBACK (shortcuts_drag_end_cb), impl);
2910   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-delete",
2911                     G_CALLBACK (shortcuts_drag_data_delete_cb), impl);
2912
2913   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-leave",
2914                     G_CALLBACK (shortcuts_drag_leave_cb), impl);
2915   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-motion",
2916                     G_CALLBACK (shortcuts_drag_motion_cb), impl);
2917   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-drop",
2918                     G_CALLBACK (shortcuts_drag_drop_cb), impl);
2919   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-received",
2920                     G_CALLBACK (shortcuts_drag_data_received_cb), impl);
2921
2922   gtk_container_add (GTK_CONTAINER (swin), impl->browse_shortcuts_tree_view);
2923   gtk_widget_show (impl->browse_shortcuts_tree_view);
2924
2925   /* Column */
2926
2927   column = gtk_tree_view_column_new ();
2928   gtk_tree_view_column_set_title (column, _("Folder"));
2929
2930   renderer = gtk_cell_renderer_pixbuf_new ();
2931   gtk_tree_view_column_pack_start (column, renderer, FALSE);
2932   gtk_tree_view_column_set_attributes (column, renderer,
2933                                        "pixbuf", SHORTCUTS_COL_PIXBUF,
2934                                        "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
2935                                        NULL);
2936
2937   renderer = gtk_cell_renderer_text_new ();
2938   gtk_tree_view_column_pack_start (column, renderer, TRUE);
2939   gtk_tree_view_column_set_attributes (column, renderer,
2940                                        "text", SHORTCUTS_COL_NAME,
2941                                        NULL);
2942
2943   gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
2944                                         shortcuts_row_separator_func,
2945                                         GINT_TO_POINTER (SHORTCUTS_COL_NAME),
2946                                         NULL);
2947
2948   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), column);
2949
2950   return swin;
2951 }
2952
2953 /* Creates the widgets for the shortcuts/bookmarks pane */
2954 static GtkWidget *
2955 shortcuts_pane_create (GtkFileChooserDefault *impl,
2956                        GtkSizeGroup          *size_group)
2957 {
2958   GtkWidget *vbox;
2959   GtkWidget *hbox;
2960   GtkWidget *widget;
2961
2962   vbox = gtk_vbox_new (FALSE, 6);
2963   gtk_widget_show (vbox);
2964
2965   /* Shortcuts tree */
2966
2967   widget = shortcuts_list_create (impl);
2968   gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
2969
2970   /* Box for buttons */
2971
2972   hbox = gtk_hbox_new (TRUE, 6);
2973   gtk_size_group_add_widget (size_group, hbox);
2974   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
2975   gtk_widget_show (hbox);
2976
2977   /* Add bookmark button */
2978
2979   impl->browse_shortcuts_add_button = button_new (impl,
2980                                                   _("_Add"),
2981                                                   GTK_STOCK_ADD,
2982                                                   FALSE,
2983                                                   TRUE,
2984                                                   G_CALLBACK (add_bookmark_button_clicked_cb));
2985   gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_add_button, TRUE, TRUE, 0);
2986   gtk_tooltips_set_tip (impl->tooltips, impl->browse_shortcuts_add_button,
2987                         _("Add the selected folder to the bookmarks"), NULL);
2988
2989   /* Remove bookmark button */
2990
2991   impl->browse_shortcuts_remove_button = button_new (impl,
2992                                                      _("_Remove"),
2993                                                      GTK_STOCK_REMOVE,
2994                                                      FALSE,
2995                                                      TRUE,
2996                                                      G_CALLBACK (remove_bookmark_button_clicked_cb));
2997   gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_remove_button, TRUE, TRUE, 0);
2998   gtk_tooltips_set_tip (impl->tooltips, impl->browse_shortcuts_remove_button,
2999                         _("Remove the selected bookmark"), NULL);
3000
3001   return vbox;
3002 }
3003
3004 /* Handles key press events on the file list, so that we can trap Enter to
3005  * activate the default button on our own.  Also, checks to see if '/' has been
3006  * pressed.  See comment by tree_view_keybinding_cb() for more details.
3007  */
3008 static gboolean
3009 trap_activate_cb (GtkWidget   *widget,
3010                   GdkEventKey *event,
3011                   gpointer     data)
3012 {
3013   GtkFileChooserDefault *impl;
3014
3015   impl = (GtkFileChooserDefault *) data;
3016   
3017   if (event->keyval == GDK_slash &&
3018       ! (event->state & (~GDK_SHIFT_MASK & gtk_accelerator_get_default_mod_mask ())))
3019     {
3020       location_popup_handler (impl, "/");
3021       return TRUE;
3022     }
3023
3024   if ((event->keyval == GDK_Return
3025        || event->keyval == GDK_ISO_Enter
3026        || event->keyval == GDK_KP_Enter
3027        || event->keyval == GDK_space)
3028       && !(impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
3029            impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
3030     {
3031       GtkWindow *window;
3032
3033       window = get_toplevel (widget);
3034       if (window
3035           && widget != window->default_widget
3036           && !(widget == window->focus_widget &&
3037                (!window->default_widget || !GTK_WIDGET_SENSITIVE (window->default_widget))))
3038         {
3039           gtk_window_activate_default (window);
3040           return TRUE;
3041         }
3042     }
3043
3044   return FALSE;
3045 }
3046
3047 /* Callback used when the file list's popup menu is detached */
3048 static void
3049 popup_menu_detach_cb (GtkWidget *attach_widget,
3050                       GtkMenu   *menu)
3051 {
3052   GtkFileChooserDefault *impl;
3053
3054   impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
3055   g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
3056
3057   impl->browse_files_popup_menu = NULL;
3058   impl->browse_files_popup_menu_add_shortcut_item = NULL;
3059   impl->browse_files_popup_menu_hidden_files_item = NULL;
3060 }
3061
3062 /* Callback used when the "Add to Shortcuts" menu item is activated */
3063 static void
3064 add_to_shortcuts_cb (GtkMenuItem           *item,
3065                      GtkFileChooserDefault *impl)
3066 {
3067   bookmarks_add_selected_folder (impl);
3068 }
3069
3070 /* Callback used when the "Open Location" menu item is activated */
3071 static void
3072 open_location_cb (GtkMenuItem           *item,
3073                   GtkFileChooserDefault *impl)
3074 {
3075   location_popup_handler (impl, "");
3076 }
3077
3078 /* Callback used when the "Show Hidden Files" menu item is toggled */
3079 static void
3080 show_hidden_toggled_cb (GtkCheckMenuItem      *item,
3081                         GtkFileChooserDefault *impl)
3082 {
3083   g_object_set (impl,
3084                 "show-hidden", gtk_check_menu_item_get_active (item),
3085                 NULL);
3086 }
3087
3088 /* Constructs the popup menu for the file list if needed */
3089 static void
3090 file_list_build_popup_menu (GtkFileChooserDefault *impl)
3091 {
3092   GtkWidget *item;
3093
3094   if (impl->browse_files_popup_menu)
3095     return;
3096
3097   impl->browse_files_popup_menu = gtk_menu_new ();
3098   gtk_menu_attach_to_widget (GTK_MENU (impl->browse_files_popup_menu),
3099                              impl->browse_files_tree_view,
3100                              popup_menu_detach_cb);
3101
3102   item = gtk_image_menu_item_new_with_mnemonic (_("_Add to Shortcuts"));
3103   impl->browse_files_popup_menu_add_shortcut_item = item;
3104   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
3105                                  gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU));
3106   gtk_widget_set_sensitive (item, FALSE);
3107   g_signal_connect (item, "activate",
3108                     G_CALLBACK (add_to_shortcuts_cb), impl);
3109   gtk_widget_show (item);
3110   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
3111
3112   item = gtk_image_menu_item_new_with_mnemonic (_("Open _Location"));
3113   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
3114                                  gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU));
3115   g_signal_connect (item, "activate",
3116                     G_CALLBACK (open_location_cb), impl);
3117   gtk_widget_show (item);
3118   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
3119
3120   item = gtk_separator_menu_item_new ();
3121   gtk_widget_show (item);
3122   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
3123
3124   item = gtk_check_menu_item_new_with_mnemonic (_("Show _Hidden Files"));
3125   impl->browse_files_popup_menu_hidden_files_item = item;
3126   g_signal_connect (item, "toggled",
3127                     G_CALLBACK (show_hidden_toggled_cb), impl);
3128   gtk_widget_show (item);
3129   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
3130 }
3131
3132 /* Updates the popup menu for the file list, creating it if necessary */
3133 static void
3134 file_list_update_popup_menu (GtkFileChooserDefault *impl)
3135 {
3136   file_list_build_popup_menu (impl);
3137
3138   /* The sensitivity of the Add to Shortcuts item is set in
3139    * bookmarks_check_add_sensitivity()
3140    */
3141
3142   g_signal_handlers_block_by_func (impl->browse_files_popup_menu_hidden_files_item,
3143                                    G_CALLBACK (show_hidden_toggled_cb), impl);
3144   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (impl->browse_files_popup_menu_hidden_files_item),
3145                                   impl->show_hidden);
3146   g_signal_handlers_unblock_by_func (impl->browse_files_popup_menu_hidden_files_item,
3147                                      G_CALLBACK (show_hidden_toggled_cb), impl);
3148 }
3149
3150 static void
3151 popup_position_func (GtkMenu   *menu,
3152                      gint      *x,
3153                      gint      *y,
3154                      gboolean  *push_in,
3155                      gpointer   user_data)
3156 {
3157   GtkWidget *widget = GTK_WIDGET (user_data);
3158   GdkScreen *screen = gtk_widget_get_screen (widget);
3159   GtkRequisition req;
3160   gint monitor_num;
3161   GdkRectangle monitor;
3162
3163   g_return_if_fail (GTK_WIDGET_REALIZED (widget));
3164
3165   gdk_window_get_origin (widget->window, x, y);
3166
3167   gtk_widget_size_request (GTK_WIDGET (menu), &req);
3168
3169   *x += (widget->allocation.width - req.width) / 2;
3170   *y += (widget->allocation.height - req.height) / 2;
3171
3172   monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
3173   gtk_menu_set_monitor (menu, monitor_num);
3174   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
3175
3176   *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
3177   *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
3178
3179   *push_in = FALSE;
3180 }
3181
3182 static void
3183 file_list_popup_menu (GtkFileChooserDefault *impl,
3184                       GdkEventButton        *event)
3185 {
3186   file_list_update_popup_menu (impl);
3187   if (event)
3188     gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
3189                     NULL, NULL, NULL, NULL,
3190                     event->button, event->time);
3191   else
3192     {
3193       gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
3194                       NULL, NULL,
3195                       popup_position_func, impl->browse_files_tree_view,
3196                       0, GDK_CURRENT_TIME);
3197       gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->browse_files_popup_menu),
3198                                    FALSE);
3199     }
3200
3201 }
3202
3203 /* Callback used for the GtkWidget::popup-menu signal of the file list */
3204 static gboolean
3205 list_popup_menu_cb (GtkWidget *widget,
3206                     GtkFileChooserDefault *impl)
3207 {
3208   file_list_popup_menu (impl, NULL);
3209   return TRUE;
3210 }
3211
3212 /* Callback used when a button is pressed on the file list.  We trap button 3 to
3213  * bring up a popup menu.
3214  */
3215 static gboolean
3216 list_button_press_event_cb (GtkWidget             *widget,
3217                             GdkEventButton        *event,
3218                             GtkFileChooserDefault *impl)
3219 {
3220   if (event->button != 3)
3221     return FALSE;
3222
3223   file_list_popup_menu (impl, event);
3224   return TRUE;
3225 }
3226
3227 /* Creates the widgets for the file list */
3228 static GtkWidget *
3229 create_file_list (GtkFileChooserDefault *impl)
3230 {
3231   GtkWidget *swin;
3232   GtkTreeSelection *selection;
3233   GtkTreeViewColumn *column;
3234   GtkCellRenderer *renderer;
3235
3236   /* Scrolled window */
3237
3238   swin = gtk_scrolled_window_new (NULL, NULL);
3239   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
3240                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3241   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
3242                                        GTK_SHADOW_IN);
3243
3244   /* Tree/list view */
3245
3246   impl->browse_files_tree_view = gtk_tree_view_new ();
3247   g_object_set_data (G_OBJECT (impl->browse_files_tree_view), "GtkFileChooserDefault", impl);
3248   atk_object_set_name (gtk_widget_get_accessible (impl->browse_files_tree_view), _("Files"));
3249
3250   gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->browse_files_tree_view), TRUE);
3251   gtk_container_add (GTK_CONTAINER (swin), impl->browse_files_tree_view);
3252   g_signal_connect (impl->browse_files_tree_view, "row-activated",
3253                     G_CALLBACK (list_row_activated), impl);
3254   g_signal_connect (impl->browse_files_tree_view, "key-press-event",
3255                     G_CALLBACK (trap_activate_cb), impl);
3256   g_signal_connect (impl->browse_files_tree_view, "popup-menu",
3257                     G_CALLBACK (list_popup_menu_cb), impl);
3258   g_signal_connect (impl->browse_files_tree_view, "button-press-event",
3259                     G_CALLBACK (list_button_press_event_cb), impl);
3260
3261   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3262   gtk_tree_selection_set_select_function (selection,
3263                                           list_select_func,
3264                                           impl, NULL);
3265   gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_files_tree_view),
3266                                           GDK_BUTTON1_MASK,
3267                                           file_list_source_targets,
3268                                           num_file_list_source_targets,
3269                                           GDK_ACTION_COPY);
3270
3271   g_signal_connect (selection, "changed",
3272                     G_CALLBACK (list_selection_changed), impl);
3273
3274   /* Filename column */
3275
3276   impl->list_name_column = gtk_tree_view_column_new ();
3277   gtk_tree_view_column_set_expand (impl->list_name_column, TRUE);
3278   gtk_tree_view_column_set_resizable (impl->list_name_column, TRUE);
3279   gtk_tree_view_column_set_title (impl->list_name_column, _("Name"));
3280   gtk_tree_view_column_set_sort_column_id (impl->list_name_column, FILE_LIST_COL_NAME);
3281
3282   renderer = gtk_cell_renderer_pixbuf_new ();
3283   gtk_tree_view_column_pack_start (impl->list_name_column, renderer, FALSE);
3284   gtk_tree_view_column_set_cell_data_func (impl->list_name_column, renderer,
3285                                            list_icon_data_func, impl, NULL);
3286
3287   impl->list_name_renderer = gtk_cell_renderer_text_new ();
3288   g_object_set (impl->list_name_renderer,
3289                 "ellipsize", PANGO_ELLIPSIZE_END,
3290                 NULL);
3291   g_signal_connect (impl->list_name_renderer, "edited",
3292                     G_CALLBACK (renderer_edited_cb), impl);
3293   g_signal_connect (impl->list_name_renderer, "editing-canceled",
3294                     G_CALLBACK (renderer_editing_canceled_cb), impl);
3295   gtk_tree_view_column_pack_start (impl->list_name_column, impl->list_name_renderer, TRUE);
3296   gtk_tree_view_column_set_cell_data_func (impl->list_name_column, impl->list_name_renderer,
3297                                            list_name_data_func, impl, NULL);
3298
3299   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), impl->list_name_column);
3300 #if 0
3301   /* Size column */
3302
3303   column = gtk_tree_view_column_new ();
3304   gtk_tree_view_column_set_title (column, _("Size"));
3305
3306   renderer = gtk_cell_renderer_text_new ();
3307   gtk_tree_view_column_pack_start (column, renderer, TRUE);
3308   gtk_tree_view_column_set_cell_data_func (column, renderer,
3309                                            list_size_data_func, impl, NULL);
3310   gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_SIZE);
3311   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
3312 #endif
3313   /* Modification time column */
3314
3315   column = gtk_tree_view_column_new ();
3316   gtk_tree_view_column_set_resizable (column, TRUE);
3317   gtk_tree_view_column_set_title (column, _("Modified"));
3318
3319   renderer = gtk_cell_renderer_text_new ();
3320   gtk_tree_view_column_pack_start (column, renderer, TRUE);
3321   gtk_tree_view_column_set_cell_data_func (column, renderer,
3322                                            list_mtime_data_func, impl, NULL);
3323   gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_MTIME);
3324   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
3325   gtk_widget_show_all (swin);
3326
3327   return swin;
3328 }
3329
3330 static GtkWidget *
3331 create_path_bar (GtkFileChooserDefault *impl)
3332 {
3333   GtkWidget *path_bar;
3334
3335   path_bar = g_object_new (GTK_TYPE_PATH_BAR, NULL);
3336   _gtk_path_bar_set_file_system (GTK_PATH_BAR (path_bar), impl->file_system);
3337
3338   return path_bar;
3339 }
3340
3341 static void
3342 set_filter_tooltip (GtkWidget *widget, 
3343                     gpointer   data)
3344 {
3345   GtkTooltips *tooltips = (GtkTooltips *)data;
3346
3347   if (GTK_IS_BUTTON (widget))
3348     gtk_tooltips_set_tip (tooltips, widget,
3349                           _("Select which types of files are shown"), 
3350                           NULL);
3351 }
3352
3353 static void
3354 realize_filter_combo (GtkWidget *combo,
3355                       gpointer   data)
3356 {
3357   GtkFileChooserDefault *impl = (GtkFileChooserDefault *)data;
3358
3359   gtk_container_forall (GTK_CONTAINER (combo),
3360                         set_filter_tooltip,
3361                         impl->tooltips);
3362 }
3363
3364 /* Creates the widgets for the files/folders pane */
3365 static GtkWidget *
3366 file_pane_create (GtkFileChooserDefault *impl,
3367                   GtkSizeGroup          *size_group)
3368 {
3369   GtkWidget *vbox;
3370   GtkWidget *hbox;
3371   GtkWidget *widget;
3372
3373   vbox = gtk_vbox_new (FALSE, 6);
3374   gtk_widget_show (vbox);
3375
3376   /* The path bar and 'Create Folder' button */
3377   hbox = gtk_hbox_new (FALSE, 12);
3378   gtk_widget_show (hbox);
3379   impl->browse_path_bar = create_path_bar (impl);
3380   g_signal_connect (impl->browse_path_bar, "path-clicked", G_CALLBACK (path_bar_clicked), impl);
3381   gtk_widget_show_all (impl->browse_path_bar);
3382   gtk_box_pack_start (GTK_BOX (hbox), impl->browse_path_bar, TRUE, TRUE, 0);
3383
3384   /* Create Folder */
3385   impl->browse_new_folder_button = gtk_button_new_with_mnemonic (_("Create Fo_lder"));
3386   g_signal_connect (impl->browse_new_folder_button, "clicked",
3387                     G_CALLBACK (new_folder_button_clicked), impl);
3388   gtk_box_pack_end (GTK_BOX (hbox), impl->browse_new_folder_button, FALSE, FALSE, 0);
3389   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
3390
3391   /* Box for lists and preview */
3392
3393   hbox = gtk_hbox_new (FALSE, PREVIEW_HBOX_SPACING);
3394   gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
3395   gtk_widget_show (hbox);
3396
3397   /* File list */
3398
3399   widget = create_file_list (impl);
3400   gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
3401
3402   /* Preview */
3403
3404   impl->preview_box = gtk_vbox_new (FALSE, 12);
3405   gtk_box_pack_start (GTK_BOX (hbox), impl->preview_box, FALSE, FALSE, 0);
3406   /* Don't show preview box initially */
3407
3408   /* Filter combo */
3409
3410   impl->filter_combo_hbox = gtk_hbox_new (FALSE, 12);
3411
3412   widget = filter_create (impl);
3413
3414   g_signal_connect (widget, "realize",
3415                     G_CALLBACK (realize_filter_combo), impl);
3416
3417   gtk_widget_show (widget);
3418   gtk_box_pack_end (GTK_BOX (impl->filter_combo_hbox), widget, FALSE, FALSE, 0);
3419
3420   gtk_size_group_add_widget (size_group, impl->filter_combo_hbox);
3421   gtk_box_pack_end (GTK_BOX (vbox), impl->filter_combo_hbox, FALSE, FALSE, 0);
3422
3423   return vbox;
3424 }
3425 /* Callback used when the "Browse for more folders" expander is toggled */
3426 static void
3427 expander_changed_cb (GtkExpander           *expander,
3428                      GParamSpec            *pspec,
3429                      GtkFileChooserDefault *impl)
3430 {
3431   update_appearance (impl);
3432 }
3433
3434 /* Callback used when the selection changes in the save folder combo box */
3435 static void
3436 save_folder_combo_changed_cb (GtkComboBox           *combo,
3437                               GtkFileChooserDefault *impl)
3438 {
3439   GtkTreeIter iter;
3440
3441   if (impl->changing_folder)
3442     return;
3443
3444   if (gtk_combo_box_get_active_iter (combo, &iter))
3445     shortcuts_activate_iter (impl, &iter);
3446 }
3447
3448 /* Creates the combo box with the save folders */
3449 static GtkWidget *
3450 save_folder_combo_create (GtkFileChooserDefault *impl)
3451 {
3452   GtkWidget *combo;
3453   GtkCellRenderer *cell;
3454
3455   combo = g_object_new (GTK_TYPE_COMBO_BOX,
3456                         "model", impl->shortcuts_model,
3457                         "focus-on-click", FALSE,
3458                         NULL);
3459   gtk_widget_show (combo);
3460
3461   cell = gtk_cell_renderer_pixbuf_new ();
3462   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE);
3463   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
3464                                   "pixbuf", SHORTCUTS_COL_PIXBUF,
3465                                   "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
3466                                   "sensitive", SHORTCUTS_COL_PIXBUF_VISIBLE,
3467                                   NULL);
3468
3469   cell = gtk_cell_renderer_text_new ();
3470   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
3471   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
3472                                   "text", SHORTCUTS_COL_NAME,
3473                                   "sensitive", SHORTCUTS_COL_PIXBUF_VISIBLE,
3474                                   NULL);
3475
3476   gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo),
3477                                         shortcuts_row_separator_func,
3478                                         GINT_TO_POINTER (SHORTCUTS_COL_NAME),
3479                                         NULL);
3480
3481   g_signal_connect (combo, "changed",
3482                     G_CALLBACK (save_folder_combo_changed_cb), impl);
3483
3484   return combo;
3485 }
3486
3487 /* Creates the widgets specific to Save mode */
3488 static GtkWidget *
3489 save_widgets_create (GtkFileChooserDefault *impl)
3490 {
3491   GtkWidget *vbox;
3492   GtkWidget *table;
3493   GtkWidget *widget;
3494   GtkWidget *alignment;
3495
3496   vbox = gtk_vbox_new (FALSE, 12);
3497
3498   table = gtk_table_new (2, 2, FALSE);
3499   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
3500   gtk_widget_show (table);
3501   gtk_table_set_row_spacings (GTK_TABLE (table), 12);
3502   gtk_table_set_col_spacings (GTK_TABLE (table), 12);
3503
3504   /* Name entry */
3505
3506   widget = gtk_label_new_with_mnemonic (_("_Name:"));
3507   gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
3508   gtk_table_attach (GTK_TABLE (table), widget,
3509                     0, 1, 0, 1,
3510                     GTK_FILL, GTK_FILL,
3511                     0, 0);
3512   gtk_widget_show (widget);
3513
3514   impl->save_file_name_entry = _gtk_file_chooser_entry_new (TRUE);
3515   _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry),
3516                                            impl->file_system);
3517   gtk_entry_set_width_chars (GTK_ENTRY (impl->save_file_name_entry), 45);
3518   gtk_entry_set_activates_default (GTK_ENTRY (impl->save_file_name_entry), TRUE);
3519   gtk_table_attach (GTK_TABLE (table), impl->save_file_name_entry,
3520                     1, 2, 0, 1,
3521                     GTK_EXPAND | GTK_FILL, 0,
3522                     0, 0);
3523   gtk_widget_show (impl->save_file_name_entry);
3524   gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->save_file_name_entry);
3525
3526   /* Folder combo */
3527   impl->save_folder_label = gtk_label_new (NULL);
3528   gtk_misc_set_alignment (GTK_MISC (impl->save_folder_label), 0.0, 0.5);
3529   gtk_table_attach (GTK_TABLE (table), impl->save_folder_label,
3530                     0, 1, 1, 2,
3531                     GTK_FILL, GTK_FILL,
3532                     0, 0);
3533   gtk_widget_show (impl->save_folder_label);
3534
3535   impl->save_folder_combo = save_folder_combo_create (impl);
3536   gtk_table_attach (GTK_TABLE (table), impl->save_folder_combo,
3537                     1, 2, 1, 2,
3538                     GTK_EXPAND | GTK_FILL, GTK_FILL,
3539                     0, 0);
3540   gtk_label_set_mnemonic_widget (GTK_LABEL (impl->save_folder_label), impl->save_folder_combo);
3541
3542   /* Expander */
3543   alignment = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
3544   gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
3545
3546   impl->save_expander = gtk_expander_new_with_mnemonic (_("_Browse for other folders"));
3547   gtk_container_add (GTK_CONTAINER (alignment), impl->save_expander);
3548   g_signal_connect (impl->save_expander, "notify::expanded",
3549                     G_CALLBACK (expander_changed_cb),
3550                     impl);
3551   gtk_widget_show_all (alignment);
3552
3553   return vbox;
3554 }
3555
3556 /* Creates the main hpaned with the widgets shared by Open and Save mode */
3557 static GtkWidget *
3558 browse_widgets_create (GtkFileChooserDefault *impl)
3559 {
3560   GtkWidget *vbox;
3561   GtkWidget *hpaned;
3562   GtkWidget *widget;
3563   GtkSizeGroup *size_group;
3564
3565   /* size group is used by the [+][-] buttons and the filter combo */
3566   size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
3567   vbox = gtk_vbox_new (FALSE, 12);
3568
3569   /* Paned widget */
3570   hpaned = gtk_hpaned_new ();
3571   gtk_widget_show (hpaned);
3572   gtk_paned_set_position (GTK_PANED (hpaned), 200); /* FIXME: this sucks */
3573   gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
3574
3575   widget = shortcuts_pane_create (impl, size_group);
3576   gtk_paned_pack1 (GTK_PANED (hpaned), widget, FALSE, FALSE);
3577   widget = file_pane_create (impl, size_group);
3578   gtk_paned_pack2 (GTK_PANED (hpaned), widget, TRUE, FALSE);
3579
3580   g_object_unref (size_group);
3581
3582   return vbox;
3583 }
3584
3585 static GObject*
3586 gtk_file_chooser_default_constructor (GType                  type,
3587                                       guint                  n_construct_properties,
3588                                       GObjectConstructParam *construct_params)
3589 {
3590   GtkFileChooserDefault *impl;
3591   GObject *object;
3592
3593   object = parent_class->constructor (type,
3594                                       n_construct_properties,
3595                                       construct_params);
3596   impl = GTK_FILE_CHOOSER_DEFAULT (object);
3597
3598   g_assert (impl->file_system);
3599
3600   gtk_widget_push_composite_child ();
3601
3602   /* Shortcuts model */
3603
3604   shortcuts_model_create (impl);
3605
3606   /* Widgets for Save mode */
3607   impl->save_widgets = save_widgets_create (impl);
3608   gtk_box_pack_start (GTK_BOX (impl), impl->save_widgets, FALSE, FALSE, 0);
3609
3610   /* The browse widgets */
3611   impl->browse_widgets = browse_widgets_create (impl);
3612   gtk_box_pack_start (GTK_BOX (impl), impl->browse_widgets, TRUE, TRUE, 0);
3613
3614   /* Alignment to hold extra widget */
3615   impl->extra_align = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
3616   gtk_box_pack_start (GTK_BOX (impl), impl->extra_align, FALSE, FALSE, 0);
3617
3618   gtk_widget_pop_composite_child ();
3619   update_appearance (impl);
3620
3621   return object;
3622 }
3623
3624 /* Sets the extra_widget by packing it in the appropriate place */
3625 static void
3626 set_extra_widget (GtkFileChooserDefault *impl,
3627                   GtkWidget             *extra_widget)
3628 {
3629   if (extra_widget)
3630     {
3631       g_object_ref (extra_widget);
3632       /* FIXME: is this right ? */
3633       gtk_widget_show (extra_widget);
3634     }
3635
3636   if (impl->extra_widget)
3637     {
3638       gtk_container_remove (GTK_CONTAINER (impl->extra_align), impl->extra_widget);
3639       g_object_unref (impl->extra_widget);
3640     }
3641
3642   impl->extra_widget = extra_widget;
3643   if (impl->extra_widget)
3644     {
3645       gtk_container_add (GTK_CONTAINER (impl->extra_align), impl->extra_widget);
3646       gtk_widget_show (impl->extra_align);
3647     }
3648   else
3649     gtk_widget_hide (impl->extra_align);
3650 }
3651
3652 static void
3653 set_local_only (GtkFileChooserDefault *impl,
3654                 gboolean               local_only)
3655 {
3656   if (local_only != impl->local_only)
3657     {
3658       impl->local_only = local_only;
3659
3660       if (impl->shortcuts_model && impl->file_system)
3661         {
3662           shortcuts_add_volumes (impl);
3663           shortcuts_add_bookmarks (impl);
3664         }
3665
3666       if (local_only &&
3667           !gtk_file_system_path_is_local (impl->file_system, impl->current_folder))
3668         {
3669           /* If we are pointing to a non-local folder, make an effort to change
3670            * back to a local folder, but it's really up to the app to not cause
3671            * such a situation, so we ignore errors.
3672            */
3673           const gchar *home = g_get_home_dir ();
3674           GtkFilePath *home_path;
3675
3676           if (home == NULL)
3677             return;
3678
3679           home_path = gtk_file_system_filename_to_path (impl->file_system, home);
3680
3681           _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), home_path, NULL);
3682
3683           gtk_file_path_free (home_path);
3684         }
3685     }
3686 }
3687
3688 static void
3689 volumes_changed_cb (GtkFileSystem         *file_system,
3690                     GtkFileChooserDefault *impl)
3691 {
3692   shortcuts_add_volumes (impl);
3693 }
3694
3695 /* Callback used when the set of bookmarks changes in the file system */
3696 static void
3697 bookmarks_changed_cb (GtkFileSystem         *file_system,
3698                       GtkFileChooserDefault *impl)
3699 {
3700   shortcuts_add_bookmarks (impl);
3701
3702   bookmarks_check_add_sensitivity (impl);
3703   bookmarks_check_remove_sensitivity (impl);
3704 }
3705
3706 /* Sets the file chooser to multiple selection mode */
3707 static void
3708 set_select_multiple (GtkFileChooserDefault *impl,
3709                      gboolean               select_multiple,
3710                      gboolean               property_notify)
3711 {
3712   GtkTreeSelection *selection;
3713   GtkSelectionMode mode;
3714
3715   if (select_multiple == impl->select_multiple)
3716     return;
3717
3718   mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE;
3719
3720   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3721   gtk_tree_selection_set_mode (selection, mode);
3722
3723   impl->select_multiple = select_multiple;
3724   g_object_notify (G_OBJECT (impl), "select-multiple");
3725
3726   check_preview_change (impl);
3727 }
3728
3729 static void
3730 set_file_system_backend (GtkFileChooserDefault *impl,
3731                          const char *backend)
3732 {
3733   if (impl->file_system)
3734     {
3735       g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
3736       impl->volumes_changed_id = 0;
3737       g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
3738       impl->bookmarks_changed_id = 0;
3739       g_object_unref (impl->file_system);
3740     }
3741
3742   impl->file_system = NULL;
3743   if (backend)
3744     impl->file_system = _gtk_file_system_create (backend);
3745   else
3746     {
3747       GtkSettings *settings = gtk_settings_get_default ();
3748       gchar *default_backend = NULL;
3749
3750       g_object_get (settings, "gtk-file-chooser-backend", &default_backend, NULL);
3751       if (default_backend)
3752         {
3753           impl->file_system = _gtk_file_system_create (default_backend);
3754           g_free (default_backend);
3755         }
3756     }
3757
3758   if (!impl->file_system)
3759     {
3760 #if defined (G_OS_UNIX)
3761       impl->file_system = gtk_file_system_unix_new ();
3762 #elif defined (G_OS_WIN32)
3763       impl->file_system = gtk_file_system_win32_new ();
3764 #else
3765 #error "No default filesystem implementation on the platform"
3766 #endif
3767     }
3768
3769   if (impl->file_system)
3770     {
3771       impl->volumes_changed_id = g_signal_connect (impl->file_system, "volumes-changed",
3772                                                    G_CALLBACK (volumes_changed_cb),
3773                                                    impl);
3774       impl->bookmarks_changed_id = g_signal_connect (impl->file_system, "bookmarks-changed",
3775                                                      G_CALLBACK (bookmarks_changed_cb),
3776                                                      impl);
3777     }
3778 }
3779
3780 /* This function is basically a do_all function.
3781  *
3782  * It sets the visibility on all the widgets based on the current state, and
3783  * moves the custom_widget if needed.
3784  */
3785 static void
3786 update_appearance (GtkFileChooserDefault *impl)
3787 {
3788   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
3789       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
3790     {
3791       const char *text;
3792
3793       gtk_widget_show (impl->save_widgets);
3794
3795       if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
3796         text = _("Save in _folder:");
3797       else
3798         text = _("Create in _folder:");
3799
3800       gtk_label_set_text_with_mnemonic (GTK_LABEL (impl->save_folder_label), text);
3801
3802       if (gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
3803         {
3804           gtk_widget_set_sensitive (impl->save_folder_label, FALSE);
3805           gtk_widget_set_sensitive (impl->save_folder_combo, FALSE);
3806           gtk_widget_show (impl->browse_widgets);
3807         }
3808       else
3809         {
3810           gtk_widget_set_sensitive (impl->save_folder_label, TRUE);
3811           gtk_widget_set_sensitive (impl->save_folder_combo, TRUE);
3812           gtk_widget_hide (impl->browse_widgets);
3813         }
3814
3815       gtk_widget_show (impl->browse_new_folder_button);
3816
3817       if (impl->select_multiple)
3818         {
3819           g_warning ("Save mode cannot be set in conjunction with multiple selection mode.  "
3820                      "Re-setting to single selection mode.");
3821           set_select_multiple (impl, FALSE, TRUE);
3822         }
3823     }
3824   else if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
3825            impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
3826     {
3827       gtk_widget_hide (impl->save_widgets);
3828       gtk_widget_show (impl->browse_widgets);
3829     }
3830
3831   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
3832     gtk_widget_hide (impl->browse_new_folder_button);
3833   else
3834     gtk_widget_show (impl->browse_new_folder_button);
3835
3836   gtk_widget_queue_draw (impl->browse_files_tree_view);
3837
3838   g_signal_emit_by_name (impl, "default-size-changed");
3839 }
3840
3841 static void
3842 gtk_file_chooser_default_set_property (GObject      *object,
3843                                        guint         prop_id,
3844                                        const GValue *value,
3845                                        GParamSpec   *pspec)
3846
3847 {
3848   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
3849
3850   switch (prop_id)
3851     {
3852     case GTK_FILE_CHOOSER_PROP_ACTION:
3853       {
3854         GtkFileChooserAction action = g_value_get_enum (value);
3855
3856         if (action != impl->action)
3857           {
3858             gtk_file_chooser_default_unselect_all (GTK_FILE_CHOOSER (impl));
3859             
3860             if (action == GTK_FILE_CHOOSER_ACTION_SAVE && impl->select_multiple)
3861               {
3862                 g_warning ("Multiple selection mode is not allowed in Save mode");
3863                 set_select_multiple (impl, FALSE, TRUE);
3864               }
3865             impl->action = action;
3866             update_appearance (impl);
3867           }
3868         
3869         if (impl->save_file_name_entry)
3870           _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry),
3871                                               action);
3872       }
3873       break;
3874     case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
3875       set_file_system_backend (impl, g_value_get_string (value));
3876       break;
3877     case GTK_FILE_CHOOSER_PROP_FILTER:
3878       set_current_filter (impl, g_value_get_object (value));
3879       break;
3880     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
3881       set_local_only (impl, g_value_get_boolean (value));
3882       break;
3883     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
3884       set_preview_widget (impl, g_value_get_object (value));
3885       break;
3886     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
3887       impl->preview_widget_active = g_value_get_boolean (value);
3888       update_preview_widget_visibility (impl);
3889       break;
3890     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
3891       impl->use_preview_label = g_value_get_boolean (value);
3892       update_preview_widget_visibility (impl);
3893       break;
3894     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
3895       set_extra_widget (impl, g_value_get_object (value));
3896       break;
3897     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
3898       {
3899         gboolean select_multiple = g_value_get_boolean (value);
3900         if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE && select_multiple)
3901           {
3902             g_warning ("Multiple selection mode is not allowed in Save mode");
3903             return;
3904           }
3905
3906         set_select_multiple (impl, select_multiple, FALSE);
3907       }
3908       break;
3909     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
3910       {
3911         gboolean show_hidden = g_value_get_boolean (value);
3912         if (show_hidden != impl->show_hidden)
3913           {
3914             impl->show_hidden = show_hidden;
3915
3916             if (impl->browse_files_model)
3917               _gtk_file_system_model_set_show_hidden (impl->browse_files_model, show_hidden);
3918           }
3919       }
3920       break;
3921     default:
3922       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3923       break;
3924     }
3925 }
3926
3927 static void
3928 gtk_file_chooser_default_get_property (GObject    *object,
3929                                        guint       prop_id,
3930                                        GValue     *value,
3931                                        GParamSpec *pspec)
3932 {
3933   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
3934
3935   switch (prop_id)
3936     {
3937     case GTK_FILE_CHOOSER_PROP_ACTION:
3938       g_value_set_enum (value, impl->action);
3939       break;
3940     case GTK_FILE_CHOOSER_PROP_FILTER:
3941       g_value_set_object (value, impl->current_filter);
3942       break;
3943     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
3944       g_value_set_boolean (value, impl->local_only);
3945       break;
3946     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
3947       g_value_set_object (value, impl->preview_widget);
3948       break;
3949     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
3950       g_value_set_boolean (value, impl->preview_widget_active);
3951       break;
3952     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
3953       g_value_set_boolean (value, impl->use_preview_label);
3954       break;
3955     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
3956       g_value_set_object (value, impl->extra_widget);
3957       break;
3958     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
3959       g_value_set_boolean (value, impl->select_multiple);
3960       break;
3961     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
3962       g_value_set_boolean (value, impl->show_hidden);
3963       break;
3964     default:
3965       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3966       break;
3967     }
3968 }
3969
3970 /* Removes the settings signal handler.  It's safe to call multiple times */
3971 static void
3972 remove_settings_signal (GtkFileChooserDefault *impl,
3973                         GdkScreen             *screen)
3974 {
3975   if (impl->settings_signal_id)
3976     {
3977       GtkSettings *settings;
3978
3979       settings = gtk_settings_get_for_screen (screen);
3980       g_signal_handler_disconnect (settings,
3981                                    impl->settings_signal_id);
3982       impl->settings_signal_id = 0;
3983     }
3984 }
3985
3986 static void
3987 gtk_file_chooser_default_dispose (GObject *object)
3988 {
3989   GtkFileChooserDefault *impl = (GtkFileChooserDefault *) object;
3990
3991   if (impl->extra_widget)
3992     {
3993       g_object_unref (impl->extra_widget);
3994       impl->extra_widget = NULL;
3995     }
3996
3997   remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl)));
3998
3999   G_OBJECT_CLASS (parent_class)->dispose (object);
4000 }
4001
4002 /* We override show-all since we have internal widgets that
4003  * shouldn't be shown when you call show_all(), like the filter
4004  * combo box.
4005  */
4006 static void
4007 gtk_file_chooser_default_show_all (GtkWidget *widget)
4008 {
4009   GtkFileChooserDefault *impl = (GtkFileChooserDefault *) widget;
4010
4011   gtk_widget_show (widget);
4012
4013   if (impl->extra_widget)
4014     gtk_widget_show_all (impl->extra_widget);
4015 }
4016
4017 /* Handler for GtkWindow::set-focus; this is where we save the last-focused
4018  * widget on our toplevel.  See gtk_file_chooser_default_hierarchy_changed()
4019  */
4020 static void
4021 toplevel_set_focus_cb (GtkWindow             *window,
4022                        GtkWidget             *focus,
4023                        GtkFileChooserDefault *impl)
4024 {
4025   impl->toplevel_last_focus_widget = gtk_window_get_focus (window);
4026 }
4027
4028 /* We monitor the focus widget on our toplevel to be able to know which widget
4029  * was last focused at the time our "should_respond" method gets called.
4030  */
4031 static void
4032 gtk_file_chooser_default_hierarchy_changed (GtkWidget *widget,
4033                                             GtkWidget *previous_toplevel)
4034 {
4035   GtkFileChooserDefault *impl;
4036   GtkWidget *toplevel;
4037
4038   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
4039
4040   if (previous_toplevel)
4041     {
4042       g_assert (impl->toplevel_set_focus_id != 0);
4043       g_signal_handler_disconnect (previous_toplevel, impl->toplevel_set_focus_id);
4044       impl->toplevel_set_focus_id = 0;
4045       impl->toplevel_last_focus_widget = NULL;
4046     }
4047   else
4048     g_assert (impl->toplevel_set_focus_id == 0);
4049
4050   toplevel = gtk_widget_get_toplevel (widget);
4051   if (GTK_IS_WINDOW (toplevel))
4052     {
4053       impl->toplevel_set_focus_id = g_signal_connect (toplevel, "set-focus",
4054                                                       G_CALLBACK (toplevel_set_focus_cb), impl);
4055       impl->toplevel_last_focus_widget = gtk_window_get_focus (GTK_WINDOW (toplevel));
4056     }
4057 }
4058
4059 /* Changes the icons wherever it is needed */
4060 static void
4061 change_icon_theme (GtkFileChooserDefault *impl)
4062 {
4063   GtkSettings *settings;
4064   gint width, height;
4065
4066   settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
4067
4068   if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU, &width, &height))
4069     impl->icon_size = MAX (width, height);
4070   else
4071     impl->icon_size = FALLBACK_ICON_SIZE;
4072
4073   shortcuts_reload_icons (impl);
4074   gtk_widget_queue_resize (impl->browse_files_tree_view);
4075 }
4076
4077 /* Callback used when a GtkSettings value changes */
4078 static void
4079 settings_notify_cb (GObject               *object,
4080                     GParamSpec            *pspec,
4081                     GtkFileChooserDefault *impl)
4082 {
4083   const char *name;
4084
4085   name = g_param_spec_get_name (pspec);
4086
4087   if (strcmp (name, "gtk-icon-theme-name") == 0
4088       || strcmp (name, "gtk-icon-sizes") == 0)
4089     change_icon_theme (impl);
4090 }
4091
4092 /* Installs a signal handler for GtkSettings so that we can monitor changes in
4093  * the icon theme.
4094  */
4095 static void
4096 check_icon_theme (GtkFileChooserDefault *impl)
4097 {
4098   GtkSettings *settings;
4099
4100   if (impl->settings_signal_id)
4101     return;
4102
4103   if (gtk_widget_has_screen (GTK_WIDGET (impl)))
4104     {
4105       settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
4106       impl->settings_signal_id = g_signal_connect (settings, "notify",
4107                                                    G_CALLBACK (settings_notify_cb), impl);
4108
4109       change_icon_theme (impl);
4110     }
4111 }
4112
4113 static void
4114 gtk_file_chooser_default_style_set (GtkWidget *widget,
4115                                     GtkStyle  *previous_style)
4116 {
4117   GtkFileChooserDefault *impl;
4118
4119   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
4120
4121   if (GTK_WIDGET_CLASS (parent_class)->style_set)
4122     GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
4123
4124   if (gtk_widget_has_screen (GTK_WIDGET (impl)))
4125     change_icon_theme (impl);
4126
4127   g_signal_emit_by_name (widget, "default-size-changed");
4128 }
4129
4130 static void
4131 gtk_file_chooser_default_screen_changed (GtkWidget *widget,
4132                                          GdkScreen *previous_screen)
4133 {
4134   GtkFileChooserDefault *impl;
4135
4136   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
4137
4138   if (GTK_WIDGET_CLASS (parent_class)->screen_changed)
4139     GTK_WIDGET_CLASS (parent_class)->screen_changed (widget, previous_screen);
4140
4141   remove_settings_signal (impl, previous_screen);
4142   check_icon_theme (impl);
4143
4144   g_signal_emit_by_name (widget, "default-size-changed");
4145 }
4146
4147 static gboolean
4148 get_is_file_filtered (GtkFileChooserDefault *impl,
4149                       const GtkFilePath     *path,
4150                       GtkFileInfo           *file_info)
4151 {
4152   GtkFileFilterInfo filter_info;
4153   GtkFileFilterFlags needed;
4154   gboolean result;
4155
4156   if (!impl->current_filter)
4157     return FALSE;
4158
4159   filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
4160
4161   needed = gtk_file_filter_get_needed (impl->current_filter);
4162
4163   filter_info.display_name = gtk_file_info_get_display_name (file_info);
4164   filter_info.mime_type = gtk_file_info_get_mime_type (file_info);
4165
4166   if (needed & GTK_FILE_FILTER_FILENAME)
4167     {
4168       filter_info.filename = gtk_file_system_path_to_filename (impl->file_system, path);
4169       if (filter_info.filename)
4170         filter_info.contains |= GTK_FILE_FILTER_FILENAME;
4171     }
4172   else
4173     filter_info.filename = NULL;
4174
4175   if (needed & GTK_FILE_FILTER_URI)
4176     {
4177       filter_info.uri = gtk_file_system_path_to_uri (impl->file_system, path);
4178       if (filter_info.uri)
4179         filter_info.contains |= GTK_FILE_FILTER_URI;
4180     }
4181   else
4182     filter_info.uri = NULL;
4183
4184   result = gtk_file_filter_filter (impl->current_filter, &filter_info);
4185
4186   if (filter_info.filename)
4187     g_free ((gchar *)filter_info.filename);
4188   if (filter_info.uri)
4189     g_free ((gchar *)filter_info.uri);
4190
4191   return !result;
4192 }
4193
4194 /* GtkWidget::map method */
4195 static void
4196 gtk_file_chooser_default_map (GtkWidget *widget)
4197 {
4198   GtkFileChooserDefault *impl;
4199
4200   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
4201
4202   GTK_WIDGET_CLASS (parent_class)->map (widget);
4203
4204   if (impl->current_folder)
4205     {
4206       pending_select_paths_store_selection (impl);
4207       change_folder_and_display_error (impl, impl->current_folder);
4208     }
4209
4210   bookmarks_changed_cb (impl->file_system, impl);
4211 }
4212
4213 static gboolean
4214 list_model_filter_func (GtkFileSystemModel *model,
4215                         GtkFilePath        *path,
4216                         const GtkFileInfo  *file_info,
4217                         gpointer            user_data)
4218 {
4219   GtkFileChooserDefault *impl = user_data;
4220
4221   if (!impl->current_filter)
4222     return TRUE;
4223
4224   if (gtk_file_info_get_is_folder (file_info))
4225     return TRUE;
4226
4227   return !get_is_file_filtered (impl, path, (GtkFileInfo *) file_info);
4228 }
4229
4230 static void
4231 install_list_model_filter (GtkFileChooserDefault *impl)
4232 {
4233   GtkFileSystemModelFilter filter;
4234   gpointer data;
4235
4236   g_assert (impl->browse_files_model != NULL);
4237
4238   if (impl->current_filter)
4239     {
4240       filter = list_model_filter_func;
4241       data   = impl;
4242     }
4243   else
4244     {
4245       filter = NULL;
4246       data   = NULL;
4247     }
4248   
4249   _gtk_file_system_model_set_filter (impl->browse_files_model,
4250                                      filter,
4251                                      data);
4252 }
4253
4254 #define COMPARE_DIRECTORIES                                                                                    \
4255   GtkFileChooserDefault *impl = user_data;                                                                     \
4256   const GtkFileInfo *info_a = _gtk_file_system_model_get_info (impl->browse_files_model, a);                           \
4257   const GtkFileInfo *info_b = _gtk_file_system_model_get_info (impl->browse_files_model, b);                           \
4258   gboolean dir_a, dir_b;                                                                                       \
4259                                                                                                                \
4260   if (info_a)                                                                                                  \
4261     dir_a = gtk_file_info_get_is_folder (info_a);                                                              \
4262   else                                                                                                         \
4263     return impl->list_sort_ascending ? -1 : 1;                                                                 \
4264                                                                                                                \
4265   if (info_b)                                                                                                  \
4266     dir_b = gtk_file_info_get_is_folder (info_b);                                                              \
4267   else                                                                                                         \
4268     return impl->list_sort_ascending ? 1 : -1;                                                                 \
4269                                                                                                                \
4270   if (dir_a != dir_b)                                                                                          \
4271     return impl->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */
4272
4273 /* Sort callback for the filename column */
4274 static gint
4275 name_sort_func (GtkTreeModel *model,
4276                 GtkTreeIter  *a,
4277                 GtkTreeIter  *b,
4278                 gpointer      user_data)
4279 {
4280   COMPARE_DIRECTORIES;
4281   else
4282     return strcmp (gtk_file_info_get_display_key (info_a), gtk_file_info_get_display_key (info_b));
4283 }
4284
4285 /* Sort callback for the size column */
4286 static gint
4287 size_sort_func (GtkTreeModel *model,
4288                 GtkTreeIter  *a,
4289                 GtkTreeIter  *b,
4290                 gpointer      user_data)
4291 {
4292   COMPARE_DIRECTORIES;
4293   else
4294     {
4295       gint64 size_a = gtk_file_info_get_size (info_a);
4296       gint64 size_b = gtk_file_info_get_size (info_b);
4297
4298       return size_a > size_b ? -1 : (size_a == size_b ? 0 : 1);
4299     }
4300 }
4301
4302 /* Sort callback for the mtime column */
4303 static gint
4304 mtime_sort_func (GtkTreeModel *model,
4305                  GtkTreeIter  *a,
4306                  GtkTreeIter  *b,
4307                  gpointer      user_data)
4308 {
4309   COMPARE_DIRECTORIES;
4310   else
4311     {
4312       GtkFileTime ta = gtk_file_info_get_modification_time (info_a);
4313       GtkFileTime tb = gtk_file_info_get_modification_time (info_b);
4314
4315       return ta > tb ? -1 : (ta == tb ? 0 : 1);
4316     }
4317 }
4318
4319 /* Callback used when the sort column changes.  We cache the sort order for use
4320  * in name_sort_func().
4321  */
4322 static void
4323 list_sort_column_changed_cb (GtkTreeSortable       *sortable,
4324                              GtkFileChooserDefault *impl)
4325 {
4326   GtkSortType sort_type;
4327
4328   if (gtk_tree_sortable_get_sort_column_id (sortable, NULL, &sort_type))
4329     impl->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
4330 }
4331
4332 static void
4333 set_busy_cursor (GtkFileChooserDefault *impl,
4334                  gboolean               busy)
4335 {
4336   GtkWindow *toplevel;
4337   GdkDisplay *display;
4338   GdkCursor *cursor;
4339
4340   toplevel = get_toplevel (GTK_WIDGET (impl));
4341   if (!toplevel || !GTK_WIDGET_REALIZED (toplevel))
4342     return;
4343
4344   display = gtk_widget_get_display (GTK_WIDGET (toplevel));
4345
4346   if (busy)
4347     cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
4348   else
4349     cursor = NULL;
4350
4351   gdk_window_set_cursor (GTK_WIDGET (toplevel)->window, cursor);
4352   gdk_display_flush (display);
4353
4354   if (cursor)
4355     gdk_cursor_unref (cursor);
4356 }
4357
4358 /* Creates a sort model to wrap the file system model and sets it on the tree view */
4359 static void
4360 load_set_model (GtkFileChooserDefault *impl)
4361 {
4362   g_assert (impl->browse_files_model != NULL);
4363   g_assert (impl->sort_model == NULL);
4364
4365   impl->sort_model = (GtkTreeModelSort *)gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (impl->browse_files_model));
4366   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, name_sort_func, impl, NULL);
4367   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_SIZE, size_sort_func, impl, NULL);
4368   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_MTIME, mtime_sort_func, impl, NULL);
4369   gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->sort_model), NULL, NULL, NULL);
4370   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, GTK_SORT_ASCENDING);
4371   impl->list_sort_ascending = TRUE;
4372
4373   g_signal_connect (impl->sort_model, "sort-column-changed",
4374                     G_CALLBACK (list_sort_column_changed_cb), impl);
4375
4376   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
4377                            GTK_TREE_MODEL (impl->sort_model));
4378   gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->browse_files_tree_view));
4379   gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_files_tree_view),
4380                                    GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
4381 }
4382
4383 /* Timeout callback used when the loading timer expires */
4384 static gboolean
4385 load_timeout_cb (gpointer data)
4386 {
4387   GtkFileChooserDefault *impl;
4388
4389   GDK_THREADS_ENTER ();
4390
4391   impl = GTK_FILE_CHOOSER_DEFAULT (data);
4392   g_assert (impl->load_state == LOAD_PRELOAD);
4393   g_assert (impl->load_timeout_id != 0);
4394   g_assert (impl->browse_files_model != NULL);
4395
4396   impl->load_timeout_id = 0;
4397   impl->load_state = LOAD_LOADING;
4398
4399   load_set_model (impl);
4400
4401   GDK_THREADS_LEAVE ();
4402
4403   return FALSE;
4404 }
4405
4406 /* Sets up a new load timer for the model and switches to the LOAD_LOADING state */
4407 static void
4408 load_setup_timer (GtkFileChooserDefault *impl)
4409 {
4410   g_assert (impl->load_timeout_id == 0);
4411   g_assert (impl->load_state != LOAD_PRELOAD);
4412
4413   impl->load_timeout_id = g_timeout_add (MAX_LOADING_TIME, load_timeout_cb, impl);
4414   impl->load_state = LOAD_PRELOAD;
4415 }
4416
4417 /* Removes the load timeout and switches to the LOAD_FINISHED state */
4418 static void
4419 load_remove_timer (GtkFileChooserDefault *impl)
4420 {
4421   if (impl->load_timeout_id != 0)
4422     {
4423       g_assert (impl->load_state == LOAD_PRELOAD);
4424
4425       g_source_remove (impl->load_timeout_id);
4426       impl->load_timeout_id = 0;
4427       impl->load_state = LOAD_EMPTY;
4428     }
4429   else
4430     g_assert (impl->load_state == LOAD_EMPTY ||
4431               impl->load_state == LOAD_LOADING ||
4432               impl->load_state == LOAD_FINISHED);
4433 }
4434
4435 /* Selects the first row in the file list */
4436 static void
4437 browse_files_select_first_row (GtkFileChooserDefault *impl)
4438 {
4439   GtkTreePath *path;
4440
4441   if (!impl->sort_model)
4442     return;
4443
4444   path = gtk_tree_path_new_from_indices (0, -1);
4445   gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), path, NULL, FALSE);
4446   gtk_tree_path_free (path);
4447 }
4448
4449 struct center_selected_row_closure {
4450   GtkFileChooserDefault *impl;
4451   gboolean already_centered;
4452 };
4453
4454 /* Callback used from gtk_tree_selection_selected_foreach(); centers the
4455  * selected row in the tree view.
4456  */
4457 static void
4458 center_selected_row_foreach_cb (GtkTreeModel      *model,
4459                                 GtkTreePath       *path,
4460                                 GtkTreeIter       *iter,
4461                                 gpointer           data)
4462 {
4463   struct center_selected_row_closure *closure;
4464
4465   closure = data;
4466   if (closure->already_centered)
4467     return;
4468
4469   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (closure->impl->browse_files_tree_view), path, NULL, TRUE, 0.5, 0.0);
4470   closure->already_centered = TRUE;
4471 }
4472
4473 /* Centers the selected row in the tree view */
4474 static void
4475 browse_files_center_selected_row (GtkFileChooserDefault *impl)
4476 {
4477   struct center_selected_row_closure closure;
4478   GtkTreeSelection *selection;
4479
4480   closure.impl = impl;
4481   closure.already_centered = FALSE;
4482
4483   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4484   gtk_tree_selection_selected_foreach (selection, center_selected_row_foreach_cb, &closure);
4485 }
4486
4487 static gboolean
4488 show_and_select_paths (GtkFileChooserDefault *impl,
4489                        const GtkFilePath     *parent_path,
4490                        const GtkFilePath     *only_one_path,
4491                        GSList                *paths,
4492                        GError               **error)
4493 {
4494   GtkFileFolder *folder;
4495   gboolean success;
4496   gboolean have_hidden;
4497   gboolean have_filtered;
4498
4499   if (!only_one_path && !paths)
4500     return TRUE;
4501
4502   folder = gtk_file_system_get_folder (impl->file_system, parent_path, GTK_FILE_INFO_IS_HIDDEN, error);
4503   if (!folder)
4504     return FALSE;
4505
4506   success = FALSE;
4507   have_hidden = FALSE;
4508   have_filtered = FALSE;
4509
4510   if (only_one_path)
4511     {
4512       GtkFileInfo *info;
4513
4514       info = gtk_file_folder_get_info (folder, only_one_path, error);
4515       if (info)
4516         {
4517           success = TRUE;
4518           have_hidden = gtk_file_info_get_is_hidden (info);
4519           have_filtered = get_is_file_filtered (impl, only_one_path, info);
4520           gtk_file_info_free (info);
4521         }
4522     }
4523   else
4524     {
4525       GSList *l;
4526
4527       for (l = paths; l; l = l->next)
4528         {
4529           const GtkFilePath *path;
4530           GtkFileInfo *info;
4531
4532           path = l->data;
4533
4534           /* NULL GError */
4535           info = gtk_file_folder_get_info (folder, path, NULL);
4536           if (info)
4537             {
4538               if (!have_hidden)
4539                 have_hidden = gtk_file_info_get_is_hidden (info);
4540
4541               if (!have_filtered)
4542                 have_filtered = get_is_file_filtered (impl, path, info);
4543
4544               gtk_file_info_free (info);
4545
4546               if (have_hidden && have_filtered)
4547                 break; /* we now have all the information we need */
4548             }
4549         }
4550
4551       success = TRUE;
4552     }
4553
4554   g_object_unref (folder);
4555
4556   if (!success)
4557     return FALSE;
4558
4559   if (have_hidden)
4560     g_object_set (impl, "show-hidden", TRUE, NULL);
4561
4562   if (have_filtered)
4563     set_current_filter (impl, NULL);
4564
4565   if (only_one_path)
4566     _gtk_file_system_model_path_do (impl->browse_files_model, only_one_path, select_func, impl);
4567   else
4568     {
4569       GSList *l;
4570
4571       for (l = paths; l; l = l->next)
4572         {
4573           const GtkFilePath *path;
4574
4575           path = l->data;
4576           _gtk_file_system_model_path_do (impl->browse_files_model, path, select_func, impl);
4577         }
4578     }
4579
4580   return TRUE;
4581 }
4582
4583 /* Processes the pending operation when a folder is finished loading */
4584 static void
4585 pending_select_paths_process (GtkFileChooserDefault *impl)
4586 {
4587   g_assert (impl->load_state == LOAD_FINISHED);
4588   g_assert (impl->browse_files_model != NULL);
4589   g_assert (impl->sort_model != NULL);
4590
4591   if (impl->pending_select_paths)
4592     {
4593       /* NULL GError */
4594       show_and_select_paths (impl, impl->current_folder, NULL, impl->pending_select_paths, NULL);
4595       pending_select_paths_free (impl);
4596       browse_files_center_selected_row (impl);
4597     }
4598   else
4599     {
4600       /* We only select the first row if the chooser is actually mapped ---
4601        * selecting the first row is to help the user when he is interacting with
4602        * the chooser, but sometimes a chooser works not on behalf of the user,
4603        * but rather on behalf of something else like GtkFileChooserButton.  In
4604        * that case, the chooser's selection should be what the caller expects,
4605        * as the user can't see that something else got selected.  See bug #165264.
4606        *
4607        * Also, we don't select the first file if we are in SAVE or CREATE_FOLDER
4608        * modes.  Doing so would change the contents of the filename entry.
4609        */
4610       if (GTK_WIDGET_MAPPED (impl)
4611           && !(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
4612         browse_files_select_first_row (impl);
4613     }
4614
4615   g_assert (impl->pending_select_paths == NULL);
4616 }
4617
4618 /* Callback used when the file system model finishes loading */
4619 static void
4620 browse_files_model_finished_loading_cb (GtkFileSystemModel    *model,
4621                                         GtkFileChooserDefault *impl)
4622 {
4623   if (impl->load_state == LOAD_PRELOAD)
4624     {
4625       load_remove_timer (impl);
4626       load_set_model (impl);
4627     }
4628   else if (impl->load_state == LOAD_LOADING)
4629     {
4630       /* Nothing */
4631     }
4632   else
4633     {
4634       /* We can't g_assert_not_reached(), as something other than us may have
4635        *  initiated a folder reload.  See #165556.
4636        */
4637       return;
4638     }
4639
4640   g_assert (impl->load_timeout_id == 0);
4641
4642   impl->load_state = LOAD_FINISHED;
4643
4644   pending_select_paths_process (impl);
4645   set_busy_cursor (impl, FALSE);
4646 }
4647
4648 /* Gets rid of the old list model and creates a new one for the current folder */
4649 static gboolean
4650 set_list_model (GtkFileChooserDefault *impl,
4651                 GError               **error)
4652 {
4653   g_assert (impl->current_folder != NULL);
4654
4655   load_remove_timer (impl); /* This changes the state to LOAD_EMPTY */
4656
4657   if (impl->browse_files_model)
4658     {
4659       g_object_unref (impl->browse_files_model);
4660       impl->browse_files_model = NULL;
4661     }
4662
4663   if (impl->sort_model)
4664     {
4665       g_object_unref (impl->sort_model);
4666       impl->sort_model = NULL;
4667     }
4668
4669   set_busy_cursor (impl, TRUE);
4670   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
4671
4672   impl->browse_files_model = _gtk_file_system_model_new (impl->file_system,
4673                                                          impl->current_folder, 0,
4674                                                          GTK_FILE_INFO_ALL,
4675                                                          error);
4676   if (!impl->browse_files_model)
4677     {
4678       set_busy_cursor (impl, FALSE);
4679       return FALSE;
4680     }
4681
4682   load_setup_timer (impl); /* This changes the state to LOAD_PRELOAD */
4683
4684   g_signal_connect (impl->browse_files_model, "finished-loading",
4685                     G_CALLBACK (browse_files_model_finished_loading_cb), impl);
4686
4687   _gtk_file_system_model_set_show_hidden (impl->browse_files_model, impl->show_hidden);
4688
4689   install_list_model_filter (impl);
4690
4691   return TRUE;
4692 }
4693
4694 static void
4695 update_chooser_entry (GtkFileChooserDefault *impl)
4696 {
4697   GtkTreeSelection *selection;
4698   const GtkFileInfo *info;
4699   GtkTreeIter iter;
4700   GtkTreeIter child_iter;
4701
4702   if (impl->action != GTK_FILE_CHOOSER_ACTION_SAVE)
4703     return;
4704
4705   g_assert (!impl->select_multiple);
4706   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4707
4708   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
4709     return;
4710
4711   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
4712                                                   &child_iter,
4713                                                   &iter);
4714
4715   info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
4716
4717   if (!gtk_file_info_get_is_folder (info))
4718     _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry),
4719                                            gtk_file_info_get_display_name (info));
4720 }
4721
4722 static gboolean
4723 gtk_file_chooser_default_set_current_folder (GtkFileChooser    *chooser,
4724                                              const GtkFilePath *path,
4725                                              GError           **error)
4726 {
4727   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4728   gboolean result;
4729
4730   g_assert (path != NULL);
4731
4732   if (impl->local_only &&
4733       !gtk_file_system_path_is_local (impl->file_system, path))
4734     {
4735       g_set_error (error,
4736                    GTK_FILE_CHOOSER_ERROR,
4737                    GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
4738                    _("Cannot change to folder because it is not local"));
4739
4740       return FALSE;
4741     }
4742
4743   /* Test validity of path here.  */
4744   if (!check_is_folder (impl->file_system, path, error))
4745     return FALSE;
4746
4747   if (!_gtk_path_bar_set_path (GTK_PATH_BAR (impl->browse_path_bar), path, error))
4748     return FALSE;
4749
4750   if (impl->current_folder != path)
4751     {
4752       if (impl->current_folder)
4753         gtk_file_path_free (impl->current_folder);
4754
4755       impl->current_folder = gtk_file_path_copy (path);
4756     }
4757
4758   /* Update the widgets that may trigger a folder change themselves.  */
4759
4760   if (!impl->changing_folder)
4761     {
4762       impl->changing_folder = TRUE;
4763
4764       shortcuts_update_current_folder (impl);
4765
4766       impl->changing_folder = FALSE;
4767     }
4768
4769   /* Set the folder on the save entry */
4770
4771   _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry),
4772                                            impl->current_folder);
4773
4774   /* Create a new list model.  This is slightly evil; we store the result value
4775    * but perform more actions rather than returning immediately even if it
4776    * generates an error.
4777    */
4778   result = set_list_model (impl, error);
4779
4780   /* Refresh controls */
4781
4782   shortcuts_find_current_folder (impl);
4783
4784   g_signal_emit_by_name (impl, "current-folder-changed", 0);
4785
4786   check_preview_change (impl);
4787   bookmarks_check_add_sensitivity (impl);
4788
4789   g_signal_emit_by_name (impl, "selection-changed", 0);
4790
4791   return result;
4792 }
4793
4794 static GtkFilePath *
4795 gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser)
4796 {
4797   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4798
4799   return gtk_file_path_copy (impl->current_folder);
4800 }
4801
4802 static void
4803 gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
4804                                            const gchar    *name)
4805 {
4806   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4807
4808   g_return_if_fail (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
4809                     || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
4810
4811   _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry), name);
4812 }
4813
4814 static void
4815 select_func (GtkFileSystemModel *model,
4816              GtkTreePath        *path,
4817              GtkTreeIter        *iter,
4818              gpointer            user_data)
4819 {
4820   GtkFileChooserDefault *impl = user_data;
4821   GtkTreeSelection *selection;
4822   GtkTreeIter sorted_iter;
4823
4824   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4825
4826   gtk_tree_model_sort_convert_child_iter_to_iter (impl->sort_model, &sorted_iter, iter);
4827   gtk_tree_selection_select_iter (selection, &sorted_iter);
4828 }
4829
4830 static gboolean
4831 gtk_file_chooser_default_select_path (GtkFileChooser    *chooser,
4832                                       const GtkFilePath *path,
4833                                       GError           **error)
4834 {
4835   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4836   GtkFilePath *parent_path;
4837   gboolean same_path;
4838
4839   if (!gtk_file_system_get_parent (impl->file_system, path, &parent_path, error))
4840     return FALSE;
4841
4842   if (!parent_path)
4843     return _gtk_file_chooser_set_current_folder_path (chooser, path, error);
4844
4845   if (impl->load_state == LOAD_EMPTY)
4846     same_path = FALSE;
4847   else
4848     {
4849       g_assert (impl->current_folder != NULL);
4850
4851       same_path = gtk_file_path_compare (parent_path, impl->current_folder) == 0;
4852     }
4853
4854   if (same_path && impl->load_state == LOAD_FINISHED)
4855     {
4856       gboolean result;
4857
4858       result = show_and_select_paths (impl, parent_path, path, NULL, error);
4859       gtk_file_path_free (parent_path);
4860       return result;
4861     }
4862
4863   pending_select_paths_add (impl, path);
4864
4865   if (!same_path)
4866     {
4867       gboolean result;
4868
4869       result = _gtk_file_chooser_set_current_folder_path (chooser, parent_path, error);
4870       gtk_file_path_free (parent_path);
4871       return result;
4872     }
4873
4874   gtk_file_path_free (parent_path);
4875   return TRUE;
4876 }
4877
4878 static void
4879 unselect_func (GtkFileSystemModel *model,
4880                GtkTreePath        *path,
4881                GtkTreeIter        *iter,
4882                gpointer            user_data)
4883 {
4884   GtkFileChooserDefault *impl = user_data;
4885   GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
4886   GtkTreePath *sorted_path;
4887
4888   sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model,
4889                                                                 path);
4890   gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (tree_view),
4891                                     sorted_path);
4892   gtk_tree_path_free (sorted_path);
4893 }
4894
4895 static void
4896 gtk_file_chooser_default_unselect_path (GtkFileChooser    *chooser,
4897                                         const GtkFilePath *path)
4898 {
4899   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4900
4901   if (!impl->browse_files_model)
4902     return;
4903
4904   _gtk_file_system_model_path_do (impl->browse_files_model, path,
4905                                   unselect_func, impl);
4906 }
4907
4908 static gboolean
4909 maybe_select (GtkTreeModel *model, 
4910               GtkTreePath  *path, 
4911               GtkTreeIter  *iter, 
4912               gpointer     data)
4913 {
4914   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (data);
4915   GtkTreeSelection *selection;
4916   const GtkFileInfo *info;
4917   gboolean is_folder;
4918   
4919   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4920   
4921   info = get_list_file_info (impl, iter);
4922   is_folder = gtk_file_info_get_is_folder (info);
4923
4924   if ((is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) ||
4925       (!is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN))
4926     gtk_tree_selection_select_iter (selection, iter);
4927   else
4928     gtk_tree_selection_unselect_iter (selection, iter);
4929     
4930   return FALSE;
4931 }
4932
4933 static void
4934 gtk_file_chooser_default_select_all (GtkFileChooser *chooser)
4935 {
4936   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4937   if (impl->select_multiple)
4938     gtk_tree_model_foreach (GTK_TREE_MODEL (impl->sort_model), 
4939                             maybe_select, impl);
4940 }
4941
4942 static void
4943 gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser)
4944 {
4945   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4946   GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4947
4948   gtk_tree_selection_unselect_all (selection);
4949 }
4950
4951 /* Checks whether the filename entry for the Save modes contains a valid filename */
4952 static GtkFilePath *
4953 check_save_entry (GtkFileChooserDefault *impl,
4954                   gboolean              *is_valid,
4955                   gboolean              *is_empty)
4956 {
4957   GtkFileChooserEntry *chooser_entry;
4958   const GtkFilePath *current_folder;
4959   const char *file_part;
4960   GtkFilePath *path;
4961   GError *error;
4962
4963   g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
4964             || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
4965
4966   chooser_entry = GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry);
4967
4968   current_folder = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
4969   file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
4970
4971   if (!file_part || file_part[0] == '\0')
4972     {
4973       *is_valid = FALSE;
4974       *is_empty = TRUE;
4975       return NULL;
4976     }
4977
4978   *is_empty = FALSE;
4979
4980   error = NULL;
4981   path = gtk_file_system_make_path (impl->file_system, current_folder, file_part, &error);
4982
4983   if (!path)
4984     {
4985       error_building_filename_dialog (impl, current_folder, file_part, error);
4986       *is_valid = FALSE;
4987       return NULL;
4988     }
4989
4990   *is_valid = TRUE;
4991   return path;
4992 }
4993
4994 struct get_paths_closure {
4995   GtkFileChooserDefault *impl;
4996   GSList *result;
4997   GtkFilePath *path_from_entry;
4998 };
4999
5000 static void
5001 get_paths_foreach (GtkTreeModel *model,
5002                    GtkTreePath  *path,
5003                    GtkTreeIter  *iter,
5004                    gpointer      data)
5005 {
5006   struct get_paths_closure *info;
5007   const GtkFilePath *file_path;
5008   GtkFileSystemModel *fs_model;
5009   GtkTreeIter sel_iter;
5010
5011   info = data;
5012   fs_model = info->impl->browse_files_model;
5013   gtk_tree_model_sort_convert_iter_to_child_iter (info->impl->sort_model, &sel_iter, iter);
5014
5015   file_path = _gtk_file_system_model_get_path (fs_model, &sel_iter);
5016   if (!file_path)
5017     return; /* We are on the editable row */
5018
5019   if (!info->path_from_entry
5020       || gtk_file_path_compare (info->path_from_entry, file_path) != 0)
5021     info->result = g_slist_prepend (info->result, gtk_file_path_copy (file_path));
5022 }
5023
5024 static GSList *
5025 gtk_file_chooser_default_get_paths (GtkFileChooser *chooser)
5026 {
5027   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5028   struct get_paths_closure info;
5029
5030   info.impl = impl;
5031   info.result = NULL;
5032   info.path_from_entry = NULL;
5033
5034   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5035       || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5036     {
5037       gboolean is_valid, is_empty;
5038
5039       info.path_from_entry = check_save_entry (impl, &is_valid, &is_empty);
5040       if (!is_valid && !is_empty)
5041         return NULL;
5042     }
5043
5044   if (!info.path_from_entry || impl->select_multiple)
5045     {
5046       GtkTreeSelection *selection;
5047
5048       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5049       gtk_tree_selection_selected_foreach (selection, get_paths_foreach, &info);
5050     }
5051
5052   if (info.path_from_entry)
5053     info.result = g_slist_prepend (info.result, info.path_from_entry);
5054
5055   /* If there's no folder selected, and we're in SELECT_FOLDER mode, then we
5056    * fall back to the current directory */
5057   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
5058       info.result == NULL)
5059     {
5060       info.result = g_slist_prepend (info.result, gtk_file_path_copy (impl->current_folder));
5061     }
5062
5063   return g_slist_reverse (info.result);
5064 }
5065
5066 static GtkFilePath *
5067 gtk_file_chooser_default_get_preview_path (GtkFileChooser *chooser)
5068 {
5069   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5070
5071   if (impl->preview_path)
5072     return gtk_file_path_copy (impl->preview_path);
5073   else
5074     return NULL;
5075 }
5076
5077 static GtkFileSystem *
5078 gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser)
5079 {
5080   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5081
5082   return impl->file_system;
5083 }
5084
5085 /* Shows or hides the filter widgets */
5086 static void
5087 show_filters (GtkFileChooserDefault *impl,
5088               gboolean               show)
5089 {
5090   if (show)
5091     gtk_widget_show (impl->filter_combo_hbox);
5092   else
5093     gtk_widget_hide (impl->filter_combo_hbox);
5094 }
5095
5096 static void
5097 gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
5098                                      GtkFileFilter  *filter)
5099 {
5100   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5101   const gchar *name;
5102
5103   if (g_slist_find (impl->filters, filter))
5104     {
5105       g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
5106       return;
5107     }
5108
5109   g_object_ref (filter);
5110   gtk_object_sink (GTK_OBJECT (filter));
5111   impl->filters = g_slist_append (impl->filters, filter);
5112
5113   name = gtk_file_filter_get_name (filter);
5114   if (!name)
5115     name = "Untitled filter";   /* Place-holder, doesn't need to be marked for translation */
5116
5117   gtk_combo_box_append_text (GTK_COMBO_BOX (impl->filter_combo), name);
5118
5119   if (!g_slist_find (impl->filters, impl->current_filter))
5120     set_current_filter (impl, filter);
5121
5122   show_filters (impl, TRUE);
5123 }
5124
5125 static void
5126 gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
5127                                         GtkFileFilter  *filter)
5128 {
5129   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5130   GtkTreeModel *model;
5131   GtkTreeIter iter;
5132   gint filter_index;
5133
5134   filter_index = g_slist_index (impl->filters, filter);
5135
5136   if (filter_index < 0)
5137     {
5138       g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
5139       return;
5140     }
5141
5142   impl->filters = g_slist_remove (impl->filters, filter);
5143
5144   if (filter == impl->current_filter)
5145     {
5146       if (impl->filters)
5147         set_current_filter (impl, impl->filters->data);
5148       else
5149         set_current_filter (impl, NULL);
5150     }
5151
5152   /* Remove row from the combo box */
5153   model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->filter_combo));
5154   gtk_tree_model_iter_nth_child  (model, &iter, NULL, filter_index);
5155   gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
5156
5157   g_object_unref (filter);
5158
5159   if (!impl->filters)
5160     show_filters (impl, FALSE);
5161 }
5162
5163 static GSList *
5164 gtk_file_chooser_default_list_filters (GtkFileChooser *chooser)
5165 {
5166   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5167
5168   return g_slist_copy (impl->filters);
5169 }
5170
5171 /* Returns the position in the shortcuts tree where the nth specified shortcut would appear */
5172 static int
5173 shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl,
5174                                        int                    pos)
5175 {
5176   return pos + shortcuts_get_index (impl, SHORTCUTS_SHORTCUTS);
5177 }
5178
5179 static gboolean
5180 gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser    *chooser,
5181                                               const GtkFilePath *path,
5182                                               GError           **error)
5183 {
5184   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5185   gboolean result;
5186   int pos;
5187
5188   /* Test validity of path here.  */
5189   if (!check_is_folder (impl->file_system, path, error))
5190     return FALSE;
5191
5192   pos = shortcuts_get_pos_for_shortcut_folder (impl, impl->num_shortcuts);
5193
5194   result = shortcuts_insert_path (impl, pos, FALSE, NULL, path, NULL, FALSE, error);
5195
5196   if (result)
5197     impl->num_shortcuts++;
5198
5199   if (impl->shortcuts_filter_model)
5200     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
5201
5202   return result;
5203 }
5204
5205 static gboolean
5206 gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
5207                                                  const GtkFilePath *path,
5208                                                  GError           **error)
5209 {
5210   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5211   int pos;
5212   GtkTreeIter iter;
5213   char *uri;
5214   int i;
5215
5216   if (impl->num_shortcuts == 0)
5217     goto out;
5218
5219   pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
5220   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
5221     g_assert_not_reached ();
5222
5223   for (i = 0; i < impl->num_shortcuts; i++)
5224     {
5225       gpointer col_data;
5226       gboolean is_volume;
5227       GtkFilePath *shortcut;
5228
5229       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
5230                           SHORTCUTS_COL_DATA, &col_data,
5231                           SHORTCUTS_COL_IS_VOLUME, &is_volume,
5232                           -1);
5233       g_assert (col_data != NULL);
5234       g_assert (!is_volume);
5235
5236       shortcut = col_data;
5237       if (gtk_file_path_compare (shortcut, path) == 0)
5238         {
5239           shortcuts_remove_rows (impl, pos + i, 1);
5240           impl->num_shortcuts--;
5241           return TRUE;
5242         }
5243
5244       if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
5245         g_assert_not_reached ();
5246     }
5247
5248  out:
5249
5250   uri = gtk_file_system_path_to_uri (impl->file_system, path);
5251   g_set_error (error,
5252                GTK_FILE_CHOOSER_ERROR,
5253                GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
5254                _("Shortcut %s does not exist"),
5255                uri);
5256   g_free (uri);
5257
5258   return FALSE;
5259 }
5260
5261 static GSList *
5262 gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser)
5263 {
5264   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5265   int pos;
5266   GtkTreeIter iter;
5267   int i;
5268   GSList *list;
5269
5270   if (impl->num_shortcuts == 0)
5271     return NULL;
5272
5273   pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
5274   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
5275     g_assert_not_reached ();
5276
5277   list = NULL;
5278
5279   for (i = 0; i < impl->num_shortcuts; i++)
5280     {
5281       gpointer col_data;
5282       gboolean is_volume;
5283       GtkFilePath *shortcut;
5284
5285       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
5286                           SHORTCUTS_COL_DATA, &col_data,
5287                           SHORTCUTS_COL_IS_VOLUME, &is_volume,
5288                           -1);
5289       g_assert (col_data != NULL);
5290       g_assert (!is_volume);
5291
5292       shortcut = col_data;
5293       list = g_slist_prepend (list, gtk_file_path_copy (shortcut));
5294
5295       if (i != impl->num_shortcuts - 1)
5296         {
5297           if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
5298             g_assert_not_reached ();
5299         }
5300     }
5301
5302   return g_slist_reverse (list);
5303 }
5304
5305 /* Guesses a size based upon font sizes */
5306 static void
5307 find_good_size_from_style (GtkWidget *widget,
5308                            gint      *width,
5309                            gint      *height)
5310 {
5311   GtkFileChooserDefault *impl;
5312   gint default_width, default_height;
5313   int font_size;
5314   GtkRequisition req;
5315   GtkRequisition preview_req;
5316
5317   g_assert (widget->style != NULL);
5318   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5319
5320   font_size = pango_font_description_get_size (widget->style->font_desc);
5321   font_size = PANGO_PIXELS (font_size);
5322
5323   default_width = font_size * NUM_CHARS;
5324   default_height = font_size * NUM_LINES;
5325
5326   /* Use at least the requisition size not including the preview widget */
5327   gtk_widget_size_request (widget, &req);
5328
5329   if (impl->preview_widget_active && impl->preview_widget)
5330     gtk_widget_size_request (impl->preview_box, &preview_req);
5331   else
5332     preview_req.width = 0;
5333
5334   default_width = MAX (default_width, (req.width - (preview_req.width + PREVIEW_HBOX_SPACING)));
5335   default_height = MAX (default_height, req.height);
5336
5337   *width = default_width;
5338   *height = default_height;
5339 }
5340
5341 static void
5342 gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
5343                                            gint                *default_width,
5344                                            gint                *default_height)
5345 {
5346   GtkFileChooserDefault *impl;
5347
5348   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
5349
5350   find_good_size_from_style (GTK_WIDGET (chooser_embed), default_width, default_height);
5351
5352   if (impl->preview_widget_active && impl->preview_widget)
5353     *default_width += impl->preview_box->requisition.width + PREVIEW_HBOX_SPACING;
5354 }
5355
5356 static void
5357 gtk_file_chooser_default_get_resizable_hints (GtkFileChooserEmbed *chooser_embed,
5358                                               gboolean            *resize_horizontally,
5359                                               gboolean            *resize_vertically)
5360 {
5361   GtkFileChooserDefault *impl;
5362
5363   g_return_if_fail (resize_horizontally != NULL);
5364   g_return_if_fail (resize_vertically != NULL);
5365
5366   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
5367
5368   *resize_horizontally = TRUE;
5369   *resize_vertically = TRUE;
5370
5371   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
5372       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5373     {
5374       if (! gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
5375         {
5376           *resize_horizontally = FALSE;
5377           *resize_vertically = FALSE;
5378         }
5379     }
5380 }
5381
5382 struct switch_folder_closure {
5383   GtkFileChooserDefault *impl;
5384   const GtkFilePath *path;
5385   int num_selected;
5386 };
5387
5388 /* Used from gtk_tree_selection_selected_foreach() in switch_to_selected_folder() */
5389 static void
5390 switch_folder_foreach_cb (GtkTreeModel      *model,
5391                           GtkTreePath       *path,
5392                           GtkTreeIter       *iter,
5393                           gpointer           data)
5394 {
5395   struct switch_folder_closure *closure;
5396   GtkTreeIter child_iter;
5397
5398   closure = data;
5399
5400   gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
5401
5402   closure->path = _gtk_file_system_model_get_path (closure->impl->browse_files_model, &child_iter);
5403   closure->num_selected++;
5404 }
5405
5406 /* Changes to the selected folder in the list view */
5407 static void
5408 switch_to_selected_folder (GtkFileChooserDefault *impl)
5409 {
5410   GtkTreeSelection *selection;
5411   struct switch_folder_closure closure;
5412
5413   /* We do this with foreach() rather than get_selected() as we may be in
5414    * multiple selection mode
5415    */
5416
5417   closure.impl = impl;
5418   closure.path = NULL;
5419   closure.num_selected = 0;
5420
5421   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5422   gtk_tree_selection_selected_foreach (selection, switch_folder_foreach_cb, &closure);
5423
5424   g_assert (closure.path && closure.num_selected == 1);
5425
5426   change_folder_and_display_error (impl, closure.path);
5427 }
5428
5429 /* Implementation for GtkFileChooserEmbed::should_respond() */
5430 static gboolean
5431 gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
5432 {
5433   GtkFileChooserDefault *impl;
5434   GtkWidget *toplevel;
5435   GtkWidget *current_focus;
5436
5437   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
5438
5439   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
5440   g_assert (GTK_IS_WINDOW (toplevel));
5441
5442   current_focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
5443
5444   if (current_focus == impl->browse_files_tree_view)
5445     {
5446       int num_selected;
5447       gboolean all_files, all_folders;
5448
5449     file_list:
5450
5451       selection_check (impl, &num_selected, &all_files, &all_folders);
5452
5453       if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5454         {
5455           if (num_selected != 1)         
5456             return TRUE; /* zero means current folder; more than one means use the whole selection */    
5457           else if (current_focus != impl->browse_files_tree_view)        
5458             {
5459               /* a single folder is selected and a button was clicked */
5460               switch_to_selected_folder (impl);          
5461               return TRUE;
5462             }
5463         }
5464
5465       if (num_selected == 0)
5466         {
5467           if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5468               || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5469             goto save_entry; /* it makes sense to use the typed name */
5470           else
5471             return FALSE;
5472         }
5473
5474       if (num_selected == 1 && all_folders)
5475         {
5476           switch_to_selected_folder (impl);
5477           return FALSE;
5478         }
5479       else
5480         return all_files;
5481     }
5482   else if (current_focus == impl->save_file_name_entry)
5483     {
5484       GtkFilePath *path;
5485       gboolean is_valid, is_empty;
5486       gboolean is_folder;
5487       gboolean retval;
5488       GtkFileChooserEntry *entry;  
5489
5490     save_entry:
5491
5492       g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5493                 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
5494
5495       entry = GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry);
5496       path = check_save_entry (impl, &is_valid, &is_empty);
5497
5498       if (!is_empty && !is_valid)
5499         return FALSE;
5500
5501       if (is_empty)
5502         path = gtk_file_path_copy (_gtk_file_chooser_entry_get_current_folder (entry));
5503       
5504       is_folder = check_is_folder (impl->file_system, path, NULL);
5505       if (is_folder)
5506         {
5507           _gtk_file_chooser_entry_set_file_part (entry, "");
5508           change_folder_and_display_error (impl, path);
5509           retval = FALSE;
5510         }
5511       else
5512         {
5513           /* check that everything up to the last component exists */
5514           gtk_file_path_free (path);
5515           path = gtk_file_path_copy (_gtk_file_chooser_entry_get_current_folder (entry));
5516           is_folder = check_is_folder (impl->file_system, path, NULL);
5517           if (!is_folder)
5518             {
5519               change_folder_and_display_error (impl, path);
5520               retval = FALSE;
5521             }
5522           else
5523             retval = TRUE;
5524         }
5525
5526       gtk_file_path_free (path);
5527       return retval;
5528     }
5529   else if (impl->toplevel_last_focus_widget == impl->browse_shortcuts_tree_view)
5530     {
5531       /* The focus is on a dialog's action area button, *and* the widget that
5532        * was focused immediately before it is the shortcuts list.  Switch to the
5533        * selected shortcut and tell the caller not to respond.
5534        */
5535       GtkTreeIter iter;
5536
5537       if (shortcuts_get_selected (impl, &iter))
5538         {
5539           shortcuts_activate_iter (impl, &iter);
5540           
5541           gtk_widget_grab_focus (impl->browse_files_tree_view);
5542         }
5543       else
5544         goto file_list;
5545
5546       return FALSE;
5547     }
5548   else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
5549     {
5550       /* The focus is on a dialog's action area button, *and* the widget that
5551        * was focused immediately before it is the file list.  
5552        */
5553       goto file_list;
5554     }
5555   else
5556     /* The focus is on a dialog's action area button or something else */
5557     if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5558         || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5559       goto save_entry;
5560     else
5561       goto file_list; 
5562   
5563   g_assert_not_reached ();
5564   return FALSE;
5565 }
5566
5567 /* Implementation for GtkFileChooserEmbed::initial_focus() */
5568 static void
5569 gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed)
5570 {
5571   GtkFileChooserDefault *impl;
5572   GtkWidget *widget;
5573
5574   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
5575
5576   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
5577       || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5578     widget = impl->browse_files_tree_view;
5579   else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5580            || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5581     widget = impl->save_file_name_entry;
5582   else
5583     {
5584       g_assert_not_reached ();
5585       widget = NULL;
5586     }
5587
5588   gtk_widget_grab_focus (widget);
5589 }
5590
5591 static void
5592 set_current_filter (GtkFileChooserDefault *impl,
5593                     GtkFileFilter         *filter)
5594 {
5595   if (impl->current_filter != filter)
5596     {
5597       int filter_index;
5598
5599       /* NULL filters are allowed to reset to non-filtered status
5600        */
5601       filter_index = g_slist_index (impl->filters, filter);
5602       if (impl->filters && filter && filter_index < 0)
5603         return;
5604
5605       if (impl->current_filter)
5606         g_object_unref (impl->current_filter);
5607       impl->current_filter = filter;
5608       if (impl->current_filter)
5609         {
5610           g_object_ref (impl->current_filter);
5611           gtk_object_sink (GTK_OBJECT (filter));
5612         }
5613
5614       if (impl->filters)
5615         gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
5616                                   filter_index);
5617
5618       if (impl->browse_files_model)
5619         install_list_model_filter (impl);
5620
5621       g_object_notify (G_OBJECT (impl), "filter");
5622     }
5623 }
5624
5625 static void
5626 filter_combo_changed (GtkComboBox           *combo_box,
5627                       GtkFileChooserDefault *impl)
5628 {
5629   gint new_index = gtk_combo_box_get_active (combo_box);
5630   GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
5631
5632   set_current_filter (impl, new_filter);
5633 }
5634
5635 static void
5636 check_preview_change (GtkFileChooserDefault *impl)
5637 {
5638   GtkTreePath *cursor_path;
5639   const GtkFilePath *new_path;
5640   const GtkFileInfo *new_info;
5641
5642   gtk_tree_view_get_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), &cursor_path, NULL);
5643   if (cursor_path && impl->sort_model)
5644     {
5645       GtkTreeIter iter;
5646       GtkTreeIter child_iter;
5647
5648       gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, cursor_path);
5649       gtk_tree_path_free (cursor_path);
5650
5651       gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
5652
5653       new_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
5654       new_info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
5655     }
5656   else
5657     {
5658       new_path = NULL;
5659       new_info = NULL;
5660     }
5661
5662   if (new_path != impl->preview_path &&
5663       !(new_path && impl->preview_path &&
5664         gtk_file_path_compare (new_path, impl->preview_path) == 0))
5665     {
5666       if (impl->preview_path)
5667         {
5668           gtk_file_path_free (impl->preview_path);
5669           g_free (impl->preview_display_name);
5670         }
5671
5672       if (new_path)
5673         {
5674           impl->preview_path = gtk_file_path_copy (new_path);
5675           impl->preview_display_name = g_strdup (gtk_file_info_get_display_name (new_info));
5676         }
5677       else
5678         {
5679           impl->preview_path = NULL;
5680           impl->preview_display_name = NULL;
5681         }
5682
5683       if (impl->use_preview_label && impl->preview_label)
5684         gtk_label_set_text (GTK_LABEL (impl->preview_label), impl->preview_display_name);
5685
5686       g_signal_emit_by_name (impl, "update-preview");
5687     }
5688 }
5689
5690 /* Activates a volume by mounting it if necessary and then switching to its
5691  * base path.
5692  */
5693 static void
5694 shortcuts_activate_volume (GtkFileChooserDefault *impl,
5695                            GtkFileSystemVolume   *volume)
5696 {
5697   GtkFilePath *path;
5698
5699   /* We ref the file chooser since volume_mount() may run a main loop, and the
5700    * user could close the file chooser window in the meantime.
5701    */
5702   g_object_ref (impl);
5703
5704   if (!gtk_file_system_volume_get_is_mounted (impl->file_system, volume))
5705     {
5706       GError *error;
5707       gboolean result;
5708
5709       set_busy_cursor (impl, TRUE);
5710
5711       error = NULL;
5712       result = gtk_file_system_volume_mount (impl->file_system, volume, &error);
5713
5714       if (!result)
5715         {
5716           char *msg;
5717
5718           msg = g_strdup_printf (_("Could not mount %s"),
5719                                  gtk_file_system_volume_get_display_name (impl->file_system, volume));
5720           error_message (impl, msg, error->message);
5721           g_free (msg);
5722           g_error_free (error);
5723         }
5724
5725       set_busy_cursor (impl, FALSE);
5726
5727       if (!result)
5728         goto out;
5729     }
5730
5731   path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
5732   change_folder_and_display_error (impl, path);
5733   gtk_file_path_free (path);
5734
5735  out:
5736
5737   g_object_unref (impl);
5738 }
5739
5740 /* Opens the folder or volume at the specified iter in the shortcuts model */
5741 static void
5742 shortcuts_activate_iter (GtkFileChooserDefault *impl,
5743                          GtkTreeIter           *iter)
5744 {
5745   gpointer col_data;
5746   gboolean is_volume;
5747
5748   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
5749                       SHORTCUTS_COL_DATA, &col_data,
5750                       SHORTCUTS_COL_IS_VOLUME, &is_volume,
5751                       -1);
5752
5753   if (!col_data)
5754     return; /* We are on a separator */
5755
5756   if (is_volume)
5757     {
5758       GtkFileSystemVolume *volume;
5759
5760       volume = col_data;
5761
5762       shortcuts_activate_volume (impl, volume);
5763     }
5764   else
5765     {
5766       const GtkFilePath *file_path;
5767
5768       file_path = col_data;
5769       change_folder_and_display_error (impl, file_path);
5770     }
5771 }
5772
5773 /* Callback used when a row in the shortcuts list is activated */
5774 static void
5775 shortcuts_row_activated_cb (GtkTreeView           *tree_view,
5776                             GtkTreePath           *path,
5777                             GtkTreeViewColumn     *column,
5778                             GtkFileChooserDefault *impl)
5779 {
5780   GtkTreeIter iter;
5781   GtkTreeIter child_iter;
5782
5783   if (!gtk_tree_model_get_iter (impl->shortcuts_filter_model, &iter, path))
5784     return;
5785
5786   gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
5787                                                     &child_iter,
5788                                                     &iter);
5789   shortcuts_activate_iter (impl, &child_iter);
5790
5791   gtk_widget_grab_focus (impl->browse_files_tree_view);
5792 }
5793
5794 /* Handler for GtkWidget::key-press-event on the shortcuts list */
5795 static gboolean
5796 shortcuts_key_press_event_cb (GtkWidget             *widget,
5797                               GdkEventKey           *event,
5798                               GtkFileChooserDefault *impl)
5799 {
5800   guint modifiers;
5801
5802   modifiers = gtk_accelerator_get_default_mod_mask ();
5803
5804   if ((event->keyval == GDK_BackSpace
5805       || event->keyval == GDK_Delete
5806       || event->keyval == GDK_KP_Delete)
5807       && (event->state & modifiers) == 0)
5808     {
5809       remove_selected_bookmarks (impl);
5810       return TRUE;
5811     }
5812
5813   return FALSE;
5814 }
5815
5816 static gboolean
5817 shortcuts_select_func  (GtkTreeSelection  *selection,
5818                         GtkTreeModel      *model,
5819                         GtkTreePath       *path,
5820                         gboolean           path_currently_selected,
5821                         gpointer           data)
5822 {
5823   GtkFileChooserDefault *impl = data;
5824
5825   return (*gtk_tree_path_get_indices (path) != shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR));
5826 }
5827
5828 static gboolean
5829 list_select_func  (GtkTreeSelection  *selection,
5830                    GtkTreeModel      *model,
5831                    GtkTreePath       *path,
5832                    gboolean           path_currently_selected,
5833                    gpointer           data)
5834 {
5835   GtkFileChooserDefault *impl = data;
5836
5837   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
5838       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5839     {
5840       GtkTreeIter iter, child_iter;
5841       const GtkFileInfo *info;
5842
5843       if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
5844         return FALSE;
5845       
5846       gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
5847
5848       info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
5849
5850       if (info && !gtk_file_info_get_is_folder (info))
5851         return FALSE;
5852     }
5853     
5854   return TRUE;
5855 }
5856
5857 static void
5858 list_selection_changed (GtkTreeSelection      *selection,
5859                         GtkFileChooserDefault *impl)
5860 {
5861   /* See if we are in the new folder editable row for Save mode */
5862   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
5863     {
5864       GtkTreeSelection *selection;
5865       GtkTreeIter iter, child_iter;
5866       const GtkFileInfo *info;
5867
5868       g_assert (!impl->select_multiple);
5869       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5870       if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
5871         return;
5872
5873       gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
5874                                                       &child_iter,
5875                                                       &iter);
5876
5877       info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
5878       if (!info)
5879         return; /* We are on the editable row for New Folder */
5880     }
5881
5882   update_chooser_entry (impl);
5883   check_preview_change (impl);
5884   bookmarks_check_add_sensitivity (impl);
5885
5886   g_signal_emit_by_name (impl, "selection-changed", 0);
5887 }
5888
5889 /* Callback used when a row in the file list is activated */
5890 static void
5891 list_row_activated (GtkTreeView           *tree_view,
5892                     GtkTreePath           *path,
5893                     GtkTreeViewColumn     *column,
5894                     GtkFileChooserDefault *impl)
5895 {
5896   GtkTreeIter iter, child_iter;
5897   const GtkFileInfo *info;
5898
5899   if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
5900     return;
5901
5902   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
5903
5904   info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
5905
5906   if (gtk_file_info_get_is_folder (info))
5907     {
5908       const GtkFilePath *file_path;
5909
5910       file_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
5911       change_folder_and_display_error (impl, file_path);
5912
5913       return;
5914     }
5915
5916   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
5917       impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
5918     g_signal_emit_by_name (impl, "file-activated");
5919 }
5920
5921 static void
5922 path_bar_clicked (GtkPathBar            *path_bar,
5923                   GtkFilePath           *file_path,
5924                   gboolean               child_is_hidden,
5925                   GtkFileChooserDefault *impl)
5926 {
5927   if (!change_folder_and_display_error (impl, file_path))
5928     return;
5929
5930   /* Say we have "/foo/bar/[.baz]" and the user clicks on "bar".  We should then
5931    * show hidden files so that ".baz" appears in the file list, as it will still
5932    * be shown in the path bar: "/foo/[bar]/.baz"
5933    */
5934   if (child_is_hidden)
5935     g_object_set (impl, "show-hidden", TRUE, NULL);
5936 }
5937
5938 static const GtkFileInfo *
5939 get_list_file_info (GtkFileChooserDefault *impl,
5940                     GtkTreeIter           *iter)
5941 {
5942   GtkTreeIter child_iter;
5943
5944   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
5945                                                   &child_iter,
5946                                                   iter);
5947
5948   return _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
5949 }
5950
5951 static void
5952 list_icon_data_func (GtkTreeViewColumn *tree_column,
5953                      GtkCellRenderer   *cell,
5954                      GtkTreeModel      *tree_model,
5955                      GtkTreeIter       *iter,
5956                      gpointer           data)
5957 {
5958   GtkFileChooserDefault *impl = data;
5959   GtkTreeIter child_iter;
5960   const GtkFilePath *path;
5961   GdkPixbuf *pixbuf;
5962   const GtkFileInfo *info; 
5963   gboolean sensitive = TRUE;
5964   
5965   info = get_list_file_info (impl, iter);
5966
5967   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
5968                                                   &child_iter,
5969                                                   iter);
5970   path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
5971
5972   if (path)
5973     {
5974       /* FIXME: NULL GError */
5975       pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
5976                                             impl->icon_size, NULL);
5977     }
5978   else
5979     {
5980       /* We are on the editable row */
5981       pixbuf = NULL;
5982     }
5983
5984   if (info && (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
5985                impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
5986     sensitive =  gtk_file_info_get_is_folder (info);    
5987     
5988   g_object_set (cell,
5989                 "pixbuf", pixbuf,
5990                 "sensitive", sensitive,
5991                 NULL);
5992     
5993   if (pixbuf)
5994     g_object_unref (pixbuf);
5995 }
5996
5997 static void
5998 list_name_data_func (GtkTreeViewColumn *tree_column,
5999                      GtkCellRenderer   *cell,
6000                      GtkTreeModel      *tree_model,
6001                      GtkTreeIter       *iter,
6002                      gpointer           data)
6003 {
6004   GtkFileChooserDefault *impl = data;
6005   const GtkFileInfo *info = get_list_file_info (impl, iter);
6006   gboolean sensitive = TRUE;
6007
6008   if (!info)
6009     {
6010       g_object_set (cell,
6011                     "text", _("Type name of new folder"),
6012                     NULL);
6013
6014       return;
6015     }
6016
6017
6018   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
6019          || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6020     {
6021       sensitive = gtk_file_info_get_is_folder (info);
6022     } 
6023     
6024   g_object_set (cell,
6025                 "text", gtk_file_info_get_display_name (info),
6026                 "sensitive", sensitive,
6027                 NULL);
6028 }
6029
6030 #if 0
6031 static void
6032 list_size_data_func (GtkTreeViewColumn *tree_column,
6033                      GtkCellRenderer   *cell,
6034                      GtkTreeModel      *tree_model,
6035                      GtkTreeIter       *iter,
6036                      gpointer           data)
6037 {
6038   GtkFileChooserDefault *impl = data;
6039   const GtkFileInfo *info = get_list_file_info (impl, iter);
6040   gint64 size;
6041   gchar *str;
6042   gboolean sensitive = TRUE;
6043
6044   if (!info || gtk_file_info_get_is_folder (info)) 
6045     {
6046       g_object_set (cell,"sensitive", sensitive, NULL);
6047       return;
6048     }
6049
6050   size = gtk_file_info_get_size (info);
6051
6052   if (size < (gint64)1024)
6053     str = g_strdup_printf (ngettext ("%d byte", "%d bytes", (gint)size), (gint)size);
6054   else if (size < (gint64)1024*1024)
6055     str = g_strdup_printf (_("%.1f K"), size / (1024.));
6056   else if (size < (gint64)1024*1024*1024)
6057     str = g_strdup_printf (_("%.1f M"), size / (1024.*1024.));
6058   else
6059     str = g_strdup_printf (_("%.1f G"), size / (1024.*1024.*1024.));
6060
6061   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
6062       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6063     sensitive = FALSE;
6064
6065   g_object_set (cell,
6066                 "text", str,
6067                 "sensitive", sensitive,
6068                 NULL);
6069
6070   g_free (str);
6071 }
6072 #endif
6073
6074 /* Tree column data callback for the file list; fetches the mtime of a file */
6075 static void
6076 list_mtime_data_func (GtkTreeViewColumn *tree_column,
6077                       GtkCellRenderer   *cell,
6078                       GtkTreeModel      *tree_model,
6079                       GtkTreeIter       *iter,
6080                       gpointer           data)
6081 {
6082   GtkFileChooserDefault *impl;
6083   const GtkFileInfo *info;
6084   GtkFileTime time_mtime, time_now;
6085   GDate mtime, now;
6086   int days_diff;
6087   char buf[256];
6088   gboolean sensitive = TRUE;
6089
6090   impl = data;
6091
6092   info = get_list_file_info (impl, iter);
6093   if (!info)
6094     {
6095       g_object_set (cell,
6096                     "text", "",
6097                     "sensitive", TRUE,
6098                     NULL);
6099       return;
6100     }
6101
6102   time_mtime = gtk_file_info_get_modification_time (info);
6103   g_date_set_time (&mtime, (GTime) time_mtime);
6104
6105   time_now = (GTime ) time (NULL);
6106   g_date_set_time (&now, (GTime) time_now);
6107
6108   days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
6109
6110   if (days_diff == 0)
6111     strcpy (buf, _("Today"));
6112   else if (days_diff == 1)
6113     strcpy (buf, _("Yesterday"));
6114   else
6115     {
6116       char *format;
6117
6118       if (days_diff > 1 && days_diff < 7)
6119         format = "%A"; /* Days from last week */
6120       else
6121         format = "%x"; /* Any other date */
6122
6123       if (g_date_strftime (buf, sizeof (buf), format, &mtime) == 0)
6124         strcpy (buf, _("Unknown"));
6125     }
6126
6127   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
6128       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6129     sensitive = gtk_file_info_get_is_folder (info);
6130
6131   g_object_set (cell,
6132                 "text", buf,
6133                 "sensitive", sensitive,
6134                 NULL);
6135 }
6136
6137 GtkWidget *
6138 _gtk_file_chooser_default_new (const char *file_system)
6139 {
6140   return  g_object_new (GTK_TYPE_FILE_CHOOSER_DEFAULT,
6141                         "file-system-backend", file_system,
6142                         NULL);
6143 }
6144
6145 static GtkWidget *
6146 location_entry_create (GtkFileChooserDefault *impl,
6147                        const gchar           *path)
6148 {
6149   GtkWidget *entry;
6150
6151   entry = _gtk_file_chooser_entry_new (TRUE);
6152   /* Pick a good width for the entry */
6153   gtk_entry_set_width_chars (GTK_ENTRY (entry), 30);
6154   gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
6155   _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (entry), impl->file_system);
6156   _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (entry), impl->action);
6157   if (path[0])
6158     {
6159       _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (entry), 
6160                                                gtk_file_path_new_steal ((gchar *)path));
6161       _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (entry), path);
6162     }
6163   else
6164     {
6165       _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (entry), impl->current_folder);
6166       if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
6167           || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
6168         _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (entry), "");
6169       else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6170                || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6171         _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (entry),
6172                                                gtk_entry_get_text (GTK_ENTRY (impl->save_file_name_entry)));
6173       else
6174         g_assert_not_reached ();
6175     }
6176
6177   return GTK_WIDGET (entry);
6178 }
6179
6180 static gboolean
6181 update_from_entry (GtkFileChooserDefault *impl,
6182                    GtkWindow             *parent,
6183                    GtkFileChooserEntry   *chooser_entry)
6184 {
6185   const GtkFilePath *folder_path;
6186   const char *file_part;
6187
6188   folder_path = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
6189   file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
6190
6191   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN && !folder_path)
6192     {
6193       error_message_with_parent (parent,
6194                                  _("Cannot change folder"),
6195                                  _("The folder you specified is an invalid path."));
6196       return FALSE;
6197     }
6198
6199   if (file_part[0] == '\0')
6200     return change_folder_and_display_error (impl, folder_path);
6201   else
6202     {
6203       GtkFileFolder *folder = NULL;
6204       GtkFilePath *subfolder_path = NULL;
6205       GtkFileInfo *info = NULL;
6206       GError *error;
6207       gboolean result;
6208
6209       result = FALSE;
6210
6211       /* If the file part is non-empty, we need to figure out if it refers to a
6212        * folder within folder. We could optimize the case here where the folder
6213        * is already loaded for one of our tree models.
6214        */
6215
6216       error = NULL;
6217       folder = gtk_file_system_get_folder (impl->file_system, folder_path, GTK_FILE_INFO_IS_FOLDER, &error);
6218
6219       if (!folder)
6220         {
6221           error_getting_info_dialog (impl, folder_path, error);
6222           goto out;
6223         }
6224
6225       error = NULL;
6226       subfolder_path = gtk_file_system_make_path (impl->file_system, folder_path, file_part, &error);
6227
6228       if (!subfolder_path)
6229         {
6230           char *msg;
6231           char *uri;
6232
6233           uri = gtk_file_system_path_to_uri (impl->file_system, folder_path);
6234           msg = g_strdup_printf (_("Could not build file name from '%s' and '%s'"),
6235                                  uri, file_part);
6236           error_message (impl, msg, error->message);
6237           g_free (uri);
6238           g_free (msg);
6239           goto out;
6240         }
6241
6242       error = NULL;
6243       info = gtk_file_folder_get_info (folder, subfolder_path, &error);
6244
6245       if (!info)
6246         {
6247           if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6248               || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6249             {
6250               if (!change_folder_and_display_error (impl, folder_path))
6251                 goto out;
6252
6253               gtk_file_chooser_default_set_current_name (GTK_FILE_CHOOSER (impl), file_part);
6254             }
6255           else
6256             error_getting_info_dialog (impl, subfolder_path, error);
6257
6258           goto out;
6259         }
6260
6261       if (gtk_file_info_get_is_folder (info))
6262         result = change_folder_and_display_error (impl, subfolder_path);
6263       else
6264         {
6265           GError *error;
6266
6267           error = NULL;
6268           result = _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (impl), subfolder_path, &error);
6269           if (!result)
6270             error_dialog (impl, _("Could not select item"),
6271                           subfolder_path, error);
6272         }
6273
6274     out:
6275
6276       if (folder)
6277         g_object_unref (folder);
6278
6279       gtk_file_path_free (subfolder_path);
6280
6281       if (info)
6282         gtk_file_info_free (info);
6283
6284       return result;
6285     }
6286
6287   g_assert_not_reached ();
6288 }
6289
6290 static void
6291 location_popup_handler (GtkFileChooserDefault *impl,
6292                         const gchar           *path)
6293 {
6294   GtkWidget *dialog;
6295   GtkWindow *toplevel;
6296   GtkWidget *hbox;
6297   GtkWidget *label;
6298   GtkWidget *entry;
6299   gboolean refocus;
6300   const char *title;
6301   const char *accept_stock;
6302
6303   /* Create dialog */
6304
6305   toplevel = get_toplevel (GTK_WIDGET (impl));
6306
6307   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
6308       || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
6309     {
6310       title = _("Open Location");
6311       accept_stock = GTK_STOCK_OPEN;
6312     }
6313   else
6314     {
6315       g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6316                 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
6317       title = _("Save in Location");
6318       accept_stock = GTK_STOCK_SAVE;
6319     }
6320
6321   dialog = gtk_dialog_new_with_buttons (title,
6322                                         toplevel,
6323                                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
6324                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
6325                                         accept_stock, GTK_RESPONSE_ACCEPT,
6326                                         NULL);
6327   gtk_window_set_default_size (GTK_WINDOW (dialog), 300, -1);
6328   gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
6329   gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2);
6330   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
6331
6332   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
6333                                            GTK_RESPONSE_ACCEPT,
6334                                            GTK_RESPONSE_CANCEL,
6335                                            -1);
6336
6337   hbox = gtk_hbox_new (FALSE, 12);
6338   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0);
6339   gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
6340
6341   label = gtk_label_new_with_mnemonic (_("_Location:"));
6342   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
6343
6344   entry = location_entry_create (impl, path);
6345
6346   gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6347   gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
6348
6349   /* Run */
6350
6351   gtk_widget_show_all (dialog);
6352   /* If the dialog is brought up by typing the first characters
6353    * of a path, unselect the text in the entry, so that you can
6354    * just type on without erasing the initial part.
6355    */
6356   if (path[0])
6357     gtk_editable_select_region (GTK_EDITABLE (entry), -1, -1);
6358
6359   refocus = TRUE;
6360
6361   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
6362     {
6363       if (update_from_entry (impl, GTK_WINDOW (dialog), GTK_FILE_CHOOSER_ENTRY (entry)))
6364         {
6365           if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
6366               || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
6367             {
6368               gtk_widget_grab_focus (impl->browse_files_tree_view);
6369             }
6370           else
6371             {
6372               g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6373                         || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
6374               gtk_widget_grab_focus (impl->save_file_name_entry);
6375             }
6376           refocus = FALSE;
6377         }
6378     }
6379
6380   if (refocus)
6381     {
6382       GtkWindow *toplevel;
6383
6384       toplevel = get_toplevel (GTK_WIDGET (impl));
6385       if (toplevel && toplevel->focus_widget)
6386         gtk_widget_grab_focus (toplevel->focus_widget);
6387     }
6388
6389   gtk_widget_destroy (dialog);
6390 }
6391
6392 /* Handler for the "up-folder" keybinding signal */
6393 static void
6394 up_folder_handler (GtkFileChooserDefault *impl)
6395 {
6396   pending_select_paths_add (impl, impl->current_folder);
6397   _gtk_path_bar_up (GTK_PATH_BAR (impl->browse_path_bar));
6398 }
6399
6400 /* Handler for the "down-folder" keybinding signal */
6401 static void
6402 down_folder_handler (GtkFileChooserDefault *impl)
6403 {
6404   _gtk_path_bar_down (GTK_PATH_BAR (impl->browse_path_bar));
6405 }
6406
6407 /* Handler for the "home-folder" keybinding signal */
6408 static void
6409 home_folder_handler (GtkFileChooserDefault *impl)
6410 {
6411   int pos;
6412   GtkTreeIter iter;
6413
6414   if (!impl->has_home)
6415     return; /* Should we put up an error dialog? */
6416
6417   pos = shortcuts_get_index (impl, SHORTCUTS_HOME);
6418   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
6419     g_assert_not_reached ();
6420
6421   shortcuts_activate_iter (impl, &iter);
6422 }
6423
6424 \f
6425
6426 /* Drag and drop interfaces */
6427
6428 static void
6429 _shortcuts_model_filter_class_init (ShortcutsModelFilterClass *class)
6430 {
6431 }
6432
6433 static void
6434 _shortcuts_model_filter_init (ShortcutsModelFilter *model)
6435 {
6436   model->impl = NULL;
6437 }
6438
6439 /* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */
6440 static gboolean
6441 shortcuts_model_filter_row_draggable (GtkTreeDragSource *drag_source,
6442                                       GtkTreePath       *path)
6443 {
6444   ShortcutsModelFilter *model;
6445   int pos;
6446   int bookmarks_pos;
6447
6448   model = SHORTCUTS_MODEL_FILTER (drag_source);
6449
6450   pos = *gtk_tree_path_get_indices (path);
6451   bookmarks_pos = shortcuts_get_index (model->impl, SHORTCUTS_BOOKMARKS);
6452
6453   return (pos >= bookmarks_pos && pos < bookmarks_pos + model->impl->num_bookmarks);
6454 }
6455
6456 /* GtkTreeDragSource::drag_data_get implementation for the shortcuts filter model */
6457 static gboolean
6458 shortcuts_model_filter_drag_data_get (GtkTreeDragSource *drag_source,
6459                                       GtkTreePath       *path,
6460                                       GtkSelectionData  *selection_data)
6461 {
6462   ShortcutsModelFilter *model;
6463
6464   model = SHORTCUTS_MODEL_FILTER (drag_source);
6465
6466   /* FIXME */
6467
6468   return FALSE;
6469 }
6470
6471 /* Fill the GtkTreeDragSourceIface vtable */
6472 static void
6473 shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface)
6474 {
6475   iface->row_draggable = shortcuts_model_filter_row_draggable;
6476   iface->drag_data_get = shortcuts_model_filter_drag_data_get;
6477 }
6478
6479 #if 0
6480 /* Fill the GtkTreeDragDestIface vtable */
6481 static void
6482 shortcuts_model_filter_drag_dest_iface_init (GtkTreeDragDestIface *iface)
6483 {
6484   iface->drag_data_received = shortcuts_model_filter_drag_data_received;
6485   iface->row_drop_possible = shortcuts_model_filter_row_drop_possible;
6486 }
6487 #endif
6488
6489 static GtkTreeModel *
6490 shortcuts_model_filter_new (GtkFileChooserDefault *impl,
6491                             GtkTreeModel          *child_model,
6492                             GtkTreePath           *root)
6493 {
6494   ShortcutsModelFilter *model;
6495
6496   model = g_object_new (SHORTCUTS_MODEL_FILTER_TYPE,
6497                         "child_model", child_model,
6498                         "virtual_root", root,
6499                         NULL);
6500
6501   model->impl = impl;
6502
6503   return GTK_TREE_MODEL (model);
6504 }
6505
6506 #define __GTK_FILE_CHOOSER_DEFAULT_C__
6507 #include "gtkaliasdef.c"