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