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