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