]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserdefault.c
Do nothing if we have no screen. Fixes #137260.
[~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   GError *error;
1629
1630   if (shortcut_find_position (impl, path) != -1)
1631     return FALSE;
1632
1633   /* FIXME: this check really belongs in gtk_file_system_insert_bookmark.  */
1634   error = NULL;
1635   if (!check_is_folder (impl->file_system, path, &error))
1636     {
1637       error_dialog (impl,
1638                     _("Could not add bookmark for %s because it is not a folder."),
1639                     path,
1640                     error);
1641       return FALSE;
1642     }
1643
1644   error = NULL;
1645   if (!gtk_file_system_insert_bookmark (impl->file_system, path, pos, &error))
1646     {
1647       error_could_not_add_bookmark_dialog (impl, path, error);
1648       return FALSE;
1649     }
1650
1651   return TRUE;
1652 }
1653
1654 static void
1655 add_bookmark_foreach_cb (GtkTreeModel *model,
1656                          GtkTreePath  *path,
1657                          GtkTreeIter  *iter,
1658                          gpointer      data)
1659 {
1660   GtkFileChooserDefault *impl;
1661   GtkFileSystemModel *fs_model;
1662   GtkTreeIter child_iter;
1663   const GtkFilePath *file_path;
1664
1665   impl = (GtkFileChooserDefault *) data;
1666
1667   fs_model = impl->browse_files_model;
1668   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter);
1669
1670   file_path = _gtk_file_system_model_get_path (GTK_FILE_SYSTEM_MODEL (fs_model), &child_iter);
1671   shortcuts_add_bookmark_from_path (impl, file_path, -1);
1672 }
1673
1674 /* Callback used when the "Add bookmark" button is clicked */
1675 static void
1676 add_bookmark_button_clicked_cb (GtkButton *button,
1677                                 GtkFileChooserDefault *impl)
1678 {
1679   GtkTreeSelection *selection;
1680
1681   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
1682
1683   if (gtk_tree_selection_count_selected_rows (selection) == 0)
1684     shortcuts_add_bookmark_from_path (impl, impl->current_folder, -1);
1685   else
1686     gtk_tree_selection_selected_foreach (selection,
1687                                          add_bookmark_foreach_cb,
1688                                          impl);
1689 }
1690
1691 /* Callback used when the "Remove bookmark" button is clicked */
1692 static void
1693 remove_bookmark_button_clicked_cb (GtkButton *button,
1694                                    GtkFileChooserDefault *impl)
1695 {
1696   GtkTreeSelection *selection;
1697   GtkTreeIter iter;
1698   GtkFilePath *path;
1699   gboolean removable;
1700   GError *error;
1701
1702   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1703
1704   if (gtk_tree_selection_get_selected (selection, NULL, &iter))
1705     {
1706       gtk_tree_model_get (impl->shortcuts_filter_model, &iter,
1707                           SHORTCUTS_COL_PATH, &path,
1708                           SHORTCUTS_COL_REMOVABLE, &removable, -1);
1709       if (!removable)
1710         {
1711           g_assert_not_reached ();
1712           return;
1713         }
1714
1715       error = NULL;
1716       if (!gtk_file_system_remove_bookmark (impl->file_system, path, &error))
1717         {
1718           error_dialog (impl,
1719                         _("Could not remove bookmark for %s:\n%s"),
1720                         path,
1721                         error);
1722         }
1723     }
1724 }
1725
1726 struct selection_check_closure {
1727   GtkFileChooserDefault *impl;
1728   gboolean empty;
1729   gboolean all_files;
1730   gboolean all_folders;
1731 };
1732
1733 /* Used from gtk_tree_selection_selected_foreach() */
1734 static void
1735 selection_check_foreach_cb (GtkTreeModel *model,
1736                             GtkTreePath  *path,
1737                             GtkTreeIter  *iter,
1738                             gpointer      data)
1739 {
1740   struct selection_check_closure *closure;
1741   GtkTreeIter child_iter;
1742   const GtkFileInfo *info;
1743   gboolean is_folder;
1744
1745   closure = data;
1746   closure->empty = FALSE;
1747
1748   gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
1749
1750   info = _gtk_file_system_model_get_info (closure->impl->browse_files_model, &child_iter);
1751   is_folder = gtk_file_info_get_is_folder (info);
1752
1753   closure->all_folders &= is_folder;
1754   closure->all_files &= !is_folder;
1755 }
1756
1757 /* Checks whether the selected items in the file list are all files or all folders */
1758 static void
1759 selection_check (GtkFileChooserDefault *impl,
1760                  gboolean              *empty,
1761                  gboolean              *all_files,
1762                  gboolean              *all_folders)
1763 {
1764   struct selection_check_closure closure;
1765   GtkTreeSelection *selection;
1766
1767   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
1768
1769   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
1770       || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
1771     {
1772       if (gtk_tree_selection_count_selected_rows (selection) == 0)
1773         closure.empty = TRUE;
1774       else
1775         {
1776           closure.empty = FALSE;
1777           closure.all_files = FALSE;
1778           closure.all_folders = TRUE;
1779         }
1780     }
1781   else
1782     {
1783       g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
1784                 || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE);
1785
1786       closure.impl = impl;
1787       closure.empty = TRUE;
1788       closure.all_files = TRUE;
1789       closure.all_folders = TRUE;
1790
1791       gtk_tree_selection_selected_foreach (selection,
1792                                            selection_check_foreach_cb,
1793                                            &closure);
1794     }
1795
1796   g_assert (closure.empty || !(closure.all_files && closure.all_folders));
1797
1798   if (empty)
1799     *empty = closure.empty;
1800
1801   if (all_files)
1802     *all_files = closure.all_files;
1803
1804   if (all_folders)
1805     *all_folders = closure.all_folders;
1806 }
1807
1808 /* Sensitize the "add bookmark" button if all the selected items are folders, or
1809  * if there are no selected items *and* the current folder is not in the
1810  * bookmarks list.  De-sensitize the button otherwise.
1811  */
1812 static void
1813 bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl)
1814 {
1815   GtkTreeSelection *selection;
1816   gboolean active;
1817
1818   /* Check selection */
1819
1820   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
1821
1822   if (gtk_tree_selection_count_selected_rows (selection) == 0)
1823     active = (shortcut_find_position (impl, impl->current_folder) == -1);
1824   else
1825     {
1826       gboolean all_folders;
1827
1828       selection_check (impl, NULL, NULL, &all_folders);
1829       active = (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
1830                 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER ||
1831                 all_folders);
1832     }
1833
1834   gtk_widget_set_sensitive (impl->browse_shortcuts_add_button, active);
1835 }
1836
1837 /* Sets the sensitivity of the "remove bookmark" button depending on whether a
1838  * bookmark row is selected in the shortcuts tree.
1839  */
1840 static void
1841 bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl)
1842 {
1843   GtkTreeSelection *selection;
1844   GtkTreeIter iter;
1845   gboolean removable = FALSE;
1846
1847   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1848
1849   if (gtk_tree_selection_get_selected (selection, NULL, &iter))
1850     gtk_tree_model_get (impl->shortcuts_filter_model, &iter,
1851                         SHORTCUTS_COL_REMOVABLE, &removable,
1852                         -1);
1853
1854   gtk_widget_set_sensitive (impl->browse_shortcuts_remove_button, removable);
1855 }
1856
1857 /* GtkWidget::drag-begin handler for the shortcuts list. */
1858 static void
1859 shortcuts_drag_begin_cb (GtkWidget             *widget,
1860                          GdkDragContext        *context,
1861                          GtkFileChooserDefault *impl)
1862 {
1863 #if 0
1864   impl->shortcuts_drag_context = g_object_ref (context);
1865 #endif
1866 }
1867
1868 #if 0
1869 /* Removes the idle handler for outside drags */
1870 static void
1871 shortcuts_cancel_drag_outside_idle (GtkFileChooserDefault *impl)
1872 {
1873   if (!impl->shortcuts_drag_outside_idle)
1874     return;
1875
1876   g_source_destroy (impl->shortcuts_drag_outside_idle);
1877   impl->shortcuts_drag_outside_idle = NULL;
1878 }
1879 #endif
1880
1881 /* GtkWidget::drag-end handler for the shortcuts list. */
1882 static void
1883 shortcuts_drag_end_cb (GtkWidget             *widget,
1884                        GdkDragContext        *context,
1885                        GtkFileChooserDefault *impl)
1886 {
1887 #if 0
1888   g_object_unref (impl->shortcuts_drag_context);
1889
1890   shortcuts_cancel_drag_outside_idle (impl);
1891
1892   if (!impl->shortcuts_drag_outside)
1893     return;
1894
1895   gtk_button_clicked (GTK_BUTTON (impl->browse_shortcuts_remove_button));
1896
1897   impl->shortcuts_drag_outside = FALSE;
1898 #endif
1899 }
1900
1901 /* GtkWidget::drag-data-delete handler for the shortcuts list. */
1902 static void
1903 shortcuts_drag_data_delete_cb (GtkWidget             *widget,
1904                                GdkDragContext        *context,
1905                                GtkFileChooserDefault *impl)
1906 {
1907   g_signal_stop_emission_by_name (widget, "drag-data-delete");
1908 }
1909
1910 #if 0
1911 /* Creates a suitable drag cursor to indicate that the selected bookmark will be
1912  * deleted or not.
1913  */
1914 static void
1915 shortcuts_drag_set_delete_cursor (GtkFileChooserDefault *impl,
1916                                   gboolean               delete)
1917 {
1918   GtkTreeView *tree_view;
1919   GtkTreeSelection *selection;
1920   GtkTreeIter iter, child_iter;
1921   GtkTreePath *path;
1922   GdkPixmap *row_pixmap;
1923   GdkBitmap *mask;
1924   int row_pixmap_y;
1925   int cell_y;
1926
1927   tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
1928
1929   /* Find the selected path and get its drag pixmap */
1930
1931   selection = gtk_tree_view_get_selection (tree_view);
1932   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
1933     g_assert_not_reached ();
1934
1935   gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
1936                                                     &child_iter,
1937                                                     &iter);
1938
1939   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &child_iter);
1940
1941   row_pixmap = gtk_tree_view_create_row_drag_icon (tree_view, path);
1942   gtk_tree_path_free (path);
1943
1944   mask = NULL;
1945   row_pixmap_y = 0;
1946
1947   if (delete)
1948     {
1949       GdkPixbuf *pixbuf;
1950
1951       pixbuf = gtk_widget_render_icon (impl->browse_shortcuts_tree_view,
1952                                        GTK_STOCK_DELETE,
1953                                        GTK_ICON_SIZE_DND,
1954                                        NULL);
1955       if (pixbuf)
1956         {
1957           GdkPixmap *composite;
1958           int row_pixmap_width, row_pixmap_height;
1959           int pixbuf_width, pixbuf_height;
1960           int composite_width, composite_height;
1961           int pixbuf_x, pixbuf_y;
1962           GdkGC *gc, *mask_gc;
1963           GdkColor color;
1964           GdkBitmap *pixbuf_mask;
1965
1966           /* Create pixmap and mask for composite image */
1967
1968           gdk_drawable_get_size (row_pixmap, &row_pixmap_width, &row_pixmap_height);
1969           pixbuf_width = gdk_pixbuf_get_width (pixbuf);
1970           pixbuf_height = gdk_pixbuf_get_height (pixbuf);
1971
1972           composite_width = MAX (row_pixmap_width, pixbuf_width);
1973           composite_height = MAX (row_pixmap_height, pixbuf_height);
1974
1975           row_pixmap_y = (composite_height - row_pixmap_height) / 2;
1976
1977           if (gtk_widget_get_direction (impl->browse_shortcuts_tree_view) == GTK_TEXT_DIR_RTL)
1978             pixbuf_x = 0;
1979           else
1980             pixbuf_x = composite_width - pixbuf_width;
1981
1982           pixbuf_y = (composite_height - pixbuf_height) / 2;
1983
1984           composite = gdk_pixmap_new (row_pixmap, composite_width, composite_height, -1);
1985           gc = gdk_gc_new (composite);
1986
1987           mask = gdk_pixmap_new (row_pixmap, composite_width, composite_height, 1);
1988           mask_gc = gdk_gc_new (mask);
1989           color.pixel = 0;
1990           gdk_gc_set_foreground (mask_gc, &color);
1991           gdk_draw_rectangle (mask, mask_gc, TRUE, 0, 0, composite_width, composite_height);
1992
1993           color.red = 0xffff;
1994           color.green = 0xffff;
1995           color.blue = 0xffff;
1996           gdk_gc_set_rgb_fg_color (gc, &color);
1997           gdk_draw_rectangle (composite, gc, TRUE, 0, 0, composite_width, composite_height);
1998
1999           /* Composite the row pixmap and the pixbuf */
2000
2001           gdk_pixbuf_render_pixmap_and_mask_for_colormap
2002             (pixbuf,
2003              gtk_widget_get_colormap (impl->browse_shortcuts_tree_view),
2004              NULL, &pixbuf_mask, 128);
2005           gdk_draw_drawable (mask, mask_gc, pixbuf_mask,
2006                              0, 0,
2007                              pixbuf_x, pixbuf_y,
2008                              pixbuf_width, pixbuf_height);
2009           g_object_unref (pixbuf_mask);
2010
2011           gdk_draw_drawable (composite, gc, row_pixmap,
2012                              0, 0,
2013                              0, row_pixmap_y,
2014                              row_pixmap_width, row_pixmap_height);
2015           color.pixel = 1;
2016           gdk_gc_set_foreground (mask_gc, &color);
2017           gdk_draw_rectangle (mask, mask_gc, TRUE, 0, row_pixmap_y, row_pixmap_width, row_pixmap_height);
2018
2019           gdk_draw_pixbuf (composite, gc, pixbuf,
2020                            0, 0,
2021                            pixbuf_x, pixbuf_y,
2022                            pixbuf_width, pixbuf_height,
2023                            GDK_RGB_DITHER_MAX,
2024                            0, 0);
2025
2026           g_object_unref (pixbuf);
2027           g_object_unref (row_pixmap);
2028
2029           row_pixmap = composite;
2030         }
2031     }
2032
2033   /* The hotspot offsets here are copied from gtk_tree_view_drag_begin(), ugh */
2034
2035   gtk_tree_view_get_path_at_pos (tree_view,
2036                                  tree_view->priv->press_start_x,
2037                                  tree_view->priv->press_start_y,
2038                                  NULL,
2039                                  NULL,
2040                                  NULL,
2041                                  &cell_y);
2042
2043   gtk_drag_set_icon_pixmap (impl->shortcuts_drag_context,
2044                             gdk_drawable_get_colormap (row_pixmap),
2045                             row_pixmap,
2046                             mask,
2047                             tree_view->priv->press_start_x + 1,
2048                             row_pixmap_y + cell_y + 1);
2049
2050   g_object_unref (row_pixmap);
2051   if (mask)
2052     g_object_unref (mask);
2053 }
2054
2055 /* We set the delete cursor and the shortcuts_drag_outside flag in an idle
2056  * handler so that we can tell apart the drag_leave event that comes right
2057  * before a drag_drop, from a normal drag_leave.  We don't want to set the
2058  * cursor nor the flag in the latter case.
2059  */
2060 static gboolean
2061 shortcuts_drag_outside_idle_cb (GtkFileChooserDefault *impl)
2062 {
2063   shortcuts_drag_set_delete_cursor (impl, TRUE);
2064   impl->shortcuts_drag_outside = TRUE;
2065
2066   shortcuts_cancel_drag_outside_idle (impl);
2067   return FALSE;
2068 }
2069 #endif
2070
2071 /* GtkWidget::drag-leave handler for the shortcuts list.  We unhighlight the
2072  * drop position.
2073  */
2074 static void
2075 shortcuts_drag_leave_cb (GtkWidget             *widget,
2076                          GdkDragContext        *context,
2077                          guint                  time_,
2078                          GtkFileChooserDefault *impl)
2079 {
2080 #if 0
2081   if (gtk_drag_get_source_widget (context) == widget && !impl->shortcuts_drag_outside_idle)
2082     {
2083       impl->shortcuts_drag_outside_idle = g_idle_source_new ();
2084       g_source_set_closure (impl->shortcuts_drag_outside_idle,
2085                             g_cclosure_new_object (G_CALLBACK (shortcuts_drag_outside_idle_cb),
2086                                                    G_OBJECT (impl)));
2087       g_source_attach (impl->shortcuts_drag_outside_idle, NULL);
2088     }
2089 #endif
2090
2091   gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
2092                                    NULL,
2093                                    GTK_TREE_VIEW_DROP_BEFORE);
2094
2095   g_signal_stop_emission_by_name (widget, "drag-leave");
2096 }
2097
2098 /* Computes the appropriate row and position for dropping */
2099 static void
2100 shortcuts_compute_drop_position (GtkFileChooserDefault   *impl,
2101                                  int                      x,
2102                                  int                      y,
2103                                  GtkTreePath            **path,
2104                                  GtkTreeViewDropPosition *pos)
2105 {
2106   GtkTreeView *tree_view;
2107   GtkTreeViewColumn *column;
2108   int cell_y;
2109   GdkRectangle cell;
2110   int row;
2111   int bookmarks_index;
2112
2113   tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
2114
2115   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2116
2117   if (!gtk_tree_view_get_path_at_pos (tree_view,
2118                                       x,
2119                                       y - TREE_VIEW_HEADER_HEIGHT (tree_view),
2120                                       path,
2121                                       &column,
2122                                       NULL,
2123                                       &cell_y))
2124     {
2125       row = bookmarks_index + impl->num_bookmarks - 1;
2126       *path = gtk_tree_path_new_from_indices (row, -1);
2127       *pos = GTK_TREE_VIEW_DROP_AFTER;
2128       return;
2129     }
2130
2131   row = *gtk_tree_path_get_indices (*path);
2132   gtk_tree_view_get_background_area (tree_view, *path, column, &cell);
2133   gtk_tree_path_free (*path);
2134
2135   if (row < bookmarks_index)
2136     {
2137       row = bookmarks_index;
2138       *pos = GTK_TREE_VIEW_DROP_BEFORE;
2139     }
2140   else if (row > bookmarks_index + impl->num_bookmarks - 1)
2141     {
2142       row = bookmarks_index + impl->num_bookmarks - 1;
2143       *pos = GTK_TREE_VIEW_DROP_AFTER;
2144     }
2145   else
2146     {
2147       if (cell_y < cell.height / 2)
2148         *pos = GTK_TREE_VIEW_DROP_BEFORE;
2149       else
2150         *pos = GTK_TREE_VIEW_DROP_AFTER;
2151     }
2152
2153   *path = gtk_tree_path_new_from_indices (row, -1);
2154 }
2155
2156 /* GtkWidget::drag-motion handler for the shortcuts list.  We basically
2157  * implement the destination side of DnD by hand, due to limitations in
2158  * GtkTreeView's DnD API.
2159  */
2160 static gboolean
2161 shortcuts_drag_motion_cb (GtkWidget             *widget,
2162                           GdkDragContext        *context,
2163                           gint                   x,
2164                           gint                   y,
2165                           guint                  time_,
2166                           GtkFileChooserDefault *impl)
2167 {
2168   GtkTreePath *path;
2169   GtkTreeViewDropPosition pos;
2170   GdkDragAction action;
2171
2172 #if 0
2173   if (gtk_drag_get_source_widget (context) == widget)
2174     {
2175       shortcuts_cancel_drag_outside_idle (impl);
2176
2177       if (impl->shortcuts_drag_outside)
2178         {
2179           shortcuts_drag_set_delete_cursor (impl, FALSE);
2180           impl->shortcuts_drag_outside = FALSE;
2181         }
2182     }
2183 #endif
2184
2185   if (context->suggested_action == GDK_ACTION_COPY || (context->actions & GDK_ACTION_COPY) != 0)
2186     action = GDK_ACTION_COPY;
2187   else if (context->suggested_action == GDK_ACTION_MOVE || (context->actions & GDK_ACTION_MOVE) != 0)
2188     action = GDK_ACTION_MOVE;
2189   else
2190     {
2191       action = 0;
2192       goto out;
2193     }
2194
2195   shortcuts_compute_drop_position (impl, x, y, &path, &pos);
2196   gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), path, pos);
2197   gtk_tree_path_free (path);
2198
2199  out:
2200
2201   g_signal_stop_emission_by_name (widget, "drag-motion");
2202
2203   if (action != 0)
2204     {
2205       gdk_drag_status (context, action, time_);
2206       return TRUE;
2207     }
2208   else
2209     return FALSE;
2210 }
2211
2212 /* GtkWidget::drag-drop handler for the shortcuts list. */
2213 static gboolean
2214 shortcuts_drag_drop_cb (GtkWidget             *widget,
2215                         GdkDragContext        *context,
2216                         gint                   x,
2217                         gint                   y,
2218                         guint                  time_,
2219                         GtkFileChooserDefault *impl)
2220 {
2221 #if 0
2222   shortcuts_cancel_drag_outside_idle (impl);
2223 #endif
2224
2225   g_signal_stop_emission_by_name (widget, "drag-drop");
2226   return TRUE;
2227 }
2228
2229 /* Converts raw selection data from text/uri-list to a list of strings */
2230 static GSList *
2231 split_uris (const char *data)
2232 {
2233   GSList *uris;
2234   const char *p, *start;
2235
2236   uris = NULL;
2237
2238   start = data;
2239
2240   for (p = start; *p != 0; p++)
2241     if (*p == '\r' && *(p + 1) == '\n')
2242       {
2243         char *name;
2244
2245         name = g_strndup (start, p - start);
2246         uris = g_slist_prepend (uris, name);
2247
2248         start = p + 2;
2249         p = start;
2250       }
2251
2252   uris = g_slist_reverse (uris);
2253   return uris;
2254 }
2255
2256 /* Parses a "text/uri-list" string and inserts its URIs as bookmarks */
2257 static void
2258 shortcuts_drop_uris (GtkFileChooserDefault *impl,
2259                      const char            *data,
2260                      int                    position)
2261 {
2262   GSList *uris, *l;
2263
2264   uris = split_uris (data);
2265
2266   for (l = uris; l; l = l->next)
2267     {
2268       char *uri;
2269       GtkFilePath *path;
2270
2271       uri = l->data;
2272       path = gtk_file_system_uri_to_path (impl->file_system, uri);
2273
2274       if (path)
2275         {
2276           if (shortcuts_add_bookmark_from_path (impl, path, position))
2277             position++;
2278
2279           gtk_file_path_free (path);
2280         }
2281       else
2282         {
2283           char *msg;
2284
2285           msg = g_strdup_printf (_("Could not add a bookmark for %s because it is an invalid path name."),
2286                                  uri);
2287           error_message (impl, msg);
2288           g_free (msg);
2289         }
2290
2291       g_free (uri);
2292     }
2293
2294   g_slist_free (uris);
2295 }
2296
2297 /* Reorders the selected bookmark to the specified position */
2298 static void
2299 shortcuts_reorder (GtkFileChooserDefault *impl,
2300                    int                    new_position)
2301 {
2302   GtkTreeSelection *selection;
2303   GtkTreeIter iter, child_iter;
2304   GtkTreePath *path;
2305   int old_position;
2306   int bookmarks_index;
2307   const GtkFilePath *file_path;
2308   GtkFilePath *file_path_copy;
2309   GError *error;
2310
2311   /* Get the selected path */
2312
2313   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
2314   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
2315     g_assert_not_reached ();
2316
2317   gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
2318                                                     &child_iter,
2319                                                     &iter);
2320
2321   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &child_iter);
2322   old_position = *gtk_tree_path_get_indices (path);
2323   gtk_tree_path_free (path);
2324
2325   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2326   old_position -= bookmarks_index;
2327   g_assert (old_position >= 0 && old_position < impl->num_bookmarks);
2328
2329   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &child_iter,
2330                       SHORTCUTS_COL_PATH, &file_path,
2331                       -1);
2332   file_path_copy = gtk_file_path_copy (file_path); /* removal below will free file_path, so we need a copy */
2333
2334   /* Remove the path from the old position and insert it in the new one */
2335
2336   if (new_position > old_position)
2337     new_position--;
2338
2339   if (old_position == new_position)
2340     return;
2341
2342   error = NULL;
2343   if (gtk_file_system_remove_bookmark (impl->file_system, file_path_copy, &error))
2344     shortcuts_add_bookmark_from_path (impl, file_path_copy, new_position);
2345   else
2346     error_could_not_add_bookmark_dialog (impl, file_path_copy, error);
2347
2348   gtk_file_path_free (file_path_copy);
2349 }
2350
2351 /* Callback used when we get the drag data for the bookmarks list.  We add the
2352  * received URIs as bookmarks if they are folders.
2353  */
2354 static void
2355 shortcuts_drag_data_received_cb (GtkWidget          *widget,
2356                                  GdkDragContext     *context,
2357                                  gint                x,
2358                                  gint                y,
2359                                  GtkSelectionData   *selection_data,
2360                                  guint               info,
2361                                  guint               time_,
2362                                  gpointer            data)
2363 {
2364   GtkFileChooserDefault *impl;
2365   GtkTreePath *tree_path;
2366   GtkTreeViewDropPosition tree_pos;
2367   int position;
2368   int bookmarks_index;
2369
2370   impl = GTK_FILE_CHOOSER_DEFAULT (data);
2371
2372   /* Compute position */
2373
2374   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2375
2376   shortcuts_compute_drop_position (impl, x, y, &tree_path, &tree_pos);
2377   position = *gtk_tree_path_get_indices (tree_path);
2378   gtk_tree_path_free (tree_path);
2379
2380   if (tree_pos == GTK_TREE_VIEW_DROP_AFTER)
2381     position++;
2382
2383   g_assert (position >= bookmarks_index);
2384   position -= bookmarks_index;
2385
2386   if (selection_data->target == gdk_atom_intern ("text/uri-list", FALSE))
2387     shortcuts_drop_uris (impl, selection_data->data, position);
2388   else if (selection_data->target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE))
2389     shortcuts_reorder (impl, position);
2390
2391   g_signal_stop_emission_by_name (widget, "drag-data-received");
2392 }
2393
2394 /* Callback used when the selection in the shortcuts tree changes */
2395 static void
2396 shortcuts_selection_changed_cb (GtkTreeSelection      *selection,
2397                                 GtkFileChooserDefault *impl)
2398 {
2399   bookmarks_check_remove_sensitivity (impl);
2400 }
2401
2402 /* Creates the widgets for the shortcuts and bookmarks tree */
2403 static GtkWidget *
2404 shortcuts_list_create (GtkFileChooserDefault *impl)
2405 {
2406   GtkWidget *swin;
2407   GtkTreeSelection *selection;
2408   GtkTreeViewColumn *column;
2409   GtkCellRenderer *renderer;
2410
2411   /* Scrolled window */
2412
2413   swin = gtk_scrolled_window_new (NULL, NULL);
2414   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
2415                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2416   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
2417                                        GTK_SHADOW_IN);
2418   gtk_widget_show (swin);
2419
2420   /* Tree */
2421
2422   impl->browse_shortcuts_tree_view = gtk_tree_view_new ();
2423   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), FALSE);
2424
2425   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), impl->shortcuts_filter_model);
2426
2427   gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
2428                                           GDK_BUTTON1_MASK,
2429                                           shortcuts_source_targets,
2430                                           num_shortcuts_source_targets,
2431                                           GDK_ACTION_MOVE);
2432
2433   gtk_drag_dest_set (impl->browse_shortcuts_tree_view,
2434                      GTK_DEST_DEFAULT_ALL,
2435                      shortcuts_dest_targets,
2436                      num_shortcuts_dest_targets,
2437                      GDK_ACTION_COPY | GDK_ACTION_MOVE);
2438
2439   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
2440   gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
2441   gtk_tree_selection_set_select_function (selection,
2442                                           shortcuts_select_func,
2443                                           impl, NULL);
2444
2445   g_signal_connect (selection, "changed",
2446                     G_CALLBACK (shortcuts_selection_changed_cb), impl);
2447
2448   g_signal_connect (impl->browse_shortcuts_tree_view, "row-activated",
2449                     G_CALLBACK (shortcuts_row_activated_cb), impl);
2450
2451   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-begin",
2452                     G_CALLBACK (shortcuts_drag_begin_cb), impl);
2453   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-end",
2454                     G_CALLBACK (shortcuts_drag_end_cb), impl);
2455   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-delete",
2456                     G_CALLBACK (shortcuts_drag_data_delete_cb), impl);
2457
2458   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-leave",
2459                     G_CALLBACK (shortcuts_drag_leave_cb), impl);
2460   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-motion",
2461                     G_CALLBACK (shortcuts_drag_motion_cb), impl);
2462   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-drop",
2463                     G_CALLBACK (shortcuts_drag_drop_cb), impl);
2464   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-received",
2465                     G_CALLBACK (shortcuts_drag_data_received_cb), impl);
2466
2467   gtk_container_add (GTK_CONTAINER (swin), impl->browse_shortcuts_tree_view);
2468   gtk_widget_show (impl->browse_shortcuts_tree_view);
2469
2470   /* Column */
2471
2472   column = gtk_tree_view_column_new ();
2473   gtk_tree_view_column_set_title (column, _("Folder"));
2474
2475   renderer = gtk_cell_renderer_pixbuf_new ();
2476   gtk_tree_view_column_pack_start (column, renderer, FALSE);
2477   gtk_tree_view_column_set_attributes (column, renderer,
2478                                        "pixbuf", SHORTCUTS_COL_PIXBUF,
2479                                        "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
2480                                        NULL);
2481
2482   renderer = _gtk_cell_renderer_sep_text_new ();
2483   gtk_tree_view_column_pack_start (column, renderer, TRUE);
2484   gtk_tree_view_column_set_attributes (column, renderer,
2485                                        "text", SHORTCUTS_COL_NAME,
2486                                        NULL);
2487
2488   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), column);
2489
2490   return swin;
2491 }
2492
2493 /* Creates the widgets for the shortcuts/bookmarks pane */
2494 static GtkWidget *
2495 shortcuts_pane_create (GtkFileChooserDefault *impl,
2496                        GtkSizeGroup          *size_group)
2497 {
2498   GtkWidget *vbox;
2499   GtkWidget *hbox;
2500   GtkWidget *widget;
2501
2502   vbox = gtk_vbox_new (FALSE, 6);
2503   gtk_widget_show (vbox);
2504
2505   /* Shortcuts tree */
2506
2507   widget = shortcuts_list_create (impl);
2508   gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
2509
2510   /* Box for buttons */
2511
2512   hbox = gtk_hbox_new (TRUE, 6);
2513   gtk_size_group_add_widget (size_group, hbox);
2514   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
2515   gtk_widget_show (hbox);
2516
2517   /* Add bookmark button */
2518
2519   impl->browse_shortcuts_add_button = button_new (impl,
2520                                                   _("_Add"),
2521                                                   GTK_STOCK_ADD,
2522                                                   FALSE,
2523                                                   TRUE,
2524                                                   G_CALLBACK (add_bookmark_button_clicked_cb));
2525   gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_add_button, TRUE, TRUE, 0);
2526
2527   /* Remove bookmark button */
2528
2529   impl->browse_shortcuts_remove_button = button_new (impl,
2530                                                      _("_Remove"),
2531                                                      GTK_STOCK_REMOVE,
2532                                                      FALSE,
2533                                                      TRUE,
2534                                                      G_CALLBACK (remove_bookmark_button_clicked_cb));
2535   gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_remove_button, TRUE, TRUE, 0);
2536
2537   return vbox;
2538 }
2539
2540 static gboolean
2541 trap_activate_cb (GtkWidget   *widget,
2542                   GdkEventKey *event,
2543                   gpointer     data)
2544 {
2545   GtkFileChooserDefault *impl;
2546
2547   impl = (GtkFileChooserDefault *) data;
2548
2549   if (event->keyval == GDK_Return
2550       || event->keyval == GDK_ISO_Enter
2551       || event->keyval == GDK_KP_Enter
2552       || event->keyval == GDK_space)
2553     {
2554       GtkWidget *toplevel;
2555
2556       toplevel = gtk_widget_get_toplevel (widget);
2557       if (GTK_IS_WINDOW (toplevel))
2558         {
2559           GtkWindow *window;
2560
2561           window = GTK_WINDOW (toplevel);
2562
2563           if (window &&
2564               widget != window->default_widget &&
2565               !(widget == window->focus_widget &&
2566                 (!window->default_widget || !GTK_WIDGET_SENSITIVE (window->default_widget))))
2567             gtk_window_activate_default (window);
2568         }
2569       return TRUE;
2570     }
2571   return FALSE;
2572 }
2573
2574
2575 /* Creates the widgets for the file list */
2576 static GtkWidget *
2577 create_file_list (GtkFileChooserDefault *impl)
2578 {
2579   GtkWidget *swin;
2580   GtkTreeSelection *selection;
2581   GtkTreeViewColumn *column;
2582   GtkCellRenderer *renderer;
2583
2584   /* Scrolled window */
2585
2586   swin = gtk_scrolled_window_new (NULL, NULL);
2587   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
2588                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2589   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
2590                                        GTK_SHADOW_IN);
2591
2592   /* Tree/list view */
2593
2594   impl->browse_files_tree_view = gtk_tree_view_new ();
2595   gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->browse_files_tree_view), TRUE);
2596   gtk_container_add (GTK_CONTAINER (swin), impl->browse_files_tree_view);
2597   g_signal_connect (impl->browse_files_tree_view, "row-activated",
2598                     G_CALLBACK (list_row_activated), impl);
2599   g_signal_connect (impl->browse_files_tree_view, "key-press-event",
2600                     G_CALLBACK (trap_activate_cb), impl);
2601
2602   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2603   gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_files_tree_view),
2604                                           GDK_BUTTON1_MASK,
2605                                           file_list_source_targets,
2606                                           num_file_list_source_targets,
2607                                           GDK_ACTION_COPY);
2608
2609   g_signal_connect (selection, "changed",
2610                     G_CALLBACK (list_selection_changed), impl);
2611
2612   /* Filename column */
2613
2614   impl->list_name_column = gtk_tree_view_column_new ();
2615   gtk_tree_view_column_set_expand (impl->list_name_column, TRUE);
2616   gtk_tree_view_column_set_title (impl->list_name_column, _("Name"));
2617   gtk_tree_view_column_set_sort_column_id (impl->list_name_column, FILE_LIST_COL_NAME);
2618
2619   renderer = gtk_cell_renderer_pixbuf_new ();
2620   gtk_tree_view_column_pack_start (impl->list_name_column, renderer, FALSE);
2621   gtk_tree_view_column_set_cell_data_func (impl->list_name_column, renderer,
2622                                            list_icon_data_func, impl, NULL);
2623
2624   impl->list_name_renderer = gtk_cell_renderer_text_new ();
2625   g_signal_connect (impl->list_name_renderer, "edited",
2626                     G_CALLBACK (renderer_edited_cb), impl);
2627   g_signal_connect (impl->list_name_renderer, "editing-canceled",
2628                     G_CALLBACK (renderer_editing_canceled_cb), impl);
2629   gtk_tree_view_column_pack_start (impl->list_name_column, impl->list_name_renderer, TRUE);
2630   gtk_tree_view_column_set_cell_data_func (impl->list_name_column, impl->list_name_renderer,
2631                                            list_name_data_func, impl, NULL);
2632
2633   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), impl->list_name_column);
2634 #if 0
2635   /* Size column */
2636
2637   column = gtk_tree_view_column_new ();
2638   gtk_tree_view_column_set_title (column, _("Size"));
2639
2640   renderer = gtk_cell_renderer_text_new ();
2641   gtk_tree_view_column_pack_start (column, renderer, TRUE);
2642   gtk_tree_view_column_set_cell_data_func (column, renderer,
2643                                            list_size_data_func, impl, NULL);
2644   gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_SIZE);
2645   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
2646 #endif
2647   /* Modification time column */
2648
2649   column = gtk_tree_view_column_new ();
2650   gtk_tree_view_column_set_title (column, _("Modified"));
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_mtime_data_func, impl, NULL);
2656   gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_MTIME);
2657   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
2658   gtk_widget_show_all (swin);
2659
2660   return swin;
2661 }
2662
2663 static GtkWidget *
2664 create_filename_entry_and_filter_combo (GtkFileChooserDefault *impl)
2665 {
2666   GtkWidget *hbox;
2667   GtkWidget *widget;
2668
2669   hbox = gtk_hbox_new (FALSE, 12);
2670   gtk_widget_show (hbox);
2671
2672   /* Filter combo */
2673
2674   widget = filter_create (impl);
2675   gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
2676
2677   return hbox;
2678 }
2679
2680 static GtkWidget *
2681 create_path_bar (GtkFileChooserDefault *impl)
2682 {
2683   GtkWidget *path_bar;
2684
2685   path_bar = g_object_new (GTK_TYPE_PATH_BAR, NULL);
2686   _gtk_path_bar_set_file_system (GTK_PATH_BAR (path_bar), impl->file_system);
2687
2688   return path_bar;
2689 }
2690
2691 /* Creates the widgets for the files/folders pane */
2692 static GtkWidget *
2693 file_pane_create (GtkFileChooserDefault *impl,
2694                   GtkSizeGroup          *size_group)
2695 {
2696   GtkWidget *vbox;
2697   GtkWidget *hbox;
2698   GtkWidget *widget;
2699
2700   vbox = gtk_vbox_new (FALSE, 6);
2701   gtk_widget_show (vbox);
2702
2703   /* The path bar and 'Create Folder' button */
2704   hbox = gtk_hbox_new (FALSE, 12);
2705   gtk_widget_show (hbox);
2706   impl->browse_path_bar = create_path_bar (impl);
2707   g_signal_connect (impl->browse_path_bar, "path-clicked", G_CALLBACK (path_bar_clicked), impl);
2708   gtk_widget_show_all (impl->browse_path_bar);
2709   gtk_box_pack_start (GTK_BOX (hbox), impl->browse_path_bar, TRUE, TRUE, 0);
2710
2711   /* Create Folder */
2712   impl->browse_new_folder_button = gtk_button_new_with_mnemonic (_("Create _Folder"));
2713   g_signal_connect (impl->browse_new_folder_button, "clicked",
2714                     G_CALLBACK (new_folder_button_clicked), impl);
2715   gtk_box_pack_end (GTK_BOX (hbox), impl->browse_new_folder_button, FALSE, FALSE, 0);
2716   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
2717
2718   /* Box for lists and preview */
2719
2720   hbox = gtk_hbox_new (FALSE, PREVIEW_HBOX_SPACING);
2721   gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
2722   gtk_widget_show (hbox);
2723
2724   /* File list */
2725
2726   widget = create_file_list (impl);
2727   gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
2728
2729   /* Preview */
2730
2731   impl->preview_box = gtk_vbox_new (FALSE, 12);
2732   gtk_box_pack_start (GTK_BOX (hbox), impl->preview_box, FALSE, FALSE, 0);
2733   /* Don't show preview box initially */
2734
2735   /* Filename entry and filter combo */
2736   hbox = gtk_hbox_new (FALSE, 0);
2737   gtk_size_group_add_widget (size_group, hbox);
2738   widget = create_filename_entry_and_filter_combo (impl);
2739   gtk_box_pack_end (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
2740   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
2741   gtk_widget_show (hbox);
2742
2743   return vbox;
2744 }
2745 /* Callback used when the "Browse for more folders" expander is toggled */
2746 static void
2747 expander_changed_cb (GtkExpander           *expander,
2748                      GParamSpec            *pspec,
2749                      GtkFileChooserDefault *impl)
2750 {
2751   update_appearance (impl);
2752 }
2753
2754 /* Callback used when the selection changes in the save folder combo box */
2755 static void
2756 save_folder_combo_changed_cb (GtkComboBox           *combo,
2757                               GtkFileChooserDefault *impl)
2758 {
2759   int active;
2760
2761   if (impl->changing_folder)
2762     return;
2763
2764   active = gtk_combo_box_get_active (combo);
2765   if (active == -1)
2766     return;
2767
2768   shortcuts_activate_item (impl, active);
2769 }
2770
2771 /* Creates the combo box with the save folders */
2772 static GtkWidget *
2773 save_folder_combo_create (GtkFileChooserDefault *impl)
2774 {
2775   GtkWidget *combo;
2776   GtkCellRenderer *cell;
2777
2778   combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (impl->shortcuts_model));
2779   gtk_widget_show (combo);
2780
2781   cell = gtk_cell_renderer_pixbuf_new ();
2782   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE);
2783   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
2784                                   "pixbuf", SHORTCUTS_COL_PIXBUF,
2785                                   "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
2786                                   NULL);
2787
2788   cell = _gtk_cell_renderer_sep_text_new ();
2789   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
2790   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
2791                                   "text", SHORTCUTS_COL_NAME,
2792                                   NULL);
2793
2794   g_signal_connect (combo, "changed",
2795                     G_CALLBACK (save_folder_combo_changed_cb), impl);
2796
2797   return combo;
2798 }
2799
2800 /* Creates the widgets specific to Save mode */
2801 static GtkWidget *
2802 save_widgets_create (GtkFileChooserDefault *impl)
2803 {
2804   GtkWidget *vbox;
2805   GtkWidget *table;
2806   GtkWidget *widget;
2807   GtkWidget *alignment;
2808
2809   vbox = gtk_vbox_new (FALSE, 12);
2810
2811   table = gtk_table_new (2, 2, FALSE);
2812   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
2813   gtk_widget_show (table);
2814   gtk_table_set_row_spacings (GTK_TABLE (table), 12);
2815   gtk_table_set_col_spacings (GTK_TABLE (table), 12);
2816
2817   /* Name entry */
2818
2819   widget = gtk_label_new_with_mnemonic (_("_Name:"));
2820   gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
2821   gtk_table_attach (GTK_TABLE (table), widget,
2822                     0, 1, 0, 1,
2823                     GTK_FILL, GTK_FILL,
2824                     0, 0);
2825   gtk_widget_show (widget);
2826
2827   impl->save_file_name_entry = gtk_entry_new ();
2828   gtk_entry_set_width_chars (GTK_ENTRY (impl->save_file_name_entry), 45);
2829   gtk_entry_set_activates_default (GTK_ENTRY (impl->save_file_name_entry), TRUE);
2830   gtk_table_attach (GTK_TABLE (table), impl->save_file_name_entry,
2831                     1, 2, 0, 1,
2832                     GTK_EXPAND | GTK_FILL, 0,
2833                     0, 0);
2834   gtk_widget_show (impl->save_file_name_entry);
2835   gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->save_file_name_entry);
2836
2837   /* Folder combo */
2838   impl->save_folder_label = gtk_label_new (NULL);
2839   gtk_misc_set_alignment (GTK_MISC (impl->save_folder_label), 0.0, 0.5);
2840   gtk_table_attach (GTK_TABLE (table), impl->save_folder_label,
2841                     0, 1, 1, 2,
2842                     GTK_FILL, GTK_FILL,
2843                     0, 0);
2844   gtk_widget_show (impl->save_folder_label);
2845
2846   impl->save_folder_combo = save_folder_combo_create (impl);
2847   gtk_table_attach (GTK_TABLE (table), impl->save_folder_combo,
2848                     1, 2, 1, 2,
2849                     GTK_EXPAND | GTK_FILL, GTK_FILL,
2850                     0, 0);
2851   gtk_label_set_mnemonic_widget (GTK_LABEL (impl->save_folder_label), impl->save_folder_combo);
2852
2853   /* custom widget */
2854   impl->save_extra_align = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
2855   gtk_box_pack_start (GTK_BOX (vbox), impl->save_extra_align, FALSE, FALSE, 0);
2856
2857   /* Expander */
2858   alignment = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
2859   gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
2860
2861   impl->save_expander = gtk_expander_new_with_mnemonic (_("_Browse for other folders"));
2862   gtk_container_add (GTK_CONTAINER (alignment), impl->save_expander);
2863   g_signal_connect (impl->save_expander, "notify::expanded",
2864                     G_CALLBACK (expander_changed_cb),
2865                     impl);
2866   gtk_widget_show_all (alignment);
2867
2868   return vbox;
2869 }
2870
2871 /* Creates the main hpaned with the widgets shared by Open and Save mode */
2872 static GtkWidget *
2873 browse_widgets_create (GtkFileChooserDefault *impl)
2874 {
2875   GtkWidget *vbox;
2876   GtkWidget *hpaned;
2877   GtkWidget *widget;
2878   GtkSizeGroup *size_group;
2879
2880   /* size group is used by the [+][-] buttons and the filter combo */
2881   size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
2882   vbox = gtk_vbox_new (FALSE, 12);
2883
2884   /* Paned widget */
2885   hpaned = gtk_hpaned_new ();
2886   gtk_widget_show (hpaned);
2887   gtk_paned_set_position (GTK_PANED (hpaned), 200); /* FIXME: this sucks */
2888   gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
2889
2890   widget = shortcuts_pane_create (impl, size_group);
2891   gtk_paned_pack1 (GTK_PANED (hpaned), widget, FALSE, FALSE);
2892   widget = file_pane_create (impl, size_group);
2893   gtk_paned_pack2 (GTK_PANED (hpaned), widget, TRUE, FALSE);
2894
2895   g_object_unref (size_group);
2896
2897   /* Alignment to hold custom widget */
2898   impl->browse_extra_align = gtk_alignment_new (0.0, .5, 1.0, 1.0);
2899   gtk_box_pack_start (GTK_BOX (vbox), impl->browse_extra_align, FALSE, FALSE, 0);
2900
2901   return vbox;
2902 }
2903
2904 static GObject*
2905 gtk_file_chooser_default_constructor (GType                  type,
2906                                       guint                  n_construct_properties,
2907                                       GObjectConstructParam *construct_params)
2908 {
2909   GtkFileChooserDefault *impl;
2910   GObject *object;
2911
2912   object = parent_class->constructor (type,
2913                                       n_construct_properties,
2914                                       construct_params);
2915   impl = GTK_FILE_CHOOSER_DEFAULT (object);
2916
2917   g_assert (impl->file_system);
2918
2919   gtk_widget_push_composite_child ();
2920
2921   /* Shortcuts model */
2922
2923   shortcuts_model_create (impl);
2924
2925   /* Widgets for Save mode */
2926   impl->save_widgets = save_widgets_create (impl);
2927   gtk_box_pack_start (GTK_BOX (impl), impl->save_widgets, FALSE, FALSE, 0);
2928
2929   /* The browse widgets */
2930   impl->browse_widgets = browse_widgets_create (impl);
2931   gtk_box_pack_start (GTK_BOX (impl), impl->browse_widgets, TRUE, TRUE, 0);
2932
2933   gtk_widget_pop_composite_child ();
2934   update_appearance (impl);
2935
2936   return object;
2937 }
2938
2939 /* Sets the extra_widget by packing it in the appropriate place */
2940 static void
2941 set_extra_widget (GtkFileChooserDefault *impl,
2942                   GtkWidget             *extra_widget)
2943 {
2944   if (extra_widget)
2945     {
2946       g_object_ref (extra_widget);
2947       /* FIXME: is this right ? */
2948       gtk_widget_show (extra_widget);
2949     }
2950
2951   if (impl->extra_widget)
2952     g_object_unref (impl->extra_widget);
2953
2954   impl->extra_widget = extra_widget;
2955 }
2956
2957 static void
2958 set_local_only (GtkFileChooserDefault *impl,
2959                 gboolean               local_only)
2960 {
2961   if (local_only != impl->local_only)
2962     {
2963       impl->local_only = local_only;
2964
2965       if (impl->shortcuts_model && impl->file_system)
2966         {
2967           shortcuts_add_volumes (impl);
2968           shortcuts_add_bookmarks (impl);
2969         }
2970
2971       if (local_only &&
2972           !gtk_file_system_path_is_local (impl->file_system, impl->current_folder))
2973         {
2974           /* If we are pointing to a non-local folder, make an effort to change
2975            * back to a local folder, but it's really up to the app to not cause
2976            * such a situation, so we ignore errors.
2977            */
2978           const gchar *home = g_get_home_dir ();
2979           GtkFilePath *home_path = gtk_file_system_filename_to_path (impl->file_system, home);
2980
2981           _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), home_path, NULL);
2982
2983           gtk_file_path_free (home_path);
2984         }
2985     }
2986 }
2987
2988 static void
2989 volumes_changed_cb (GtkFileSystem         *file_system,
2990                     GtkFileChooserDefault *impl)
2991 {
2992   shortcuts_add_volumes (impl);
2993 }
2994
2995 /* Callback used when the set of bookmarks changes in the file system */
2996 static void
2997 bookmarks_changed_cb (GtkFileSystem         *file_system,
2998                       GtkFileChooserDefault *impl)
2999 {
3000   shortcuts_add_bookmarks (impl);
3001
3002   bookmarks_check_add_sensitivity (impl);
3003   bookmarks_check_remove_sensitivity (impl);
3004 }
3005
3006 /* Sets the file chooser to multiple selection mode */
3007 static void
3008 set_select_multiple (GtkFileChooserDefault *impl,
3009                      gboolean               select_multiple,
3010                      gboolean               property_notify)
3011 {
3012   GtkTreeSelection *selection;
3013   GtkSelectionMode mode;
3014
3015   if (select_multiple == impl->select_multiple)
3016     return;
3017
3018   mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE;
3019
3020   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3021   gtk_tree_selection_set_mode (selection, mode);
3022
3023   impl->select_multiple = select_multiple;
3024   g_object_notify (G_OBJECT (impl), "select-multiple");
3025
3026   /* FIXME #132255: See note in check_preview_change() */
3027   check_preview_change (impl);
3028 }
3029
3030 static void
3031 set_file_system_backend (GtkFileChooserDefault *impl,
3032                          const char *backend)
3033 {
3034   if (impl->file_system)
3035     {
3036       g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
3037       impl->volumes_changed_id = 0;
3038       g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
3039       impl->bookmarks_changed_id = 0;
3040       g_object_unref (impl->file_system);
3041     }
3042
3043   impl->file_system = NULL;
3044   if (backend)
3045     impl->file_system = _gtk_file_system_create (backend);
3046   else
3047     {
3048       GtkSettings *settings = gtk_settings_get_default ();
3049       gchar *default_backend = NULL;
3050
3051       g_object_get (settings, "gtk-file-chooser-backend", &default_backend, NULL);
3052       if (default_backend)
3053         {
3054           impl->file_system = _gtk_file_system_create (default_backend);
3055           g_free (default_backend);
3056         }
3057     }
3058
3059   if (!impl->file_system)
3060     {
3061 #if defined (G_OS_UNIX)
3062       impl->file_system = gtk_file_system_unix_new ();
3063 #elif defined (G_OS_WIN32)
3064       impl->file_system = gtk_file_system_win32_new ();
3065 #else
3066 #error "No default filesystem implementation on the platform"
3067 #endif
3068     }
3069
3070   if (impl->file_system)
3071     {
3072       impl->volumes_changed_id = g_signal_connect (impl->file_system, "volumes-changed",
3073                                                    G_CALLBACK (volumes_changed_cb),
3074                                                    impl);
3075       impl->bookmarks_changed_id = g_signal_connect (impl->file_system, "bookmarks-changed",
3076                                                      G_CALLBACK (bookmarks_changed_cb),
3077                                                      impl);
3078     }
3079 }
3080
3081 /* This function is basically a do_all function.
3082  *
3083  * It sets the visibility on all the widgets based on the current state, and
3084  * moves the custom_widget if needed.
3085  */
3086 static void
3087 update_appearance (GtkFileChooserDefault *impl)
3088 {
3089   GtkWidget *child;
3090
3091   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
3092       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
3093     {
3094       const char *text;
3095
3096       gtk_widget_show (impl->save_widgets);
3097
3098       if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
3099         text = _("Save in _folder:");
3100       else
3101         text = _("Create in _folder:");
3102
3103       gtk_label_set_text_with_mnemonic (GTK_LABEL (impl->save_folder_label), text);
3104
3105       if (gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
3106         {
3107           gtk_widget_set_sensitive (impl->save_folder_label, FALSE);
3108           gtk_widget_set_sensitive (impl->save_folder_combo, FALSE);
3109           gtk_widget_show (impl->browse_widgets);
3110         }
3111       else
3112         {
3113           gtk_widget_set_sensitive (impl->save_folder_label, TRUE);
3114           gtk_widget_set_sensitive (impl->save_folder_combo, TRUE);
3115           gtk_widget_hide (impl->browse_widgets);
3116         }
3117
3118       gtk_widget_show (impl->browse_new_folder_button);
3119
3120       if (impl->select_multiple)
3121         {
3122           g_warning ("Save mode cannot be set in conjunction with multiple selection mode.  "
3123                      "Re-setting to single selection mode.");
3124           set_select_multiple (impl, FALSE, TRUE);
3125         }
3126     }
3127   else if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
3128            impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
3129     {
3130       gtk_widget_hide (impl->save_widgets);
3131       gtk_widget_show (impl->browse_widgets);
3132     }
3133   /* FIXME: */
3134   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
3135       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
3136     {
3137       if (impl->browse_files_model)
3138         _gtk_file_system_model_set_show_files (impl->browse_files_model, FALSE);
3139     }
3140   else
3141     {
3142       if (impl->browse_files_model)
3143         _gtk_file_system_model_set_show_files (impl->browse_files_model, TRUE);
3144     }
3145
3146   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
3147     gtk_widget_hide (impl->browse_new_folder_button);
3148   else
3149     gtk_widget_show (impl->browse_new_folder_button);
3150
3151   if (impl->extra_widget)
3152     {
3153       GtkWidget *align;
3154       GtkWidget *unused_align;
3155
3156       if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
3157           || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
3158         {
3159           align = impl->save_extra_align;
3160           unused_align = impl->browse_extra_align;
3161         }
3162       else
3163         {
3164           align = impl->browse_extra_align;
3165           unused_align = impl->save_extra_align;
3166         }
3167
3168       /* We own a ref on extra_widget, so it's safe to do this */
3169       child = GTK_BIN (unused_align)->child;
3170       if (child)
3171         gtk_container_remove (GTK_CONTAINER (unused_align), child);
3172
3173       child = GTK_BIN (align)->child;
3174       if (child && child != impl->extra_widget)
3175         {
3176           gtk_container_remove (GTK_CONTAINER (align), child);
3177           gtk_container_add (GTK_CONTAINER (align), impl->extra_widget);
3178         }
3179       else if (child == NULL)
3180         {
3181           gtk_container_add (GTK_CONTAINER (align), impl->extra_widget);
3182         }
3183
3184       gtk_widget_show (align);
3185       gtk_widget_hide (unused_align);
3186     }
3187   else
3188     {
3189       child = GTK_BIN (impl->browse_extra_align)->child;
3190       if (child)
3191         gtk_container_remove (GTK_CONTAINER (impl->browse_extra_align), child);
3192
3193       child = GTK_BIN (impl->save_extra_align)->child;
3194       if (child)
3195         gtk_container_remove (GTK_CONTAINER (impl->save_extra_align), child);
3196
3197       gtk_widget_hide (impl->save_extra_align);
3198       gtk_widget_hide (impl->browse_extra_align);
3199     }
3200
3201   g_signal_emit_by_name (impl, "default-size-changed");
3202 }
3203
3204 static void
3205 gtk_file_chooser_default_set_property (GObject      *object,
3206                                        guint         prop_id,
3207                                        const GValue *value,
3208                                        GParamSpec   *pspec)
3209
3210 {
3211   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
3212
3213   switch (prop_id)
3214     {
3215     case GTK_FILE_CHOOSER_PROP_ACTION:
3216       {
3217         GtkFileChooserAction action = g_value_get_enum (value);
3218
3219         if (action != impl->action)
3220           {
3221             if (action == GTK_FILE_CHOOSER_ACTION_SAVE && impl->select_multiple)
3222               {
3223                 g_warning ("Multiple selection mode is not allowed in Save mode");
3224                 set_select_multiple (impl, FALSE, TRUE);
3225               }
3226             impl->action = action;
3227             update_appearance (impl);
3228           }
3229       }
3230       break;
3231     case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
3232       set_file_system_backend (impl, g_value_get_string (value));
3233       break;
3234     case GTK_FILE_CHOOSER_PROP_FILTER:
3235       set_current_filter (impl, g_value_get_object (value));
3236       break;
3237     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
3238       set_local_only (impl, g_value_get_boolean (value));
3239       break;
3240     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
3241       set_preview_widget (impl, g_value_get_object (value));
3242       break;
3243     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
3244       impl->preview_widget_active = g_value_get_boolean (value);
3245       update_preview_widget_visibility (impl);
3246       break;
3247     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
3248       impl->use_preview_label = g_value_get_boolean (value);
3249       update_preview_widget_visibility (impl);
3250       break;
3251     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
3252       set_extra_widget (impl, g_value_get_object (value));
3253       update_appearance (impl);
3254       break;
3255     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
3256       {
3257         gboolean select_multiple = g_value_get_boolean (value);
3258         if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE && select_multiple)
3259           {
3260             g_warning ("Multiple selection mode is not allowed in Save mode");
3261             return;
3262           }
3263
3264         set_select_multiple (impl, select_multiple, FALSE);
3265       }
3266       break;
3267     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
3268       {
3269         gboolean show_hidden = g_value_get_boolean (value);
3270         if (show_hidden != impl->show_hidden)
3271           {
3272             impl->show_hidden = show_hidden;
3273             _gtk_file_system_model_set_show_hidden (GTK_FILE_SYSTEM_MODEL (impl->browse_files_model),
3274                                                     show_hidden);
3275           }
3276       }
3277       break;
3278     default:
3279       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3280       break;
3281     }
3282 }
3283
3284 static void
3285 gtk_file_chooser_default_get_property (GObject    *object,
3286                                        guint       prop_id,
3287                                        GValue     *value,
3288                                        GParamSpec *pspec)
3289 {
3290   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
3291
3292   switch (prop_id)
3293     {
3294     case GTK_FILE_CHOOSER_PROP_ACTION:
3295       g_value_set_enum (value, impl->action);
3296       break;
3297     case GTK_FILE_CHOOSER_PROP_FILTER:
3298       g_value_set_object (value, impl->current_filter);
3299       break;
3300     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
3301       g_value_set_boolean (value, impl->local_only);
3302       break;
3303     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
3304       g_value_set_object (value, impl->preview_widget);
3305       break;
3306     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
3307       g_value_set_boolean (value, impl->preview_widget_active);
3308       break;
3309     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
3310       g_value_set_boolean (value, impl->use_preview_label);
3311       break;
3312     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
3313       g_value_set_object (value, impl->extra_widget);
3314       break;
3315     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
3316       g_value_set_boolean (value, impl->select_multiple);
3317       break;
3318     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
3319       g_value_set_boolean (value, impl->show_hidden);
3320       break;
3321     default:
3322       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3323       break;
3324     }
3325 }
3326
3327 /* Removes the settings signal handler.  It's safe to call multiple times */
3328 static void
3329 remove_settings_signal (GtkFileChooserDefault *impl,
3330                         GdkScreen             *screen)
3331 {
3332   if (impl->settings_signal_id)
3333     {
3334       GtkSettings *settings;
3335
3336       settings = gtk_settings_get_for_screen (screen);
3337       g_signal_handler_disconnect (settings,
3338                                    impl->settings_signal_id);
3339       impl->settings_signal_id = 0;
3340     }
3341 }
3342
3343 static void
3344 gtk_file_chooser_default_dispose (GObject *object)
3345 {
3346   GtkFileChooserDefault *impl = (GtkFileChooserDefault *) object;
3347
3348   if (impl->extra_widget)
3349     {
3350       g_object_unref (impl->extra_widget);
3351       impl->extra_widget = NULL;
3352     }
3353
3354   remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl)));
3355
3356   G_OBJECT_CLASS (parent_class)->dispose (object);
3357 }
3358
3359 /* We override show-all since we have internal widgets that
3360  * shouldn't be shown when you call show_all(), like the filter
3361  * combo box.
3362  */
3363 static void
3364 gtk_file_chooser_default_show_all (GtkWidget *widget)
3365 {
3366   GtkFileChooserDefault *impl = (GtkFileChooserDefault *) widget;
3367
3368   gtk_widget_show (widget);
3369
3370   if (impl->extra_widget)
3371     gtk_widget_show_all (impl->extra_widget);
3372 }
3373
3374 /* Changes the icons wherever it is needed */
3375 static void
3376 change_icon_theme (GtkFileChooserDefault *impl)
3377 {
3378   GtkSettings *settings;
3379   gint width, height;
3380
3381   settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
3382
3383   if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_SMALL_TOOLBAR, &width, &height))
3384     impl->icon_size = MAX (width, height);
3385   else
3386     impl->icon_size = FALLBACK_ICON_SIZE;
3387
3388   shortcuts_reload_icons (impl);
3389   gtk_widget_queue_resize (impl->browse_files_tree_view);
3390 }
3391
3392 /* Callback used when a GtkSettings value changes */
3393 static void
3394 settings_notify_cb (GObject               *object,
3395                     GParamSpec            *pspec,
3396                     GtkFileChooserDefault *impl)
3397 {
3398   const char *name;
3399
3400   name = g_param_spec_get_name (pspec);
3401
3402   if (strcmp (name, "gtk-icon-theme-name") == 0
3403       || strcmp (name, "gtk-icon-sizes") == 0)
3404     change_icon_theme (impl);
3405 }
3406
3407 /* Installs a signal handler for GtkSettings so that we can monitor changes in
3408  * the icon theme.
3409  */
3410 static void
3411 check_icon_theme (GtkFileChooserDefault *impl)
3412 {
3413   GtkSettings *settings;
3414
3415   if (impl->settings_signal_id)
3416     return;
3417
3418   if (gtk_widget_has_screen (GTK_WIDGET (impl)))
3419     {
3420       settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
3421       impl->settings_signal_id = g_signal_connect (settings, "notify",
3422                                                    G_CALLBACK (settings_notify_cb), impl);
3423
3424       change_icon_theme (impl);
3425     }
3426 }
3427
3428 static void
3429 gtk_file_chooser_default_style_set      (GtkWidget *widget,
3430                                          GtkStyle  *previous_style)
3431 {
3432   GtkFileChooserDefault *impl;
3433
3434   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
3435
3436   if (GTK_WIDGET_CLASS (parent_class)->style_set)
3437     GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
3438
3439   check_icon_theme (impl);
3440
3441   g_signal_emit_by_name (widget, "default-size-changed");
3442 }
3443
3444 static void
3445 gtk_file_chooser_default_screen_changed (GtkWidget *widget,
3446                                          GdkScreen *previous_screen)
3447 {
3448   GtkFileChooserDefault *impl;
3449
3450   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
3451
3452   if (GTK_WIDGET_CLASS (parent_class)->screen_changed)
3453     GTK_WIDGET_CLASS (parent_class)->screen_changed (widget, previous_screen);
3454
3455   remove_settings_signal (impl, previous_screen);
3456   check_icon_theme (impl);
3457
3458   g_signal_emit_by_name (widget, "default-size-changed");
3459 }
3460
3461 static gboolean
3462 list_model_filter_func (GtkFileSystemModel *model,
3463                         GtkFilePath        *path,
3464                         const GtkFileInfo  *file_info,
3465                         gpointer            user_data)
3466 {
3467   GtkFileChooserDefault *impl = user_data;
3468   GtkFileFilterInfo filter_info;
3469   GtkFileFilterFlags needed;
3470   gboolean result;
3471
3472   if (!impl->current_filter)
3473     return TRUE;
3474
3475   if (gtk_file_info_get_is_folder (file_info))
3476     return TRUE;
3477
3478   filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
3479
3480   needed = gtk_file_filter_get_needed (impl->current_filter);
3481
3482   filter_info.display_name = gtk_file_info_get_display_name (file_info);
3483   filter_info.mime_type = gtk_file_info_get_mime_type (file_info);
3484
3485   if (needed & GTK_FILE_FILTER_FILENAME)
3486     {
3487       filter_info.filename = gtk_file_system_path_to_filename (impl->file_system, path);
3488       if (filter_info.filename)
3489         filter_info.contains |= GTK_FILE_FILTER_FILENAME;
3490     }
3491   else
3492     filter_info.filename = NULL;
3493
3494   if (needed & GTK_FILE_FILTER_URI)
3495     {
3496       filter_info.uri = gtk_file_system_path_to_uri (impl->file_system, path);
3497       if (filter_info.uri)
3498         filter_info.contains |= GTK_FILE_FILTER_URI;
3499     }
3500   else
3501     filter_info.uri = NULL;
3502
3503   result = gtk_file_filter_filter (impl->current_filter, &filter_info);
3504
3505   if (filter_info.filename)
3506     g_free ((gchar *)filter_info.filename);
3507   if (filter_info.uri)
3508     g_free ((gchar *)filter_info.uri);
3509
3510   return result;
3511 }
3512
3513 static void
3514 install_list_model_filter (GtkFileChooserDefault *impl)
3515 {
3516   if (impl->current_filter)
3517     _gtk_file_system_model_set_filter (impl->browse_files_model,
3518                                        list_model_filter_func,
3519                                        impl);
3520 }
3521
3522 #define COMPARE_DIRECTORIES                                                                                    \
3523   GtkFileChooserDefault *impl = user_data;                                                                     \
3524   const GtkFileInfo *info_a = _gtk_file_system_model_get_info (impl->browse_files_model, a);                           \
3525   const GtkFileInfo *info_b = _gtk_file_system_model_get_info (impl->browse_files_model, b);                           \
3526   gboolean dir_a, dir_b;                                                                                       \
3527                                                                                                                \
3528   if (info_a)                                                                                                  \
3529     dir_a = gtk_file_info_get_is_folder (info_a);                                                              \
3530   else                                                                                                         \
3531     return impl->list_sort_ascending ? -1 : 1;                                                                 \
3532                                                                                                                \
3533   if (info_b)                                                                                                  \
3534     dir_b = gtk_file_info_get_is_folder (info_b);                                                              \
3535   else                                                                                                         \
3536     return impl->list_sort_ascending ? 1 : -1;                                                                 \
3537                                                                                                                \
3538   if (dir_a != dir_b)                                                                                          \
3539     return impl->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */
3540
3541 /* Sort callback for the filename column */
3542 static gint
3543 name_sort_func (GtkTreeModel *model,
3544                 GtkTreeIter  *a,
3545                 GtkTreeIter  *b,
3546                 gpointer      user_data)
3547 {
3548   COMPARE_DIRECTORIES;
3549   else
3550     return strcmp (gtk_file_info_get_display_key (info_a), gtk_file_info_get_display_key (info_b));
3551 }
3552
3553 /* Sort callback for the size column */
3554 static gint
3555 size_sort_func (GtkTreeModel *model,
3556                 GtkTreeIter  *a,
3557                 GtkTreeIter  *b,
3558                 gpointer      user_data)
3559 {
3560   COMPARE_DIRECTORIES;
3561   else
3562     {
3563       gint64 size_a = gtk_file_info_get_size (info_a);
3564       gint64 size_b = gtk_file_info_get_size (info_b);
3565
3566       return size_a > size_b ? -1 : (size_a == size_b ? 0 : 1);
3567     }
3568 }
3569
3570 /* Sort callback for the mtime column */
3571 static gint
3572 mtime_sort_func (GtkTreeModel *model,
3573                  GtkTreeIter  *a,
3574                  GtkTreeIter  *b,
3575                  gpointer      user_data)
3576 {
3577   COMPARE_DIRECTORIES;
3578   else
3579     {
3580       GtkFileTime ta = gtk_file_info_get_modification_time (info_a);
3581       GtkFileTime tb = gtk_file_info_get_modification_time (info_b);
3582
3583       return ta > tb ? -1 : (ta == tb ? 0 : 1);
3584     }
3585 }
3586
3587 /* Callback used when the sort column changes.  We cache the sort order for use
3588  * in name_sort_func().
3589  */
3590 static void
3591 list_sort_column_changed_cb (GtkTreeSortable       *sortable,
3592                              GtkFileChooserDefault *impl)
3593 {
3594   GtkSortType sort_type;
3595
3596   if (gtk_tree_sortable_get_sort_column_id (sortable, NULL, &sort_type))
3597     impl->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
3598 }
3599
3600 /* Gets rid of the old list model and creates a new one for the current folder */
3601 static void
3602 set_list_model (GtkFileChooserDefault *impl)
3603 {
3604   if (impl->browse_files_model)
3605     {
3606       g_object_unref (impl->browse_files_model);
3607       g_object_unref (impl->sort_model);
3608     }
3609
3610   impl->browse_files_model = _gtk_file_system_model_new (impl->file_system,
3611                                                          impl->current_folder, 0,
3612                                                          GTK_FILE_INFO_ALL);
3613   _gtk_file_system_model_set_show_hidden (impl->browse_files_model, impl->show_hidden);
3614   switch (impl->action)
3615     {
3616     case GTK_FILE_CHOOSER_ACTION_OPEN:
3617     case GTK_FILE_CHOOSER_ACTION_SAVE:
3618       _gtk_file_system_model_set_show_files (impl->browse_files_model, TRUE);
3619       break;
3620     case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
3621     case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
3622       _gtk_file_system_model_set_show_files (impl->browse_files_model, FALSE);
3623       break;
3624     default:
3625       g_assert_not_reached ();
3626     }
3627   install_list_model_filter (impl);
3628
3629   impl->sort_model = (GtkTreeModelSort *)gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (impl->browse_files_model));
3630   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, name_sort_func, impl, NULL);
3631   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_SIZE, size_sort_func, impl, NULL);
3632   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_MTIME, mtime_sort_func, impl, NULL);
3633   gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->sort_model), NULL, NULL, NULL);
3634   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, GTK_SORT_ASCENDING);
3635   impl->list_sort_ascending = TRUE;
3636
3637   g_signal_connect (impl->sort_model, "sort-column-changed",
3638                     G_CALLBACK (list_sort_column_changed_cb), impl);
3639
3640   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
3641                            GTK_TREE_MODEL (impl->sort_model));
3642   gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->browse_files_tree_view));
3643   gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_files_tree_view),
3644                                    GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
3645 }
3646
3647 static void
3648 update_chooser_entry (GtkFileChooserDefault *impl)
3649 {
3650   GtkTreeSelection *selection;
3651   const GtkFileInfo *info;
3652   GtkTreeIter iter;
3653   GtkTreeIter child_iter;
3654
3655   if (impl->action != GTK_FILE_CHOOSER_ACTION_SAVE)
3656     return;
3657
3658   g_assert (!impl->select_multiple);
3659   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3660
3661   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
3662     return;
3663
3664   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
3665                                                   &child_iter,
3666                                                   &iter);
3667
3668   info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
3669
3670   if (!gtk_file_info_get_is_folder (info))
3671     gtk_entry_set_text (GTK_ENTRY (impl->save_file_name_entry),
3672                         gtk_file_info_get_display_name (info));
3673 }
3674
3675 static gboolean
3676 gtk_file_chooser_default_set_current_folder (GtkFileChooser    *chooser,
3677                                              const GtkFilePath *path,
3678                                              GError           **error)
3679 {
3680   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3681
3682   /* Test validity of path here.  */
3683   if (!check_is_folder (impl->file_system, path, error))
3684     return FALSE;
3685
3686   if (impl->local_only &&
3687       !gtk_file_system_path_is_local (impl->file_system, path))
3688     {
3689       g_set_error (error,
3690                    GTK_FILE_SYSTEM_ERROR,
3691                    GTK_FILE_SYSTEM_ERROR_FAILED,
3692                    _("Can't change to folder because it isn't local"));
3693
3694       return FALSE;
3695     }
3696
3697   if (!_gtk_path_bar_set_path (GTK_PATH_BAR (impl->browse_path_bar), path, error))
3698     return FALSE;
3699
3700   if (impl->current_folder != path)
3701     {
3702       if (impl->current_folder)
3703         gtk_file_path_free (impl->current_folder);
3704
3705       impl->current_folder = gtk_file_path_copy (path);
3706     }
3707
3708   /* Update the widgets that may trigger a folder change themselves.  */
3709
3710   if (!impl->changing_folder)
3711     {
3712       impl->changing_folder = TRUE;
3713
3714       shortcuts_update_current_folder (impl);
3715
3716       impl->changing_folder = FALSE;
3717     }
3718
3719   /* Create a new list model */
3720   set_list_model (impl);
3721
3722   /* Refresh controls */
3723
3724   shortcuts_find_current_folder (impl);
3725
3726   g_signal_emit_by_name (impl, "current-folder-changed", 0);
3727
3728   check_preview_change (impl);
3729   bookmarks_check_add_sensitivity (impl);
3730
3731   g_signal_emit_by_name (impl, "selection-changed", 0);
3732
3733   return TRUE;
3734 }
3735
3736 static GtkFilePath *
3737 gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser)
3738 {
3739   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3740
3741   return gtk_file_path_copy (impl->current_folder);
3742 }
3743
3744 static void
3745 gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
3746                                            const gchar    *name)
3747 {
3748   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3749
3750   g_return_if_fail (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
3751                     || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
3752
3753   gtk_entry_set_text (GTK_ENTRY (impl->save_file_name_entry), name);
3754 }
3755
3756 static void
3757 select_func (GtkFileSystemModel *model,
3758              GtkTreePath        *path,
3759              GtkTreeIter        *iter,
3760              gpointer            user_data)
3761 {
3762   GtkFileChooserDefault *impl = user_data;
3763   GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
3764   GtkTreePath *sorted_path;
3765
3766   sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model, path);
3767   gtk_tree_view_set_cursor (tree_view, sorted_path, NULL, FALSE);
3768   gtk_tree_path_free (sorted_path);
3769 }
3770
3771 static gboolean
3772 gtk_file_chooser_default_select_path (GtkFileChooser    *chooser,
3773                                       const GtkFilePath *path,
3774                                       GError           **error)
3775 {
3776   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3777   GtkFilePath *parent_path;
3778
3779   if (!gtk_file_system_get_parent (impl->file_system, path, &parent_path, error))
3780     return FALSE;
3781
3782   if (!parent_path)
3783     return _gtk_file_chooser_set_current_folder_path (chooser, path, error);
3784   else
3785     {
3786       gboolean result;
3787
3788       result = _gtk_file_chooser_set_current_folder_path (chooser, parent_path, error);
3789       gtk_file_path_free (parent_path);
3790       _gtk_file_system_model_path_do (impl->browse_files_model, path,
3791                                       select_func, impl);
3792
3793       return result;
3794     }
3795
3796   g_assert_not_reached ();
3797 }
3798
3799 static void
3800 unselect_func (GtkFileSystemModel *model,
3801                GtkTreePath        *path,
3802                GtkTreeIter        *iter,
3803                gpointer            user_data)
3804 {
3805   GtkFileChooserDefault *impl = user_data;
3806   GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
3807   GtkTreePath *sorted_path;
3808
3809   sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model,
3810                                                                 path);
3811   gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (tree_view),
3812                                     sorted_path);
3813   gtk_tree_path_free (sorted_path);
3814 }
3815
3816 static void
3817 gtk_file_chooser_default_unselect_path (GtkFileChooser    *chooser,
3818                                         const GtkFilePath *path)
3819 {
3820   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3821
3822   _gtk_file_system_model_path_do (impl->browse_files_model, path,
3823                                  unselect_func, impl);
3824 }
3825
3826 static void
3827 gtk_file_chooser_default_select_all (GtkFileChooser *chooser)
3828 {
3829   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3830   if (impl->select_multiple)
3831     {
3832       GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3833       gtk_tree_selection_select_all (selection);
3834     }
3835 }
3836
3837 static void
3838 gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser)
3839 {
3840   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3841   GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3842
3843   gtk_tree_selection_unselect_all (selection);
3844 }
3845
3846 /* Checks whether the filename entry for the Save modes contains a valid filename */
3847 static GtkFilePath *
3848 check_save_entry (GtkFileChooserDefault *impl,
3849                   gboolean              *is_valid,
3850                   gboolean              *is_empty)
3851 {
3852   const char *filename;
3853   GtkFilePath *path;
3854   GError *error;
3855
3856   g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
3857             || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
3858
3859   filename = gtk_entry_get_text (GTK_ENTRY (impl->save_file_name_entry));
3860
3861   if (!filename || filename[0] == '\0')
3862     {
3863       *is_valid = FALSE;
3864       *is_empty = TRUE;
3865       return NULL;
3866     }
3867
3868   *is_empty = FALSE;
3869
3870   error = NULL;
3871   path = gtk_file_system_make_path (impl->file_system, impl->current_folder, filename, &error);
3872
3873   if (!path)
3874     {
3875       error_building_filename_dialog (impl, impl->current_folder, filename, error);
3876       *is_valid = FALSE;
3877       return NULL;
3878     }
3879
3880   *is_valid = TRUE;
3881   return path;
3882 }
3883
3884 struct get_paths_closure {
3885   GtkFileChooserDefault *impl;
3886   GSList *result;
3887   GtkFilePath *path_from_entry;
3888 };
3889
3890 static void
3891 get_paths_foreach (GtkTreeModel *model,
3892                    GtkTreePath  *path,
3893                    GtkTreeIter  *iter,
3894                    gpointer      data)
3895 {
3896   struct get_paths_closure *info;
3897   const GtkFilePath *file_path;
3898   GtkFileSystemModel *fs_model;
3899   GtkTreeIter sel_iter;
3900
3901   info = data;
3902   fs_model = info->impl->browse_files_model;
3903   gtk_tree_model_sort_convert_iter_to_child_iter (info->impl->sort_model, &sel_iter, iter);
3904
3905   file_path = _gtk_file_system_model_get_path (GTK_FILE_SYSTEM_MODEL (fs_model), &sel_iter);
3906   if (!file_path)
3907     return; /* We are on the editable row */
3908
3909   if (!info->path_from_entry
3910       || gtk_file_path_compare (info->path_from_entry, file_path) != 0)
3911     info->result = g_slist_prepend (info->result, gtk_file_path_copy (file_path));
3912 }
3913
3914 static GSList *
3915 gtk_file_chooser_default_get_paths (GtkFileChooser *chooser)
3916 {
3917   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3918   struct get_paths_closure info;
3919
3920   info.impl = impl;
3921   info.result = NULL;
3922   info.path_from_entry = NULL;
3923
3924   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
3925       || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
3926     {
3927       gboolean is_valid, is_empty;
3928
3929       info.path_from_entry = check_save_entry (impl, &is_valid, &is_empty);
3930       if (!is_valid && !is_empty)
3931         return NULL;
3932     }
3933
3934   if (!info.path_from_entry || impl->select_multiple)
3935     {
3936       GtkTreeSelection *selection;
3937
3938       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3939       gtk_tree_selection_selected_foreach (selection, get_paths_foreach, &info);
3940     }
3941
3942   if (info.path_from_entry)
3943     info.result = g_slist_prepend (info.result, info.path_from_entry);
3944
3945   /* If there's no folder selected, and we're in SELECT_FOLDER mode, then we
3946    * fall back to the current directory */
3947   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
3948       info.result == NULL)
3949     {
3950       info.result = g_slist_prepend (info.result, gtk_file_path_copy (impl->current_folder));
3951     }
3952
3953   return g_slist_reverse (info.result);
3954 }
3955
3956 static GtkFilePath *
3957 gtk_file_chooser_default_get_preview_path (GtkFileChooser *chooser)
3958 {
3959   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3960
3961   if (impl->preview_path)
3962     return gtk_file_path_copy (impl->preview_path);
3963   else
3964     return NULL;
3965 }
3966
3967 static GtkFileSystem *
3968 gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser)
3969 {
3970   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3971
3972   return impl->file_system;
3973 }
3974
3975 /* Shows or hides the filter widgets */
3976 static void
3977 toolbar_show_filters (GtkFileChooserDefault *impl,
3978                       gboolean               show)
3979 {
3980   if (show)
3981     gtk_widget_show (impl->filter_combo);
3982   else
3983     gtk_widget_hide (impl->filter_combo);
3984 }
3985
3986 static void
3987 gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
3988                                      GtkFileFilter  *filter)
3989 {
3990   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3991   const gchar *name;
3992
3993   if (g_slist_find (impl->filters, filter))
3994     {
3995       g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
3996       return;
3997     }
3998
3999   g_object_ref (filter);
4000   gtk_object_sink (GTK_OBJECT (filter));
4001   impl->filters = g_slist_append (impl->filters, filter);
4002
4003   name = gtk_file_filter_get_name (filter);
4004   if (!name)
4005     name = "Untitled filter";   /* Place-holder, doesn't need to be marked for translation */
4006
4007   gtk_combo_box_append_text (GTK_COMBO_BOX (impl->filter_combo), name);
4008
4009   if (!g_slist_find (impl->filters, impl->current_filter))
4010     set_current_filter (impl, filter);
4011
4012   toolbar_show_filters (impl, TRUE);
4013 }
4014
4015 static void
4016 gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
4017                                         GtkFileFilter  *filter)
4018 {
4019   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4020   GtkTreeModel *model;
4021   GtkTreeIter iter;
4022   gint filter_index;
4023
4024   filter_index = g_slist_index (impl->filters, filter);
4025
4026   if (filter_index < 0)
4027     {
4028       g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
4029       return;
4030     }
4031
4032   impl->filters = g_slist_remove (impl->filters, filter);
4033
4034   if (filter == impl->current_filter)
4035     {
4036       if (impl->filters)
4037         set_current_filter (impl, impl->filters->data);
4038       else
4039         set_current_filter (impl, NULL);
4040     }
4041
4042   /* Remove row from the combo box */
4043   model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->filter_combo));
4044   gtk_tree_model_iter_nth_child  (model, &iter, NULL, filter_index);
4045   gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
4046
4047   g_object_unref (filter);
4048
4049   if (!impl->filters)
4050     toolbar_show_filters (impl, FALSE);
4051 }
4052
4053 static GSList *
4054 gtk_file_chooser_default_list_filters (GtkFileChooser *chooser)
4055 {
4056   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4057
4058   return g_slist_copy (impl->filters);
4059 }
4060
4061 /* Returns the position in the shortcuts tree where the nth specified shortcut would appear */
4062 static int
4063 shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl,
4064                                        int                    pos)
4065 {
4066   return pos + shortcuts_get_index (impl, SHORTCUTS_SHORTCUTS);
4067 }
4068
4069 static gboolean
4070 gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser    *chooser,
4071                                               const GtkFilePath *path,
4072                                               GError           **error)
4073 {
4074   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4075   gboolean result;
4076   int pos;
4077
4078   /* Test validity of path here.  */
4079   if (!check_is_folder (impl->file_system, path, error))
4080     return FALSE;
4081
4082   pos = shortcuts_get_pos_for_shortcut_folder (impl, impl->num_shortcuts);
4083
4084   result = shortcuts_insert_path (impl, pos, FALSE, NULL, path, NULL, FALSE, error);
4085
4086   if (result)
4087     impl->num_shortcuts++;
4088
4089   if (impl->shortcuts_filter_model)
4090     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
4091
4092   return result;
4093 }
4094
4095 static gboolean
4096 gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
4097                                                  const GtkFilePath *path,
4098                                                  GError           **error)
4099 {
4100   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4101   int pos;
4102   GtkTreeIter iter;
4103   char *uri;
4104   int i;
4105
4106   if (impl->num_shortcuts == 0)
4107     goto out;
4108
4109   pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
4110   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
4111     g_assert_not_reached ();
4112
4113   for (i = 0; i < impl->num_shortcuts; i++)
4114     {
4115       GtkFilePath *shortcut;
4116
4117       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &shortcut, -1);
4118       g_assert (shortcut != NULL);
4119
4120       if (gtk_file_path_compare (shortcut, path) == 0)
4121         {
4122           /* The other columns are freed by the GtkTreeStore */
4123           gtk_file_path_free (shortcut);
4124           gtk_list_store_remove (impl->shortcuts_model, &iter);
4125           impl->num_shortcuts--;
4126           return TRUE;
4127         }
4128
4129       if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
4130         g_assert_not_reached ();
4131     }
4132
4133  out:
4134
4135   uri = gtk_file_system_path_to_uri (impl->file_system, path);
4136   g_set_error (error,
4137                GTK_FILE_CHOOSER_ERROR,
4138                GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
4139                _("shortcut %s does not exist"),
4140                uri);
4141   g_free (uri);
4142
4143   return FALSE;
4144 }
4145
4146 static GSList *
4147 gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser)
4148 {
4149   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4150   int pos;
4151   GtkTreeIter iter;
4152   int i;
4153   GSList *list;
4154
4155   if (impl->num_shortcuts == 0)
4156     return NULL;
4157
4158   pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
4159   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
4160     g_assert_not_reached ();
4161
4162   list = NULL;
4163
4164   for (i = 0; i < impl->num_shortcuts; i++)
4165     {
4166       GtkFilePath *shortcut;
4167
4168       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &shortcut, -1);
4169       g_assert (shortcut != NULL);
4170
4171       list = g_slist_prepend (list, gtk_file_path_copy (shortcut));
4172
4173       if (i != impl->num_shortcuts - 1)
4174         {
4175           if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
4176             g_assert_not_reached ();
4177         }
4178     }
4179
4180   return g_slist_reverse (list);
4181 }
4182
4183 /* Guesses a size based upon font sizes */
4184 static void
4185 find_good_size_from_style (GtkWidget *widget,
4186                            gint      *width,
4187                            gint      *height)
4188 {
4189   GtkFileChooserDefault *impl;
4190   gint default_width, default_height;
4191   int font_size;
4192   GtkRequisition req;
4193   GtkRequisition preview_req;
4194
4195   g_assert (widget->style != NULL);
4196   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
4197
4198   font_size = pango_font_description_get_size (widget->style->font_desc);
4199   font_size = PANGO_PIXELS (font_size);
4200
4201   default_width = font_size * NUM_CHARS;
4202   default_height = font_size * NUM_LINES;
4203
4204   /* Use at least the requisition size not including the preview widget */
4205   gtk_widget_size_request (widget, &req);
4206
4207   if (impl->preview_widget_active && impl->preview_widget)
4208     gtk_widget_size_request (impl->preview_box, &preview_req);
4209   else
4210     preview_req.width = 0;
4211
4212   default_width = MAX (default_width, (req.width - (preview_req.width + PREVIEW_HBOX_SPACING)));
4213   default_height = MAX (default_height, req.height);
4214
4215   *width = default_width;
4216   *height = default_height;
4217 }
4218
4219 static void
4220 gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
4221                                            gint                *default_width,
4222                                            gint                *default_height)
4223 {
4224   GtkFileChooserDefault *impl;
4225
4226   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
4227
4228   find_good_size_from_style (GTK_WIDGET (chooser_embed), default_width, default_height);
4229
4230   if (impl->preview_widget_active && impl->preview_widget)
4231     *default_width += impl->preview_box->requisition.width + PREVIEW_HBOX_SPACING;
4232 }
4233
4234 static void
4235 gtk_file_chooser_default_get_resizable_hints (GtkFileChooserEmbed *chooser_embed,
4236                                               gboolean            *resize_horizontally,
4237                                               gboolean            *resize_vertically)
4238 {
4239   GtkFileChooserDefault *impl;
4240
4241   g_return_if_fail (resize_horizontally != NULL);
4242   g_return_if_fail (resize_vertically != NULL);
4243
4244   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
4245
4246   *resize_horizontally = TRUE;
4247   *resize_vertically = TRUE;
4248
4249   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
4250       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4251     {
4252       if (! gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
4253         {
4254           *resize_horizontally = FALSE;
4255           *resize_vertically = FALSE;
4256         }
4257     }
4258 }
4259
4260 struct switch_folder_closure {
4261   GtkFileChooserDefault *impl;
4262   const GtkFilePath *path;
4263   int num_selected;
4264 };
4265
4266 /* Used from gtk_tree_selection_selected_foreach() in switch_to_selected_folder() */
4267 static void
4268 switch_folder_foreach_cb (GtkTreeModel      *model,
4269                           GtkTreePath       *path,
4270                           GtkTreeIter       *iter,
4271                           gpointer           data)
4272 {
4273   struct switch_folder_closure *closure;
4274   GtkTreeIter child_iter;
4275
4276   closure = data;
4277
4278   gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
4279
4280   closure->path = _gtk_file_system_model_get_path (closure->impl->browse_files_model, &child_iter);
4281   closure->num_selected++;
4282 }
4283
4284 /* Changes to the selected folder in the list view */
4285 static void
4286 switch_to_selected_folder (GtkFileChooserDefault *impl)
4287 {
4288   GtkTreeSelection *selection;
4289   struct switch_folder_closure closure;
4290
4291   g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
4292             || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE);
4293
4294   /* We do this with foreach() rather than get_selected() as we may be in
4295    * multiple selection mode
4296    */
4297
4298   closure.impl = impl;
4299   closure.path = NULL;
4300   closure.num_selected = 0;
4301
4302   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4303   gtk_tree_selection_selected_foreach (selection, switch_folder_foreach_cb, &closure);
4304
4305   g_assert (closure.path && closure.num_selected == 1);
4306
4307   change_folder_and_display_error (impl, closure.path);
4308 }
4309
4310 /* Implementation for GtkFileChooserEmbed::should_respond() */
4311 static gboolean
4312 gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
4313 {
4314   GtkFileChooserDefault *impl;
4315   GtkTreeSelection *selection;
4316   int num_selected;
4317
4318   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
4319
4320   /* First, check the save entry.  If it has a valid name, we are done */
4321
4322   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
4323       || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4324     {
4325       GtkFilePath *path;
4326       gboolean is_valid, is_empty;
4327
4328       path = check_save_entry (impl, &is_valid, &is_empty);
4329
4330       if (is_valid)
4331         {
4332           gtk_file_path_free (path);
4333           return TRUE;
4334         }
4335       else if (!is_empty)
4336         return FALSE;
4337     }
4338
4339   /* Second, do we have an empty selection */
4340   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
4341       || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
4342     {
4343       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4344       num_selected = gtk_tree_selection_count_selected_rows (selection);
4345       if (num_selected == 0)
4346         return FALSE;
4347     }
4348
4349   /* Third, should we return file names or folder names? */
4350
4351   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
4352       || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
4353     {
4354       gboolean all_files, all_folders;
4355
4356       selection_check (impl, NULL, &all_files, &all_folders);
4357
4358       if (num_selected == 1)
4359         {
4360           if (all_folders)
4361             {
4362               switch_to_selected_folder (impl);
4363               return FALSE;
4364             }
4365           else if (all_files)
4366             return TRUE;
4367         }
4368       else
4369         return all_files;
4370     }
4371   else if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
4372            || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4373     /* There can be no files selected in folder mode since we don't show them,
4374      * anyway.
4375      */
4376     return TRUE;
4377
4378   g_assert_not_reached ();
4379   return FALSE;
4380 }
4381
4382 /* Implementation for GtkFileChooserEmbed::initial_focus() */
4383 static void
4384 gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed)
4385 {
4386   GtkFileChooserDefault *impl;
4387   GtkWidget *widget;
4388
4389   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
4390
4391   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
4392       || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
4393     {
4394       GtkTreePath *path;
4395
4396       path = gtk_tree_path_new_from_indices (0, -1);
4397       gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), path, NULL, FALSE);
4398       gtk_tree_path_free (path);
4399
4400       widget = impl->browse_files_tree_view;
4401     }
4402   else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
4403            || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4404     widget = impl->save_file_name_entry;
4405   else
4406     {
4407       g_assert_not_reached ();
4408       widget = NULL;
4409     }
4410
4411   gtk_widget_grab_focus (widget);
4412 }
4413
4414 static void
4415 set_current_filter (GtkFileChooserDefault *impl,
4416                     GtkFileFilter         *filter)
4417 {
4418   if (impl->current_filter != filter)
4419     {
4420       int filter_index;
4421
4422       /* If we have filters, new filter must be one of them
4423        */
4424       filter_index = g_slist_index (impl->filters, filter);
4425       if (impl->filters && filter_index < 0)
4426         return;
4427
4428       if (impl->current_filter)
4429         g_object_unref (impl->current_filter);
4430       impl->current_filter = filter;
4431       if (impl->current_filter)
4432         {
4433           g_object_ref (impl->current_filter);
4434           gtk_object_sink (GTK_OBJECT (filter));
4435         }
4436
4437       if (impl->filters)
4438         gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
4439                                   filter_index);
4440
4441       install_list_model_filter (impl);
4442
4443       g_object_notify (G_OBJECT (impl), "filter");
4444     }
4445 }
4446
4447 static void
4448 filter_combo_changed (GtkComboBox           *combo_box,
4449                       GtkFileChooserDefault *impl)
4450 {
4451   gint new_index = gtk_combo_box_get_active (combo_box);
4452   GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
4453
4454   set_current_filter (impl, new_filter);
4455 }
4456
4457 static void
4458 check_preview_change (GtkFileChooserDefault *impl)
4459 {
4460   const GtkFilePath *new_path = NULL;
4461   const GtkFileInfo *new_info = NULL;
4462
4463   /* FIXME #132255: Fixing preview for multiple selection involves getting the
4464    * full selection and diffing to find out what the most recently selected file
4465    * is; there is logic in GtkFileSelection that probably can be
4466    * copied.
4467    */
4468   if (impl->sort_model && !impl->select_multiple)
4469     {
4470       GtkTreeSelection *selection;
4471       GtkTreeIter iter;
4472
4473       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4474       if (gtk_tree_selection_get_selected (selection, NULL, &iter))
4475         {
4476           GtkTreeIter child_iter;
4477
4478           gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
4479                                                           &child_iter, &iter);
4480
4481           new_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
4482           new_info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
4483         }
4484     }
4485
4486   if (new_path != impl->preview_path &&
4487       !(new_path && impl->preview_path &&
4488         gtk_file_path_compare (new_path, impl->preview_path) == 0))
4489     {
4490       if (impl->preview_path)
4491         {
4492           gtk_file_path_free (impl->preview_path);
4493           g_free (impl->preview_display_name);
4494         }
4495
4496       if (new_path)
4497         {
4498           impl->preview_path = gtk_file_path_copy (new_path);
4499           impl->preview_display_name = g_strdup (gtk_file_info_get_display_name (new_info));
4500         }
4501       else
4502         {
4503           impl->preview_path = NULL;
4504           impl->preview_display_name = NULL;
4505         }
4506
4507       if (impl->use_preview_label && impl->preview_label)
4508         gtk_label_set_text (GTK_LABEL (impl->preview_label), impl->preview_display_name);
4509
4510       g_signal_emit_by_name (impl, "update-preview");
4511     }
4512 }
4513
4514 /* Activates a volume by mounting it if necessary and then switching to its
4515  * base path.
4516  */
4517 static void
4518 shortcuts_activate_volume (GtkFileChooserDefault *impl,
4519                            GtkFileSystemVolume   *volume)
4520 {
4521   GtkFilePath *path;
4522
4523   if (!gtk_file_system_volume_get_is_mounted (impl->file_system, volume))
4524     {
4525       GError *error;
4526
4527       error = NULL;
4528       if (!gtk_file_system_volume_mount (impl->file_system, volume, &error))
4529         {
4530           char *msg;
4531
4532           msg = g_strdup_printf ("Could not mount %s:\n%s",
4533                                  gtk_file_system_volume_get_display_name (impl->file_system, volume),
4534                                  error->message);
4535           error_message (impl, msg);
4536           g_free (msg);
4537           g_error_free (error);
4538
4539           return;
4540         }
4541     }
4542
4543   path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
4544   change_folder_and_display_error (impl, path);
4545   gtk_file_path_free (path);
4546 }
4547
4548 /* Opens the folder or volume at the specified index in the shortcuts list */
4549 static void
4550 shortcuts_activate_item (GtkFileChooserDefault *impl,
4551                          int                    item_num)
4552 {
4553   GtkTreePath *path;
4554   gboolean result;
4555   GtkTreeIter iter;
4556   gpointer data;
4557   int start_row;
4558
4559   if (item_num == shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR)
4560       || item_num == shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR))
4561     return;
4562
4563   path = gtk_tree_path_new_from_indices (item_num, -1);
4564   result = gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path);
4565   gtk_tree_path_free (path);
4566
4567   if (!result)
4568     return;
4569
4570   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &data, -1);
4571
4572   start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
4573   if ((item_num >= start_row && item_num < start_row + impl->num_volumes)
4574       || (item_num == shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER) && impl->shortcuts_current_folder_is_volume))
4575     {
4576       GtkFileSystemVolume *volume;
4577
4578       volume = data;
4579       shortcuts_activate_volume (impl, volume);
4580     }
4581   else
4582     {
4583       const GtkFilePath *file_path;
4584
4585       file_path = data;
4586       change_folder_and_display_error (impl, file_path);
4587     }
4588 }
4589
4590 /* Callback used when a row in the shortcuts list is activated */
4591 static void
4592 shortcuts_row_activated_cb (GtkTreeView           *tree_view,
4593                             GtkTreePath           *path,
4594                             GtkTreeViewColumn     *column,
4595                             GtkFileChooserDefault *impl)
4596 {
4597   int selected;
4598   GtkTreeIter iter;
4599   GtkTreeIter child_iter;
4600   GtkTreePath *child_path;
4601
4602   if (!gtk_tree_model_get_iter (impl->shortcuts_filter_model, &iter, path))
4603     return;
4604
4605   gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
4606                                                     &child_iter,
4607                                                     &iter);
4608   child_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &child_iter);
4609   if (!child_path)
4610     return;
4611
4612   selected = *gtk_tree_path_get_indices (child_path);
4613   gtk_tree_path_free (child_path);
4614
4615   shortcuts_activate_item (impl, selected);
4616 }
4617
4618 static gboolean
4619 shortcuts_select_func  (GtkTreeSelection  *selection,
4620                         GtkTreeModel      *model,
4621                         GtkTreePath       *path,
4622                         gboolean           path_currently_selected,
4623                         gpointer           data)
4624 {
4625   GtkFileChooserDefault *impl = data;
4626
4627   return (*gtk_tree_path_get_indices (path) != shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR));
4628 }
4629
4630 static void
4631 list_selection_changed (GtkTreeSelection      *selection,
4632                         GtkFileChooserDefault *impl)
4633 {
4634   /* See if we are in the new folder editable row for Save mode */
4635   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
4636     {
4637       GtkTreeSelection *selection;
4638       GtkTreeIter iter, child_iter;
4639       const GtkFileInfo *info;
4640
4641       g_assert (!impl->select_multiple);
4642       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4643       if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
4644         return;
4645
4646       gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
4647                                                       &child_iter,
4648                                                       &iter);
4649
4650       info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
4651       if (!info)
4652         return; /* We are on the editable row for New Folder */
4653     }
4654
4655   update_chooser_entry (impl);
4656   check_preview_change (impl);
4657   bookmarks_check_add_sensitivity (impl);
4658
4659   g_signal_emit_by_name (impl, "selection-changed", 0);
4660 }
4661
4662 /* Callback used when a row in the file list is activated */
4663 static void
4664 list_row_activated (GtkTreeView           *tree_view,
4665                     GtkTreePath           *path,
4666                     GtkTreeViewColumn     *column,
4667                     GtkFileChooserDefault *impl)
4668 {
4669   GtkTreeIter iter, child_iter;
4670   const GtkFileInfo *info;
4671
4672   if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
4673     return;
4674
4675   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
4676
4677   info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
4678
4679   if (gtk_file_info_get_is_folder (info))
4680     {
4681       const GtkFilePath *file_path;
4682
4683       file_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
4684       change_folder_and_display_error (impl, file_path);
4685
4686       return;
4687     }
4688
4689   g_signal_emit_by_name (impl, "file-activated");
4690 }
4691
4692 static void
4693 path_bar_clicked (GtkPathBar            *path_bar,
4694                   GtkFilePath           *file_path,
4695                   GtkFileChooserDefault *impl)
4696 {
4697   change_folder_and_display_error (impl, file_path);
4698 }
4699
4700 static const GtkFileInfo *
4701 get_list_file_info (GtkFileChooserDefault *impl,
4702                     GtkTreeIter           *iter)
4703 {
4704   GtkTreeIter child_iter;
4705
4706   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
4707                                                   &child_iter,
4708                                                   iter);
4709
4710   return _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
4711 }
4712
4713 static void
4714 list_icon_data_func (GtkTreeViewColumn *tree_column,
4715                      GtkCellRenderer   *cell,
4716                      GtkTreeModel      *tree_model,
4717                      GtkTreeIter       *iter,
4718                      gpointer           data)
4719 {
4720   GtkFileChooserDefault *impl = data;
4721   GtkTreeIter child_iter;
4722   const GtkFilePath *path;
4723   GdkPixbuf *pixbuf;
4724
4725   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
4726                                                   &child_iter,
4727                                                   iter);
4728   path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
4729   if (!path)
4730     return;
4731
4732   /* FIXME: NULL GError */
4733   pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
4734                                         impl->icon_size, NULL);
4735   g_object_set (cell,
4736                 "pixbuf", pixbuf,
4737                 NULL);
4738
4739   if (pixbuf)
4740     g_object_unref (pixbuf);
4741 }
4742
4743 static void
4744 list_name_data_func (GtkTreeViewColumn *tree_column,
4745                      GtkCellRenderer   *cell,
4746                      GtkTreeModel      *tree_model,
4747                      GtkTreeIter       *iter,
4748                      gpointer           data)
4749 {
4750   GtkFileChooserDefault *impl = data;
4751   const GtkFileInfo *info = get_list_file_info (impl, iter);
4752
4753   if (!info)
4754     {
4755       g_object_set (cell,
4756                     "text", _("Type name of new folder"),
4757                     NULL);
4758       return;
4759     }
4760
4761   g_object_set (cell,
4762                 "text", gtk_file_info_get_display_name (info),
4763                 NULL);
4764 }
4765
4766 #if 0
4767 static void
4768 list_size_data_func (GtkTreeViewColumn *tree_column,
4769                      GtkCellRenderer   *cell,
4770                      GtkTreeModel      *tree_model,
4771                      GtkTreeIter       *iter,
4772                      gpointer           data)
4773 {
4774   GtkFileChooserDefault *impl = data;
4775   const GtkFileInfo *info = get_list_file_info (impl, iter);
4776   gint64 size;
4777   gchar *str;
4778
4779   if (!info || gtk_file_info_get_is_folder (info))
4780     return;
4781
4782   size = gtk_file_info_get_size (info);
4783
4784   if (size < (gint64)1024)
4785     str = g_strdup_printf (ngettext ("%d byte", "%d bytes", (gint)size), (gint)size);
4786   else if (size < (gint64)1024*1024)
4787     str = g_strdup_printf (_("%.1f K"), size / (1024.));
4788   else if (size < (gint64)1024*1024*1024)
4789     str = g_strdup_printf (_("%.1f M"), size / (1024.*1024.));
4790   else
4791     str = g_strdup_printf (_("%.1f G"), size / (1024.*1024.*1024.));
4792
4793   g_object_set (cell,
4794                 "text", str,
4795                 NULL);
4796
4797   g_free (str);
4798 }
4799 #endif
4800
4801 /* Tree column data callback for the file list; fetches the mtime of a file */
4802 static void
4803 list_mtime_data_func (GtkTreeViewColumn *tree_column,
4804                       GtkCellRenderer   *cell,
4805                       GtkTreeModel      *tree_model,
4806                       GtkTreeIter       *iter,
4807                       gpointer           data)
4808 {
4809   GtkFileChooserDefault *impl;
4810   const GtkFileInfo *info;
4811   GtkFileTime time_mtime, time_now;
4812   GDate mtime, now;
4813   int days_diff;
4814   char buf[256];
4815
4816   impl = data;
4817
4818   info = get_list_file_info (impl, iter);
4819   if (!info)
4820     {
4821       g_object_set (cell,
4822                     "text", "",
4823                     NULL);
4824       return;
4825     }
4826
4827   time_mtime = gtk_file_info_get_modification_time (info);
4828   g_date_set_time (&mtime, (GTime) time_mtime);
4829
4830   time_now = (GTime ) time (NULL);
4831   g_date_set_time (&now, (GTime) time_now);
4832
4833   days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
4834
4835   if (days_diff == 0)
4836     strcpy (buf, _("Today"));
4837   else if (days_diff == 1)
4838     strcpy (buf, _("Yesterday"));
4839   else
4840     {
4841       char *format;
4842
4843       if (days_diff > 1 && days_diff < 7)
4844         format = "%A"; /* Days from last week */
4845       else
4846         format = "%x"; /* Any other date */
4847
4848       if (g_date_strftime (buf, sizeof (buf), format, &mtime) == 0)
4849         strcpy (buf, _("Unknown"));
4850     }
4851
4852   g_object_set (cell,
4853                 "text", buf,
4854                 NULL);
4855 }
4856
4857 GtkWidget *
4858 _gtk_file_chooser_default_new (const char *file_system)
4859 {
4860   return  g_object_new (GTK_TYPE_FILE_CHOOSER_DEFAULT,
4861                         "file-system-backend", file_system,
4862                         NULL);
4863 }
4864
4865 static GtkWidget *
4866 location_entry_create (GtkFileChooserDefault *impl)
4867 {
4868   GtkWidget *entry;
4869
4870   entry = _gtk_file_chooser_entry_new ();
4871   /* Pick a good width for the entry */
4872   gtk_entry_set_width_chars (GTK_ENTRY (entry), 30);
4873   gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
4874   _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (entry), impl->file_system);
4875   _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (entry), impl->current_folder);
4876
4877   return GTK_WIDGET (entry);
4878 }
4879
4880 static gboolean
4881 update_from_entry (GtkFileChooserDefault *impl,
4882                    GtkWindow             *parent,
4883                    GtkFileChooserEntry   *chooser_entry)
4884 {
4885   const GtkFilePath *folder_path;
4886   const char *file_part;
4887
4888   folder_path = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
4889   file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
4890
4891   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN && !folder_path)
4892     {
4893       error_message_with_parent (parent,
4894                                  _("Cannot change to the folder you specified as it is an invalid path."));
4895       return FALSE;
4896     }
4897
4898   if (file_part[0] == '\0')
4899     return change_folder_and_display_error (impl, folder_path);
4900   else
4901     {
4902       GtkFileFolder *folder = NULL;
4903       GtkFilePath *subfolder_path = NULL;
4904       GtkFileInfo *info = NULL;
4905       GError *error;
4906       gboolean result;
4907
4908       result = FALSE;
4909
4910       /* If the file part is non-empty, we need to figure out if it refers to a
4911        * folder within folder. We could optimize the case here where the folder
4912        * is already loaded for one of our tree models.
4913        */
4914
4915       error = NULL;
4916       folder = gtk_file_system_get_folder (impl->file_system, folder_path, GTK_FILE_INFO_IS_FOLDER, &error);
4917
4918       if (!folder)
4919         {
4920           error_getting_info_dialog (impl, folder_path, error);
4921           goto out;
4922         }
4923
4924       error = NULL;
4925       subfolder_path = gtk_file_system_make_path (impl->file_system, folder_path, file_part, &error);
4926
4927       if (!subfolder_path)
4928         {
4929           char *msg;
4930           char *uri;
4931
4932           uri = gtk_file_system_path_to_uri (impl->file_system, folder_path);
4933           msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
4934                                  uri, file_part,
4935                                  error->message);
4936           error_message (impl, msg);
4937           g_free (uri);
4938           g_free (msg);
4939           goto out;
4940         }
4941
4942       error = NULL;
4943       info = gtk_file_folder_get_info (folder, subfolder_path, &error);
4944
4945       if (!info)
4946         {
4947           if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
4948               || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4949             {
4950               if (!change_folder_and_display_error (impl, folder_path))
4951                 goto out;
4952
4953               gtk_file_chooser_default_set_current_name (GTK_FILE_CHOOSER (impl), file_part);
4954             }
4955           else
4956             error_getting_info_dialog (impl, subfolder_path, error);
4957
4958           goto out;
4959         }
4960
4961       if (gtk_file_info_get_is_folder (info))
4962         result = change_folder_and_display_error (impl, subfolder_path);
4963       else
4964         {
4965           GError *error;
4966
4967           error = NULL;
4968           result = _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (impl), subfolder_path, &error);
4969           if (!result)
4970             error_dialog (impl,
4971                           _("Could not select %s:\n%s"),
4972                           subfolder_path, error);
4973         }
4974
4975     out:
4976
4977       if (folder)
4978         g_object_unref (folder);
4979
4980       gtk_file_path_free (subfolder_path);
4981
4982       if (info)
4983         gtk_file_info_free (info);
4984
4985       return result;
4986     }
4987
4988   g_assert_not_reached ();
4989 }
4990
4991 static void
4992 location_popup_handler (GtkFileChooserDefault *impl)
4993 {
4994   GtkWidget *dialog;
4995   GtkWidget *toplevel;
4996   GtkWidget *hbox;
4997   GtkWidget *label;
4998   GtkWidget *entry;
4999   gboolean refocus;
5000   char *title;
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   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
5009       || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5010     {
5011       title = _("Open Location");
5012     }
5013   else
5014     {
5015       g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5016                 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
5017       title = ""; /* FIXME: #137272, fix for 2.4.1 */
5018     }
5019
5020   dialog = gtk_dialog_new_with_buttons (title,
5021                                         GTK_WINDOW (toplevel),
5022                                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
5023                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
5024                                         GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
5025                                         NULL);
5026   gtk_window_set_default_size (GTK_WINDOW (dialog), 300, -1);
5027   gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
5028   gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2);
5029   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
5030
5031   hbox = gtk_hbox_new (FALSE, 12);
5032   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0);
5033   gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
5034
5035   label = gtk_label_new_with_mnemonic (_("_Location:"));
5036   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
5037
5038   entry = location_entry_create (impl);
5039   gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
5040   gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
5041
5042   /* Run */
5043
5044   gtk_widget_show_all (dialog);
5045
5046   refocus = TRUE;
5047
5048   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
5049     {
5050       if (update_from_entry (impl, GTK_WINDOW (dialog), GTK_FILE_CHOOSER_ENTRY (entry)))
5051         {
5052           if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
5053               || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5054             {
5055               gtk_widget_grab_focus (impl->browse_files_tree_view);
5056             }
5057           else
5058             {
5059               g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5060                         || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
5061               gtk_widget_grab_focus (impl->save_file_name_entry);
5062             }
5063           refocus = FALSE;
5064         }
5065     }
5066
5067   if (refocus)
5068     {
5069       GtkWidget *toplevel;
5070
5071       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
5072       if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WINDOW (toplevel)->focus_widget)
5073         gtk_widget_grab_focus (GTK_WINDOW (toplevel)->focus_widget);
5074     }
5075
5076   gtk_widget_destroy (dialog);
5077 }
5078
5079 /* Handler for the "up-folder" keybinding signal */
5080 static void
5081 up_folder_handler (GtkFileChooserDefault *impl)
5082 {
5083   _gtk_path_bar_up (GTK_PATH_BAR (impl->browse_path_bar));
5084 }
5085
5086 /* Handler for the "down-folder" keybinding signal */
5087 static void
5088 down_folder_handler (GtkFileChooserDefault *impl)
5089 {
5090   _gtk_path_bar_down (GTK_PATH_BAR (impl->browse_path_bar));
5091 }
5092
5093 /* Handler for the "home-folder" keybinding signal */
5094 static void
5095 home_folder_handler (GtkFileChooserDefault *impl)
5096 {
5097   int pos;
5098   GtkTreeIter iter;
5099   GtkFilePath *path;
5100
5101   if (!impl->has_home)
5102     return; /* Should we put up an error dialog? */
5103
5104   pos = shortcuts_get_index (impl, SHORTCUTS_HOME);
5105   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
5106     g_assert_not_reached ();
5107
5108   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &path, -1);
5109   g_assert (path != NULL);
5110
5111   change_folder_and_display_error (impl, path);
5112 }
5113
5114 \f
5115
5116 /* Drag and drop interfaces */
5117
5118 static void
5119 shortcuts_model_filter_class_init (ShortcutsModelFilterClass *class)
5120 {
5121 }
5122
5123 static void
5124 shortcuts_model_filter_init (ShortcutsModelFilter *model)
5125 {
5126   model->impl = NULL;
5127 }
5128
5129 /* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */
5130 static gboolean
5131 shortcuts_model_filter_row_draggable (GtkTreeDragSource *drag_source,
5132                                       GtkTreePath       *path)
5133 {
5134   ShortcutsModelFilter *model;
5135   int pos;
5136   int bookmarks_pos;
5137
5138   model = SHORTCUTS_MODEL_FILTER (drag_source);
5139
5140   pos = *gtk_tree_path_get_indices (path);
5141   bookmarks_pos = shortcuts_get_index (model->impl, SHORTCUTS_BOOKMARKS);
5142
5143   return (pos >= bookmarks_pos && pos < bookmarks_pos + model->impl->num_bookmarks);
5144 }
5145
5146 /* GtkTreeDragSource::drag_data_get implementation for the shortcuts filter model */
5147 static gboolean
5148 shortcuts_model_filter_drag_data_get (GtkTreeDragSource *drag_source,
5149                                       GtkTreePath       *path,
5150                                       GtkSelectionData  *selection_data)
5151 {
5152   ShortcutsModelFilter *model;
5153
5154   model = SHORTCUTS_MODEL_FILTER (drag_source);
5155
5156   /* FIXME */
5157
5158   return FALSE;
5159 }
5160
5161 /* Fill the GtkTreeDragSourceIface vtable */
5162 static void
5163 shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface)
5164 {
5165   iface->row_draggable = shortcuts_model_filter_row_draggable;
5166   iface->drag_data_get = shortcuts_model_filter_drag_data_get;
5167 }
5168
5169 #if 0
5170 /* Fill the GtkTreeDragDestIface vtable */
5171 static void
5172 shortcuts_model_filter_drag_dest_iface_init (GtkTreeDragDestIface *iface)
5173 {
5174   iface->drag_data_received = shortcuts_model_filter_drag_data_received;
5175   iface->row_drop_possible = shortcuts_model_filter_row_drop_possible;
5176 }
5177 #endif
5178
5179 static GtkTreeModel *
5180 shortcuts_model_filter_new (GtkFileChooserDefault *impl,
5181                             GtkTreeModel          *child_model,
5182                             GtkTreePath           *root)
5183 {
5184   ShortcutsModelFilter *model;
5185
5186   model = g_object_new (SHORTCUTS_MODEL_FILTER_TYPE,
5187                         "child_model", child_model,
5188                         "virtual_root", root,
5189                         NULL);
5190
5191   model->impl = impl;
5192
5193   return GTK_TREE_MODEL (model);
5194 }