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