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