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