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