]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserdefault.c
Merged from gtk-2-6:
[~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 *hbox;
1878   GtkWidget *widget;
1879   GtkWidget *align;
1880
1881   button = gtk_button_new ();
1882   hbox = gtk_hbox_new (FALSE, 2);
1883   align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1884
1885   gtk_container_add (GTK_CONTAINER (button), align);
1886   gtk_container_add (GTK_CONTAINER (align), hbox);
1887   widget = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
1888
1889   gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1890
1891   widget = gtk_label_new_with_mnemonic (text);
1892   gtk_label_set_mnemonic_widget (GTK_LABEL (widget), GTK_WIDGET (button));
1893   gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1894
1895   gtk_widget_set_sensitive (button, sensitive);
1896   g_signal_connect (button, "clicked", callback, impl);
1897
1898   gtk_widget_show_all (align);
1899
1900   if (show)
1901     gtk_widget_show (button);
1902
1903   return button;
1904 }
1905
1906 /* Looks for a path among the shortcuts; returns its index or -1 if it doesn't exist */
1907 static int
1908 shortcut_find_position (GtkFileChooserDefault *impl,
1909                         const GtkFilePath     *path)
1910 {
1911   GtkTreeIter iter;
1912   int i;
1913   int current_folder_separator_idx;
1914
1915   if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
1916     return -1;
1917
1918   current_folder_separator_idx = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1919
1920   for (i = 0; i < current_folder_separator_idx; i++)
1921     {
1922       gpointer col_data;
1923       gboolean is_volume;
1924
1925       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
1926                           SHORTCUTS_COL_DATA, &col_data,
1927                           SHORTCUTS_COL_IS_VOLUME, &is_volume,
1928                           -1);
1929
1930       if (col_data)
1931         {
1932           if (is_volume)
1933             {
1934               GtkFileSystemVolume *volume;
1935               GtkFilePath *base_path;
1936               gboolean exists;
1937
1938               volume = col_data;
1939               base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
1940
1941               exists = strcmp (gtk_file_path_get_string (path),
1942                                gtk_file_path_get_string (base_path)) == 0;
1943               g_free (base_path);
1944
1945               if (exists)
1946                 return i;
1947             }
1948           else
1949             {
1950               GtkFilePath *model_path;
1951
1952               model_path = col_data;
1953
1954               if (model_path && gtk_file_path_compare (model_path, path) == 0)
1955                 return i;
1956             }
1957         }
1958
1959       gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
1960     }
1961
1962   return -1;
1963 }
1964
1965 /* Tries to add a bookmark from a path name */
1966 static gboolean
1967 shortcuts_add_bookmark_from_path (GtkFileChooserDefault *impl,
1968                                   const GtkFilePath     *path,
1969                                   int                    pos)
1970 {
1971   GError *error;
1972
1973   if (shortcut_find_position (impl, path) != -1)
1974     return FALSE;
1975
1976   /* FIXME: this check really belongs in gtk_file_system_insert_bookmark.  */
1977   error = NULL;
1978   if (!check_is_folder (impl->file_system, path, &error))
1979     {
1980       error_adding_bookmark_dialog (impl, path, error);
1981       return FALSE;
1982     }
1983
1984   error = NULL;
1985   if (!gtk_file_system_insert_bookmark (impl->file_system, path, pos, &error))
1986     {
1987       error_adding_bookmark_dialog (impl, path, error);
1988       return FALSE;
1989     }
1990
1991   return TRUE;
1992 }
1993
1994 static void
1995 add_bookmark_foreach_cb (GtkTreeModel *model,
1996                          GtkTreePath  *path,
1997                          GtkTreeIter  *iter,
1998                          gpointer      data)
1999 {
2000   GtkFileChooserDefault *impl;
2001   GtkFileSystemModel *fs_model;
2002   GtkTreeIter child_iter;
2003   const GtkFilePath *file_path;
2004
2005   impl = (GtkFileChooserDefault *) data;
2006
2007   fs_model = impl->browse_files_model;
2008   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter);
2009
2010   file_path = _gtk_file_system_model_get_path (fs_model, &child_iter);
2011   shortcuts_add_bookmark_from_path (impl, file_path, -1);
2012 }
2013
2014 /* Adds a bookmark from the currently selected item in the file list */
2015 static void
2016 bookmarks_add_selected_folder (GtkFileChooserDefault *impl)
2017 {
2018   GtkTreeSelection *selection;
2019
2020   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2021
2022   if (gtk_tree_selection_count_selected_rows (selection) == 0)
2023     shortcuts_add_bookmark_from_path (impl, impl->current_folder, -1);
2024   else
2025     gtk_tree_selection_selected_foreach (selection,
2026                                          add_bookmark_foreach_cb,
2027                                          impl);
2028 }
2029
2030 /* Callback used when the "Add bookmark" button is clicked */
2031 static void
2032 add_bookmark_button_clicked_cb (GtkButton *button,
2033                                 GtkFileChooserDefault *impl)
2034 {
2035   bookmarks_add_selected_folder (impl);
2036 }
2037
2038 /* Returns TRUE plus an iter in the shortcuts_model if a row is selected;
2039  * returns FALSE if no shortcut is selected.
2040  */
2041 static gboolean
2042 shortcuts_get_selected (GtkFileChooserDefault *impl,
2043                         GtkTreeIter           *iter)
2044 {
2045   GtkTreeSelection *selection;
2046   GtkTreeIter parent_iter;
2047
2048   if (!impl->browse_shortcuts_tree_view)
2049     return FALSE;
2050
2051   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
2052
2053   if (!gtk_tree_selection_get_selected (selection, NULL, &parent_iter))
2054     return FALSE;
2055
2056   gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
2057                                                     iter,
2058                                                     &parent_iter);
2059   return TRUE;
2060 }
2061
2062 /* Removes the selected bookmarks */
2063 static void
2064 remove_selected_bookmarks (GtkFileChooserDefault *impl)
2065 {
2066   GtkTreeIter iter;
2067   gpointer col_data;
2068   GtkFilePath *path;
2069   gboolean removable;
2070   GError *error;
2071
2072   if (!shortcuts_get_selected (impl, &iter))
2073     return;
2074
2075   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2076                       SHORTCUTS_COL_DATA, &col_data,
2077                       SHORTCUTS_COL_REMOVABLE, &removable,
2078                       -1);
2079   g_assert (col_data != NULL);
2080
2081   if (!removable)
2082     return;
2083
2084   path = col_data;
2085
2086   error = NULL;
2087   if (!gtk_file_system_remove_bookmark (impl->file_system, path, &error))
2088     error_removing_bookmark_dialog (impl, path, error);
2089 }
2090
2091 /* Callback used when the "Remove bookmark" button is clicked */
2092 static void
2093 remove_bookmark_button_clicked_cb (GtkButton *button,
2094                                    GtkFileChooserDefault *impl)
2095 {
2096   remove_selected_bookmarks (impl);
2097 }
2098
2099 struct selection_check_closure {
2100   GtkFileChooserDefault *impl;
2101   int num_selected;
2102   gboolean all_files;
2103   gboolean all_folders;
2104 };
2105
2106 /* Used from gtk_tree_selection_selected_foreach() */
2107 static void
2108 selection_check_foreach_cb (GtkTreeModel *model,
2109                             GtkTreePath  *path,
2110                             GtkTreeIter  *iter,
2111                             gpointer      data)
2112 {
2113   struct selection_check_closure *closure;
2114   GtkTreeIter child_iter;
2115   const GtkFileInfo *info;
2116   gboolean is_folder;
2117
2118   closure = data;
2119   closure->num_selected++;
2120
2121   gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
2122
2123   info = _gtk_file_system_model_get_info (closure->impl->browse_files_model, &child_iter);
2124   is_folder = info ? gtk_file_info_get_is_folder (info) : FALSE;
2125
2126   closure->all_folders = closure->all_folders && is_folder;
2127   closure->all_files = closure->all_files && !is_folder;
2128 }
2129
2130 /* Checks whether the selected items in the file list are all files or all folders */
2131 static void
2132 selection_check (GtkFileChooserDefault *impl,
2133                  gint                  *num_selected,
2134                  gboolean              *all_files,
2135                  gboolean              *all_folders)
2136 {
2137   struct selection_check_closure closure;
2138   GtkTreeSelection *selection;
2139
2140   closure.impl = impl;
2141   closure.num_selected = 0;
2142   closure.all_files = TRUE;
2143   closure.all_folders = TRUE;
2144
2145   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2146   gtk_tree_selection_selected_foreach (selection,
2147                                        selection_check_foreach_cb,
2148                                        &closure);
2149
2150   g_assert (closure.num_selected == 0 || !(closure.all_files && closure.all_folders));
2151
2152   if (num_selected)
2153     *num_selected = closure.num_selected;
2154
2155   if (all_files)
2156     *all_files = closure.all_files;
2157
2158   if (all_folders)
2159     *all_folders = closure.all_folders;
2160 }
2161
2162 struct get_selected_path_closure {
2163   GtkFileChooserDefault *impl;
2164   const GtkFilePath *path;
2165 };
2166
2167 static void
2168 get_selected_path_foreach_cb (GtkTreeModel *model,
2169                               GtkTreePath  *path,
2170                               GtkTreeIter  *iter,
2171                               gpointer      data)
2172 {
2173   struct get_selected_path_closure *closure;
2174   GtkTreeIter child_iter;
2175
2176   closure = data;
2177
2178   gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
2179   closure->path = _gtk_file_system_model_get_path (closure->impl->browse_files_model, &child_iter);
2180 }
2181
2182 /* Returns a selected path from the file list */
2183 static const GtkFilePath *
2184 get_selected_path (GtkFileChooserDefault *impl)
2185 {
2186   struct get_selected_path_closure closure;
2187   GtkTreeSelection *selection;
2188
2189   closure.impl = impl;
2190   closure.path = NULL;
2191
2192   selection =  gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2193   gtk_tree_selection_selected_foreach (selection,
2194                                        get_selected_path_foreach_cb,
2195                                        &closure);
2196
2197   return closure.path;
2198 }
2199
2200 typedef struct {
2201   GtkFileChooserDefault *impl;
2202   gchar *tip;
2203 } UpdateTooltipData;
2204
2205 static void 
2206 update_tooltip (GtkTreeModel      *model,
2207                 GtkTreePath       *path,
2208                 GtkTreeIter       *iter,
2209                 gpointer           data)
2210 {
2211   UpdateTooltipData *udata = data;
2212   GtkTreeIter child_iter;
2213   const GtkFileInfo *info;
2214
2215   if (udata->tip == NULL)
2216     {
2217       gtk_tree_model_sort_convert_iter_to_child_iter (udata->impl->sort_model,
2218                                                       &child_iter,
2219                                                       iter);
2220   
2221       info = _gtk_file_system_model_get_info (udata->impl->browse_files_model, &child_iter);
2222       udata->tip = g_strdup_printf (_("Add the folder '%s' to the bookmarks"),
2223                                     gtk_file_info_get_display_name (info));
2224     }
2225 }
2226
2227
2228 /* Sensitize the "add bookmark" button if all the selected items are folders, or
2229  * if there are no selected items *and* the current folder is not in the
2230  * bookmarks list.  De-sensitize the button otherwise.
2231  */
2232 static void
2233 bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl)
2234 {
2235   gint num_selected;
2236   gboolean all_folders;
2237   gboolean active;
2238   gchar *tip;
2239
2240   selection_check (impl, &num_selected, NULL, &all_folders);
2241
2242   if (num_selected == 0)
2243     active = (impl->current_folder != NULL) && (shortcut_find_position (impl, impl->current_folder) == -1);
2244   else if (num_selected == 1)
2245     {
2246       const GtkFilePath *path;
2247
2248       path = get_selected_path (impl);
2249       active = all_folders && (shortcut_find_position (impl, path) == -1);
2250     }
2251   else
2252     active = all_folders;
2253
2254   gtk_widget_set_sensitive (impl->browse_shortcuts_add_button, active);
2255
2256   if (impl->browse_files_popup_menu_add_shortcut_item)
2257     gtk_widget_set_sensitive (impl->browse_files_popup_menu_add_shortcut_item,
2258                               (num_selected == 0) ? FALSE : active);
2259
2260   if (active)
2261     {
2262       if (num_selected == 0)
2263         tip = g_strdup_printf (_("Add the current folder to the bookmarks"));    
2264       else if (num_selected > 1)
2265         tip = g_strdup_printf (_("Add the selected folders to the bookmarks"));
2266       else
2267         {
2268           GtkTreeSelection *selection;
2269           UpdateTooltipData data;
2270           
2271           selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2272           data.impl = impl;
2273           data.tip = NULL;
2274           gtk_tree_selection_selected_foreach (selection, update_tooltip, &data);
2275           tip = data.tip;
2276           
2277         }
2278       gtk_tooltips_set_tip (impl->tooltips, impl->browse_shortcuts_add_button, tip, NULL);
2279       g_free (tip);
2280     }
2281 }
2282
2283 /* Sets the sensitivity of the "remove bookmark" button depending on whether a
2284  * bookmark row is selected in the shortcuts tree.
2285  */
2286 static void
2287 bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl)
2288 {
2289   GtkTreeIter iter;
2290   gboolean removable = FALSE;
2291   gchar *name = NULL;
2292   
2293   if (shortcuts_get_selected (impl, &iter))
2294     gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2295                         SHORTCUTS_COL_REMOVABLE, &removable,
2296                         SHORTCUTS_COL_NAME, &name,
2297                         -1);
2298
2299   gtk_widget_set_sensitive (impl->browse_shortcuts_remove_button, removable);
2300
2301   if (removable)
2302     {
2303       gchar *tip;
2304
2305       tip = g_strdup_printf (_("Remove the bookmark '%s'"), name);
2306       gtk_tooltips_set_tip (impl->tooltips, impl->browse_shortcuts_remove_button,
2307                             tip, NULL);
2308       g_free (tip);
2309     }
2310
2311   g_free (name);
2312 }
2313
2314 static void
2315 shortcuts_check_popup_sensitivity (GtkFileChooserDefault *impl)
2316 {
2317   GtkTreeIter iter;
2318   gboolean removable = FALSE;
2319
2320   if (impl->browse_shortcuts_popup_menu == NULL)
2321     return;
2322
2323   if (shortcuts_get_selected (impl, &iter))
2324     gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2325                         SHORTCUTS_COL_REMOVABLE, &removable,
2326                         -1);
2327
2328   gtk_widget_set_sensitive (impl->browse_shortcuts_popup_menu_remove_item, removable);
2329   gtk_widget_set_sensitive (impl->browse_shortcuts_popup_menu_rename_item, removable);
2330 }
2331
2332 /* GtkWidget::drag-begin handler for the shortcuts list. */
2333 static void
2334 shortcuts_drag_begin_cb (GtkWidget             *widget,
2335                          GdkDragContext        *context,
2336                          GtkFileChooserDefault *impl)
2337 {
2338 #if 0
2339   impl->shortcuts_drag_context = g_object_ref (context);
2340 #endif
2341 }
2342
2343 #if 0
2344 /* Removes the idle handler for outside drags */
2345 static void
2346 shortcuts_cancel_drag_outside_idle (GtkFileChooserDefault *impl)
2347 {
2348   if (!impl->shortcuts_drag_outside_idle)
2349     return;
2350
2351   g_source_destroy (impl->shortcuts_drag_outside_idle);
2352   impl->shortcuts_drag_outside_idle = NULL;
2353 }
2354 #endif
2355
2356 /* GtkWidget::drag-end handler for the shortcuts list. */
2357 static void
2358 shortcuts_drag_end_cb (GtkWidget             *widget,
2359                        GdkDragContext        *context,
2360                        GtkFileChooserDefault *impl)
2361 {
2362 #if 0
2363   g_object_unref (impl->shortcuts_drag_context);
2364
2365   shortcuts_cancel_drag_outside_idle (impl);
2366
2367   if (!impl->shortcuts_drag_outside)
2368     return;
2369
2370   gtk_button_clicked (GTK_BUTTON (impl->browse_shortcuts_remove_button));
2371
2372   impl->shortcuts_drag_outside = FALSE;
2373 #endif
2374 }
2375
2376 /* GtkWidget::drag-data-delete handler for the shortcuts list. */
2377 static void
2378 shortcuts_drag_data_delete_cb (GtkWidget             *widget,
2379                                GdkDragContext        *context,
2380                                GtkFileChooserDefault *impl)
2381 {
2382   g_signal_stop_emission_by_name (widget, "drag-data-delete");
2383 }
2384
2385 #if 0
2386 /* Creates a suitable drag cursor to indicate that the selected bookmark will be
2387  * deleted or not.
2388  */
2389 static void
2390 shortcuts_drag_set_delete_cursor (GtkFileChooserDefault *impl,
2391                                   gboolean               delete)
2392 {
2393   GtkTreeView *tree_view;
2394   GtkTreeIter iter;
2395   GtkTreePath *path;
2396   GdkPixmap *row_pixmap;
2397   GdkBitmap *mask;
2398   int row_pixmap_y;
2399   int cell_y;
2400
2401   tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
2402
2403   /* Find the selected path and get its drag pixmap */
2404
2405   if (!shortcuts_get_selected (impl, &iter))
2406     g_assert_not_reached ();
2407
2408   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
2409
2410   row_pixmap = gtk_tree_view_create_row_drag_icon (tree_view, path);
2411   gtk_tree_path_free (path);
2412
2413   mask = NULL;
2414   row_pixmap_y = 0;
2415
2416   if (delete)
2417     {
2418       GdkPixbuf *pixbuf;
2419
2420       pixbuf = gtk_widget_render_icon (impl->browse_shortcuts_tree_view,
2421                                        GTK_STOCK_DELETE,
2422                                        GTK_ICON_SIZE_DND,
2423                                        NULL);
2424       if (pixbuf)
2425         {
2426           GdkPixmap *composite;
2427           int row_pixmap_width, row_pixmap_height;
2428           int pixbuf_width, pixbuf_height;
2429           int composite_width, composite_height;
2430           int pixbuf_x, pixbuf_y;
2431           GdkGC *gc, *mask_gc;
2432           GdkColor color;
2433           GdkBitmap *pixbuf_mask;
2434
2435           /* Create pixmap and mask for composite image */
2436
2437           gdk_drawable_get_size (row_pixmap, &row_pixmap_width, &row_pixmap_height);
2438           pixbuf_width = gdk_pixbuf_get_width (pixbuf);
2439           pixbuf_height = gdk_pixbuf_get_height (pixbuf);
2440
2441           composite_width = MAX (row_pixmap_width, pixbuf_width);
2442           composite_height = MAX (row_pixmap_height, pixbuf_height);
2443
2444           row_pixmap_y = (composite_height - row_pixmap_height) / 2;
2445
2446           if (gtk_widget_get_direction (impl->browse_shortcuts_tree_view) == GTK_TEXT_DIR_RTL)
2447             pixbuf_x = 0;
2448           else
2449             pixbuf_x = composite_width - pixbuf_width;
2450
2451           pixbuf_y = (composite_height - pixbuf_height) / 2;
2452
2453           composite = gdk_pixmap_new (row_pixmap, composite_width, composite_height, -1);
2454           gc = gdk_gc_new (composite);
2455
2456           mask = gdk_pixmap_new (row_pixmap, composite_width, composite_height, 1);
2457           mask_gc = gdk_gc_new (mask);
2458           color.pixel = 0;
2459           gdk_gc_set_foreground (mask_gc, &color);
2460           gdk_draw_rectangle (mask, mask_gc, TRUE, 0, 0, composite_width, composite_height);
2461
2462           color.red = 0xffff;
2463           color.green = 0xffff;
2464           color.blue = 0xffff;
2465           gdk_gc_set_rgb_fg_color (gc, &color);
2466           gdk_draw_rectangle (composite, gc, TRUE, 0, 0, composite_width, composite_height);
2467
2468           /* Composite the row pixmap and the pixbuf */
2469
2470           gdk_pixbuf_render_pixmap_and_mask_for_colormap
2471             (pixbuf,
2472              gtk_widget_get_colormap (impl->browse_shortcuts_tree_view),
2473              NULL, &pixbuf_mask, 128);
2474           gdk_draw_drawable (mask, mask_gc, pixbuf_mask,
2475                              0, 0,
2476                              pixbuf_x, pixbuf_y,
2477                              pixbuf_width, pixbuf_height);
2478           g_object_unref (pixbuf_mask);
2479
2480           gdk_draw_drawable (composite, gc, row_pixmap,
2481                              0, 0,
2482                              0, row_pixmap_y,
2483                              row_pixmap_width, row_pixmap_height);
2484           color.pixel = 1;
2485           gdk_gc_set_foreground (mask_gc, &color);
2486           gdk_draw_rectangle (mask, mask_gc, TRUE, 0, row_pixmap_y, row_pixmap_width, row_pixmap_height);
2487
2488           gdk_draw_pixbuf (composite, gc, pixbuf,
2489                            0, 0,
2490                            pixbuf_x, pixbuf_y,
2491                            pixbuf_width, pixbuf_height,
2492                            GDK_RGB_DITHER_MAX,
2493                            0, 0);
2494
2495           g_object_unref (pixbuf);
2496           g_object_unref (row_pixmap);
2497
2498           row_pixmap = composite;
2499         }
2500     }
2501
2502   /* The hotspot offsets here are copied from gtk_tree_view_drag_begin(), ugh */
2503
2504   gtk_tree_view_get_path_at_pos (tree_view,
2505                                  tree_view->priv->press_start_x,
2506                                  tree_view->priv->press_start_y,
2507                                  NULL,
2508                                  NULL,
2509                                  NULL,
2510                                  &cell_y);
2511
2512   gtk_drag_set_icon_pixmap (impl->shortcuts_drag_context,
2513                             gdk_drawable_get_colormap (row_pixmap),
2514                             row_pixmap,
2515                             mask,
2516                             tree_view->priv->press_start_x + 1,
2517                             row_pixmap_y + cell_y + 1);
2518
2519   g_object_unref (row_pixmap);
2520   if (mask)
2521     g_object_unref (mask);
2522 }
2523
2524 /* We set the delete cursor and the shortcuts_drag_outside flag in an idle
2525  * handler so that we can tell apart the drag_leave event that comes right
2526  * before a drag_drop, from a normal drag_leave.  We don't want to set the
2527  * cursor nor the flag in the latter case.
2528  */
2529 static gboolean
2530 shortcuts_drag_outside_idle_cb (GtkFileChooserDefault *impl)
2531 {
2532   GDK_THREADS_ENTER ();
2533   
2534   shortcuts_drag_set_delete_cursor (impl, TRUE);
2535   impl->shortcuts_drag_outside = TRUE;
2536
2537   shortcuts_cancel_drag_outside_idle (impl);
2538
2539   GDK_THREADS_LEAVE ();
2540
2541   return FALSE;
2542 }
2543 #endif
2544
2545 /* GtkWidget::drag-leave handler for the shortcuts list.  We unhighlight the
2546  * drop position.
2547  */
2548 static void
2549 shortcuts_drag_leave_cb (GtkWidget             *widget,
2550                          GdkDragContext        *context,
2551                          guint                  time_,
2552                          GtkFileChooserDefault *impl)
2553 {
2554 #if 0
2555   if (gtk_drag_get_source_widget (context) == widget && !impl->shortcuts_drag_outside_idle)
2556     {
2557       impl->shortcuts_drag_outside_idle = g_idle_source_new ();
2558       g_source_set_closure (impl->shortcuts_drag_outside_idle,
2559                             g_cclosure_new_object (G_CALLBACK (shortcuts_drag_outside_idle_cb),
2560                                                    G_OBJECT (impl)));
2561       g_source_attach (impl->shortcuts_drag_outside_idle, NULL);
2562     }
2563 #endif
2564
2565   gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
2566                                    NULL,
2567                                    GTK_TREE_VIEW_DROP_BEFORE);
2568
2569   g_signal_stop_emission_by_name (widget, "drag-leave");
2570 }
2571
2572 /* Computes the appropriate row and position for dropping */
2573 static void
2574 shortcuts_compute_drop_position (GtkFileChooserDefault   *impl,
2575                                  int                      x,
2576                                  int                      y,
2577                                  GtkTreePath            **path,
2578                                  GtkTreeViewDropPosition *pos)
2579 {
2580   GtkTreeView *tree_view;
2581   GtkTreeViewColumn *column;
2582   int cell_y;
2583   GdkRectangle cell;
2584   int row;
2585   int bookmarks_index;
2586
2587   tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
2588
2589   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2590
2591   if (!gtk_tree_view_get_path_at_pos (tree_view,
2592                                       x,
2593                                       y - TREE_VIEW_HEADER_HEIGHT (tree_view),
2594                                       path,
2595                                       &column,
2596                                       NULL,
2597                                       &cell_y))
2598     {
2599       row = bookmarks_index + impl->num_bookmarks - 1;
2600       *path = gtk_tree_path_new_from_indices (row, -1);
2601       *pos = GTK_TREE_VIEW_DROP_AFTER;
2602       return;
2603     }
2604
2605   row = *gtk_tree_path_get_indices (*path);
2606   gtk_tree_view_get_background_area (tree_view, *path, column, &cell);
2607   gtk_tree_path_free (*path);
2608
2609   if (row < bookmarks_index)
2610     {
2611       row = bookmarks_index;
2612       *pos = GTK_TREE_VIEW_DROP_BEFORE;
2613     }
2614   else if (row > bookmarks_index + impl->num_bookmarks - 1)
2615     {
2616       row = bookmarks_index + impl->num_bookmarks - 1;
2617       *pos = GTK_TREE_VIEW_DROP_AFTER;
2618     }
2619   else
2620     {
2621       if (cell_y < cell.height / 2)
2622         *pos = GTK_TREE_VIEW_DROP_BEFORE;
2623       else
2624         *pos = GTK_TREE_VIEW_DROP_AFTER;
2625     }
2626
2627   *path = gtk_tree_path_new_from_indices (row, -1);
2628 }
2629
2630 /* GtkWidget::drag-motion handler for the shortcuts list.  We basically
2631  * implement the destination side of DnD by hand, due to limitations in
2632  * GtkTreeView's DnD API.
2633  */
2634 static gboolean
2635 shortcuts_drag_motion_cb (GtkWidget             *widget,
2636                           GdkDragContext        *context,
2637                           gint                   x,
2638                           gint                   y,
2639                           guint                  time_,
2640                           GtkFileChooserDefault *impl)
2641 {
2642   GtkTreePath *path;
2643   GtkTreeViewDropPosition pos;
2644   GdkDragAction action;
2645
2646 #if 0
2647   if (gtk_drag_get_source_widget (context) == widget)
2648     {
2649       shortcuts_cancel_drag_outside_idle (impl);
2650
2651       if (impl->shortcuts_drag_outside)
2652         {
2653           shortcuts_drag_set_delete_cursor (impl, FALSE);
2654           impl->shortcuts_drag_outside = FALSE;
2655         }
2656     }
2657 #endif
2658
2659   if (context->suggested_action == GDK_ACTION_COPY || (context->actions & GDK_ACTION_COPY) != 0)
2660     action = GDK_ACTION_COPY;
2661   else if (context->suggested_action == GDK_ACTION_MOVE || (context->actions & GDK_ACTION_MOVE) != 0)
2662     action = GDK_ACTION_MOVE;
2663   else
2664     {
2665       action = 0;
2666       goto out;
2667     }
2668
2669   shortcuts_compute_drop_position (impl, x, y, &path, &pos);
2670   gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), path, pos);
2671   gtk_tree_path_free (path);
2672
2673  out:
2674
2675   g_signal_stop_emission_by_name (widget, "drag-motion");
2676
2677   if (action != 0)
2678     {
2679       gdk_drag_status (context, action, time_);
2680       return TRUE;
2681     }
2682   else
2683     return FALSE;
2684 }
2685
2686 /* GtkWidget::drag-drop handler for the shortcuts list. */
2687 static gboolean
2688 shortcuts_drag_drop_cb (GtkWidget             *widget,
2689                         GdkDragContext        *context,
2690                         gint                   x,
2691                         gint                   y,
2692                         guint                  time_,
2693                         GtkFileChooserDefault *impl)
2694 {
2695 #if 0
2696   shortcuts_cancel_drag_outside_idle (impl);
2697 #endif
2698
2699   g_signal_stop_emission_by_name (widget, "drag-drop");
2700   return TRUE;
2701 }
2702
2703 /* Parses a "text/uri-list" string and inserts its URIs as bookmarks */
2704 static void
2705 shortcuts_drop_uris (GtkFileChooserDefault *impl,
2706                      const char            *data,
2707                      int                    position)
2708 {
2709   gchar **uris;
2710   gint i;
2711
2712   uris = g_uri_list_extract_uris (data);
2713
2714   for (i = 0; uris[i]; i++)
2715     {
2716       char *uri;
2717       GtkFilePath *path;
2718
2719       uri = uris[i];
2720       path = gtk_file_system_uri_to_path (impl->file_system, uri);
2721
2722       if (path)
2723         {
2724           if (shortcuts_add_bookmark_from_path (impl, path, position))
2725             position++;
2726
2727           gtk_file_path_free (path);
2728         }
2729       else
2730         {
2731           GError *error;
2732
2733           g_set_error (&error,
2734                        GTK_FILE_CHOOSER_ERROR,
2735                        GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
2736                        _("Could not add a bookmark for '%s' "
2737                          "because it is an invalid path name."),
2738                        uri);
2739           error_adding_bookmark_dialog (impl, path, error);
2740         }
2741     }
2742
2743   g_strfreev (uris);
2744 }
2745
2746 /* Reorders the selected bookmark to the specified position */
2747 static void
2748 shortcuts_reorder (GtkFileChooserDefault *impl,
2749                    int                    new_position)
2750 {
2751   GtkTreeIter iter;
2752   gpointer col_data;
2753   gboolean is_volume;
2754   GtkTreePath *path;
2755   int old_position;
2756   int bookmarks_index;
2757   const GtkFilePath *file_path;
2758   GtkFilePath *file_path_copy;
2759   GError *error;
2760
2761   /* Get the selected path */
2762
2763   if (!shortcuts_get_selected (impl, &iter))
2764     g_assert_not_reached ();
2765
2766   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
2767   old_position = *gtk_tree_path_get_indices (path);
2768   gtk_tree_path_free (path);
2769
2770   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2771   old_position -= bookmarks_index;
2772   g_assert (old_position >= 0 && old_position < impl->num_bookmarks);
2773
2774   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2775                       SHORTCUTS_COL_DATA, &col_data,
2776                       SHORTCUTS_COL_IS_VOLUME, &is_volume,
2777                       -1);
2778   g_assert (col_data != NULL);
2779   g_assert (!is_volume);
2780
2781   file_path = col_data;
2782   file_path_copy = gtk_file_path_copy (file_path); /* removal below will free file_path, so we need a copy */
2783
2784   /* Remove the path from the old position and insert it in the new one */
2785
2786   if (new_position > old_position)
2787     new_position--;
2788
2789   if (old_position == new_position)
2790     goto out;
2791
2792   error = NULL;
2793   if (gtk_file_system_remove_bookmark (impl->file_system, file_path_copy, &error))
2794     shortcuts_add_bookmark_from_path (impl, file_path_copy, new_position);
2795   else
2796     error_adding_bookmark_dialog (impl, file_path_copy, error);
2797
2798  out:
2799
2800   gtk_file_path_free (file_path_copy);
2801 }
2802
2803 /* Callback used when we get the drag data for the bookmarks list.  We add the
2804  * received URIs as bookmarks if they are folders.
2805  */
2806 static void
2807 shortcuts_drag_data_received_cb (GtkWidget          *widget,
2808                                  GdkDragContext     *context,
2809                                  gint                x,
2810                                  gint                y,
2811                                  GtkSelectionData   *selection_data,
2812                                  guint               info,
2813                                  guint               time_,
2814                                  gpointer            data)
2815 {
2816   GtkFileChooserDefault *impl;
2817   GtkTreePath *tree_path;
2818   GtkTreeViewDropPosition tree_pos;
2819   int position;
2820   int bookmarks_index;
2821
2822   impl = GTK_FILE_CHOOSER_DEFAULT (data);
2823
2824   /* Compute position */
2825
2826   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2827
2828   shortcuts_compute_drop_position (impl, x, y, &tree_path, &tree_pos);
2829   position = *gtk_tree_path_get_indices (tree_path);
2830   gtk_tree_path_free (tree_path);
2831
2832   if (tree_pos == GTK_TREE_VIEW_DROP_AFTER)
2833     position++;
2834
2835   g_assert (position >= bookmarks_index);
2836   position -= bookmarks_index;
2837
2838   if (selection_data->target == gdk_atom_intern ("text/uri-list", FALSE))
2839     shortcuts_drop_uris (impl, selection_data->data, position);
2840   else if (selection_data->target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE))
2841     shortcuts_reorder (impl, position);
2842
2843   g_signal_stop_emission_by_name (widget, "drag-data-received");
2844 }
2845
2846 /* Callback used when the selection in the shortcuts tree changes */
2847 static void
2848 shortcuts_selection_changed_cb (GtkTreeSelection      *selection,
2849                                 GtkFileChooserDefault *impl)
2850 {
2851   bookmarks_check_remove_sensitivity (impl);
2852   shortcuts_check_popup_sensitivity (impl);
2853 }
2854
2855 static gboolean
2856 shortcuts_row_separator_func (GtkTreeModel *model,
2857                               GtkTreeIter  *iter,
2858                               gpointer      data)
2859 {
2860   gint column = GPOINTER_TO_INT (data);
2861   gchar *text;
2862
2863   gtk_tree_model_get (model, iter, column, &text, -1);
2864   
2865   if (!text)
2866     return TRUE;
2867
2868   g_free (text);
2869
2870   return FALSE;
2871 }
2872
2873 /* Since GtkTreeView has a keybinding attached to '/', we need to catch
2874  * keypresses before the TreeView gets them.
2875  */
2876 static gboolean
2877 tree_view_keybinding_cb (GtkWidget             *tree_view,
2878                          GdkEventKey           *event,
2879                          GtkFileChooserDefault *impl)
2880 {
2881   if (event->keyval == GDK_slash &&
2882       ! (event->state & (~GDK_SHIFT_MASK & gtk_accelerator_get_default_mod_mask ())))
2883     {
2884       location_popup_handler (impl, "/");
2885       return TRUE;
2886     }
2887   
2888   return FALSE;
2889 }
2890
2891 /* Callback used when the file list's popup menu is detached */
2892 static void
2893 shortcuts_popup_menu_detach_cb (GtkWidget *attach_widget,
2894                                 GtkMenu   *menu)
2895 {
2896   GtkFileChooserDefault *impl;
2897   
2898   impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
2899   g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
2900
2901   impl->browse_shortcuts_popup_menu = NULL;
2902   impl->browse_shortcuts_popup_menu_remove_item = NULL;
2903   impl->browse_shortcuts_popup_menu_rename_item = NULL;
2904 }
2905
2906 static void
2907 remove_shortcut_cb (GtkMenuItem           *item,
2908                     GtkFileChooserDefault *impl)
2909 {
2910   remove_selected_bookmarks (impl);
2911 }
2912
2913 static void
2914 rename_shortcut_cb (GtkMenuItem           *item,
2915                     GtkFileChooserDefault *impl)
2916 {
2917   GtkTreeIter iter;
2918   GtkTreePath *path;
2919   GtkTreeViewColumn *column;
2920   GtkCellRenderer *cell;
2921   GList *renderers;
2922
2923   if (shortcuts_get_selected (impl, &iter))
2924     {
2925       path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
2926       column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), 0);
2927       renderers = gtk_tree_view_column_get_cell_renderers (column);
2928       cell = g_list_nth_data (renderers, 1);
2929       g_list_free (renderers);
2930       g_object_set (cell, "editable", TRUE, NULL);
2931       gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
2932                                         path, column, cell, TRUE);
2933       gtk_tree_path_free (path);
2934     }
2935 }
2936
2937 /* Constructs the popup menu for the file list if needed */
2938 static void
2939 shortcuts_build_popup_menu (GtkFileChooserDefault *impl)
2940 {
2941   GtkWidget *item;
2942
2943   if (impl->browse_shortcuts_popup_menu)
2944     return;
2945
2946   impl->browse_shortcuts_popup_menu = gtk_menu_new ();
2947   gtk_menu_attach_to_widget (GTK_MENU (impl->browse_shortcuts_popup_menu),
2948                              impl->browse_shortcuts_tree_view,
2949                              shortcuts_popup_menu_detach_cb);
2950
2951   item = gtk_image_menu_item_new_with_label (_("Remove"));
2952   impl->browse_shortcuts_popup_menu_remove_item = item;
2953   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
2954                                  gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU));
2955   g_signal_connect (item, "activate",
2956                     G_CALLBACK (remove_shortcut_cb), impl);
2957   gtk_widget_show (item);
2958   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu), item);
2959
2960   item = gtk_menu_item_new_with_label (_("Rename..."));
2961   impl->browse_shortcuts_popup_menu_rename_item = item;
2962   g_signal_connect (item, "activate",
2963                     G_CALLBACK (rename_shortcut_cb), impl);
2964   gtk_widget_show (item);
2965   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu), item);
2966
2967   shortcuts_check_popup_sensitivity (impl);
2968 }
2969
2970 static void
2971 shortcuts_update_popup_menu (GtkFileChooserDefault *impl)
2972 {
2973   shortcuts_build_popup_menu (impl);  
2974 }
2975
2976 static void
2977 popup_position_func (GtkMenu   *menu,
2978                      gint      *x,
2979                      gint      *y,
2980                      gboolean  *push_in,
2981                      gpointer   user_data);
2982
2983 static void
2984 shortcuts_popup_menu (GtkFileChooserDefault *impl,
2985                       GdkEventButton        *event)
2986 {
2987   shortcuts_update_popup_menu (impl);
2988   if (event)
2989     gtk_menu_popup (GTK_MENU (impl->browse_shortcuts_popup_menu),
2990                     NULL, NULL, NULL, NULL,
2991                     event->button, event->time);
2992   else
2993     {
2994       gtk_menu_popup (GTK_MENU (impl->browse_shortcuts_popup_menu),
2995                       NULL, NULL,
2996                       popup_position_func, impl->browse_shortcuts_tree_view,
2997                       0, GDK_CURRENT_TIME);
2998       gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu),
2999                                    FALSE);
3000     }
3001 }
3002
3003 /* Callback used for the GtkWidget::popup-menu signal of the shortcuts list */
3004 static gboolean
3005 shortcuts_popup_menu_cb (GtkWidget *widget,
3006                          GtkFileChooserDefault *impl)
3007 {
3008   shortcuts_popup_menu (impl, NULL);
3009   return TRUE;
3010 }
3011
3012 /* Callback used when a button is pressed on the shortcuts list.  
3013  * We trap button 3 to bring up a popup menu.
3014  */
3015 static gboolean
3016 shortcuts_button_press_event_cb (GtkWidget             *widget,
3017                                  GdkEventButton        *event,
3018                                  GtkFileChooserDefault *impl)
3019 {
3020   if (event->button != 3)
3021     return FALSE;
3022
3023   shortcuts_popup_menu (impl, event);
3024   return TRUE;
3025 }
3026
3027 static void
3028 shortcuts_edited (GtkCellRenderer       *cell,
3029                   gchar                 *path_string,
3030                   gchar                 *new_text,
3031                   GtkFileChooserDefault *impl)
3032 {
3033   GtkTreePath *path;
3034   GtkTreeIter iter;
3035   GtkFilePath *shortcut;
3036
3037   g_object_set (cell, "editable", FALSE, NULL);
3038
3039   path = gtk_tree_path_new_from_string (path_string);
3040   gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path);
3041   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
3042                       SHORTCUTS_COL_DATA, &shortcut,
3043                       -1);
3044   gtk_tree_path_free (path);
3045   
3046   gtk_file_system_set_bookmark_label (impl->file_system, shortcut, new_text);
3047 }
3048
3049 static void
3050 shortcuts_editing_canceled (GtkCellRenderer       *cell,
3051                             GtkFileChooserDefault *impl)
3052 {
3053   g_object_set (cell, "editable", FALSE, NULL);
3054 }
3055
3056 /* Creates the widgets for the shortcuts and bookmarks tree */
3057 static GtkWidget *
3058 shortcuts_list_create (GtkFileChooserDefault *impl)
3059 {
3060   GtkWidget *swin;
3061   GtkTreeSelection *selection;
3062   GtkTreeViewColumn *column;
3063   GtkCellRenderer *renderer;
3064
3065   /* Scrolled window */
3066
3067   swin = gtk_scrolled_window_new (NULL, NULL);
3068   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
3069                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3070   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
3071                                        GTK_SHADOW_IN);
3072   gtk_widget_show (swin);
3073
3074   /* Tree */
3075
3076   impl->browse_shortcuts_tree_view = gtk_tree_view_new ();
3077   g_signal_connect (impl->browse_shortcuts_tree_view, "key-press-event",
3078                     G_CALLBACK (tree_view_keybinding_cb), impl);
3079   g_signal_connect (impl->browse_shortcuts_tree_view, "popup-menu",
3080                     G_CALLBACK (shortcuts_popup_menu_cb), impl);
3081   g_signal_connect (impl->browse_shortcuts_tree_view, "button-press-event",
3082                     G_CALLBACK (shortcuts_button_press_event_cb), impl);
3083   atk_object_set_name (gtk_widget_get_accessible (impl->browse_shortcuts_tree_view), _("Shortcuts"));
3084   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), FALSE);
3085
3086   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), impl->shortcuts_filter_model);
3087
3088   gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3089                                           GDK_BUTTON1_MASK,
3090                                           shortcuts_source_targets,
3091                                           num_shortcuts_source_targets,
3092                                           GDK_ACTION_MOVE);
3093
3094   gtk_drag_dest_set (impl->browse_shortcuts_tree_view,
3095                      GTK_DEST_DEFAULT_ALL,
3096                      shortcuts_dest_targets,
3097                      num_shortcuts_dest_targets,
3098                      GDK_ACTION_COPY | GDK_ACTION_MOVE);
3099
3100   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
3101   gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
3102   gtk_tree_selection_set_select_function (selection,
3103                                           shortcuts_select_func,
3104                                           impl, NULL);
3105
3106   g_signal_connect (selection, "changed",
3107                     G_CALLBACK (shortcuts_selection_changed_cb), impl);
3108
3109   g_signal_connect (impl->browse_shortcuts_tree_view, "row-activated",
3110                     G_CALLBACK (shortcuts_row_activated_cb), impl);
3111
3112   g_signal_connect (impl->browse_shortcuts_tree_view, "key-press-event",
3113                     G_CALLBACK (shortcuts_key_press_event_cb), impl);
3114
3115   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-begin",
3116                     G_CALLBACK (shortcuts_drag_begin_cb), impl);
3117   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-end",
3118                     G_CALLBACK (shortcuts_drag_end_cb), impl);
3119   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-delete",
3120                     G_CALLBACK (shortcuts_drag_data_delete_cb), impl);
3121
3122   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-leave",
3123                     G_CALLBACK (shortcuts_drag_leave_cb), impl);
3124   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-motion",
3125                     G_CALLBACK (shortcuts_drag_motion_cb), impl);
3126   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-drop",
3127                     G_CALLBACK (shortcuts_drag_drop_cb), impl);
3128   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-received",
3129                     G_CALLBACK (shortcuts_drag_data_received_cb), impl);
3130
3131   gtk_container_add (GTK_CONTAINER (swin), impl->browse_shortcuts_tree_view);
3132   gtk_widget_show (impl->browse_shortcuts_tree_view);
3133
3134   /* Column */
3135
3136   column = gtk_tree_view_column_new ();
3137   gtk_tree_view_column_set_title (column, _("Folder"));
3138
3139   renderer = gtk_cell_renderer_pixbuf_new ();
3140   gtk_tree_view_column_pack_start (column, renderer, FALSE);
3141   gtk_tree_view_column_set_attributes (column, renderer,
3142                                        "pixbuf", SHORTCUTS_COL_PIXBUF,
3143                                        "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
3144                                        NULL);
3145
3146   renderer = gtk_cell_renderer_text_new ();
3147   g_signal_connect (renderer, "edited", 
3148                     G_CALLBACK (shortcuts_edited), impl);
3149   g_signal_connect (renderer, "editing-canceled", 
3150                     G_CALLBACK (shortcuts_editing_canceled), impl);
3151   gtk_tree_view_column_pack_start (column, renderer, TRUE);
3152   gtk_tree_view_column_set_attributes (column, renderer,
3153                                        "text", SHORTCUTS_COL_NAME,
3154                                        NULL);
3155
3156   gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3157                                         shortcuts_row_separator_func,
3158                                         GINT_TO_POINTER (SHORTCUTS_COL_NAME),
3159                                         NULL);
3160
3161   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), column);
3162
3163   return swin;
3164 }
3165
3166 /* Creates the widgets for the shortcuts/bookmarks pane */
3167 static GtkWidget *
3168 shortcuts_pane_create (GtkFileChooserDefault *impl,
3169                        GtkSizeGroup          *size_group)
3170 {
3171   GtkWidget *vbox;
3172   GtkWidget *hbox;
3173   GtkWidget *widget;
3174
3175   vbox = gtk_vbox_new (FALSE, 6);
3176   gtk_widget_show (vbox);
3177
3178   /* Shortcuts tree */
3179
3180   widget = shortcuts_list_create (impl);
3181   gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
3182
3183   /* Box for buttons */
3184
3185   hbox = gtk_hbox_new (TRUE, 6);
3186   gtk_size_group_add_widget (size_group, hbox);
3187   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
3188   gtk_widget_show (hbox);
3189
3190   /* Add bookmark button */
3191
3192   impl->browse_shortcuts_add_button = button_new (impl,
3193                                                   _("_Add"),
3194                                                   GTK_STOCK_ADD,
3195                                                   FALSE,
3196                                                   TRUE,
3197                                                   G_CALLBACK (add_bookmark_button_clicked_cb));
3198   gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_add_button, TRUE, TRUE, 0);
3199   gtk_tooltips_set_tip (impl->tooltips, impl->browse_shortcuts_add_button,
3200                         _("Add the selected folder to the Bookmarks"), NULL);
3201
3202   /* Remove bookmark button */
3203
3204   impl->browse_shortcuts_remove_button = button_new (impl,
3205                                                      _("_Remove"),
3206                                                      GTK_STOCK_REMOVE,
3207                                                      FALSE,
3208                                                      TRUE,
3209                                                      G_CALLBACK (remove_bookmark_button_clicked_cb));
3210   gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_remove_button, TRUE, TRUE, 0);
3211   gtk_tooltips_set_tip (impl->tooltips, impl->browse_shortcuts_remove_button,
3212                         _("Remove the selected bookmark"), NULL);
3213
3214   return vbox;
3215 }
3216
3217 /* Handles key press events on the file list, so that we can trap Enter to
3218  * activate the default button on our own.  Also, checks to see if '/' has been
3219  * pressed.  See comment by tree_view_keybinding_cb() for more details.
3220  */
3221 static gboolean
3222 trap_activate_cb (GtkWidget   *widget,
3223                   GdkEventKey *event,
3224                   gpointer     data)
3225 {
3226   GtkFileChooserDefault *impl;
3227   int modifiers;
3228
3229   impl = (GtkFileChooserDefault *) data;
3230
3231   modifiers = gtk_accelerator_get_default_mod_mask ();
3232   
3233   if (event->keyval == GDK_slash &&
3234       ! (event->state & (~GDK_SHIFT_MASK & modifiers)))
3235     {
3236       location_popup_handler (impl, "/");
3237       return TRUE;
3238     }
3239
3240   if ((event->keyval == GDK_Return
3241        || event->keyval == GDK_ISO_Enter
3242        || event->keyval == GDK_KP_Enter
3243        || event->keyval == GDK_space)
3244       && ((event->state && modifiers) == 0)
3245       && !(impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
3246            impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
3247     {
3248       GtkWindow *window;
3249
3250       window = get_toplevel (widget);
3251       if (window
3252           && widget != window->default_widget
3253           && !(widget == window->focus_widget &&
3254                (!window->default_widget || !GTK_WIDGET_SENSITIVE (window->default_widget))))
3255         {
3256           gtk_window_activate_default (window);
3257           return TRUE;
3258         }
3259     }
3260
3261   return FALSE;
3262 }
3263
3264 /* Callback used when the file list's popup menu is detached */
3265 static void
3266 popup_menu_detach_cb (GtkWidget *attach_widget,
3267                       GtkMenu   *menu)
3268 {
3269   GtkFileChooserDefault *impl;
3270
3271   impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
3272   g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
3273
3274   impl->browse_files_popup_menu = NULL;
3275   impl->browse_files_popup_menu_add_shortcut_item = NULL;
3276   impl->browse_files_popup_menu_hidden_files_item = NULL;
3277 }
3278
3279 /* Callback used when the "Add to Bookmarks" menu item is activated */
3280 static void
3281 add_to_shortcuts_cb (GtkMenuItem           *item,
3282                      GtkFileChooserDefault *impl)
3283 {
3284   bookmarks_add_selected_folder (impl);
3285 }
3286
3287 /* Callback used when the "Open Location" menu item is activated */
3288 static void
3289 open_location_cb (GtkMenuItem           *item,
3290                   GtkFileChooserDefault *impl)
3291 {
3292   location_popup_handler (impl, "");
3293 }
3294
3295 /* Callback used when the "Show Hidden Files" menu item is toggled */
3296 static void
3297 show_hidden_toggled_cb (GtkCheckMenuItem      *item,
3298                         GtkFileChooserDefault *impl)
3299 {
3300   g_object_set (impl,
3301                 "show-hidden", gtk_check_menu_item_get_active (item),
3302                 NULL);
3303 }
3304
3305 /* Constructs the popup menu for the file list if needed */
3306 static void
3307 file_list_build_popup_menu (GtkFileChooserDefault *impl)
3308 {
3309   GtkWidget *item;
3310
3311   if (impl->browse_files_popup_menu)
3312     return;
3313
3314   impl->browse_files_popup_menu = gtk_menu_new ();
3315   gtk_menu_attach_to_widget (GTK_MENU (impl->browse_files_popup_menu),
3316                              impl->browse_files_tree_view,
3317                              popup_menu_detach_cb);
3318
3319   item = gtk_image_menu_item_new_with_mnemonic (_("_Add to Bookmarks"));
3320   impl->browse_files_popup_menu_add_shortcut_item = item;
3321   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
3322                                  gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU));
3323   gtk_widget_set_sensitive (item, FALSE);
3324   g_signal_connect (item, "activate",
3325                     G_CALLBACK (add_to_shortcuts_cb), impl);
3326   gtk_widget_show (item);
3327   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
3328
3329   item = gtk_image_menu_item_new_with_mnemonic (_("Open _Location"));
3330   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
3331                                  gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU));
3332   g_signal_connect (item, "activate",
3333                     G_CALLBACK (open_location_cb), impl);
3334   gtk_widget_show (item);
3335   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
3336
3337   item = gtk_separator_menu_item_new ();
3338   gtk_widget_show (item);
3339   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
3340
3341   item = gtk_check_menu_item_new_with_mnemonic (_("Show _Hidden Files"));
3342   impl->browse_files_popup_menu_hidden_files_item = item;
3343   g_signal_connect (item, "toggled",
3344                     G_CALLBACK (show_hidden_toggled_cb), impl);
3345   gtk_widget_show (item);
3346   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
3347 }
3348
3349 /* Updates the popup menu for the file list, creating it if necessary */
3350 static void
3351 file_list_update_popup_menu (GtkFileChooserDefault *impl)
3352 {
3353   file_list_build_popup_menu (impl);
3354
3355   /* The sensitivity of the Add to Bookmarks item is set in
3356    * bookmarks_check_add_sensitivity()
3357    */
3358
3359   g_signal_handlers_block_by_func (impl->browse_files_popup_menu_hidden_files_item,
3360                                    G_CALLBACK (show_hidden_toggled_cb), impl);
3361   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (impl->browse_files_popup_menu_hidden_files_item),
3362                                   impl->show_hidden);
3363   g_signal_handlers_unblock_by_func (impl->browse_files_popup_menu_hidden_files_item,
3364                                      G_CALLBACK (show_hidden_toggled_cb), impl);
3365 }
3366
3367 static void
3368 popup_position_func (GtkMenu   *menu,
3369                      gint      *x,
3370                      gint      *y,
3371                      gboolean  *push_in,
3372                      gpointer   user_data)
3373 {
3374   GtkWidget *widget = GTK_WIDGET (user_data);
3375   GdkScreen *screen = gtk_widget_get_screen (widget);
3376   GtkRequisition req;
3377   gint monitor_num;
3378   GdkRectangle monitor;
3379
3380   g_return_if_fail (GTK_WIDGET_REALIZED (widget));
3381
3382   gdk_window_get_origin (widget->window, x, y);
3383
3384   gtk_widget_size_request (GTK_WIDGET (menu), &req);
3385
3386   *x += (widget->allocation.width - req.width) / 2;
3387   *y += (widget->allocation.height - req.height) / 2;
3388
3389   monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
3390   gtk_menu_set_monitor (menu, monitor_num);
3391   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
3392
3393   *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
3394   *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
3395
3396   *push_in = FALSE;
3397 }
3398
3399 static void
3400 file_list_popup_menu (GtkFileChooserDefault *impl,
3401                       GdkEventButton        *event)
3402 {
3403   file_list_update_popup_menu (impl);
3404   if (event)
3405     gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
3406                     NULL, NULL, NULL, NULL,
3407                     event->button, event->time);
3408   else
3409     {
3410       gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
3411                       NULL, NULL,
3412                       popup_position_func, impl->browse_files_tree_view,
3413                       0, GDK_CURRENT_TIME);
3414       gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->browse_files_popup_menu),
3415                                    FALSE);
3416     }
3417
3418 }
3419
3420 /* Callback used for the GtkWidget::popup-menu signal of the file list */
3421 static gboolean
3422 list_popup_menu_cb (GtkWidget *widget,
3423                     GtkFileChooserDefault *impl)
3424 {
3425   file_list_popup_menu (impl, NULL);
3426   return TRUE;
3427 }
3428
3429 /* Callback used when a button is pressed on the file list.  We trap button 3 to
3430  * bring up a popup menu.
3431  */
3432 static gboolean
3433 list_button_press_event_cb (GtkWidget             *widget,
3434                             GdkEventButton        *event,
3435                             GtkFileChooserDefault *impl)
3436 {
3437   if (event->button != 3)
3438     return FALSE;
3439
3440   file_list_popup_menu (impl, event);
3441   return TRUE;
3442 }
3443
3444 /* Creates the widgets for the file list */
3445 static GtkWidget *
3446 create_file_list (GtkFileChooserDefault *impl)
3447 {
3448   GtkWidget *swin;
3449   GtkTreeSelection *selection;
3450   GtkTreeViewColumn *column;
3451   GtkCellRenderer *renderer;
3452
3453   /* Scrolled window */
3454
3455   swin = gtk_scrolled_window_new (NULL, NULL);
3456   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
3457                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3458   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
3459                                        GTK_SHADOW_IN);
3460
3461   /* Tree/list view */
3462
3463   impl->browse_files_tree_view = gtk_tree_view_new ();
3464   g_object_set_data (G_OBJECT (impl->browse_files_tree_view), "GtkFileChooserDefault", impl);
3465   atk_object_set_name (gtk_widget_get_accessible (impl->browse_files_tree_view), _("Files"));
3466
3467   gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->browse_files_tree_view), TRUE);
3468   gtk_container_add (GTK_CONTAINER (swin), impl->browse_files_tree_view);
3469   g_signal_connect (impl->browse_files_tree_view, "row-activated",
3470                     G_CALLBACK (list_row_activated), impl);
3471   g_signal_connect (impl->browse_files_tree_view, "key-press-event",
3472                     G_CALLBACK (trap_activate_cb), impl);
3473   g_signal_connect (impl->browse_files_tree_view, "popup-menu",
3474                     G_CALLBACK (list_popup_menu_cb), impl);
3475   g_signal_connect (impl->browse_files_tree_view, "button-press-event",
3476                     G_CALLBACK (list_button_press_event_cb), impl);
3477
3478   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3479   gtk_tree_selection_set_select_function (selection,
3480                                           list_select_func,
3481                                           impl, NULL);
3482   gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_files_tree_view),
3483                                           GDK_BUTTON1_MASK,
3484                                           file_list_source_targets,
3485                                           num_file_list_source_targets,
3486                                           GDK_ACTION_COPY);
3487
3488   g_signal_connect (selection, "changed",
3489                     G_CALLBACK (list_selection_changed), impl);
3490
3491   /* Filename column */
3492
3493   impl->list_name_column = gtk_tree_view_column_new ();
3494   gtk_tree_view_column_set_expand (impl->list_name_column, TRUE);
3495   gtk_tree_view_column_set_resizable (impl->list_name_column, TRUE);
3496   gtk_tree_view_column_set_title (impl->list_name_column, _("Name"));
3497   gtk_tree_view_column_set_sort_column_id (impl->list_name_column, FILE_LIST_COL_NAME);
3498
3499   renderer = gtk_cell_renderer_pixbuf_new ();
3500   gtk_tree_view_column_pack_start (impl->list_name_column, renderer, FALSE);
3501   gtk_tree_view_column_set_cell_data_func (impl->list_name_column, renderer,
3502                                            list_icon_data_func, impl, NULL);
3503
3504   impl->list_name_renderer = gtk_cell_renderer_text_new ();
3505   g_object_set (impl->list_name_renderer,
3506                 "ellipsize", PANGO_ELLIPSIZE_END,
3507                 NULL);
3508   g_signal_connect (impl->list_name_renderer, "edited",
3509                     G_CALLBACK (renderer_edited_cb), impl);
3510   g_signal_connect (impl->list_name_renderer, "editing-canceled",
3511                     G_CALLBACK (renderer_editing_canceled_cb), impl);
3512   gtk_tree_view_column_pack_start (impl->list_name_column, impl->list_name_renderer, TRUE);
3513   gtk_tree_view_column_set_cell_data_func (impl->list_name_column, impl->list_name_renderer,
3514                                            list_name_data_func, impl, NULL);
3515
3516   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), impl->list_name_column);
3517 #if 0
3518   /* Size column */
3519
3520   column = gtk_tree_view_column_new ();
3521   gtk_tree_view_column_set_title (column, _("Size"));
3522
3523   renderer = gtk_cell_renderer_text_new ();
3524   gtk_tree_view_column_pack_start (column, renderer, TRUE);
3525   gtk_tree_view_column_set_cell_data_func (column, renderer,
3526                                            list_size_data_func, impl, NULL);
3527   gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_SIZE);
3528   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
3529 #endif
3530   /* Modification time column */
3531
3532   column = gtk_tree_view_column_new ();
3533   gtk_tree_view_column_set_resizable (column, TRUE);
3534   gtk_tree_view_column_set_title (column, _("Modified"));
3535
3536   renderer = gtk_cell_renderer_text_new ();
3537   gtk_tree_view_column_pack_start (column, renderer, TRUE);
3538   gtk_tree_view_column_set_cell_data_func (column, renderer,
3539                                            list_mtime_data_func, impl, NULL);
3540   gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_MTIME);
3541   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
3542   gtk_widget_show_all (swin);
3543
3544   return swin;
3545 }
3546
3547 static GtkWidget *
3548 create_path_bar (GtkFileChooserDefault *impl)
3549 {
3550   GtkWidget *path_bar;
3551
3552   path_bar = g_object_new (GTK_TYPE_PATH_BAR, NULL);
3553   _gtk_path_bar_set_file_system (GTK_PATH_BAR (path_bar), impl->file_system);
3554
3555   return path_bar;
3556 }
3557
3558 static void
3559 set_filter_tooltip (GtkWidget *widget, 
3560                     gpointer   data)
3561 {
3562   GtkTooltips *tooltips = (GtkTooltips *)data;
3563
3564   if (GTK_IS_BUTTON (widget))
3565     gtk_tooltips_set_tip (tooltips, widget,
3566                           _("Select which types of files are shown"), 
3567                           NULL);
3568 }
3569
3570 static void
3571 realize_filter_combo (GtkWidget *combo,
3572                       gpointer   data)
3573 {
3574   GtkFileChooserDefault *impl = (GtkFileChooserDefault *)data;
3575
3576   gtk_container_forall (GTK_CONTAINER (combo),
3577                         set_filter_tooltip,
3578                         impl->tooltips);
3579 }
3580
3581 /* Creates the widgets for the files/folders pane */
3582 static GtkWidget *
3583 file_pane_create (GtkFileChooserDefault *impl,
3584                   GtkSizeGroup          *size_group)
3585 {
3586   GtkWidget *vbox;
3587   GtkWidget *hbox;
3588   GtkWidget *widget;
3589
3590   vbox = gtk_vbox_new (FALSE, 6);
3591   gtk_widget_show (vbox);
3592
3593   /* The path bar and 'Create Folder' button */
3594   hbox = gtk_hbox_new (FALSE, 12);
3595   gtk_widget_show (hbox);
3596   impl->browse_path_bar = create_path_bar (impl);
3597   g_signal_connect (impl->browse_path_bar, "path-clicked", G_CALLBACK (path_bar_clicked), impl);
3598   gtk_widget_show_all (impl->browse_path_bar);
3599   gtk_box_pack_start (GTK_BOX (hbox), impl->browse_path_bar, TRUE, TRUE, 0);
3600
3601   /* Create Folder */
3602   impl->browse_new_folder_button = gtk_button_new_with_mnemonic (_("Create Fo_lder"));
3603   g_signal_connect (impl->browse_new_folder_button, "clicked",
3604                     G_CALLBACK (new_folder_button_clicked), impl);
3605   gtk_box_pack_end (GTK_BOX (hbox), impl->browse_new_folder_button, FALSE, FALSE, 0);
3606   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
3607
3608   /* Box for lists and preview */
3609
3610   hbox = gtk_hbox_new (FALSE, PREVIEW_HBOX_SPACING);
3611   gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
3612   gtk_widget_show (hbox);
3613
3614   /* File list */
3615
3616   widget = create_file_list (impl);
3617   gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
3618
3619   /* Preview */
3620
3621   impl->preview_box = gtk_vbox_new (FALSE, 12);
3622   gtk_box_pack_start (GTK_BOX (hbox), impl->preview_box, FALSE, FALSE, 0);
3623   /* Don't show preview box initially */
3624
3625   /* Filter combo */
3626
3627   impl->filter_combo_hbox = gtk_hbox_new (FALSE, 12);
3628
3629   widget = filter_create (impl);
3630
3631   g_signal_connect (widget, "realize",
3632                     G_CALLBACK (realize_filter_combo), impl);
3633
3634   gtk_widget_show (widget);
3635   gtk_box_pack_end (GTK_BOX (impl->filter_combo_hbox), widget, FALSE, FALSE, 0);
3636
3637   gtk_size_group_add_widget (size_group, impl->filter_combo_hbox);
3638   gtk_box_pack_end (GTK_BOX (vbox), impl->filter_combo_hbox, FALSE, FALSE, 0);
3639
3640   return vbox;
3641 }
3642 /* Callback used when the "Browse for more folders" expander is toggled */
3643 static void
3644 expander_changed_cb (GtkExpander           *expander,
3645                      GParamSpec            *pspec,
3646                      GtkFileChooserDefault *impl)
3647 {
3648   update_appearance (impl);
3649 }
3650
3651 /* Callback used when the selection changes in the save folder combo box */
3652 static void
3653 save_folder_combo_changed_cb (GtkComboBox           *combo,
3654                               GtkFileChooserDefault *impl)
3655 {
3656   GtkTreeIter iter;
3657
3658   if (impl->changing_folder)
3659     return;
3660
3661   if (gtk_combo_box_get_active_iter (combo, &iter))
3662     shortcuts_activate_iter (impl, &iter);
3663 }
3664
3665 /* Creates the combo box with the save folders */
3666 static GtkWidget *
3667 save_folder_combo_create (GtkFileChooserDefault *impl)
3668 {
3669   GtkWidget *combo;
3670   GtkCellRenderer *cell;
3671
3672   combo = g_object_new (GTK_TYPE_COMBO_BOX,
3673                         "model", impl->shortcuts_model,
3674                         "focus-on-click", FALSE,
3675                         NULL);
3676   gtk_widget_show (combo);
3677
3678   cell = gtk_cell_renderer_pixbuf_new ();
3679   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE);
3680   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
3681                                   "pixbuf", SHORTCUTS_COL_PIXBUF,
3682                                   "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
3683                                   "sensitive", SHORTCUTS_COL_PIXBUF_VISIBLE,
3684                                   NULL);
3685
3686   cell = gtk_cell_renderer_text_new ();
3687   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
3688   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
3689                                   "text", SHORTCUTS_COL_NAME,
3690                                   "sensitive", SHORTCUTS_COL_PIXBUF_VISIBLE,
3691                                   NULL);
3692
3693   gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo),
3694                                         shortcuts_row_separator_func,
3695                                         GINT_TO_POINTER (SHORTCUTS_COL_NAME),
3696                                         NULL);
3697
3698   g_signal_connect (combo, "changed",
3699                     G_CALLBACK (save_folder_combo_changed_cb), impl);
3700
3701   return combo;
3702 }
3703
3704 /* Creates the widgets specific to Save mode */
3705 static GtkWidget *
3706 save_widgets_create (GtkFileChooserDefault *impl)
3707 {
3708   GtkWidget *vbox;
3709   GtkWidget *table;
3710   GtkWidget *widget;
3711   GtkWidget *alignment;
3712
3713   vbox = gtk_vbox_new (FALSE, 12);
3714
3715   table = gtk_table_new (2, 2, FALSE);
3716   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
3717   gtk_widget_show (table);
3718   gtk_table_set_row_spacings (GTK_TABLE (table), 12);
3719   gtk_table_set_col_spacings (GTK_TABLE (table), 12);
3720
3721   /* Name entry */
3722
3723   widget = gtk_label_new_with_mnemonic (_("_Name:"));
3724   gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
3725   gtk_table_attach (GTK_TABLE (table), widget,
3726                     0, 1, 0, 1,
3727                     GTK_FILL, GTK_FILL,
3728                     0, 0);
3729   gtk_widget_show (widget);
3730
3731   impl->save_file_name_entry = _gtk_file_chooser_entry_new (TRUE);
3732   _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry),
3733                                            impl->file_system);
3734   gtk_entry_set_width_chars (GTK_ENTRY (impl->save_file_name_entry), 45);
3735   gtk_entry_set_activates_default (GTK_ENTRY (impl->save_file_name_entry), TRUE);
3736   gtk_table_attach (GTK_TABLE (table), impl->save_file_name_entry,
3737                     1, 2, 0, 1,
3738                     GTK_EXPAND | GTK_FILL, 0,
3739                     0, 0);
3740   gtk_widget_show (impl->save_file_name_entry);
3741   gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->save_file_name_entry);
3742
3743   /* Folder combo */
3744   impl->save_folder_label = gtk_label_new (NULL);
3745   gtk_misc_set_alignment (GTK_MISC (impl->save_folder_label), 0.0, 0.5);
3746   gtk_table_attach (GTK_TABLE (table), impl->save_folder_label,
3747                     0, 1, 1, 2,
3748                     GTK_FILL, GTK_FILL,
3749                     0, 0);
3750   gtk_widget_show (impl->save_folder_label);
3751
3752   impl->save_folder_combo = save_folder_combo_create (impl);
3753   gtk_table_attach (GTK_TABLE (table), impl->save_folder_combo,
3754                     1, 2, 1, 2,
3755                     GTK_EXPAND | GTK_FILL, GTK_FILL,
3756                     0, 0);
3757   gtk_label_set_mnemonic_widget (GTK_LABEL (impl->save_folder_label), impl->save_folder_combo);
3758
3759   /* Expander */
3760   alignment = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
3761   gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
3762
3763   impl->save_expander = gtk_expander_new_with_mnemonic (_("_Browse for other folders"));
3764   gtk_container_add (GTK_CONTAINER (alignment), impl->save_expander);
3765   g_signal_connect (impl->save_expander, "notify::expanded",
3766                     G_CALLBACK (expander_changed_cb),
3767                     impl);
3768   gtk_widget_show_all (alignment);
3769
3770   return vbox;
3771 }
3772
3773 /* Creates the main hpaned with the widgets shared by Open and Save mode */
3774 static GtkWidget *
3775 browse_widgets_create (GtkFileChooserDefault *impl)
3776 {
3777   GtkWidget *vbox;
3778   GtkWidget *hpaned;
3779   GtkWidget *widget;
3780   GtkSizeGroup *size_group;
3781
3782   /* size group is used by the [+][-] buttons and the filter combo */
3783   size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
3784   vbox = gtk_vbox_new (FALSE, 12);
3785
3786   /* Paned widget */
3787   hpaned = gtk_hpaned_new ();
3788   gtk_widget_show (hpaned);
3789   gtk_paned_set_position (GTK_PANED (hpaned), 200); /* FIXME: this sucks */
3790   gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
3791
3792   widget = shortcuts_pane_create (impl, size_group);
3793   gtk_paned_pack1 (GTK_PANED (hpaned), widget, FALSE, FALSE);
3794   widget = file_pane_create (impl, size_group);
3795   gtk_paned_pack2 (GTK_PANED (hpaned), widget, TRUE, FALSE);
3796
3797   g_object_unref (size_group);
3798
3799   return vbox;
3800 }
3801
3802 static GObject*
3803 gtk_file_chooser_default_constructor (GType                  type,
3804                                       guint                  n_construct_properties,
3805                                       GObjectConstructParam *construct_params)
3806 {
3807   GtkFileChooserDefault *impl;
3808   GObject *object;
3809
3810   object = parent_class->constructor (type,
3811                                       n_construct_properties,
3812                                       construct_params);
3813   impl = GTK_FILE_CHOOSER_DEFAULT (object);
3814
3815   g_assert (impl->file_system);
3816
3817   gtk_widget_push_composite_child ();
3818
3819   /* Shortcuts model */
3820
3821   shortcuts_model_create (impl);
3822
3823   /* Widgets for Save mode */
3824   impl->save_widgets = save_widgets_create (impl);
3825   gtk_box_pack_start (GTK_BOX (impl), impl->save_widgets, FALSE, FALSE, 0);
3826
3827   /* The browse widgets */
3828   impl->browse_widgets = browse_widgets_create (impl);
3829   gtk_box_pack_start (GTK_BOX (impl), impl->browse_widgets, TRUE, TRUE, 0);
3830
3831   /* Alignment to hold extra widget */
3832   impl->extra_align = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
3833   gtk_box_pack_start (GTK_BOX (impl), impl->extra_align, FALSE, FALSE, 0);
3834
3835   gtk_widget_pop_composite_child ();
3836   update_appearance (impl);
3837
3838   return object;
3839 }
3840
3841 /* Sets the extra_widget by packing it in the appropriate place */
3842 static void
3843 set_extra_widget (GtkFileChooserDefault *impl,
3844                   GtkWidget             *extra_widget)
3845 {
3846   if (extra_widget)
3847     {
3848       g_object_ref (extra_widget);
3849       /* FIXME: is this right ? */
3850       gtk_widget_show (extra_widget);
3851     }
3852
3853   if (impl->extra_widget)
3854     {
3855       gtk_container_remove (GTK_CONTAINER (impl->extra_align), impl->extra_widget);
3856       g_object_unref (impl->extra_widget);
3857     }
3858
3859   impl->extra_widget = extra_widget;
3860   if (impl->extra_widget)
3861     {
3862       gtk_container_add (GTK_CONTAINER (impl->extra_align), impl->extra_widget);
3863       gtk_widget_show (impl->extra_align);
3864     }
3865   else
3866     gtk_widget_hide (impl->extra_align);
3867 }
3868
3869 static void
3870 set_local_only (GtkFileChooserDefault *impl,
3871                 gboolean               local_only)
3872 {
3873   if (local_only != impl->local_only)
3874     {
3875       impl->local_only = local_only;
3876
3877       if (impl->shortcuts_model && impl->file_system)
3878         {
3879           shortcuts_add_volumes (impl);
3880           shortcuts_add_bookmarks (impl);
3881         }
3882
3883       if (local_only &&
3884           !gtk_file_system_path_is_local (impl->file_system, impl->current_folder))
3885         {
3886           /* If we are pointing to a non-local folder, make an effort to change
3887            * back to a local folder, but it's really up to the app to not cause
3888            * such a situation, so we ignore errors.
3889            */
3890           const gchar *home = g_get_home_dir ();
3891           GtkFilePath *home_path;
3892
3893           if (home == NULL)
3894             return;
3895
3896           home_path = gtk_file_system_filename_to_path (impl->file_system, home);
3897
3898           _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), home_path, NULL);
3899
3900           gtk_file_path_free (home_path);
3901         }
3902     }
3903 }
3904
3905 static void
3906 volumes_changed_cb (GtkFileSystem         *file_system,
3907                     GtkFileChooserDefault *impl)
3908 {
3909   shortcuts_add_volumes (impl);
3910 }
3911
3912 /* Callback used when the set of bookmarks changes in the file system */
3913 static void
3914 bookmarks_changed_cb (GtkFileSystem         *file_system,
3915                       GtkFileChooserDefault *impl)
3916 {
3917   shortcuts_add_bookmarks (impl);
3918
3919   bookmarks_check_add_sensitivity (impl);
3920   bookmarks_check_remove_sensitivity (impl);
3921   shortcuts_check_popup_sensitivity (impl);
3922 }
3923
3924 /* Sets the file chooser to multiple selection mode */
3925 static void
3926 set_select_multiple (GtkFileChooserDefault *impl,
3927                      gboolean               select_multiple,
3928                      gboolean               property_notify)
3929 {
3930   GtkTreeSelection *selection;
3931   GtkSelectionMode mode;
3932
3933   if (select_multiple == impl->select_multiple)
3934     return;
3935
3936   mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE;
3937
3938   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3939   gtk_tree_selection_set_mode (selection, mode);
3940
3941   impl->select_multiple = select_multiple;
3942   g_object_notify (G_OBJECT (impl), "select-multiple");
3943
3944   check_preview_change (impl);
3945 }
3946
3947 static void
3948 set_file_system_backend (GtkFileChooserDefault *impl,
3949                          const char *backend)
3950 {
3951   if (impl->file_system)
3952     {
3953       g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
3954       impl->volumes_changed_id = 0;
3955       g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
3956       impl->bookmarks_changed_id = 0;
3957       g_object_unref (impl->file_system);
3958     }
3959
3960   impl->file_system = NULL;
3961   if (backend)
3962     impl->file_system = _gtk_file_system_create (backend);
3963   else
3964     {
3965       GtkSettings *settings = gtk_settings_get_default ();
3966       gchar *default_backend = NULL;
3967
3968       g_object_get (settings, "gtk-file-chooser-backend", &default_backend, NULL);
3969       if (default_backend)
3970         {
3971           impl->file_system = _gtk_file_system_create (default_backend);
3972           g_free (default_backend);
3973         }
3974     }
3975
3976   if (!impl->file_system)
3977     {
3978 #if defined (G_OS_UNIX)
3979       impl->file_system = gtk_file_system_unix_new ();
3980 #elif defined (G_OS_WIN32)
3981       impl->file_system = gtk_file_system_win32_new ();
3982 #else
3983 #error "No default filesystem implementation on the platform"
3984 #endif
3985     }
3986
3987   if (impl->file_system)
3988     {
3989       impl->volumes_changed_id = g_signal_connect (impl->file_system, "volumes-changed",
3990                                                    G_CALLBACK (volumes_changed_cb),
3991                                                    impl);
3992       impl->bookmarks_changed_id = g_signal_connect (impl->file_system, "bookmarks-changed",
3993                                                      G_CALLBACK (bookmarks_changed_cb),
3994                                                      impl);
3995     }
3996 }
3997
3998 /* This function is basically a do_all function.
3999  *
4000  * It sets the visibility on all the widgets based on the current state, and
4001  * moves the custom_widget if needed.
4002  */
4003 static void
4004 update_appearance (GtkFileChooserDefault *impl)
4005 {
4006   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
4007       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4008     {
4009       const char *text;
4010
4011       gtk_widget_show (impl->save_widgets);
4012
4013       if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
4014         text = _("Save in _folder:");
4015       else
4016         text = _("Create in _folder:");
4017
4018       gtk_label_set_text_with_mnemonic (GTK_LABEL (impl->save_folder_label), text);
4019
4020       if (gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
4021         {
4022           gtk_widget_set_sensitive (impl->save_folder_label, FALSE);
4023           gtk_widget_set_sensitive (impl->save_folder_combo, FALSE);
4024           gtk_widget_show (impl->browse_widgets);
4025         }
4026       else
4027         {
4028           gtk_widget_set_sensitive (impl->save_folder_label, TRUE);
4029           gtk_widget_set_sensitive (impl->save_folder_combo, TRUE);
4030           gtk_widget_hide (impl->browse_widgets);
4031         }
4032
4033       gtk_widget_show (impl->browse_new_folder_button);
4034
4035       if (impl->select_multiple)
4036         {
4037           g_warning ("Save mode cannot be set in conjunction with multiple selection mode.  "
4038                      "Re-setting to single selection mode.");
4039           set_select_multiple (impl, FALSE, TRUE);
4040         }
4041     }
4042   else if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
4043            impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
4044     {
4045       gtk_widget_hide (impl->save_widgets);
4046       gtk_widget_show (impl->browse_widgets);
4047     }
4048
4049   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
4050     gtk_widget_hide (impl->browse_new_folder_button);
4051   else
4052     gtk_widget_show (impl->browse_new_folder_button);
4053
4054   gtk_widget_queue_draw (impl->browse_files_tree_view);
4055
4056   g_signal_emit_by_name (impl, "default-size-changed");
4057 }
4058
4059 static void
4060 gtk_file_chooser_default_set_property (GObject      *object,
4061                                        guint         prop_id,
4062                                        const GValue *value,
4063                                        GParamSpec   *pspec)
4064
4065 {
4066   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
4067
4068   switch (prop_id)
4069     {
4070     case GTK_FILE_CHOOSER_PROP_ACTION:
4071       {
4072         GtkFileChooserAction action = g_value_get_enum (value);
4073
4074         if (action != impl->action)
4075           {
4076             gtk_file_chooser_default_unselect_all (GTK_FILE_CHOOSER (impl));
4077             
4078             if ((action == GTK_FILE_CHOOSER_ACTION_SAVE || action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4079                 && impl->select_multiple)
4080               {
4081                 g_warning ("Tried to change the file chooser action to SAVE or CREATE_FOLDER, but "
4082                            "this is not allowed in multiple selection mode.  Resetting the file chooser "
4083                            "to single selection mode.");
4084                 set_select_multiple (impl, FALSE, TRUE);
4085               }
4086             impl->action = action;
4087             update_appearance (impl);
4088           }
4089         
4090         if (impl->save_file_name_entry)
4091           _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry),
4092                                               action);
4093       }
4094       break;
4095     case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
4096       set_file_system_backend (impl, g_value_get_string (value));
4097       break;
4098     case GTK_FILE_CHOOSER_PROP_FILTER:
4099       set_current_filter (impl, g_value_get_object (value));
4100       break;
4101     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
4102       set_local_only (impl, g_value_get_boolean (value));
4103       break;
4104     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
4105       set_preview_widget (impl, g_value_get_object (value));
4106       break;
4107     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
4108       impl->preview_widget_active = g_value_get_boolean (value);
4109       update_preview_widget_visibility (impl);
4110       break;
4111     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
4112       impl->use_preview_label = g_value_get_boolean (value);
4113       update_preview_widget_visibility (impl);
4114       break;
4115     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
4116       set_extra_widget (impl, g_value_get_object (value));
4117       break;
4118     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
4119       {
4120         gboolean select_multiple = g_value_get_boolean (value);
4121         if ((impl->action == GTK_FILE_CHOOSER_ACTION_SAVE || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4122             && select_multiple)
4123           {
4124             g_warning ("Tried to set the file chooser to multiple selection mode, but this is "
4125                        "not allowed in SAVE or CREATE_FOLDER modes.  Ignoring the change and "
4126                        "leaving the file chooser in single selection mode.");
4127             return;
4128           }
4129
4130         set_select_multiple (impl, select_multiple, FALSE);
4131       }
4132       break;
4133     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
4134       {
4135         gboolean show_hidden = g_value_get_boolean (value);
4136         if (show_hidden != impl->show_hidden)
4137           {
4138             impl->show_hidden = show_hidden;
4139
4140             if (impl->browse_files_model)
4141               _gtk_file_system_model_set_show_hidden (impl->browse_files_model, show_hidden);
4142           }
4143       }
4144       break;
4145     default:
4146       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4147       break;
4148     }
4149 }
4150
4151 static void
4152 gtk_file_chooser_default_get_property (GObject    *object,
4153                                        guint       prop_id,
4154                                        GValue     *value,
4155                                        GParamSpec *pspec)
4156 {
4157   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
4158
4159   switch (prop_id)
4160     {
4161     case GTK_FILE_CHOOSER_PROP_ACTION:
4162       g_value_set_enum (value, impl->action);
4163       break;
4164     case GTK_FILE_CHOOSER_PROP_FILTER:
4165       g_value_set_object (value, impl->current_filter);
4166       break;
4167     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
4168       g_value_set_boolean (value, impl->local_only);
4169       break;
4170     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
4171       g_value_set_object (value, impl->preview_widget);
4172       break;
4173     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
4174       g_value_set_boolean (value, impl->preview_widget_active);
4175       break;
4176     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
4177       g_value_set_boolean (value, impl->use_preview_label);
4178       break;
4179     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
4180       g_value_set_object (value, impl->extra_widget);
4181       break;
4182     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
4183       g_value_set_boolean (value, impl->select_multiple);
4184       break;
4185     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
4186       g_value_set_boolean (value, impl->show_hidden);
4187       break;
4188     default:
4189       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4190       break;
4191     }
4192 }
4193
4194 /* Removes the settings signal handler.  It's safe to call multiple times */
4195 static void
4196 remove_settings_signal (GtkFileChooserDefault *impl,
4197                         GdkScreen             *screen)
4198 {
4199   if (impl->settings_signal_id)
4200     {
4201       GtkSettings *settings;
4202
4203       settings = gtk_settings_get_for_screen (screen);
4204       g_signal_handler_disconnect (settings,
4205                                    impl->settings_signal_id);
4206       impl->settings_signal_id = 0;
4207     }
4208 }
4209
4210 static void
4211 gtk_file_chooser_default_dispose (GObject *object)
4212 {
4213   GtkFileChooserDefault *impl = (GtkFileChooserDefault *) object;
4214
4215   if (impl->extra_widget)
4216     {
4217       g_object_unref (impl->extra_widget);
4218       impl->extra_widget = NULL;
4219     }
4220
4221   remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl)));
4222
4223   G_OBJECT_CLASS (parent_class)->dispose (object);
4224 }
4225
4226 /* We override show-all since we have internal widgets that
4227  * shouldn't be shown when you call show_all(), like the filter
4228  * combo box.
4229  */
4230 static void
4231 gtk_file_chooser_default_show_all (GtkWidget *widget)
4232 {
4233   GtkFileChooserDefault *impl = (GtkFileChooserDefault *) widget;
4234
4235   gtk_widget_show (widget);
4236
4237   if (impl->extra_widget)
4238     gtk_widget_show_all (impl->extra_widget);
4239 }
4240
4241 /* Handler for GtkWindow::set-focus; this is where we save the last-focused
4242  * widget on our toplevel.  See gtk_file_chooser_default_hierarchy_changed()
4243  */
4244 static void
4245 toplevel_set_focus_cb (GtkWindow             *window,
4246                        GtkWidget             *focus,
4247                        GtkFileChooserDefault *impl)
4248 {
4249   impl->toplevel_last_focus_widget = gtk_window_get_focus (window);
4250 }
4251
4252 /* We monitor the focus widget on our toplevel to be able to know which widget
4253  * was last focused at the time our "should_respond" method gets called.
4254  */
4255 static void
4256 gtk_file_chooser_default_hierarchy_changed (GtkWidget *widget,
4257                                             GtkWidget *previous_toplevel)
4258 {
4259   GtkFileChooserDefault *impl;
4260   GtkWidget *toplevel;
4261
4262   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
4263
4264   if (previous_toplevel)
4265     {
4266       g_assert (impl->toplevel_set_focus_id != 0);
4267       g_signal_handler_disconnect (previous_toplevel, impl->toplevel_set_focus_id);
4268       impl->toplevel_set_focus_id = 0;
4269       impl->toplevel_last_focus_widget = NULL;
4270     }
4271   else
4272     g_assert (impl->toplevel_set_focus_id == 0);
4273
4274   toplevel = gtk_widget_get_toplevel (widget);
4275   if (GTK_IS_WINDOW (toplevel))
4276     {
4277       impl->toplevel_set_focus_id = g_signal_connect (toplevel, "set-focus",
4278                                                       G_CALLBACK (toplevel_set_focus_cb), impl);
4279       impl->toplevel_last_focus_widget = gtk_window_get_focus (GTK_WINDOW (toplevel));
4280     }
4281 }
4282
4283 /* Changes the icons wherever it is needed */
4284 static void
4285 change_icon_theme (GtkFileChooserDefault *impl)
4286 {
4287   GtkSettings *settings;
4288   gint width, height;
4289
4290   settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
4291
4292   if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU, &width, &height))
4293     impl->icon_size = MAX (width, height);
4294   else
4295     impl->icon_size = FALLBACK_ICON_SIZE;
4296
4297   shortcuts_reload_icons (impl);
4298   gtk_widget_queue_resize (impl->browse_files_tree_view);
4299 }
4300
4301 /* Callback used when a GtkSettings value changes */
4302 static void
4303 settings_notify_cb (GObject               *object,
4304                     GParamSpec            *pspec,
4305                     GtkFileChooserDefault *impl)
4306 {
4307   const char *name;
4308
4309   name = g_param_spec_get_name (pspec);
4310
4311   if (strcmp (name, "gtk-icon-theme-name") == 0
4312       || strcmp (name, "gtk-icon-sizes") == 0)
4313     change_icon_theme (impl);
4314 }
4315
4316 /* Installs a signal handler for GtkSettings so that we can monitor changes in
4317  * the icon theme.
4318  */
4319 static void
4320 check_icon_theme (GtkFileChooserDefault *impl)
4321 {
4322   GtkSettings *settings;
4323
4324   if (impl->settings_signal_id)
4325     return;
4326
4327   if (gtk_widget_has_screen (GTK_WIDGET (impl)))
4328     {
4329       settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
4330       impl->settings_signal_id = g_signal_connect (settings, "notify",
4331                                                    G_CALLBACK (settings_notify_cb), impl);
4332
4333       change_icon_theme (impl);
4334     }
4335 }
4336
4337 static void
4338 gtk_file_chooser_default_style_set (GtkWidget *widget,
4339                                     GtkStyle  *previous_style)
4340 {
4341   GtkFileChooserDefault *impl;
4342
4343   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
4344
4345   if (GTK_WIDGET_CLASS (parent_class)->style_set)
4346     GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
4347
4348   if (gtk_widget_has_screen (GTK_WIDGET (impl)))
4349     change_icon_theme (impl);
4350
4351   g_signal_emit_by_name (widget, "default-size-changed");
4352 }
4353
4354 static void
4355 gtk_file_chooser_default_screen_changed (GtkWidget *widget,
4356                                          GdkScreen *previous_screen)
4357 {
4358   GtkFileChooserDefault *impl;
4359
4360   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
4361
4362   if (GTK_WIDGET_CLASS (parent_class)->screen_changed)
4363     GTK_WIDGET_CLASS (parent_class)->screen_changed (widget, previous_screen);
4364
4365   remove_settings_signal (impl, previous_screen);
4366   check_icon_theme (impl);
4367
4368   g_signal_emit_by_name (widget, "default-size-changed");
4369 }
4370
4371 static gboolean
4372 get_is_file_filtered (GtkFileChooserDefault *impl,
4373                       const GtkFilePath     *path,
4374                       GtkFileInfo           *file_info)
4375 {
4376   GtkFileFilterInfo filter_info;
4377   GtkFileFilterFlags needed;
4378   gboolean result;
4379
4380   if (!impl->current_filter)
4381     return FALSE;
4382
4383   filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
4384
4385   needed = gtk_file_filter_get_needed (impl->current_filter);
4386
4387   filter_info.display_name = gtk_file_info_get_display_name (file_info);
4388   filter_info.mime_type = gtk_file_info_get_mime_type (file_info);
4389
4390   if (needed & GTK_FILE_FILTER_FILENAME)
4391     {
4392       filter_info.filename = gtk_file_system_path_to_filename (impl->file_system, path);
4393       if (filter_info.filename)
4394         filter_info.contains |= GTK_FILE_FILTER_FILENAME;
4395     }
4396   else
4397     filter_info.filename = NULL;
4398
4399   if (needed & GTK_FILE_FILTER_URI)
4400     {
4401       filter_info.uri = gtk_file_system_path_to_uri (impl->file_system, path);
4402       if (filter_info.uri)
4403         filter_info.contains |= GTK_FILE_FILTER_URI;
4404     }
4405   else
4406     filter_info.uri = NULL;
4407
4408   result = gtk_file_filter_filter (impl->current_filter, &filter_info);
4409
4410   if (filter_info.filename)
4411     g_free ((gchar *)filter_info.filename);
4412   if (filter_info.uri)
4413     g_free ((gchar *)filter_info.uri);
4414
4415   return !result;
4416 }
4417
4418 /* GtkWidget::map method */
4419 static void
4420 gtk_file_chooser_default_map (GtkWidget *widget)
4421 {
4422   GtkFileChooserDefault *impl;
4423
4424   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
4425
4426   GTK_WIDGET_CLASS (parent_class)->map (widget);
4427
4428   if (impl->current_folder)
4429     {
4430       pending_select_paths_store_selection (impl);
4431       change_folder_and_display_error (impl, impl->current_folder);
4432     }
4433
4434   bookmarks_changed_cb (impl->file_system, impl);
4435 }
4436
4437 static gboolean
4438 list_model_filter_func (GtkFileSystemModel *model,
4439                         GtkFilePath        *path,
4440                         const GtkFileInfo  *file_info,
4441                         gpointer            user_data)
4442 {
4443   GtkFileChooserDefault *impl = user_data;
4444
4445   if (!impl->current_filter)
4446     return TRUE;
4447
4448   if (gtk_file_info_get_is_folder (file_info))
4449     return TRUE;
4450
4451   return !get_is_file_filtered (impl, path, (GtkFileInfo *) file_info);
4452 }
4453
4454 static void
4455 install_list_model_filter (GtkFileChooserDefault *impl)
4456 {
4457   GtkFileSystemModelFilter filter;
4458   gpointer data;
4459
4460   g_assert (impl->browse_files_model != NULL);
4461
4462   if (impl->current_filter)
4463     {
4464       filter = list_model_filter_func;
4465       data   = impl;
4466     }
4467   else
4468     {
4469       filter = NULL;
4470       data   = NULL;
4471     }
4472   
4473   _gtk_file_system_model_set_filter (impl->browse_files_model,
4474                                      filter,
4475                                      data);
4476 }
4477
4478 #define COMPARE_DIRECTORIES                                                                                    \
4479   GtkFileChooserDefault *impl = user_data;                                                                     \
4480   const GtkFileInfo *info_a = _gtk_file_system_model_get_info (impl->browse_files_model, a);                           \
4481   const GtkFileInfo *info_b = _gtk_file_system_model_get_info (impl->browse_files_model, b);                           \
4482   gboolean dir_a, dir_b;                                                                                       \
4483                                                                                                                \
4484   if (info_a)                                                                                                  \
4485     dir_a = gtk_file_info_get_is_folder (info_a);                                                              \
4486   else                                                                                                         \
4487     return impl->list_sort_ascending ? -1 : 1;                                                                 \
4488                                                                                                                \
4489   if (info_b)                                                                                                  \
4490     dir_b = gtk_file_info_get_is_folder (info_b);                                                              \
4491   else                                                                                                         \
4492     return impl->list_sort_ascending ? 1 : -1;                                                                 \
4493                                                                                                                \
4494   if (dir_a != dir_b)                                                                                          \
4495     return impl->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */
4496
4497 /* Sort callback for the filename column */
4498 static gint
4499 name_sort_func (GtkTreeModel *model,
4500                 GtkTreeIter  *a,
4501                 GtkTreeIter  *b,
4502                 gpointer      user_data)
4503 {
4504   COMPARE_DIRECTORIES;
4505   else
4506     return strcmp (gtk_file_info_get_display_key (info_a), gtk_file_info_get_display_key (info_b));
4507 }
4508
4509 /* Sort callback for the size column */
4510 static gint
4511 size_sort_func (GtkTreeModel *model,
4512                 GtkTreeIter  *a,
4513                 GtkTreeIter  *b,
4514                 gpointer      user_data)
4515 {
4516   COMPARE_DIRECTORIES;
4517   else
4518     {
4519       gint64 size_a = gtk_file_info_get_size (info_a);
4520       gint64 size_b = gtk_file_info_get_size (info_b);
4521
4522       return size_a > size_b ? -1 : (size_a == size_b ? 0 : 1);
4523     }
4524 }
4525
4526 /* Sort callback for the mtime column */
4527 static gint
4528 mtime_sort_func (GtkTreeModel *model,
4529                  GtkTreeIter  *a,
4530                  GtkTreeIter  *b,
4531                  gpointer      user_data)
4532 {
4533   COMPARE_DIRECTORIES;
4534   else
4535     {
4536       GtkFileTime ta = gtk_file_info_get_modification_time (info_a);
4537       GtkFileTime tb = gtk_file_info_get_modification_time (info_b);
4538
4539       return ta > tb ? -1 : (ta == tb ? 0 : 1);
4540     }
4541 }
4542
4543 /* Callback used when the sort column changes.  We cache the sort order for use
4544  * in name_sort_func().
4545  */
4546 static void
4547 list_sort_column_changed_cb (GtkTreeSortable       *sortable,
4548                              GtkFileChooserDefault *impl)
4549 {
4550   GtkSortType sort_type;
4551
4552   if (gtk_tree_sortable_get_sort_column_id (sortable, NULL, &sort_type))
4553     impl->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
4554 }
4555
4556 static void
4557 set_busy_cursor (GtkFileChooserDefault *impl,
4558                  gboolean               busy)
4559 {
4560   GtkWindow *toplevel;
4561   GdkDisplay *display;
4562   GdkCursor *cursor;
4563
4564   toplevel = get_toplevel (GTK_WIDGET (impl));
4565   if (!toplevel || !GTK_WIDGET_REALIZED (toplevel))
4566     return;
4567
4568   display = gtk_widget_get_display (GTK_WIDGET (toplevel));
4569
4570   if (busy)
4571     cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
4572   else
4573     cursor = NULL;
4574
4575   gdk_window_set_cursor (GTK_WIDGET (toplevel)->window, cursor);
4576   gdk_display_flush (display);
4577
4578   if (cursor)
4579     gdk_cursor_unref (cursor);
4580 }
4581
4582 /* Creates a sort model to wrap the file system model and sets it on the tree view */
4583 static void
4584 load_set_model (GtkFileChooserDefault *impl)
4585 {
4586   g_assert (impl->browse_files_model != NULL);
4587   g_assert (impl->sort_model == NULL);
4588
4589   impl->sort_model = (GtkTreeModelSort *)gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (impl->browse_files_model));
4590   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, name_sort_func, impl, NULL);
4591   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_SIZE, size_sort_func, impl, NULL);
4592   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_MTIME, mtime_sort_func, impl, NULL);
4593   gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->sort_model), NULL, NULL, NULL);
4594   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, GTK_SORT_ASCENDING);
4595   impl->list_sort_ascending = TRUE;
4596
4597   g_signal_connect (impl->sort_model, "sort-column-changed",
4598                     G_CALLBACK (list_sort_column_changed_cb), impl);
4599
4600   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
4601                            GTK_TREE_MODEL (impl->sort_model));
4602   gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->browse_files_tree_view));
4603   gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_files_tree_view),
4604                                    GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
4605 }
4606
4607 /* Timeout callback used when the loading timer expires */
4608 static gboolean
4609 load_timeout_cb (gpointer data)
4610 {
4611   GtkFileChooserDefault *impl;
4612
4613   GDK_THREADS_ENTER ();
4614
4615   impl = GTK_FILE_CHOOSER_DEFAULT (data);
4616   g_assert (impl->load_state == LOAD_PRELOAD);
4617   g_assert (impl->load_timeout_id != 0);
4618   g_assert (impl->browse_files_model != NULL);
4619
4620   impl->load_timeout_id = 0;
4621   impl->load_state = LOAD_LOADING;
4622
4623   load_set_model (impl);
4624
4625   GDK_THREADS_LEAVE ();
4626
4627   return FALSE;
4628 }
4629
4630 /* Sets up a new load timer for the model and switches to the LOAD_LOADING state */
4631 static void
4632 load_setup_timer (GtkFileChooserDefault *impl)
4633 {
4634   g_assert (impl->load_timeout_id == 0);
4635   g_assert (impl->load_state != LOAD_PRELOAD);
4636
4637   impl->load_timeout_id = g_timeout_add (MAX_LOADING_TIME, load_timeout_cb, impl);
4638   impl->load_state = LOAD_PRELOAD;
4639 }
4640
4641 /* Removes the load timeout and switches to the LOAD_FINISHED state */
4642 static void
4643 load_remove_timer (GtkFileChooserDefault *impl)
4644 {
4645   if (impl->load_timeout_id != 0)
4646     {
4647       g_assert (impl->load_state == LOAD_PRELOAD);
4648
4649       g_source_remove (impl->load_timeout_id);
4650       impl->load_timeout_id = 0;
4651       impl->load_state = LOAD_EMPTY;
4652     }
4653   else
4654     g_assert (impl->load_state == LOAD_EMPTY ||
4655               impl->load_state == LOAD_LOADING ||
4656               impl->load_state == LOAD_FINISHED);
4657 }
4658
4659 /* Selects the first row in the file list */
4660 static void
4661 browse_files_select_first_row (GtkFileChooserDefault *impl)
4662 {
4663   GtkTreePath *path;
4664
4665   if (!impl->sort_model)
4666     return;
4667
4668   path = gtk_tree_path_new_from_indices (0, -1);
4669   gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), path, NULL, FALSE);
4670   gtk_tree_path_free (path);
4671 }
4672
4673 struct center_selected_row_closure {
4674   GtkFileChooserDefault *impl;
4675   gboolean already_centered;
4676 };
4677
4678 /* Callback used from gtk_tree_selection_selected_foreach(); centers the
4679  * selected row in the tree view.
4680  */
4681 static void
4682 center_selected_row_foreach_cb (GtkTreeModel      *model,
4683                                 GtkTreePath       *path,
4684                                 GtkTreeIter       *iter,
4685                                 gpointer           data)
4686 {
4687   struct center_selected_row_closure *closure;
4688
4689   closure = data;
4690   if (closure->already_centered)
4691     return;
4692
4693   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (closure->impl->browse_files_tree_view), path, NULL, TRUE, 0.5, 0.0);
4694   closure->already_centered = TRUE;
4695 }
4696
4697 /* Centers the selected row in the tree view */
4698 static void
4699 browse_files_center_selected_row (GtkFileChooserDefault *impl)
4700 {
4701   struct center_selected_row_closure closure;
4702   GtkTreeSelection *selection;
4703
4704   closure.impl = impl;
4705   closure.already_centered = FALSE;
4706
4707   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4708   gtk_tree_selection_selected_foreach (selection, center_selected_row_foreach_cb, &closure);
4709 }
4710
4711 static gboolean
4712 show_and_select_paths (GtkFileChooserDefault *impl,
4713                        const GtkFilePath     *parent_path,
4714                        const GtkFilePath     *only_one_path,
4715                        GSList                *paths,
4716                        GError               **error)
4717 {
4718   GtkFileFolder *folder;
4719   gboolean success;
4720   gboolean have_hidden;
4721   gboolean have_filtered;
4722
4723   if (!only_one_path && !paths)
4724     return TRUE;
4725
4726   folder = gtk_file_system_get_folder (impl->file_system, parent_path, GTK_FILE_INFO_IS_HIDDEN, error);
4727   if (!folder)
4728     return FALSE;
4729
4730   success = FALSE;
4731   have_hidden = FALSE;
4732   have_filtered = FALSE;
4733
4734   if (only_one_path)
4735     {
4736       GtkFileInfo *info;
4737
4738       info = gtk_file_folder_get_info (folder, only_one_path, error);
4739       if (info)
4740         {
4741           success = TRUE;
4742           have_hidden = gtk_file_info_get_is_hidden (info);
4743           have_filtered = get_is_file_filtered (impl, only_one_path, info);
4744           gtk_file_info_free (info);
4745         }
4746     }
4747   else
4748     {
4749       GSList *l;
4750
4751       for (l = paths; l; l = l->next)
4752         {
4753           const GtkFilePath *path;
4754           GtkFileInfo *info;
4755
4756           path = l->data;
4757
4758           /* NULL GError */
4759           info = gtk_file_folder_get_info (folder, path, NULL);
4760           if (info)
4761             {
4762               if (!have_hidden)
4763                 have_hidden = gtk_file_info_get_is_hidden (info);
4764
4765               if (!have_filtered)
4766                 have_filtered = get_is_file_filtered (impl, path, info);
4767
4768               gtk_file_info_free (info);
4769
4770               if (have_hidden && have_filtered)
4771                 break; /* we now have all the information we need */
4772             }
4773         }
4774
4775       success = TRUE;
4776     }
4777
4778   g_object_unref (folder);
4779
4780   if (!success)
4781     return FALSE;
4782
4783   if (have_hidden)
4784     g_object_set (impl, "show-hidden", TRUE, NULL);
4785
4786   if (have_filtered)
4787     set_current_filter (impl, NULL);
4788
4789   if (only_one_path)
4790     _gtk_file_system_model_path_do (impl->browse_files_model, only_one_path, select_func, impl);
4791   else
4792     {
4793       GSList *l;
4794
4795       for (l = paths; l; l = l->next)
4796         {
4797           const GtkFilePath *path;
4798
4799           path = l->data;
4800           _gtk_file_system_model_path_do (impl->browse_files_model, path, select_func, impl);
4801         }
4802     }
4803
4804   return TRUE;
4805 }
4806
4807 /* Processes the pending operation when a folder is finished loading */
4808 static void
4809 pending_select_paths_process (GtkFileChooserDefault *impl)
4810 {
4811   g_assert (impl->load_state == LOAD_FINISHED);
4812   g_assert (impl->browse_files_model != NULL);
4813   g_assert (impl->sort_model != NULL);
4814
4815   if (impl->pending_select_paths)
4816     {
4817       /* NULL GError */
4818       show_and_select_paths (impl, impl->current_folder, NULL, impl->pending_select_paths, NULL);
4819       pending_select_paths_free (impl);
4820       browse_files_center_selected_row (impl);
4821     }
4822   else
4823     {
4824       /* We only select the first row if the chooser is actually mapped ---
4825        * selecting the first row is to help the user when he is interacting with
4826        * the chooser, but sometimes a chooser works not on behalf of the user,
4827        * but rather on behalf of something else like GtkFileChooserButton.  In
4828        * that case, the chooser's selection should be what the caller expects,
4829        * as the user can't see that something else got selected.  See bug #165264.
4830        *
4831        * Also, we don't select the first file if we are not in OPEN mode.  Doing
4832        * so would change the contents of the filename entry for SAVE or
4833        * CREATE_FOLDER, which is undesired; in SELECT_FOLDER, we don't want to
4834        * select a *different* folder from the one into which the user just
4835        * navigated.
4836        */
4837       if (GTK_WIDGET_MAPPED (impl) && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
4838         browse_files_select_first_row (impl);
4839     }
4840
4841   g_assert (impl->pending_select_paths == NULL);
4842 }
4843
4844 /* Callback used when the file system model finishes loading */
4845 static void
4846 browse_files_model_finished_loading_cb (GtkFileSystemModel    *model,
4847                                         GtkFileChooserDefault *impl)
4848 {
4849   if (impl->load_state == LOAD_PRELOAD)
4850     {
4851       load_remove_timer (impl);
4852       load_set_model (impl);
4853     }
4854   else if (impl->load_state == LOAD_LOADING)
4855     {
4856       /* Nothing */
4857     }
4858   else
4859     {
4860       /* We can't g_assert_not_reached(), as something other than us may have
4861        *  initiated a folder reload.  See #165556.
4862        */
4863       return;
4864     }
4865
4866   g_assert (impl->load_timeout_id == 0);
4867
4868   impl->load_state = LOAD_FINISHED;
4869
4870   pending_select_paths_process (impl);
4871   set_busy_cursor (impl, FALSE);
4872 }
4873
4874 /* Gets rid of the old list model and creates a new one for the current folder */
4875 static gboolean
4876 set_list_model (GtkFileChooserDefault *impl,
4877                 GError               **error)
4878 {
4879   g_assert (impl->current_folder != NULL);
4880
4881   load_remove_timer (impl); /* This changes the state to LOAD_EMPTY */
4882
4883   if (impl->browse_files_model)
4884     {
4885       g_object_unref (impl->browse_files_model);
4886       impl->browse_files_model = NULL;
4887     }
4888
4889   if (impl->sort_model)
4890     {
4891       g_object_unref (impl->sort_model);
4892       impl->sort_model = NULL;
4893     }
4894
4895   set_busy_cursor (impl, TRUE);
4896   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
4897
4898   impl->browse_files_model = _gtk_file_system_model_new (impl->file_system,
4899                                                          impl->current_folder, 0,
4900                                                          GTK_FILE_INFO_ALL,
4901                                                          error);
4902   if (!impl->browse_files_model)
4903     {
4904       set_busy_cursor (impl, FALSE);
4905       return FALSE;
4906     }
4907
4908   load_setup_timer (impl); /* This changes the state to LOAD_PRELOAD */
4909
4910   g_signal_connect (impl->browse_files_model, "finished-loading",
4911                     G_CALLBACK (browse_files_model_finished_loading_cb), impl);
4912
4913   _gtk_file_system_model_set_show_hidden (impl->browse_files_model, impl->show_hidden);
4914
4915   install_list_model_filter (impl);
4916
4917   return TRUE;
4918 }
4919
4920 static void
4921 update_chooser_entry (GtkFileChooserDefault *impl)
4922 {
4923   GtkTreeSelection *selection;
4924   const GtkFileInfo *info;
4925   GtkTreeIter iter;
4926   GtkTreeIter child_iter;
4927   gboolean change_entry;
4928
4929   if (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
4930     return;
4931
4932   g_assert (!impl->select_multiple);
4933   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4934
4935   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
4936     {
4937       _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry), "");
4938       return;
4939     }
4940
4941   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
4942                                                   &child_iter,
4943                                                   &iter);
4944
4945   info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
4946
4947   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
4948     change_entry = !gtk_file_info_get_is_folder (info); /* We don't want the name to change when clicking on a folder... */
4949   else
4950     change_entry = TRUE;                                /* ... unless we are in CREATE_FOLDER mode */
4951
4952   if (change_entry)
4953     _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry),
4954                                            gtk_file_info_get_display_name (info));
4955 }
4956
4957 static gboolean
4958 gtk_file_chooser_default_set_current_folder (GtkFileChooser    *chooser,
4959                                              const GtkFilePath *path,
4960                                              GError           **error)
4961 {
4962   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4963   gboolean result;
4964
4965   g_assert (path != NULL);
4966
4967   if (impl->local_only &&
4968       !gtk_file_system_path_is_local (impl->file_system, path))
4969     {
4970       g_set_error (error,
4971                    GTK_FILE_CHOOSER_ERROR,
4972                    GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
4973                    _("Cannot change to folder because it is not local"));
4974
4975       return FALSE;
4976     }
4977
4978   /* Test validity of path here.  */
4979   if (!check_is_folder (impl->file_system, path, error))
4980     return FALSE;
4981
4982   if (!_gtk_path_bar_set_path (GTK_PATH_BAR (impl->browse_path_bar), path, error))
4983     return FALSE;
4984
4985   if (impl->current_folder != path)
4986     {
4987       if (impl->current_folder)
4988         gtk_file_path_free (impl->current_folder);
4989
4990       impl->current_folder = gtk_file_path_copy (path);
4991     }
4992
4993   /* Update the widgets that may trigger a folder change themselves.  */
4994
4995   if (!impl->changing_folder)
4996     {
4997       impl->changing_folder = TRUE;
4998
4999       shortcuts_update_current_folder (impl);
5000
5001       impl->changing_folder = FALSE;
5002     }
5003
5004   /* Set the folder on the save entry */
5005
5006   _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry),
5007                                            impl->current_folder);
5008
5009   /* Create a new list model.  This is slightly evil; we store the result value
5010    * but perform more actions rather than returning immediately even if it
5011    * generates an error.
5012    */
5013   result = set_list_model (impl, error);
5014
5015   /* Refresh controls */
5016
5017   shortcuts_find_current_folder (impl);
5018
5019   g_signal_emit_by_name (impl, "current-folder-changed", 0);
5020
5021   check_preview_change (impl);
5022   bookmarks_check_add_sensitivity (impl);
5023
5024   g_signal_emit_by_name (impl, "selection-changed", 0);
5025
5026   return result;
5027 }
5028
5029 static GtkFilePath *
5030 gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser)
5031 {
5032   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5033
5034   return gtk_file_path_copy (impl->current_folder);
5035 }
5036
5037 static void
5038 gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
5039                                            const gchar    *name)
5040 {
5041   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5042
5043   g_return_if_fail (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5044                     || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
5045
5046   pending_select_paths_free (impl);
5047   _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry), name);
5048 }
5049
5050 static void
5051 select_func (GtkFileSystemModel *model,
5052              GtkTreePath        *path,
5053              GtkTreeIter        *iter,
5054              gpointer            user_data)
5055 {
5056   GtkFileChooserDefault *impl = user_data;
5057   GtkTreeSelection *selection;
5058   GtkTreeIter sorted_iter;
5059
5060   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5061
5062   gtk_tree_model_sort_convert_child_iter_to_iter (impl->sort_model, &sorted_iter, iter);
5063   gtk_tree_selection_select_iter (selection, &sorted_iter);
5064 }
5065
5066 static gboolean
5067 gtk_file_chooser_default_select_path (GtkFileChooser    *chooser,
5068                                       const GtkFilePath *path,
5069                                       GError           **error)
5070 {
5071   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5072   GtkFilePath *parent_path;
5073   gboolean same_path;
5074
5075   if (!gtk_file_system_get_parent (impl->file_system, path, &parent_path, error))
5076     return FALSE;
5077
5078   if (!parent_path)
5079     return _gtk_file_chooser_set_current_folder_path (chooser, path, error);
5080
5081   if (impl->load_state == LOAD_EMPTY)
5082     same_path = FALSE;
5083   else
5084     {
5085       g_assert (impl->current_folder != NULL);
5086
5087       same_path = gtk_file_path_compare (parent_path, impl->current_folder) == 0;
5088     }
5089
5090   if (same_path && impl->load_state == LOAD_FINISHED)
5091     {
5092       gboolean result;
5093
5094       result = show_and_select_paths (impl, parent_path, path, NULL, error);
5095       gtk_file_path_free (parent_path);
5096       return result;
5097     }
5098
5099   pending_select_paths_add (impl, path);
5100
5101   if (!same_path)
5102     {
5103       gboolean result;
5104
5105       result = _gtk_file_chooser_set_current_folder_path (chooser, parent_path, error);
5106       gtk_file_path_free (parent_path);
5107       return result;
5108     }
5109
5110   gtk_file_path_free (parent_path);
5111   return TRUE;
5112 }
5113
5114 static void
5115 unselect_func (GtkFileSystemModel *model,
5116                GtkTreePath        *path,
5117                GtkTreeIter        *iter,
5118                gpointer            user_data)
5119 {
5120   GtkFileChooserDefault *impl = user_data;
5121   GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
5122   GtkTreePath *sorted_path;
5123
5124   sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model,
5125                                                                 path);
5126   gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (tree_view),
5127                                     sorted_path);
5128   gtk_tree_path_free (sorted_path);
5129 }
5130
5131 static void
5132 gtk_file_chooser_default_unselect_path (GtkFileChooser    *chooser,
5133                                         const GtkFilePath *path)
5134 {
5135   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5136
5137   if (!impl->browse_files_model)
5138     return;
5139
5140   _gtk_file_system_model_path_do (impl->browse_files_model, path,
5141                                   unselect_func, impl);
5142 }
5143
5144 static gboolean
5145 maybe_select (GtkTreeModel *model, 
5146               GtkTreePath  *path, 
5147               GtkTreeIter  *iter, 
5148               gpointer     data)
5149 {
5150   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (data);
5151   GtkTreeSelection *selection;
5152   const GtkFileInfo *info;
5153   gboolean is_folder;
5154   
5155   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5156   
5157   info = get_list_file_info (impl, iter);
5158   is_folder = gtk_file_info_get_is_folder (info);
5159
5160   if ((is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) ||
5161       (!is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN))
5162     gtk_tree_selection_select_iter (selection, iter);
5163   else
5164     gtk_tree_selection_unselect_iter (selection, iter);
5165     
5166   return FALSE;
5167 }
5168
5169 static void
5170 gtk_file_chooser_default_select_all (GtkFileChooser *chooser)
5171 {
5172   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5173   if (impl->select_multiple)
5174     gtk_tree_model_foreach (GTK_TREE_MODEL (impl->sort_model), 
5175                             maybe_select, impl);
5176 }
5177
5178 static void
5179 gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser)
5180 {
5181   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5182   GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5183
5184   gtk_tree_selection_unselect_all (selection);
5185   pending_select_paths_free (impl);
5186 }
5187
5188 /* Checks whether the filename entry for the Save modes contains a well-formed filename.
5189  *
5190  * is_well_formed_ret - whether what the user typed passes gkt_file_system_make_path()
5191  *
5192  * is_empty_ret - whether the file entry is totally empty
5193  *
5194  * is_file_part_empty_ret - whether the file part is empty (will be if user types "foobar/", and
5195  *                          the path will be "$cwd/foobar")
5196  */
5197 static void
5198 check_save_entry (GtkFileChooserDefault *impl,
5199                   GtkFilePath          **path_ret,
5200                   gboolean              *is_well_formed_ret,
5201                   gboolean              *is_empty_ret,
5202                   gboolean              *is_file_part_empty_ret)
5203 {
5204   GtkFileChooserEntry *chooser_entry;
5205   const GtkFilePath *current_folder;
5206   const char *file_part;
5207   GtkFilePath *path;
5208   GError *error;
5209
5210   g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5211             || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
5212
5213   chooser_entry = GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry);
5214
5215   if (strlen (gtk_entry_get_text (GTK_ENTRY (chooser_entry))) == 0)
5216     {
5217       *path_ret = NULL;
5218       *is_well_formed_ret = TRUE;
5219       *is_empty_ret = TRUE;
5220       *is_file_part_empty_ret = TRUE;
5221
5222       return;
5223     }
5224
5225   *is_empty_ret = FALSE;
5226
5227   current_folder = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
5228   file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
5229
5230   if (!file_part || file_part[0] == '\0')
5231     {
5232       *path_ret = gtk_file_path_copy (current_folder);
5233       *is_well_formed_ret = TRUE;
5234       *is_file_part_empty_ret = TRUE;
5235
5236       return;
5237     }
5238
5239   *is_file_part_empty_ret = FALSE;
5240
5241   error = NULL;
5242   path = gtk_file_system_make_path (impl->file_system, current_folder, file_part, &error);
5243
5244   if (!path)
5245     {
5246       error_building_filename_dialog (impl, current_folder, file_part, error);
5247       *path_ret = NULL;
5248       *is_well_formed_ret = FALSE;
5249
5250       return;
5251     }
5252
5253   *path_ret = path;
5254   *is_well_formed_ret = TRUE;
5255 }
5256
5257 struct get_paths_closure {
5258   GtkFileChooserDefault *impl;
5259   GSList *result;
5260   GtkFilePath *path_from_entry;
5261 };
5262
5263 static void
5264 get_paths_foreach (GtkTreeModel *model,
5265                    GtkTreePath  *path,
5266                    GtkTreeIter  *iter,
5267                    gpointer      data)
5268 {
5269   struct get_paths_closure *info;
5270   const GtkFilePath *file_path;
5271   GtkFileSystemModel *fs_model;
5272   GtkTreeIter sel_iter;
5273
5274   info = data;
5275   fs_model = info->impl->browse_files_model;
5276   gtk_tree_model_sort_convert_iter_to_child_iter (info->impl->sort_model, &sel_iter, iter);
5277
5278   file_path = _gtk_file_system_model_get_path (fs_model, &sel_iter);
5279   if (!file_path)
5280     return; /* We are on the editable row */
5281
5282   if (!info->path_from_entry
5283       || gtk_file_path_compare (info->path_from_entry, file_path) != 0)
5284     info->result = g_slist_prepend (info->result, gtk_file_path_copy (file_path));
5285 }
5286
5287 static GSList *
5288 gtk_file_chooser_default_get_paths (GtkFileChooser *chooser)
5289 {
5290   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5291   struct get_paths_closure info;
5292
5293   info.impl = impl;
5294   info.result = NULL;
5295   info.path_from_entry = NULL;
5296
5297   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5298       || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5299     {
5300       gboolean is_well_formed, is_empty, is_file_part_empty;
5301
5302       check_save_entry (impl, &info.path_from_entry, &is_well_formed, &is_empty, &is_file_part_empty);
5303
5304       if (!is_well_formed)
5305         return NULL;
5306
5307       if (!is_empty)
5308         {
5309           if (is_file_part_empty && impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
5310             {
5311               gtk_file_path_free (info.path_from_entry);
5312               return NULL;
5313             }
5314         }
5315     }
5316
5317   if (!info.path_from_entry || impl->select_multiple)
5318     {
5319       GtkTreeSelection *selection;
5320
5321       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5322       gtk_tree_selection_selected_foreach (selection, get_paths_foreach, &info);
5323     }
5324
5325   if (info.path_from_entry)
5326     info.result = g_slist_prepend (info.result, info.path_from_entry);
5327
5328   /* If there's no folder selected, and we're in SELECT_FOLDER mode, then we
5329    * fall back to the current directory */
5330   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
5331       info.result == NULL)
5332     {
5333       info.result = g_slist_prepend (info.result, gtk_file_path_copy (impl->current_folder));
5334     }
5335
5336   return g_slist_reverse (info.result);
5337 }
5338
5339 static GtkFilePath *
5340 gtk_file_chooser_default_get_preview_path (GtkFileChooser *chooser)
5341 {
5342   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5343
5344   if (impl->preview_path)
5345     return gtk_file_path_copy (impl->preview_path);
5346   else
5347     return NULL;
5348 }
5349
5350 static GtkFileSystem *
5351 gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser)
5352 {
5353   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5354
5355   return impl->file_system;
5356 }
5357
5358 /* Shows or hides the filter widgets */
5359 static void
5360 show_filters (GtkFileChooserDefault *impl,
5361               gboolean               show)
5362 {
5363   if (show)
5364     gtk_widget_show (impl->filter_combo_hbox);
5365   else
5366     gtk_widget_hide (impl->filter_combo_hbox);
5367 }
5368
5369 static void
5370 gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
5371                                      GtkFileFilter  *filter)
5372 {
5373   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5374   const gchar *name;
5375
5376   if (g_slist_find (impl->filters, filter))
5377     {
5378       g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
5379       return;
5380     }
5381
5382   g_object_ref (filter);
5383   gtk_object_sink (GTK_OBJECT (filter));
5384   impl->filters = g_slist_append (impl->filters, filter);
5385
5386   name = gtk_file_filter_get_name (filter);
5387   if (!name)
5388     name = "Untitled filter";   /* Place-holder, doesn't need to be marked for translation */
5389
5390   gtk_combo_box_append_text (GTK_COMBO_BOX (impl->filter_combo), name);
5391
5392   if (!g_slist_find (impl->filters, impl->current_filter))
5393     set_current_filter (impl, filter);
5394
5395   show_filters (impl, TRUE);
5396 }
5397
5398 static void
5399 gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
5400                                         GtkFileFilter  *filter)
5401 {
5402   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5403   GtkTreeModel *model;
5404   GtkTreeIter iter;
5405   gint filter_index;
5406
5407   filter_index = g_slist_index (impl->filters, filter);
5408
5409   if (filter_index < 0)
5410     {
5411       g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
5412       return;
5413     }
5414
5415   impl->filters = g_slist_remove (impl->filters, filter);
5416
5417   if (filter == impl->current_filter)
5418     {
5419       if (impl->filters)
5420         set_current_filter (impl, impl->filters->data);
5421       else
5422         set_current_filter (impl, NULL);
5423     }
5424
5425   /* Remove row from the combo box */
5426   model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->filter_combo));
5427   gtk_tree_model_iter_nth_child  (model, &iter, NULL, filter_index);
5428   gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
5429
5430   g_object_unref (filter);
5431
5432   if (!impl->filters)
5433     show_filters (impl, FALSE);
5434 }
5435
5436 static GSList *
5437 gtk_file_chooser_default_list_filters (GtkFileChooser *chooser)
5438 {
5439   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5440
5441   return g_slist_copy (impl->filters);
5442 }
5443
5444 /* Returns the position in the shortcuts tree where the nth specified shortcut would appear */
5445 static int
5446 shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl,
5447                                        int                    pos)
5448 {
5449   return pos + shortcuts_get_index (impl, SHORTCUTS_SHORTCUTS);
5450 }
5451
5452 static gboolean
5453 gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser    *chooser,
5454                                               const GtkFilePath *path,
5455                                               GError           **error)
5456 {
5457   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5458   gboolean result;
5459   int pos;
5460
5461   /* Test validity of path here.  */
5462   if (!check_is_folder (impl->file_system, path, error))
5463     return FALSE;
5464
5465   pos = shortcuts_get_pos_for_shortcut_folder (impl, impl->num_shortcuts);
5466
5467   result = shortcuts_insert_path (impl, pos, FALSE, NULL, path, NULL, FALSE, error);
5468
5469   if (result)
5470     impl->num_shortcuts++;
5471
5472   if (impl->shortcuts_filter_model)
5473     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
5474
5475   return result;
5476 }
5477
5478 static gboolean
5479 gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
5480                                                  const GtkFilePath *path,
5481                                                  GError           **error)
5482 {
5483   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5484   int pos;
5485   GtkTreeIter iter;
5486   char *uri;
5487   int i;
5488
5489   if (impl->num_shortcuts == 0)
5490     goto out;
5491
5492   pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
5493   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
5494     g_assert_not_reached ();
5495
5496   for (i = 0; i < impl->num_shortcuts; i++)
5497     {
5498       gpointer col_data;
5499       gboolean is_volume;
5500       GtkFilePath *shortcut;
5501
5502       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
5503                           SHORTCUTS_COL_DATA, &col_data,
5504                           SHORTCUTS_COL_IS_VOLUME, &is_volume,
5505                           -1);
5506       g_assert (col_data != NULL);
5507       g_assert (!is_volume);
5508
5509       shortcut = col_data;
5510       if (gtk_file_path_compare (shortcut, path) == 0)
5511         {
5512           shortcuts_remove_rows (impl, pos + i, 1);
5513           impl->num_shortcuts--;
5514           return TRUE;
5515         }
5516
5517       if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
5518         g_assert_not_reached ();
5519     }
5520
5521  out:
5522
5523   uri = gtk_file_system_path_to_uri (impl->file_system, path);
5524   g_set_error (error,
5525                GTK_FILE_CHOOSER_ERROR,
5526                GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
5527                _("Shortcut %s does not exist"),
5528                uri);
5529   g_free (uri);
5530
5531   return FALSE;
5532 }
5533
5534 static GSList *
5535 gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser)
5536 {
5537   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5538   int pos;
5539   GtkTreeIter iter;
5540   int i;
5541   GSList *list;
5542
5543   if (impl->num_shortcuts == 0)
5544     return NULL;
5545
5546   pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
5547   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
5548     g_assert_not_reached ();
5549
5550   list = NULL;
5551
5552   for (i = 0; i < impl->num_shortcuts; i++)
5553     {
5554       gpointer col_data;
5555       gboolean is_volume;
5556       GtkFilePath *shortcut;
5557
5558       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
5559                           SHORTCUTS_COL_DATA, &col_data,
5560                           SHORTCUTS_COL_IS_VOLUME, &is_volume,
5561                           -1);
5562       g_assert (col_data != NULL);
5563       g_assert (!is_volume);
5564
5565       shortcut = col_data;
5566       list = g_slist_prepend (list, gtk_file_path_copy (shortcut));
5567
5568       if (i != impl->num_shortcuts - 1)
5569         {
5570           if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
5571             g_assert_not_reached ();
5572         }
5573     }
5574
5575   return g_slist_reverse (list);
5576 }
5577
5578 /* Guesses a size based upon font sizes */
5579 static void
5580 find_good_size_from_style (GtkWidget *widget,
5581                            gint      *width,
5582                            gint      *height)
5583 {
5584   GtkFileChooserDefault *impl;
5585   gint default_width, default_height;
5586   int font_size;
5587   GtkRequisition req;
5588   GtkRequisition preview_req;
5589
5590   g_assert (widget->style != NULL);
5591   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5592
5593   font_size = pango_font_description_get_size (widget->style->font_desc);
5594   font_size = PANGO_PIXELS (font_size);
5595
5596   default_width = font_size * NUM_CHARS;
5597   default_height = font_size * NUM_LINES;
5598
5599   /* Use at least the requisition size not including the preview widget */
5600   gtk_widget_size_request (widget, &req);
5601
5602   if (impl->preview_widget_active && impl->preview_widget)
5603     gtk_widget_size_request (impl->preview_box, &preview_req);
5604   else
5605     preview_req.width = 0;
5606
5607   default_width = MAX (default_width, (req.width - (preview_req.width + PREVIEW_HBOX_SPACING)));
5608   default_height = MAX (default_height, req.height);
5609
5610   *width = default_width;
5611   *height = default_height;
5612 }
5613
5614 static void
5615 gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
5616                                            gint                *default_width,
5617                                            gint                *default_height)
5618 {
5619   GtkFileChooserDefault *impl;
5620
5621   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
5622
5623   find_good_size_from_style (GTK_WIDGET (chooser_embed), default_width, default_height);
5624
5625   if (impl->preview_widget_active && impl->preview_widget)
5626     *default_width += impl->preview_box->requisition.width + PREVIEW_HBOX_SPACING;
5627 }
5628
5629 static void
5630 gtk_file_chooser_default_get_resizable_hints (GtkFileChooserEmbed *chooser_embed,
5631                                               gboolean            *resize_horizontally,
5632                                               gboolean            *resize_vertically)
5633 {
5634   GtkFileChooserDefault *impl;
5635
5636   g_return_if_fail (resize_horizontally != NULL);
5637   g_return_if_fail (resize_vertically != NULL);
5638
5639   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
5640
5641   *resize_horizontally = TRUE;
5642   *resize_vertically = TRUE;
5643
5644   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
5645       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5646     {
5647       if (! gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
5648         {
5649           *resize_horizontally = FALSE;
5650           *resize_vertically = FALSE;
5651         }
5652     }
5653 }
5654
5655 struct switch_folder_closure {
5656   GtkFileChooserDefault *impl;
5657   const GtkFilePath *path;
5658   int num_selected;
5659 };
5660
5661 /* Used from gtk_tree_selection_selected_foreach() in switch_to_selected_folder() */
5662 static void
5663 switch_folder_foreach_cb (GtkTreeModel      *model,
5664                           GtkTreePath       *path,
5665                           GtkTreeIter       *iter,
5666                           gpointer           data)
5667 {
5668   struct switch_folder_closure *closure;
5669   GtkTreeIter child_iter;
5670
5671   closure = data;
5672
5673   gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
5674
5675   closure->path = _gtk_file_system_model_get_path (closure->impl->browse_files_model, &child_iter);
5676   closure->num_selected++;
5677 }
5678
5679 /* Changes to the selected folder in the list view */
5680 static void
5681 switch_to_selected_folder (GtkFileChooserDefault *impl)
5682 {
5683   GtkTreeSelection *selection;
5684   struct switch_folder_closure closure;
5685
5686   /* We do this with foreach() rather than get_selected() as we may be in
5687    * multiple selection mode
5688    */
5689
5690   closure.impl = impl;
5691   closure.path = NULL;
5692   closure.num_selected = 0;
5693
5694   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5695   gtk_tree_selection_selected_foreach (selection, switch_folder_foreach_cb, &closure);
5696
5697   g_assert (closure.path && closure.num_selected == 1);
5698
5699   change_folder_and_display_error (impl, closure.path);
5700 }
5701
5702 /* Implementation for GtkFileChooserEmbed::should_respond() */
5703 static gboolean
5704 gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
5705 {
5706   GtkFileChooserDefault *impl;
5707   GtkWidget *toplevel;
5708   GtkWidget *current_focus;
5709
5710   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
5711
5712   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
5713   g_assert (GTK_IS_WINDOW (toplevel));
5714
5715   current_focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
5716
5717   if (current_focus == impl->browse_files_tree_view)
5718     {
5719       /* The following array encodes what we do based on the impl->action and the
5720        * number of files selected.
5721        */
5722       typedef enum {
5723         NOOP,                   /* Do nothing (don't respond) */
5724         RESPOND,                /* Respond immediately */
5725         RESPOND_OR_SWITCH,      /* Respond immediately if the selected item is a file; switch to it if it is a folder */
5726         ALL_FILES,              /* Respond only if everything selected is a file */
5727         ALL_FOLDERS,            /* Respond only if everything selected is a folder */
5728         SAVE_ENTRY,             /* Go to the code for handling the save entry */
5729         NOT_REACHED             /* Sanity check */
5730       } ActionToTake;
5731       static const ActionToTake what_to_do[4][3] = {
5732         /*                                0 selected            1 selected              many selected */
5733         /* ACTION_OPEN */               { NOOP,                 RESPOND_OR_SWITCH,      ALL_FILES   },
5734         /* ACTION_SAVE */               { SAVE_ENTRY,           RESPOND_OR_SWITCH,      NOT_REACHED },
5735         /* ACTION_SELECT_FOLDER */      { RESPOND,              ALL_FOLDERS,            ALL_FOLDERS },
5736         /* ACTION_CREATE_FOLDER */      { SAVE_ENTRY,           ALL_FOLDERS,            NOT_REACHED }
5737       };
5738
5739       int num_selected;
5740       gboolean all_files, all_folders;
5741       int k;
5742       ActionToTake action;
5743
5744     file_list:
5745
5746       g_assert (impl->action >= GTK_FILE_CHOOSER_ACTION_OPEN && impl->action <= GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
5747
5748       selection_check (impl, &num_selected, &all_files, &all_folders);
5749
5750       if (num_selected > 2)
5751         k = 2;
5752       else
5753         k = num_selected;
5754
5755       action = what_to_do [impl->action] [k];
5756
5757       switch (action)
5758         {
5759         case NOOP:
5760           return FALSE;
5761
5762         case RESPOND:
5763           return TRUE;
5764
5765         case RESPOND_OR_SWITCH:
5766           g_assert (num_selected == 1);
5767
5768           if (all_folders)
5769             {
5770               switch_to_selected_folder (impl);
5771               return FALSE;
5772             }
5773           else
5774             return TRUE;
5775
5776         case ALL_FILES:
5777           return all_files;
5778
5779         case ALL_FOLDERS:
5780           return all_folders;
5781
5782         case SAVE_ENTRY:
5783           goto save_entry;
5784
5785         default:
5786           g_assert_not_reached ();
5787         }
5788     }
5789   else if (current_focus == impl->save_file_name_entry)
5790     {
5791       GtkFilePath *path;
5792       gboolean is_well_formed, is_empty, is_file_part_empty;
5793       gboolean is_folder;
5794       gboolean retval;
5795       GtkFileChooserEntry *entry;
5796       GError *error;
5797
5798     save_entry:
5799
5800       g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5801                 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
5802
5803       entry = GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry);
5804       check_save_entry (impl, &path, &is_well_formed, &is_empty, &is_file_part_empty);
5805
5806       if (is_empty || !is_well_formed)
5807         return FALSE;
5808
5809       g_assert (path != NULL);
5810
5811       error = NULL;
5812       is_folder = check_is_folder (impl->file_system, path, &error);
5813       if (is_folder)
5814         {
5815           if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
5816             {
5817               _gtk_file_chooser_entry_set_file_part (entry, "");
5818               change_folder_and_display_error (impl, path);
5819               retval = FALSE;
5820             }
5821           else /* GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER */
5822             {
5823               /* The folder already exists, so we do not need to create it.
5824                * Just respond to terminate the dialog.
5825                */
5826               retval = TRUE;
5827             }
5828         }
5829       else
5830         {
5831           if (impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
5832               && g_error_matches (error, GTK_FILE_SYSTEM_ERROR, GTK_FILE_SYSTEM_ERROR_NOT_FOLDER))
5833             {
5834               /* Oops, the user typed the name of an existing path which is not a folder */
5835               error_creating_folder_over_existing_file_dialog (impl, path, error);
5836               error = NULL; /* as it will be freed below for the general case */
5837               retval = FALSE;
5838             }
5839           else
5840             {
5841               GtkFilePath *parent_path;
5842
5843               /* check that everything up to the last component exists */
5844
5845               parent_path = gtk_file_path_copy (_gtk_file_chooser_entry_get_current_folder (entry));
5846               is_folder = check_is_folder (impl->file_system, parent_path, NULL);
5847               if (is_folder)
5848                 {
5849                   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
5850                     retval = TRUE;
5851                   else /* GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER */
5852                     {
5853                       GError *create_error;
5854
5855                       create_error = NULL;
5856                       if (gtk_file_system_create_folder (impl->file_system, path, &create_error))
5857                         retval = TRUE;
5858                       else
5859                         {
5860                           error_creating_folder_dialog (impl, path, create_error);
5861                           retval = FALSE;
5862                         }
5863                     }
5864                 }
5865               else
5866                 {
5867                   /* This will display an error, which is what we want */
5868                   change_folder_and_display_error (impl, parent_path);
5869                   retval = FALSE;
5870                 }
5871
5872               gtk_file_path_free (parent_path);
5873             }
5874
5875           if (error != NULL)
5876             g_error_free (error);
5877         }
5878
5879       gtk_file_path_free (path);
5880       return retval;
5881     }
5882   else if (impl->toplevel_last_focus_widget == impl->browse_shortcuts_tree_view)
5883     {
5884       /* The focus is on a dialog's action area button, *and* the widget that
5885        * was focused immediately before it is the shortcuts list.  Switch to the
5886        * selected shortcut and tell the caller not to respond.
5887        */
5888       GtkTreeIter iter;
5889
5890       if (shortcuts_get_selected (impl, &iter))
5891         {
5892           shortcuts_activate_iter (impl, &iter);
5893           
5894           gtk_widget_grab_focus (impl->browse_files_tree_view);
5895         }
5896       else
5897         goto file_list;
5898
5899       return FALSE;
5900     }
5901   else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
5902     {
5903       /* The focus is on a dialog's action area button, *and* the widget that
5904        * was focused immediately before it is the file list.  
5905        */
5906       goto file_list;
5907     }
5908   else
5909     /* The focus is on a dialog's action area button or something else */
5910     if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5911         || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5912       goto save_entry;
5913     else
5914       goto file_list; 
5915   
5916   g_assert_not_reached ();
5917   return FALSE;
5918 }
5919
5920 /* Implementation for GtkFileChooserEmbed::initial_focus() */
5921 static void
5922 gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed)
5923 {
5924   GtkFileChooserDefault *impl;
5925   GtkWidget *widget;
5926
5927   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
5928
5929   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
5930       || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5931     widget = impl->browse_files_tree_view;
5932   else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5933            || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5934     widget = impl->save_file_name_entry;
5935   else
5936     {
5937       g_assert_not_reached ();
5938       widget = NULL;
5939     }
5940
5941   gtk_widget_grab_focus (widget);
5942 }
5943
5944 static void
5945 set_current_filter (GtkFileChooserDefault *impl,
5946                     GtkFileFilter         *filter)
5947 {
5948   if (impl->current_filter != filter)
5949     {
5950       int filter_index;
5951
5952       /* NULL filters are allowed to reset to non-filtered status
5953        */
5954       filter_index = g_slist_index (impl->filters, filter);
5955       if (impl->filters && filter && filter_index < 0)
5956         return;
5957
5958       if (impl->current_filter)
5959         g_object_unref (impl->current_filter);
5960       impl->current_filter = filter;
5961       if (impl->current_filter)
5962         {
5963           g_object_ref (impl->current_filter);
5964           gtk_object_sink (GTK_OBJECT (filter));
5965         }
5966
5967       if (impl->filters)
5968         gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
5969                                   filter_index);
5970
5971       if (impl->browse_files_model)
5972         install_list_model_filter (impl);
5973
5974       g_object_notify (G_OBJECT (impl), "filter");
5975     }
5976 }
5977
5978 static void
5979 filter_combo_changed (GtkComboBox           *combo_box,
5980                       GtkFileChooserDefault *impl)
5981 {
5982   gint new_index = gtk_combo_box_get_active (combo_box);
5983   GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
5984
5985   set_current_filter (impl, new_filter);
5986 }
5987
5988 static void
5989 check_preview_change (GtkFileChooserDefault *impl)
5990 {
5991   GtkTreePath *cursor_path;
5992   const GtkFilePath *new_path;
5993   const GtkFileInfo *new_info;
5994
5995   gtk_tree_view_get_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), &cursor_path, NULL);
5996   if (cursor_path && impl->sort_model)
5997     {
5998       GtkTreeIter iter;
5999       GtkTreeIter child_iter;
6000
6001       gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, cursor_path);
6002       gtk_tree_path_free (cursor_path);
6003
6004       gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
6005
6006       new_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
6007       new_info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
6008     }
6009   else
6010     {
6011       new_path = NULL;
6012       new_info = NULL;
6013     }
6014
6015   if (new_path != impl->preview_path &&
6016       !(new_path && impl->preview_path &&
6017         gtk_file_path_compare (new_path, impl->preview_path) == 0))
6018     {
6019       if (impl->preview_path)
6020         {
6021           gtk_file_path_free (impl->preview_path);
6022           g_free (impl->preview_display_name);
6023         }
6024
6025       if (new_path)
6026         {
6027           impl->preview_path = gtk_file_path_copy (new_path);
6028           impl->preview_display_name = g_strdup (gtk_file_info_get_display_name (new_info));
6029         }
6030       else
6031         {
6032           impl->preview_path = NULL;
6033           impl->preview_display_name = NULL;
6034         }
6035
6036       if (impl->use_preview_label && impl->preview_label)
6037         gtk_label_set_text (GTK_LABEL (impl->preview_label), impl->preview_display_name);
6038
6039       g_signal_emit_by_name (impl, "update-preview");
6040     }
6041 }
6042
6043 /* Activates a volume by mounting it if necessary and then switching to its
6044  * base path.
6045  */
6046 static void
6047 shortcuts_activate_volume (GtkFileChooserDefault *impl,
6048                            GtkFileSystemVolume   *volume)
6049 {
6050   GtkFilePath *path;
6051
6052   /* We ref the file chooser since volume_mount() may run a main loop, and the
6053    * user could close the file chooser window in the meantime.
6054    */
6055   g_object_ref (impl);
6056
6057   if (!gtk_file_system_volume_get_is_mounted (impl->file_system, volume))
6058     {
6059       GError *error;
6060       gboolean result;
6061
6062       set_busy_cursor (impl, TRUE);
6063
6064       error = NULL;
6065       result = gtk_file_system_volume_mount (impl->file_system, volume, &error);
6066
6067       if (!result)
6068         {
6069           char *msg;
6070
6071           msg = g_strdup_printf (_("Could not mount %s"),
6072                                  gtk_file_system_volume_get_display_name (impl->file_system, volume));
6073           error_message (impl, msg, error->message);
6074           g_free (msg);
6075           g_error_free (error);
6076         }
6077
6078       set_busy_cursor (impl, FALSE);
6079
6080       if (!result)
6081         goto out;
6082     }
6083
6084   path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
6085   change_folder_and_display_error (impl, path);
6086   gtk_file_path_free (path);
6087
6088  out:
6089
6090   g_object_unref (impl);
6091 }
6092
6093 /* Opens the folder or volume at the specified iter in the shortcuts model */
6094 static void
6095 shortcuts_activate_iter (GtkFileChooserDefault *impl,
6096                          GtkTreeIter           *iter)
6097 {
6098   gpointer col_data;
6099   gboolean is_volume;
6100
6101   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
6102                       SHORTCUTS_COL_DATA, &col_data,
6103                       SHORTCUTS_COL_IS_VOLUME, &is_volume,
6104                       -1);
6105
6106   if (!col_data)
6107     return; /* We are on a separator */
6108
6109   if (is_volume)
6110     {
6111       GtkFileSystemVolume *volume;
6112
6113       volume = col_data;
6114
6115       shortcuts_activate_volume (impl, volume);
6116     }
6117   else
6118     {
6119       const GtkFilePath *file_path;
6120
6121       file_path = col_data;
6122       change_folder_and_display_error (impl, file_path);
6123     }
6124 }
6125
6126 /* Callback used when a row in the shortcuts list is activated */
6127 static void
6128 shortcuts_row_activated_cb (GtkTreeView           *tree_view,
6129                             GtkTreePath           *path,
6130                             GtkTreeViewColumn     *column,
6131                             GtkFileChooserDefault *impl)
6132 {
6133   GtkTreeIter iter;
6134   GtkTreeIter child_iter;
6135
6136   if (!gtk_tree_model_get_iter (impl->shortcuts_filter_model, &iter, path))
6137     return;
6138
6139   gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
6140                                                     &child_iter,
6141                                                     &iter);
6142   shortcuts_activate_iter (impl, &child_iter);
6143
6144   gtk_widget_grab_focus (impl->browse_files_tree_view);
6145 }
6146
6147 /* Handler for GtkWidget::key-press-event on the shortcuts list */
6148 static gboolean
6149 shortcuts_key_press_event_cb (GtkWidget             *widget,
6150                               GdkEventKey           *event,
6151                               GtkFileChooserDefault *impl)
6152 {
6153   guint modifiers;
6154
6155   modifiers = gtk_accelerator_get_default_mod_mask ();
6156
6157   if ((event->keyval == GDK_BackSpace
6158       || event->keyval == GDK_Delete
6159       || event->keyval == GDK_KP_Delete)
6160       && (event->state & modifiers) == 0)
6161     {
6162       remove_selected_bookmarks (impl);
6163       return TRUE;
6164     }
6165
6166   return FALSE;
6167 }
6168
6169 static gboolean
6170 shortcuts_select_func  (GtkTreeSelection  *selection,
6171                         GtkTreeModel      *model,
6172                         GtkTreePath       *path,
6173                         gboolean           path_currently_selected,
6174                         gpointer           data)
6175 {
6176   GtkFileChooserDefault *impl = data;
6177
6178   return (*gtk_tree_path_get_indices (path) != shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR));
6179 }
6180
6181 static gboolean
6182 list_select_func  (GtkTreeSelection  *selection,
6183                    GtkTreeModel      *model,
6184                    GtkTreePath       *path,
6185                    gboolean           path_currently_selected,
6186                    gpointer           data)
6187 {
6188   GtkFileChooserDefault *impl = data;
6189
6190   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
6191       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6192     {
6193       GtkTreeIter iter, child_iter;
6194       const GtkFileInfo *info;
6195
6196       if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
6197         return FALSE;
6198       
6199       gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
6200
6201       info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
6202
6203       if (info && !gtk_file_info_get_is_folder (info))
6204         return FALSE;
6205     }
6206     
6207   return TRUE;
6208 }
6209
6210 static void
6211 list_selection_changed (GtkTreeSelection      *selection,
6212                         GtkFileChooserDefault *impl)
6213 {
6214   /* See if we are in the new folder editable row for Save mode */
6215   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
6216     {
6217       GtkTreeSelection *selection;
6218       GtkTreeIter iter, child_iter;
6219       const GtkFileInfo *info;
6220
6221       g_assert (!impl->select_multiple);
6222       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6223       if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
6224         goto out; /* normal processing */
6225
6226       gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
6227                                                       &child_iter,
6228                                                       &iter);
6229
6230       info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
6231       if (!info)
6232         return; /* We are on the editable row for New Folder */
6233     }
6234
6235  out:
6236
6237   update_chooser_entry (impl);
6238   check_preview_change (impl);
6239   bookmarks_check_add_sensitivity (impl);
6240
6241   g_signal_emit_by_name (impl, "selection-changed", 0);
6242 }
6243
6244 /* Callback used when a row in the file list is activated */
6245 static void
6246 list_row_activated (GtkTreeView           *tree_view,
6247                     GtkTreePath           *path,
6248                     GtkTreeViewColumn     *column,
6249                     GtkFileChooserDefault *impl)
6250 {
6251   GtkTreeIter iter, child_iter;
6252   const GtkFileInfo *info;
6253
6254   if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
6255     return;
6256
6257   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
6258
6259   info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
6260
6261   if (gtk_file_info_get_is_folder (info))
6262     {
6263       const GtkFilePath *file_path;
6264
6265       file_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
6266       change_folder_and_display_error (impl, file_path);
6267
6268       return;
6269     }
6270
6271   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
6272       impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
6273     g_signal_emit_by_name (impl, "file-activated");
6274 }
6275
6276 static void
6277 path_bar_clicked (GtkPathBar            *path_bar,
6278                   GtkFilePath           *file_path,
6279                   gboolean               child_is_hidden,
6280                   GtkFileChooserDefault *impl)
6281 {
6282   if (!change_folder_and_display_error (impl, file_path))
6283     return;
6284
6285   /* Say we have "/foo/bar/[.baz]" and the user clicks on "bar".  We should then
6286    * show hidden files so that ".baz" appears in the file list, as it will still
6287    * be shown in the path bar: "/foo/[bar]/.baz"
6288    */
6289   if (child_is_hidden)
6290     g_object_set (impl, "show-hidden", TRUE, NULL);
6291 }
6292
6293 static const GtkFileInfo *
6294 get_list_file_info (GtkFileChooserDefault *impl,
6295                     GtkTreeIter           *iter)
6296 {
6297   GtkTreeIter child_iter;
6298
6299   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
6300                                                   &child_iter,
6301                                                   iter);
6302
6303   return _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
6304 }
6305
6306 static void
6307 list_icon_data_func (GtkTreeViewColumn *tree_column,
6308                      GtkCellRenderer   *cell,
6309                      GtkTreeModel      *tree_model,
6310                      GtkTreeIter       *iter,
6311                      gpointer           data)
6312 {
6313   GtkFileChooserDefault *impl = data;
6314   GtkTreeIter child_iter;
6315   const GtkFilePath *path;
6316   GdkPixbuf *pixbuf;
6317   const GtkFileInfo *info; 
6318   gboolean sensitive = TRUE;
6319   
6320   info = get_list_file_info (impl, iter);
6321
6322   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
6323                                                   &child_iter,
6324                                                   iter);
6325   path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
6326
6327   if (path)
6328     {
6329       /* FIXME: NULL GError */
6330       pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
6331                                             impl->icon_size, NULL);
6332     }
6333   else
6334     {
6335       /* We are on the editable row */
6336       pixbuf = NULL;
6337     }
6338
6339   if (info && (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
6340                impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
6341     sensitive =  gtk_file_info_get_is_folder (info);    
6342     
6343   g_object_set (cell,
6344                 "pixbuf", pixbuf,
6345                 "sensitive", sensitive,
6346                 NULL);
6347     
6348   if (pixbuf)
6349     g_object_unref (pixbuf);
6350 }
6351
6352 static void
6353 list_name_data_func (GtkTreeViewColumn *tree_column,
6354                      GtkCellRenderer   *cell,
6355                      GtkTreeModel      *tree_model,
6356                      GtkTreeIter       *iter,
6357                      gpointer           data)
6358 {
6359   GtkFileChooserDefault *impl = data;
6360   const GtkFileInfo *info = get_list_file_info (impl, iter);
6361   gboolean sensitive = TRUE;
6362
6363   if (!info)
6364     {
6365       g_object_set (cell,
6366                     "text", _("Type name of new folder"),
6367                     NULL);
6368
6369       return;
6370     }
6371
6372
6373   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
6374          || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6375     {
6376       sensitive = gtk_file_info_get_is_folder (info);
6377     } 
6378     
6379   g_object_set (cell,
6380                 "text", gtk_file_info_get_display_name (info),
6381                 "sensitive", sensitive,
6382                 NULL);
6383 }
6384
6385 #if 0
6386 static void
6387 list_size_data_func (GtkTreeViewColumn *tree_column,
6388                      GtkCellRenderer   *cell,
6389                      GtkTreeModel      *tree_model,
6390                      GtkTreeIter       *iter,
6391                      gpointer           data)
6392 {
6393   GtkFileChooserDefault *impl = data;
6394   const GtkFileInfo *info = get_list_file_info (impl, iter);
6395   gint64 size;
6396   gchar *str;
6397   gboolean sensitive = TRUE;
6398
6399   if (!info || gtk_file_info_get_is_folder (info)) 
6400     {
6401       g_object_set (cell,"sensitive", sensitive, NULL);
6402       return;
6403     }
6404
6405   size = gtk_file_info_get_size (info);
6406
6407   if (size < (gint64)1024)
6408     str = g_strdup_printf (ngettext ("%d byte", "%d bytes", (gint)size), (gint)size);
6409   else if (size < (gint64)1024*1024)
6410     str = g_strdup_printf (_("%.1f K"), size / (1024.));
6411   else if (size < (gint64)1024*1024*1024)
6412     str = g_strdup_printf (_("%.1f M"), size / (1024.*1024.));
6413   else
6414     str = g_strdup_printf (_("%.1f G"), size / (1024.*1024.*1024.));
6415
6416   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
6417       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6418     sensitive = FALSE;
6419
6420   g_object_set (cell,
6421                 "text", str,
6422                 "sensitive", sensitive,
6423                 NULL);
6424
6425   g_free (str);
6426 }
6427 #endif
6428
6429 /* Tree column data callback for the file list; fetches the mtime of a file */
6430 static void
6431 list_mtime_data_func (GtkTreeViewColumn *tree_column,
6432                       GtkCellRenderer   *cell,
6433                       GtkTreeModel      *tree_model,
6434                       GtkTreeIter       *iter,
6435                       gpointer           data)
6436 {
6437   GtkFileChooserDefault *impl;
6438   const GtkFileInfo *info;
6439   GtkFileTime time_mtime, time_now;
6440   GDate mtime, now;
6441   int days_diff;
6442   char buf[256];
6443   gboolean sensitive = TRUE;
6444
6445   impl = data;
6446
6447   info = get_list_file_info (impl, iter);
6448   if (!info)
6449     {
6450       g_object_set (cell,
6451                     "text", "",
6452                     "sensitive", TRUE,
6453                     NULL);
6454       return;
6455     }
6456
6457   time_mtime = gtk_file_info_get_modification_time (info);
6458
6459   if (time_mtime == 0)
6460     strcpy (buf, _("Unknown"));
6461   else
6462     {
6463       g_date_set_time (&mtime, (GTime) time_mtime);
6464
6465       time_now = (GTime ) time (NULL);
6466       g_date_set_time (&now, (GTime) time_now);
6467
6468       days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
6469
6470       if (days_diff == 0)
6471         strcpy (buf, _("Today"));
6472       else if (days_diff == 1)
6473         strcpy (buf, _("Yesterday"));
6474       else
6475         {
6476           char *format;
6477
6478           if (days_diff > 1 && days_diff < 7)
6479             format = "%A"; /* Days from last week */
6480           else
6481             format = "%x"; /* Any other date */
6482
6483           if (g_date_strftime (buf, sizeof (buf), format, &mtime) == 0)
6484             strcpy (buf, _("Unknown"));
6485         }
6486     }
6487
6488   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
6489       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6490     sensitive = gtk_file_info_get_is_folder (info);
6491
6492   g_object_set (cell,
6493                 "text", buf,
6494                 "sensitive", sensitive,
6495                 NULL);
6496 }
6497
6498 GtkWidget *
6499 _gtk_file_chooser_default_new (const char *file_system)
6500 {
6501   return  g_object_new (GTK_TYPE_FILE_CHOOSER_DEFAULT,
6502                         "file-system-backend", file_system,
6503                         NULL);
6504 }
6505
6506 static GtkWidget *
6507 location_entry_create (GtkFileChooserDefault *impl,
6508                        const gchar           *path)
6509 {
6510   GtkWidget *entry;
6511
6512   entry = _gtk_file_chooser_entry_new (TRUE);
6513   /* Pick a good width for the entry */
6514   gtk_entry_set_width_chars (GTK_ENTRY (entry), 30);
6515   gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
6516   _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (entry), impl->file_system);
6517   _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (entry), impl->action);
6518   if (path[0])
6519     {
6520       _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (entry), 
6521                                                gtk_file_path_new_steal ((gchar *)path));
6522       _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (entry), path);
6523     }
6524   else
6525     {
6526       _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (entry), impl->current_folder);
6527       if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
6528           || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
6529         _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (entry), "");
6530       else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6531                || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6532         _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (entry),
6533                                                gtk_entry_get_text (GTK_ENTRY (impl->save_file_name_entry)));
6534       else
6535         g_assert_not_reached ();
6536     }
6537
6538   return GTK_WIDGET (entry);
6539 }
6540
6541 static gboolean
6542 update_from_entry (GtkFileChooserDefault *impl,
6543                    GtkWindow             *parent,
6544                    GtkFileChooserEntry   *chooser_entry)
6545 {
6546   const GtkFilePath *folder_path;
6547   const char *file_part;
6548
6549   folder_path = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
6550   file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
6551
6552   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN && !folder_path)
6553     {
6554       error_message_with_parent (parent,
6555                                  _("Cannot change folder"),
6556                                  _("The folder you specified is an invalid path."));
6557       return FALSE;
6558     }
6559
6560   if (file_part[0] == '\0')
6561     return change_folder_and_display_error (impl, folder_path);
6562   else
6563     {
6564       GtkFileFolder *folder = NULL;
6565       GtkFilePath *subfolder_path = NULL;
6566       GtkFileInfo *info = NULL;
6567       GError *error;
6568       gboolean result;
6569
6570       result = FALSE;
6571
6572       /* If the file part is non-empty, we need to figure out if it refers to a
6573        * folder within folder. We could optimize the case here where the folder
6574        * is already loaded for one of our tree models.
6575        */
6576
6577       error = NULL;
6578       folder = gtk_file_system_get_folder (impl->file_system, folder_path, GTK_FILE_INFO_IS_FOLDER, &error);
6579
6580       if (!folder)
6581         {
6582           error_getting_info_dialog (impl, folder_path, error);
6583           goto out;
6584         }
6585
6586       error = NULL;
6587       subfolder_path = gtk_file_system_make_path (impl->file_system, folder_path, file_part, &error);
6588
6589       if (!subfolder_path)
6590         {
6591           char *msg;
6592           char *uri;
6593
6594           uri = gtk_file_system_path_to_uri (impl->file_system, folder_path);
6595           msg = g_strdup_printf (_("Could not build file name from '%s' and '%s'"),
6596                                  uri, file_part);
6597           error_message (impl, msg, error->message);
6598           g_free (uri);
6599           g_free (msg);
6600           goto out;
6601         }
6602
6603       error = NULL;
6604       info = gtk_file_folder_get_info (folder, subfolder_path, &error);
6605
6606       if (!info)
6607         {
6608           if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6609               || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6610             {
6611               if (!change_folder_and_display_error (impl, folder_path))
6612                 goto out;
6613
6614               gtk_file_chooser_default_set_current_name (GTK_FILE_CHOOSER (impl), file_part);
6615             }
6616           else
6617             error_getting_info_dialog (impl, subfolder_path, error);
6618
6619           goto out;
6620         }
6621
6622       if (gtk_file_info_get_is_folder (info))
6623         result = change_folder_and_display_error (impl, subfolder_path);
6624       else
6625         {
6626           GError *error;
6627
6628           error = NULL;
6629           result = _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (impl), subfolder_path, &error);
6630           if (!result)
6631             error_dialog (impl, _("Could not select item"),
6632                           subfolder_path, error);
6633         }
6634
6635     out:
6636
6637       if (folder)
6638         g_object_unref (folder);
6639
6640       gtk_file_path_free (subfolder_path);
6641
6642       if (info)
6643         gtk_file_info_free (info);
6644
6645       return result;
6646     }
6647
6648   g_assert_not_reached ();
6649 }
6650
6651 static void
6652 location_popup_handler (GtkFileChooserDefault *impl,
6653                         const gchar           *path)
6654 {
6655   GtkWidget *dialog;
6656   GtkWindow *toplevel;
6657   GtkWidget *hbox;
6658   GtkWidget *label;
6659   GtkWidget *entry;
6660   gboolean refocus;
6661   const char *title;
6662   const char *accept_stock;
6663
6664   /* Create dialog */
6665
6666   toplevel = get_toplevel (GTK_WIDGET (impl));
6667
6668   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
6669       || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
6670     {
6671       title = _("Open Location");
6672       accept_stock = GTK_STOCK_OPEN;
6673     }
6674   else
6675     {
6676       g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6677                 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
6678       title = _("Save in Location");
6679       accept_stock = GTK_STOCK_SAVE;
6680     }
6681
6682   dialog = gtk_dialog_new_with_buttons (title,
6683                                         toplevel,
6684                                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
6685                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
6686                                         accept_stock, GTK_RESPONSE_ACCEPT,
6687                                         NULL);
6688   gtk_window_set_default_size (GTK_WINDOW (dialog), 300, -1);
6689   gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
6690   gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2);
6691   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
6692
6693   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
6694                                            GTK_RESPONSE_ACCEPT,
6695                                            GTK_RESPONSE_CANCEL,
6696                                            -1);
6697
6698   hbox = gtk_hbox_new (FALSE, 12);
6699   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0);
6700   gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
6701
6702   label = gtk_label_new_with_mnemonic (_("_Location:"));
6703   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
6704
6705   entry = location_entry_create (impl, path);
6706
6707   gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6708   gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
6709
6710   /* Run */
6711
6712   gtk_widget_show_all (dialog);
6713   /* If the dialog is brought up by typing the first characters
6714    * of a path, unselect the text in the entry, so that you can
6715    * just type on without erasing the initial part.
6716    */
6717   if (path[0])
6718     gtk_editable_select_region (GTK_EDITABLE (entry), -1, -1);
6719
6720   refocus = TRUE;
6721
6722   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
6723     {
6724       if (update_from_entry (impl, GTK_WINDOW (dialog), GTK_FILE_CHOOSER_ENTRY (entry)))
6725         {
6726           if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
6727               || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
6728             {
6729               gtk_widget_grab_focus (impl->browse_files_tree_view);
6730             }
6731           else
6732             {
6733               g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6734                         || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
6735               gtk_widget_grab_focus (impl->save_file_name_entry);
6736             }
6737           refocus = FALSE;
6738         }
6739     }
6740
6741   if (refocus)
6742     {
6743       GtkWindow *toplevel;
6744
6745       toplevel = get_toplevel (GTK_WIDGET (impl));
6746       if (toplevel && toplevel->focus_widget)
6747         gtk_widget_grab_focus (toplevel->focus_widget);
6748     }
6749
6750   gtk_widget_destroy (dialog);
6751 }
6752
6753 /* Handler for the "up-folder" keybinding signal */
6754 static void
6755 up_folder_handler (GtkFileChooserDefault *impl)
6756 {
6757   pending_select_paths_add (impl, impl->current_folder);
6758   _gtk_path_bar_up (GTK_PATH_BAR (impl->browse_path_bar));
6759 }
6760
6761 /* Handler for the "down-folder" keybinding signal */
6762 static void
6763 down_folder_handler (GtkFileChooserDefault *impl)
6764 {
6765   _gtk_path_bar_down (GTK_PATH_BAR (impl->browse_path_bar));
6766 }
6767
6768 /* Handler for the "home-folder" keybinding signal */
6769 static void
6770 home_folder_handler (GtkFileChooserDefault *impl)
6771 {
6772   int pos;
6773   GtkTreeIter iter;
6774
6775   if (!impl->has_home)
6776     return; /* Should we put up an error dialog? */
6777
6778   pos = shortcuts_get_index (impl, SHORTCUTS_HOME);
6779   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
6780     g_assert_not_reached ();
6781
6782   shortcuts_activate_iter (impl, &iter);
6783 }
6784
6785 \f
6786
6787 /* Drag and drop interfaces */
6788
6789 static void
6790 _shortcuts_model_filter_class_init (ShortcutsModelFilterClass *class)
6791 {
6792 }
6793
6794 static void
6795 _shortcuts_model_filter_init (ShortcutsModelFilter *model)
6796 {
6797   model->impl = NULL;
6798 }
6799
6800 /* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */
6801 static gboolean
6802 shortcuts_model_filter_row_draggable (GtkTreeDragSource *drag_source,
6803                                       GtkTreePath       *path)
6804 {
6805   ShortcutsModelFilter *model;
6806   int pos;
6807   int bookmarks_pos;
6808
6809   model = SHORTCUTS_MODEL_FILTER (drag_source);
6810
6811   pos = *gtk_tree_path_get_indices (path);
6812   bookmarks_pos = shortcuts_get_index (model->impl, SHORTCUTS_BOOKMARKS);
6813
6814   return (pos >= bookmarks_pos && pos < bookmarks_pos + model->impl->num_bookmarks);
6815 }
6816
6817 /* GtkTreeDragSource::drag_data_get implementation for the shortcuts filter model */
6818 static gboolean
6819 shortcuts_model_filter_drag_data_get (GtkTreeDragSource *drag_source,
6820                                       GtkTreePath       *path,
6821                                       GtkSelectionData  *selection_data)
6822 {
6823   ShortcutsModelFilter *model;
6824
6825   model = SHORTCUTS_MODEL_FILTER (drag_source);
6826
6827   /* FIXME */
6828
6829   return FALSE;
6830 }
6831
6832 /* Fill the GtkTreeDragSourceIface vtable */
6833 static void
6834 shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface)
6835 {
6836   iface->row_draggable = shortcuts_model_filter_row_draggable;
6837   iface->drag_data_get = shortcuts_model_filter_drag_data_get;
6838 }
6839
6840 #if 0
6841 /* Fill the GtkTreeDragDestIface vtable */
6842 static void
6843 shortcuts_model_filter_drag_dest_iface_init (GtkTreeDragDestIface *iface)
6844 {
6845   iface->drag_data_received = shortcuts_model_filter_drag_data_received;
6846   iface->row_drop_possible = shortcuts_model_filter_row_drop_possible;
6847 }
6848 #endif
6849
6850 static GtkTreeModel *
6851 shortcuts_model_filter_new (GtkFileChooserDefault *impl,
6852                             GtkTreeModel          *child_model,
6853                             GtkTreePath           *root)
6854 {
6855   ShortcutsModelFilter *model;
6856
6857   model = g_object_new (SHORTCUTS_MODEL_FILTER_TYPE,
6858                         "child-model", child_model,
6859                         "virtual-root", root,
6860                         NULL);
6861
6862   model->impl = impl;
6863
6864   return GTK_TREE_MODEL (model);
6865 }
6866
6867 #define __GTK_FILE_CHOOSER_DEFAULT_C__
6868 #include "gtkaliasdef.c"