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