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