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