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