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