]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserdefault.c
dbb556839d8341f9636582b02419aa42b5135b53
[~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_CLOSE,
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       goto out;
2107       action = 0;
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
3209 static void
3210 gtk_file_chooser_default_dispose (GObject *object)
3211 {
3212   GtkFileChooserDefault *impl = (GtkFileChooserDefault *) object;
3213
3214   if (impl->extra_widget)
3215     {
3216       g_object_unref (impl->extra_widget);
3217       impl->extra_widget = NULL;
3218     }
3219
3220   if (impl->settings_signal_id)
3221     {
3222       GtkSettings *settings;
3223
3224       settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
3225       g_signal_handler_disconnect (settings, impl->settings_signal_id);
3226       impl->settings_signal_id = 0;
3227     }
3228
3229   G_OBJECT_CLASS (parent_class)->dispose (object);
3230 }
3231
3232 /* We override show-all since we have internal widgets that
3233  * shouldn't be shown when you call show_all(), like the filter
3234  * combo box.
3235  */
3236 static void
3237 gtk_file_chooser_default_show_all (GtkWidget *widget)
3238 {
3239   GtkFileChooserDefault *impl = (GtkFileChooserDefault *) widget;
3240
3241   gtk_widget_show (widget);
3242
3243   if (impl->extra_widget)
3244     gtk_widget_show_all (impl->extra_widget);
3245 }
3246
3247 /* Changes the icons wherever it is needed */
3248 static void
3249 change_icon_theme (GtkFileChooserDefault *impl)
3250 {
3251   GtkSettings *settings;
3252   gint width, height;
3253
3254   settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
3255
3256   if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_SMALL_TOOLBAR, &width, &height))
3257     impl->icon_size = MAX (width, height);
3258   else
3259     impl->icon_size = FALLBACK_ICON_SIZE;
3260
3261   shortcuts_reload_icons (impl);
3262   gtk_widget_queue_resize (impl->browse_files_tree_view);
3263 }
3264
3265 /* Callback used when a GtkSettings value changes */
3266 static void
3267 settings_notify_cb (GObject               *object,
3268                     GParamSpec            *pspec,
3269                     GtkFileChooserDefault *impl)
3270 {
3271   const char *name;
3272
3273   name = g_param_spec_get_name (pspec);
3274
3275   if (strcmp (name, "gtk-icon-theme-name") == 0
3276       || strcmp (name, "gtk-icon-sizes") == 0)
3277     change_icon_theme (impl);
3278 }
3279
3280 /* Installs a signal handler for GtkSettings so that we can monitor changes in
3281  * the icon theme.
3282  */
3283 static void
3284 check_icon_theme (GtkFileChooserDefault *impl)
3285 {
3286   GtkSettings *settings;
3287
3288   if (impl->settings_signal_id)
3289     return;
3290
3291   settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
3292   impl->settings_signal_id = g_signal_connect (settings, "notify",
3293                                                G_CALLBACK (settings_notify_cb), impl);
3294
3295   change_icon_theme (impl);
3296 }
3297
3298 static void
3299 gtk_file_chooser_default_style_set      (GtkWidget *widget,
3300                                          GtkStyle  *previous_style)
3301 {
3302   GtkFileChooserDefault *impl;
3303
3304   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
3305
3306   if (GTK_WIDGET_CLASS (parent_class)->style_set)
3307     GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
3308
3309   check_icon_theme (impl);
3310
3311   g_signal_emit_by_name (widget, "default-size-changed");
3312 }
3313
3314 static void
3315 gtk_file_chooser_default_screen_changed (GtkWidget *widget,
3316                                          GdkScreen *previous_screen)
3317 {
3318   GtkFileChooserDefault *impl;
3319
3320   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
3321
3322   if (GTK_WIDGET_CLASS (parent_class)->screen_changed)
3323     GTK_WIDGET_CLASS (parent_class)->screen_changed (widget, previous_screen);
3324
3325   check_icon_theme (impl);
3326
3327   g_signal_emit_by_name (widget, "default-size-changed");
3328 }
3329
3330 static gboolean
3331 list_model_filter_func (GtkFileSystemModel *model,
3332                         GtkFilePath        *path,
3333                         const GtkFileInfo  *file_info,
3334                         gpointer            user_data)
3335 {
3336   GtkFileChooserDefault *impl = user_data;
3337   GtkFileFilterInfo filter_info;
3338   GtkFileFilterFlags needed;
3339   gboolean result;
3340
3341   if (!impl->current_filter)
3342     return TRUE;
3343
3344   if (gtk_file_info_get_is_folder (file_info))
3345     return TRUE;
3346
3347   filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
3348
3349   needed = gtk_file_filter_get_needed (impl->current_filter);
3350
3351   filter_info.display_name = gtk_file_info_get_display_name (file_info);
3352   filter_info.mime_type = gtk_file_info_get_mime_type (file_info);
3353
3354   if (needed & GTK_FILE_FILTER_FILENAME)
3355     {
3356       filter_info.filename = gtk_file_system_path_to_filename (impl->file_system, path);
3357       if (filter_info.filename)
3358         filter_info.contains |= GTK_FILE_FILTER_FILENAME;
3359     }
3360   else
3361     filter_info.filename = NULL;
3362
3363   if (needed & GTK_FILE_FILTER_URI)
3364     {
3365       filter_info.uri = gtk_file_system_path_to_uri (impl->file_system, path);
3366       if (filter_info.uri)
3367         filter_info.contains |= GTK_FILE_FILTER_URI;
3368     }
3369   else
3370     filter_info.uri = NULL;
3371
3372   result = gtk_file_filter_filter (impl->current_filter, &filter_info);
3373
3374   if (filter_info.filename)
3375     g_free ((gchar *)filter_info.filename);
3376   if (filter_info.uri)
3377     g_free ((gchar *)filter_info.uri);
3378
3379   return result;
3380 }
3381
3382 static void
3383 install_list_model_filter (GtkFileChooserDefault *impl)
3384 {
3385   if (impl->current_filter)
3386     _gtk_file_system_model_set_filter (impl->browse_files_model,
3387                                        list_model_filter_func,
3388                                        impl);
3389 }
3390
3391 #define COMPARE_DIRECTORIES                                                                                    \
3392   GtkFileChooserDefault *impl = user_data;                                                                     \
3393   const GtkFileInfo *info_a = _gtk_file_system_model_get_info (impl->browse_files_model, a);                           \
3394   const GtkFileInfo *info_b = _gtk_file_system_model_get_info (impl->browse_files_model, b);                           \
3395   gboolean dir_a, dir_b;                                                                                       \
3396                                                                                                                \
3397   if (info_a)                                                                                                  \
3398     dir_a = gtk_file_info_get_is_folder (info_a);                                                              \
3399   else                                                                                                         \
3400     return impl->list_sort_ascending ? -1 : 1;                                                                 \
3401                                                                                                                \
3402   if (info_b)                                                                                                  \
3403     dir_b = gtk_file_info_get_is_folder (info_b);                                                              \
3404   else                                                                                                         \
3405     return impl->list_sort_ascending ? 1 : -1;                                                                 \
3406                                                                                                                \
3407   if (dir_a != dir_b)                                                                                          \
3408     return impl->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */
3409
3410 /* Sort callback for the filename column */
3411 static gint
3412 name_sort_func (GtkTreeModel *model,
3413                 GtkTreeIter  *a,
3414                 GtkTreeIter  *b,
3415                 gpointer      user_data)
3416 {
3417   COMPARE_DIRECTORIES;
3418   else
3419     return strcmp (gtk_file_info_get_display_key (info_a), gtk_file_info_get_display_key (info_b));
3420 }
3421
3422 /* Sort callback for the size column */
3423 static gint
3424 size_sort_func (GtkTreeModel *model,
3425                 GtkTreeIter  *a,
3426                 GtkTreeIter  *b,
3427                 gpointer      user_data)
3428 {
3429   COMPARE_DIRECTORIES;
3430   else
3431     {
3432       gint64 size_a = gtk_file_info_get_size (info_a);
3433       gint64 size_b = gtk_file_info_get_size (info_b);
3434
3435       return size_a > size_b ? -1 : (size_a == size_b ? 0 : 1);
3436     }
3437 }
3438
3439 /* Sort callback for the mtime column */
3440 static gint
3441 mtime_sort_func (GtkTreeModel *model,
3442                  GtkTreeIter  *a,
3443                  GtkTreeIter  *b,
3444                  gpointer      user_data)
3445 {
3446   COMPARE_DIRECTORIES;
3447   else
3448     {
3449       GtkFileTime ta = gtk_file_info_get_modification_time (info_a);
3450       GtkFileTime tb = gtk_file_info_get_modification_time (info_b);
3451
3452       return ta > tb ? -1 : (ta == tb ? 0 : 1);
3453     }
3454 }
3455
3456 /* Callback used when the sort column changes.  We cache the sort order for use
3457  * in name_sort_func().
3458  */
3459 static void
3460 list_sort_column_changed_cb (GtkTreeSortable       *sortable,
3461                              GtkFileChooserDefault *impl)
3462 {
3463   GtkSortType sort_type;
3464
3465   if (gtk_tree_sortable_get_sort_column_id (sortable, NULL, &sort_type))
3466     impl->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
3467 }
3468
3469 /* Gets rid of the old list model and creates a new one for the current folder */
3470 static void
3471 set_list_model (GtkFileChooserDefault *impl)
3472 {
3473   if (impl->browse_files_model)
3474     {
3475       g_object_unref (impl->browse_files_model);
3476       g_object_unref (impl->sort_model);
3477     }
3478
3479   impl->browse_files_model = _gtk_file_system_model_new (impl->file_system,
3480                                                          impl->current_folder, 0,
3481                                                          GTK_FILE_INFO_ALL);
3482   _gtk_file_system_model_set_show_hidden (impl->browse_files_model, impl->show_hidden);
3483   switch (impl->action)
3484     {
3485     case GTK_FILE_CHOOSER_ACTION_OPEN:
3486     case GTK_FILE_CHOOSER_ACTION_SAVE:
3487       _gtk_file_system_model_set_show_files (impl->browse_files_model, TRUE);
3488       break;
3489     case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
3490     case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
3491       _gtk_file_system_model_set_show_files (impl->browse_files_model, FALSE);
3492       break;
3493     default:
3494       g_assert_not_reached ();
3495     }
3496   install_list_model_filter (impl);
3497
3498   impl->sort_model = (GtkTreeModelSort *)gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (impl->browse_files_model));
3499   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, name_sort_func, impl, NULL);
3500   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_SIZE, size_sort_func, impl, NULL);
3501   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_MTIME, mtime_sort_func, impl, NULL);
3502   gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->sort_model), NULL, NULL, NULL);
3503   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, GTK_SORT_ASCENDING);
3504   impl->list_sort_ascending = TRUE;
3505
3506   g_signal_connect (impl->sort_model, "sort-column-changed",
3507                     G_CALLBACK (list_sort_column_changed_cb), impl);
3508
3509   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
3510                            GTK_TREE_MODEL (impl->sort_model));
3511   gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->browse_files_tree_view));
3512   gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_files_tree_view),
3513                                    GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
3514 }
3515
3516 static void
3517 update_chooser_entry (GtkFileChooserDefault *impl)
3518 {
3519   GtkTreeSelection *selection;
3520   const GtkFileInfo *info;
3521   GtkTreeIter iter;
3522   GtkTreeIter child_iter;
3523
3524   if (impl->action != GTK_FILE_CHOOSER_ACTION_SAVE)
3525     return;
3526
3527   g_assert (!impl->select_multiple);
3528   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3529
3530   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
3531     return;
3532
3533   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
3534                                                   &child_iter,
3535                                                   &iter);
3536
3537   info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
3538
3539   if (!gtk_file_info_get_is_folder (info))
3540     gtk_entry_set_text (GTK_ENTRY (impl->save_file_name_entry),
3541                         gtk_file_info_get_display_name (info));
3542 }
3543
3544 static gboolean
3545 gtk_file_chooser_default_set_current_folder (GtkFileChooser    *chooser,
3546                                              const GtkFilePath *path,
3547                                              GError           **error)
3548 {
3549   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3550   GtkFileInfo *info;
3551
3552   /* Test validity of path here.  */
3553   info = get_file_info (impl->file_system, path, FALSE, error);
3554   if (!info)
3555     return FALSE;
3556   gtk_file_info_free (info);
3557
3558   if (!_gtk_path_bar_set_path (GTK_PATH_BAR (impl->browse_path_bar), path, error))
3559     return FALSE;
3560
3561   if (impl->current_folder != path)
3562     {
3563       if (impl->current_folder)
3564         gtk_file_path_free (impl->current_folder);
3565
3566       impl->current_folder = gtk_file_path_copy (path);
3567     }
3568
3569   /* Update the widgets that may trigger a folder change themselves.  */
3570
3571   if (!impl->changing_folder)
3572     {
3573       impl->changing_folder = TRUE;
3574
3575       shortcuts_update_current_folder (impl);
3576
3577       impl->changing_folder = FALSE;
3578     }
3579
3580   /* Create a new list model */
3581   set_list_model (impl);
3582
3583   /* Refresh controls */
3584
3585   shortcuts_unselect_all (impl);
3586
3587   g_signal_emit_by_name (impl, "current-folder-changed", 0);
3588
3589   check_preview_change (impl);
3590   bookmarks_check_add_sensitivity (impl);
3591
3592   g_signal_emit_by_name (impl, "selection-changed", 0);
3593
3594   return TRUE;
3595 }
3596
3597 static GtkFilePath *
3598 gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser)
3599 {
3600   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3601
3602   return gtk_file_path_copy (impl->current_folder);
3603 }
3604
3605 static void
3606 gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
3607                                            const gchar    *name)
3608 {
3609   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3610
3611   g_return_if_fail (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE);
3612
3613   gtk_entry_set_text (GTK_ENTRY (impl->save_file_name_entry), name);
3614 }
3615
3616 static void
3617 select_func (GtkFileSystemModel *model,
3618              GtkTreePath        *path,
3619              GtkTreeIter        *iter,
3620              gpointer            user_data)
3621 {
3622   GtkFileChooserDefault *impl = user_data;
3623   GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
3624   GtkTreePath *sorted_path;
3625
3626   sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model, path);
3627   gtk_tree_view_set_cursor (tree_view, sorted_path, NULL, FALSE);
3628   gtk_tree_path_free (sorted_path);
3629 }
3630
3631 static gboolean
3632 gtk_file_chooser_default_select_path (GtkFileChooser    *chooser,
3633                                       const GtkFilePath *path,
3634                                       GError           **error)
3635 {
3636   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3637   GtkFilePath *parent_path;
3638
3639   if (!gtk_file_system_get_parent (impl->file_system, path, &parent_path, error))
3640     return FALSE;
3641
3642   if (!parent_path)
3643     return _gtk_file_chooser_set_current_folder_path (chooser, path, error);
3644   else
3645     {
3646       gboolean result;
3647
3648       result = _gtk_file_chooser_set_current_folder_path (chooser, parent_path, error);
3649       gtk_file_path_free (parent_path);
3650       _gtk_file_system_model_path_do (impl->browse_files_model, path,
3651                                       select_func, impl);
3652
3653       return result;
3654     }
3655
3656   g_assert_not_reached ();
3657 }
3658
3659 static void
3660 unselect_func (GtkFileSystemModel *model,
3661                GtkTreePath        *path,
3662                GtkTreeIter        *iter,
3663                gpointer            user_data)
3664 {
3665   GtkFileChooserDefault *impl = user_data;
3666   GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
3667   GtkTreePath *sorted_path;
3668
3669   sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model,
3670                                                                 path);
3671   gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (tree_view),
3672                                     sorted_path);
3673   gtk_tree_path_free (sorted_path);
3674 }
3675
3676 static void
3677 gtk_file_chooser_default_unselect_path (GtkFileChooser    *chooser,
3678                                         const GtkFilePath *path)
3679 {
3680   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3681
3682   _gtk_file_system_model_path_do (impl->browse_files_model, path,
3683                                  unselect_func, impl);
3684 }
3685
3686 static void
3687 gtk_file_chooser_default_select_all (GtkFileChooser *chooser)
3688 {
3689   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3690   if (impl->select_multiple)
3691     {
3692       GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3693       gtk_tree_selection_select_all (selection);
3694     }
3695 }
3696
3697 static void
3698 gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser)
3699 {
3700   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3701   GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3702
3703   gtk_tree_selection_unselect_all (selection);
3704 }
3705
3706 /* Checks whether the filename entry for the Save modes contains a valid filename */
3707 static GtkFilePath *
3708 check_save_entry (GtkFileChooserDefault *impl,
3709                   gboolean              *is_valid,
3710                   gboolean              *is_empty)
3711 {
3712   const char *filename;
3713   GtkFilePath *path;
3714   GError *error;
3715
3716   g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
3717             || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
3718
3719   filename = gtk_entry_get_text (GTK_ENTRY (impl->save_file_name_entry));
3720
3721   if (!filename || filename[0] == '\0')
3722     {
3723       *is_valid = FALSE;
3724       *is_empty = TRUE;
3725       return NULL;
3726     }
3727
3728   *is_empty = FALSE;
3729
3730   error = NULL;
3731   path = gtk_file_system_make_path (impl->file_system, impl->current_folder, filename, &error);
3732
3733   if (!path)
3734     {
3735       error_building_filename_dialog (impl, impl->current_folder, filename, error);
3736       *is_valid = FALSE;
3737       return NULL;
3738     }
3739
3740   *is_valid = TRUE;
3741   return path;
3742 }
3743
3744 struct get_paths_closure {
3745   GtkFileChooserDefault *impl;
3746   GSList *result;
3747   GtkFilePath *path_from_entry;
3748 };
3749
3750 static void
3751 get_paths_foreach (GtkTreeModel *model,
3752                    GtkTreePath  *path,
3753                    GtkTreeIter  *iter,
3754                    gpointer      data)
3755 {
3756   struct get_paths_closure *info;
3757   const GtkFilePath *file_path;
3758   GtkFileSystemModel *fs_model;
3759   GtkTreeIter sel_iter;
3760
3761   info = data;
3762   fs_model = info->impl->browse_files_model;
3763   gtk_tree_model_sort_convert_iter_to_child_iter (info->impl->sort_model, &sel_iter, iter);
3764
3765   file_path = _gtk_file_system_model_get_path (GTK_FILE_SYSTEM_MODEL (fs_model), &sel_iter);
3766
3767   if (!info->path_from_entry
3768       || gtk_file_path_compare (info->path_from_entry, file_path) != 0)
3769     info->result = g_slist_prepend (info->result, gtk_file_path_copy (file_path));
3770 }
3771
3772 static GSList *
3773 gtk_file_chooser_default_get_paths (GtkFileChooser *chooser)
3774 {
3775   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3776   struct get_paths_closure info;
3777
3778   info.impl = impl;
3779   info.result = NULL;
3780   info.path_from_entry = NULL;
3781
3782   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
3783       || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
3784     {
3785       gboolean is_valid, is_empty;
3786
3787       info.path_from_entry = check_save_entry (impl, &is_valid, &is_empty);
3788       if (!is_valid && !is_empty)
3789         return NULL;
3790     }
3791
3792   if (!info.path_from_entry || impl->select_multiple)
3793     {
3794       GtkTreeSelection *selection;
3795
3796       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3797       gtk_tree_selection_selected_foreach (selection, get_paths_foreach, &info);
3798     }
3799
3800   if (info.path_from_entry)
3801     info.result = g_slist_prepend (info.result, info.path_from_entry);
3802
3803   /* If there's no folder selected, and we're in SELECT_FOLDER mode, then we
3804    * fall back to the current directory */
3805   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
3806       info.result == NULL)
3807     {
3808       info.result = g_slist_prepend (info.result, gtk_file_path_copy (impl->current_folder));
3809     }
3810
3811   return g_slist_reverse (info.result);
3812 }
3813
3814 static GtkFilePath *
3815 gtk_file_chooser_default_get_preview_path (GtkFileChooser *chooser)
3816 {
3817   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3818
3819   if (impl->preview_path)
3820     return gtk_file_path_copy (impl->preview_path);
3821   else
3822     return NULL;
3823 }
3824
3825 static GtkFileSystem *
3826 gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser)
3827 {
3828   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3829
3830   return impl->file_system;
3831 }
3832
3833 /* Shows or hides the filter widgets */
3834 static void
3835 toolbar_show_filters (GtkFileChooserDefault *impl,
3836                       gboolean               show)
3837 {
3838   if (show)
3839     gtk_widget_show (impl->filter_combo);
3840   else
3841     gtk_widget_hide (impl->filter_combo);
3842 }
3843
3844 static void
3845 gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
3846                                      GtkFileFilter  *filter)
3847 {
3848   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3849   const gchar *name;
3850
3851   if (g_slist_find (impl->filters, filter))
3852     {
3853       g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
3854       return;
3855     }
3856
3857   g_object_ref (filter);
3858   gtk_object_sink (GTK_OBJECT (filter));
3859   impl->filters = g_slist_append (impl->filters, filter);
3860
3861   name = gtk_file_filter_get_name (filter);
3862   if (!name)
3863     name = "Untitled filter";   /* Place-holder, doesn't need to be marked for translation */
3864
3865   gtk_combo_box_append_text (GTK_COMBO_BOX (impl->filter_combo), name);
3866
3867   if (!g_slist_find (impl->filters, impl->current_filter))
3868     set_current_filter (impl, filter);
3869
3870   toolbar_show_filters (impl, TRUE);
3871 }
3872
3873 static void
3874 gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
3875                                         GtkFileFilter  *filter)
3876 {
3877   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3878   GtkTreeModel *model;
3879   GtkTreeIter iter;
3880   gint filter_index;
3881
3882   filter_index = g_slist_index (impl->filters, filter);
3883
3884   if (filter_index < 0)
3885     {
3886       g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
3887       return;
3888     }
3889
3890   impl->filters = g_slist_remove (impl->filters, filter);
3891
3892   if (filter == impl->current_filter)
3893     {
3894       if (impl->filters)
3895         set_current_filter (impl, impl->filters->data);
3896       else
3897         set_current_filter (impl, NULL);
3898     }
3899
3900   /* Remove row from the combo box */
3901   model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->filter_combo));
3902   gtk_tree_model_iter_nth_child  (model, &iter, NULL, filter_index);
3903   gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
3904
3905   g_object_unref (filter);
3906
3907   if (!impl->filters)
3908     toolbar_show_filters (impl, FALSE);
3909 }
3910
3911 static GSList *
3912 gtk_file_chooser_default_list_filters (GtkFileChooser *chooser)
3913 {
3914   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3915
3916   return g_slist_copy (impl->filters);
3917 }
3918
3919 /* Returns the position in the shortcuts tree where the nth specified shortcut would appear */
3920 static int
3921 shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl,
3922                                        int                    pos)
3923 {
3924   return pos + shortcuts_get_index (impl, SHORTCUTS_SHORTCUTS);
3925 }
3926
3927 static gboolean
3928 gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser    *chooser,
3929                                               const GtkFilePath *path,
3930                                               GError           **error)
3931 {
3932   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3933   gboolean result;
3934   GtkFileInfo *info;
3935   int pos;
3936
3937   /* Test validity of path here.  */
3938   info = get_file_info (impl->file_system, path, FALSE, error);
3939   if (!info)
3940     return FALSE;
3941   gtk_file_info_free (info);
3942
3943   pos = shortcuts_get_pos_for_shortcut_folder (impl, impl->num_shortcuts);
3944
3945   result = shortcuts_insert_path (impl, pos, FALSE, NULL, path, NULL, FALSE, error);
3946
3947   if (result)
3948     impl->num_shortcuts++;
3949
3950   if (impl->shortcuts_filter_model)
3951     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
3952
3953   return result;
3954 }
3955
3956 static gboolean
3957 gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
3958                                                  const GtkFilePath *path,
3959                                                  GError           **error)
3960 {
3961   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3962   int pos;
3963   GtkTreeIter iter;
3964   char *uri;
3965   int i;
3966
3967   if (impl->num_shortcuts == 0)
3968     goto out;
3969
3970   pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
3971   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
3972     g_assert_not_reached ();
3973
3974   for (i = 0; i < impl->num_shortcuts; i++)
3975     {
3976       GtkFilePath *shortcut;
3977
3978       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &shortcut, -1);
3979       g_assert (shortcut != NULL);
3980
3981       if (gtk_file_path_compare (shortcut, path) == 0)
3982         {
3983           /* The other columns are freed by the GtkTreeStore */
3984           gtk_file_path_free (shortcut);
3985           gtk_list_store_remove (impl->shortcuts_model, &iter);
3986           impl->num_shortcuts--;
3987           return TRUE;
3988         }
3989
3990       if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
3991         g_assert_not_reached ();
3992     }
3993
3994  out:
3995
3996   uri = gtk_file_system_path_to_uri (impl->file_system, path);
3997   g_set_error (error,
3998                GTK_FILE_CHOOSER_ERROR,
3999                GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
4000                _("shortcut %s does not exist"),
4001                uri);
4002   g_free (uri);
4003
4004   return FALSE;
4005 }
4006
4007 static GSList *
4008 gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser)
4009 {
4010   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4011   int pos;
4012   GtkTreeIter iter;
4013   int i;
4014   GSList *list;
4015
4016   if (impl->num_shortcuts == 0)
4017     return NULL;
4018
4019   pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
4020   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
4021     g_assert_not_reached ();
4022
4023   list = NULL;
4024
4025   for (i = 0; i < impl->num_shortcuts; i++)
4026     {
4027       GtkFilePath *shortcut;
4028
4029       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &shortcut, -1);
4030       g_assert (shortcut != NULL);
4031
4032       list = g_slist_prepend (list, gtk_file_path_copy (shortcut));
4033
4034       if (i != impl->num_shortcuts - 1)
4035         {
4036           if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
4037             g_assert_not_reached ();
4038         }
4039     }
4040
4041   return g_slist_reverse (list);
4042 }
4043
4044 /* Guesses a size based upon font sizes */
4045 static void
4046 find_good_size_from_style (GtkWidget *widget,
4047                            gint      *width,
4048                            gint      *height)
4049 {
4050   GtkFileChooserDefault *impl;
4051   gint default_width, default_height;
4052   int font_size;
4053   GtkRequisition req;
4054   GtkRequisition preview_req;
4055
4056   g_assert (widget->style != NULL);
4057   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
4058
4059   font_size = pango_font_description_get_size (widget->style->font_desc);
4060   font_size = PANGO_PIXELS (font_size);
4061
4062   default_width = font_size * NUM_CHARS;
4063   default_height = font_size * NUM_LINES;
4064
4065   /* Use at least the requisition size not including the preview widget */
4066   gtk_widget_size_request (widget, &req);
4067
4068   if (impl->preview_widget_active && impl->preview_widget)
4069     gtk_widget_size_request (impl->preview_box, &preview_req);
4070   else
4071     preview_req.width = 0;
4072
4073   default_width = MAX (default_width, (req.width - (preview_req.width + PREVIEW_HBOX_SPACING)));
4074   default_height = MAX (default_height, req.height);
4075
4076   *width = default_width;
4077   *height = default_height;
4078 }
4079
4080 static void
4081 gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
4082                                            gint                *default_width,
4083                                            gint                *default_height)
4084 {
4085   GtkFileChooserDefault *impl;
4086
4087   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
4088
4089   find_good_size_from_style (GTK_WIDGET (chooser_embed), default_width, default_height);
4090
4091   if (impl->preview_widget_active && impl->preview_widget)
4092     *default_width += impl->preview_box->requisition.width + PREVIEW_HBOX_SPACING;
4093 }
4094
4095 static void
4096 gtk_file_chooser_default_get_resizable_hints (GtkFileChooserEmbed *chooser_embed,
4097                                               gboolean            *resize_horizontally,
4098                                               gboolean            *resize_vertically)
4099 {
4100   GtkFileChooserDefault *impl;
4101
4102   g_return_if_fail (resize_horizontally != NULL);
4103   g_return_if_fail (resize_vertically != NULL);
4104
4105   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
4106
4107   *resize_horizontally = TRUE;
4108   *resize_vertically = TRUE;
4109
4110   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
4111       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4112     {
4113       if (! gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
4114         {
4115           *resize_horizontally = FALSE;
4116           *resize_vertically = FALSE;
4117         }
4118     }
4119 }
4120
4121 struct switch_folder_closure {
4122   GtkFileChooserDefault *impl;
4123   const GtkFilePath *path;
4124   int num_selected;
4125 };
4126
4127 /* Used from gtk_tree_selection_selected_foreach() in switch_to_selected_folder() */
4128 static void
4129 switch_folder_foreach_cb (GtkTreeModel      *model,
4130                           GtkTreePath       *path,
4131                           GtkTreeIter       *iter,
4132                           gpointer           data)
4133 {
4134   struct switch_folder_closure *closure;
4135   GtkTreeIter child_iter;
4136
4137   closure = data;
4138
4139   gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
4140
4141   closure->path = _gtk_file_system_model_get_path (closure->impl->browse_files_model, &child_iter);
4142   closure->num_selected++;
4143 }
4144
4145 /* Changes to the selected folder in the list view */
4146 static void
4147 switch_to_selected_folder (GtkFileChooserDefault *impl)
4148 {
4149   GtkTreeSelection *selection;
4150   struct switch_folder_closure closure;
4151
4152   g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
4153             || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE);
4154
4155   /* We do this with foreach() rather than get_selected() as we may be in
4156    * multiple selection mode
4157    */
4158
4159   closure.impl = impl;
4160   closure.path = NULL;
4161   closure.num_selected = 0;
4162
4163   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4164   gtk_tree_selection_selected_foreach (selection, switch_folder_foreach_cb, &closure);
4165
4166   g_assert (closure.path && closure.num_selected == 1);
4167
4168   change_folder_and_display_error (impl, closure.path);
4169 }
4170
4171 /* Implementation for GtkFileChooserEmbed::should_respond() */
4172 static gboolean
4173 gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
4174 {
4175   GtkFileChooserDefault *impl;
4176   GtkTreeSelection *selection;
4177   int num_selected;
4178
4179   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
4180
4181   /* First, check the save entry.  If it has a valid name, we are done */
4182
4183   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
4184       || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4185     {
4186       GtkFilePath *path;
4187       gboolean is_valid, is_empty;
4188
4189       path = check_save_entry (impl, &is_valid, &is_empty);
4190
4191       if (is_valid)
4192         {
4193           gtk_file_path_free (path);
4194           return TRUE;
4195         }
4196       else if (!is_empty)
4197         return FALSE;
4198     }
4199
4200   /* Second, do we have an empty selection */
4201   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
4202       || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
4203     {
4204       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4205       num_selected = gtk_tree_selection_count_selected_rows (selection);
4206       if (num_selected == 0)
4207         return FALSE;
4208     }
4209
4210   /* Third, should we return file names or folder names? */
4211
4212   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
4213       || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
4214     {
4215       gboolean all_files, all_folders;
4216
4217       selection_check (impl, NULL, &all_files, &all_folders);
4218
4219       if (num_selected == 1)
4220         {
4221           if (all_folders)
4222             {
4223               switch_to_selected_folder (impl);
4224               return FALSE;
4225             }
4226           else if (all_files)
4227             return TRUE;
4228         }
4229       else
4230         return all_files;
4231     }
4232   else if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
4233            || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4234     /* There can be no files selected in folder mode since we don't show them,
4235      * anyway.
4236      */
4237     return TRUE;
4238
4239   g_assert_not_reached ();
4240   return FALSE;
4241 }
4242
4243 /* Implementation for GtkFileChooserEmbed::initial_focus() */
4244 static void
4245 gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed)
4246 {
4247   GtkFileChooserDefault *impl;
4248   GtkWidget *widget;
4249
4250   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
4251
4252   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
4253       || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
4254     {
4255       GtkTreePath *path;
4256
4257       path = gtk_tree_path_new_from_indices (0, -1);
4258       gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), path, NULL, FALSE);
4259       gtk_tree_path_free (path);
4260
4261       widget = impl->browse_files_tree_view;
4262     }
4263   else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
4264            || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4265     widget = impl->save_file_name_entry;
4266   else
4267     {
4268       g_assert_not_reached ();
4269       widget = NULL;
4270     }
4271
4272   gtk_widget_grab_focus (widget);
4273 }
4274
4275 static void
4276 set_current_filter (GtkFileChooserDefault *impl,
4277                     GtkFileFilter         *filter)
4278 {
4279   if (impl->current_filter != filter)
4280     {
4281       int filter_index;
4282
4283       /* If we have filters, new filter must be one of them
4284        */
4285       filter_index = g_slist_index (impl->filters, filter);
4286       if (impl->filters && filter_index < 0)
4287         return;
4288
4289       if (impl->current_filter)
4290         g_object_unref (impl->current_filter);
4291       impl->current_filter = filter;
4292       if (impl->current_filter)
4293         {
4294           g_object_ref (impl->current_filter);
4295           gtk_object_sink (GTK_OBJECT (filter));
4296         }
4297
4298       if (impl->filters)
4299         gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
4300                                   filter_index);
4301
4302       install_list_model_filter (impl);
4303
4304       g_object_notify (G_OBJECT (impl), "filter");
4305     }
4306 }
4307
4308 static void
4309 filter_combo_changed (GtkComboBox           *combo_box,
4310                       GtkFileChooserDefault *impl)
4311 {
4312   gint new_index = gtk_combo_box_get_active (combo_box);
4313   GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
4314
4315   set_current_filter (impl, new_filter);
4316 }
4317
4318 static void
4319 check_preview_change (GtkFileChooserDefault *impl)
4320 {
4321   const GtkFilePath *new_path = NULL;
4322   const GtkFileInfo *new_info = NULL;
4323
4324   /* FIXME #132255: Fixing preview for multiple selection involves getting the
4325    * full selection and diffing to find out what the most recently selected file
4326    * is; there is logic in GtkFileSelection that probably can be
4327    * copied.
4328    */
4329   if (impl->sort_model && !impl->select_multiple)
4330     {
4331       GtkTreeSelection *selection;
4332       GtkTreeIter iter;
4333
4334       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4335       if (gtk_tree_selection_get_selected (selection, NULL, &iter))
4336         {
4337           GtkTreeIter child_iter;
4338
4339           gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
4340                                                           &child_iter, &iter);
4341
4342           new_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
4343           new_info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
4344         }
4345     }
4346
4347   if (new_path != impl->preview_path &&
4348       !(new_path && impl->preview_path &&
4349         gtk_file_path_compare (new_path, impl->preview_path) == 0))
4350     {
4351       if (impl->preview_path)
4352         {
4353           gtk_file_path_free (impl->preview_path);
4354           g_free (impl->preview_display_name);
4355         }
4356
4357       if (new_path)
4358         {
4359           impl->preview_path = gtk_file_path_copy (new_path);
4360           impl->preview_display_name = g_strdup (gtk_file_info_get_display_name (new_info));
4361         }
4362       else
4363         {
4364           impl->preview_path = NULL;
4365           impl->preview_display_name = NULL;
4366         }
4367
4368       if (impl->use_preview_label && impl->preview_label)
4369         gtk_label_set_text (GTK_LABEL (impl->preview_label), impl->preview_display_name);
4370
4371       g_signal_emit_by_name (impl, "update-preview");
4372     }
4373 }
4374
4375 /* Activates a volume by mounting it if necessary and then switching to its
4376  * base path.
4377  */
4378 static void
4379 shortcuts_activate_volume (GtkFileChooserDefault *impl,
4380                            GtkFileSystemVolume   *volume)
4381 {
4382   GtkFilePath *path;
4383
4384   if (!gtk_file_system_volume_get_is_mounted (impl->file_system, volume))
4385     {
4386       GError *error;
4387
4388       error = NULL;
4389       if (!gtk_file_system_volume_mount (impl->file_system, volume, &error))
4390         {
4391           char *msg;
4392
4393           msg = g_strdup_printf ("Could not mount %s:\n%s",
4394                                  gtk_file_system_volume_get_display_name (impl->file_system, volume),
4395                                  error->message);
4396           error_message (impl, msg);
4397           g_free (msg);
4398           g_error_free (error);
4399
4400           return;
4401         }
4402     }
4403
4404   path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
4405   change_folder_and_display_error (impl, path);
4406   gtk_file_path_free (path);
4407 }
4408
4409 /* Opens the folder or volume at the specified index in the shortcuts list */
4410 static void
4411 shortcuts_activate_item (GtkFileChooserDefault *impl,
4412                          int                    item_num)
4413 {
4414   GtkTreePath *path;
4415   gboolean result;
4416   GtkTreeIter iter;
4417   gpointer data;
4418   int start_row;
4419
4420   if (item_num == shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR)
4421       || item_num == shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR))
4422     return;
4423
4424   path = gtk_tree_path_new_from_indices (item_num, -1);
4425   result = gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path);
4426   gtk_tree_path_free (path);
4427
4428   if (!result)
4429     return;
4430
4431   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &data, -1);
4432
4433   start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
4434   if ((item_num >= start_row && item_num < start_row + impl->num_volumes)
4435       || (item_num == shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER) && impl->shortcuts_current_folder_is_volume))
4436     {
4437       GtkFileSystemVolume *volume;
4438
4439       volume = data;
4440       shortcuts_activate_volume (impl, volume);
4441     }
4442   else
4443     {
4444       const GtkFilePath *file_path;
4445
4446       file_path = data;
4447       change_folder_and_display_error (impl, file_path);
4448     }
4449 }
4450
4451 /* Callback used when a row in the shortcuts list is activated */
4452 static void
4453 shortcuts_row_activated_cb (GtkTreeView           *tree_view,
4454                             GtkTreePath           *path,
4455                             GtkTreeViewColumn     *column,
4456                             GtkFileChooserDefault *impl)
4457 {
4458   int selected;
4459   GtkTreeIter iter;
4460   GtkTreeIter child_iter;
4461   GtkTreePath *child_path;
4462
4463   if (!gtk_tree_model_get_iter (impl->shortcuts_filter_model, &iter, path))
4464     return;
4465
4466   gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
4467                                                     &child_iter,
4468                                                     &iter);
4469   child_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &child_iter);
4470   if (!child_path)
4471     return;
4472
4473   selected = *gtk_tree_path_get_indices (child_path);
4474   gtk_tree_path_free (child_path);
4475
4476   shortcuts_activate_item (impl, selected);
4477 }
4478
4479 static gboolean
4480 shortcuts_select_func  (GtkTreeSelection  *selection,
4481                         GtkTreeModel      *model,
4482                         GtkTreePath       *path,
4483                         gboolean           path_currently_selected,
4484                         gpointer           data)
4485 {
4486   GtkFileChooserDefault *impl = data;
4487
4488   return (*gtk_tree_path_get_indices (path) != shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR));
4489 }
4490
4491 static void
4492 list_selection_changed (GtkTreeSelection      *selection,
4493                         GtkFileChooserDefault *impl)
4494 {
4495   /* See if we are in the new folder editable row for Save mode */
4496   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
4497     {
4498       GtkTreeSelection *selection;
4499       GtkTreeIter iter, child_iter;
4500       const GtkFileInfo *info;
4501
4502       g_assert (!impl->select_multiple);
4503       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4504       if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
4505         return;
4506
4507       gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
4508                                                       &child_iter,
4509                                                       &iter);
4510
4511       info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
4512       if (!info)
4513         return; /* We are on the editable row for New Folder */
4514     }
4515
4516   update_chooser_entry (impl);
4517   check_preview_change (impl);
4518   bookmarks_check_add_sensitivity (impl);
4519
4520   g_signal_emit_by_name (impl, "selection-changed", 0);
4521 }
4522
4523 /* Callback used when a row in the file list is activated */
4524 static void
4525 list_row_activated (GtkTreeView           *tree_view,
4526                     GtkTreePath           *path,
4527                     GtkTreeViewColumn     *column,
4528                     GtkFileChooserDefault *impl)
4529 {
4530   GtkTreeIter iter, child_iter;
4531   const GtkFileInfo *info;
4532
4533   if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
4534     return;
4535
4536   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
4537
4538   info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
4539
4540   if (gtk_file_info_get_is_folder (info))
4541     {
4542       const GtkFilePath *file_path;
4543
4544       file_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
4545       change_folder_and_display_error (impl, file_path);
4546
4547       return;
4548     }
4549
4550   g_signal_emit_by_name (impl, "file-activated");
4551 }
4552
4553 static void
4554 path_bar_clicked (GtkPathBar            *path_bar,
4555                   GtkFilePath           *file_path,
4556                   GtkFileChooserDefault *impl)
4557 {
4558   change_folder_and_display_error (impl, file_path);
4559 }
4560
4561 static const GtkFileInfo *
4562 get_list_file_info (GtkFileChooserDefault *impl,
4563                     GtkTreeIter           *iter)
4564 {
4565   GtkTreeIter child_iter;
4566
4567   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
4568                                                   &child_iter,
4569                                                   iter);
4570
4571   return _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
4572 }
4573
4574 static void
4575 list_icon_data_func (GtkTreeViewColumn *tree_column,
4576                      GtkCellRenderer   *cell,
4577                      GtkTreeModel      *tree_model,
4578                      GtkTreeIter       *iter,
4579                      gpointer           data)
4580 {
4581   GtkFileChooserDefault *impl = data;
4582   GtkTreeIter child_iter;
4583   const GtkFilePath *path;
4584   GdkPixbuf *pixbuf;
4585
4586   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
4587                                                   &child_iter,
4588                                                   iter);
4589   path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
4590   if (!path)
4591     return;
4592
4593   /* FIXME: NULL GError */
4594   pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
4595                                         impl->icon_size, NULL);
4596   g_object_set (cell,
4597                 "pixbuf", pixbuf,
4598                 NULL);
4599
4600   if (pixbuf)
4601     g_object_unref (pixbuf);
4602 }
4603
4604 static void
4605 list_name_data_func (GtkTreeViewColumn *tree_column,
4606                      GtkCellRenderer   *cell,
4607                      GtkTreeModel      *tree_model,
4608                      GtkTreeIter       *iter,
4609                      gpointer           data)
4610 {
4611   GtkFileChooserDefault *impl = data;
4612   const GtkFileInfo *info = get_list_file_info (impl, iter);
4613
4614   if (!info)
4615     {
4616       g_object_set (cell,
4617                     "text", _("Type name of new folder"),
4618                     NULL);
4619       return;
4620     }
4621
4622   g_object_set (cell,
4623                 "text", gtk_file_info_get_display_name (info),
4624                 NULL);
4625 }
4626
4627 #if 0
4628 static void
4629 list_size_data_func (GtkTreeViewColumn *tree_column,
4630                      GtkCellRenderer   *cell,
4631                      GtkTreeModel      *tree_model,
4632                      GtkTreeIter       *iter,
4633                      gpointer           data)
4634 {
4635   GtkFileChooserDefault *impl = data;
4636   const GtkFileInfo *info = get_list_file_info (impl, iter);
4637   gint64 size;
4638   gchar *str;
4639
4640   if (!info || gtk_file_info_get_is_folder (info))
4641     return;
4642
4643   size = gtk_file_info_get_size (info);
4644
4645   if (size < (gint64)1024)
4646     str = g_strdup_printf (ngettext ("%d byte", "%d bytes", (gint)size), (gint)size);
4647   else if (size < (gint64)1024*1024)
4648     str = g_strdup_printf (_("%.1f K"), size / (1024.));
4649   else if (size < (gint64)1024*1024*1024)
4650     str = g_strdup_printf (_("%.1f M"), size / (1024.*1024.));
4651   else
4652     str = g_strdup_printf (_("%.1f G"), size / (1024.*1024.*1024.));
4653
4654   g_object_set (cell,
4655                 "text", str,
4656                 NULL);
4657
4658   g_free (str);
4659 }
4660 #endif
4661
4662 /* Tree column data callback for the file list; fetches the mtime of a file */
4663 static void
4664 list_mtime_data_func (GtkTreeViewColumn *tree_column,
4665                       GtkCellRenderer   *cell,
4666                       GtkTreeModel      *tree_model,
4667                       GtkTreeIter       *iter,
4668                       gpointer           data)
4669 {
4670   GtkFileChooserDefault *impl;
4671   const GtkFileInfo *info;
4672   GtkFileTime time_mtime, time_now;
4673   GDate mtime, now;
4674   int days_diff;
4675   char buf[256];
4676
4677   impl = data;
4678
4679   info = get_list_file_info (impl, iter);
4680   if (!info)
4681     {
4682       g_object_set (cell,
4683                     "text", "",
4684                     NULL);
4685       return;
4686     }
4687
4688   time_mtime = gtk_file_info_get_modification_time (info);
4689   g_date_set_time (&mtime, (GTime) time_mtime);
4690
4691   time_now = (GTime ) time (NULL);
4692   g_date_set_time (&now, (GTime) time_now);
4693
4694   days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
4695
4696   if (days_diff == 0)
4697     strcpy (buf, _("Today"));
4698   else if (days_diff == 1)
4699     strcpy (buf, _("Yesterday"));
4700   else
4701     {
4702       char *format;
4703
4704       if (days_diff > 1 && days_diff < 7)
4705         format = "%A"; /* Days from last week */
4706       else
4707         format = "%x"; /* Any other date */
4708
4709       if (g_date_strftime (buf, sizeof (buf), format, &mtime) == 0)
4710         strcpy (buf, _("Unknown"));
4711     }
4712
4713   g_object_set (cell,
4714                 "text", buf,
4715                 NULL);
4716 }
4717
4718 GtkWidget *
4719 _gtk_file_chooser_default_new (const char *file_system)
4720 {
4721   return  g_object_new (GTK_TYPE_FILE_CHOOSER_DEFAULT,
4722                         "file-system-backend", file_system,
4723                         NULL);
4724 }
4725
4726 static GtkWidget *
4727 location_entry_create (GtkFileChooserDefault *impl)
4728 {
4729   GtkWidget *entry;
4730
4731   entry = _gtk_file_chooser_entry_new ();
4732   /* Pick a good width for the entry */
4733   gtk_entry_set_width_chars (GTK_ENTRY (entry), 30);
4734   gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
4735   _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (entry), impl->file_system);
4736   _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (entry), impl->current_folder);
4737
4738   return GTK_WIDGET (entry);
4739 }
4740
4741 static gboolean
4742 update_from_entry (GtkFileChooserDefault *impl,
4743                    GtkWindow             *parent,
4744                    GtkFileChooserEntry   *chooser_entry)
4745 {
4746   const GtkFilePath *folder_path;
4747   const char *file_part;
4748
4749   folder_path = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
4750   file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
4751
4752   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN && !folder_path)
4753     {
4754       error_message_with_parent (parent,
4755                                  _("Cannot change to the folder you specified as it is an invalid path."));
4756       return FALSE;
4757     }
4758
4759   if (file_part[0] == '\0')
4760     return change_folder_and_display_error (impl, folder_path);
4761   else
4762     {
4763       GtkFileFolder *folder = NULL;
4764       GtkFilePath *subfolder_path = NULL;
4765       GtkFileInfo *info = NULL;
4766       GError *error;
4767       gboolean result;
4768
4769       result = FALSE;
4770
4771       /* If the file part is non-empty, we need to figure out if it refers to a
4772        * folder within folder. We could optimize the case here where the folder
4773        * is already loaded for one of our tree models.
4774        */
4775
4776       error = NULL;
4777       folder = gtk_file_system_get_folder (impl->file_system, folder_path, GTK_FILE_INFO_IS_FOLDER, &error);
4778
4779       if (!folder)
4780         {
4781           error_getting_info_dialog (impl, folder_path, error);
4782           goto out;
4783         }
4784
4785       error = NULL;
4786       subfolder_path = gtk_file_system_make_path (impl->file_system, folder_path, file_part, &error);
4787
4788       if (!subfolder_path)
4789         {
4790           char *msg;
4791           char *uri;
4792
4793           uri = gtk_file_system_path_to_uri (impl->file_system, folder_path);
4794           msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
4795                                  uri, file_part,
4796                                  error->message);
4797           error_message (impl, msg);
4798           g_free (uri);
4799           g_free (msg);
4800           goto out;
4801         }
4802
4803       error = NULL;
4804       info = gtk_file_folder_get_info (folder, subfolder_path, &error);
4805
4806       if (!info)
4807         {
4808 #if 0
4809           if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
4810             return;
4811 #endif
4812           error_getting_info_dialog (impl, subfolder_path, error);
4813           goto out;
4814         }
4815
4816       if (gtk_file_info_get_is_folder (info))
4817         result = change_folder_and_display_error (impl, subfolder_path);
4818       else
4819         {
4820           GError *error;
4821
4822           error = NULL;
4823           result = _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (impl), subfolder_path, &error);
4824           if (!result)
4825             error_dialog (impl,
4826                           _("Could not select %s:\n%s"),
4827                           subfolder_path, error);
4828         }
4829
4830     out:
4831
4832       if (folder)
4833         g_object_unref (folder);
4834
4835       gtk_file_path_free (subfolder_path);
4836
4837       if (info)
4838         gtk_file_info_free (info);
4839
4840       return result;
4841     }
4842
4843   g_assert_not_reached ();
4844 }
4845
4846 static void
4847 location_popup_handler (GtkFileChooserDefault *impl)
4848 {
4849   GtkWidget *dialog;
4850   GtkWidget *toplevel;
4851   GtkWidget *hbox;
4852   GtkWidget *label;
4853   GtkWidget *entry;
4854   gboolean refocus;
4855
4856   /* Create dialog */
4857
4858   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
4859   if (!GTK_WIDGET_TOPLEVEL (toplevel))
4860     toplevel = NULL;
4861
4862   dialog = gtk_dialog_new_with_buttons (_("Open Location"),
4863                                         GTK_WINDOW (toplevel),
4864                                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
4865                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4866                                         GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
4867                                         NULL);
4868   gtk_window_set_default_size (GTK_WINDOW (dialog), 300, -1);
4869   gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
4870   gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2);
4871   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
4872
4873   hbox = gtk_hbox_new (FALSE, 12);
4874   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0);
4875   gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
4876
4877   label = gtk_label_new_with_mnemonic (_("_Location:"));
4878   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
4879
4880   entry = location_entry_create (impl);
4881   gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
4882   gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
4883
4884   /* Run */
4885
4886   gtk_widget_show_all (dialog);
4887
4888   refocus = TRUE;
4889
4890   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
4891     {
4892       if (update_from_entry (impl, GTK_WINDOW (dialog), GTK_FILE_CHOOSER_ENTRY (entry)))
4893         {
4894           gtk_widget_grab_focus (impl->browse_files_tree_view);
4895           refocus = FALSE;
4896         }
4897     }
4898
4899   if (refocus)
4900     {
4901       GtkWidget *toplevel;
4902
4903       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
4904       if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WINDOW (toplevel)->focus_widget)
4905         gtk_widget_grab_focus (GTK_WINDOW (toplevel)->focus_widget);
4906     }
4907
4908   gtk_widget_destroy (dialog);
4909 }
4910
4911 /* Handler for the "up-folder" keybinding signal */
4912 static void
4913 up_folder_handler (GtkFileChooserDefault *impl)
4914 {
4915   GtkFilePath *parent_path;
4916   GError *error;
4917
4918   error = NULL;
4919   if (gtk_file_system_get_parent (impl->file_system, impl->current_folder, &parent_path, &error))
4920     {
4921       if (parent_path) /* If we were on a root, parent_path will be NULL */
4922         {
4923           change_folder_and_display_error (impl, parent_path);
4924           gtk_file_path_free (parent_path);
4925         }
4926     }
4927   else
4928     {
4929       error_dialog (impl,
4930                     _("Could not go to the parent folder of %s:\n%s"),
4931                     impl->current_folder, error);
4932     }
4933 }
4934
4935 /* Handler for the "home-folder" keybinding signal */
4936 static void
4937 home_folder_handler (GtkFileChooserDefault *impl)
4938 {
4939   int pos;
4940   GtkTreeIter iter;
4941   GtkFilePath *path;
4942
4943   if (!impl->has_home)
4944     return; /* Should we put up an error dialog? */
4945
4946   pos = shortcuts_get_index (impl, SHORTCUTS_HOME);
4947   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
4948     g_assert_not_reached ();
4949
4950   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &path, -1);
4951   g_assert (path != NULL);
4952
4953   change_folder_and_display_error (impl, path);
4954 }
4955
4956 \f
4957
4958 /* Drag and drop interfaces */
4959
4960 static void
4961 shortcuts_model_filter_class_init (ShortcutsModelFilterClass *class)
4962 {
4963 }
4964
4965 static void
4966 shortcuts_model_filter_init (ShortcutsModelFilter *model)
4967 {
4968   model->impl = NULL;
4969 }
4970
4971 /* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */
4972 static gboolean
4973 shortcuts_model_filter_row_draggable (GtkTreeDragSource *drag_source,
4974                                       GtkTreePath       *path)
4975 {
4976   ShortcutsModelFilter *model;
4977   int pos;
4978   int bookmarks_pos;
4979
4980   model = SHORTCUTS_MODEL_FILTER (drag_source);
4981
4982   pos = *gtk_tree_path_get_indices (path);
4983   bookmarks_pos = shortcuts_get_index (model->impl, SHORTCUTS_BOOKMARKS);
4984
4985   return (pos >= bookmarks_pos && pos < bookmarks_pos + model->impl->num_bookmarks);
4986 }
4987
4988 /* GtkTreeDragSource::drag_data_get implementation for the shortcuts filter model */
4989 static gboolean
4990 shortcuts_model_filter_drag_data_get (GtkTreeDragSource *drag_source,
4991                                       GtkTreePath       *path,
4992                                       GtkSelectionData  *selection_data)
4993 {
4994   ShortcutsModelFilter *model;
4995
4996   model = SHORTCUTS_MODEL_FILTER (drag_source);
4997
4998   /* FIXME */
4999
5000   return FALSE;
5001 }
5002
5003 /* Fill the GtkTreeDragSourceIface vtable */
5004 static void
5005 shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface)
5006 {
5007   iface->row_draggable = shortcuts_model_filter_row_draggable;
5008   iface->drag_data_get = shortcuts_model_filter_drag_data_get;
5009 }
5010
5011 #if 0
5012 /* Fill the GtkTreeDragDestIface vtable */
5013 static void
5014 shortcuts_model_filter_drag_dest_iface_init (GtkTreeDragDestIface *iface)
5015 {
5016   iface->drag_data_received = shortcuts_model_filter_drag_data_received;
5017   iface->row_drop_possible = shortcuts_model_filter_row_drop_possible;
5018 }
5019 #endif
5020
5021 static GtkTreeModel *
5022 shortcuts_model_filter_new (GtkFileChooserDefault *impl,
5023                             GtkTreeModel          *child_model,
5024                             GtkTreePath           *root)
5025 {
5026   ShortcutsModelFilter *model;
5027
5028   model = g_object_new (SHORTCUTS_MODEL_FILTER_TYPE,
5029                         "child_model", child_model,
5030                         "virtual_root", root,
5031                         NULL);
5032
5033   model->impl = impl;
5034
5035   return GTK_TREE_MODEL (model);
5036 }