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