]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserdefault.c
Make the new-folder button say "Create Fo_lder" rather than "Create
[~andy/gtk] / gtk / gtkfilechooserdefault.c
1 /* GTK - The GIMP Toolkit
2  * gtkfilechooserdefault.c: Default implementation of GtkFileChooser
3  * Copyright (C) 2003, Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include <config.h>
22 #include "gdk/gdkkeysyms.h"
23 #include "gtkalignment.h"
24 #include "gtkbindings.h"
25 #include "gtkbutton.h"
26 #include "gtkcelllayout.h"
27 #include "gtkcellrendererpixbuf.h"
28 #include "gtkcellrendererseptext.h"
29 #include "gtkcellrenderertext.h"
30 #include "gtkcheckmenuitem.h"
31 #include "gtkcombobox.h"
32 #include "gtkentry.h"
33 #include "gtkexpander.h"
34 #include "gtkfilechooserdefault.h"
35 #include "gtkfilechooserembed.h"
36 #include "gtkfilechooserentry.h"
37 #include "gtkfilechooserutils.h"
38 #include "gtkfilechooser.h"
39 #include "gtkfilesystemmodel.h"
40 #include "gtkframe.h"
41 #include "gtkhbox.h"
42 #include "gtkhpaned.h"
43 #include "gtkiconfactory.h"
44 #include "gtkicontheme.h"
45 #include "gtkimage.h"
46 #include "gtkintl.h"
47 #include "gtklabel.h"
48 #include "gtkmarshalers.h"
49 #include "gtkmenuitem.h"
50 #include "gtkmessagedialog.h"
51 #include "gtkpathbar.h"
52 #include "gtkprivate.h"
53 #include "gtkscrolledwindow.h"
54 #include "gtksizegroup.h"
55 #include "gtkstock.h"
56 #include "gtktable.h"
57 #include "gtktreednd.h"
58 #include "gtktreeprivate.h"
59 #include "gtktreeview.h"
60 #include "gtktreemodelsort.h"
61 #include "gtktreeselection.h"
62 #include "gtktreestore.h"
63 #include "gtktypebuiltins.h"
64 #include "gtkvbox.h"
65
66 #if defined (G_OS_UNIX)
67 #include "gtkfilesystemunix.h"
68 #elif defined (G_OS_WIN32)
69 #include "gtkfilesystemwin32.h"
70 #endif
71
72 #include <string.h>
73 #include <time.h>
74
75 typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
76
77 #define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
78 #define GTK_IS_FILE_CHOOSER_DEFAULT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT))
79 #define GTK_FILE_CHOOSER_DEFAULT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
80
81
82 struct _GtkFileChooserDefaultClass
83 {
84   GtkVBoxClass parent_class;
85 };
86
87 struct _GtkFileChooserDefault
88 {
89   GtkVBox parent_instance;
90
91   GtkFileChooserAction action;
92
93   GtkFileSystem *file_system;
94
95   /* Save mode widgets */
96   GtkWidget *save_widgets;
97
98   GtkWidget *save_file_name_entry;
99   GtkWidget *save_folder_label;
100   GtkWidget *save_folder_combo;
101   GtkWidget *save_extra_align;
102   GtkWidget *save_expander;
103
104   /* The file browsing widgets */
105   GtkWidget *browse_widgets;
106   GtkWidget *browse_shortcuts_tree_view;
107   GtkWidget *browse_shortcuts_add_button;
108   GtkWidget *browse_shortcuts_remove_button;
109   GtkWidget *browse_files_tree_view;
110   GtkWidget *browse_files_popup_menu;
111   GtkWidget *browse_files_popup_menu_hidden_files_item;
112   GtkWidget *browse_new_folder_button;
113   GtkWidget *browse_path_bar;
114   GtkWidget *browse_extra_align;
115
116   GtkFileSystemModel *browse_files_model;
117
118   GtkWidget *filter_combo;
119   GtkWidget *preview_box;
120   GtkWidget *preview_label;
121   GtkWidget *preview_widget;
122   GtkWidget *extra_widget;
123
124   GtkListStore *shortcuts_model;
125   GtkTreeModel *shortcuts_filter_model;
126
127   GtkTreeModelSort *sort_model;
128
129   GtkFileFilter *current_filter;
130   GSList *filters;
131
132   gboolean has_home;
133   gboolean has_desktop;
134
135   int num_volumes;
136   int num_shortcuts;
137   int num_bookmarks;
138
139   guint volumes_changed_id;
140   guint bookmarks_changed_id;
141
142   GtkFilePath *current_volume_path;
143   GtkFilePath *current_folder;
144   GtkFilePath *preview_path;
145   char *preview_display_name;
146
147   GtkTreeViewColumn *list_name_column;
148   GtkCellRenderer *list_name_renderer;
149
150   guint settings_signal_id;
151   int icon_size;
152
153 #if 0
154   GdkDragContext *shortcuts_drag_context;
155   GSource *shortcuts_drag_outside_idle;
156 #endif
157
158   /* Flags */
159
160   guint local_only : 1;
161   guint preview_widget_active : 1;
162   guint use_preview_label : 1;
163   guint select_multiple : 1;
164   guint show_hidden : 1;
165   guint list_sort_ascending : 1;
166   guint changing_folder : 1;
167   guint shortcuts_current_folder_active : 1;
168   guint shortcuts_current_folder_is_volume : 1;
169
170 #if 0
171   guint shortcuts_drag_outside : 1;
172 #endif
173 };
174
175 /* Signal IDs */
176 enum {
177   LOCATION_POPUP,
178   UP_FOLDER,
179   DOWN_FOLDER,
180   HOME_FOLDER,
181   LAST_SIGNAL
182 };
183
184 static guint signals[LAST_SIGNAL] = { 0 };
185
186 /* Column numbers for the shortcuts tree.  Keep these in sync with shortcuts_model_create() */
187 enum {
188   SHORTCUTS_COL_PIXBUF,
189   SHORTCUTS_COL_NAME,
190   SHORTCUTS_COL_PATH,
191   SHORTCUTS_COL_REMOVABLE,
192   SHORTCUTS_COL_PIXBUF_VISIBLE,
193   SHORTCUTS_COL_NUM_COLUMNS
194 };
195
196 /* Column numbers for the file list */
197 enum {
198   FILE_LIST_COL_NAME,
199   FILE_LIST_COL_SIZE,
200   FILE_LIST_COL_MTIME,
201   FILE_LIST_COL_NUM_COLUMNS
202 };
203
204 /* Identifiers for target types */
205 enum {
206   GTK_TREE_MODEL_ROW,
207   TEXT_URI_LIST
208 };
209
210 /* Target types for dragging from the shortcuts list */
211 static GtkTargetEntry shortcuts_source_targets[] = {
212   { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }
213 };
214
215 static const int num_shortcuts_source_targets = (sizeof (shortcuts_source_targets)
216                                                  / sizeof (shortcuts_source_targets[0]));
217
218 /* Target types for dropping into the shortcuts list */
219 static GtkTargetEntry shortcuts_dest_targets[] = {
220   { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW },
221   { "text/uri-list", 0, TEXT_URI_LIST }
222 };
223
224 static const int num_shortcuts_dest_targets = (sizeof (shortcuts_dest_targets)
225                                                / sizeof (shortcuts_dest_targets[0]));
226
227 /* Target types for DnD from the file list */
228 static GtkTargetEntry file_list_source_targets[] = {
229   { "text/uri-list", 0, TEXT_URI_LIST }
230 };
231
232 static const int num_file_list_source_targets = (sizeof (file_list_source_targets)
233                                                  / sizeof (file_list_source_targets[0]));
234
235 /* Interesting places in the shortcuts bar */
236 typedef enum {
237   SHORTCUTS_HOME,
238   SHORTCUTS_DESKTOP,
239   SHORTCUTS_VOLUMES,
240   SHORTCUTS_SHORTCUTS,
241   SHORTCUTS_BOOKMARKS_SEPARATOR,
242   SHORTCUTS_BOOKMARKS,
243   SHORTCUTS_CURRENT_FOLDER_SEPARATOR,
244   SHORTCUTS_CURRENT_FOLDER
245 } ShortcutsIndex;
246
247 /* Icon size for if we can't get it from the theme */
248 #define FALLBACK_ICON_SIZE 20
249
250 #define PREVIEW_HBOX_SPACING 12
251 #define NUM_LINES 40
252 #define NUM_CHARS 60
253
254 static void gtk_file_chooser_default_class_init       (GtkFileChooserDefaultClass *class);
255 static void gtk_file_chooser_default_iface_init       (GtkFileChooserIface        *iface);
256 static void gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface   *iface);
257 static void gtk_file_chooser_default_init             (GtkFileChooserDefault      *impl);
258
259 static GObject* gtk_file_chooser_default_constructor  (GType                  type,
260                                                        guint                  n_construct_properties,
261                                                        GObjectConstructParam *construct_params);
262 static void     gtk_file_chooser_default_finalize     (GObject               *object);
263 static void     gtk_file_chooser_default_set_property (GObject               *object,
264                                                        guint                  prop_id,
265                                                        const GValue          *value,
266                                                        GParamSpec            *pspec);
267 static void     gtk_file_chooser_default_get_property (GObject               *object,
268                                                        guint                  prop_id,
269                                                        GValue                *value,
270                                                        GParamSpec            *pspec);
271 static void     gtk_file_chooser_default_dispose      (GObject               *object);
272 static void     gtk_file_chooser_default_show_all       (GtkWidget             *widget);
273 static void     gtk_file_chooser_default_style_set      (GtkWidget             *widget,
274                                                          GtkStyle              *previous_style);
275 static void     gtk_file_chooser_default_screen_changed (GtkWidget             *widget,
276                                                          GdkScreen             *previous_screen);
277
278 static gboolean       gtk_file_chooser_default_set_current_folder          (GtkFileChooser    *chooser,
279                                                                             const GtkFilePath *path,
280                                                                             GError           **error);
281 static GtkFilePath *  gtk_file_chooser_default_get_current_folder          (GtkFileChooser    *chooser);
282 static void           gtk_file_chooser_default_set_current_name            (GtkFileChooser    *chooser,
283                                                                             const gchar       *name);
284 static gboolean       gtk_file_chooser_default_select_path                 (GtkFileChooser    *chooser,
285                                                                             const GtkFilePath *path,
286                                                                             GError           **error);
287 static void           gtk_file_chooser_default_unselect_path               (GtkFileChooser    *chooser,
288                                                                             const GtkFilePath *path);
289 static void           gtk_file_chooser_default_select_all                  (GtkFileChooser    *chooser);
290 static void           gtk_file_chooser_default_unselect_all                (GtkFileChooser    *chooser);
291 static GSList *       gtk_file_chooser_default_get_paths                   (GtkFileChooser    *chooser);
292 static GtkFilePath *  gtk_file_chooser_default_get_preview_path            (GtkFileChooser    *chooser);
293 static GtkFileSystem *gtk_file_chooser_default_get_file_system             (GtkFileChooser    *chooser);
294 static void           gtk_file_chooser_default_add_filter                  (GtkFileChooser    *chooser,
295                                                                             GtkFileFilter     *filter);
296 static void           gtk_file_chooser_default_remove_filter               (GtkFileChooser    *chooser,
297                                                                             GtkFileFilter     *filter);
298 static GSList *       gtk_file_chooser_default_list_filters                (GtkFileChooser    *chooser);
299 static gboolean       gtk_file_chooser_default_add_shortcut_folder    (GtkFileChooser    *chooser,
300                                                                        const GtkFilePath *path,
301                                                                        GError           **error);
302 static gboolean       gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
303                                                                        const GtkFilePath *path,
304                                                                        GError           **error);
305 static GSList *       gtk_file_chooser_default_list_shortcut_folders  (GtkFileChooser    *chooser);
306
307 static void           gtk_file_chooser_default_get_default_size       (GtkFileChooserEmbed *chooser_embed,
308                                                                        gint                *default_width,
309                                                                        gint                *default_height);
310 static void           gtk_file_chooser_default_get_resizable_hints    (GtkFileChooserEmbed *chooser_embed,
311                                                                        gboolean            *resize_horizontally,
312                                                                        gboolean            *resize_vertically);
313 static gboolean       gtk_file_chooser_default_should_respond         (GtkFileChooserEmbed *chooser_embed);
314 static void           gtk_file_chooser_default_initial_focus          (GtkFileChooserEmbed *chooser_embed);
315
316 static void location_popup_handler (GtkFileChooserDefault *impl);
317 static void up_folder_handler      (GtkFileChooserDefault *impl);
318 static void down_folder_handler    (GtkFileChooserDefault *impl);
319 static void home_folder_handler    (GtkFileChooserDefault *impl);
320 static void update_appearance      (GtkFileChooserDefault *impl);
321
322 static void set_current_filter   (GtkFileChooserDefault *impl,
323                                   GtkFileFilter         *filter);
324 static void check_preview_change (GtkFileChooserDefault *impl);
325
326 static void filter_combo_changed       (GtkComboBox           *combo_box,
327                                         GtkFileChooserDefault *impl);
328 static void     shortcuts_row_activated_cb (GtkTreeView           *tree_view,
329                                             GtkTreePath           *path,
330                                             GtkTreeViewColumn     *column,
331                                             GtkFileChooserDefault *impl);
332 static gboolean shortcuts_select_func   (GtkTreeSelection      *selection,
333                                          GtkTreeModel          *model,
334                                          GtkTreePath           *path,
335                                          gboolean               path_currently_selected,
336                                          gpointer               data);
337 static void shortcuts_activate_item (GtkFileChooserDefault *impl,
338                                      int                    item_num);
339 static int shortcuts_get_index (GtkFileChooserDefault *impl,
340                                 ShortcutsIndex         where);
341 static int shortcut_find_position (GtkFileChooserDefault *impl,
342                                    const GtkFilePath     *path);
343
344 static void list_selection_changed     (GtkTreeSelection      *tree_selection,
345                                         GtkFileChooserDefault *impl);
346 static void list_row_activated         (GtkTreeView           *tree_view,
347                                         GtkTreePath           *path,
348                                         GtkTreeViewColumn     *column,
349                                         GtkFileChooserDefault *impl);
350
351 static void path_bar_clicked           (GtkPathBar            *path_bar,
352                                         GtkFilePath           *file_path,
353                                         gboolean               child_is_hidden,
354                                         GtkFileChooserDefault *impl);
355
356 static void add_bookmark_button_clicked_cb    (GtkButton             *button,
357                                                GtkFileChooserDefault *impl);
358 static void remove_bookmark_button_clicked_cb (GtkButton             *button,
359                                                GtkFileChooserDefault *impl);
360
361 static void list_icon_data_func (GtkTreeViewColumn *tree_column,
362                                  GtkCellRenderer   *cell,
363                                  GtkTreeModel      *tree_model,
364                                  GtkTreeIter       *iter,
365                                  gpointer           data);
366 static void list_name_data_func (GtkTreeViewColumn *tree_column,
367                                  GtkCellRenderer   *cell,
368                                  GtkTreeModel      *tree_model,
369                                  GtkTreeIter       *iter,
370                                  gpointer           data);
371 #if 0
372 static void list_size_data_func (GtkTreeViewColumn *tree_column,
373                                  GtkCellRenderer   *cell,
374                                  GtkTreeModel      *tree_model,
375                                  GtkTreeIter       *iter,
376                                  gpointer           data);
377 #endif
378 static void list_mtime_data_func (GtkTreeViewColumn *tree_column,
379                                   GtkCellRenderer   *cell,
380                                   GtkTreeModel      *tree_model,
381                                   GtkTreeIter       *iter,
382                                   gpointer           data);
383
384 static GObjectClass *parent_class;
385
386 \f
387
388 /* Drag and drop interface declarations */
389
390 typedef struct {
391   GtkTreeModelFilter parent;
392
393   GtkFileChooserDefault *impl;
394 } ShortcutsModelFilter;
395
396 typedef struct {
397   GtkTreeModelFilterClass parent_class;
398 } ShortcutsModelFilterClass;
399
400 #define SHORTCUTS_MODEL_FILTER_TYPE (shortcuts_model_filter_get_type ())
401 #define SHORTCUTS_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_MODEL_FILTER_TYPE, ShortcutsModelFilter))
402
403 static void shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface);
404
405 G_DEFINE_TYPE_WITH_CODE (ShortcutsModelFilter,
406                          shortcuts_model_filter,
407                          GTK_TYPE_TREE_MODEL_FILTER,
408                          G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
409                                                 shortcuts_model_filter_drag_source_iface_init));
410
411 static GtkTreeModel *shortcuts_model_filter_new (GtkFileChooserDefault *impl,
412                                                  GtkTreeModel          *child_model,
413                                                  GtkTreePath           *root);
414
415 \f
416
417 GType
418 _gtk_file_chooser_default_get_type (void)
419 {
420   static GType file_chooser_default_type = 0;
421
422   if (!file_chooser_default_type)
423     {
424       static const GTypeInfo file_chooser_default_info =
425       {
426         sizeof (GtkFileChooserDefaultClass),
427         NULL,           /* base_init */
428         NULL,           /* base_finalize */
429         (GClassInitFunc) gtk_file_chooser_default_class_init,
430         NULL,           /* class_finalize */
431         NULL,           /* class_data */
432         sizeof (GtkFileChooserDefault),
433         0,              /* n_preallocs */
434         (GInstanceInitFunc) gtk_file_chooser_default_init,
435       };
436
437       static const GInterfaceInfo file_chooser_info =
438       {
439         (GInterfaceInitFunc) gtk_file_chooser_default_iface_init, /* interface_init */
440         NULL,                                                          /* interface_finalize */
441         NULL                                                           /* interface_data */
442       };
443
444       static const GInterfaceInfo file_chooser_embed_info =
445       {
446         (GInterfaceInitFunc) gtk_file_chooser_embed_default_iface_init, /* interface_init */
447         NULL,                                                          /* interface_finalize */
448         NULL                                                           /* interface_data */
449       };
450
451       file_chooser_default_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserDefault",
452                                                          &file_chooser_default_info, 0);
453
454       g_type_add_interface_static (file_chooser_default_type,
455                                    GTK_TYPE_FILE_CHOOSER,
456                                    &file_chooser_info);
457       g_type_add_interface_static (file_chooser_default_type,
458                                    GTK_TYPE_FILE_CHOOSER_EMBED,
459                                    &file_chooser_embed_info);
460     }
461
462   return file_chooser_default_type;
463 }
464
465 static void
466 gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
467 {
468   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
469   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
470   GtkBindingSet *binding_set;
471
472   parent_class = g_type_class_peek_parent (class);
473
474   gobject_class->finalize = gtk_file_chooser_default_finalize;
475   gobject_class->constructor = gtk_file_chooser_default_constructor;
476   gobject_class->set_property = gtk_file_chooser_default_set_property;
477   gobject_class->get_property = gtk_file_chooser_default_get_property;
478   gobject_class->dispose = gtk_file_chooser_default_dispose;
479
480   widget_class->show_all = gtk_file_chooser_default_show_all;
481   widget_class->style_set = gtk_file_chooser_default_style_set;
482   widget_class->screen_changed = gtk_file_chooser_default_screen_changed;
483
484   signals[LOCATION_POPUP] =
485     _gtk_binding_signal_new ("location-popup",
486                              G_OBJECT_CLASS_TYPE (class),
487                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
488                              G_CALLBACK (location_popup_handler),
489                              NULL, NULL,
490                              _gtk_marshal_VOID__VOID,
491                              G_TYPE_NONE, 0);
492   signals[UP_FOLDER] =
493     _gtk_binding_signal_new ("up-folder",
494                              G_OBJECT_CLASS_TYPE (class),
495                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
496                              G_CALLBACK (up_folder_handler),
497                              NULL, NULL,
498                              _gtk_marshal_VOID__VOID,
499                              G_TYPE_NONE, 0);
500   signals[DOWN_FOLDER] =
501     _gtk_binding_signal_new ("down-folder",
502                              G_OBJECT_CLASS_TYPE (class),
503                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
504                              G_CALLBACK (down_folder_handler),
505                              NULL, NULL,
506                              _gtk_marshal_VOID__VOID,
507                              G_TYPE_NONE, 0);
508   signals[HOME_FOLDER] =
509     _gtk_binding_signal_new ("home-folder",
510                              G_OBJECT_CLASS_TYPE (class),
511                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
512                              G_CALLBACK (home_folder_handler),
513                              NULL, NULL,
514                              _gtk_marshal_VOID__VOID,
515                              G_TYPE_NONE, 0);
516
517   binding_set = gtk_binding_set_by_class (class);
518
519   gtk_binding_entry_add_signal (binding_set,
520                                 GDK_l, GDK_CONTROL_MASK,
521                                 "location-popup",
522                                 0);
523
524   gtk_binding_entry_add_signal (binding_set,
525                                 GDK_Up, GDK_MOD1_MASK,
526                                 "up-folder",
527                                 0);
528   gtk_binding_entry_add_signal (binding_set,
529                                 GDK_KP_Up, GDK_MOD1_MASK,
530                                 "up-folder",
531                                 0);
532
533   gtk_binding_entry_add_signal (binding_set,
534                                 GDK_Down, GDK_MOD1_MASK,
535                                 "down-folder",
536                                 0);
537   gtk_binding_entry_add_signal (binding_set,
538                                 GDK_KP_Down, GDK_MOD1_MASK,
539                                 "down-folder",
540                                 0);
541
542   gtk_binding_entry_add_signal (binding_set,
543                                 GDK_Home, GDK_MOD1_MASK,
544                                 "home-folder",
545                                 0);
546   gtk_binding_entry_add_signal (binding_set,
547                                 GDK_KP_Home, GDK_MOD1_MASK,
548                                 "home-folder",
549                                 0);
550
551   _gtk_file_chooser_install_properties (gobject_class);
552
553   gtk_settings_install_property (g_param_spec_string ("gtk-file-chooser-backend",
554                                                       P_("Default file chooser backend"),
555                                                       P_("Name of the GtkFileChooser backend to use by default"),
556                                                       NULL,
557                                                       G_PARAM_READWRITE));
558 }
559
560 static void
561 gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
562 {
563   iface->select_path = gtk_file_chooser_default_select_path;
564   iface->unselect_path = gtk_file_chooser_default_unselect_path;
565   iface->select_all = gtk_file_chooser_default_select_all;
566   iface->unselect_all = gtk_file_chooser_default_unselect_all;
567   iface->get_paths = gtk_file_chooser_default_get_paths;
568   iface->get_preview_path = gtk_file_chooser_default_get_preview_path;
569   iface->get_file_system = gtk_file_chooser_default_get_file_system;
570   iface->set_current_folder = gtk_file_chooser_default_set_current_folder;
571   iface->get_current_folder = gtk_file_chooser_default_get_current_folder;
572   iface->set_current_name = gtk_file_chooser_default_set_current_name;
573   iface->add_filter = gtk_file_chooser_default_add_filter;
574   iface->remove_filter = gtk_file_chooser_default_remove_filter;
575   iface->list_filters = gtk_file_chooser_default_list_filters;
576   iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder;
577   iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder;
578   iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders;
579 }
580
581 static void
582 gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
583 {
584   iface->get_default_size = gtk_file_chooser_default_get_default_size;
585   iface->get_resizable_hints = gtk_file_chooser_default_get_resizable_hints;
586   iface->should_respond = gtk_file_chooser_default_should_respond;
587   iface->initial_focus = gtk_file_chooser_default_initial_focus;
588 }
589 static void
590 gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
591 {
592   impl->local_only = TRUE;
593   impl->preview_widget_active = TRUE;
594   impl->use_preview_label = TRUE;
595   impl->select_multiple = FALSE;
596   impl->show_hidden = FALSE;
597   impl->icon_size = FALLBACK_ICON_SIZE;
598
599   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (impl), TRUE);
600   gtk_box_set_spacing (GTK_BOX (impl), 12);
601 }
602
603 static void
604 gtk_file_chooser_default_finalize (GObject *object)
605 {
606   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
607   GSList *l;
608
609   g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
610   impl->volumes_changed_id = 0;
611   g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
612   impl->bookmarks_changed_id = 0;
613   g_object_unref (impl->file_system);
614
615   for (l = impl->filters; l; l = l->next)
616     {
617       GtkFileFilter *filter;
618
619       filter = GTK_FILE_FILTER (l->data);
620       g_object_unref (filter);
621     }
622   g_slist_free (impl->filters);
623
624   if (impl->current_filter)
625     g_object_unref (impl->current_filter);
626
627   if (impl->current_volume_path)
628     gtk_file_path_free (impl->current_volume_path);
629
630   if (impl->current_folder)
631     gtk_file_path_free (impl->current_folder);
632
633   if (impl->preview_path)
634     gtk_file_path_free (impl->preview_path);
635
636   /* Free all the Models we have */
637   if (impl->browse_files_model)
638     g_object_unref (impl->browse_files_model);
639
640   if (impl->shortcuts_model)
641     g_object_unref (impl->shortcuts_model);
642
643   if (impl->shortcuts_filter_model)
644     g_object_unref (impl->shortcuts_filter_model);
645
646   if (impl->sort_model)
647     g_object_unref (impl->sort_model);
648
649   g_free (impl->preview_display_name);
650
651   G_OBJECT_CLASS (parent_class)->finalize (object);
652 }
653
654 /* Shows an error dialog set as transient for the specified window */
655 static void
656 error_message_with_parent (GtkWindow  *parent,
657                            const char *msg)
658 {
659   GtkWidget *dialog;
660
661   dialog = gtk_message_dialog_new (parent,
662                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
663                                    GTK_MESSAGE_ERROR,
664                                    GTK_BUTTONS_OK,
665                                    "%s",
666                                    msg);
667   gtk_dialog_run (GTK_DIALOG (dialog));
668   gtk_widget_destroy (dialog);
669 }
670
671 /* Returns a toplevel GtkWindow, or NULL if none */
672 static GtkWindow *
673 get_toplevel (GtkWidget *widget)
674 {
675   GtkWidget *toplevel;
676
677   toplevel = gtk_widget_get_toplevel (widget);
678   if (!GTK_WIDGET_TOPLEVEL (toplevel))
679     return NULL;
680   else
681     return GTK_WINDOW (toplevel);
682 }
683
684 /* Shows an error dialog for the file chooser */
685 static void
686 error_message (GtkFileChooserDefault *impl,
687                const char            *msg)
688 {
689   error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg);
690 }
691
692 /* Shows a simple error dialog relative to a path.  Frees the GError as well. */
693 static void
694 error_dialog (GtkFileChooserDefault *impl,
695               const char            *msg,
696               const GtkFilePath     *path,
697               GError                *error)
698 {
699   g_return_if_fail (path != NULL);
700
701   if (error)
702     {
703       char *uri = gtk_file_system_path_to_uri (impl->file_system, path);
704       char *text = g_strdup_printf (msg,
705                                     uri,
706                                     error->message);
707       error_message (impl, text);
708       g_free (text);
709       g_free (uri);
710       g_error_free (error);
711     }
712 }
713
714 /* Displays an error message about not being able to get information for a file.
715  * Frees the GError as well.
716  */
717 static void
718 error_getting_info_dialog (GtkFileChooserDefault *impl,
719                            const GtkFilePath     *path,
720                            GError                *error)
721 {
722   error_dialog (impl,
723                 _("Could not retrieve information about %s:\n%s"),
724                 path, error);
725 }
726
727 /* Shows an error dialog about not being able to add a bookmark */
728 static void
729 error_could_not_add_bookmark_dialog (GtkFileChooserDefault *impl,
730                                      const GtkFilePath     *path,
731                                      GError                *error)
732 {
733   error_dialog (impl,
734                 _("Could not add a bookmark for %s:\n%s"),
735                 path, error);
736 }
737
738 /* Shows an error dialog about not being able to compose a filename */
739 static void
740 error_building_filename_dialog (GtkFileChooserDefault *impl,
741                                 const GtkFilePath     *base_path,
742                                 const char            *file_part,
743                                 GError                *error)
744 {
745   char *uri;
746   char *msg;
747
748   uri = gtk_file_system_path_to_uri (impl->file_system, base_path);
749   msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
750                          uri, file_part,
751                          error->message);
752   error_message (impl, msg);
753   g_free (uri);
754   g_free (msg);
755   g_error_free (error);
756 }
757
758 /* Shows an error dialog when we cannot switch to a folder */
759 static void
760 error_changing_folder_dialog (GtkFileChooserDefault *impl,
761                               const GtkFilePath     *path,
762                               GError                *error)
763 {
764   error_dialog (impl,
765                 _("Could not change the current folder to %s:\n%s"),
766                 path,
767                 error);
768 }
769
770 /* Changes folders, displaying an error dialog if this fails */
771 static gboolean
772 change_folder_and_display_error (GtkFileChooserDefault *impl,
773                                  const GtkFilePath     *path)
774 {
775   GError *error;
776   gboolean result;
777
778   error = NULL;
779   result = _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), path, &error);
780
781   if (!result)
782     error_changing_folder_dialog (impl, path, error);
783
784   return result;
785 }
786
787 static void
788 update_preview_widget_visibility (GtkFileChooserDefault *impl)
789 {
790   if (impl->use_preview_label)
791     {
792       if (!impl->preview_label)
793         {
794           impl->preview_label = gtk_label_new (impl->preview_display_name);
795           gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_label, FALSE, FALSE, 0);
796           gtk_box_reorder_child (GTK_BOX (impl->preview_box), impl->preview_label, 0);
797           gtk_widget_show (impl->preview_label);
798         }
799     }
800   else
801     {
802       if (impl->preview_label)
803         {
804           gtk_widget_destroy (impl->preview_label);
805           impl->preview_label = NULL;
806         }
807     }
808
809   if (impl->preview_widget_active && impl->preview_widget)
810     gtk_widget_show (impl->preview_box);
811   else
812     gtk_widget_hide (impl->preview_box);
813
814   g_signal_emit_by_name (impl, "default-size-changed");
815 }
816
817 static void
818 set_preview_widget (GtkFileChooserDefault *impl,
819                     GtkWidget             *preview_widget)
820 {
821   if (preview_widget == impl->preview_widget)
822     return;
823
824   if (impl->preview_widget)
825     gtk_container_remove (GTK_CONTAINER (impl->preview_box),
826                           impl->preview_widget);
827
828   impl->preview_widget = preview_widget;
829   if (impl->preview_widget)
830     {
831       gtk_widget_show (impl->preview_widget);
832       gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_widget, TRUE, TRUE, 0);
833       gtk_box_reorder_child (GTK_BOX (impl->preview_box),
834                              impl->preview_widget,
835                              (impl->use_preview_label && impl->preview_label) ? 1 : 0);
836     }
837
838   update_preview_widget_visibility (impl);
839 }
840
841 /* Re-reads all the icons for the shortcuts, used when the theme changes */
842 static void
843 shortcuts_reload_icons (GtkFileChooserDefault *impl)
844 {
845   GtkTreeIter iter;
846   int i;
847   int bookmarks_separator_idx;
848   int current_folder_separator_idx;
849   int volumes_idx;
850
851   if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
852     return;
853
854   bookmarks_separator_idx = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
855   current_folder_separator_idx = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
856   volumes_idx = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
857
858   i = 0;
859
860   do {
861     gpointer data;
862     gboolean pixbuf_visible;
863     GdkPixbuf *pixbuf;
864
865     gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
866                         SHORTCUTS_COL_PATH, &data,
867                         SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible,
868                         -1);
869
870     if (!pixbuf_visible || !data)
871       goto next_iter;
872
873     if (i >= volumes_idx && i < volumes_idx + impl->num_volumes)
874       {
875         GtkFileSystemVolume *volume;
876
877         volume = data;
878         pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl),
879                                                      impl->icon_size, NULL);
880       }
881     else
882       {
883         const GtkFilePath *path;
884
885         path = data;
886         pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
887                                               impl->icon_size, NULL);
888       }
889
890     gtk_list_store_set (impl->shortcuts_model, &iter,
891                         SHORTCUTS_COL_PIXBUF, pixbuf,
892                         -1);
893
894   next_iter:
895     i++;
896   } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
897 }
898
899 /* If a shortcut corresponds to the current folder, selects it */
900 static void
901 shortcuts_find_current_folder (GtkFileChooserDefault *impl)
902 {
903   GtkTreeSelection *selection;
904   int pos;
905   GtkTreePath *path;
906
907   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
908
909   pos = shortcut_find_position (impl, impl->current_folder);
910   if (pos == -1)
911     {
912       gtk_tree_selection_unselect_all (selection);
913       return;
914     }
915
916   path = gtk_tree_path_new_from_indices (pos, -1);
917   gtk_tree_selection_select_path (selection, path);
918   gtk_tree_path_free (path);
919 }
920
921 /* Returns whether a path is a folder */
922 static gboolean
923 check_is_folder (GtkFileSystem *file_system, const GtkFilePath *path, GError **error)
924 {
925   GtkFileFolder *folder;
926
927   folder = gtk_file_system_get_folder (file_system, path,
928                                        GTK_FILE_INFO_DISPLAY_NAME,
929                                        error);
930   if (!folder)
931     return FALSE;
932
933   g_object_unref (folder);
934   return TRUE;
935 }
936
937 /* Convenience function to get the display name and icon info for a path */
938 static GtkFileInfo *
939 get_file_info (GtkFileSystem *file_system, const GtkFilePath *path, gboolean name_only, GError **error)
940 {
941   GtkFilePath *parent_path;
942   GtkFileFolder *parent_folder;
943   GtkFileInfo *info;
944
945   info = NULL;
946
947   if (!gtk_file_system_get_parent (file_system, path, &parent_path, error))
948     return NULL;
949
950   parent_folder = gtk_file_system_get_folder (file_system, parent_path ? parent_path : path,
951                                               GTK_FILE_INFO_DISPLAY_NAME
952                                               | (name_only ? 0 : GTK_FILE_INFO_IS_FOLDER),
953                                               error);
954   if (!parent_folder)
955     goto out;
956
957   info = gtk_file_folder_get_info (parent_folder, parent_path ? path : NULL, error);
958   g_object_unref (parent_folder);
959
960  out:
961
962   gtk_file_path_free (parent_path);
963   return info;
964 }
965
966 /* Inserts a path in the shortcuts tree, making a copy of it; alternatively,
967  * inserts a volume.  A position of -1 indicates the end of the tree.
968  */
969 static gboolean
970 shortcuts_insert_path (GtkFileChooserDefault *impl,
971                        int                    pos,
972                        gboolean               is_volume,
973                        GtkFileSystemVolume   *volume,
974                        const GtkFilePath     *path,
975                        const char            *label,
976                        gboolean               removable,
977                        GError               **error)
978 {
979   char *label_copy;
980   GdkPixbuf *pixbuf;
981   gpointer data;
982   GtkTreeIter iter;
983
984   if (is_volume)
985     {
986       data = volume;
987       label_copy = gtk_file_system_volume_get_display_name (impl->file_system, volume);
988       pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl),
989                                                    impl->icon_size, NULL);
990     }
991   else
992     {
993       if (!check_is_folder (impl->file_system, path, error))
994         return FALSE;
995
996       if (label)
997         label_copy = g_strdup (label);
998       else
999         {
1000           GtkFileInfo *info = get_file_info (impl->file_system, path, TRUE, error);
1001
1002           if (!info)
1003             return FALSE;
1004
1005           label_copy = g_strdup (gtk_file_info_get_display_name (info));
1006           gtk_file_info_free (info);
1007         }
1008
1009       data = gtk_file_path_copy (path);
1010       pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
1011                                             impl->icon_size, NULL);
1012     }
1013
1014   if (pos == -1)
1015     gtk_list_store_append (impl->shortcuts_model, &iter);
1016   else
1017     gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
1018
1019   gtk_list_store_set (impl->shortcuts_model, &iter,
1020                       SHORTCUTS_COL_PIXBUF, pixbuf,
1021                       SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1022                       SHORTCUTS_COL_NAME, label_copy,
1023                       SHORTCUTS_COL_PATH, data,
1024                       SHORTCUTS_COL_REMOVABLE, removable,
1025                       -1);
1026
1027   g_free (label_copy);
1028
1029   if (pixbuf)
1030     g_object_unref (pixbuf);
1031
1032   return TRUE;
1033 }
1034
1035 /* Appends an item for the user's home directory to the shortcuts model */
1036 static void
1037 shortcuts_append_home (GtkFileChooserDefault *impl)
1038 {
1039   const char *home;
1040   GtkFilePath *home_path;
1041   GError *error;
1042
1043   home = g_get_home_dir ();
1044   home_path = gtk_file_system_filename_to_path (impl->file_system, home);
1045
1046   error = NULL;
1047   impl->has_home = shortcuts_insert_path (impl, -1, FALSE, NULL, home_path, _("Home"), FALSE, &error);
1048   if (!impl->has_home)
1049     error_getting_info_dialog (impl, home_path, error);
1050
1051   gtk_file_path_free (home_path);
1052 }
1053
1054 /* Appends the ~/Desktop directory to the shortcuts model */
1055 static void
1056 shortcuts_append_desktop (GtkFileChooserDefault *impl)
1057 {
1058   char *name;
1059   GtkFilePath *path;
1060
1061   name = g_build_filename (g_get_home_dir (), "Desktop", NULL);
1062   path = gtk_file_system_filename_to_path (impl->file_system, name);
1063   g_free (name);
1064
1065   impl->has_desktop = shortcuts_insert_path (impl, -1, FALSE, NULL, path, _("Desktop"), FALSE, NULL);
1066   /* We do not actually pop up an error dialog if there is no desktop directory
1067    * because some people may really not want to have one.
1068    */
1069
1070   gtk_file_path_free (path);
1071 }
1072
1073 /* Appends a list of GtkFilePath to the shortcuts model; returns how many were inserted */
1074 static int
1075 shortcuts_append_paths (GtkFileChooserDefault *impl,
1076                         GSList                *paths)
1077 {
1078   int start_row;
1079   int num_inserted;
1080
1081   /* As there is no separator now, we want to start there.
1082    */
1083   start_row = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
1084   num_inserted = 0;
1085
1086   for (; paths; paths = paths->next)
1087     {
1088       GtkFilePath *path;
1089       GError *error;
1090
1091       path = paths->data;
1092       error = NULL;
1093
1094       if (impl->local_only &&
1095           !gtk_file_system_path_is_local (impl->file_system, path))
1096         continue;
1097
1098       /* NULL GError, but we don't really want to show error boxes here */
1099       if (shortcuts_insert_path (impl, start_row + num_inserted, FALSE, NULL, path, NULL, TRUE, NULL))
1100         num_inserted++;
1101     }
1102
1103   return num_inserted;
1104 }
1105
1106 /* Returns the index for the corresponding item in the shortcuts bar */
1107 static int
1108 shortcuts_get_index (GtkFileChooserDefault *impl,
1109                      ShortcutsIndex         where)
1110 {
1111   int n;
1112
1113   n = 0;
1114
1115   if (where == SHORTCUTS_HOME)
1116     goto out;
1117
1118   n += impl->has_home ? 1 : 0;
1119
1120   if (where == SHORTCUTS_DESKTOP)
1121     goto out;
1122
1123   n += impl->has_desktop ? 1 : 0;
1124
1125   if (where == SHORTCUTS_VOLUMES)
1126     goto out;
1127
1128   n += impl->num_volumes;
1129
1130   if (where == SHORTCUTS_SHORTCUTS)
1131     goto out;
1132
1133   n += impl->num_shortcuts;
1134
1135   if (where == SHORTCUTS_BOOKMARKS_SEPARATOR)
1136     goto out;
1137
1138   /* If there are no bookmarks there won't be a separator */
1139   n += (impl->num_bookmarks > 0) ? 1 : 0;
1140
1141   if (where == SHORTCUTS_BOOKMARKS)
1142     goto out;
1143
1144   n += impl->num_bookmarks;
1145
1146   if (where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR)
1147     goto out;
1148
1149   n += 1;
1150
1151   if (where == SHORTCUTS_CURRENT_FOLDER)
1152     goto out;
1153
1154   g_assert_not_reached ();
1155
1156  out:
1157
1158   return n;
1159 }
1160
1161 typedef void (* RemoveFunc) (GtkFileChooserDefault *impl, gpointer data);
1162
1163 /* Removes the specified number of rows from the shortcuts list */
1164 static void
1165 shortcuts_remove_rows (GtkFileChooserDefault *impl,
1166                        int start_row,
1167                        int n_rows,
1168                        RemoveFunc remove_fn)
1169 {
1170   GtkTreePath *path;
1171
1172   path = gtk_tree_path_new_from_indices (start_row, -1);
1173
1174   for (; n_rows; n_rows--)
1175     {
1176       GtkTreeIter iter;
1177       gpointer data;
1178
1179       if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
1180         g_assert_not_reached ();
1181
1182       if (remove_fn)
1183         {
1184           gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &data, -1);
1185           (* remove_fn) (impl, data);
1186         }
1187
1188       gtk_list_store_remove (impl->shortcuts_model, &iter);
1189     }
1190
1191   gtk_tree_path_free (path);
1192 }
1193
1194 /* Used from shortcuts_remove_rows() in shortcuts_add_volumes() */
1195 static void
1196 volume_remove_cb (GtkFileChooserDefault *impl, gpointer data)
1197 {
1198   GtkFileSystemVolume *volume;
1199
1200   volume = data;
1201   gtk_file_system_volume_free (impl->file_system, volume);
1202 }
1203
1204 /* Adds all the file system volumes to the shortcuts model */
1205 static void
1206 shortcuts_add_volumes (GtkFileChooserDefault *impl)
1207 {
1208   int start_row;
1209   GSList *list, *l;
1210   int n;
1211   gboolean old_changing_folders;
1212
1213   old_changing_folders = impl->changing_folder;
1214   impl->changing_folder = TRUE;
1215
1216   start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
1217   shortcuts_remove_rows (impl, start_row, impl->num_volumes, volume_remove_cb);
1218   impl->num_volumes = 0;
1219
1220   list = gtk_file_system_list_volumes (impl->file_system);
1221
1222   n = 0;
1223
1224   for (l = list; l; l = l->next)
1225     {
1226       GtkFileSystemVolume *volume;
1227
1228       volume = l->data;
1229
1230       if (impl->local_only)
1231         {
1232           GtkFilePath *base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
1233           gboolean is_local = gtk_file_system_path_is_local (impl->file_system, base_path);
1234           gtk_file_path_free (base_path);
1235
1236           if (!is_local)
1237             {
1238               gtk_file_system_volume_free (impl->file_system, volume);
1239               continue;
1240             }
1241         }
1242
1243       if (shortcuts_insert_path (impl, start_row + n, TRUE, volume, NULL, NULL, FALSE, NULL))
1244         n++;
1245       else
1246         gtk_file_system_volume_free (impl->file_system, volume);
1247     }
1248
1249   impl->num_volumes = n;
1250   g_slist_free (list);
1251
1252   if (impl->shortcuts_filter_model)
1253     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
1254
1255   impl->changing_folder = old_changing_folders;
1256 }
1257
1258 /* Used from shortcuts_remove_rows() */
1259 static void
1260 remove_bookmark_cb (GtkFileChooserDefault *impl, gpointer data)
1261 {
1262   GtkFilePath *path;
1263
1264   path = data;
1265   gtk_file_path_free (path);
1266 }
1267
1268 /* Inserts a separator node in the shortcuts list */
1269 static void
1270 shortcuts_insert_separator (GtkFileChooserDefault *impl,
1271                             ShortcutsIndex where)
1272 {
1273   GtkTreeIter iter;
1274
1275   g_assert (where == SHORTCUTS_BOOKMARKS_SEPARATOR || where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1276
1277   gtk_list_store_insert (impl->shortcuts_model, &iter,
1278                          shortcuts_get_index (impl, where));
1279   gtk_list_store_set (impl->shortcuts_model, &iter,
1280                       SHORTCUTS_COL_PIXBUF, NULL,
1281                       SHORTCUTS_COL_PIXBUF_VISIBLE, FALSE,
1282                       SHORTCUTS_COL_NAME, NULL,
1283                       SHORTCUTS_COL_PATH, NULL,
1284                       -1);
1285 }
1286
1287 /* Updates the list of bookmarks */
1288 static void
1289 shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
1290 {
1291   GSList *bookmarks;
1292   gboolean old_changing_folders;
1293
1294   old_changing_folders = impl->changing_folder;
1295   impl->changing_folder = TRUE;
1296
1297   if (impl->num_bookmarks > 0)
1298     {
1299       shortcuts_remove_rows (impl,
1300                              shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR),
1301                              impl->num_bookmarks + 1,
1302                              remove_bookmark_cb);
1303
1304     }
1305
1306   bookmarks = gtk_file_system_list_bookmarks (impl->file_system);
1307   impl->num_bookmarks = shortcuts_append_paths (impl, bookmarks);
1308   gtk_file_paths_free (bookmarks);
1309
1310   if (impl->num_bookmarks > 0)
1311     {
1312       shortcuts_insert_separator (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
1313     }
1314   if (impl->shortcuts_filter_model)
1315     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
1316
1317   impl->changing_folder = old_changing_folders;
1318 }
1319
1320 /* Appends a separator and a row to the shortcuts list for the current folder */
1321 static void
1322 shortcuts_add_current_folder (GtkFileChooserDefault *impl)
1323 {
1324   int pos;
1325   gboolean success;
1326
1327   g_assert (!impl->shortcuts_current_folder_active);
1328
1329   success = TRUE;
1330
1331   pos = shortcut_find_position (impl, impl->current_folder);
1332   if (pos == -1)
1333     {
1334       GtkFileSystemVolume *volume;
1335       GtkFilePath *base_path;
1336
1337       /* Separator */
1338
1339       shortcuts_insert_separator (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1340
1341       /* Item */
1342
1343       pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER);
1344
1345       volume = gtk_file_system_get_volume_for_path (impl->file_system, impl->current_folder);
1346       if (volume)
1347         base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
1348       else
1349         base_path = NULL;
1350
1351       if (base_path &&
1352           strcmp (gtk_file_path_get_string (base_path), gtk_file_path_get_string (impl->current_folder)) == 0)
1353         {
1354           success = shortcuts_insert_path (impl, pos, TRUE, volume, NULL, NULL, FALSE, NULL);
1355           impl->shortcuts_current_folder_is_volume = TRUE;
1356         }
1357       else
1358         {
1359           success = shortcuts_insert_path (impl, pos, FALSE, NULL, impl->current_folder, NULL, FALSE, NULL);
1360           impl->shortcuts_current_folder_is_volume = FALSE;
1361         }
1362
1363       if (volume)
1364         gtk_file_system_volume_free (impl->file_system, volume);
1365       gtk_file_path_free (base_path);
1366
1367       if (!success)
1368         shortcuts_remove_rows (impl, pos - 1, 1, NULL); /* remove the separator */
1369
1370       impl->shortcuts_current_folder_active = success;
1371     }
1372
1373   if (success)
1374     gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), pos);
1375 }
1376
1377 /* Used from shortcuts_remove_rows() in shortcuts_update_current_folder() */
1378 static void
1379 remove_current_folder_cb (GtkFileChooserDefault *impl,
1380                           gpointer               data)
1381 {
1382   if (impl->shortcuts_current_folder_is_volume)
1383     gtk_file_system_volume_free (impl->file_system, data);
1384   else
1385     gtk_file_path_free (data);
1386 }
1387
1388 /* Updates the current folder row in the shortcuts model */
1389 static void
1390 shortcuts_update_current_folder (GtkFileChooserDefault *impl)
1391 {
1392   int pos;
1393
1394   pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1395
1396   if (impl->shortcuts_current_folder_active)
1397     {
1398       shortcuts_remove_rows (impl, pos, 2, remove_current_folder_cb);
1399       impl->shortcuts_current_folder_active = FALSE;
1400     }
1401
1402   shortcuts_add_current_folder (impl);
1403 }
1404
1405 /* Filter function used for the shortcuts filter model */
1406 static gboolean
1407 shortcuts_filter_cb (GtkTreeModel          *model,
1408                      GtkTreeIter           *iter,
1409                      gpointer               data)
1410 {
1411   GtkFileChooserDefault *impl;
1412   GtkTreePath *path;
1413   int pos;
1414
1415   impl = GTK_FILE_CHOOSER_DEFAULT (data);
1416
1417   path = gtk_tree_model_get_path (model, iter);
1418   if (!path)
1419     return FALSE;
1420
1421   pos = *gtk_tree_path_get_indices (path);
1422   gtk_tree_path_free (path);
1423
1424   return (pos < shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR));
1425 }
1426
1427 /* Creates the list model for shortcuts */
1428 static void
1429 shortcuts_model_create (GtkFileChooserDefault *impl)
1430 {
1431   /* Keep this order in sync with the SHORCUTS_COL_* enum values */
1432   impl->shortcuts_model = gtk_list_store_new (SHORTCUTS_COL_NUM_COLUMNS,
1433                                               GDK_TYPE_PIXBUF,  /* pixbuf */
1434                                               G_TYPE_STRING,    /* name */
1435                                               G_TYPE_POINTER,   /* path or volume */
1436                                               G_TYPE_BOOLEAN,   /* removable */
1437                                               G_TYPE_BOOLEAN);  /* pixbuf cell visibility */
1438
1439   if (impl->file_system)
1440     {
1441       shortcuts_append_home (impl);
1442       shortcuts_append_desktop (impl);
1443       shortcuts_add_volumes (impl);
1444       shortcuts_add_bookmarks (impl);
1445     }
1446
1447   impl->shortcuts_filter_model = shortcuts_model_filter_new (impl,
1448                                                              GTK_TREE_MODEL (impl->shortcuts_model),
1449                                                              NULL);
1450
1451   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
1452                                           shortcuts_filter_cb,
1453                                           impl,
1454                                           NULL);
1455 }
1456
1457 /* Callback used when the "New Folder" toolbar button is clicked */
1458 static void
1459 new_folder_button_clicked (GtkButton             *button,
1460                            GtkFileChooserDefault *impl)
1461 {
1462   GtkTreeIter iter;
1463   GtkTreePath *path;
1464
1465   _gtk_file_system_model_add_editable (impl->browse_files_model, &iter);
1466
1467   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->browse_files_model), &iter);
1468   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_files_tree_view),
1469                                 path, impl->list_name_column,
1470                                 FALSE, 0.0, 0.0);
1471
1472   g_object_set (impl->list_name_renderer, "editable", TRUE, NULL);
1473   gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view),
1474                             path,
1475                             impl->list_name_column,
1476                             TRUE);
1477 }
1478
1479 /* Callback used from the text cell renderer when the new folder is named */
1480 static void
1481 renderer_edited_cb (GtkCellRendererText   *cell_renderer_text,
1482                     const gchar           *path,
1483                     const gchar           *new_text,
1484                     GtkFileChooserDefault *impl)
1485 {
1486   GError *error;
1487   GtkFilePath *file_path;
1488
1489   _gtk_file_system_model_remove_editable (impl->browse_files_model);
1490   g_object_set (impl->list_name_renderer, "editable", FALSE, NULL);
1491
1492   error = NULL;
1493   file_path = gtk_file_system_make_path (impl->file_system, impl->current_folder, new_text, &error);
1494   if (!file_path)
1495     {
1496       error_building_filename_dialog (impl, impl->current_folder, new_text, error);
1497       return;
1498     }
1499
1500   error = NULL;
1501   if (!gtk_file_system_create_folder (impl->file_system, file_path, &error))
1502     {
1503       error_dialog (impl,
1504                     _("Could not create folder %s:\n%s"),
1505                     file_path, error);
1506     }
1507
1508   gtk_file_path_free (file_path);
1509
1510   /* FIXME: scroll to the new folder and select it */
1511 }
1512
1513 /* Callback used from the text cell renderer when the new folder edition gets
1514  * canceled.
1515  */
1516 static void
1517 renderer_editing_canceled_cb (GtkCellRendererText   *cell_renderer_text,
1518                               GtkFileChooserDefault *impl)
1519 {
1520   _gtk_file_system_model_remove_editable (impl->browse_files_model);
1521   g_object_set (impl->list_name_renderer, "editable", FALSE, NULL);
1522 }
1523
1524 /* Creates the widgets for the filter combo box */
1525 static GtkWidget *
1526 filter_create (GtkFileChooserDefault *impl)
1527 {
1528   impl->filter_combo = gtk_combo_box_new_text ();
1529   g_signal_connect (impl->filter_combo, "changed",
1530                     G_CALLBACK (filter_combo_changed), impl);
1531
1532   return impl->filter_combo;
1533 }
1534
1535 static GtkWidget *
1536 button_new (GtkFileChooserDefault *impl,
1537             const char *text,
1538             const char *stock_id,
1539             gboolean    sensitive,
1540             gboolean    show,
1541             GCallback   callback)
1542 {
1543   GtkWidget *button;
1544   GtkWidget *hbox;
1545   GtkWidget *widget;
1546   GtkWidget *align;
1547
1548   button = gtk_button_new ();
1549   hbox = gtk_hbox_new (FALSE, 2);
1550   align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1551
1552   gtk_container_add (GTK_CONTAINER (button), align);
1553   gtk_container_add (GTK_CONTAINER (align), hbox);
1554   widget = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
1555
1556   gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1557
1558   widget = gtk_label_new_with_mnemonic (text);
1559   gtk_label_set_mnemonic_widget (GTK_LABEL (widget), GTK_WIDGET (button));
1560   gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1561
1562   gtk_widget_set_sensitive (button, sensitive);
1563   g_signal_connect (button, "clicked", callback, impl);
1564
1565   gtk_widget_show_all (align);
1566
1567   if (show)
1568     gtk_widget_show (button);
1569
1570   return button;
1571 }
1572
1573 /* Looks for a path among the shortcuts; returns its index or -1 if it doesn't exist */
1574 static int
1575 shortcut_find_position (GtkFileChooserDefault *impl,
1576                         const GtkFilePath     *path)
1577 {
1578   GtkTreeIter iter;
1579   int i;
1580   int bookmarks_separator_idx;
1581   int current_folder_separator_idx;
1582   int volumes_idx;
1583
1584   if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
1585     return -1;
1586
1587   bookmarks_separator_idx = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
1588   current_folder_separator_idx = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1589   volumes_idx = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
1590
1591   i = 0;
1592
1593   for (i = 0; i < current_folder_separator_idx; i++)
1594     {
1595       gpointer data;
1596
1597       if (i == bookmarks_separator_idx)
1598         goto next_iter;
1599
1600       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &data, -1);
1601
1602       if (i >= volumes_idx && i < volumes_idx + impl->num_volumes)
1603         {
1604           GtkFileSystemVolume *volume;
1605           GtkFilePath *base_path;
1606           gboolean exists;
1607
1608           volume = data;
1609           base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
1610
1611           exists = strcmp (gtk_file_path_get_string (path),
1612                            gtk_file_path_get_string (base_path)) == 0;
1613           g_free (base_path);
1614
1615           if (exists)
1616             return i;
1617         }
1618       else
1619         {
1620           GtkFilePath *model_path;
1621
1622           model_path = data;
1623
1624           if (model_path && gtk_file_path_compare (model_path, path) == 0)
1625             return i;
1626         }
1627
1628     next_iter:
1629       gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
1630     }
1631
1632   return -1;
1633 }
1634
1635 /* Tries to add a bookmark from a path name */
1636 static gboolean
1637 shortcuts_add_bookmark_from_path (GtkFileChooserDefault *impl,
1638                                   const GtkFilePath     *path,
1639                                   int                    pos)
1640 {
1641   GError *error;
1642
1643   if (shortcut_find_position (impl, path) != -1)
1644     return FALSE;
1645
1646   /* FIXME: this check really belongs in gtk_file_system_insert_bookmark.  */
1647   error = NULL;
1648   if (!check_is_folder (impl->file_system, path, &error))
1649     {
1650       error_dialog (impl,
1651                     _("Could not add bookmark for %s because it is not a folder."),
1652                     path,
1653                     error);
1654       return FALSE;
1655     }
1656
1657   error = NULL;
1658   if (!gtk_file_system_insert_bookmark (impl->file_system, path, pos, &error))
1659     {
1660       error_could_not_add_bookmark_dialog (impl, path, error);
1661       return FALSE;
1662     }
1663
1664   return TRUE;
1665 }
1666
1667 static void
1668 add_bookmark_foreach_cb (GtkTreeModel *model,
1669                          GtkTreePath  *path,
1670                          GtkTreeIter  *iter,
1671                          gpointer      data)
1672 {
1673   GtkFileChooserDefault *impl;
1674   GtkFileSystemModel *fs_model;
1675   GtkTreeIter child_iter;
1676   const GtkFilePath *file_path;
1677
1678   impl = (GtkFileChooserDefault *) data;
1679
1680   fs_model = impl->browse_files_model;
1681   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter);
1682
1683   file_path = _gtk_file_system_model_get_path (fs_model, &child_iter);
1684   shortcuts_add_bookmark_from_path (impl, file_path, -1);
1685 }
1686
1687 /* Callback used when the "Add bookmark" button is clicked */
1688 static void
1689 add_bookmark_button_clicked_cb (GtkButton *button,
1690                                 GtkFileChooserDefault *impl)
1691 {
1692   GtkTreeSelection *selection;
1693
1694   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
1695
1696   if (gtk_tree_selection_count_selected_rows (selection) == 0)
1697     shortcuts_add_bookmark_from_path (impl, impl->current_folder, -1);
1698   else
1699     gtk_tree_selection_selected_foreach (selection,
1700                                          add_bookmark_foreach_cb,
1701                                          impl);
1702 }
1703
1704 /* Callback used when the "Remove bookmark" button is clicked */
1705 static void
1706 remove_bookmark_button_clicked_cb (GtkButton *button,
1707                                    GtkFileChooserDefault *impl)
1708 {
1709   GtkTreeSelection *selection;
1710   GtkTreeIter iter;
1711   GtkFilePath *path;
1712   gboolean removable;
1713   GError *error;
1714
1715   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1716
1717   if (gtk_tree_selection_get_selected (selection, NULL, &iter))
1718     {
1719       gtk_tree_model_get (impl->shortcuts_filter_model, &iter,
1720                           SHORTCUTS_COL_PATH, &path,
1721                           SHORTCUTS_COL_REMOVABLE, &removable, -1);
1722       if (!removable)
1723         {
1724           g_assert_not_reached ();
1725           return;
1726         }
1727
1728       error = NULL;
1729       if (!gtk_file_system_remove_bookmark (impl->file_system, path, &error))
1730         {
1731           error_dialog (impl,
1732                         _("Could not remove bookmark for %s:\n%s"),
1733                         path,
1734                         error);
1735         }
1736     }
1737 }
1738
1739 struct selection_check_closure {
1740   GtkFileChooserDefault *impl;
1741   gboolean empty;
1742   gboolean all_files;
1743   gboolean all_folders;
1744 };
1745
1746 /* Used from gtk_tree_selection_selected_foreach() */
1747 static void
1748 selection_check_foreach_cb (GtkTreeModel *model,
1749                             GtkTreePath  *path,
1750                             GtkTreeIter  *iter,
1751                             gpointer      data)
1752 {
1753   struct selection_check_closure *closure;
1754   GtkTreeIter child_iter;
1755   const GtkFileInfo *info;
1756   gboolean is_folder;
1757
1758   closure = data;
1759   closure->empty = FALSE;
1760
1761   gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
1762
1763   info = _gtk_file_system_model_get_info (closure->impl->browse_files_model, &child_iter);
1764   is_folder = gtk_file_info_get_is_folder (info);
1765
1766   closure->all_folders &= is_folder;
1767   closure->all_files &= !is_folder;
1768 }
1769
1770 /* Checks whether the selected items in the file list are all files or all folders */
1771 static void
1772 selection_check (GtkFileChooserDefault *impl,
1773                  gboolean              *empty,
1774                  gboolean              *all_files,
1775                  gboolean              *all_folders)
1776 {
1777   struct selection_check_closure closure;
1778   GtkTreeSelection *selection;
1779
1780   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
1781
1782   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
1783       || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
1784     {
1785       if (gtk_tree_selection_count_selected_rows (selection) == 0)
1786         closure.empty = TRUE;
1787       else
1788         {
1789           closure.empty = FALSE;
1790           closure.all_files = FALSE;
1791           closure.all_folders = TRUE;
1792         }
1793     }
1794   else
1795     {
1796       g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
1797                 || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE);
1798
1799       closure.impl = impl;
1800       closure.empty = TRUE;
1801       closure.all_files = TRUE;
1802       closure.all_folders = TRUE;
1803
1804       gtk_tree_selection_selected_foreach (selection,
1805                                            selection_check_foreach_cb,
1806                                            &closure);
1807     }
1808
1809   g_assert (closure.empty || !(closure.all_files && closure.all_folders));
1810
1811   if (empty)
1812     *empty = closure.empty;
1813
1814   if (all_files)
1815     *all_files = closure.all_files;
1816
1817   if (all_folders)
1818     *all_folders = closure.all_folders;
1819 }
1820
1821 /* Sensitize the "add bookmark" button if all the selected items are folders, or
1822  * if there are no selected items *and* the current folder is not in the
1823  * bookmarks list.  De-sensitize the button otherwise.
1824  */
1825 static void
1826 bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl)
1827 {
1828   GtkTreeSelection *selection;
1829   gboolean active;
1830
1831   /* Check selection */
1832
1833   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
1834
1835   if (gtk_tree_selection_count_selected_rows (selection) == 0)
1836     active = (shortcut_find_position (impl, impl->current_folder) == -1);
1837   else
1838     {
1839       gboolean all_folders;
1840
1841       selection_check (impl, NULL, NULL, &all_folders);
1842       active = (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
1843                 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER ||
1844                 all_folders);
1845     }
1846
1847   gtk_widget_set_sensitive (impl->browse_shortcuts_add_button, active);
1848 }
1849
1850 /* Sets the sensitivity of the "remove bookmark" button depending on whether a
1851  * bookmark row is selected in the shortcuts tree.
1852  */
1853 static void
1854 bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl)
1855 {
1856   GtkTreeSelection *selection;
1857   GtkTreeIter iter;
1858   gboolean removable = FALSE;
1859
1860   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1861
1862   if (gtk_tree_selection_get_selected (selection, NULL, &iter))
1863     gtk_tree_model_get (impl->shortcuts_filter_model, &iter,
1864                         SHORTCUTS_COL_REMOVABLE, &removable,
1865                         -1);
1866
1867   gtk_widget_set_sensitive (impl->browse_shortcuts_remove_button, removable);
1868 }
1869
1870 /* GtkWidget::drag-begin handler for the shortcuts list. */
1871 static void
1872 shortcuts_drag_begin_cb (GtkWidget             *widget,
1873                          GdkDragContext        *context,
1874                          GtkFileChooserDefault *impl)
1875 {
1876 #if 0
1877   impl->shortcuts_drag_context = g_object_ref (context);
1878 #endif
1879 }
1880
1881 #if 0
1882 /* Removes the idle handler for outside drags */
1883 static void
1884 shortcuts_cancel_drag_outside_idle (GtkFileChooserDefault *impl)
1885 {
1886   if (!impl->shortcuts_drag_outside_idle)
1887     return;
1888
1889   g_source_destroy (impl->shortcuts_drag_outside_idle);
1890   impl->shortcuts_drag_outside_idle = NULL;
1891 }
1892 #endif
1893
1894 /* GtkWidget::drag-end handler for the shortcuts list. */
1895 static void
1896 shortcuts_drag_end_cb (GtkWidget             *widget,
1897                        GdkDragContext        *context,
1898                        GtkFileChooserDefault *impl)
1899 {
1900 #if 0
1901   g_object_unref (impl->shortcuts_drag_context);
1902
1903   shortcuts_cancel_drag_outside_idle (impl);
1904
1905   if (!impl->shortcuts_drag_outside)
1906     return;
1907
1908   gtk_button_clicked (GTK_BUTTON (impl->browse_shortcuts_remove_button));
1909
1910   impl->shortcuts_drag_outside = FALSE;
1911 #endif
1912 }
1913
1914 /* GtkWidget::drag-data-delete handler for the shortcuts list. */
1915 static void
1916 shortcuts_drag_data_delete_cb (GtkWidget             *widget,
1917                                GdkDragContext        *context,
1918                                GtkFileChooserDefault *impl)
1919 {
1920   g_signal_stop_emission_by_name (widget, "drag-data-delete");
1921 }
1922
1923 #if 0
1924 /* Creates a suitable drag cursor to indicate that the selected bookmark will be
1925  * deleted or not.
1926  */
1927 static void
1928 shortcuts_drag_set_delete_cursor (GtkFileChooserDefault *impl,
1929                                   gboolean               delete)
1930 {
1931   GtkTreeView *tree_view;
1932   GtkTreeSelection *selection;
1933   GtkTreeIter iter, child_iter;
1934   GtkTreePath *path;
1935   GdkPixmap *row_pixmap;
1936   GdkBitmap *mask;
1937   int row_pixmap_y;
1938   int cell_y;
1939
1940   tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
1941
1942   /* Find the selected path and get its drag pixmap */
1943
1944   selection = gtk_tree_view_get_selection (tree_view);
1945   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
1946     g_assert_not_reached ();
1947
1948   gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
1949                                                     &child_iter,
1950                                                     &iter);
1951
1952   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &child_iter);
1953
1954   row_pixmap = gtk_tree_view_create_row_drag_icon (tree_view, path);
1955   gtk_tree_path_free (path);
1956
1957   mask = NULL;
1958   row_pixmap_y = 0;
1959
1960   if (delete)
1961     {
1962       GdkPixbuf *pixbuf;
1963
1964       pixbuf = gtk_widget_render_icon (impl->browse_shortcuts_tree_view,
1965                                        GTK_STOCK_DELETE,
1966                                        GTK_ICON_SIZE_DND,
1967                                        NULL);
1968       if (pixbuf)
1969         {
1970           GdkPixmap *composite;
1971           int row_pixmap_width, row_pixmap_height;
1972           int pixbuf_width, pixbuf_height;
1973           int composite_width, composite_height;
1974           int pixbuf_x, pixbuf_y;
1975           GdkGC *gc, *mask_gc;
1976           GdkColor color;
1977           GdkBitmap *pixbuf_mask;
1978
1979           /* Create pixmap and mask for composite image */
1980
1981           gdk_drawable_get_size (row_pixmap, &row_pixmap_width, &row_pixmap_height);
1982           pixbuf_width = gdk_pixbuf_get_width (pixbuf);
1983           pixbuf_height = gdk_pixbuf_get_height (pixbuf);
1984
1985           composite_width = MAX (row_pixmap_width, pixbuf_width);
1986           composite_height = MAX (row_pixmap_height, pixbuf_height);
1987
1988           row_pixmap_y = (composite_height - row_pixmap_height) / 2;
1989
1990           if (gtk_widget_get_direction (impl->browse_shortcuts_tree_view) == GTK_TEXT_DIR_RTL)
1991             pixbuf_x = 0;
1992           else
1993             pixbuf_x = composite_width - pixbuf_width;
1994
1995           pixbuf_y = (composite_height - pixbuf_height) / 2;
1996
1997           composite = gdk_pixmap_new (row_pixmap, composite_width, composite_height, -1);
1998           gc = gdk_gc_new (composite);
1999
2000           mask = gdk_pixmap_new (row_pixmap, composite_width, composite_height, 1);
2001           mask_gc = gdk_gc_new (mask);
2002           color.pixel = 0;
2003           gdk_gc_set_foreground (mask_gc, &color);
2004           gdk_draw_rectangle (mask, mask_gc, TRUE, 0, 0, composite_width, composite_height);
2005
2006           color.red = 0xffff;
2007           color.green = 0xffff;
2008           color.blue = 0xffff;
2009           gdk_gc_set_rgb_fg_color (gc, &color);
2010           gdk_draw_rectangle (composite, gc, TRUE, 0, 0, composite_width, composite_height);
2011
2012           /* Composite the row pixmap and the pixbuf */
2013
2014           gdk_pixbuf_render_pixmap_and_mask_for_colormap
2015             (pixbuf,
2016              gtk_widget_get_colormap (impl->browse_shortcuts_tree_view),
2017              NULL, &pixbuf_mask, 128);
2018           gdk_draw_drawable (mask, mask_gc, pixbuf_mask,
2019                              0, 0,
2020                              pixbuf_x, pixbuf_y,
2021                              pixbuf_width, pixbuf_height);
2022           g_object_unref (pixbuf_mask);
2023
2024           gdk_draw_drawable (composite, gc, row_pixmap,
2025                              0, 0,
2026                              0, row_pixmap_y,
2027                              row_pixmap_width, row_pixmap_height);
2028           color.pixel = 1;
2029           gdk_gc_set_foreground (mask_gc, &color);
2030           gdk_draw_rectangle (mask, mask_gc, TRUE, 0, row_pixmap_y, row_pixmap_width, row_pixmap_height);
2031
2032           gdk_draw_pixbuf (composite, gc, pixbuf,
2033                            0, 0,
2034                            pixbuf_x, pixbuf_y,
2035                            pixbuf_width, pixbuf_height,
2036                            GDK_RGB_DITHER_MAX,
2037                            0, 0);
2038
2039           g_object_unref (pixbuf);
2040           g_object_unref (row_pixmap);
2041
2042           row_pixmap = composite;
2043         }
2044     }
2045
2046   /* The hotspot offsets here are copied from gtk_tree_view_drag_begin(), ugh */
2047
2048   gtk_tree_view_get_path_at_pos (tree_view,
2049                                  tree_view->priv->press_start_x,
2050                                  tree_view->priv->press_start_y,
2051                                  NULL,
2052                                  NULL,
2053                                  NULL,
2054                                  &cell_y);
2055
2056   gtk_drag_set_icon_pixmap (impl->shortcuts_drag_context,
2057                             gdk_drawable_get_colormap (row_pixmap),
2058                             row_pixmap,
2059                             mask,
2060                             tree_view->priv->press_start_x + 1,
2061                             row_pixmap_y + cell_y + 1);
2062
2063   g_object_unref (row_pixmap);
2064   if (mask)
2065     g_object_unref (mask);
2066 }
2067
2068 /* We set the delete cursor and the shortcuts_drag_outside flag in an idle
2069  * handler so that we can tell apart the drag_leave event that comes right
2070  * before a drag_drop, from a normal drag_leave.  We don't want to set the
2071  * cursor nor the flag in the latter case.
2072  */
2073 static gboolean
2074 shortcuts_drag_outside_idle_cb (GtkFileChooserDefault *impl)
2075 {
2076   shortcuts_drag_set_delete_cursor (impl, TRUE);
2077   impl->shortcuts_drag_outside = TRUE;
2078
2079   shortcuts_cancel_drag_outside_idle (impl);
2080   return FALSE;
2081 }
2082 #endif
2083
2084 /* GtkWidget::drag-leave handler for the shortcuts list.  We unhighlight the
2085  * drop position.
2086  */
2087 static void
2088 shortcuts_drag_leave_cb (GtkWidget             *widget,
2089                          GdkDragContext        *context,
2090                          guint                  time_,
2091                          GtkFileChooserDefault *impl)
2092 {
2093 #if 0
2094   if (gtk_drag_get_source_widget (context) == widget && !impl->shortcuts_drag_outside_idle)
2095     {
2096       impl->shortcuts_drag_outside_idle = g_idle_source_new ();
2097       g_source_set_closure (impl->shortcuts_drag_outside_idle,
2098                             g_cclosure_new_object (G_CALLBACK (shortcuts_drag_outside_idle_cb),
2099                                                    G_OBJECT (impl)));
2100       g_source_attach (impl->shortcuts_drag_outside_idle, NULL);
2101     }
2102 #endif
2103
2104   gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
2105                                    NULL,
2106                                    GTK_TREE_VIEW_DROP_BEFORE);
2107
2108   g_signal_stop_emission_by_name (widget, "drag-leave");
2109 }
2110
2111 /* Computes the appropriate row and position for dropping */
2112 static void
2113 shortcuts_compute_drop_position (GtkFileChooserDefault   *impl,
2114                                  int                      x,
2115                                  int                      y,
2116                                  GtkTreePath            **path,
2117                                  GtkTreeViewDropPosition *pos)
2118 {
2119   GtkTreeView *tree_view;
2120   GtkTreeViewColumn *column;
2121   int cell_y;
2122   GdkRectangle cell;
2123   int row;
2124   int bookmarks_index;
2125
2126   tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
2127
2128   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2129
2130   if (!gtk_tree_view_get_path_at_pos (tree_view,
2131                                       x,
2132                                       y - TREE_VIEW_HEADER_HEIGHT (tree_view),
2133                                       path,
2134                                       &column,
2135                                       NULL,
2136                                       &cell_y))
2137     {
2138       row = bookmarks_index + impl->num_bookmarks - 1;
2139       *path = gtk_tree_path_new_from_indices (row, -1);
2140       *pos = GTK_TREE_VIEW_DROP_AFTER;
2141       return;
2142     }
2143
2144   row = *gtk_tree_path_get_indices (*path);
2145   gtk_tree_view_get_background_area (tree_view, *path, column, &cell);
2146   gtk_tree_path_free (*path);
2147
2148   if (row < bookmarks_index)
2149     {
2150       row = bookmarks_index;
2151       *pos = GTK_TREE_VIEW_DROP_BEFORE;
2152     }
2153   else if (row > bookmarks_index + impl->num_bookmarks - 1)
2154     {
2155       row = bookmarks_index + impl->num_bookmarks - 1;
2156       *pos = GTK_TREE_VIEW_DROP_AFTER;
2157     }
2158   else
2159     {
2160       if (cell_y < cell.height / 2)
2161         *pos = GTK_TREE_VIEW_DROP_BEFORE;
2162       else
2163         *pos = GTK_TREE_VIEW_DROP_AFTER;
2164     }
2165
2166   *path = gtk_tree_path_new_from_indices (row, -1);
2167 }
2168
2169 /* GtkWidget::drag-motion handler for the shortcuts list.  We basically
2170  * implement the destination side of DnD by hand, due to limitations in
2171  * GtkTreeView's DnD API.
2172  */
2173 static gboolean
2174 shortcuts_drag_motion_cb (GtkWidget             *widget,
2175                           GdkDragContext        *context,
2176                           gint                   x,
2177                           gint                   y,
2178                           guint                  time_,
2179                           GtkFileChooserDefault *impl)
2180 {
2181   GtkTreePath *path;
2182   GtkTreeViewDropPosition pos;
2183   GdkDragAction action;
2184
2185 #if 0
2186   if (gtk_drag_get_source_widget (context) == widget)
2187     {
2188       shortcuts_cancel_drag_outside_idle (impl);
2189
2190       if (impl->shortcuts_drag_outside)
2191         {
2192           shortcuts_drag_set_delete_cursor (impl, FALSE);
2193           impl->shortcuts_drag_outside = FALSE;
2194         }
2195     }
2196 #endif
2197
2198   if (context->suggested_action == GDK_ACTION_COPY || (context->actions & GDK_ACTION_COPY) != 0)
2199     action = GDK_ACTION_COPY;
2200   else if (context->suggested_action == GDK_ACTION_MOVE || (context->actions & GDK_ACTION_MOVE) != 0)
2201     action = GDK_ACTION_MOVE;
2202   else
2203     {
2204       action = 0;
2205       goto out;
2206     }
2207
2208   shortcuts_compute_drop_position (impl, x, y, &path, &pos);
2209   gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), path, pos);
2210   gtk_tree_path_free (path);
2211
2212  out:
2213
2214   g_signal_stop_emission_by_name (widget, "drag-motion");
2215
2216   if (action != 0)
2217     {
2218       gdk_drag_status (context, action, time_);
2219       return TRUE;
2220     }
2221   else
2222     return FALSE;
2223 }
2224
2225 /* GtkWidget::drag-drop handler for the shortcuts list. */
2226 static gboolean
2227 shortcuts_drag_drop_cb (GtkWidget             *widget,
2228                         GdkDragContext        *context,
2229                         gint                   x,
2230                         gint                   y,
2231                         guint                  time_,
2232                         GtkFileChooserDefault *impl)
2233 {
2234 #if 0
2235   shortcuts_cancel_drag_outside_idle (impl);
2236 #endif
2237
2238   g_signal_stop_emission_by_name (widget, "drag-drop");
2239   return TRUE;
2240 }
2241
2242 /* Converts raw selection data from text/uri-list to a list of strings */
2243 static GSList *
2244 split_uris (const char *data)
2245 {
2246   GSList *uris;
2247   const char *p, *start;
2248
2249   uris = NULL;
2250
2251   start = data;
2252
2253   for (p = start; *p != 0; p++)
2254     if (*p == '\r' && *(p + 1) == '\n')
2255       {
2256         char *name;
2257
2258         name = g_strndup (start, p - start);
2259         uris = g_slist_prepend (uris, name);
2260
2261         start = p + 2;
2262         p = start;
2263       }
2264
2265   uris = g_slist_reverse (uris);
2266   return uris;
2267 }
2268
2269 /* Parses a "text/uri-list" string and inserts its URIs as bookmarks */
2270 static void
2271 shortcuts_drop_uris (GtkFileChooserDefault *impl,
2272                      const char            *data,
2273                      int                    position)
2274 {
2275   GSList *uris, *l;
2276
2277   uris = split_uris (data);
2278
2279   for (l = uris; l; l = l->next)
2280     {
2281       char *uri;
2282       GtkFilePath *path;
2283
2284       uri = l->data;
2285       path = gtk_file_system_uri_to_path (impl->file_system, uri);
2286
2287       if (path)
2288         {
2289           if (shortcuts_add_bookmark_from_path (impl, path, position))
2290             position++;
2291
2292           gtk_file_path_free (path);
2293         }
2294       else
2295         {
2296           char *msg;
2297
2298           msg = g_strdup_printf (_("Could not add a bookmark for %s because it is an invalid path name."),
2299                                  uri);
2300           error_message (impl, msg);
2301           g_free (msg);
2302         }
2303
2304       g_free (uri);
2305     }
2306
2307   g_slist_free (uris);
2308 }
2309
2310 /* Reorders the selected bookmark to the specified position */
2311 static void
2312 shortcuts_reorder (GtkFileChooserDefault *impl,
2313                    int                    new_position)
2314 {
2315   GtkTreeSelection *selection;
2316   GtkTreeIter iter, child_iter;
2317   GtkTreePath *path;
2318   int old_position;
2319   int bookmarks_index;
2320   const GtkFilePath *file_path;
2321   GtkFilePath *file_path_copy;
2322   GError *error;
2323
2324   /* Get the selected path */
2325
2326   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
2327   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
2328     g_assert_not_reached ();
2329
2330   gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
2331                                                     &child_iter,
2332                                                     &iter);
2333
2334   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &child_iter);
2335   old_position = *gtk_tree_path_get_indices (path);
2336   gtk_tree_path_free (path);
2337
2338   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2339   old_position -= bookmarks_index;
2340   g_assert (old_position >= 0 && old_position < impl->num_bookmarks);
2341
2342   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &child_iter,
2343                       SHORTCUTS_COL_PATH, &file_path,
2344                       -1);
2345   file_path_copy = gtk_file_path_copy (file_path); /* removal below will free file_path, so we need a copy */
2346
2347   /* Remove the path from the old position and insert it in the new one */
2348
2349   if (new_position > old_position)
2350     new_position--;
2351
2352   if (old_position == new_position)
2353     return;
2354
2355   error = NULL;
2356   if (gtk_file_system_remove_bookmark (impl->file_system, file_path_copy, &error))
2357     shortcuts_add_bookmark_from_path (impl, file_path_copy, new_position);
2358   else
2359     error_could_not_add_bookmark_dialog (impl, file_path_copy, error);
2360
2361   gtk_file_path_free (file_path_copy);
2362 }
2363
2364 /* Callback used when we get the drag data for the bookmarks list.  We add the
2365  * received URIs as bookmarks if they are folders.
2366  */
2367 static void
2368 shortcuts_drag_data_received_cb (GtkWidget          *widget,
2369                                  GdkDragContext     *context,
2370                                  gint                x,
2371                                  gint                y,
2372                                  GtkSelectionData   *selection_data,
2373                                  guint               info,
2374                                  guint               time_,
2375                                  gpointer            data)
2376 {
2377   GtkFileChooserDefault *impl;
2378   GtkTreePath *tree_path;
2379   GtkTreeViewDropPosition tree_pos;
2380   int position;
2381   int bookmarks_index;
2382
2383   impl = GTK_FILE_CHOOSER_DEFAULT (data);
2384
2385   /* Compute position */
2386
2387   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2388
2389   shortcuts_compute_drop_position (impl, x, y, &tree_path, &tree_pos);
2390   position = *gtk_tree_path_get_indices (tree_path);
2391   gtk_tree_path_free (tree_path);
2392
2393   if (tree_pos == GTK_TREE_VIEW_DROP_AFTER)
2394     position++;
2395
2396   g_assert (position >= bookmarks_index);
2397   position -= bookmarks_index;
2398
2399   if (selection_data->target == gdk_atom_intern ("text/uri-list", FALSE))
2400     shortcuts_drop_uris (impl, selection_data->data, position);
2401   else if (selection_data->target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE))
2402     shortcuts_reorder (impl, position);
2403
2404   g_signal_stop_emission_by_name (widget, "drag-data-received");
2405 }
2406
2407 /* Callback used when the selection in the shortcuts tree changes */
2408 static void
2409 shortcuts_selection_changed_cb (GtkTreeSelection      *selection,
2410                                 GtkFileChooserDefault *impl)
2411 {
2412   bookmarks_check_remove_sensitivity (impl);
2413 }
2414
2415 /* Creates the widgets for the shortcuts and bookmarks tree */
2416 static GtkWidget *
2417 shortcuts_list_create (GtkFileChooserDefault *impl)
2418 {
2419   GtkWidget *swin;
2420   GtkTreeSelection *selection;
2421   GtkTreeViewColumn *column;
2422   GtkCellRenderer *renderer;
2423
2424   /* Scrolled window */
2425
2426   swin = gtk_scrolled_window_new (NULL, NULL);
2427   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
2428                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2429   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
2430                                        GTK_SHADOW_IN);
2431   gtk_widget_show (swin);
2432
2433   /* Tree */
2434
2435   impl->browse_shortcuts_tree_view = gtk_tree_view_new ();
2436   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), FALSE);
2437
2438   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), impl->shortcuts_filter_model);
2439
2440   gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
2441                                           GDK_BUTTON1_MASK,
2442                                           shortcuts_source_targets,
2443                                           num_shortcuts_source_targets,
2444                                           GDK_ACTION_MOVE);
2445
2446   gtk_drag_dest_set (impl->browse_shortcuts_tree_view,
2447                      GTK_DEST_DEFAULT_ALL,
2448                      shortcuts_dest_targets,
2449                      num_shortcuts_dest_targets,
2450                      GDK_ACTION_COPY | GDK_ACTION_MOVE);
2451
2452   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
2453   gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
2454   gtk_tree_selection_set_select_function (selection,
2455                                           shortcuts_select_func,
2456                                           impl, NULL);
2457
2458   g_signal_connect (selection, "changed",
2459                     G_CALLBACK (shortcuts_selection_changed_cb), impl);
2460
2461   g_signal_connect (impl->browse_shortcuts_tree_view, "row-activated",
2462                     G_CALLBACK (shortcuts_row_activated_cb), impl);
2463
2464   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-begin",
2465                     G_CALLBACK (shortcuts_drag_begin_cb), impl);
2466   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-end",
2467                     G_CALLBACK (shortcuts_drag_end_cb), impl);
2468   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-delete",
2469                     G_CALLBACK (shortcuts_drag_data_delete_cb), impl);
2470
2471   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-leave",
2472                     G_CALLBACK (shortcuts_drag_leave_cb), impl);
2473   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-motion",
2474                     G_CALLBACK (shortcuts_drag_motion_cb), impl);
2475   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-drop",
2476                     G_CALLBACK (shortcuts_drag_drop_cb), impl);
2477   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-received",
2478                     G_CALLBACK (shortcuts_drag_data_received_cb), impl);
2479
2480   gtk_container_add (GTK_CONTAINER (swin), impl->browse_shortcuts_tree_view);
2481   gtk_widget_show (impl->browse_shortcuts_tree_view);
2482
2483   /* Column */
2484
2485   column = gtk_tree_view_column_new ();
2486   gtk_tree_view_column_set_title (column, _("Folder"));
2487
2488   renderer = gtk_cell_renderer_pixbuf_new ();
2489   gtk_tree_view_column_pack_start (column, renderer, FALSE);
2490   gtk_tree_view_column_set_attributes (column, renderer,
2491                                        "pixbuf", SHORTCUTS_COL_PIXBUF,
2492                                        "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
2493                                        NULL);
2494
2495   renderer = _gtk_cell_renderer_sep_text_new ();
2496   gtk_tree_view_column_pack_start (column, renderer, TRUE);
2497   gtk_tree_view_column_set_attributes (column, renderer,
2498                                        "text", SHORTCUTS_COL_NAME,
2499                                        NULL);
2500
2501   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), column);
2502
2503   return swin;
2504 }
2505
2506 /* Creates the widgets for the shortcuts/bookmarks pane */
2507 static GtkWidget *
2508 shortcuts_pane_create (GtkFileChooserDefault *impl,
2509                        GtkSizeGroup          *size_group)
2510 {
2511   GtkWidget *vbox;
2512   GtkWidget *hbox;
2513   GtkWidget *widget;
2514
2515   vbox = gtk_vbox_new (FALSE, 6);
2516   gtk_widget_show (vbox);
2517
2518   /* Shortcuts tree */
2519
2520   widget = shortcuts_list_create (impl);
2521   gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
2522
2523   /* Box for buttons */
2524
2525   hbox = gtk_hbox_new (TRUE, 6);
2526   gtk_size_group_add_widget (size_group, hbox);
2527   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
2528   gtk_widget_show (hbox);
2529
2530   /* Add bookmark button */
2531
2532   impl->browse_shortcuts_add_button = button_new (impl,
2533                                                   _("_Add"),
2534                                                   GTK_STOCK_ADD,
2535                                                   FALSE,
2536                                                   TRUE,
2537                                                   G_CALLBACK (add_bookmark_button_clicked_cb));
2538   gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_add_button, TRUE, TRUE, 0);
2539
2540   /* Remove bookmark button */
2541
2542   impl->browse_shortcuts_remove_button = button_new (impl,
2543                                                      _("_Remove"),
2544                                                      GTK_STOCK_REMOVE,
2545                                                      FALSE,
2546                                                      TRUE,
2547                                                      G_CALLBACK (remove_bookmark_button_clicked_cb));
2548   gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_remove_button, TRUE, TRUE, 0);
2549
2550   return vbox;
2551 }
2552
2553 /* Handles key press events on the file list, so that we can trap Enter to
2554  * activate the default button on our own.
2555  */
2556 static gboolean
2557 trap_activate_cb (GtkWidget   *widget,
2558                   GdkEventKey *event,
2559                   gpointer     data)
2560 {
2561   GtkFileChooserDefault *impl;
2562
2563   impl = (GtkFileChooserDefault *) data;
2564
2565   if (event->keyval == GDK_Return
2566       || event->keyval == GDK_ISO_Enter
2567       || event->keyval == GDK_KP_Enter
2568       || event->keyval == GDK_space)
2569     {
2570       GtkWindow *window;
2571
2572       window = get_toplevel (widget);
2573       if (window
2574           && widget != window->default_widget
2575           && !(widget == window->focus_widget &&
2576                (!window->default_widget || !GTK_WIDGET_SENSITIVE (window->default_widget))))
2577         gtk_window_activate_default (window);
2578
2579       return TRUE;
2580     }
2581   return FALSE;
2582 }
2583
2584 /* Callback used when the file list's popup menu is detached */
2585 static void
2586 popup_menu_detach_cb (GtkWidget *attach_widget,
2587                       GtkMenu   *menu)
2588 {
2589   GtkFileChooserDefault *impl;
2590
2591   impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
2592   g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
2593
2594   impl->browse_files_popup_menu = NULL;
2595   impl->browse_files_popup_menu_hidden_files_item = NULL;
2596 }
2597
2598 /* Callback used when the "Show Hidden Files" menu item is toggled */
2599 static void
2600 show_hidden_toggled_cb (GtkCheckMenuItem      *item,
2601                         GtkFileChooserDefault *impl)
2602 {
2603   g_object_set (impl,
2604                 "show-hidden", gtk_check_menu_item_get_active (item),
2605                 NULL);
2606 }
2607
2608 /* Constructs the popup menu for the file list if needed */
2609 static void
2610 file_list_build_popup_menu (GtkFileChooserDefault *impl)
2611 {
2612   if (!impl->browse_files_popup_menu)
2613     {
2614       impl->browse_files_popup_menu = gtk_menu_new ();
2615       gtk_menu_attach_to_widget (GTK_MENU (impl->browse_files_popup_menu),
2616                                  impl->browse_files_tree_view,
2617                                  popup_menu_detach_cb);
2618
2619       impl->browse_files_popup_menu_hidden_files_item =
2620         gtk_check_menu_item_new_with_mnemonic (_("Show _Hidden Files"));
2621       g_signal_connect (impl->browse_files_popup_menu_hidden_files_item, "toggled",
2622                         G_CALLBACK (show_hidden_toggled_cb), impl);
2623
2624       gtk_widget_show (impl->browse_files_popup_menu_hidden_files_item);
2625       gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu),
2626                              impl->browse_files_popup_menu_hidden_files_item);
2627     }
2628
2629   g_signal_handlers_block_by_func (impl->browse_files_popup_menu_hidden_files_item,
2630                                    G_CALLBACK (show_hidden_toggled_cb), impl);
2631   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (impl->browse_files_popup_menu_hidden_files_item),
2632                                   impl->show_hidden);
2633   g_signal_handlers_unblock_by_func (impl->browse_files_popup_menu_hidden_files_item,
2634                                      G_CALLBACK (show_hidden_toggled_cb), impl);
2635 }
2636
2637 static void
2638 file_list_popup_menu (GtkFileChooserDefault *impl,
2639                       GdkEventButton        *event)
2640 {
2641   int button;
2642   guint32 timestamp;
2643
2644   if (event)
2645     {
2646       button = event->button;
2647       timestamp = event->time;
2648     }
2649   else
2650     {
2651       button = 0;
2652       timestamp = GDK_CURRENT_TIME;
2653     }
2654
2655   file_list_build_popup_menu (impl);
2656   gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
2657                   NULL, NULL, NULL, NULL,
2658                   button, timestamp);
2659 }
2660
2661 /* Callback used for the GtkWidget::popup-menu signal of the file list */
2662 static gboolean
2663 list_popup_menu_cb (GtkWidget *widget,
2664                     GtkFileChooserDefault *impl)
2665 {
2666   file_list_popup_menu (impl, NULL);
2667   return TRUE;
2668 }
2669
2670 /* Callback used when a button is pressed on the file list.  We trap button 3 to
2671  * bring up a popup menu.
2672  */
2673 static gboolean
2674 list_button_press_event_cb (GtkWidget             *widget,
2675                             GdkEventButton        *event,
2676                             GtkFileChooserDefault *impl)
2677 {
2678   if (event->button != 3)
2679     return FALSE;
2680
2681   file_list_popup_menu (impl, event);
2682   return TRUE;
2683 }
2684
2685 /* Creates the widgets for the file list */
2686 static GtkWidget *
2687 create_file_list (GtkFileChooserDefault *impl)
2688 {
2689   GtkWidget *swin;
2690   GtkTreeSelection *selection;
2691   GtkTreeViewColumn *column;
2692   GtkCellRenderer *renderer;
2693
2694   /* Scrolled window */
2695
2696   swin = gtk_scrolled_window_new (NULL, NULL);
2697   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
2698                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2699   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
2700                                        GTK_SHADOW_IN);
2701
2702   /* Tree/list view */
2703
2704   impl->browse_files_tree_view = gtk_tree_view_new ();
2705   g_object_set_data (G_OBJECT (impl->browse_files_tree_view), "GtkFileChooserDefault", impl);
2706
2707   gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->browse_files_tree_view), TRUE);
2708   gtk_container_add (GTK_CONTAINER (swin), impl->browse_files_tree_view);
2709   g_signal_connect (impl->browse_files_tree_view, "row-activated",
2710                     G_CALLBACK (list_row_activated), impl);
2711   g_signal_connect (impl->browse_files_tree_view, "key-press-event",
2712                     G_CALLBACK (trap_activate_cb), impl);
2713   g_signal_connect (impl->browse_files_tree_view, "popup-menu",
2714                     G_CALLBACK (list_popup_menu_cb), impl);
2715   g_signal_connect (impl->browse_files_tree_view, "button-press-event",
2716                     G_CALLBACK (list_button_press_event_cb), impl);
2717
2718   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2719   gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_files_tree_view),
2720                                           GDK_BUTTON1_MASK,
2721                                           file_list_source_targets,
2722                                           num_file_list_source_targets,
2723                                           GDK_ACTION_COPY);
2724
2725   g_signal_connect (selection, "changed",
2726                     G_CALLBACK (list_selection_changed), impl);
2727
2728   /* Filename column */
2729
2730   impl->list_name_column = gtk_tree_view_column_new ();
2731   gtk_tree_view_column_set_expand (impl->list_name_column, TRUE);
2732   gtk_tree_view_column_set_title (impl->list_name_column, _("Name"));
2733   gtk_tree_view_column_set_sort_column_id (impl->list_name_column, FILE_LIST_COL_NAME);
2734
2735   renderer = gtk_cell_renderer_pixbuf_new ();
2736   gtk_tree_view_column_pack_start (impl->list_name_column, renderer, FALSE);
2737   gtk_tree_view_column_set_cell_data_func (impl->list_name_column, renderer,
2738                                            list_icon_data_func, impl, NULL);
2739
2740   impl->list_name_renderer = gtk_cell_renderer_text_new ();
2741   g_signal_connect (impl->list_name_renderer, "edited",
2742                     G_CALLBACK (renderer_edited_cb), impl);
2743   g_signal_connect (impl->list_name_renderer, "editing-canceled",
2744                     G_CALLBACK (renderer_editing_canceled_cb), impl);
2745   gtk_tree_view_column_pack_start (impl->list_name_column, impl->list_name_renderer, TRUE);
2746   gtk_tree_view_column_set_cell_data_func (impl->list_name_column, impl->list_name_renderer,
2747                                            list_name_data_func, impl, NULL);
2748
2749   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), impl->list_name_column);
2750 #if 0
2751   /* Size column */
2752
2753   column = gtk_tree_view_column_new ();
2754   gtk_tree_view_column_set_title (column, _("Size"));
2755
2756   renderer = gtk_cell_renderer_text_new ();
2757   gtk_tree_view_column_pack_start (column, renderer, TRUE);
2758   gtk_tree_view_column_set_cell_data_func (column, renderer,
2759                                            list_size_data_func, impl, NULL);
2760   gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_SIZE);
2761   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
2762 #endif
2763   /* Modification time column */
2764
2765   column = gtk_tree_view_column_new ();
2766   gtk_tree_view_column_set_title (column, _("Modified"));
2767
2768   renderer = gtk_cell_renderer_text_new ();
2769   gtk_tree_view_column_pack_start (column, renderer, TRUE);
2770   gtk_tree_view_column_set_cell_data_func (column, renderer,
2771                                            list_mtime_data_func, impl, NULL);
2772   gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_MTIME);
2773   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
2774   gtk_widget_show_all (swin);
2775
2776   return swin;
2777 }
2778
2779 static GtkWidget *
2780 create_filename_entry_and_filter_combo (GtkFileChooserDefault *impl)
2781 {
2782   GtkWidget *hbox;
2783   GtkWidget *widget;
2784
2785   hbox = gtk_hbox_new (FALSE, 12);
2786   gtk_widget_show (hbox);
2787
2788   /* Filter combo */
2789
2790   widget = filter_create (impl);
2791   gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
2792
2793   return hbox;
2794 }
2795
2796 static GtkWidget *
2797 create_path_bar (GtkFileChooserDefault *impl)
2798 {
2799   GtkWidget *path_bar;
2800
2801   path_bar = g_object_new (GTK_TYPE_PATH_BAR, NULL);
2802   _gtk_path_bar_set_file_system (GTK_PATH_BAR (path_bar), impl->file_system);
2803
2804   return path_bar;
2805 }
2806
2807 /* Creates the widgets for the files/folders pane */
2808 static GtkWidget *
2809 file_pane_create (GtkFileChooserDefault *impl,
2810                   GtkSizeGroup          *size_group)
2811 {
2812   GtkWidget *vbox;
2813   GtkWidget *hbox;
2814   GtkWidget *widget;
2815
2816   vbox = gtk_vbox_new (FALSE, 6);
2817   gtk_widget_show (vbox);
2818
2819   /* The path bar and 'Create Folder' button */
2820   hbox = gtk_hbox_new (FALSE, 12);
2821   gtk_widget_show (hbox);
2822   impl->browse_path_bar = create_path_bar (impl);
2823   g_signal_connect (impl->browse_path_bar, "path-clicked", G_CALLBACK (path_bar_clicked), impl);
2824   gtk_widget_show_all (impl->browse_path_bar);
2825   gtk_box_pack_start (GTK_BOX (hbox), impl->browse_path_bar, TRUE, TRUE, 0);
2826
2827   /* Create Folder */
2828   impl->browse_new_folder_button = gtk_button_new_with_mnemonic (_("Create Fo_lder"));
2829   g_signal_connect (impl->browse_new_folder_button, "clicked",
2830                     G_CALLBACK (new_folder_button_clicked), impl);
2831   gtk_box_pack_end (GTK_BOX (hbox), impl->browse_new_folder_button, FALSE, FALSE, 0);
2832   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
2833
2834   /* Box for lists and preview */
2835
2836   hbox = gtk_hbox_new (FALSE, PREVIEW_HBOX_SPACING);
2837   gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
2838   gtk_widget_show (hbox);
2839
2840   /* File list */
2841
2842   widget = create_file_list (impl);
2843   gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
2844
2845   /* Preview */
2846
2847   impl->preview_box = gtk_vbox_new (FALSE, 12);
2848   gtk_box_pack_start (GTK_BOX (hbox), impl->preview_box, FALSE, FALSE, 0);
2849   /* Don't show preview box initially */
2850
2851   /* Filename entry and filter combo */
2852   hbox = gtk_hbox_new (FALSE, 0);
2853   gtk_size_group_add_widget (size_group, hbox);
2854   widget = create_filename_entry_and_filter_combo (impl);
2855   gtk_box_pack_end (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
2856   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
2857   gtk_widget_show (hbox);
2858
2859   return vbox;
2860 }
2861 /* Callback used when the "Browse for more folders" expander is toggled */
2862 static void
2863 expander_changed_cb (GtkExpander           *expander,
2864                      GParamSpec            *pspec,
2865                      GtkFileChooserDefault *impl)
2866 {
2867   update_appearance (impl);
2868 }
2869
2870 /* Callback used when the selection changes in the save folder combo box */
2871 static void
2872 save_folder_combo_changed_cb (GtkComboBox           *combo,
2873                               GtkFileChooserDefault *impl)
2874 {
2875   int active;
2876
2877   if (impl->changing_folder)
2878     return;
2879
2880   active = gtk_combo_box_get_active (combo);
2881   if (active == -1)
2882     return;
2883
2884   shortcuts_activate_item (impl, active);
2885 }
2886
2887 /* Creates the combo box with the save folders */
2888 static GtkWidget *
2889 save_folder_combo_create (GtkFileChooserDefault *impl)
2890 {
2891   GtkWidget *combo;
2892   GtkCellRenderer *cell;
2893
2894   combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (impl->shortcuts_model));
2895   gtk_widget_show (combo);
2896
2897   cell = gtk_cell_renderer_pixbuf_new ();
2898   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE);
2899   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
2900                                   "pixbuf", SHORTCUTS_COL_PIXBUF,
2901                                   "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
2902                                   NULL);
2903
2904   cell = _gtk_cell_renderer_sep_text_new ();
2905   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
2906   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
2907                                   "text", SHORTCUTS_COL_NAME,
2908                                   NULL);
2909
2910   g_signal_connect (combo, "changed",
2911                     G_CALLBACK (save_folder_combo_changed_cb), impl);
2912
2913   return combo;
2914 }
2915
2916 /* Creates the widgets specific to Save mode */
2917 static GtkWidget *
2918 save_widgets_create (GtkFileChooserDefault *impl)
2919 {
2920   GtkWidget *vbox;
2921   GtkWidget *table;
2922   GtkWidget *widget;
2923   GtkWidget *alignment;
2924
2925   vbox = gtk_vbox_new (FALSE, 12);
2926
2927   table = gtk_table_new (2, 2, FALSE);
2928   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
2929   gtk_widget_show (table);
2930   gtk_table_set_row_spacings (GTK_TABLE (table), 12);
2931   gtk_table_set_col_spacings (GTK_TABLE (table), 12);
2932
2933   /* Name entry */
2934
2935   widget = gtk_label_new_with_mnemonic (_("_Name:"));
2936   gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
2937   gtk_table_attach (GTK_TABLE (table), widget,
2938                     0, 1, 0, 1,
2939                     GTK_FILL, GTK_FILL,
2940                     0, 0);
2941   gtk_widget_show (widget);
2942
2943   impl->save_file_name_entry = gtk_entry_new ();
2944   gtk_entry_set_width_chars (GTK_ENTRY (impl->save_file_name_entry), 45);
2945   gtk_entry_set_activates_default (GTK_ENTRY (impl->save_file_name_entry), TRUE);
2946   gtk_table_attach (GTK_TABLE (table), impl->save_file_name_entry,
2947                     1, 2, 0, 1,
2948                     GTK_EXPAND | GTK_FILL, 0,
2949                     0, 0);
2950   gtk_widget_show (impl->save_file_name_entry);
2951   gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->save_file_name_entry);
2952
2953   /* Folder combo */
2954   impl->save_folder_label = gtk_label_new (NULL);
2955   gtk_misc_set_alignment (GTK_MISC (impl->save_folder_label), 0.0, 0.5);
2956   gtk_table_attach (GTK_TABLE (table), impl->save_folder_label,
2957                     0, 1, 1, 2,
2958                     GTK_FILL, GTK_FILL,
2959                     0, 0);
2960   gtk_widget_show (impl->save_folder_label);
2961
2962   impl->save_folder_combo = save_folder_combo_create (impl);
2963   gtk_table_attach (GTK_TABLE (table), impl->save_folder_combo,
2964                     1, 2, 1, 2,
2965                     GTK_EXPAND | GTK_FILL, GTK_FILL,
2966                     0, 0);
2967   gtk_label_set_mnemonic_widget (GTK_LABEL (impl->save_folder_label), impl->save_folder_combo);
2968
2969   /* custom widget */
2970   impl->save_extra_align = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
2971   gtk_box_pack_start (GTK_BOX (vbox), impl->save_extra_align, FALSE, FALSE, 0);
2972
2973   /* Expander */
2974   alignment = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
2975   gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
2976
2977   impl->save_expander = gtk_expander_new_with_mnemonic (_("_Browse for other folders"));
2978   gtk_container_add (GTK_CONTAINER (alignment), impl->save_expander);
2979   g_signal_connect (impl->save_expander, "notify::expanded",
2980                     G_CALLBACK (expander_changed_cb),
2981                     impl);
2982   gtk_widget_show_all (alignment);
2983
2984   return vbox;
2985 }
2986
2987 /* Creates the main hpaned with the widgets shared by Open and Save mode */
2988 static GtkWidget *
2989 browse_widgets_create (GtkFileChooserDefault *impl)
2990 {
2991   GtkWidget *vbox;
2992   GtkWidget *hpaned;
2993   GtkWidget *widget;
2994   GtkSizeGroup *size_group;
2995
2996   /* size group is used by the [+][-] buttons and the filter combo */
2997   size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
2998   vbox = gtk_vbox_new (FALSE, 12);
2999
3000   /* Paned widget */
3001   hpaned = gtk_hpaned_new ();
3002   gtk_widget_show (hpaned);
3003   gtk_paned_set_position (GTK_PANED (hpaned), 200); /* FIXME: this sucks */
3004   gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
3005
3006   widget = shortcuts_pane_create (impl, size_group);
3007   gtk_paned_pack1 (GTK_PANED (hpaned), widget, FALSE, FALSE);
3008   widget = file_pane_create (impl, size_group);
3009   gtk_paned_pack2 (GTK_PANED (hpaned), widget, TRUE, FALSE);
3010
3011   g_object_unref (size_group);
3012
3013   /* Alignment to hold custom widget */
3014   impl->browse_extra_align = gtk_alignment_new (0.0, .5, 1.0, 1.0);
3015   gtk_box_pack_start (GTK_BOX (vbox), impl->browse_extra_align, FALSE, FALSE, 0);
3016
3017   return vbox;
3018 }
3019
3020 static GObject*
3021 gtk_file_chooser_default_constructor (GType                  type,
3022                                       guint                  n_construct_properties,
3023                                       GObjectConstructParam *construct_params)
3024 {
3025   GtkFileChooserDefault *impl;
3026   GObject *object;
3027
3028   object = parent_class->constructor (type,
3029                                       n_construct_properties,
3030                                       construct_params);
3031   impl = GTK_FILE_CHOOSER_DEFAULT (object);
3032
3033   g_assert (impl->file_system);
3034
3035   gtk_widget_push_composite_child ();
3036
3037   /* Shortcuts model */
3038
3039   shortcuts_model_create (impl);
3040
3041   /* Widgets for Save mode */
3042   impl->save_widgets = save_widgets_create (impl);
3043   gtk_box_pack_start (GTK_BOX (impl), impl->save_widgets, FALSE, FALSE, 0);
3044
3045   /* The browse widgets */
3046   impl->browse_widgets = browse_widgets_create (impl);
3047   gtk_box_pack_start (GTK_BOX (impl), impl->browse_widgets, TRUE, TRUE, 0);
3048
3049   gtk_widget_pop_composite_child ();
3050   update_appearance (impl);
3051
3052   return object;
3053 }
3054
3055 /* Sets the extra_widget by packing it in the appropriate place */
3056 static void
3057 set_extra_widget (GtkFileChooserDefault *impl,
3058                   GtkWidget             *extra_widget)
3059 {
3060   if (extra_widget)
3061     {
3062       g_object_ref (extra_widget);
3063       /* FIXME: is this right ? */
3064       gtk_widget_show (extra_widget);
3065     }
3066
3067   if (impl->extra_widget)
3068     g_object_unref (impl->extra_widget);
3069
3070   impl->extra_widget = extra_widget;
3071 }
3072
3073 static void
3074 set_local_only (GtkFileChooserDefault *impl,
3075                 gboolean               local_only)
3076 {
3077   if (local_only != impl->local_only)
3078     {
3079       impl->local_only = local_only;
3080
3081       if (impl->shortcuts_model && impl->file_system)
3082         {
3083           shortcuts_add_volumes (impl);
3084           shortcuts_add_bookmarks (impl);
3085         }
3086
3087       if (local_only &&
3088           !gtk_file_system_path_is_local (impl->file_system, impl->current_folder))
3089         {
3090           /* If we are pointing to a non-local folder, make an effort to change
3091            * back to a local folder, but it's really up to the app to not cause
3092            * such a situation, so we ignore errors.
3093            */
3094           const gchar *home = g_get_home_dir ();
3095           GtkFilePath *home_path = gtk_file_system_filename_to_path (impl->file_system, home);
3096
3097           _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), home_path, NULL);
3098
3099           gtk_file_path_free (home_path);
3100         }
3101     }
3102 }
3103
3104 static void
3105 volumes_changed_cb (GtkFileSystem         *file_system,
3106                     GtkFileChooserDefault *impl)
3107 {
3108   shortcuts_add_volumes (impl);
3109 }
3110
3111 /* Callback used when the set of bookmarks changes in the file system */
3112 static void
3113 bookmarks_changed_cb (GtkFileSystem         *file_system,
3114                       GtkFileChooserDefault *impl)
3115 {
3116   shortcuts_add_bookmarks (impl);
3117
3118   bookmarks_check_add_sensitivity (impl);
3119   bookmarks_check_remove_sensitivity (impl);
3120 }
3121
3122 /* Sets the file chooser to multiple selection mode */
3123 static void
3124 set_select_multiple (GtkFileChooserDefault *impl,
3125                      gboolean               select_multiple,
3126                      gboolean               property_notify)
3127 {
3128   GtkTreeSelection *selection;
3129   GtkSelectionMode mode;
3130
3131   if (select_multiple == impl->select_multiple)
3132     return;
3133
3134   mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE;
3135
3136   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3137   gtk_tree_selection_set_mode (selection, mode);
3138
3139   impl->select_multiple = select_multiple;
3140   g_object_notify (G_OBJECT (impl), "select-multiple");
3141
3142   check_preview_change (impl);
3143 }
3144
3145 static void
3146 set_file_system_backend (GtkFileChooserDefault *impl,
3147                          const char *backend)
3148 {
3149   if (impl->file_system)
3150     {
3151       g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
3152       impl->volumes_changed_id = 0;
3153       g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
3154       impl->bookmarks_changed_id = 0;
3155       g_object_unref (impl->file_system);
3156     }
3157
3158   impl->file_system = NULL;
3159   if (backend)
3160     impl->file_system = _gtk_file_system_create (backend);
3161   else
3162     {
3163       GtkSettings *settings = gtk_settings_get_default ();
3164       gchar *default_backend = NULL;
3165
3166       g_object_get (settings, "gtk-file-chooser-backend", &default_backend, NULL);
3167       if (default_backend)
3168         {
3169           impl->file_system = _gtk_file_system_create (default_backend);
3170           g_free (default_backend);
3171         }
3172     }
3173
3174   if (!impl->file_system)
3175     {
3176 #if defined (G_OS_UNIX)
3177       impl->file_system = gtk_file_system_unix_new ();
3178 #elif defined (G_OS_WIN32)
3179       impl->file_system = gtk_file_system_win32_new ();
3180 #else
3181 #error "No default filesystem implementation on the platform"
3182 #endif
3183     }
3184
3185   if (impl->file_system)
3186     {
3187       impl->volumes_changed_id = g_signal_connect (impl->file_system, "volumes-changed",
3188                                                    G_CALLBACK (volumes_changed_cb),
3189                                                    impl);
3190       impl->bookmarks_changed_id = g_signal_connect (impl->file_system, "bookmarks-changed",
3191                                                      G_CALLBACK (bookmarks_changed_cb),
3192                                                      impl);
3193     }
3194 }
3195
3196 /* This function is basically a do_all function.
3197  *
3198  * It sets the visibility on all the widgets based on the current state, and
3199  * moves the custom_widget if needed.
3200  */
3201 static void
3202 update_appearance (GtkFileChooserDefault *impl)
3203 {
3204   GtkWidget *child;
3205
3206   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
3207       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
3208     {
3209       const char *text;
3210
3211       gtk_widget_show (impl->save_widgets);
3212
3213       if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
3214         text = _("Save in _folder:");
3215       else
3216         text = _("Create in _folder:");
3217
3218       gtk_label_set_text_with_mnemonic (GTK_LABEL (impl->save_folder_label), text);
3219
3220       if (gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
3221         {
3222           gtk_widget_set_sensitive (impl->save_folder_label, FALSE);
3223           gtk_widget_set_sensitive (impl->save_folder_combo, FALSE);
3224           gtk_widget_show (impl->browse_widgets);
3225         }
3226       else
3227         {
3228           gtk_widget_set_sensitive (impl->save_folder_label, TRUE);
3229           gtk_widget_set_sensitive (impl->save_folder_combo, TRUE);
3230           gtk_widget_hide (impl->browse_widgets);
3231         }
3232
3233       gtk_widget_show (impl->browse_new_folder_button);
3234
3235       if (impl->select_multiple)
3236         {
3237           g_warning ("Save mode cannot be set in conjunction with multiple selection mode.  "
3238                      "Re-setting to single selection mode.");
3239           set_select_multiple (impl, FALSE, TRUE);
3240         }
3241     }
3242   else if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
3243            impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
3244     {
3245       gtk_widget_hide (impl->save_widgets);
3246       gtk_widget_show (impl->browse_widgets);
3247     }
3248   /* FIXME: */
3249   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
3250       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
3251     {
3252       if (impl->browse_files_model)
3253         _gtk_file_system_model_set_show_files (impl->browse_files_model, FALSE);
3254     }
3255   else
3256     {
3257       if (impl->browse_files_model)
3258         _gtk_file_system_model_set_show_files (impl->browse_files_model, TRUE);
3259     }
3260
3261   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
3262     gtk_widget_hide (impl->browse_new_folder_button);
3263   else
3264     gtk_widget_show (impl->browse_new_folder_button);
3265
3266   if (impl->extra_widget)
3267     {
3268       GtkWidget *align;
3269       GtkWidget *unused_align;
3270
3271       if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
3272           || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
3273         {
3274           align = impl->save_extra_align;
3275           unused_align = impl->browse_extra_align;
3276         }
3277       else
3278         {
3279           align = impl->browse_extra_align;
3280           unused_align = impl->save_extra_align;
3281         }
3282
3283       /* We own a ref on extra_widget, so it's safe to do this */
3284       child = GTK_BIN (unused_align)->child;
3285       if (child)
3286         gtk_container_remove (GTK_CONTAINER (unused_align), child);
3287
3288       child = GTK_BIN (align)->child;
3289       if (child && child != impl->extra_widget)
3290         {
3291           gtk_container_remove (GTK_CONTAINER (align), child);
3292           gtk_container_add (GTK_CONTAINER (align), impl->extra_widget);
3293         }
3294       else if (child == NULL)
3295         {
3296           gtk_container_add (GTK_CONTAINER (align), impl->extra_widget);
3297         }
3298
3299       gtk_widget_show (align);
3300       gtk_widget_hide (unused_align);
3301     }
3302   else
3303     {
3304       child = GTK_BIN (impl->browse_extra_align)->child;
3305       if (child)
3306         gtk_container_remove (GTK_CONTAINER (impl->browse_extra_align), child);
3307
3308       child = GTK_BIN (impl->save_extra_align)->child;
3309       if (child)
3310         gtk_container_remove (GTK_CONTAINER (impl->save_extra_align), child);
3311
3312       gtk_widget_hide (impl->save_extra_align);
3313       gtk_widget_hide (impl->browse_extra_align);
3314     }
3315
3316   g_signal_emit_by_name (impl, "default-size-changed");
3317 }
3318
3319 static void
3320 gtk_file_chooser_default_set_property (GObject      *object,
3321                                        guint         prop_id,
3322                                        const GValue *value,
3323                                        GParamSpec   *pspec)
3324
3325 {
3326   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
3327
3328   switch (prop_id)
3329     {
3330     case GTK_FILE_CHOOSER_PROP_ACTION:
3331       {
3332         GtkFileChooserAction action = g_value_get_enum (value);
3333
3334         if (action != impl->action)
3335           {
3336             if (action == GTK_FILE_CHOOSER_ACTION_SAVE && impl->select_multiple)
3337               {
3338                 g_warning ("Multiple selection mode is not allowed in Save mode");
3339                 set_select_multiple (impl, FALSE, TRUE);
3340               }
3341             impl->action = action;
3342             update_appearance (impl);
3343           }
3344       }
3345       break;
3346     case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
3347       set_file_system_backend (impl, g_value_get_string (value));
3348       break;
3349     case GTK_FILE_CHOOSER_PROP_FILTER:
3350       set_current_filter (impl, g_value_get_object (value));
3351       break;
3352     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
3353       set_local_only (impl, g_value_get_boolean (value));
3354       break;
3355     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
3356       set_preview_widget (impl, g_value_get_object (value));
3357       break;
3358     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
3359       impl->preview_widget_active = g_value_get_boolean (value);
3360       update_preview_widget_visibility (impl);
3361       break;
3362     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
3363       impl->use_preview_label = g_value_get_boolean (value);
3364       update_preview_widget_visibility (impl);
3365       break;
3366     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
3367       set_extra_widget (impl, g_value_get_object (value));
3368       update_appearance (impl);
3369       break;
3370     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
3371       {
3372         gboolean select_multiple = g_value_get_boolean (value);
3373         if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE && select_multiple)
3374           {
3375             g_warning ("Multiple selection mode is not allowed in Save mode");
3376             return;
3377           }
3378
3379         set_select_multiple (impl, select_multiple, FALSE);
3380       }
3381       break;
3382     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
3383       {
3384         gboolean show_hidden = g_value_get_boolean (value);
3385         if (show_hidden != impl->show_hidden)
3386           {
3387             impl->show_hidden = show_hidden;
3388             _gtk_file_system_model_set_show_hidden (impl->browse_files_model, show_hidden);
3389           }
3390       }
3391       break;
3392     default:
3393       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3394       break;
3395     }
3396 }
3397
3398 static void
3399 gtk_file_chooser_default_get_property (GObject    *object,
3400                                        guint       prop_id,
3401                                        GValue     *value,
3402                                        GParamSpec *pspec)
3403 {
3404   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
3405
3406   switch (prop_id)
3407     {
3408     case GTK_FILE_CHOOSER_PROP_ACTION:
3409       g_value_set_enum (value, impl->action);
3410       break;
3411     case GTK_FILE_CHOOSER_PROP_FILTER:
3412       g_value_set_object (value, impl->current_filter);
3413       break;
3414     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
3415       g_value_set_boolean (value, impl->local_only);
3416       break;
3417     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
3418       g_value_set_object (value, impl->preview_widget);
3419       break;
3420     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
3421       g_value_set_boolean (value, impl->preview_widget_active);
3422       break;
3423     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
3424       g_value_set_boolean (value, impl->use_preview_label);
3425       break;
3426     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
3427       g_value_set_object (value, impl->extra_widget);
3428       break;
3429     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
3430       g_value_set_boolean (value, impl->select_multiple);
3431       break;
3432     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
3433       g_value_set_boolean (value, impl->show_hidden);
3434       break;
3435     default:
3436       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3437       break;
3438     }
3439 }
3440
3441 /* Removes the settings signal handler.  It's safe to call multiple times */
3442 static void
3443 remove_settings_signal (GtkFileChooserDefault *impl,
3444                         GdkScreen             *screen)
3445 {
3446   if (impl->settings_signal_id)
3447     {
3448       GtkSettings *settings;
3449
3450       settings = gtk_settings_get_for_screen (screen);
3451       g_signal_handler_disconnect (settings,
3452                                    impl->settings_signal_id);
3453       impl->settings_signal_id = 0;
3454     }
3455 }
3456
3457 static void
3458 gtk_file_chooser_default_dispose (GObject *object)
3459 {
3460   GtkFileChooserDefault *impl = (GtkFileChooserDefault *) object;
3461
3462   if (impl->extra_widget)
3463     {
3464       g_object_unref (impl->extra_widget);
3465       impl->extra_widget = NULL;
3466     }
3467
3468   remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl)));
3469
3470   G_OBJECT_CLASS (parent_class)->dispose (object);
3471 }
3472
3473 /* We override show-all since we have internal widgets that
3474  * shouldn't be shown when you call show_all(), like the filter
3475  * combo box.
3476  */
3477 static void
3478 gtk_file_chooser_default_show_all (GtkWidget *widget)
3479 {
3480   GtkFileChooserDefault *impl = (GtkFileChooserDefault *) widget;
3481
3482   gtk_widget_show (widget);
3483
3484   if (impl->extra_widget)
3485     gtk_widget_show_all (impl->extra_widget);
3486 }
3487
3488 /* Changes the icons wherever it is needed */
3489 static void
3490 change_icon_theme (GtkFileChooserDefault *impl)
3491 {
3492   GtkSettings *settings;
3493   gint width, height;
3494
3495   settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
3496
3497   if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_SMALL_TOOLBAR, &width, &height))
3498     impl->icon_size = MAX (width, height);
3499   else
3500     impl->icon_size = FALLBACK_ICON_SIZE;
3501
3502   shortcuts_reload_icons (impl);
3503   gtk_widget_queue_resize (impl->browse_files_tree_view);
3504 }
3505
3506 /* Callback used when a GtkSettings value changes */
3507 static void
3508 settings_notify_cb (GObject               *object,
3509                     GParamSpec            *pspec,
3510                     GtkFileChooserDefault *impl)
3511 {
3512   const char *name;
3513
3514   name = g_param_spec_get_name (pspec);
3515
3516   if (strcmp (name, "gtk-icon-theme-name") == 0
3517       || strcmp (name, "gtk-icon-sizes") == 0)
3518     change_icon_theme (impl);
3519 }
3520
3521 /* Installs a signal handler for GtkSettings so that we can monitor changes in
3522  * the icon theme.
3523  */
3524 static void
3525 check_icon_theme (GtkFileChooserDefault *impl)
3526 {
3527   GtkSettings *settings;
3528
3529   if (impl->settings_signal_id)
3530     return;
3531
3532   if (gtk_widget_has_screen (GTK_WIDGET (impl)))
3533     {
3534       settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
3535       impl->settings_signal_id = g_signal_connect (settings, "notify",
3536                                                    G_CALLBACK (settings_notify_cb), impl);
3537
3538       change_icon_theme (impl);
3539     }
3540 }
3541
3542 static void
3543 gtk_file_chooser_default_style_set      (GtkWidget *widget,
3544                                          GtkStyle  *previous_style)
3545 {
3546   GtkFileChooserDefault *impl;
3547
3548   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
3549
3550   if (GTK_WIDGET_CLASS (parent_class)->style_set)
3551     GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
3552
3553   check_icon_theme (impl);
3554
3555   g_signal_emit_by_name (widget, "default-size-changed");
3556 }
3557
3558 static void
3559 gtk_file_chooser_default_screen_changed (GtkWidget *widget,
3560                                          GdkScreen *previous_screen)
3561 {
3562   GtkFileChooserDefault *impl;
3563
3564   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
3565
3566   if (GTK_WIDGET_CLASS (parent_class)->screen_changed)
3567     GTK_WIDGET_CLASS (parent_class)->screen_changed (widget, previous_screen);
3568
3569   remove_settings_signal (impl, previous_screen);
3570   check_icon_theme (impl);
3571
3572   g_signal_emit_by_name (widget, "default-size-changed");
3573 }
3574
3575 static gboolean
3576 list_model_filter_func (GtkFileSystemModel *model,
3577                         GtkFilePath        *path,
3578                         const GtkFileInfo  *file_info,
3579                         gpointer            user_data)
3580 {
3581   GtkFileChooserDefault *impl = user_data;
3582   GtkFileFilterInfo filter_info;
3583   GtkFileFilterFlags needed;
3584   gboolean result;
3585
3586   if (!impl->current_filter)
3587     return TRUE;
3588
3589   if (gtk_file_info_get_is_folder (file_info))
3590     return TRUE;
3591
3592   filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
3593
3594   needed = gtk_file_filter_get_needed (impl->current_filter);
3595
3596   filter_info.display_name = gtk_file_info_get_display_name (file_info);
3597   filter_info.mime_type = gtk_file_info_get_mime_type (file_info);
3598
3599   if (needed & GTK_FILE_FILTER_FILENAME)
3600     {
3601       filter_info.filename = gtk_file_system_path_to_filename (impl->file_system, path);
3602       if (filter_info.filename)
3603         filter_info.contains |= GTK_FILE_FILTER_FILENAME;
3604     }
3605   else
3606     filter_info.filename = NULL;
3607
3608   if (needed & GTK_FILE_FILTER_URI)
3609     {
3610       filter_info.uri = gtk_file_system_path_to_uri (impl->file_system, path);
3611       if (filter_info.uri)
3612         filter_info.contains |= GTK_FILE_FILTER_URI;
3613     }
3614   else
3615     filter_info.uri = NULL;
3616
3617   result = gtk_file_filter_filter (impl->current_filter, &filter_info);
3618
3619   if (filter_info.filename)
3620     g_free ((gchar *)filter_info.filename);
3621   if (filter_info.uri)
3622     g_free ((gchar *)filter_info.uri);
3623
3624   return result;
3625 }
3626
3627 static void
3628 install_list_model_filter (GtkFileChooserDefault *impl)
3629 {
3630   if (impl->current_filter)
3631     _gtk_file_system_model_set_filter (impl->browse_files_model,
3632                                        list_model_filter_func,
3633                                        impl);
3634 }
3635
3636 #define COMPARE_DIRECTORIES                                                                                    \
3637   GtkFileChooserDefault *impl = user_data;                                                                     \
3638   const GtkFileInfo *info_a = _gtk_file_system_model_get_info (impl->browse_files_model, a);                           \
3639   const GtkFileInfo *info_b = _gtk_file_system_model_get_info (impl->browse_files_model, b);                           \
3640   gboolean dir_a, dir_b;                                                                                       \
3641                                                                                                                \
3642   if (info_a)                                                                                                  \
3643     dir_a = gtk_file_info_get_is_folder (info_a);                                                              \
3644   else                                                                                                         \
3645     return impl->list_sort_ascending ? -1 : 1;                                                                 \
3646                                                                                                                \
3647   if (info_b)                                                                                                  \
3648     dir_b = gtk_file_info_get_is_folder (info_b);                                                              \
3649   else                                                                                                         \
3650     return impl->list_sort_ascending ? 1 : -1;                                                                 \
3651                                                                                                                \
3652   if (dir_a != dir_b)                                                                                          \
3653     return impl->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */
3654
3655 /* Sort callback for the filename column */
3656 static gint
3657 name_sort_func (GtkTreeModel *model,
3658                 GtkTreeIter  *a,
3659                 GtkTreeIter  *b,
3660                 gpointer      user_data)
3661 {
3662   COMPARE_DIRECTORIES;
3663   else
3664     return strcmp (gtk_file_info_get_display_key (info_a), gtk_file_info_get_display_key (info_b));
3665 }
3666
3667 /* Sort callback for the size column */
3668 static gint
3669 size_sort_func (GtkTreeModel *model,
3670                 GtkTreeIter  *a,
3671                 GtkTreeIter  *b,
3672                 gpointer      user_data)
3673 {
3674   COMPARE_DIRECTORIES;
3675   else
3676     {
3677       gint64 size_a = gtk_file_info_get_size (info_a);
3678       gint64 size_b = gtk_file_info_get_size (info_b);
3679
3680       return size_a > size_b ? -1 : (size_a == size_b ? 0 : 1);
3681     }
3682 }
3683
3684 /* Sort callback for the mtime column */
3685 static gint
3686 mtime_sort_func (GtkTreeModel *model,
3687                  GtkTreeIter  *a,
3688                  GtkTreeIter  *b,
3689                  gpointer      user_data)
3690 {
3691   COMPARE_DIRECTORIES;
3692   else
3693     {
3694       GtkFileTime ta = gtk_file_info_get_modification_time (info_a);
3695       GtkFileTime tb = gtk_file_info_get_modification_time (info_b);
3696
3697       return ta > tb ? -1 : (ta == tb ? 0 : 1);
3698     }
3699 }
3700
3701 /* Callback used when the sort column changes.  We cache the sort order for use
3702  * in name_sort_func().
3703  */
3704 static void
3705 list_sort_column_changed_cb (GtkTreeSortable       *sortable,
3706                              GtkFileChooserDefault *impl)
3707 {
3708   GtkSortType sort_type;
3709
3710   if (gtk_tree_sortable_get_sort_column_id (sortable, NULL, &sort_type))
3711     impl->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
3712 }
3713
3714 static void
3715 set_busy_cursor (GtkFileChooserDefault *impl,
3716                  gboolean               busy)
3717 {
3718   GtkWindow *toplevel;
3719   GdkDisplay *display;
3720   GdkCursor *cursor;
3721
3722   toplevel = get_toplevel (GTK_WIDGET (impl));
3723   if (!toplevel || !GTK_WIDGET_REALIZED (toplevel))
3724     return;
3725
3726   display = gtk_widget_get_display (GTK_WIDGET (toplevel));
3727
3728   if (busy)
3729     cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
3730   else
3731     cursor = NULL;
3732
3733   gdk_window_set_cursor (GTK_WIDGET (toplevel)->window, cursor);
3734   gdk_display_flush (display);
3735
3736   if (cursor)
3737     gdk_cursor_unref (cursor);
3738 }
3739
3740 /* Callback used when the file system model finishes loading */
3741 static void
3742 browse_files_model_finished_loading_cb (GtkFileSystemModel    *model,
3743                                         GtkFileChooserDefault *impl)
3744 {
3745   set_busy_cursor (impl, FALSE);
3746 }
3747
3748 /* Gets rid of the old list model and creates a new one for the current folder */
3749 static void
3750 set_list_model (GtkFileChooserDefault *impl)
3751 {
3752   if (impl->browse_files_model)
3753     {
3754       g_object_unref (impl->browse_files_model);
3755       g_object_unref (impl->sort_model);
3756     }
3757
3758   set_busy_cursor (impl, TRUE);
3759
3760   impl->browse_files_model = _gtk_file_system_model_new (impl->file_system,
3761                                                          impl->current_folder, 0,
3762                                                          GTK_FILE_INFO_ALL);
3763   g_signal_connect (impl->browse_files_model, "finished-loading",
3764                     G_CALLBACK (browse_files_model_finished_loading_cb), impl);
3765
3766   _gtk_file_system_model_set_show_hidden (impl->browse_files_model, impl->show_hidden);
3767   switch (impl->action)
3768     {
3769     case GTK_FILE_CHOOSER_ACTION_OPEN:
3770     case GTK_FILE_CHOOSER_ACTION_SAVE:
3771       _gtk_file_system_model_set_show_files (impl->browse_files_model, TRUE);
3772       break;
3773     case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
3774     case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
3775       _gtk_file_system_model_set_show_files (impl->browse_files_model, FALSE);
3776       break;
3777     default:
3778       g_assert_not_reached ();
3779     }
3780   install_list_model_filter (impl);
3781
3782   impl->sort_model = (GtkTreeModelSort *)gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (impl->browse_files_model));
3783   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, name_sort_func, impl, NULL);
3784   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_SIZE, size_sort_func, impl, NULL);
3785   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_MTIME, mtime_sort_func, impl, NULL);
3786   gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->sort_model), NULL, NULL, NULL);
3787   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, GTK_SORT_ASCENDING);
3788   impl->list_sort_ascending = TRUE;
3789
3790   g_signal_connect (impl->sort_model, "sort-column-changed",
3791                     G_CALLBACK (list_sort_column_changed_cb), impl);
3792
3793   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
3794                            GTK_TREE_MODEL (impl->sort_model));
3795   gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->browse_files_tree_view));
3796   gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_files_tree_view),
3797                                    GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
3798 }
3799
3800 static void
3801 update_chooser_entry (GtkFileChooserDefault *impl)
3802 {
3803   GtkTreeSelection *selection;
3804   const GtkFileInfo *info;
3805   GtkTreeIter iter;
3806   GtkTreeIter child_iter;
3807
3808   if (impl->action != GTK_FILE_CHOOSER_ACTION_SAVE)
3809     return;
3810
3811   g_assert (!impl->select_multiple);
3812   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3813
3814   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
3815     return;
3816
3817   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
3818                                                   &child_iter,
3819                                                   &iter);
3820
3821   info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
3822
3823   if (!gtk_file_info_get_is_folder (info))
3824     gtk_entry_set_text (GTK_ENTRY (impl->save_file_name_entry),
3825                         gtk_file_info_get_display_name (info));
3826 }
3827
3828 static gboolean
3829 gtk_file_chooser_default_set_current_folder (GtkFileChooser    *chooser,
3830                                              const GtkFilePath *path,
3831                                              GError           **error)
3832 {
3833   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3834
3835   if (impl->local_only &&
3836       !gtk_file_system_path_is_local (impl->file_system, path))
3837     {
3838       g_set_error (error,
3839                    GTK_FILE_SYSTEM_ERROR,
3840                    GTK_FILE_SYSTEM_ERROR_FAILED,
3841                    _("Can't change to folder because it isn't local"));
3842
3843       return FALSE;
3844     }
3845
3846   /* Test validity of path here.  */
3847   if (!check_is_folder (impl->file_system, path, error))
3848     return FALSE;
3849
3850   if (!_gtk_path_bar_set_path (GTK_PATH_BAR (impl->browse_path_bar), path, error))
3851     return FALSE;
3852
3853   if (impl->current_folder != path)
3854     {
3855       if (impl->current_folder)
3856         gtk_file_path_free (impl->current_folder);
3857
3858       impl->current_folder = gtk_file_path_copy (path);
3859     }
3860
3861   /* Update the widgets that may trigger a folder change themselves.  */
3862
3863   if (!impl->changing_folder)
3864     {
3865       impl->changing_folder = TRUE;
3866
3867       shortcuts_update_current_folder (impl);
3868
3869       impl->changing_folder = FALSE;
3870     }
3871
3872   /* Create a new list model */
3873   set_list_model (impl);
3874
3875   /* Refresh controls */
3876
3877   shortcuts_find_current_folder (impl);
3878
3879   g_signal_emit_by_name (impl, "current-folder-changed", 0);
3880
3881   check_preview_change (impl);
3882   bookmarks_check_add_sensitivity (impl);
3883
3884   g_signal_emit_by_name (impl, "selection-changed", 0);
3885
3886   return TRUE;
3887 }
3888
3889 static GtkFilePath *
3890 gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser)
3891 {
3892   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3893
3894   return gtk_file_path_copy (impl->current_folder);
3895 }
3896
3897 static void
3898 gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
3899                                            const gchar    *name)
3900 {
3901   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3902
3903   g_return_if_fail (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
3904                     || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
3905
3906   gtk_entry_set_text (GTK_ENTRY (impl->save_file_name_entry), name);
3907 }
3908
3909 static void
3910 select_func (GtkFileSystemModel *model,
3911              GtkTreePath        *path,
3912              GtkTreeIter        *iter,
3913              gpointer            user_data)
3914 {
3915   GtkFileChooserDefault *impl = user_data;
3916   GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
3917   GtkTreePath *sorted_path;
3918
3919   sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model, path);
3920   gtk_tree_view_set_cursor (tree_view, sorted_path, NULL, FALSE);
3921   gtk_tree_path_free (sorted_path);
3922 }
3923
3924 static gboolean
3925 gtk_file_chooser_default_select_path (GtkFileChooser    *chooser,
3926                                       const GtkFilePath *path,
3927                                       GError           **error)
3928 {
3929   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3930   GtkFilePath *parent_path;
3931
3932   if (!gtk_file_system_get_parent (impl->file_system, path, &parent_path, error))
3933     return FALSE;
3934
3935   if (!parent_path)
3936     return _gtk_file_chooser_set_current_folder_path (chooser, path, error);
3937   else
3938     {
3939       gboolean result;
3940       GtkFileFolder *folder;
3941       GtkFileInfo *info;
3942       gboolean is_hidden;
3943
3944       result = _gtk_file_chooser_set_current_folder_path (chooser, parent_path, error);
3945
3946       if (!result)
3947         {
3948           gtk_file_path_free (parent_path);
3949           return result;
3950         }
3951
3952       folder = gtk_file_system_get_folder (impl->file_system, parent_path, GTK_FILE_INFO_IS_HIDDEN, error);
3953       gtk_file_path_free (parent_path);
3954
3955       if (!folder)
3956         return FALSE;
3957
3958       info = gtk_file_folder_get_info (folder, path, error);
3959       g_object_unref (folder);
3960
3961       if (!info)
3962         return FALSE;
3963
3964       is_hidden = gtk_file_info_get_is_hidden (info);
3965       gtk_file_info_free (info);
3966
3967       if (is_hidden)
3968         g_object_set (impl, "show-hidden", TRUE, NULL);
3969
3970       result = _gtk_file_system_model_path_do (impl->browse_files_model, path,
3971                                                select_func, impl);
3972       if (!result)
3973         g_set_error (error,
3974                      GTK_FILE_CHOOSER_ERROR,
3975                      GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
3976                      _("Could not find the path"));
3977
3978       return result;
3979     }
3980
3981   g_assert_not_reached ();
3982 }
3983
3984 static void
3985 unselect_func (GtkFileSystemModel *model,
3986                GtkTreePath        *path,
3987                GtkTreeIter        *iter,
3988                gpointer            user_data)
3989 {
3990   GtkFileChooserDefault *impl = user_data;
3991   GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
3992   GtkTreePath *sorted_path;
3993
3994   sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model,
3995                                                                 path);
3996   gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (tree_view),
3997                                     sorted_path);
3998   gtk_tree_path_free (sorted_path);
3999 }
4000
4001 static void
4002 gtk_file_chooser_default_unselect_path (GtkFileChooser    *chooser,
4003                                         const GtkFilePath *path)
4004 {
4005   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4006
4007   _gtk_file_system_model_path_do (impl->browse_files_model, path,
4008                                   unselect_func, impl);
4009 }
4010
4011 static void
4012 gtk_file_chooser_default_select_all (GtkFileChooser *chooser)
4013 {
4014   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4015   if (impl->select_multiple)
4016     {
4017       GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4018       gtk_tree_selection_select_all (selection);
4019     }
4020 }
4021
4022 static void
4023 gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser)
4024 {
4025   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4026   GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4027
4028   gtk_tree_selection_unselect_all (selection);
4029 }
4030
4031 /* Checks whether the filename entry for the Save modes contains a valid filename */
4032 static GtkFilePath *
4033 check_save_entry (GtkFileChooserDefault *impl,
4034                   gboolean              *is_valid,
4035                   gboolean              *is_empty)
4036 {
4037   const char *filename;
4038   GtkFilePath *path;
4039   GError *error;
4040
4041   g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
4042             || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
4043
4044   filename = gtk_entry_get_text (GTK_ENTRY (impl->save_file_name_entry));
4045
4046   if (!filename || filename[0] == '\0')
4047     {
4048       *is_valid = FALSE;
4049       *is_empty = TRUE;
4050       return NULL;
4051     }
4052
4053   *is_empty = FALSE;
4054
4055   error = NULL;
4056   path = gtk_file_system_make_path (impl->file_system, impl->current_folder, filename, &error);
4057
4058   if (!path)
4059     {
4060       error_building_filename_dialog (impl, impl->current_folder, filename, error);
4061       *is_valid = FALSE;
4062       return NULL;
4063     }
4064
4065   *is_valid = TRUE;
4066   return path;
4067 }
4068
4069 struct get_paths_closure {
4070   GtkFileChooserDefault *impl;
4071   GSList *result;
4072   GtkFilePath *path_from_entry;
4073 };
4074
4075 static void
4076 get_paths_foreach (GtkTreeModel *model,
4077                    GtkTreePath  *path,
4078                    GtkTreeIter  *iter,
4079                    gpointer      data)
4080 {
4081   struct get_paths_closure *info;
4082   const GtkFilePath *file_path;
4083   GtkFileSystemModel *fs_model;
4084   GtkTreeIter sel_iter;
4085
4086   info = data;
4087   fs_model = info->impl->browse_files_model;
4088   gtk_tree_model_sort_convert_iter_to_child_iter (info->impl->sort_model, &sel_iter, iter);
4089
4090   file_path = _gtk_file_system_model_get_path (fs_model, &sel_iter);
4091   if (!file_path)
4092     return; /* We are on the editable row */
4093
4094   if (!info->path_from_entry
4095       || gtk_file_path_compare (info->path_from_entry, file_path) != 0)
4096     info->result = g_slist_prepend (info->result, gtk_file_path_copy (file_path));
4097 }
4098
4099 static GSList *
4100 gtk_file_chooser_default_get_paths (GtkFileChooser *chooser)
4101 {
4102   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4103   struct get_paths_closure info;
4104
4105   info.impl = impl;
4106   info.result = NULL;
4107   info.path_from_entry = NULL;
4108
4109   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
4110       || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4111     {
4112       gboolean is_valid, is_empty;
4113
4114       info.path_from_entry = check_save_entry (impl, &is_valid, &is_empty);
4115       if (!is_valid && !is_empty)
4116         return NULL;
4117     }
4118
4119   if (!info.path_from_entry || impl->select_multiple)
4120     {
4121       GtkTreeSelection *selection;
4122
4123       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4124       gtk_tree_selection_selected_foreach (selection, get_paths_foreach, &info);
4125     }
4126
4127   if (info.path_from_entry)
4128     info.result = g_slist_prepend (info.result, info.path_from_entry);
4129
4130   /* If there's no folder selected, and we're in SELECT_FOLDER mode, then we
4131    * fall back to the current directory */
4132   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
4133       info.result == NULL)
4134     {
4135       info.result = g_slist_prepend (info.result, gtk_file_path_copy (impl->current_folder));
4136     }
4137
4138   return g_slist_reverse (info.result);
4139 }
4140
4141 static GtkFilePath *
4142 gtk_file_chooser_default_get_preview_path (GtkFileChooser *chooser)
4143 {
4144   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4145
4146   if (impl->preview_path)
4147     return gtk_file_path_copy (impl->preview_path);
4148   else
4149     return NULL;
4150 }
4151
4152 static GtkFileSystem *
4153 gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser)
4154 {
4155   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4156
4157   return impl->file_system;
4158 }
4159
4160 /* Shows or hides the filter widgets */
4161 static void
4162 toolbar_show_filters (GtkFileChooserDefault *impl,
4163                       gboolean               show)
4164 {
4165   if (show)
4166     gtk_widget_show (impl->filter_combo);
4167   else
4168     gtk_widget_hide (impl->filter_combo);
4169 }
4170
4171 static void
4172 gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
4173                                      GtkFileFilter  *filter)
4174 {
4175   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4176   const gchar *name;
4177
4178   if (g_slist_find (impl->filters, filter))
4179     {
4180       g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
4181       return;
4182     }
4183
4184   g_object_ref (filter);
4185   gtk_object_sink (GTK_OBJECT (filter));
4186   impl->filters = g_slist_append (impl->filters, filter);
4187
4188   name = gtk_file_filter_get_name (filter);
4189   if (!name)
4190     name = "Untitled filter";   /* Place-holder, doesn't need to be marked for translation */
4191
4192   gtk_combo_box_append_text (GTK_COMBO_BOX (impl->filter_combo), name);
4193
4194   if (!g_slist_find (impl->filters, impl->current_filter))
4195     set_current_filter (impl, filter);
4196
4197   toolbar_show_filters (impl, TRUE);
4198 }
4199
4200 static void
4201 gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
4202                                         GtkFileFilter  *filter)
4203 {
4204   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4205   GtkTreeModel *model;
4206   GtkTreeIter iter;
4207   gint filter_index;
4208
4209   filter_index = g_slist_index (impl->filters, filter);
4210
4211   if (filter_index < 0)
4212     {
4213       g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
4214       return;
4215     }
4216
4217   impl->filters = g_slist_remove (impl->filters, filter);
4218
4219   if (filter == impl->current_filter)
4220     {
4221       if (impl->filters)
4222         set_current_filter (impl, impl->filters->data);
4223       else
4224         set_current_filter (impl, NULL);
4225     }
4226
4227   /* Remove row from the combo box */
4228   model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->filter_combo));
4229   gtk_tree_model_iter_nth_child  (model, &iter, NULL, filter_index);
4230   gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
4231
4232   g_object_unref (filter);
4233
4234   if (!impl->filters)
4235     toolbar_show_filters (impl, FALSE);
4236 }
4237
4238 static GSList *
4239 gtk_file_chooser_default_list_filters (GtkFileChooser *chooser)
4240 {
4241   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4242
4243   return g_slist_copy (impl->filters);
4244 }
4245
4246 /* Returns the position in the shortcuts tree where the nth specified shortcut would appear */
4247 static int
4248 shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl,
4249                                        int                    pos)
4250 {
4251   return pos + shortcuts_get_index (impl, SHORTCUTS_SHORTCUTS);
4252 }
4253
4254 static gboolean
4255 gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser    *chooser,
4256                                               const GtkFilePath *path,
4257                                               GError           **error)
4258 {
4259   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4260   gboolean result;
4261   int pos;
4262
4263   /* Test validity of path here.  */
4264   if (!check_is_folder (impl->file_system, path, error))
4265     return FALSE;
4266
4267   pos = shortcuts_get_pos_for_shortcut_folder (impl, impl->num_shortcuts);
4268
4269   result = shortcuts_insert_path (impl, pos, FALSE, NULL, path, NULL, FALSE, error);
4270
4271   if (result)
4272     impl->num_shortcuts++;
4273
4274   if (impl->shortcuts_filter_model)
4275     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
4276
4277   return result;
4278 }
4279
4280 static gboolean
4281 gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
4282                                                  const GtkFilePath *path,
4283                                                  GError           **error)
4284 {
4285   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4286   int pos;
4287   GtkTreeIter iter;
4288   char *uri;
4289   int i;
4290
4291   if (impl->num_shortcuts == 0)
4292     goto out;
4293
4294   pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
4295   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
4296     g_assert_not_reached ();
4297
4298   for (i = 0; i < impl->num_shortcuts; i++)
4299     {
4300       GtkFilePath *shortcut;
4301
4302       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &shortcut, -1);
4303       g_assert (shortcut != NULL);
4304
4305       if (gtk_file_path_compare (shortcut, path) == 0)
4306         {
4307           /* The other columns are freed by the GtkTreeStore */
4308           gtk_file_path_free (shortcut);
4309           gtk_list_store_remove (impl->shortcuts_model, &iter);
4310           impl->num_shortcuts--;
4311           return TRUE;
4312         }
4313
4314       if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
4315         g_assert_not_reached ();
4316     }
4317
4318  out:
4319
4320   uri = gtk_file_system_path_to_uri (impl->file_system, path);
4321   g_set_error (error,
4322                GTK_FILE_CHOOSER_ERROR,
4323                GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
4324                _("shortcut %s does not exist"),
4325                uri);
4326   g_free (uri);
4327
4328   return FALSE;
4329 }
4330
4331 static GSList *
4332 gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser)
4333 {
4334   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4335   int pos;
4336   GtkTreeIter iter;
4337   int i;
4338   GSList *list;
4339
4340   if (impl->num_shortcuts == 0)
4341     return NULL;
4342
4343   pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
4344   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
4345     g_assert_not_reached ();
4346
4347   list = NULL;
4348
4349   for (i = 0; i < impl->num_shortcuts; i++)
4350     {
4351       GtkFilePath *shortcut;
4352
4353       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &shortcut, -1);
4354       g_assert (shortcut != NULL);
4355
4356       list = g_slist_prepend (list, gtk_file_path_copy (shortcut));
4357
4358       if (i != impl->num_shortcuts - 1)
4359         {
4360           if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
4361             g_assert_not_reached ();
4362         }
4363     }
4364
4365   return g_slist_reverse (list);
4366 }
4367
4368 /* Guesses a size based upon font sizes */
4369 static void
4370 find_good_size_from_style (GtkWidget *widget,
4371                            gint      *width,
4372                            gint      *height)
4373 {
4374   GtkFileChooserDefault *impl;
4375   gint default_width, default_height;
4376   int font_size;
4377   GtkRequisition req;
4378   GtkRequisition preview_req;
4379
4380   g_assert (widget->style != NULL);
4381   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
4382
4383   font_size = pango_font_description_get_size (widget->style->font_desc);
4384   font_size = PANGO_PIXELS (font_size);
4385
4386   default_width = font_size * NUM_CHARS;
4387   default_height = font_size * NUM_LINES;
4388
4389   /* Use at least the requisition size not including the preview widget */
4390   gtk_widget_size_request (widget, &req);
4391
4392   if (impl->preview_widget_active && impl->preview_widget)
4393     gtk_widget_size_request (impl->preview_box, &preview_req);
4394   else
4395     preview_req.width = 0;
4396
4397   default_width = MAX (default_width, (req.width - (preview_req.width + PREVIEW_HBOX_SPACING)));
4398   default_height = MAX (default_height, req.height);
4399
4400   *width = default_width;
4401   *height = default_height;
4402 }
4403
4404 static void
4405 gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
4406                                            gint                *default_width,
4407                                            gint                *default_height)
4408 {
4409   GtkFileChooserDefault *impl;
4410
4411   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
4412
4413   find_good_size_from_style (GTK_WIDGET (chooser_embed), default_width, default_height);
4414
4415   if (impl->preview_widget_active && impl->preview_widget)
4416     *default_width += impl->preview_box->requisition.width + PREVIEW_HBOX_SPACING;
4417 }
4418
4419 static void
4420 gtk_file_chooser_default_get_resizable_hints (GtkFileChooserEmbed *chooser_embed,
4421                                               gboolean            *resize_horizontally,
4422                                               gboolean            *resize_vertically)
4423 {
4424   GtkFileChooserDefault *impl;
4425
4426   g_return_if_fail (resize_horizontally != NULL);
4427   g_return_if_fail (resize_vertically != NULL);
4428
4429   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
4430
4431   *resize_horizontally = TRUE;
4432   *resize_vertically = TRUE;
4433
4434   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
4435       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4436     {
4437       if (! gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
4438         {
4439           *resize_horizontally = FALSE;
4440           *resize_vertically = FALSE;
4441         }
4442     }
4443 }
4444
4445 struct switch_folder_closure {
4446   GtkFileChooserDefault *impl;
4447   const GtkFilePath *path;
4448   int num_selected;
4449 };
4450
4451 /* Used from gtk_tree_selection_selected_foreach() in switch_to_selected_folder() */
4452 static void
4453 switch_folder_foreach_cb (GtkTreeModel      *model,
4454                           GtkTreePath       *path,
4455                           GtkTreeIter       *iter,
4456                           gpointer           data)
4457 {
4458   struct switch_folder_closure *closure;
4459   GtkTreeIter child_iter;
4460
4461   closure = data;
4462
4463   gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
4464
4465   closure->path = _gtk_file_system_model_get_path (closure->impl->browse_files_model, &child_iter);
4466   closure->num_selected++;
4467 }
4468
4469 /* Changes to the selected folder in the list view */
4470 static void
4471 switch_to_selected_folder (GtkFileChooserDefault *impl)
4472 {
4473   GtkTreeSelection *selection;
4474   struct switch_folder_closure closure;
4475
4476   g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
4477             || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE);
4478
4479   /* We do this with foreach() rather than get_selected() as we may be in
4480    * multiple selection mode
4481    */
4482
4483   closure.impl = impl;
4484   closure.path = NULL;
4485   closure.num_selected = 0;
4486
4487   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4488   gtk_tree_selection_selected_foreach (selection, switch_folder_foreach_cb, &closure);
4489
4490   g_assert (closure.path && closure.num_selected == 1);
4491
4492   change_folder_and_display_error (impl, closure.path);
4493 }
4494
4495 /* Implementation for GtkFileChooserEmbed::should_respond() */
4496 static gboolean
4497 gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
4498 {
4499   GtkFileChooserDefault *impl;
4500   GtkTreeSelection *selection;
4501   int num_selected;
4502
4503   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
4504
4505   /* First, check the save entry.  If it has a valid name, we are done */
4506
4507   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
4508       || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4509     {
4510       GtkFilePath *path;
4511       gboolean is_valid, is_empty;
4512
4513       path = check_save_entry (impl, &is_valid, &is_empty);
4514
4515       if (is_valid)
4516         {
4517           gtk_file_path_free (path);
4518           return TRUE;
4519         }
4520       else if (!is_empty)
4521         return FALSE;
4522     }
4523
4524   /* Second, do we have an empty selection */
4525   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
4526       || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
4527     {
4528       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4529       num_selected = gtk_tree_selection_count_selected_rows (selection);
4530       if (num_selected == 0)
4531         return FALSE;
4532     }
4533
4534   /* Third, should we return file names or folder names? */
4535
4536   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
4537       || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
4538     {
4539       gboolean all_files, all_folders;
4540
4541       selection_check (impl, NULL, &all_files, &all_folders);
4542
4543       if (num_selected == 1)
4544         {
4545           if (all_folders)
4546             {
4547               switch_to_selected_folder (impl);
4548               return FALSE;
4549             }
4550           else if (all_files)
4551             return TRUE;
4552         }
4553       else
4554         return all_files;
4555     }
4556   else if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
4557            || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4558     /* There can be no files selected in folder mode since we don't show them,
4559      * anyway.
4560      */
4561     return TRUE;
4562
4563   g_assert_not_reached ();
4564   return FALSE;
4565 }
4566
4567 /* Implementation for GtkFileChooserEmbed::initial_focus() */
4568 static void
4569 gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed)
4570 {
4571   GtkFileChooserDefault *impl;
4572   GtkWidget *widget;
4573
4574   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
4575
4576   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
4577       || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
4578     {
4579       GtkTreePath *path;
4580
4581       path = gtk_tree_path_new_from_indices (0, -1);
4582       gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), path, NULL, FALSE);
4583       gtk_tree_path_free (path);
4584
4585       widget = impl->browse_files_tree_view;
4586     }
4587   else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
4588            || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4589     widget = impl->save_file_name_entry;
4590   else
4591     {
4592       g_assert_not_reached ();
4593       widget = NULL;
4594     }
4595
4596   gtk_widget_grab_focus (widget);
4597 }
4598
4599 static void
4600 set_current_filter (GtkFileChooserDefault *impl,
4601                     GtkFileFilter         *filter)
4602 {
4603   if (impl->current_filter != filter)
4604     {
4605       int filter_index;
4606
4607       /* If we have filters, new filter must be one of them
4608        */
4609       filter_index = g_slist_index (impl->filters, filter);
4610       if (impl->filters && filter_index < 0)
4611         return;
4612
4613       if (impl->current_filter)
4614         g_object_unref (impl->current_filter);
4615       impl->current_filter = filter;
4616       if (impl->current_filter)
4617         {
4618           g_object_ref (impl->current_filter);
4619           gtk_object_sink (GTK_OBJECT (filter));
4620         }
4621
4622       if (impl->filters)
4623         gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
4624                                   filter_index);
4625
4626       install_list_model_filter (impl);
4627
4628       g_object_notify (G_OBJECT (impl), "filter");
4629     }
4630 }
4631
4632 static void
4633 filter_combo_changed (GtkComboBox           *combo_box,
4634                       GtkFileChooserDefault *impl)
4635 {
4636   gint new_index = gtk_combo_box_get_active (combo_box);
4637   GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
4638
4639   set_current_filter (impl, new_filter);
4640 }
4641
4642 static void
4643 check_preview_change (GtkFileChooserDefault *impl)
4644 {
4645   GtkTreePath *cursor_path;
4646   const GtkFilePath *new_path;
4647   const GtkFileInfo *new_info;
4648
4649   gtk_tree_view_get_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), &cursor_path, NULL);
4650   if (cursor_path)
4651     {
4652       GtkTreeIter iter;
4653       GtkTreeIter child_iter;
4654
4655       gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, cursor_path);
4656       gtk_tree_path_free (cursor_path);
4657
4658       gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
4659
4660       new_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
4661       new_info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
4662     }
4663   else
4664     {
4665       new_path = NULL;
4666       new_info = NULL;
4667     }
4668
4669   if (new_path != impl->preview_path &&
4670       !(new_path && impl->preview_path &&
4671         gtk_file_path_compare (new_path, impl->preview_path) == 0))
4672     {
4673       if (impl->preview_path)
4674         {
4675           gtk_file_path_free (impl->preview_path);
4676           g_free (impl->preview_display_name);
4677         }
4678
4679       if (new_path)
4680         {
4681           impl->preview_path = gtk_file_path_copy (new_path);
4682           impl->preview_display_name = g_strdup (gtk_file_info_get_display_name (new_info));
4683         }
4684       else
4685         {
4686           impl->preview_path = NULL;
4687           impl->preview_display_name = NULL;
4688         }
4689
4690       if (impl->use_preview_label && impl->preview_label)
4691         gtk_label_set_text (GTK_LABEL (impl->preview_label), impl->preview_display_name);
4692
4693       g_signal_emit_by_name (impl, "update-preview");
4694     }
4695 }
4696
4697 /* Activates a volume by mounting it if necessary and then switching to its
4698  * base path.
4699  */
4700 static void
4701 shortcuts_activate_volume (GtkFileChooserDefault *impl,
4702                            GtkFileSystemVolume   *volume)
4703 {
4704   GtkFilePath *path;
4705
4706   if (!gtk_file_system_volume_get_is_mounted (impl->file_system, volume))
4707     {
4708       GError *error;
4709
4710       error = NULL;
4711       if (!gtk_file_system_volume_mount (impl->file_system, volume, &error))
4712         {
4713           char *msg;
4714
4715           msg = g_strdup_printf ("Could not mount %s:\n%s",
4716                                  gtk_file_system_volume_get_display_name (impl->file_system, volume),
4717                                  error->message);
4718           error_message (impl, msg);
4719           g_free (msg);
4720           g_error_free (error);
4721
4722           return;
4723         }
4724     }
4725
4726   path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
4727   change_folder_and_display_error (impl, path);
4728   gtk_file_path_free (path);
4729 }
4730
4731 /* Opens the folder or volume at the specified index in the shortcuts list */
4732 static void
4733 shortcuts_activate_item (GtkFileChooserDefault *impl,
4734                          int                    item_num)
4735 {
4736   GtkTreePath *path;
4737   gboolean result;
4738   GtkTreeIter iter;
4739   gpointer data;
4740   int start_row;
4741
4742   if (item_num == shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR)
4743       || item_num == shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR))
4744     return;
4745
4746   path = gtk_tree_path_new_from_indices (item_num, -1);
4747   result = gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path);
4748   gtk_tree_path_free (path);
4749
4750   if (!result)
4751     return;
4752
4753   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &data, -1);
4754
4755   start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
4756   if ((item_num >= start_row && item_num < start_row + impl->num_volumes)
4757       || (item_num == shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER) && impl->shortcuts_current_folder_is_volume))
4758     {
4759       GtkFileSystemVolume *volume;
4760
4761       volume = data;
4762       shortcuts_activate_volume (impl, volume);
4763     }
4764   else
4765     {
4766       const GtkFilePath *file_path;
4767
4768       file_path = data;
4769       change_folder_and_display_error (impl, file_path);
4770     }
4771 }
4772
4773 /* Callback used when a row in the shortcuts list is activated */
4774 static void
4775 shortcuts_row_activated_cb (GtkTreeView           *tree_view,
4776                             GtkTreePath           *path,
4777                             GtkTreeViewColumn     *column,
4778                             GtkFileChooserDefault *impl)
4779 {
4780   int selected;
4781   GtkTreeIter iter;
4782   GtkTreeIter child_iter;
4783   GtkTreePath *child_path;
4784
4785   if (!gtk_tree_model_get_iter (impl->shortcuts_filter_model, &iter, path))
4786     return;
4787
4788   gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
4789                                                     &child_iter,
4790                                                     &iter);
4791   child_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &child_iter);
4792   if (!child_path)
4793     return;
4794
4795   selected = *gtk_tree_path_get_indices (child_path);
4796   gtk_tree_path_free (child_path);
4797
4798   shortcuts_activate_item (impl, selected);
4799 }
4800
4801 static gboolean
4802 shortcuts_select_func  (GtkTreeSelection  *selection,
4803                         GtkTreeModel      *model,
4804                         GtkTreePath       *path,
4805                         gboolean           path_currently_selected,
4806                         gpointer           data)
4807 {
4808   GtkFileChooserDefault *impl = data;
4809
4810   return (*gtk_tree_path_get_indices (path) != shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR));
4811 }
4812
4813 static void
4814 list_selection_changed (GtkTreeSelection      *selection,
4815                         GtkFileChooserDefault *impl)
4816 {
4817   /* See if we are in the new folder editable row for Save mode */
4818   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
4819     {
4820       GtkTreeSelection *selection;
4821       GtkTreeIter iter, child_iter;
4822       const GtkFileInfo *info;
4823
4824       g_assert (!impl->select_multiple);
4825       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4826       if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
4827         return;
4828
4829       gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
4830                                                       &child_iter,
4831                                                       &iter);
4832
4833       info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
4834       if (!info)
4835         return; /* We are on the editable row for New Folder */
4836     }
4837
4838   update_chooser_entry (impl);
4839   check_preview_change (impl);
4840   bookmarks_check_add_sensitivity (impl);
4841
4842   g_signal_emit_by_name (impl, "selection-changed", 0);
4843 }
4844
4845 /* Callback used when a row in the file list is activated */
4846 static void
4847 list_row_activated (GtkTreeView           *tree_view,
4848                     GtkTreePath           *path,
4849                     GtkTreeViewColumn     *column,
4850                     GtkFileChooserDefault *impl)
4851 {
4852   GtkTreeIter iter, child_iter;
4853   const GtkFileInfo *info;
4854
4855   if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
4856     return;
4857
4858   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
4859
4860   info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
4861
4862   if (gtk_file_info_get_is_folder (info))
4863     {
4864       const GtkFilePath *file_path;
4865
4866       file_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
4867       change_folder_and_display_error (impl, file_path);
4868
4869       return;
4870     }
4871
4872   g_signal_emit_by_name (impl, "file-activated");
4873 }
4874
4875 static void
4876 path_bar_clicked (GtkPathBar            *path_bar,
4877                   GtkFilePath           *file_path,
4878                   gboolean               child_is_hidden,
4879                   GtkFileChooserDefault *impl)
4880 {
4881   if (!change_folder_and_display_error (impl, file_path))
4882     return;
4883
4884   /* Say we have "/foo/bar/[.baz]" and the user clicks on "bar".  We should then
4885    * show hidden files so that ".baz" appears in the file list, as it will still
4886    * be shown in the path bar: "/foo/[bar]/.baz"
4887    */
4888   if (child_is_hidden)
4889     g_object_set (impl, "show-hidden", TRUE, NULL);
4890 }
4891
4892 static const GtkFileInfo *
4893 get_list_file_info (GtkFileChooserDefault *impl,
4894                     GtkTreeIter           *iter)
4895 {
4896   GtkTreeIter child_iter;
4897
4898   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
4899                                                   &child_iter,
4900                                                   iter);
4901
4902   return _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
4903 }
4904
4905 static void
4906 list_icon_data_func (GtkTreeViewColumn *tree_column,
4907                      GtkCellRenderer   *cell,
4908                      GtkTreeModel      *tree_model,
4909                      GtkTreeIter       *iter,
4910                      gpointer           data)
4911 {
4912   GtkFileChooserDefault *impl = data;
4913   GtkTreeIter child_iter;
4914   const GtkFilePath *path;
4915   GdkPixbuf *pixbuf;
4916
4917   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
4918                                                   &child_iter,
4919                                                   iter);
4920   path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
4921   if (!path)
4922     return;
4923
4924   /* FIXME: NULL GError */
4925   pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
4926                                         impl->icon_size, NULL);
4927   g_object_set (cell,
4928                 "pixbuf", pixbuf,
4929                 NULL);
4930
4931   if (pixbuf)
4932     g_object_unref (pixbuf);
4933 }
4934
4935 static void
4936 list_name_data_func (GtkTreeViewColumn *tree_column,
4937                      GtkCellRenderer   *cell,
4938                      GtkTreeModel      *tree_model,
4939                      GtkTreeIter       *iter,
4940                      gpointer           data)
4941 {
4942   GtkFileChooserDefault *impl = data;
4943   const GtkFileInfo *info = get_list_file_info (impl, iter);
4944
4945   if (!info)
4946     {
4947       g_object_set (cell,
4948                     "text", _("Type name of new folder"),
4949                     NULL);
4950       return;
4951     }
4952
4953   g_object_set (cell,
4954                 "text", gtk_file_info_get_display_name (info),
4955                 NULL);
4956 }
4957
4958 #if 0
4959 static void
4960 list_size_data_func (GtkTreeViewColumn *tree_column,
4961                      GtkCellRenderer   *cell,
4962                      GtkTreeModel      *tree_model,
4963                      GtkTreeIter       *iter,
4964                      gpointer           data)
4965 {
4966   GtkFileChooserDefault *impl = data;
4967   const GtkFileInfo *info = get_list_file_info (impl, iter);
4968   gint64 size;
4969   gchar *str;
4970
4971   if (!info || gtk_file_info_get_is_folder (info))
4972     return;
4973
4974   size = gtk_file_info_get_size (info);
4975
4976   if (size < (gint64)1024)
4977     str = g_strdup_printf (ngettext ("%d byte", "%d bytes", (gint)size), (gint)size);
4978   else if (size < (gint64)1024*1024)
4979     str = g_strdup_printf (_("%.1f K"), size / (1024.));
4980   else if (size < (gint64)1024*1024*1024)
4981     str = g_strdup_printf (_("%.1f M"), size / (1024.*1024.));
4982   else
4983     str = g_strdup_printf (_("%.1f G"), size / (1024.*1024.*1024.));
4984
4985   g_object_set (cell,
4986                 "text", str,
4987                 NULL);
4988
4989   g_free (str);
4990 }
4991 #endif
4992
4993 /* Tree column data callback for the file list; fetches the mtime of a file */
4994 static void
4995 list_mtime_data_func (GtkTreeViewColumn *tree_column,
4996                       GtkCellRenderer   *cell,
4997                       GtkTreeModel      *tree_model,
4998                       GtkTreeIter       *iter,
4999                       gpointer           data)
5000 {
5001   GtkFileChooserDefault *impl;
5002   const GtkFileInfo *info;
5003   GtkFileTime time_mtime, time_now;
5004   GDate mtime, now;
5005   int days_diff;
5006   char buf[256];
5007
5008   impl = data;
5009
5010   info = get_list_file_info (impl, iter);
5011   if (!info)
5012     {
5013       g_object_set (cell,
5014                     "text", "",
5015                     NULL);
5016       return;
5017     }
5018
5019   time_mtime = gtk_file_info_get_modification_time (info);
5020   g_date_set_time (&mtime, (GTime) time_mtime);
5021
5022   time_now = (GTime ) time (NULL);
5023   g_date_set_time (&now, (GTime) time_now);
5024
5025   days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
5026
5027   if (days_diff == 0)
5028     strcpy (buf, _("Today"));
5029   else if (days_diff == 1)
5030     strcpy (buf, _("Yesterday"));
5031   else
5032     {
5033       char *format;
5034
5035       if (days_diff > 1 && days_diff < 7)
5036         format = "%A"; /* Days from last week */
5037       else
5038         format = "%x"; /* Any other date */
5039
5040       if (g_date_strftime (buf, sizeof (buf), format, &mtime) == 0)
5041         strcpy (buf, _("Unknown"));
5042     }
5043
5044   g_object_set (cell,
5045                 "text", buf,
5046                 NULL);
5047 }
5048
5049 GtkWidget *
5050 _gtk_file_chooser_default_new (const char *file_system)
5051 {
5052   return  g_object_new (GTK_TYPE_FILE_CHOOSER_DEFAULT,
5053                         "file-system-backend", file_system,
5054                         NULL);
5055 }
5056
5057 /* Sets the file part of a file chooser entry from the file under the cursor */
5058 static void
5059 location_entry_set_from_list (GtkFileChooserDefault *impl,
5060                               GtkFileChooserEntry   *entry)
5061 {
5062   GtkTreePath *tree_path;
5063   GtkTreeIter iter, child_iter;
5064   const GtkFileInfo *info;
5065   const char *name;
5066
5067   g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
5068             || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
5069
5070   gtk_tree_view_get_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), &tree_path, NULL);
5071   if (!tree_path)
5072     return;
5073
5074   gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, tree_path);
5075   gtk_tree_path_free (tree_path);
5076
5077   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
5078  
5079   info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
5080   if (!info)
5081     return;
5082
5083   name = gtk_file_info_get_display_name (info);
5084   _gtk_file_chooser_entry_set_file_part (entry, name);
5085 }
5086
5087 /* Sets the file part of a file chooser entry from the Name entry in save mode */
5088 static void
5089 location_entry_set_from_save_name (GtkFileChooserDefault *impl,
5090                                    GtkFileChooserEntry   *entry)
5091 {
5092   g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5093             || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
5094
5095   _gtk_file_chooser_entry_set_file_part (entry, gtk_entry_get_text (GTK_ENTRY (impl->save_file_name_entry)));
5096 }
5097
5098 static GtkWidget *
5099 location_entry_create (GtkFileChooserDefault *impl)
5100 {
5101   GtkWidget *entry;
5102
5103   entry = _gtk_file_chooser_entry_new ();
5104   /* Pick a good width for the entry */
5105   gtk_entry_set_width_chars (GTK_ENTRY (entry), 30);
5106   gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
5107   _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (entry), impl->file_system);
5108   _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (entry), impl->current_folder);
5109
5110   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
5111       || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5112     location_entry_set_from_list (impl, GTK_FILE_CHOOSER_ENTRY (entry));
5113   else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5114            || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5115     location_entry_set_from_save_name (impl, GTK_FILE_CHOOSER_ENTRY (entry));
5116   else
5117     g_assert_not_reached ();
5118
5119   return GTK_WIDGET (entry);
5120 }
5121
5122 static gboolean
5123 update_from_entry (GtkFileChooserDefault *impl,
5124                    GtkWindow             *parent,
5125                    GtkFileChooserEntry   *chooser_entry)
5126 {
5127   const GtkFilePath *folder_path;
5128   const char *file_part;
5129
5130   folder_path = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
5131   file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
5132
5133   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN && !folder_path)
5134     {
5135       error_message_with_parent (parent,
5136                                  _("Cannot change to the folder you specified as it is an invalid path."));
5137       return FALSE;
5138     }
5139
5140   if (file_part[0] == '\0')
5141     return change_folder_and_display_error (impl, folder_path);
5142   else
5143     {
5144       GtkFileFolder *folder = NULL;
5145       GtkFilePath *subfolder_path = NULL;
5146       GtkFileInfo *info = NULL;
5147       GError *error;
5148       gboolean result;
5149
5150       result = FALSE;
5151
5152       /* If the file part is non-empty, we need to figure out if it refers to a
5153        * folder within folder. We could optimize the case here where the folder
5154        * is already loaded for one of our tree models.
5155        */
5156
5157       error = NULL;
5158       folder = gtk_file_system_get_folder (impl->file_system, folder_path, GTK_FILE_INFO_IS_FOLDER, &error);
5159
5160       if (!folder)
5161         {
5162           error_getting_info_dialog (impl, folder_path, error);
5163           goto out;
5164         }
5165
5166       error = NULL;
5167       subfolder_path = gtk_file_system_make_path (impl->file_system, folder_path, file_part, &error);
5168
5169       if (!subfolder_path)
5170         {
5171           char *msg;
5172           char *uri;
5173
5174           uri = gtk_file_system_path_to_uri (impl->file_system, folder_path);
5175           msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
5176                                  uri, file_part,
5177                                  error->message);
5178           error_message (impl, msg);
5179           g_free (uri);
5180           g_free (msg);
5181           goto out;
5182         }
5183
5184       error = NULL;
5185       info = gtk_file_folder_get_info (folder, subfolder_path, &error);
5186
5187       if (!info)
5188         {
5189           if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5190               || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5191             {
5192               if (!change_folder_and_display_error (impl, folder_path))
5193                 goto out;
5194
5195               gtk_file_chooser_default_set_current_name (GTK_FILE_CHOOSER (impl), file_part);
5196             }
5197           else
5198             error_getting_info_dialog (impl, subfolder_path, error);
5199
5200           goto out;
5201         }
5202
5203       if (gtk_file_info_get_is_folder (info))
5204         result = change_folder_and_display_error (impl, subfolder_path);
5205       else
5206         {
5207           GError *error;
5208
5209           error = NULL;
5210           result = _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (impl), subfolder_path, &error);
5211           if (!result)
5212             error_dialog (impl,
5213                           _("Could not select %s:\n%s"),
5214                           subfolder_path, error);
5215         }
5216
5217     out:
5218
5219       if (folder)
5220         g_object_unref (folder);
5221
5222       gtk_file_path_free (subfolder_path);
5223
5224       if (info)
5225         gtk_file_info_free (info);
5226
5227       return result;
5228     }
5229
5230   g_assert_not_reached ();
5231 }
5232
5233 static void
5234 location_popup_handler (GtkFileChooserDefault *impl)
5235 {
5236   GtkWidget *dialog;
5237   GtkWindow *toplevel;
5238   GtkWidget *hbox;
5239   GtkWidget *label;
5240   GtkWidget *entry;
5241   gboolean refocus;
5242   char *title;
5243
5244   /* Create dialog */
5245
5246   toplevel = get_toplevel (GTK_WIDGET (impl));
5247
5248   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
5249       || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5250     {
5251       title = _("Open Location");
5252     }
5253   else
5254     {
5255       g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5256                 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
5257       title = ""; /* FIXME: #137272, fix for 2.4.1 */
5258     }
5259
5260   dialog = gtk_dialog_new_with_buttons (title,
5261                                         toplevel,
5262                                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
5263                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
5264                                         GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
5265                                         NULL);
5266   gtk_window_set_default_size (GTK_WINDOW (dialog), 300, -1);
5267   gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
5268   gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2);
5269   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
5270
5271   hbox = gtk_hbox_new (FALSE, 12);
5272   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0);
5273   gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
5274
5275   label = gtk_label_new_with_mnemonic (_("_Location:"));
5276   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
5277
5278   entry = location_entry_create (impl);
5279   gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
5280   gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
5281
5282   /* Run */
5283
5284   gtk_widget_show_all (dialog);
5285
5286   refocus = TRUE;
5287
5288   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
5289     {
5290       if (update_from_entry (impl, GTK_WINDOW (dialog), GTK_FILE_CHOOSER_ENTRY (entry)))
5291         {
5292           if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
5293               || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5294             {
5295               gtk_widget_grab_focus (impl->browse_files_tree_view);
5296             }
5297           else
5298             {
5299               g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5300                         || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
5301               gtk_widget_grab_focus (impl->save_file_name_entry);
5302             }
5303           refocus = FALSE;
5304         }
5305     }
5306
5307   if (refocus)
5308     {
5309       GtkWindow *toplevel;
5310
5311       toplevel = get_toplevel (GTK_WIDGET (impl));
5312       if (toplevel && toplevel->focus_widget)
5313         gtk_widget_grab_focus (toplevel->focus_widget);
5314     }
5315
5316   gtk_widget_destroy (dialog);
5317 }
5318
5319 /* Handler for the "up-folder" keybinding signal */
5320 static void
5321 up_folder_handler (GtkFileChooserDefault *impl)
5322 {
5323   _gtk_path_bar_up (GTK_PATH_BAR (impl->browse_path_bar));
5324 }
5325
5326 /* Handler for the "down-folder" keybinding signal */
5327 static void
5328 down_folder_handler (GtkFileChooserDefault *impl)
5329 {
5330   _gtk_path_bar_down (GTK_PATH_BAR (impl->browse_path_bar));
5331 }
5332
5333 /* Handler for the "home-folder" keybinding signal */
5334 static void
5335 home_folder_handler (GtkFileChooserDefault *impl)
5336 {
5337   int pos;
5338   GtkTreeIter iter;
5339   GtkFilePath *path;
5340
5341   if (!impl->has_home)
5342     return; /* Should we put up an error dialog? */
5343
5344   pos = shortcuts_get_index (impl, SHORTCUTS_HOME);
5345   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
5346     g_assert_not_reached ();
5347
5348   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &path, -1);
5349   g_assert (path != NULL);
5350
5351   change_folder_and_display_error (impl, path);
5352 }
5353
5354 \f
5355
5356 /* Drag and drop interfaces */
5357
5358 static void
5359 shortcuts_model_filter_class_init (ShortcutsModelFilterClass *class)
5360 {
5361 }
5362
5363 static void
5364 shortcuts_model_filter_init (ShortcutsModelFilter *model)
5365 {
5366   model->impl = NULL;
5367 }
5368
5369 /* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */
5370 static gboolean
5371 shortcuts_model_filter_row_draggable (GtkTreeDragSource *drag_source,
5372                                       GtkTreePath       *path)
5373 {
5374   ShortcutsModelFilter *model;
5375   int pos;
5376   int bookmarks_pos;
5377
5378   model = SHORTCUTS_MODEL_FILTER (drag_source);
5379
5380   pos = *gtk_tree_path_get_indices (path);
5381   bookmarks_pos = shortcuts_get_index (model->impl, SHORTCUTS_BOOKMARKS);
5382
5383   return (pos >= bookmarks_pos && pos < bookmarks_pos + model->impl->num_bookmarks);
5384 }
5385
5386 /* GtkTreeDragSource::drag_data_get implementation for the shortcuts filter model */
5387 static gboolean
5388 shortcuts_model_filter_drag_data_get (GtkTreeDragSource *drag_source,
5389                                       GtkTreePath       *path,
5390                                       GtkSelectionData  *selection_data)
5391 {
5392   ShortcutsModelFilter *model;
5393
5394   model = SHORTCUTS_MODEL_FILTER (drag_source);
5395
5396   /* FIXME */
5397
5398   return FALSE;
5399 }
5400
5401 /* Fill the GtkTreeDragSourceIface vtable */
5402 static void
5403 shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface)
5404 {
5405   iface->row_draggable = shortcuts_model_filter_row_draggable;
5406   iface->drag_data_get = shortcuts_model_filter_drag_data_get;
5407 }
5408
5409 #if 0
5410 /* Fill the GtkTreeDragDestIface vtable */
5411 static void
5412 shortcuts_model_filter_drag_dest_iface_init (GtkTreeDragDestIface *iface)
5413 {
5414   iface->drag_data_received = shortcuts_model_filter_drag_data_received;
5415   iface->row_drop_possible = shortcuts_model_filter_row_drop_possible;
5416 }
5417 #endif
5418
5419 static GtkTreeModel *
5420 shortcuts_model_filter_new (GtkFileChooserDefault *impl,
5421                             GtkTreeModel          *child_model,
5422                             GtkTreePath           *root)
5423 {
5424   ShortcutsModelFilter *model;
5425
5426   model = g_object_new (SHORTCUTS_MODEL_FILTER_TYPE,
5427                         "child_model", child_model,
5428                         "virtual_root", root,
5429                         NULL);
5430
5431   model->impl = impl;
5432
5433   return GTK_TREE_MODEL (model);
5434 }