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