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