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