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