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