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