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