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