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