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