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