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