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