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