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