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