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