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