]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserdefault.c
d52459efe7b218f1943cb2d4095f78de0a4dda69
[~andy/gtk] / gtk / gtkfilechooserdefault.c
1 /* GTK - The GIMP Toolkit
2  * gtkfilechooserdefault.c: Default implementation of GtkFileChooser
3  * Copyright (C) 2003, Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include <config.h>
22 #include "gdk/gdkkeysyms.h"
23 #include "gtkalignment.h"
24 #include "gtkbindings.h"
25 #include "gtkbutton.h"
26 #include "gtkcelllayout.h"
27 #include "gtkcellrendererpixbuf.h"
28 #include "gtkcellrenderertext.h"
29 #include "gtkcellrenderertext.h"
30 #include "gtkcheckmenuitem.h"
31 #include "gtkclipboard.h"
32 #include "gtkcombobox.h"
33 #include "gtkentry.h"
34 #include "gtkeventbox.h"
35 #include "gtkexpander.h"
36 #include "gtkfilechooserprivate.h"
37 #include "gtkfilechooserdefault.h"
38 #include "gtkfilechooserembed.h"
39 #include "gtkfilechooserentry.h"
40 #include "gtkfilechoosersettings.h"
41 #include "gtkfilechooserutils.h"
42 #include "gtkfilechooser.h"
43 #include "gtkfilesystemmodel.h"
44 #include "gtkframe.h"
45 #include "gtkhbox.h"
46 #include "gtkhpaned.h"
47 #include "gtkiconfactory.h"
48 #include "gtkicontheme.h"
49 #include "gtkimage.h"
50 #include "gtkimagemenuitem.h"
51 #include "gtkintl.h"
52 #include "gtklabel.h"
53 #include "gtkmarshalers.h"
54 #include "gtkmenuitem.h"
55 #include "gtkmessagedialog.h"
56 #include "gtkpathbar.h"
57 #include "gtkprivate.h"
58 #include "gtkradiobutton.h"
59 #include "gtkscrolledwindow.h"
60 #include "gtkseparatormenuitem.h"
61 #include "gtksizegroup.h"
62 #include "gtkstock.h"
63 #include "gtktable.h"
64 #include "gtktreednd.h"
65 #include "gtktreeprivate.h"
66 #include "gtktreeselection.h"
67 #include "gtktypebuiltins.h"
68 #include "gtkvbox.h"
69
70 #if defined (G_OS_UNIX)
71 #include "gtkfilesystemunix.h"
72 #elif defined (G_OS_WIN32)
73 #include "gtkfilesystemwin32.h"
74 #endif
75
76 #include "gtkalias.h"
77
78 #include <errno.h>
79 #include <string.h>
80 #include <time.h>
81
82 \f
83
84 #ifdef HAVE_UNISTD_H
85 #include <unistd.h>
86 #endif
87 #ifdef G_OS_WIN32
88 #include <io.h>
89 #endif
90
91 /* Profiling stuff */
92 #undef PROFILE_FILE_CHOOSER
93 #ifdef PROFILE_FILE_CHOOSER
94
95
96 #ifndef F_OK 
97 #define F_OK 0
98 #endif
99
100 #define PROFILE_INDENT 4
101 static int profile_indent;
102
103 static void
104 profile_add_indent (int indent)
105 {
106   profile_indent += indent;
107   if (profile_indent < 0)
108     g_error ("You screwed up your indentation");
109 }
110
111 void
112 _gtk_file_chooser_profile_log (const char *func, int indent, const char *msg1, const char *msg2)
113 {
114   char *str;
115
116   if (indent < 0)
117     profile_add_indent (indent);
118
119   if (profile_indent == 0)
120     str = g_strdup_printf ("MARK: %s %s %s", func ? func : "", msg1 ? msg1 : "", msg2 ? msg2 : "");
121   else
122     str = g_strdup_printf ("MARK: %*c %s %s %s", profile_indent - 1, ' ', func ? func : "", msg1 ? msg1 : "", msg2 ? msg2 : "");
123
124   access (str, F_OK);
125   g_free (str);
126
127   if (indent > 0)
128     profile_add_indent (indent);
129 }
130
131 #define profile_start(x, y) _gtk_file_chooser_profile_log (G_STRFUNC, PROFILE_INDENT, x, y)
132 #define profile_end(x, y) _gtk_file_chooser_profile_log (G_STRFUNC, -PROFILE_INDENT, x, y)
133 #define profile_msg(x, y) _gtk_file_chooser_profile_log (NULL, 0, x, y)
134 #else
135 #define profile_start(x, y)
136 #define profile_end(x, y)
137 #define profile_msg(x, y)
138 #endif
139
140 \f
141
142 typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
143
144 #define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
145 #define GTK_IS_FILE_CHOOSER_DEFAULT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT))
146 #define GTK_FILE_CHOOSER_DEFAULT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
147
148 #define MAX_LOADING_TIME 500
149
150 struct _GtkFileChooserDefaultClass
151 {
152   GtkVBoxClass parent_class;
153 };
154
155 /* Signal IDs */
156 enum {
157   LOCATION_POPUP,
158   LOCATION_POPUP_ON_PASTE,
159   UP_FOLDER,
160   DOWN_FOLDER,
161   HOME_FOLDER,
162   DESKTOP_FOLDER,
163   QUICK_BOOKMARK,
164   LAST_SIGNAL
165 };
166
167 static guint signals[LAST_SIGNAL] = { 0 };
168
169 /* Column numbers for the shortcuts tree.  Keep these in sync with shortcuts_model_create() */
170 enum {
171   SHORTCUTS_COL_PIXBUF,
172   SHORTCUTS_COL_NAME,
173   SHORTCUTS_COL_DATA,
174   SHORTCUTS_COL_IS_VOLUME,
175   SHORTCUTS_COL_REMOVABLE,
176   SHORTCUTS_COL_PIXBUF_VISIBLE,
177   SHORTCUTS_COL_HANDLE,
178   SHORTCUTS_COL_NUM_COLUMNS
179 };
180
181 /* Column numbers for the file list */
182 enum {
183   FILE_LIST_COL_NAME,
184   FILE_LIST_COL_SIZE,
185   FILE_LIST_COL_MTIME,
186   FILE_LIST_COL_NUM_COLUMNS
187 };
188
189 /* Identifiers for target types */
190 enum {
191   GTK_TREE_MODEL_ROW,
192   TEXT_URI_LIST
193 };
194
195 /* Target types for dragging from the shortcuts list */
196 static const GtkTargetEntry shortcuts_source_targets[] = {
197   { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }
198 };
199
200 static const int num_shortcuts_source_targets = (sizeof (shortcuts_source_targets)
201                                                  / sizeof (shortcuts_source_targets[0]));
202
203 /* Target types for dropping into the shortcuts list */
204 static const GtkTargetEntry shortcuts_dest_targets[] = {
205   { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW },
206   { "text/uri-list", 0, TEXT_URI_LIST }
207 };
208
209 static const int num_shortcuts_dest_targets = (sizeof (shortcuts_dest_targets)
210                                                / sizeof (shortcuts_dest_targets[0]));
211
212 /* Target types for DnD from the file list */
213 static const GtkTargetEntry file_list_source_targets[] = {
214   { "text/uri-list", 0, TEXT_URI_LIST }
215 };
216
217 static const int num_file_list_source_targets = (sizeof (file_list_source_targets)
218                                                  / sizeof (file_list_source_targets[0]));
219
220 /* Target types for dropping into the file list */
221 static const GtkTargetEntry file_list_dest_targets[] = {
222   { "text/uri-list", 0, TEXT_URI_LIST }
223 };
224
225 static const int num_file_list_dest_targets = (sizeof (file_list_dest_targets)
226                                                / sizeof (file_list_dest_targets[0]));
227
228
229 /* Interesting places in the shortcuts bar */
230 typedef enum {
231   SHORTCUTS_HOME,
232   SHORTCUTS_DESKTOP,
233   SHORTCUTS_VOLUMES,
234   SHORTCUTS_SHORTCUTS,
235   SHORTCUTS_BOOKMARKS_SEPARATOR,
236   SHORTCUTS_BOOKMARKS,
237   SHORTCUTS_CURRENT_FOLDER_SEPARATOR,
238   SHORTCUTS_CURRENT_FOLDER
239 } ShortcutsIndex;
240
241 /* Icon size for if we can't get it from the theme */
242 #define FALLBACK_ICON_SIZE 16
243
244 #define PREVIEW_HBOX_SPACING 12
245 #define NUM_LINES 40
246 #define NUM_CHARS 60
247
248 static void gtk_file_chooser_default_iface_init       (GtkFileChooserIface        *iface);
249 static void gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface   *iface);
250
251 static GObject* gtk_file_chooser_default_constructor  (GType                  type,
252                                                        guint                  n_construct_properties,
253                                                        GObjectConstructParam *construct_params);
254 static void     gtk_file_chooser_default_finalize     (GObject               *object);
255 static void     gtk_file_chooser_default_set_property (GObject               *object,
256                                                        guint                  prop_id,
257                                                        const GValue          *value,
258                                                        GParamSpec            *pspec);
259 static void     gtk_file_chooser_default_get_property (GObject               *object,
260                                                        guint                  prop_id,
261                                                        GValue                *value,
262                                                        GParamSpec            *pspec);
263 static void     gtk_file_chooser_default_dispose      (GObject               *object);
264 static void     gtk_file_chooser_default_show_all       (GtkWidget             *widget);
265 static void     gtk_file_chooser_default_map            (GtkWidget             *widget);
266 static void     gtk_file_chooser_default_unmap          (GtkWidget             *widget);
267 static void     gtk_file_chooser_default_hierarchy_changed (GtkWidget          *widget,
268                                                             GtkWidget          *previous_toplevel);
269 static void     gtk_file_chooser_default_style_set      (GtkWidget             *widget,
270                                                          GtkStyle              *previous_style);
271 static void     gtk_file_chooser_default_screen_changed (GtkWidget             *widget,
272                                                          GdkScreen             *previous_screen);
273
274 static gboolean       gtk_file_chooser_default_set_current_folder          (GtkFileChooser    *chooser,
275                                                                             const GtkFilePath *path,
276                                                                             GError           **error);
277 static gboolean       gtk_file_chooser_default_update_current_folder       (GtkFileChooser    *chooser,
278                                                                             const GtkFilePath *path,
279                                                                             gboolean           keep_trail,
280                                                                             GError           **error);
281 static GtkFilePath *  gtk_file_chooser_default_get_current_folder          (GtkFileChooser    *chooser);
282 static void           gtk_file_chooser_default_set_current_name            (GtkFileChooser    *chooser,
283                                                                             const gchar       *name);
284 static gboolean       gtk_file_chooser_default_select_path                 (GtkFileChooser    *chooser,
285                                                                             const GtkFilePath *path,
286                                                                             GError           **error);
287 static void           gtk_file_chooser_default_unselect_path               (GtkFileChooser    *chooser,
288                                                                             const GtkFilePath *path);
289 static void           gtk_file_chooser_default_select_all                  (GtkFileChooser    *chooser);
290 static void           gtk_file_chooser_default_unselect_all                (GtkFileChooser    *chooser);
291 static GSList *       gtk_file_chooser_default_get_paths                   (GtkFileChooser    *chooser);
292 static GtkFilePath *  gtk_file_chooser_default_get_preview_path            (GtkFileChooser    *chooser);
293 static GtkFileSystem *gtk_file_chooser_default_get_file_system             (GtkFileChooser    *chooser);
294 static void           gtk_file_chooser_default_add_filter                  (GtkFileChooser    *chooser,
295                                                                             GtkFileFilter     *filter);
296 static void           gtk_file_chooser_default_remove_filter               (GtkFileChooser    *chooser,
297                                                                             GtkFileFilter     *filter);
298 static GSList *       gtk_file_chooser_default_list_filters                (GtkFileChooser    *chooser);
299 static gboolean       gtk_file_chooser_default_add_shortcut_folder    (GtkFileChooser    *chooser,
300                                                                        const GtkFilePath *path,
301                                                                        GError           **error);
302 static gboolean       gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
303                                                                        const GtkFilePath *path,
304                                                                        GError           **error);
305 static GSList *       gtk_file_chooser_default_list_shortcut_folders  (GtkFileChooser    *chooser);
306
307 static void           gtk_file_chooser_default_get_default_size       (GtkFileChooserEmbed *chooser_embed,
308                                                                        gint                *default_width,
309                                                                        gint                *default_height);
310 static void           gtk_file_chooser_default_get_resizable_hints    (GtkFileChooserEmbed *chooser_embed,
311                                                                        gboolean            *resize_horizontally,
312                                                                        gboolean            *resize_vertically);
313 static gboolean       gtk_file_chooser_default_should_respond         (GtkFileChooserEmbed *chooser_embed);
314 static void           gtk_file_chooser_default_initial_focus          (GtkFileChooserEmbed *chooser_embed);
315
316 static void location_popup_handler (GtkFileChooserDefault *impl,
317                                     const gchar           *path);
318 static void location_popup_on_paste_handler (GtkFileChooserDefault *impl);
319 static void up_folder_handler      (GtkFileChooserDefault *impl);
320 static void down_folder_handler    (GtkFileChooserDefault *impl);
321 static void home_folder_handler    (GtkFileChooserDefault *impl);
322 static void desktop_folder_handler (GtkFileChooserDefault *impl);
323 static void quick_bookmark_handler (GtkFileChooserDefault *impl,
324                                     gint                   bookmark_index);
325 static void update_appearance      (GtkFileChooserDefault *impl);
326
327 static void set_current_filter   (GtkFileChooserDefault *impl,
328                                   GtkFileFilter         *filter);
329 static void check_preview_change (GtkFileChooserDefault *impl);
330
331 static void filter_combo_changed       (GtkComboBox           *combo_box,
332                                         GtkFileChooserDefault *impl);
333 static void     shortcuts_row_activated_cb (GtkTreeView           *tree_view,
334                                             GtkTreePath           *path,
335                                             GtkTreeViewColumn     *column,
336                                             GtkFileChooserDefault *impl);
337
338 static gboolean shortcuts_key_press_event_cb (GtkWidget             *widget,
339                                               GdkEventKey           *event,
340                                               GtkFileChooserDefault *impl);
341
342 static gboolean shortcuts_select_func   (GtkTreeSelection      *selection,
343                                          GtkTreeModel          *model,
344                                          GtkTreePath           *path,
345                                          gboolean               path_currently_selected,
346                                          gpointer               data);
347 static gboolean shortcuts_get_selected  (GtkFileChooserDefault *impl,
348                                          GtkTreeIter           *iter);
349 static void shortcuts_activate_iter (GtkFileChooserDefault *impl,
350                                      GtkTreeIter           *iter);
351 static int shortcuts_get_index (GtkFileChooserDefault *impl,
352                                 ShortcutsIndex         where);
353 static int shortcut_find_position (GtkFileChooserDefault *impl,
354                                    const GtkFilePath     *path);
355
356 static void bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl);
357
358 static gboolean list_select_func   (GtkTreeSelection      *selection,
359                                     GtkTreeModel          *model,
360                                     GtkTreePath           *path,
361                                     gboolean               path_currently_selected,
362                                     gpointer               data);
363
364 static void list_selection_changed     (GtkTreeSelection      *tree_selection,
365                                         GtkFileChooserDefault *impl);
366 static void list_row_activated         (GtkTreeView           *tree_view,
367                                         GtkTreePath           *path,
368                                         GtkTreeViewColumn     *column,
369                                         GtkFileChooserDefault *impl);
370
371 static void select_func (GtkFileSystemModel *model,
372                          GtkTreePath        *path,
373                          GtkTreeIter        *iter,
374                          gpointer            user_data);
375
376 static void path_bar_clicked           (GtkPathBar            *path_bar,
377                                         GtkFilePath           *file_path,
378                                         GtkFilePath           *child_path,
379                                         gboolean               child_is_hidden,
380                                         GtkFileChooserDefault *impl);
381
382 static void add_bookmark_button_clicked_cb    (GtkButton             *button,
383                                                GtkFileChooserDefault *impl);
384 static void remove_bookmark_button_clicked_cb (GtkButton             *button,
385                                                GtkFileChooserDefault *impl);
386 static void save_folder_combo_changed_cb      (GtkComboBox           *combo,
387                                                GtkFileChooserDefault *impl);
388
389 static void list_icon_data_func (GtkTreeViewColumn *tree_column,
390                                  GtkCellRenderer   *cell,
391                                  GtkTreeModel      *tree_model,
392                                  GtkTreeIter       *iter,
393                                  gpointer           data);
394 static void list_name_data_func (GtkTreeViewColumn *tree_column,
395                                  GtkCellRenderer   *cell,
396                                  GtkTreeModel      *tree_model,
397                                  GtkTreeIter       *iter,
398                                  gpointer           data);
399 #if 0
400 static void list_size_data_func (GtkTreeViewColumn *tree_column,
401                                  GtkCellRenderer   *cell,
402                                  GtkTreeModel      *tree_model,
403                                  GtkTreeIter       *iter,
404                                  gpointer           data);
405 #endif
406 static void list_mtime_data_func (GtkTreeViewColumn *tree_column,
407                                   GtkCellRenderer   *cell,
408                                   GtkTreeModel      *tree_model,
409                                   GtkTreeIter       *iter,
410                                   gpointer           data);
411
412 static const GtkFileInfo *get_list_file_info (GtkFileChooserDefault *impl,
413                                               GtkTreeIter           *iter);
414
415 static void load_remove_timer (GtkFileChooserDefault *impl);
416 static void browse_files_center_selected_row (GtkFileChooserDefault *impl);
417
418 static void location_button_toggled_cb (GtkToggleButton *toggle,
419                                         GtkFileChooserDefault *impl);
420 static void location_switch_to_path_bar (GtkFileChooserDefault *impl);
421
422
423 \f
424
425 /* Drag and drop interface declarations */
426
427 typedef struct {
428   GtkTreeModelFilter parent;
429
430   GtkFileChooserDefault *impl;
431 } ShortcutsModelFilter;
432
433 typedef struct {
434   GtkTreeModelFilterClass parent_class;
435 } ShortcutsModelFilterClass;
436
437 #define SHORTCUTS_MODEL_FILTER_TYPE (_shortcuts_model_filter_get_type ())
438 #define SHORTCUTS_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_MODEL_FILTER_TYPE, ShortcutsModelFilter))
439
440 static void shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface);
441
442 G_DEFINE_TYPE_WITH_CODE (ShortcutsModelFilter,
443                          _shortcuts_model_filter,
444                          GTK_TYPE_TREE_MODEL_FILTER,
445                          G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
446                                                 shortcuts_model_filter_drag_source_iface_init))
447
448 static GtkTreeModel *shortcuts_model_filter_new (GtkFileChooserDefault *impl,
449                                                  GtkTreeModel          *child_model,
450                                                  GtkTreePath           *root);
451
452 \f
453
454 G_DEFINE_TYPE_WITH_CODE (GtkFileChooserDefault, _gtk_file_chooser_default, GTK_TYPE_VBOX,
455                          G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER,
456                                                 gtk_file_chooser_default_iface_init)
457                          G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER_EMBED,
458                                                 gtk_file_chooser_embed_default_iface_init));                                            
459
460 static void
461 _gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
462 {
463   static const guint quick_bookmark_keyvals[10] = {
464     GDK_1, GDK_2, GDK_3, GDK_4, GDK_5, GDK_6, GDK_7, GDK_8, GDK_9, GDK_0
465   };
466   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
467   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
468   GtkBindingSet *binding_set;
469   int i;
470
471   gobject_class->finalize = gtk_file_chooser_default_finalize;
472   gobject_class->constructor = gtk_file_chooser_default_constructor;
473   gobject_class->set_property = gtk_file_chooser_default_set_property;
474   gobject_class->get_property = gtk_file_chooser_default_get_property;
475   gobject_class->dispose = gtk_file_chooser_default_dispose;
476
477   widget_class->show_all = gtk_file_chooser_default_show_all;
478   widget_class->map = gtk_file_chooser_default_map;
479   widget_class->unmap = gtk_file_chooser_default_unmap;
480   widget_class->hierarchy_changed = gtk_file_chooser_default_hierarchy_changed;
481   widget_class->style_set = gtk_file_chooser_default_style_set;
482   widget_class->screen_changed = gtk_file_chooser_default_screen_changed;
483
484   signals[LOCATION_POPUP] =
485     _gtk_binding_signal_new (I_("location-popup"),
486                              G_OBJECT_CLASS_TYPE (class),
487                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
488                              G_CALLBACK (location_popup_handler),
489                              NULL, NULL,
490                              _gtk_marshal_VOID__STRING,
491                              G_TYPE_NONE, 1, G_TYPE_STRING);
492   signals[LOCATION_POPUP_ON_PASTE] =
493     _gtk_binding_signal_new ("location-popup-on-paste",
494                              G_OBJECT_CLASS_TYPE (class),
495                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
496                              G_CALLBACK (location_popup_on_paste_handler),
497                              NULL, NULL,
498                              _gtk_marshal_VOID__VOID,
499                              G_TYPE_NONE, 0);
500   signals[UP_FOLDER] =
501     _gtk_binding_signal_new (I_("up-folder"),
502                              G_OBJECT_CLASS_TYPE (class),
503                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
504                              G_CALLBACK (up_folder_handler),
505                              NULL, NULL,
506                              _gtk_marshal_VOID__VOID,
507                              G_TYPE_NONE, 0);
508   signals[DOWN_FOLDER] =
509     _gtk_binding_signal_new (I_("down-folder"),
510                              G_OBJECT_CLASS_TYPE (class),
511                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
512                              G_CALLBACK (down_folder_handler),
513                              NULL, NULL,
514                              _gtk_marshal_VOID__VOID,
515                              G_TYPE_NONE, 0);
516   signals[HOME_FOLDER] =
517     _gtk_binding_signal_new (I_("home-folder"),
518                              G_OBJECT_CLASS_TYPE (class),
519                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
520                              G_CALLBACK (home_folder_handler),
521                              NULL, NULL,
522                              _gtk_marshal_VOID__VOID,
523                              G_TYPE_NONE, 0);
524   signals[DESKTOP_FOLDER] =
525     _gtk_binding_signal_new (I_("desktop-folder"),
526                              G_OBJECT_CLASS_TYPE (class),
527                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
528                              G_CALLBACK (desktop_folder_handler),
529                              NULL, NULL,
530                              _gtk_marshal_VOID__VOID,
531                              G_TYPE_NONE, 0);
532   signals[QUICK_BOOKMARK] =
533     _gtk_binding_signal_new (I_("quick-bookmark"),
534                              G_OBJECT_CLASS_TYPE (class),
535                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
536                              G_CALLBACK (quick_bookmark_handler),
537                              NULL, NULL,
538                              _gtk_marshal_VOID__INT,
539                              G_TYPE_NONE, 1, G_TYPE_INT);
540
541   binding_set = gtk_binding_set_by_class (class);
542
543   gtk_binding_entry_add_signal (binding_set,
544                                 GDK_l, GDK_CONTROL_MASK,
545                                 "location-popup",
546                                 1, G_TYPE_STRING, "");
547   /* FMQ: remove this? */
548 #if 0
549   gtk_binding_entry_add_signal (binding_set,
550                                 GDK_slash, 0,
551                                 "location-popup",
552                                 1, G_TYPE_STRING, "/");
553   gtk_binding_entry_add_signal (binding_set,
554                                 GDK_KP_Divide, 0,
555                                 "location-popup",
556                                 1, G_TYPE_STRING, "/");
557
558 #ifdef G_OS_UNIX
559   gtk_binding_entry_add_signal (binding_set,
560                                 GDK_asciitilde, 0,
561                                 "location-popup",
562                                 1, G_TYPE_STRING, "~");
563 #endif
564 #endif
565
566   gtk_binding_entry_add_signal (binding_set,
567                                 GDK_v, GDK_CONTROL_MASK,
568                                 "location-popup-on-paste",
569                                 0);
570
571   gtk_binding_entry_add_signal (binding_set,
572                                 GDK_Up, GDK_MOD1_MASK,
573                                 "up-folder",
574                                 0);
575   gtk_binding_entry_add_signal (binding_set,
576                                 GDK_BackSpace, 0,
577                                 "up-folder",
578                                 0);
579   gtk_binding_entry_add_signal (binding_set,
580                                 GDK_KP_Up, GDK_MOD1_MASK,
581                                 "up-folder",
582                                 0);
583
584   gtk_binding_entry_add_signal (binding_set,
585                                 GDK_Down, GDK_MOD1_MASK,
586                                 "down-folder",
587                                 0);
588   gtk_binding_entry_add_signal (binding_set,
589                                 GDK_KP_Down, GDK_MOD1_MASK,
590                                 "down-folder",
591                                 0);
592
593   gtk_binding_entry_add_signal (binding_set,
594                                 GDK_Home, GDK_MOD1_MASK,
595                                 "home-folder",
596                                 0);
597   gtk_binding_entry_add_signal (binding_set,
598                                 GDK_KP_Home, GDK_MOD1_MASK,
599                                 "home-folder",
600                                 0);
601   gtk_binding_entry_add_signal (binding_set,
602                                 GDK_d, GDK_MOD1_MASK,
603                                 "desktop-folder",
604                                 0);
605
606   for (i = 0; i < 10; i++)
607     gtk_binding_entry_add_signal (binding_set,
608                                   quick_bookmark_keyvals[i], GDK_MOD1_MASK,
609                                   "quick-bookmark",
610                                   1, G_TYPE_INT, i);
611
612   _gtk_file_chooser_install_properties (gobject_class);
613
614   gtk_settings_install_property (g_param_spec_string ("gtk-file-chooser-backend",
615                                                       P_("Default file chooser backend"),
616                                                       P_("Name of the GtkFileChooser backend to use by default"),
617                                                       NULL,
618                                                       GTK_PARAM_READWRITE));
619 }
620
621 static void
622 gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
623 {
624   iface->select_path = gtk_file_chooser_default_select_path;
625   iface->unselect_path = gtk_file_chooser_default_unselect_path;
626   iface->select_all = gtk_file_chooser_default_select_all;
627   iface->unselect_all = gtk_file_chooser_default_unselect_all;
628   iface->get_paths = gtk_file_chooser_default_get_paths;
629   iface->get_preview_path = gtk_file_chooser_default_get_preview_path;
630   iface->get_file_system = gtk_file_chooser_default_get_file_system;
631   iface->set_current_folder = gtk_file_chooser_default_set_current_folder;
632   iface->get_current_folder = gtk_file_chooser_default_get_current_folder;
633   iface->set_current_name = gtk_file_chooser_default_set_current_name;
634   iface->add_filter = gtk_file_chooser_default_add_filter;
635   iface->remove_filter = gtk_file_chooser_default_remove_filter;
636   iface->list_filters = gtk_file_chooser_default_list_filters;
637   iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder;
638   iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder;
639   iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders;
640 }
641
642 static void
643 gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
644 {
645   iface->get_default_size = gtk_file_chooser_default_get_default_size;
646   iface->get_resizable_hints = gtk_file_chooser_default_get_resizable_hints;
647   iface->should_respond = gtk_file_chooser_default_should_respond;
648   iface->initial_focus = gtk_file_chooser_default_initial_focus;
649 }
650
651 static void
652 _gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
653 {
654   profile_start ("start", NULL);
655 #ifdef PROFILE_FILE_CHOOSER
656   access ("MARK: *** CREATE FILE CHOOSER", F_OK);
657 #endif
658   impl->local_only = TRUE;
659   impl->preview_widget_active = TRUE;
660   impl->use_preview_label = TRUE;
661   impl->select_multiple = FALSE;
662   impl->show_hidden = FALSE;
663   impl->icon_size = FALLBACK_ICON_SIZE;
664   impl->load_state = LOAD_EMPTY;
665   impl->reload_state = RELOAD_EMPTY;
666   impl->pending_select_paths = NULL;
667   impl->location_mode = LOCATION_MODE_PATH_BAR;
668
669   gtk_box_set_spacing (GTK_BOX (impl), 12);
670
671   impl->tooltips = gtk_tooltips_new ();
672   g_object_ref_sink (impl->tooltips);
673
674   profile_end ("end", NULL);
675 }
676
677 /* Frees the data columns for the specified iter in the shortcuts model*/
678 static void
679 shortcuts_free_row_data (GtkFileChooserDefault *impl,
680                          GtkTreeIter           *iter)
681 {
682   gpointer col_data;
683   gboolean is_volume;
684   GtkFileSystemHandle *handle;
685
686   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
687                       SHORTCUTS_COL_DATA, &col_data,
688                       SHORTCUTS_COL_IS_VOLUME, &is_volume,
689                       SHORTCUTS_COL_HANDLE, &handle,
690                       -1);
691
692   if (handle)
693     gtk_file_system_cancel_operation (handle);
694
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 pending_select_paths_free (GtkFileChooserDefault *impl)
736 {
737   GSList *l;
738
739   for (l = impl->pending_select_paths; l; l = l->next)
740     {
741       GtkFilePath *path;
742
743       path = l->data;
744       gtk_file_path_free (path);
745     }
746
747   g_slist_free (impl->pending_select_paths);
748   impl->pending_select_paths = NULL;
749 }
750
751 static void
752 pending_select_paths_add (GtkFileChooserDefault *impl,
753                           const GtkFilePath     *path)
754 {
755   impl->pending_select_paths = g_slist_prepend (impl->pending_select_paths, gtk_file_path_copy (path));
756 }
757
758 /* Used from gtk_tree_selection_selected_foreach() */
759 static void
760 store_selection_foreach (GtkTreeModel *model,
761                          GtkTreePath  *path,
762                          GtkTreeIter  *iter,
763                          gpointer      data)
764 {
765   GtkFileChooserDefault *impl;
766   GtkTreeIter child_iter;
767   const GtkFilePath *file_path;
768
769   impl = GTK_FILE_CHOOSER_DEFAULT (data);
770
771   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter);
772
773   file_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
774   pending_select_paths_add (impl, file_path);
775 }
776
777 /* Stores the current selection in the list of paths to select; this is used to
778  * preserve the selection when reloading the current folder.
779  */
780 static void
781 pending_select_paths_store_selection (GtkFileChooserDefault *impl)
782 {
783   GtkTreeSelection *selection;
784
785   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
786   gtk_tree_selection_selected_foreach (selection, store_selection_foreach, impl);
787 }
788
789 static void
790 gtk_file_chooser_default_finalize (GObject *object)
791 {
792   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
793   GSList *l;
794
795   if (impl->shortcuts_filter_model)
796     g_object_unref (impl->shortcuts_filter_model);
797
798   shortcuts_free (impl);
799
800   g_object_unref (impl->file_system);
801
802   for (l = impl->filters; l; l = l->next)
803     {
804       GtkFileFilter *filter;
805
806       filter = GTK_FILE_FILTER (l->data);
807       g_object_unref (filter);
808     }
809   g_slist_free (impl->filters);
810
811   if (impl->current_filter)
812     g_object_unref (impl->current_filter);
813
814   if (impl->current_volume_path)
815     gtk_file_path_free (impl->current_volume_path);
816
817   if (impl->current_folder)
818     gtk_file_path_free (impl->current_folder);
819
820   if (impl->preview_path)
821     gtk_file_path_free (impl->preview_path);
822
823   load_remove_timer (impl);
824
825   /* Free all the Models we have */
826   if (impl->browse_files_model)
827     g_object_unref (impl->browse_files_model);
828
829   if (impl->sort_model)
830     g_object_unref (impl->sort_model);
831
832   g_free (impl->preview_display_name);
833
834   g_free (impl->edited_new_text);
835
836   g_object_unref (impl->tooltips);
837
838   G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->finalize (object);
839 }
840
841 /* Shows an error dialog set as transient for the specified window */
842 static void
843 error_message_with_parent (GtkWindow  *parent,
844                            const char *msg,
845                            const char *detail)
846 {
847   GtkWidget *dialog;
848
849   dialog = gtk_message_dialog_new (parent,
850                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
851                                    GTK_MESSAGE_ERROR,
852                                    GTK_BUTTONS_OK,
853                                    "%s",
854                                    msg);
855   gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
856                                             "%s", detail);
857
858   if (parent->group)
859     gtk_window_group_add_window (parent->group, GTK_WINDOW (dialog));
860
861   gtk_dialog_run (GTK_DIALOG (dialog));
862   gtk_widget_destroy (dialog);
863 }
864
865 /* Returns a toplevel GtkWindow, or NULL if none */
866 static GtkWindow *
867 get_toplevel (GtkWidget *widget)
868 {
869   GtkWidget *toplevel;
870
871   toplevel = gtk_widget_get_toplevel (widget);
872   if (!GTK_WIDGET_TOPLEVEL (toplevel))
873     return NULL;
874   else
875     return GTK_WINDOW (toplevel);
876 }
877
878 /* Shows an error dialog for the file chooser */
879 static void
880 error_message (GtkFileChooserDefault *impl,
881                const char            *msg,
882                const char            *detail)
883 {
884   error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail);
885 }
886
887 /* Shows a simple error dialog relative to a path.  Frees the GError as well. */
888 static void
889 error_dialog (GtkFileChooserDefault *impl,
890               const char            *msg,
891               const GtkFilePath     *path,
892               GError                *error)
893 {
894   if (error)
895     {
896       char *uri = NULL;
897       char *text;
898
899       if (path)
900         uri = gtk_file_system_path_to_uri (impl->file_system, path);
901       text = g_strdup_printf (msg, uri);
902       error_message (impl, text, error->message);
903       g_free (text);
904       g_free (uri);
905       g_error_free (error);
906     }
907 }
908
909 /* Displays an error message about not being able to get information for a file.
910  * Frees the GError as well.
911  */
912 static void
913 error_getting_info_dialog (GtkFileChooserDefault *impl,
914                            const GtkFilePath     *path,
915                            GError                *error)
916 {
917   error_dialog (impl,
918                 _("Could not retrieve information about the file"),
919                 path, error);
920 }
921
922 /* Shows an error dialog about not being able to add a bookmark */
923 static void
924 error_adding_bookmark_dialog (GtkFileChooserDefault *impl,
925                               const GtkFilePath     *path,
926                               GError                *error)
927 {
928   error_dialog (impl,
929                 _("Could not add a bookmark"),
930                 path, error);
931 }
932
933 /* Shows an error dialog about not being able to remove a bookmark */
934 static void
935 error_removing_bookmark_dialog (GtkFileChooserDefault *impl,
936                                 const GtkFilePath     *path,
937                                 GError                *error)
938 {
939   error_dialog (impl,
940                 _("Could not remove bookmark"),
941                 path, error);
942 }
943
944 /* Shows an error dialog about not being able to create a folder */
945 static void
946 error_creating_folder_dialog (GtkFileChooserDefault *impl,
947                               const GtkFilePath     *path,
948                               GError                *error)
949 {
950   error_dialog (impl, 
951                 _("The folder could not be created"), 
952                 path, error);
953 }
954
955 /* Shows an error about not being able to create a folder because a file with
956  * the same name is already there.
957  */
958 static void
959 error_creating_folder_over_existing_file_dialog (GtkFileChooserDefault *impl,
960                                                  const GtkFilePath     *path,
961                                                  GError                *error)
962 {
963   error_dialog (impl,
964                 _("The folder could not be created, as a file with the same name "
965                   "already exists.  Try using a different name for the folder, "
966                   "or rename the file first."),
967                 path, error);
968 }
969
970 /* Shows an error dialog about not being able to create a filename */
971 static void
972 error_building_filename_dialog (GtkFileChooserDefault *impl,
973                                 const GtkFilePath     *folder_part,
974                                 const char            *file_part,
975                                 GError                *error)
976 {
977   error_dialog (impl, _("Invalid file name"), 
978                 NULL, error);
979 }
980
981 /* Shows an error dialog when we cannot switch to a folder */
982 static void
983 error_changing_folder_dialog (GtkFileChooserDefault *impl,
984                               const GtkFilePath     *path,
985                               GError                *error)
986 {
987   error_dialog (impl, _("The folder contents could not be displayed"),
988                 path, error);
989 }
990
991 /* Changes folders, displaying an error dialog if this fails */
992 static gboolean
993 change_folder_and_display_error (GtkFileChooserDefault *impl,
994                                  const GtkFilePath     *path)
995 {
996   GError *error;
997   gboolean result;
998   GtkFilePath *path_copy;
999
1000   g_return_val_if_fail (path != NULL, FALSE);
1001
1002   profile_start ("start", (char *) path);
1003
1004   /* We copy the path because of this case:
1005    *
1006    * list_row_activated()
1007    *   fetches path from model; path belongs to the model (*)
1008    *   calls change_folder_and_display_error()
1009    *     calls _gtk_file_chooser_set_current_folder_path()
1010    *       changing folders fails, sets model to NULL, thus freeing the path in (*)
1011    */
1012
1013   path_copy = gtk_file_path_copy (path);
1014
1015   error = NULL;
1016   result = gtk_file_chooser_default_update_current_folder (GTK_FILE_CHOOSER (impl), path_copy, TRUE, &error);
1017
1018   if (!result)
1019     error_changing_folder_dialog (impl, path_copy, error);
1020
1021   gtk_file_path_free (path_copy);
1022
1023   profile_end ("end", (char *) path);
1024
1025   return result;
1026 }
1027
1028 static void
1029 update_preview_widget_visibility (GtkFileChooserDefault *impl)
1030 {
1031   if (impl->use_preview_label)
1032     {
1033       if (!impl->preview_label)
1034         {
1035           impl->preview_label = gtk_label_new (impl->preview_display_name);
1036           gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_label, FALSE, FALSE, 0);
1037           gtk_box_reorder_child (GTK_BOX (impl->preview_box), impl->preview_label, 0);
1038           gtk_label_set_ellipsize (GTK_LABEL (impl->preview_label), PANGO_ELLIPSIZE_MIDDLE);
1039           gtk_widget_show (impl->preview_label);
1040         }
1041     }
1042   else
1043     {
1044       if (impl->preview_label)
1045         {
1046           gtk_widget_destroy (impl->preview_label);
1047           impl->preview_label = NULL;
1048         }
1049     }
1050
1051   if (impl->preview_widget_active && impl->preview_widget)
1052     gtk_widget_show (impl->preview_box);
1053   else
1054     gtk_widget_hide (impl->preview_box);
1055
1056   g_signal_emit_by_name (impl, "default-size-changed");
1057 }
1058
1059 static void
1060 set_preview_widget (GtkFileChooserDefault *impl,
1061                     GtkWidget             *preview_widget)
1062 {
1063   if (preview_widget == impl->preview_widget)
1064     return;
1065
1066   if (impl->preview_widget)
1067     gtk_container_remove (GTK_CONTAINER (impl->preview_box),
1068                           impl->preview_widget);
1069
1070   impl->preview_widget = preview_widget;
1071   if (impl->preview_widget)
1072     {
1073       gtk_widget_show (impl->preview_widget);
1074       gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_widget, TRUE, TRUE, 0);
1075       gtk_box_reorder_child (GTK_BOX (impl->preview_box),
1076                              impl->preview_widget,
1077                              (impl->use_preview_label && impl->preview_label) ? 1 : 0);
1078     }
1079
1080   update_preview_widget_visibility (impl);
1081 }
1082
1083 /* Re-reads all the icons for the shortcuts, used when the theme changes */
1084 struct ReloadIconsData
1085 {
1086   GtkFileChooserDefault *impl;
1087   GtkTreeRowReference *row_ref;
1088 };
1089
1090 static void
1091 shortcuts_reload_icons_get_info_cb (GtkFileSystemHandle *handle,
1092                                     const GtkFileInfo   *info,
1093                                     const GError        *error,
1094                                     gpointer             user_data)
1095 {
1096   GdkPixbuf *pixbuf;
1097   GtkTreeIter iter;
1098   GtkTreePath *path;
1099   gboolean cancelled = handle->cancelled;
1100   struct ReloadIconsData *data = user_data;
1101
1102   if (!g_slist_find (data->impl->reload_icon_handles, handle))
1103     goto out;
1104
1105   data->impl->reload_icon_handles = g_slist_remove (data->impl->reload_icon_handles, handle);
1106
1107   if (cancelled || error)
1108     goto out;
1109
1110   pixbuf = gtk_file_info_render_icon (info, GTK_WIDGET (data->impl),
1111                                       data->impl->icon_size, NULL);
1112
1113   path = gtk_tree_row_reference_get_path (data->row_ref);
1114   gtk_tree_model_get_iter (GTK_TREE_MODEL (data->impl->shortcuts_model), &iter, path);
1115   gtk_list_store_set (data->impl->shortcuts_model, &iter,
1116                       SHORTCUTS_COL_PIXBUF, pixbuf,
1117                       -1);
1118   gtk_tree_path_free (path);
1119
1120   if (pixbuf)
1121     g_object_unref (pixbuf);
1122
1123 out:
1124   gtk_tree_row_reference_free (data->row_ref);
1125   g_object_unref (data->impl);
1126   g_free (data);
1127
1128   g_object_unref (handle);
1129 }
1130
1131 static void
1132 shortcuts_reload_icons (GtkFileChooserDefault *impl)
1133 {
1134   GSList *l;
1135   GtkTreeIter iter;
1136
1137   profile_start ("start", NULL);
1138
1139   if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
1140     goto out;
1141
1142   for (l = impl->reload_icon_handles; l; l = l->next)
1143     {
1144       GtkFileSystemHandle *handle = GTK_FILE_SYSTEM_HANDLE (l->data);
1145       gtk_file_system_cancel_operation (handle);
1146     }
1147   g_slist_free (impl->reload_icon_handles);
1148   impl->reload_icon_handles = NULL;
1149
1150   do
1151     {
1152       gpointer data;
1153       gboolean is_volume;
1154       gboolean pixbuf_visible;
1155       GdkPixbuf *pixbuf;
1156
1157       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
1158                           SHORTCUTS_COL_DATA, &data,
1159                           SHORTCUTS_COL_IS_VOLUME, &is_volume,
1160                           SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible,
1161                           -1);
1162
1163       if (pixbuf_visible && data)
1164         {
1165           if (is_volume)
1166             {
1167               GtkFileSystemVolume *volume;
1168
1169               volume = data;
1170               pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl),
1171                                                            impl->icon_size, NULL);
1172
1173               gtk_list_store_set (impl->shortcuts_model, &iter,
1174                                   SHORTCUTS_COL_PIXBUF, pixbuf,
1175                                   -1);
1176
1177               if (pixbuf)
1178                 g_object_unref (pixbuf);
1179             }
1180           else
1181             {
1182               const GtkFilePath *path;
1183               struct ReloadIconsData *info;
1184               GtkTreePath *tree_path;
1185               GtkFileSystemHandle *handle;
1186
1187               path = data;
1188
1189               info = g_new0 (struct ReloadIconsData, 1);
1190               info->impl = g_object_ref (impl);
1191               tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
1192               info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->shortcuts_model), tree_path);
1193               gtk_tree_path_free (tree_path);
1194
1195               handle = gtk_file_system_get_info (impl->file_system, path,
1196                                                  GTK_FILE_INFO_ICON,
1197                                                  shortcuts_reload_icons_get_info_cb,
1198                                                  info);
1199               impl->reload_icon_handles = g_slist_append (impl->reload_icon_handles, handle);
1200             }
1201         }
1202     }
1203   while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
1204
1205  out:
1206
1207   profile_end ("end", NULL);
1208 }
1209
1210 static void 
1211 shortcuts_find_folder (GtkFileChooserDefault *impl,
1212                        GtkFilePath           *folder)
1213 {
1214   GtkTreeSelection *selection;
1215   int pos;
1216   GtkTreePath *path;
1217
1218   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1219
1220   g_assert (folder != NULL);
1221   pos = shortcut_find_position (impl, folder);
1222   if (pos == -1)
1223     {
1224       gtk_tree_selection_unselect_all (selection);
1225       return;
1226     }
1227
1228   path = gtk_tree_path_new_from_indices (pos, -1);
1229   gtk_tree_selection_select_path (selection, path);
1230   gtk_tree_path_free (path);
1231 }
1232
1233 /* If a shortcut corresponds to the current folder, selects it */
1234 static void
1235 shortcuts_find_current_folder (GtkFileChooserDefault *impl)
1236 {
1237   shortcuts_find_folder (impl, impl->current_folder);
1238 }
1239
1240 /* Removes the specified number of rows from the shortcuts list */
1241 static void
1242 shortcuts_remove_rows (GtkFileChooserDefault *impl,
1243                        int                    start_row,
1244                        int                    n_rows)
1245 {
1246   GtkTreePath *path;
1247
1248   path = gtk_tree_path_new_from_indices (start_row, -1);
1249
1250   for (; n_rows; n_rows--)
1251     {
1252       GtkTreeIter iter;
1253
1254       if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
1255         g_assert_not_reached ();
1256
1257       shortcuts_free_row_data (impl, &iter);
1258       gtk_list_store_remove (impl->shortcuts_model, &iter);
1259     }
1260
1261   gtk_tree_path_free (path);
1262 }
1263
1264 static void
1265 shortcuts_update_count (GtkFileChooserDefault *impl,
1266                         ShortcutsIndex         type,
1267                         gint                   value)
1268 {
1269   switch (type)
1270     {
1271       case SHORTCUTS_HOME:
1272         if (value < 0)
1273           impl->has_home = FALSE;
1274         else
1275           impl->has_home = TRUE;
1276         break;
1277
1278       case SHORTCUTS_DESKTOP:
1279         if (value < 0)
1280           impl->has_desktop = FALSE;
1281         else
1282           impl->has_desktop = TRUE;
1283         break;
1284
1285       case SHORTCUTS_VOLUMES:
1286         impl->num_volumes += value;
1287         break;
1288
1289       case SHORTCUTS_SHORTCUTS:
1290         impl->num_shortcuts += value;
1291         break;
1292
1293       case SHORTCUTS_BOOKMARKS:
1294         impl->num_bookmarks += value;
1295         break;
1296
1297       case SHORTCUTS_CURRENT_FOLDER:
1298         if (value < 0)
1299           impl->shortcuts_current_folder_active = FALSE;
1300         else
1301           impl->shortcuts_current_folder_active = TRUE;
1302         break;
1303
1304       default:
1305         /* nothing */
1306         break;
1307     }
1308 }
1309
1310 struct ShortcutsInsertRequest
1311 {
1312   GtkFileChooserDefault *impl;
1313   GtkFilePath *parent_path;
1314   GtkFilePath *path;
1315   int pos;
1316   char *label_copy;
1317   GtkTreeRowReference *row_ref;
1318   ShortcutsIndex type;
1319   gboolean name_only;
1320   gboolean removable;
1321 };
1322
1323 static void
1324 get_file_info_finished (GtkFileSystemHandle *handle,
1325                         const GtkFileInfo   *info,
1326                         const GError        *error,
1327                         gpointer             data)
1328 {
1329   gint pos = -1;
1330   gboolean cancelled = handle->cancelled;
1331   gboolean is_volume = FALSE;
1332   GdkPixbuf *pixbuf;
1333   GtkTreePath *path;
1334   GtkTreeIter iter;
1335   GtkFileSystemHandle *model_handle;
1336   struct ShortcutsInsertRequest *request = data;
1337
1338   path = gtk_tree_row_reference_get_path (request->row_ref);
1339   if (!path)
1340     /* Handle doesn't exist anymore in the model */
1341     goto out;
1342
1343   pos = gtk_tree_path_get_indices (path)[0];
1344   gtk_tree_model_get_iter (GTK_TREE_MODEL (request->impl->shortcuts_model),
1345                            &iter, path);
1346   gtk_tree_path_free (path);
1347
1348   /* validate handle, else goto out */
1349   gtk_tree_model_get (GTK_TREE_MODEL (request->impl->shortcuts_model), &iter,
1350                       SHORTCUTS_COL_HANDLE, &model_handle,
1351                       -1);
1352   if (handle != model_handle)
1353     goto out;
1354
1355   /* set the handle to NULL in the model (we unref later on) */
1356   gtk_list_store_set (request->impl->shortcuts_model, &iter,
1357                       SHORTCUTS_COL_HANDLE, NULL,
1358                       -1);
1359
1360   if (cancelled)
1361     goto out;
1362
1363   if (!info)
1364     {
1365       gtk_list_store_remove (request->impl->shortcuts_model, &iter);
1366       shortcuts_update_count (request->impl, request->type, -1);
1367
1368       if (request->type == SHORTCUTS_HOME)
1369         {
1370           const char *home = g_get_home_dir ();
1371           GtkFilePath *home_path;
1372
1373           home_path = gtk_file_system_filename_to_path (request->impl->file_system, home);
1374           error_getting_info_dialog (request->impl, home_path, g_error_copy (error));
1375           gtk_file_path_free (home_path);
1376         }
1377       else if (request->type == SHORTCUTS_CURRENT_FOLDER)
1378         {
1379           /* Remove the current folder separator */
1380           gint separator_pos = shortcuts_get_index (request->impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1381           shortcuts_remove_rows (request->impl, separator_pos, 1);
1382         }
1383
1384       goto out;
1385     }
1386   
1387   if (!request->label_copy)
1388     request->label_copy = g_strdup (gtk_file_info_get_display_name (info));
1389   pixbuf = gtk_file_info_render_icon (info, GTK_WIDGET (request->impl),
1390                                       request->impl->icon_size, NULL);
1391
1392   gtk_list_store_set (request->impl->shortcuts_model, &iter,
1393                       SHORTCUTS_COL_PIXBUF, pixbuf,
1394                       SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1395                       SHORTCUTS_COL_NAME, request->label_copy,
1396                       SHORTCUTS_COL_IS_VOLUME, is_volume,
1397                       SHORTCUTS_COL_REMOVABLE, request->removable,
1398                       -1);
1399
1400   if (request->impl->shortcuts_filter_model)
1401     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (request->impl->shortcuts_filter_model));
1402
1403   if (request->type == SHORTCUTS_CURRENT_FOLDER
1404       && request->impl->save_folder_combo != NULL)
1405     {
1406       /* The current folder is updated via _activate_iter(), don't
1407        * have save_folder_combo_changed_cb() call _activate_iter()
1408        * again.
1409        */
1410       g_signal_handlers_block_by_func (request->impl->save_folder_combo,
1411                                        G_CALLBACK (save_folder_combo_changed_cb),
1412                                        request->impl);
1413       gtk_combo_box_set_active (GTK_COMBO_BOX (request->impl->save_folder_combo), pos);
1414       g_signal_handlers_unblock_by_func (request->impl->save_folder_combo,
1415                                          G_CALLBACK (save_folder_combo_changed_cb),
1416                                          request->impl);
1417     }
1418
1419   if (pixbuf)
1420     g_object_unref (pixbuf);
1421
1422 out:
1423   g_object_unref (request->impl);
1424   gtk_file_path_free (request->parent_path);
1425   gtk_file_path_free (request->path);
1426   gtk_tree_row_reference_free (request->row_ref);
1427   g_free (request->label_copy);
1428   g_free (request);
1429
1430   g_object_unref (handle);
1431 }
1432
1433 /* Inserts a path in the shortcuts tree, making a copy of it; alternatively,
1434  * inserts a volume.  A position of -1 indicates the end of the tree.
1435  */
1436 static void
1437 shortcuts_insert_path (GtkFileChooserDefault *impl,
1438                        int                    pos,
1439                        gboolean               is_volume,
1440                        GtkFileSystemVolume   *volume,
1441                        const GtkFilePath     *path,
1442                        const char            *label,
1443                        gboolean               removable,
1444                        ShortcutsIndex         type)
1445 {
1446   char *label_copy;
1447   GdkPixbuf *pixbuf = NULL;
1448   gpointer data = NULL;
1449   GtkTreeIter iter;
1450
1451   profile_start ("start", is_volume ? "volume" : (char *) path);
1452
1453   if (is_volume)
1454     {
1455       data = volume;
1456       label_copy = gtk_file_system_volume_get_display_name (impl->file_system, volume);
1457       pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl),
1458                                                    impl->icon_size, NULL);
1459     }
1460   else
1461     {
1462       struct ShortcutsInsertRequest *request;
1463       GtkFileSystemHandle *handle;
1464       GtkTreePath *p;
1465
1466       request = g_new0 (struct ShortcutsInsertRequest, 1);
1467       request->impl = g_object_ref (impl);
1468       request->path = gtk_file_path_copy (path);
1469       request->name_only = TRUE;
1470       request->removable = removable;
1471       request->pos = pos;
1472       request->type = type;
1473       if (label)
1474         request->label_copy = g_strdup (label);
1475
1476       if (pos == -1)
1477         gtk_list_store_append (impl->shortcuts_model, &iter);
1478       else
1479         gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
1480
1481       p = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
1482       request->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->shortcuts_model), p);
1483       gtk_tree_path_free (p);
1484
1485       handle = gtk_file_system_get_info (request->impl->file_system, request->path,
1486                                          GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_IS_HIDDEN | GTK_FILE_INFO_ICON,
1487                                          get_file_info_finished, request);
1488
1489       gtk_list_store_set (impl->shortcuts_model, &iter,
1490                           SHORTCUTS_COL_DATA, gtk_file_path_copy (path),
1491                           SHORTCUTS_COL_IS_VOLUME, is_volume,
1492                           SHORTCUTS_COL_HANDLE, handle,
1493                           -1);
1494
1495       shortcuts_update_count (impl, type, 1);
1496
1497       return;
1498     }
1499
1500   if (!data)
1501     data = gtk_file_path_copy (path);
1502
1503   if (pos == -1)
1504     gtk_list_store_append (impl->shortcuts_model, &iter);
1505   else
1506     gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
1507
1508   shortcuts_update_count (impl, type, 1);
1509
1510   gtk_list_store_set (impl->shortcuts_model, &iter,
1511                       SHORTCUTS_COL_PIXBUF, pixbuf,
1512                       SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1513                       SHORTCUTS_COL_NAME, label_copy,
1514                       SHORTCUTS_COL_DATA, data,
1515                       SHORTCUTS_COL_IS_VOLUME, is_volume,
1516                       SHORTCUTS_COL_REMOVABLE, removable,
1517                       SHORTCUTS_COL_HANDLE, NULL,
1518                       -1);
1519
1520   if (impl->shortcuts_filter_model)
1521     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
1522
1523   if (type == SHORTCUTS_CURRENT_FOLDER && impl->save_folder_combo != NULL)
1524     {
1525       /* The current folder is updated via _activate_iter(), don't
1526        * have save_folder_combo_changed_cb() call _activate_iter()
1527        * again.
1528        */
1529       gint combo_pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER);
1530       g_signal_handlers_block_by_func (impl->save_folder_combo,
1531                                        G_CALLBACK (save_folder_combo_changed_cb),
1532                                        impl);
1533       gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), combo_pos);
1534       g_signal_handlers_unblock_by_func (impl->save_folder_combo,
1535                                          G_CALLBACK (save_folder_combo_changed_cb),
1536                                          impl);
1537     }
1538
1539   g_free (label_copy);
1540
1541   if (pixbuf)
1542     g_object_unref (pixbuf);
1543
1544   profile_end ("end", NULL);
1545 }
1546
1547 /* Appends an item for the user's home directory to the shortcuts model */
1548 static void
1549 shortcuts_append_home (GtkFileChooserDefault *impl)
1550 {
1551   const char *home;
1552   GtkFilePath *home_path;
1553
1554   profile_start ("start", NULL);
1555
1556   home = g_get_home_dir ();
1557   if (home == NULL)
1558     {
1559       profile_end ("end - no home directory!?", NULL);
1560       return;
1561     }
1562
1563   home_path = gtk_file_system_filename_to_path (impl->file_system, home);
1564
1565   shortcuts_insert_path (impl, -1, FALSE, NULL, home_path, NULL, FALSE, SHORTCUTS_HOME);
1566
1567   gtk_file_path_free (home_path);
1568
1569   profile_end ("end", NULL);
1570 }
1571
1572 /* Appends the ~/Desktop directory to the shortcuts model */
1573 static void
1574 shortcuts_append_desktop (GtkFileChooserDefault *impl)
1575 {
1576   char *name;
1577   const char *home;
1578   GtkFilePath *path;
1579
1580   profile_start ("start", NULL);
1581
1582 #ifdef G_OS_WIN32
1583   name = _gtk_file_system_win32_get_desktop ();
1584 #else
1585   home = g_get_home_dir ();
1586   if (home == NULL)
1587     {
1588       profile_end ("end - no home directory!?", NULL);
1589       return;
1590     }
1591
1592   name = g_build_filename (home, "Desktop", NULL);
1593 #endif
1594
1595   path = gtk_file_system_filename_to_path (impl->file_system, name);
1596   g_free (name);
1597
1598   shortcuts_insert_path (impl, -1, FALSE, NULL, path, _("Desktop"), FALSE, SHORTCUTS_DESKTOP);
1599   /* We do not actually pop up an error dialog if there is no desktop directory
1600    * because some people may really not want to have one.
1601    */
1602
1603   gtk_file_path_free (path);
1604
1605   profile_end ("end", NULL);
1606 }
1607
1608 /* Appends a list of GtkFilePath to the shortcuts model; returns how many were inserted */
1609 static int
1610 shortcuts_append_paths (GtkFileChooserDefault *impl,
1611                         GSList                *paths)
1612 {
1613   int start_row;
1614   int num_inserted;
1615   gchar *label;
1616
1617   profile_start ("start", NULL);
1618
1619   /* As there is no separator now, we want to start there.
1620    */
1621   start_row = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
1622   num_inserted = 0;
1623
1624   for (; paths; paths = paths->next)
1625     {
1626       GtkFilePath *path;
1627
1628       path = paths->data;
1629
1630       if (impl->local_only &&
1631           !gtk_file_system_path_is_local (impl->file_system, path))
1632         continue;
1633
1634       label = gtk_file_system_get_bookmark_label (impl->file_system, path);
1635
1636       /* NULL GError, but we don't really want to show error boxes here */
1637       shortcuts_insert_path (impl, start_row + num_inserted, FALSE, NULL, path, label, TRUE, SHORTCUTS_BOOKMARKS);
1638       num_inserted++;
1639
1640       g_free (label);
1641     }
1642
1643   profile_end ("end", NULL);
1644
1645   return num_inserted;
1646 }
1647
1648 /* Returns the index for the corresponding item in the shortcuts bar */
1649 static int
1650 shortcuts_get_index (GtkFileChooserDefault *impl,
1651                      ShortcutsIndex         where)
1652 {
1653   int n;
1654
1655   n = 0;
1656
1657   if (where == SHORTCUTS_HOME)
1658     goto out;
1659
1660   n += impl->has_home ? 1 : 0;
1661
1662   if (where == SHORTCUTS_DESKTOP)
1663     goto out;
1664
1665   n += impl->has_desktop ? 1 : 0;
1666
1667   if (where == SHORTCUTS_VOLUMES)
1668     goto out;
1669
1670   n += impl->num_volumes;
1671
1672   if (where == SHORTCUTS_SHORTCUTS)
1673     goto out;
1674
1675   n += impl->num_shortcuts;
1676
1677   if (where == SHORTCUTS_BOOKMARKS_SEPARATOR)
1678     goto out;
1679
1680   /* If there are no bookmarks there won't be a separator */
1681   n += (impl->num_bookmarks > 0) ? 1 : 0;
1682
1683   if (where == SHORTCUTS_BOOKMARKS)
1684     goto out;
1685
1686   n += impl->num_bookmarks;
1687
1688   if (where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR)
1689     goto out;
1690
1691   n += 1;
1692
1693   if (where == SHORTCUTS_CURRENT_FOLDER)
1694     goto out;
1695
1696   g_assert_not_reached ();
1697
1698  out:
1699
1700   return n;
1701 }
1702
1703 /* Adds all the file system volumes to the shortcuts model */
1704 static void
1705 shortcuts_add_volumes (GtkFileChooserDefault *impl)
1706 {
1707   int start_row;
1708   GSList *list, *l;
1709   int n;
1710   gboolean old_changing_folders;
1711
1712   profile_start ("start", NULL);
1713
1714   old_changing_folders = impl->changing_folder;
1715   impl->changing_folder = TRUE;
1716
1717   start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
1718   shortcuts_remove_rows (impl, start_row, impl->num_volumes);
1719   impl->num_volumes = 0;
1720
1721   list = gtk_file_system_list_volumes (impl->file_system);
1722
1723   n = 0;
1724
1725   for (l = list; l; l = l->next)
1726     {
1727       GtkFileSystemVolume *volume;
1728
1729       volume = l->data;
1730
1731       if (impl->local_only)
1732         {
1733           if (gtk_file_system_volume_get_is_mounted (impl->file_system, volume))
1734             {
1735               GtkFilePath *base_path;
1736
1737               base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
1738               if (base_path != NULL)
1739                 {
1740                   gboolean is_local = gtk_file_system_path_is_local (impl->file_system, base_path);
1741                   gtk_file_path_free (base_path);
1742
1743                   if (!is_local)
1744                     {
1745                       gtk_file_system_volume_free (impl->file_system, volume);
1746                       continue;
1747                     }
1748                 }
1749             }
1750         }
1751
1752       shortcuts_insert_path (impl, start_row + n, TRUE, volume, NULL, NULL, FALSE, SHORTCUTS_VOLUMES);
1753       n++;
1754     }
1755
1756   impl->num_volumes = n;
1757   g_slist_free (list);
1758
1759   if (impl->shortcuts_filter_model)
1760     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
1761
1762   impl->changing_folder = old_changing_folders;
1763
1764   profile_end ("end", NULL);
1765 }
1766
1767 /* Inserts a separator node in the shortcuts list */
1768 static void
1769 shortcuts_insert_separator (GtkFileChooserDefault *impl,
1770                             ShortcutsIndex where)
1771 {
1772   GtkTreeIter iter;
1773
1774   g_assert (where == SHORTCUTS_BOOKMARKS_SEPARATOR || where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1775
1776   gtk_list_store_insert (impl->shortcuts_model, &iter,
1777                          shortcuts_get_index (impl, where));
1778   gtk_list_store_set (impl->shortcuts_model, &iter,
1779                       SHORTCUTS_COL_PIXBUF, NULL,
1780                       SHORTCUTS_COL_PIXBUF_VISIBLE, FALSE,
1781                       SHORTCUTS_COL_NAME, NULL,
1782                       SHORTCUTS_COL_DATA, NULL,
1783                       -1);
1784 }
1785
1786 /* Updates the list of bookmarks */
1787 static void
1788 shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
1789 {
1790   GSList *bookmarks;
1791   gboolean old_changing_folders;
1792   GtkTreeIter iter;
1793   GtkFilePath *list_selected = NULL;
1794   GtkFilePath *combo_selected = NULL;
1795   gboolean is_volume;
1796   gpointer col_data;
1797
1798   profile_start ("start", NULL);
1799         
1800
1801   old_changing_folders = impl->changing_folder;
1802   impl->changing_folder = TRUE;
1803
1804   if (shortcuts_get_selected (impl, &iter))
1805     {
1806       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), 
1807                           &iter, 
1808                           SHORTCUTS_COL_DATA, &col_data,
1809                           SHORTCUTS_COL_IS_VOLUME, &is_volume,
1810                           -1);
1811
1812       if (col_data && !is_volume)
1813         list_selected = gtk_file_path_copy (col_data);
1814     }
1815
1816   if (impl->save_folder_combo &&
1817       gtk_combo_box_get_active_iter (GTK_COMBO_BOX (impl->save_folder_combo), 
1818                                      &iter))
1819     {
1820       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), 
1821                           &iter, 
1822                           SHORTCUTS_COL_DATA, &col_data,
1823                           SHORTCUTS_COL_IS_VOLUME, &is_volume,
1824                           -1);
1825       
1826       if (col_data && !is_volume)
1827         combo_selected = gtk_file_path_copy (col_data);
1828     }
1829
1830   if (impl->num_bookmarks > 0)
1831     shortcuts_remove_rows (impl,
1832                            shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR),
1833                            impl->num_bookmarks + 1);
1834
1835   impl->num_bookmarks = 0;
1836
1837   bookmarks = gtk_file_system_list_bookmarks (impl->file_system);
1838   shortcuts_append_paths (impl, bookmarks);
1839   gtk_file_paths_free (bookmarks);
1840
1841   if (impl->num_bookmarks > 0)
1842     shortcuts_insert_separator (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
1843
1844   if (impl->shortcuts_filter_model)
1845     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
1846
1847   if (list_selected)
1848     {
1849       shortcuts_find_folder (impl, list_selected);
1850       gtk_file_path_free (list_selected);
1851     }
1852
1853   if (combo_selected)
1854     {
1855       gint pos;
1856
1857       pos = shortcut_find_position (impl, combo_selected);
1858       if (pos != -1)
1859         gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), 
1860                                   pos);
1861       gtk_file_path_free (combo_selected);
1862     }
1863   
1864   impl->changing_folder = old_changing_folders;
1865
1866   profile_end ("end", NULL);
1867 }
1868
1869 /* Appends a separator and a row to the shortcuts list for the current folder */
1870 static void
1871 shortcuts_add_current_folder (GtkFileChooserDefault *impl)
1872 {
1873   int pos;
1874   gboolean success;
1875
1876   g_assert (!impl->shortcuts_current_folder_active);
1877
1878   success = TRUE;
1879
1880   g_assert (impl->current_folder != NULL);
1881
1882   pos = shortcut_find_position (impl, impl->current_folder);
1883   if (pos == -1)
1884     {
1885       GtkFileSystemVolume *volume;
1886       GtkFilePath *base_path;
1887
1888       /* Separator */
1889
1890       shortcuts_insert_separator (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1891
1892       /* Item */
1893
1894       pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER);
1895
1896       volume = gtk_file_system_get_volume_for_path (impl->file_system, impl->current_folder);
1897       if (volume)
1898         base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
1899       else
1900         base_path = NULL;
1901
1902       if (base_path &&
1903           strcmp (gtk_file_path_get_string (base_path), gtk_file_path_get_string (impl->current_folder)) == 0)
1904         {
1905           shortcuts_insert_path (impl, pos, TRUE, volume, NULL, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER);
1906         }
1907       else
1908         shortcuts_insert_path (impl, pos, FALSE, NULL, impl->current_folder, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER);
1909
1910       if (base_path)
1911         gtk_file_path_free (base_path);
1912     }
1913   else if (impl->save_folder_combo != NULL)
1914     gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), pos);
1915 }
1916
1917 /* Updates the current folder row in the shortcuts model */
1918 static void
1919 shortcuts_update_current_folder (GtkFileChooserDefault *impl)
1920 {
1921   int pos;
1922
1923   pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1924
1925   if (impl->shortcuts_current_folder_active)
1926     {
1927       shortcuts_remove_rows (impl, pos, 2);
1928       impl->shortcuts_current_folder_active = FALSE;
1929     }
1930
1931   shortcuts_add_current_folder (impl);
1932 }
1933
1934 /* Filter function used for the shortcuts filter model */
1935 static gboolean
1936 shortcuts_filter_cb (GtkTreeModel          *model,
1937                      GtkTreeIter           *iter,
1938                      gpointer               data)
1939 {
1940   GtkFileChooserDefault *impl;
1941   GtkTreePath *path;
1942   int pos;
1943
1944   impl = GTK_FILE_CHOOSER_DEFAULT (data);
1945
1946   path = gtk_tree_model_get_path (model, iter);
1947   if (!path)
1948     return FALSE;
1949
1950   pos = *gtk_tree_path_get_indices (path);
1951   gtk_tree_path_free (path);
1952
1953   return (pos < shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR));
1954 }
1955
1956 /* Creates the list model for shortcuts */
1957 static void
1958 shortcuts_model_create (GtkFileChooserDefault *impl)
1959 {
1960   /* Keep this order in sync with the SHORCUTS_COL_* enum values */
1961   impl->shortcuts_model = gtk_list_store_new (SHORTCUTS_COL_NUM_COLUMNS,
1962                                               GDK_TYPE_PIXBUF,  /* pixbuf */
1963                                               G_TYPE_STRING,    /* name */
1964                                               G_TYPE_POINTER,   /* path or volume */
1965                                               G_TYPE_BOOLEAN,   /* is the previous column a volume? */
1966                                               G_TYPE_BOOLEAN,   /* removable */
1967                                               G_TYPE_BOOLEAN,   /* pixbuf cell visibility */
1968                                               G_TYPE_OBJECT);   /* GtkFileSystemHandle */
1969
1970   if (impl->file_system)
1971     {
1972       shortcuts_append_home (impl);
1973       shortcuts_append_desktop (impl);
1974       shortcuts_add_volumes (impl);
1975     }
1976
1977   impl->shortcuts_filter_model = shortcuts_model_filter_new (impl,
1978                                                              GTK_TREE_MODEL (impl->shortcuts_model),
1979                                                              NULL);
1980
1981   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
1982                                           shortcuts_filter_cb,
1983                                           impl,
1984                                           NULL);
1985 }
1986
1987 /* Callback used when the "New Folder" button is clicked */
1988 static void
1989 new_folder_button_clicked (GtkButton             *button,
1990                            GtkFileChooserDefault *impl)
1991 {
1992   GtkTreeIter iter;
1993   GtkTreePath *path;
1994
1995   if (!impl->browse_files_model)
1996     return; /* FIXME: this sucks.  Disable the New Folder button or something. */
1997
1998   /* Prevent button from being clicked twice */
1999   gtk_widget_set_sensitive (impl->browse_new_folder_button, FALSE);
2000
2001   _gtk_file_system_model_add_editable (impl->browse_files_model, &iter);
2002
2003   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->browse_files_model), &iter);
2004   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_files_tree_view),
2005                                 path, impl->list_name_column,
2006                                 FALSE, 0.0, 0.0);
2007
2008   g_object_set (impl->list_name_renderer, "editable", TRUE, NULL);
2009   gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view),
2010                             path,
2011                             impl->list_name_column,
2012                             TRUE);
2013
2014   gtk_tree_path_free (path);
2015 }
2016
2017 static void
2018 edited_idle_create_folder_cb (GtkFileSystemHandle *handle,
2019                               const GtkFilePath   *path,
2020                               const GError        *error,
2021                               gpointer             data)
2022 {
2023   gboolean cancelled = handle->cancelled;
2024   GtkFileChooserDefault *impl = data;
2025
2026   if (!g_slist_find (impl->pending_handles, handle))
2027     goto out;
2028
2029   impl->pending_handles = g_slist_remove (impl->pending_handles, handle);
2030
2031   if (cancelled)
2032     goto out;
2033
2034   if (!error)
2035     change_folder_and_display_error (impl, path);
2036   else
2037     error_creating_folder_dialog (impl, path, g_error_copy (error));
2038
2039  out:
2040   g_object_unref (impl);
2041   g_object_unref (handle);
2042 }
2043
2044 /* Idle handler for creating a new folder after editing its name cell, or for
2045  * canceling the editing.
2046  */
2047 static gboolean
2048 edited_idle_cb (GtkFileChooserDefault *impl)
2049 {
2050   GDK_THREADS_ENTER ();
2051   
2052   g_source_destroy (impl->edited_idle);
2053   impl->edited_idle = NULL;
2054
2055   _gtk_file_system_model_remove_editable (impl->browse_files_model);
2056   g_object_set (impl->list_name_renderer, "editable", FALSE, NULL);
2057
2058   gtk_widget_set_sensitive (impl->browse_new_folder_button, TRUE);
2059
2060   if (impl->edited_new_text) /* not cancelled? */
2061     {
2062       GError *error;
2063       GtkFilePath *file_path;
2064
2065       error = NULL;
2066       file_path = gtk_file_system_make_path (impl->file_system,
2067                                              impl->current_folder,
2068                                              impl->edited_new_text,
2069                                              &error);
2070       if (file_path)
2071         {
2072           GtkFileSystemHandle *handle;
2073
2074           handle = gtk_file_system_create_folder (impl->file_system, file_path,
2075                                                   edited_idle_create_folder_cb,
2076                                                   g_object_ref (impl));
2077           impl->pending_handles = g_slist_append (impl->pending_handles, handle);
2078
2079           gtk_file_path_free (file_path);
2080         }
2081       else
2082         error_creating_folder_dialog (impl, file_path, error);
2083
2084       g_free (impl->edited_new_text);
2085       impl->edited_new_text = NULL;
2086     }
2087
2088   GDK_THREADS_LEAVE ();
2089
2090   return FALSE;
2091 }
2092
2093 static void
2094 queue_edited_idle (GtkFileChooserDefault *impl,
2095                    const gchar           *new_text)
2096 {
2097   /* We create the folder in an idle handler so that we don't modify the tree
2098    * just now.
2099    */
2100
2101   if (!impl->edited_idle)
2102     {
2103       impl->edited_idle = g_idle_source_new ();
2104       g_source_set_closure (impl->edited_idle,
2105                             g_cclosure_new_object (G_CALLBACK (edited_idle_cb),
2106                                                    G_OBJECT (impl)));
2107       g_source_attach (impl->edited_idle, NULL);
2108     }
2109
2110   g_free (impl->edited_new_text);
2111   impl->edited_new_text = g_strdup (new_text);
2112 }
2113
2114 /* Callback used from the text cell renderer when the new folder is named */
2115 static void
2116 renderer_edited_cb (GtkCellRendererText   *cell_renderer_text,
2117                     const gchar           *path,
2118                     const gchar           *new_text,
2119                     GtkFileChooserDefault *impl)
2120 {
2121  /* work around bug #154921 */
2122   g_object_set (cell_renderer_text, 
2123                 "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
2124    queue_edited_idle (impl, new_text);
2125 }
2126
2127 /* Callback used from the text cell renderer when the new folder edition gets
2128  * canceled.
2129  */
2130 static void
2131 renderer_editing_canceled_cb (GtkCellRendererText   *cell_renderer_text,
2132                               GtkFileChooserDefault *impl)
2133 {
2134  /* work around bug #154921 */
2135   g_object_set (cell_renderer_text, 
2136                 "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
2137    queue_edited_idle (impl, NULL);
2138 }
2139
2140 /* Creates the widgets for the filter combo box */
2141 static GtkWidget *
2142 filter_create (GtkFileChooserDefault *impl)
2143 {
2144   impl->filter_combo = gtk_combo_box_new_text ();
2145   gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (impl->filter_combo), FALSE);
2146
2147   g_signal_connect (impl->filter_combo, "changed",
2148                     G_CALLBACK (filter_combo_changed), impl);
2149
2150   return impl->filter_combo;
2151 }
2152
2153 static GtkWidget *
2154 button_new (GtkFileChooserDefault *impl,
2155             const char *text,
2156             const char *stock_id,
2157             gboolean    sensitive,
2158             gboolean    show,
2159             GCallback   callback)
2160 {
2161   GtkWidget *button;
2162   GtkWidget *image;
2163
2164   button = gtk_button_new_with_mnemonic (text);
2165   image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
2166   gtk_button_set_image (GTK_BUTTON (button), image);
2167
2168   gtk_widget_set_sensitive (button, sensitive);
2169   g_signal_connect (button, "clicked", callback, impl);
2170
2171   if (show)
2172     gtk_widget_show (button);
2173
2174   return button;
2175 }
2176
2177 /* Looks for a path among the shortcuts; returns its index or -1 if it doesn't exist */
2178 static int
2179 shortcut_find_position (GtkFileChooserDefault *impl,
2180                         const GtkFilePath     *path)
2181 {
2182   GtkTreeIter iter;
2183   int i;
2184   int current_folder_separator_idx;
2185
2186   if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
2187     return -1;
2188
2189   current_folder_separator_idx = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2190
2191 #if 0
2192   /* FIXME: is this still needed? */
2193   if (current_folder_separator_idx >= impl->shortcuts_model->length)
2194     return -1;
2195 #endif
2196
2197   for (i = 0; i < current_folder_separator_idx; i++)
2198     {
2199       gpointer col_data;
2200       gboolean is_volume;
2201
2202       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2203                           SHORTCUTS_COL_DATA, &col_data,
2204                           SHORTCUTS_COL_IS_VOLUME, &is_volume,
2205                           -1);
2206
2207       if (col_data)
2208         {
2209           if (is_volume)
2210             {
2211               GtkFileSystemVolume *volume;
2212               GtkFilePath *base_path;
2213               gboolean exists;
2214
2215               volume = col_data;
2216               base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
2217
2218               exists = base_path && strcmp (gtk_file_path_get_string (path),
2219                                             gtk_file_path_get_string (base_path)) == 0;
2220               g_free (base_path);
2221
2222               if (exists)
2223                 return i;
2224             }
2225           else
2226             {
2227               GtkFilePath *model_path;
2228
2229               model_path = col_data;
2230
2231               if (model_path && gtk_file_path_compare (model_path, path) == 0)
2232                 return i;
2233             }
2234         }
2235
2236       if (i < current_folder_separator_idx - 1)
2237         {
2238           if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
2239             g_assert_not_reached ();
2240         }
2241     }
2242
2243   return -1;
2244 }
2245
2246 /* Tries to add a bookmark from a path name */
2247 static gboolean
2248 shortcuts_add_bookmark_from_path (GtkFileChooserDefault *impl,
2249                                   const GtkFilePath     *path,
2250                                   int                    pos)
2251 {
2252   GError *error;
2253
2254   g_return_val_if_fail (path != NULL, FALSE);
2255  
2256   if (shortcut_find_position (impl, path) != -1)
2257     return FALSE;
2258
2259   error = NULL;
2260   if (!gtk_file_system_insert_bookmark (impl->file_system, path, pos, &error))
2261     {
2262       error_adding_bookmark_dialog (impl, path, error);
2263       return FALSE;
2264     }
2265
2266   return TRUE;
2267 }
2268
2269 static void
2270 add_bookmark_foreach_cb (GtkTreeModel *model,
2271                          GtkTreePath  *path,
2272                          GtkTreeIter  *iter,
2273                          gpointer      data)
2274 {
2275   GtkFileChooserDefault *impl;
2276   GtkFileSystemModel *fs_model;
2277   GtkTreeIter child_iter;
2278   const GtkFilePath *file_path;
2279
2280   impl = (GtkFileChooserDefault *) data;
2281
2282   fs_model = impl->browse_files_model;
2283   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter);
2284
2285   file_path = _gtk_file_system_model_get_path (fs_model, &child_iter);
2286   shortcuts_add_bookmark_from_path (impl, file_path, -1);
2287 }
2288
2289 /* Adds a bookmark from the currently selected item in the file list */
2290 static void
2291 bookmarks_add_selected_folder (GtkFileChooserDefault *impl)
2292 {
2293   GtkTreeSelection *selection;
2294
2295   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2296
2297   if (gtk_tree_selection_count_selected_rows (selection) == 0)
2298     shortcuts_add_bookmark_from_path (impl, impl->current_folder, -1);
2299   else
2300     gtk_tree_selection_selected_foreach (selection,
2301                                          add_bookmark_foreach_cb,
2302                                          impl);
2303 }
2304
2305 /* Callback used when the "Add bookmark" button is clicked */
2306 static void
2307 add_bookmark_button_clicked_cb (GtkButton *button,
2308                                 GtkFileChooserDefault *impl)
2309 {
2310   bookmarks_add_selected_folder (impl);
2311 }
2312
2313 /* Returns TRUE plus an iter in the shortcuts_model if a row is selected;
2314  * returns FALSE if no shortcut is selected.
2315  */
2316 static gboolean
2317 shortcuts_get_selected (GtkFileChooserDefault *impl,
2318                         GtkTreeIter           *iter)
2319 {
2320   GtkTreeSelection *selection;
2321   GtkTreeIter parent_iter;
2322
2323   if (!impl->browse_shortcuts_tree_view)
2324     return FALSE;
2325
2326   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
2327
2328   if (!gtk_tree_selection_get_selected (selection, NULL, &parent_iter))
2329     return FALSE;
2330
2331   gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
2332                                                     iter,
2333                                                     &parent_iter);
2334   return TRUE;
2335 }
2336
2337 /* Removes the selected bookmarks */
2338 static void
2339 remove_selected_bookmarks (GtkFileChooserDefault *impl)
2340 {
2341   GtkTreeIter iter;
2342   gpointer col_data;
2343   GtkFilePath *path;
2344   gboolean removable;
2345   GError *error;
2346
2347   if (!shortcuts_get_selected (impl, &iter))
2348     return;
2349
2350   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2351                       SHORTCUTS_COL_DATA, &col_data,
2352                       SHORTCUTS_COL_REMOVABLE, &removable,
2353                       -1);
2354   g_assert (col_data != NULL);
2355
2356   if (!removable)
2357     return;
2358
2359   path = col_data;
2360
2361   error = NULL;
2362   if (!gtk_file_system_remove_bookmark (impl->file_system, path, &error))
2363     error_removing_bookmark_dialog (impl, path, error);
2364 }
2365
2366 /* Callback used when the "Remove bookmark" button is clicked */
2367 static void
2368 remove_bookmark_button_clicked_cb (GtkButton *button,
2369                                    GtkFileChooserDefault *impl)
2370 {
2371   remove_selected_bookmarks (impl);
2372 }
2373
2374 struct selection_check_closure {
2375   GtkFileChooserDefault *impl;
2376   int num_selected;
2377   gboolean all_files;
2378   gboolean all_folders;
2379 };
2380
2381 /* Used from gtk_tree_selection_selected_foreach() */
2382 static void
2383 selection_check_foreach_cb (GtkTreeModel *model,
2384                             GtkTreePath  *path,
2385                             GtkTreeIter  *iter,
2386                             gpointer      data)
2387 {
2388   struct selection_check_closure *closure;
2389   GtkTreeIter child_iter;
2390   const GtkFileInfo *info;
2391   gboolean is_folder;
2392
2393   closure = data;
2394   closure->num_selected++;
2395
2396   gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
2397
2398   info = _gtk_file_system_model_get_info (closure->impl->browse_files_model, &child_iter);
2399   is_folder = info ? gtk_file_info_get_is_folder (info) : FALSE;
2400
2401   closure->all_folders = closure->all_folders && is_folder;
2402   closure->all_files = closure->all_files && !is_folder;
2403 }
2404
2405 /* Checks whether the selected items in the file list are all files or all folders */
2406 static void
2407 selection_check (GtkFileChooserDefault *impl,
2408                  gint                  *num_selected,
2409                  gboolean              *all_files,
2410                  gboolean              *all_folders)
2411 {
2412   struct selection_check_closure closure;
2413   GtkTreeSelection *selection;
2414
2415   closure.impl = impl;
2416   closure.num_selected = 0;
2417   closure.all_files = TRUE;
2418   closure.all_folders = TRUE;
2419
2420   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2421   gtk_tree_selection_selected_foreach (selection,
2422                                        selection_check_foreach_cb,
2423                                        &closure);
2424
2425   g_assert (closure.num_selected == 0 || !(closure.all_files && closure.all_folders));
2426
2427   if (num_selected)
2428     *num_selected = closure.num_selected;
2429
2430   if (all_files)
2431     *all_files = closure.all_files;
2432
2433   if (all_folders)
2434     *all_folders = closure.all_folders;
2435 }
2436
2437 struct get_selected_path_closure {
2438   GtkFileChooserDefault *impl;
2439   const GtkFilePath *path;
2440 };
2441
2442 static void
2443 get_selected_path_foreach_cb (GtkTreeModel *model,
2444                               GtkTreePath  *path,
2445                               GtkTreeIter  *iter,
2446                               gpointer      data)
2447 {
2448   struct get_selected_path_closure *closure;
2449   GtkTreeIter child_iter;
2450
2451   closure = data;
2452
2453   gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
2454   closure->path = _gtk_file_system_model_get_path (closure->impl->browse_files_model, &child_iter);
2455 }
2456
2457 /* Returns a selected path from the file list */
2458 static const GtkFilePath *
2459 get_selected_path (GtkFileChooserDefault *impl)
2460 {
2461   struct get_selected_path_closure closure;
2462   GtkTreeSelection *selection;
2463
2464   closure.impl = impl;
2465   closure.path = NULL;
2466
2467   selection =  gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2468   gtk_tree_selection_selected_foreach (selection,
2469                                        get_selected_path_foreach_cb,
2470                                        &closure);
2471
2472   return closure.path;
2473 }
2474
2475 typedef struct {
2476   GtkFileChooserDefault *impl;
2477   gchar *tip;
2478 } UpdateTooltipData;
2479
2480 static void 
2481 update_tooltip (GtkTreeModel      *model,
2482                 GtkTreePath       *path,
2483                 GtkTreeIter       *iter,
2484                 gpointer           data)
2485 {
2486   UpdateTooltipData *udata = data;
2487   GtkTreeIter child_iter;
2488   const GtkFileInfo *info;
2489
2490   if (udata->tip == NULL)
2491     {
2492       gtk_tree_model_sort_convert_iter_to_child_iter (udata->impl->sort_model,
2493                                                       &child_iter,
2494                                                       iter);
2495   
2496       info = _gtk_file_system_model_get_info (udata->impl->browse_files_model, &child_iter);
2497       udata->tip = g_strdup_printf (_("Add the folder '%s' to the bookmarks"),
2498                                     gtk_file_info_get_display_name (info));
2499     }
2500 }
2501
2502
2503 /* Sensitize the "add bookmark" button if all the selected items are folders, or
2504  * if there are no selected items *and* the current folder is not in the
2505  * bookmarks list.  De-sensitize the button otherwise.
2506  */
2507 static void
2508 bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl)
2509 {
2510   gint num_selected;
2511   gboolean all_folders;
2512   gboolean active;
2513   gchar *tip;
2514
2515   selection_check (impl, &num_selected, NULL, &all_folders);
2516
2517   if (num_selected == 0)
2518     active = (impl->current_folder != NULL) && (shortcut_find_position (impl, impl->current_folder) == -1);
2519   else if (num_selected == 1)
2520     {
2521       const GtkFilePath *path;
2522
2523       path = get_selected_path (impl);
2524       active = all_folders && (shortcut_find_position (impl, path) == -1);
2525     }
2526   else
2527     active = all_folders;
2528
2529   gtk_widget_set_sensitive (impl->browse_shortcuts_add_button, active);
2530
2531   if (impl->browse_files_popup_menu_add_shortcut_item)
2532     gtk_widget_set_sensitive (impl->browse_files_popup_menu_add_shortcut_item,
2533                               (num_selected == 0) ? FALSE : active);
2534
2535   if (active)
2536     {
2537       if (num_selected == 0)
2538         tip = g_strdup_printf (_("Add the current folder to the bookmarks"));    
2539       else if (num_selected > 1)
2540         tip = g_strdup_printf (_("Add the selected folders to the bookmarks"));
2541       else
2542         {
2543           GtkTreeSelection *selection;
2544           UpdateTooltipData data;
2545           
2546           selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2547           data.impl = impl;
2548           data.tip = NULL;
2549           gtk_tree_selection_selected_foreach (selection, update_tooltip, &data);
2550           tip = data.tip;
2551           
2552         }
2553       gtk_tooltips_set_tip (impl->tooltips, impl->browse_shortcuts_add_button, tip, NULL);
2554       g_free (tip);
2555     }
2556 }
2557
2558 /* Sets the sensitivity of the "remove bookmark" button depending on whether a
2559  * bookmark row is selected in the shortcuts tree.
2560  */
2561 static void
2562 bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl)
2563 {
2564   GtkTreeIter iter;
2565   gboolean removable = FALSE;
2566   gchar *name = NULL;
2567   
2568   if (shortcuts_get_selected (impl, &iter))
2569     gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2570                         SHORTCUTS_COL_REMOVABLE, &removable,
2571                         SHORTCUTS_COL_NAME, &name,
2572                         -1);
2573
2574   gtk_widget_set_sensitive (impl->browse_shortcuts_remove_button, removable);
2575
2576   if (removable)
2577     {
2578       gchar *tip;
2579
2580       tip = g_strdup_printf (_("Remove the bookmark '%s'"), name);
2581       gtk_tooltips_set_tip (impl->tooltips, impl->browse_shortcuts_remove_button,
2582                             tip, NULL);
2583       g_free (tip);
2584     }
2585
2586   g_free (name);
2587 }
2588
2589 static void
2590 shortcuts_check_popup_sensitivity (GtkFileChooserDefault *impl)
2591 {
2592   GtkTreeIter iter;
2593   gboolean removable = FALSE;
2594
2595   if (impl->browse_shortcuts_popup_menu == NULL)
2596     return;
2597
2598   if (shortcuts_get_selected (impl, &iter))
2599     gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2600                         SHORTCUTS_COL_REMOVABLE, &removable,
2601                         -1);
2602
2603   gtk_widget_set_sensitive (impl->browse_shortcuts_popup_menu_remove_item, removable);
2604   gtk_widget_set_sensitive (impl->browse_shortcuts_popup_menu_rename_item, removable);
2605 }
2606
2607 /* GtkWidget::drag-begin handler for the shortcuts list. */
2608 static void
2609 shortcuts_drag_begin_cb (GtkWidget             *widget,
2610                          GdkDragContext        *context,
2611                          GtkFileChooserDefault *impl)
2612 {
2613 #if 0
2614   impl->shortcuts_drag_context = g_object_ref (context);
2615 #endif
2616 }
2617
2618 #if 0
2619 /* Removes the idle handler for outside drags */
2620 static void
2621 shortcuts_cancel_drag_outside_idle (GtkFileChooserDefault *impl)
2622 {
2623   if (!impl->shortcuts_drag_outside_idle)
2624     return;
2625
2626   g_source_destroy (impl->shortcuts_drag_outside_idle);
2627   impl->shortcuts_drag_outside_idle = NULL;
2628 }
2629 #endif
2630
2631 /* GtkWidget::drag-end handler for the shortcuts list. */
2632 static void
2633 shortcuts_drag_end_cb (GtkWidget             *widget,
2634                        GdkDragContext        *context,
2635                        GtkFileChooserDefault *impl)
2636 {
2637 #if 0
2638   g_object_unref (impl->shortcuts_drag_context);
2639
2640   shortcuts_cancel_drag_outside_idle (impl);
2641
2642   if (!impl->shortcuts_drag_outside)
2643     return;
2644
2645   gtk_button_clicked (GTK_BUTTON (impl->browse_shortcuts_remove_button));
2646
2647   impl->shortcuts_drag_outside = FALSE;
2648 #endif
2649 }
2650
2651 /* GtkWidget::drag-data-delete handler for the shortcuts list. */
2652 static void
2653 shortcuts_drag_data_delete_cb (GtkWidget             *widget,
2654                                GdkDragContext        *context,
2655                                GtkFileChooserDefault *impl)
2656 {
2657   g_signal_stop_emission_by_name (widget, "drag_data_delete");
2658 }
2659
2660 #if 0
2661 /* Creates a suitable drag cursor to indicate that the selected bookmark will be
2662  * deleted or not.
2663  */
2664 static void
2665 shortcuts_drag_set_delete_cursor (GtkFileChooserDefault *impl,
2666                                   gboolean               delete)
2667 {
2668   GtkTreeView *tree_view;
2669   GtkTreeIter iter;
2670   GtkTreePath *path;
2671   GdkPixmap *row_pixmap;
2672   GdkBitmap *mask;
2673   int row_pixmap_y;
2674   int cell_y;
2675
2676   tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
2677
2678   /* Find the selected path and get its drag pixmap */
2679
2680   if (!shortcuts_get_selected (impl, &iter))
2681     g_assert_not_reached ();
2682
2683   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
2684
2685   row_pixmap = gtk_tree_view_create_row_drag_icon (tree_view, path);
2686   gtk_tree_path_free (path);
2687
2688   mask = NULL;
2689   row_pixmap_y = 0;
2690
2691   if (delete)
2692     {
2693       GdkPixbuf *pixbuf;
2694
2695       pixbuf = gtk_widget_render_icon (impl->browse_shortcuts_tree_view,
2696                                        GTK_STOCK_DELETE,
2697                                        GTK_ICON_SIZE_DND,
2698                                        NULL);
2699       if (pixbuf)
2700         {
2701           GdkPixmap *composite;
2702           int row_pixmap_width, row_pixmap_height;
2703           int pixbuf_width, pixbuf_height;
2704           int composite_width, composite_height;
2705           int pixbuf_x, pixbuf_y;
2706           GdkGC *gc, *mask_gc;
2707           GdkColor color;
2708           GdkBitmap *pixbuf_mask;
2709
2710           /* Create pixmap and mask for composite image */
2711
2712           gdk_drawable_get_size (row_pixmap, &row_pixmap_width, &row_pixmap_height);
2713           pixbuf_width = gdk_pixbuf_get_width (pixbuf);
2714           pixbuf_height = gdk_pixbuf_get_height (pixbuf);
2715
2716           composite_width = MAX (row_pixmap_width, pixbuf_width);
2717           composite_height = MAX (row_pixmap_height, pixbuf_height);
2718
2719           row_pixmap_y = (composite_height - row_pixmap_height) / 2;
2720
2721           if (gtk_widget_get_direction (impl->browse_shortcuts_tree_view) == GTK_TEXT_DIR_RTL)
2722             pixbuf_x = 0;
2723           else
2724             pixbuf_x = composite_width - pixbuf_width;
2725
2726           pixbuf_y = (composite_height - pixbuf_height) / 2;
2727
2728           composite = gdk_pixmap_new (row_pixmap, composite_width, composite_height, -1);
2729           gc = gdk_gc_new (composite);
2730
2731           mask = gdk_pixmap_new (row_pixmap, composite_width, composite_height, 1);
2732           mask_gc = gdk_gc_new (mask);
2733           color.pixel = 0;
2734           gdk_gc_set_foreground (mask_gc, &color);
2735           gdk_draw_rectangle (mask, mask_gc, TRUE, 0, 0, composite_width, composite_height);
2736
2737           color.red = 0xffff;
2738           color.green = 0xffff;
2739           color.blue = 0xffff;
2740           gdk_gc_set_rgb_fg_color (gc, &color);
2741           gdk_draw_rectangle (composite, gc, TRUE, 0, 0, composite_width, composite_height);
2742
2743           /* Composite the row pixmap and the pixbuf */
2744
2745           gdk_pixbuf_render_pixmap_and_mask_for_colormap
2746             (pixbuf,
2747              gtk_widget_get_colormap (impl->browse_shortcuts_tree_view),
2748              NULL, &pixbuf_mask, 128);
2749           gdk_draw_drawable (mask, mask_gc, pixbuf_mask,
2750                              0, 0,
2751                              pixbuf_x, pixbuf_y,
2752                              pixbuf_width, pixbuf_height);
2753           g_object_unref (pixbuf_mask);
2754
2755           gdk_draw_drawable (composite, gc, row_pixmap,
2756                              0, 0,
2757                              0, row_pixmap_y,
2758                              row_pixmap_width, row_pixmap_height);
2759           color.pixel = 1;
2760           gdk_gc_set_foreground (mask_gc, &color);
2761           gdk_draw_rectangle (mask, mask_gc, TRUE, 0, row_pixmap_y, row_pixmap_width, row_pixmap_height);
2762
2763           gdk_draw_pixbuf (composite, gc, pixbuf,
2764                            0, 0,
2765                            pixbuf_x, pixbuf_y,
2766                            pixbuf_width, pixbuf_height,
2767                            GDK_RGB_DITHER_MAX,
2768                            0, 0);
2769
2770           g_object_unref (pixbuf);
2771           g_object_unref (row_pixmap);
2772
2773           row_pixmap = composite;
2774         }
2775     }
2776
2777   /* The hotspot offsets here are copied from gtk_tree_view_drag_begin(), ugh */
2778
2779   gtk_tree_view_get_path_at_pos (tree_view,
2780                                  tree_view->priv->press_start_x,
2781                                  tree_view->priv->press_start_y,
2782                                  NULL,
2783                                  NULL,
2784                                  NULL,
2785                                  &cell_y);
2786
2787   gtk_drag_set_icon_pixmap (impl->shortcuts_drag_context,
2788                             gdk_drawable_get_colormap (row_pixmap),
2789                             row_pixmap,
2790                             mask,
2791                             tree_view->priv->press_start_x + 1,
2792                             row_pixmap_y + cell_y + 1);
2793
2794   g_object_unref (row_pixmap);
2795   if (mask)
2796     g_object_unref (mask);
2797 }
2798
2799 /* We set the delete cursor and the shortcuts_drag_outside flag in an idle
2800  * handler so that we can tell apart the drag_leave event that comes right
2801  * before a drag_drop, from a normal drag_leave.  We don't want to set the
2802  * cursor nor the flag in the latter case.
2803  */
2804 static gboolean
2805 shortcuts_drag_outside_idle_cb (GtkFileChooserDefault *impl)
2806 {
2807   GDK_THREADS_ENTER ();
2808   
2809   shortcuts_drag_set_delete_cursor (impl, TRUE);
2810   impl->shortcuts_drag_outside = TRUE;
2811
2812   shortcuts_cancel_drag_outside_idle (impl);
2813
2814   GDK_THREADS_LEAVE ();
2815
2816   return FALSE;
2817 }
2818 #endif
2819
2820 /* GtkWidget::drag-leave handler for the shortcuts list.  We unhighlight the
2821  * drop position.
2822  */
2823 static void
2824 shortcuts_drag_leave_cb (GtkWidget             *widget,
2825                          GdkDragContext        *context,
2826                          guint                  time_,
2827                          GtkFileChooserDefault *impl)
2828 {
2829 #if 0
2830   if (gtk_drag_get_source_widget (context) == widget && !impl->shortcuts_drag_outside_idle)
2831     {
2832       impl->shortcuts_drag_outside_idle = g_idle_source_new ();
2833       g_source_set_closure (impl->shortcuts_drag_outside_idle,
2834                             g_cclosure_new_object (G_CALLBACK (shortcuts_drag_outside_idle_cb),
2835                                                    G_OBJECT (impl)));
2836       g_source_attach (impl->shortcuts_drag_outside_idle, NULL);
2837     }
2838 #endif
2839
2840   gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
2841                                    NULL,
2842                                    GTK_TREE_VIEW_DROP_BEFORE);
2843
2844   g_signal_stop_emission_by_name (widget, "drag_leave");
2845 }
2846
2847 /* Computes the appropriate row and position for dropping */
2848 static void
2849 shortcuts_compute_drop_position (GtkFileChooserDefault   *impl,
2850                                  int                      x,
2851                                  int                      y,
2852                                  GtkTreePath            **path,
2853                                  GtkTreeViewDropPosition *pos)
2854 {
2855   GtkTreeView *tree_view;
2856   GtkTreeViewColumn *column;
2857   int cell_y;
2858   GdkRectangle cell;
2859   int row;
2860   int bookmarks_index;
2861
2862   tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
2863
2864   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2865
2866   if (!gtk_tree_view_get_path_at_pos (tree_view,
2867                                       x,
2868                                       y - TREE_VIEW_HEADER_HEIGHT (tree_view),
2869                                       path,
2870                                       &column,
2871                                       NULL,
2872                                       &cell_y))
2873     {
2874       row = bookmarks_index + impl->num_bookmarks - 1;
2875       *path = gtk_tree_path_new_from_indices (row, -1);
2876       *pos = GTK_TREE_VIEW_DROP_AFTER;
2877       return;
2878     }
2879
2880   row = *gtk_tree_path_get_indices (*path);
2881   gtk_tree_view_get_background_area (tree_view, *path, column, &cell);
2882   gtk_tree_path_free (*path);
2883
2884   if (row < bookmarks_index)
2885     {
2886       row = bookmarks_index;
2887       *pos = GTK_TREE_VIEW_DROP_BEFORE;
2888     }
2889   else if (row > bookmarks_index + impl->num_bookmarks - 1)
2890     {
2891       row = bookmarks_index + impl->num_bookmarks - 1;
2892       *pos = GTK_TREE_VIEW_DROP_AFTER;
2893     }
2894   else
2895     {
2896       if (cell_y < cell.height / 2)
2897         *pos = GTK_TREE_VIEW_DROP_BEFORE;
2898       else
2899         *pos = GTK_TREE_VIEW_DROP_AFTER;
2900     }
2901
2902   *path = gtk_tree_path_new_from_indices (row, -1);
2903 }
2904
2905 /* GtkWidget::drag-motion handler for the shortcuts list.  We basically
2906  * implement the destination side of DnD by hand, due to limitations in
2907  * GtkTreeView's DnD API.
2908  */
2909 static gboolean
2910 shortcuts_drag_motion_cb (GtkWidget             *widget,
2911                           GdkDragContext        *context,
2912                           gint                   x,
2913                           gint                   y,
2914                           guint                  time_,
2915                           GtkFileChooserDefault *impl)
2916 {
2917   GtkTreePath *path;
2918   GtkTreeViewDropPosition pos;
2919   GdkDragAction action;
2920
2921 #if 0
2922   if (gtk_drag_get_source_widget (context) == widget)
2923     {
2924       shortcuts_cancel_drag_outside_idle (impl);
2925
2926       if (impl->shortcuts_drag_outside)
2927         {
2928           shortcuts_drag_set_delete_cursor (impl, FALSE);
2929           impl->shortcuts_drag_outside = FALSE;
2930         }
2931     }
2932 #endif
2933
2934   if (context->suggested_action == GDK_ACTION_COPY || (context->actions & GDK_ACTION_COPY) != 0)
2935     action = GDK_ACTION_COPY;
2936   else if (context->suggested_action == GDK_ACTION_MOVE || (context->actions & GDK_ACTION_MOVE) != 0)
2937     action = GDK_ACTION_MOVE;
2938   else
2939     {
2940       action = 0;
2941       goto out;
2942     }
2943
2944   shortcuts_compute_drop_position (impl, x, y, &path, &pos);
2945   gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), path, pos);
2946   gtk_tree_path_free (path);
2947
2948  out:
2949
2950   g_signal_stop_emission_by_name (widget, "drag_motion");
2951
2952   if (action != 0)
2953     {
2954       gdk_drag_status (context, action, time_);
2955       return TRUE;
2956     }
2957   else
2958     return FALSE;
2959 }
2960
2961 /* GtkWidget::drag-drop handler for the shortcuts list. */
2962 static gboolean
2963 shortcuts_drag_drop_cb (GtkWidget             *widget,
2964                         GdkDragContext        *context,
2965                         gint                   x,
2966                         gint                   y,
2967                         guint                  time_,
2968                         GtkFileChooserDefault *impl)
2969 {
2970 #if 0
2971   shortcuts_cancel_drag_outside_idle (impl);
2972 #endif
2973
2974   g_signal_stop_emission_by_name (widget, "drag_drop");
2975   return TRUE;
2976 }
2977
2978 /* Parses a "text/uri-list" string and inserts its URIs as bookmarks */
2979 static void
2980 shortcuts_drop_uris (GtkFileChooserDefault *impl,
2981                      const char            *data,
2982                      int                    position)
2983 {
2984   gchar **uris;
2985   gint i;
2986
2987   uris = g_uri_list_extract_uris (data);
2988
2989   for (i = 0; uris[i]; i++)
2990     {
2991       char *uri;
2992       GtkFilePath *path;
2993
2994       uri = uris[i];
2995       path = gtk_file_system_uri_to_path (impl->file_system, uri);
2996
2997       if (path)
2998         {
2999           if (shortcuts_add_bookmark_from_path (impl, path, position))
3000             position++;
3001
3002           gtk_file_path_free (path);
3003         }
3004       else
3005         {
3006           GError *error;
3007
3008           g_set_error (&error,
3009                        GTK_FILE_CHOOSER_ERROR,
3010                        GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
3011                        _("Could not add a bookmark for '%s' "
3012                          "because it is an invalid path name."),
3013                        uri);
3014           error_adding_bookmark_dialog (impl, path, error);
3015         }
3016     }
3017
3018   g_strfreev (uris);
3019 }
3020
3021 /* Reorders the selected bookmark to the specified position */
3022 static void
3023 shortcuts_reorder (GtkFileChooserDefault *impl,
3024                    int                    new_position)
3025 {
3026   GtkTreeIter iter;
3027   gpointer col_data;
3028   gboolean is_volume;
3029   GtkTreePath *path;
3030   int old_position;
3031   int bookmarks_index;
3032   const GtkFilePath *file_path;
3033   GtkFilePath *file_path_copy;
3034   GError *error;
3035   gchar *name;
3036
3037   /* Get the selected path */
3038
3039   if (!shortcuts_get_selected (impl, &iter))
3040     g_assert_not_reached ();
3041
3042   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
3043   old_position = *gtk_tree_path_get_indices (path);
3044   gtk_tree_path_free (path);
3045
3046   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
3047   old_position -= bookmarks_index;
3048   g_assert (old_position >= 0 && old_position < impl->num_bookmarks);
3049
3050   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
3051                       SHORTCUTS_COL_NAME, &name,
3052                       SHORTCUTS_COL_DATA, &col_data,
3053                       SHORTCUTS_COL_IS_VOLUME, &is_volume,
3054                       -1);
3055   g_assert (col_data != NULL);
3056   g_assert (!is_volume);
3057   
3058   file_path = col_data;
3059   file_path_copy = gtk_file_path_copy (file_path); /* removal below will free file_path, so we need a copy */
3060
3061   /* Remove the path from the old position and insert it in the new one */
3062
3063   if (new_position > old_position)
3064     new_position--;
3065
3066   if (old_position == new_position)
3067     goto out;
3068
3069   error = NULL;
3070   if (gtk_file_system_remove_bookmark (impl->file_system, file_path_copy, &error))
3071     {
3072       shortcuts_add_bookmark_from_path (impl, file_path_copy, new_position);
3073       gtk_file_system_set_bookmark_label (impl->file_system, file_path_copy, name);
3074     }
3075   else
3076     error_adding_bookmark_dialog (impl, file_path_copy, error);
3077
3078  out:
3079
3080   gtk_file_path_free (file_path_copy);
3081 }
3082
3083 /* Callback used when we get the drag data for the bookmarks list.  We add the
3084  * received URIs as bookmarks if they are folders.
3085  */
3086 static void
3087 shortcuts_drag_data_received_cb (GtkWidget          *widget,
3088                                  GdkDragContext     *context,
3089                                  gint                x,
3090                                  gint                y,
3091                                  GtkSelectionData   *selection_data,
3092                                  guint               info,
3093                                  guint               time_,
3094                                  gpointer            data)
3095 {
3096   GtkFileChooserDefault *impl;
3097   GtkTreePath *tree_path;
3098   GtkTreeViewDropPosition tree_pos;
3099   int position;
3100   int bookmarks_index;
3101
3102   impl = GTK_FILE_CHOOSER_DEFAULT (data);
3103
3104   /* Compute position */
3105
3106   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
3107
3108   shortcuts_compute_drop_position (impl, x, y, &tree_path, &tree_pos);
3109   position = *gtk_tree_path_get_indices (tree_path);
3110   gtk_tree_path_free (tree_path);
3111
3112   if (tree_pos == GTK_TREE_VIEW_DROP_AFTER)
3113     position++;
3114
3115   g_assert (position >= bookmarks_index);
3116   position -= bookmarks_index;
3117
3118   if (selection_data->target == gdk_atom_intern_static_string ("text/uri-list"))
3119     shortcuts_drop_uris (impl, (const char *) selection_data->data, position);
3120   else if (selection_data->target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
3121     shortcuts_reorder (impl, position);
3122
3123   g_signal_stop_emission_by_name (widget, "drag_data_received");
3124 }
3125
3126 /* Callback used when the selection in the shortcuts tree changes */
3127 static void
3128 shortcuts_selection_changed_cb (GtkTreeSelection      *selection,
3129                                 GtkFileChooserDefault *impl)
3130 {
3131   bookmarks_check_remove_sensitivity (impl);
3132   shortcuts_check_popup_sensitivity (impl);
3133 }
3134
3135 static gboolean
3136 shortcuts_row_separator_func (GtkTreeModel *model,
3137                               GtkTreeIter  *iter,
3138                               gpointer      data)
3139 {
3140   gint column = GPOINTER_TO_INT (data);
3141   gchar *text;
3142
3143   gtk_tree_model_get (model, iter, column, &text, -1);
3144   
3145   if (!text)
3146     return TRUE;
3147
3148   g_free (text);
3149
3150   return FALSE;
3151 }
3152
3153 /* Since GtkTreeView has a keybinding attached to '/', we need to catch
3154  * keypresses before the TreeView gets them.
3155  */
3156 static gboolean
3157 tree_view_keybinding_cb (GtkWidget             *tree_view,
3158                          GdkEventKey           *event,
3159                          GtkFileChooserDefault *impl)
3160 {
3161   /* FMQ: remove this? */
3162 #if 0
3163   if ((event->keyval == GDK_slash
3164        || event->keyval == GDK_KP_Divide
3165 #ifdef G_OS_UNIX
3166        || event->keyval == GDK_asciitilde
3167 #endif
3168        ) && ! (event->state & (~GDK_SHIFT_MASK & gtk_accelerator_get_default_mod_mask ())))
3169     {
3170       location_popup_handler (impl, event->string);
3171       return TRUE;
3172     }
3173 #endif
3174   return FALSE;
3175 }
3176
3177 /* Callback used when the file list's popup menu is detached */
3178 static void
3179 shortcuts_popup_menu_detach_cb (GtkWidget *attach_widget,
3180                                 GtkMenu   *menu)
3181 {
3182   GtkFileChooserDefault *impl;
3183   
3184   impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
3185   g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
3186
3187   impl->browse_shortcuts_popup_menu = NULL;
3188   impl->browse_shortcuts_popup_menu_remove_item = NULL;
3189   impl->browse_shortcuts_popup_menu_rename_item = NULL;
3190 }
3191
3192 static void
3193 remove_shortcut_cb (GtkMenuItem           *item,
3194                     GtkFileChooserDefault *impl)
3195 {
3196   remove_selected_bookmarks (impl);
3197 }
3198
3199 /* Rename the selected bookmark */
3200 static void
3201 rename_selected_bookmark (GtkFileChooserDefault *impl)
3202 {
3203   GtkTreeIter iter;
3204   GtkTreePath *path;
3205   GtkTreeViewColumn *column;
3206   GtkCellRenderer *cell;
3207   GList *renderers;
3208
3209   if (shortcuts_get_selected (impl, &iter))
3210     {
3211       path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
3212       column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), 0);
3213       renderers = gtk_tree_view_column_get_cell_renderers (column);
3214       cell = g_list_nth_data (renderers, 1);
3215       g_list_free (renderers);
3216       g_object_set (cell, "editable", TRUE, NULL);
3217       gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3218                                         path, column, cell, TRUE);
3219       gtk_tree_path_free (path);
3220     }
3221 }
3222
3223 static void
3224 rename_shortcut_cb (GtkMenuItem           *item,
3225                     GtkFileChooserDefault *impl)
3226 {
3227   rename_selected_bookmark (impl);
3228 }
3229
3230 /* Constructs the popup menu for the file list if needed */
3231 static void
3232 shortcuts_build_popup_menu (GtkFileChooserDefault *impl)
3233 {
3234   GtkWidget *item;
3235
3236   if (impl->browse_shortcuts_popup_menu)
3237     return;
3238
3239   impl->browse_shortcuts_popup_menu = gtk_menu_new ();
3240   gtk_menu_attach_to_widget (GTK_MENU (impl->browse_shortcuts_popup_menu),
3241                              impl->browse_shortcuts_tree_view,
3242                              shortcuts_popup_menu_detach_cb);
3243
3244   item = gtk_image_menu_item_new_with_label (_("Remove"));
3245   impl->browse_shortcuts_popup_menu_remove_item = item;
3246   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
3247                                  gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU));
3248   g_signal_connect (item, "activate",
3249                     G_CALLBACK (remove_shortcut_cb), impl);
3250   gtk_widget_show (item);
3251   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu), item);
3252
3253   item = gtk_menu_item_new_with_label (_("Rename..."));
3254   impl->browse_shortcuts_popup_menu_rename_item = item;
3255   g_signal_connect (item, "activate",
3256                     G_CALLBACK (rename_shortcut_cb), impl);
3257   gtk_widget_show (item);
3258   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu), item);
3259
3260   shortcuts_check_popup_sensitivity (impl);
3261 }
3262
3263 static void
3264 shortcuts_update_popup_menu (GtkFileChooserDefault *impl)
3265 {
3266   shortcuts_build_popup_menu (impl);  
3267 }
3268
3269 static void
3270 popup_position_func (GtkMenu   *menu,
3271                      gint      *x,
3272                      gint      *y,
3273                      gboolean  *push_in,
3274                      gpointer   user_data);
3275
3276 static void
3277 shortcuts_popup_menu (GtkFileChooserDefault *impl,
3278                       GdkEventButton        *event)
3279 {
3280   shortcuts_update_popup_menu (impl);
3281   if (event)
3282     gtk_menu_popup (GTK_MENU (impl->browse_shortcuts_popup_menu),
3283                     NULL, NULL, NULL, NULL,
3284                     event->button, event->time);
3285   else
3286     {
3287       gtk_menu_popup (GTK_MENU (impl->browse_shortcuts_popup_menu),
3288                       NULL, NULL,
3289                       popup_position_func, impl->browse_shortcuts_tree_view,
3290                       0, GDK_CURRENT_TIME);
3291       gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu),
3292                                    FALSE);
3293     }
3294 }
3295
3296 /* Callback used for the GtkWidget::popup-menu signal of the shortcuts list */
3297 static gboolean
3298 shortcuts_popup_menu_cb (GtkWidget *widget,
3299                          GtkFileChooserDefault *impl)
3300 {
3301   shortcuts_popup_menu (impl, NULL);
3302   return TRUE;
3303 }
3304
3305 /* Callback used when a button is pressed on the shortcuts list.  
3306  * We trap button 3 to bring up a popup menu.
3307  */
3308 static gboolean
3309 shortcuts_button_press_event_cb (GtkWidget             *widget,
3310                                  GdkEventButton        *event,
3311                                  GtkFileChooserDefault *impl)
3312 {
3313   static gboolean in_press = FALSE;
3314   gboolean handled;
3315
3316   if (in_press)
3317     return FALSE;
3318
3319   if (event->button != 3)
3320     return FALSE;
3321
3322   in_press = TRUE;
3323   handled = gtk_widget_event (impl->browse_shortcuts_tree_view, (GdkEvent *) event);
3324   in_press = FALSE;
3325
3326   if (!handled)
3327     return FALSE;
3328
3329   shortcuts_popup_menu (impl, event);
3330   return TRUE;
3331 }
3332
3333 static void
3334 shortcuts_edited (GtkCellRenderer       *cell,
3335                   gchar                 *path_string,
3336                   gchar                 *new_text,
3337                   GtkFileChooserDefault *impl)
3338 {
3339   GtkTreePath *path;
3340   GtkTreeIter iter;
3341   GtkFilePath *shortcut;
3342
3343   g_object_set (cell, "editable", FALSE, NULL);
3344
3345   path = gtk_tree_path_new_from_string (path_string);
3346   if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
3347     g_assert_not_reached ();
3348
3349   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
3350                       SHORTCUTS_COL_DATA, &shortcut,
3351                       -1);
3352   gtk_tree_path_free (path);
3353   
3354   gtk_file_system_set_bookmark_label (impl->file_system, shortcut, new_text);
3355 }
3356
3357 static void
3358 shortcuts_editing_canceled (GtkCellRenderer       *cell,
3359                             GtkFileChooserDefault *impl)
3360 {
3361   g_object_set (cell, "editable", FALSE, NULL);
3362 }
3363
3364 /* Creates the widgets for the shortcuts and bookmarks tree */
3365 static GtkWidget *
3366 shortcuts_list_create (GtkFileChooserDefault *impl)
3367 {
3368   GtkWidget *swin;
3369   GtkTreeSelection *selection;
3370   GtkTreeViewColumn *column;
3371   GtkCellRenderer *renderer;
3372
3373   /* Scrolled window */
3374
3375   swin = gtk_scrolled_window_new (NULL, NULL);
3376   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
3377                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3378   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
3379                                        GTK_SHADOW_IN);
3380   gtk_widget_show (swin);
3381
3382   /* Tree */
3383
3384   impl->browse_shortcuts_tree_view = gtk_tree_view_new ();
3385 #ifdef PROFILE_FILE_CHOOSER
3386   g_object_set_data (G_OBJECT (impl->browse_shortcuts_tree_view), "fmq-name", "shortcuts");
3387 #endif
3388   g_signal_connect (impl->browse_shortcuts_tree_view, "key_press_event",
3389                     G_CALLBACK (tree_view_keybinding_cb), impl);
3390   g_signal_connect (impl->browse_shortcuts_tree_view, "popup_menu",
3391                     G_CALLBACK (shortcuts_popup_menu_cb), impl);
3392   g_signal_connect (impl->browse_shortcuts_tree_view, "button_press_event",
3393                     G_CALLBACK (shortcuts_button_press_event_cb), impl);
3394   /* Accessible object name for the file chooser's shortcuts pane */
3395   atk_object_set_name (gtk_widget_get_accessible (impl->browse_shortcuts_tree_view), _("Places"));
3396
3397   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), impl->shortcuts_filter_model);
3398
3399   gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3400                                           GDK_BUTTON1_MASK,
3401                                           shortcuts_source_targets,
3402                                           num_shortcuts_source_targets,
3403                                           GDK_ACTION_MOVE);
3404
3405   gtk_drag_dest_set (impl->browse_shortcuts_tree_view,
3406                      GTK_DEST_DEFAULT_ALL,
3407                      shortcuts_dest_targets,
3408                      num_shortcuts_dest_targets,
3409                      GDK_ACTION_COPY | GDK_ACTION_MOVE);
3410
3411   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
3412   gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
3413   gtk_tree_selection_set_select_function (selection,
3414                                           shortcuts_select_func,
3415                                           impl, NULL);
3416
3417   g_signal_connect (selection, "changed",
3418                     G_CALLBACK (shortcuts_selection_changed_cb), impl);
3419
3420   g_signal_connect (impl->browse_shortcuts_tree_view, "row_activated",
3421                     G_CALLBACK (shortcuts_row_activated_cb), impl);
3422
3423   g_signal_connect (impl->browse_shortcuts_tree_view, "key_press_event",
3424                     G_CALLBACK (shortcuts_key_press_event_cb), impl);
3425
3426   g_signal_connect (impl->browse_shortcuts_tree_view, "drag_begin",
3427                     G_CALLBACK (shortcuts_drag_begin_cb), impl);
3428   g_signal_connect (impl->browse_shortcuts_tree_view, "drag_end",
3429                     G_CALLBACK (shortcuts_drag_end_cb), impl);
3430   g_signal_connect (impl->browse_shortcuts_tree_view, "drag_data_delete",
3431                     G_CALLBACK (shortcuts_drag_data_delete_cb), impl);
3432
3433   g_signal_connect (impl->browse_shortcuts_tree_view, "drag_leave",
3434                     G_CALLBACK (shortcuts_drag_leave_cb), impl);
3435   g_signal_connect (impl->browse_shortcuts_tree_view, "drag_motion",
3436                     G_CALLBACK (shortcuts_drag_motion_cb), impl);
3437   g_signal_connect (impl->browse_shortcuts_tree_view, "drag_drop",
3438                     G_CALLBACK (shortcuts_drag_drop_cb), impl);
3439   g_signal_connect (impl->browse_shortcuts_tree_view, "drag_data_received",
3440                     G_CALLBACK (shortcuts_drag_data_received_cb), impl);
3441
3442   gtk_container_add (GTK_CONTAINER (swin), impl->browse_shortcuts_tree_view);
3443   gtk_widget_show (impl->browse_shortcuts_tree_view);
3444
3445   /* Column */
3446
3447   column = gtk_tree_view_column_new ();
3448   /* Column header for the file chooser's shortcuts pane */
3449   gtk_tree_view_column_set_title (column, _("_Places"));
3450
3451   renderer = gtk_cell_renderer_pixbuf_new ();
3452   gtk_tree_view_column_pack_start (column, renderer, FALSE);
3453   gtk_tree_view_column_set_attributes (column, renderer,
3454                                        "pixbuf", SHORTCUTS_COL_PIXBUF,
3455                                        "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
3456                                        NULL);
3457
3458   renderer = gtk_cell_renderer_text_new ();
3459   g_signal_connect (renderer, "edited", 
3460                     G_CALLBACK (shortcuts_edited), impl);
3461   g_signal_connect (renderer, "editing-canceled", 
3462                     G_CALLBACK (shortcuts_editing_canceled), impl);
3463   gtk_tree_view_column_pack_start (column, renderer, TRUE);
3464   gtk_tree_view_column_set_attributes (column, renderer,
3465                                        "text", SHORTCUTS_COL_NAME,
3466                                        NULL);
3467
3468   gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3469                                         shortcuts_row_separator_func,
3470                                         GINT_TO_POINTER (SHORTCUTS_COL_NAME),
3471                                         NULL);
3472
3473   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), column);
3474
3475   return swin;
3476 }
3477
3478 /* Creates the widgets for the shortcuts/bookmarks pane */
3479 static GtkWidget *
3480 shortcuts_pane_create (GtkFileChooserDefault *impl,
3481                        GtkSizeGroup          *size_group)
3482 {
3483   GtkWidget *vbox;
3484   GtkWidget *hbox;
3485   GtkWidget *widget;
3486
3487   vbox = gtk_vbox_new (FALSE, 6);
3488   gtk_widget_show (vbox);
3489
3490   /* Shortcuts tree */
3491
3492   widget = shortcuts_list_create (impl);
3493   gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
3494
3495   /* Box for buttons */
3496
3497   hbox = gtk_hbox_new (TRUE, 6);
3498   gtk_size_group_add_widget (size_group, hbox);
3499   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
3500   gtk_widget_show (hbox);
3501
3502   /* Add bookmark button */
3503
3504   impl->browse_shortcuts_add_button = button_new (impl,
3505                                                   _("_Add"),
3506                                                   GTK_STOCK_ADD,
3507                                                   FALSE,
3508                                                   TRUE,
3509                                                   G_CALLBACK (add_bookmark_button_clicked_cb));
3510   gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_add_button, TRUE, TRUE, 0);
3511   gtk_tooltips_set_tip (impl->tooltips, impl->browse_shortcuts_add_button,
3512                         _("Add the selected folder to the Bookmarks"), NULL);
3513
3514   /* Remove bookmark button */
3515
3516   impl->browse_shortcuts_remove_button = button_new (impl,
3517                                                      _("_Remove"),
3518                                                      GTK_STOCK_REMOVE,
3519                                                      FALSE,
3520                                                      TRUE,
3521                                                      G_CALLBACK (remove_bookmark_button_clicked_cb));
3522   gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_remove_button, TRUE, TRUE, 0);
3523   gtk_tooltips_set_tip (impl->tooltips, impl->browse_shortcuts_remove_button,
3524                         _("Remove the selected bookmark"), NULL);
3525
3526   return vbox;
3527 }
3528
3529 /* Handles key press events on the file list, so that we can trap Enter to
3530  * activate the default button on our own.  Also, checks to see if '/' has been
3531  * pressed.  See comment by tree_view_keybinding_cb() for more details.
3532  */
3533 static gboolean
3534 trap_activate_cb (GtkWidget   *widget,
3535                   GdkEventKey *event,
3536                   gpointer     data)
3537 {
3538   GtkFileChooserDefault *impl;
3539   int modifiers;
3540
3541   impl = (GtkFileChooserDefault *) data;
3542
3543   modifiers = gtk_accelerator_get_default_mod_mask ();
3544
3545   /* FMQ: remove this? */
3546 #if 0
3547   if ((event->keyval == GDK_slash
3548        || event->keyval == GDK_KP_Divide
3549 #ifdef G_OS_UNIX
3550        || event->keyval == GDK_asciitilde
3551 #endif
3552        ) && ! (event->state & (~GDK_SHIFT_MASK & modifiers)))
3553     {
3554       location_popup_handler (impl, event->string);
3555       return TRUE;
3556     }
3557 #endif
3558
3559   if ((event->keyval == GDK_Return
3560        || event->keyval == GDK_ISO_Enter
3561        || event->keyval == GDK_KP_Enter
3562        || event->keyval == GDK_space)
3563       && ((event->state & modifiers) == 0)
3564       && !(impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
3565            impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
3566     {
3567       GtkWindow *window;
3568
3569       window = get_toplevel (widget);
3570       if (window
3571           && widget != window->default_widget
3572           && !(widget == window->focus_widget &&
3573                (!window->default_widget || !GTK_WIDGET_SENSITIVE (window->default_widget))))
3574         {
3575           gtk_window_activate_default (window);
3576           return TRUE;
3577         }
3578     }
3579
3580   return FALSE;
3581 }
3582
3583 /* Callback used when the file list's popup menu is detached */
3584 static void
3585 popup_menu_detach_cb (GtkWidget *attach_widget,
3586                       GtkMenu   *menu)
3587 {
3588   GtkFileChooserDefault *impl;
3589
3590   impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
3591   g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
3592
3593   impl->browse_files_popup_menu = NULL;
3594   impl->browse_files_popup_menu_add_shortcut_item = NULL;
3595   impl->browse_files_popup_menu_hidden_files_item = NULL;
3596 }
3597
3598 /* Callback used when the "Add to Bookmarks" menu item is activated */
3599 static void
3600 add_to_shortcuts_cb (GtkMenuItem           *item,
3601                      GtkFileChooserDefault *impl)
3602 {
3603   bookmarks_add_selected_folder (impl);
3604 }
3605
3606 /* Callback used when the "Show Hidden Files" menu item is toggled */
3607 static void
3608 show_hidden_toggled_cb (GtkCheckMenuItem      *item,
3609                         GtkFileChooserDefault *impl)
3610 {
3611   g_object_set (impl,
3612                 "show-hidden", gtk_check_menu_item_get_active (item),
3613                 NULL);
3614 }
3615
3616 /* Shows an error dialog about not being able to select a dragged file */
3617 static void
3618 error_selecting_dragged_file_dialog (GtkFileChooserDefault *impl,
3619                                      const GtkFilePath     *path,
3620                                      GError                *error)
3621 {
3622   error_dialog (impl,
3623                 _("Could not select file"),
3624                 path, error);
3625 }
3626
3627 static void
3628 file_list_drag_data_select_uris (GtkFileChooserDefault  *impl,
3629                                  gchar                 **uris)
3630 {
3631   int i;
3632   char *uri;
3633   GtkFileChooser *chooser = GTK_FILE_CHOOSER (impl);
3634
3635   for (i = 1; uris[i]; i++)
3636     {
3637       GtkFilePath *path;
3638
3639       uri = uris[i];
3640       path = gtk_file_system_uri_to_path (impl->file_system, uri);
3641
3642       if (path)
3643         {
3644           GError *error = NULL;
3645
3646           gtk_file_chooser_default_select_path (chooser, path, &error);
3647           if (error)
3648             error_selecting_dragged_file_dialog (impl, path, error);
3649
3650           gtk_file_path_free (path);
3651         }
3652     }
3653 }
3654
3655 struct FileListDragData
3656 {
3657   GtkFileChooserDefault *impl;
3658   gchar **uris;
3659   GtkFilePath *path;
3660 };
3661
3662 static void
3663 file_list_drag_data_received_get_info_cb (GtkFileSystemHandle *handle,
3664                                           const GtkFileInfo   *info,
3665                                           const GError        *error,
3666                                           gpointer             user_data)
3667 {
3668   gboolean cancelled = handle->cancelled;
3669   struct FileListDragData *data = user_data;
3670   GtkFileChooser *chooser = GTK_FILE_CHOOSER (data->impl);
3671
3672   if (handle != data->impl->file_list_drag_data_received_handle)
3673     goto out;
3674
3675   data->impl->file_list_drag_data_received_handle = NULL;
3676
3677   if (cancelled || error)
3678     goto out;
3679
3680   if ((data->impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
3681        data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) &&
3682       data->uris[1] == 0 && !error &&
3683       gtk_file_info_get_is_folder (info))
3684     change_folder_and_display_error (data->impl, data->path);
3685   else
3686     {
3687       GError *error = NULL;
3688
3689       gtk_file_chooser_default_unselect_all (chooser);
3690       gtk_file_chooser_default_select_path (chooser, data->path, &error);
3691       if (error)
3692         error_selecting_dragged_file_dialog (data->impl, data->path, error);
3693       else
3694         browse_files_center_selected_row (data->impl);
3695     }
3696
3697   if (data->impl->select_multiple)
3698     file_list_drag_data_select_uris (data->impl, data->uris);
3699
3700 out:
3701   g_object_unref (data->impl);
3702   g_strfreev (data->uris);
3703   gtk_file_path_free (data->path);
3704   g_free (data);
3705
3706   g_object_unref (handle);
3707 }
3708
3709 static void
3710 file_list_drag_data_received_cb (GtkWidget          *widget,
3711                                  GdkDragContext     *context,
3712                                  gint                x,
3713                                  gint                y,
3714                                  GtkSelectionData   *selection_data,
3715                                  guint               info,
3716                                  guint               time_,
3717                                  gpointer            data)
3718 {
3719   GtkFileChooserDefault *impl;
3720   GtkFileChooser *chooser;
3721   gchar **uris;
3722   char *uri;
3723   GtkFilePath *path;
3724   GError *error = NULL;
3725  
3726   impl = GTK_FILE_CHOOSER_DEFAULT (data);
3727   chooser = GTK_FILE_CHOOSER (data);
3728   
3729   /* Parse the text/uri-list string, navigate to the first one */
3730   uris = g_uri_list_extract_uris ((const char *) selection_data->data);
3731   if (uris[0]) 
3732     {
3733       uri = uris[0];
3734       path = gtk_file_system_uri_to_path (impl->file_system, uri);
3735       
3736       if (path)
3737         {
3738           struct FileListDragData *data;
3739
3740           data = g_new0 (struct FileListDragData, 1);
3741           data->impl = g_object_ref (impl);
3742           data->uris = uris;
3743           data->path = path;
3744
3745           if (impl->file_list_drag_data_received_handle)
3746             gtk_file_system_cancel_operation (impl->file_list_drag_data_received_handle);
3747
3748           impl->file_list_drag_data_received_handle =
3749             gtk_file_system_get_info (impl->file_system, path,
3750                                       GTK_FILE_INFO_IS_FOLDER,
3751                                       file_list_drag_data_received_get_info_cb,
3752                                       data);
3753           goto out;
3754         }
3755       else
3756         {
3757           g_set_error (&error,
3758                        GTK_FILE_CHOOSER_ERROR,
3759                        GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
3760                        _("Could not select file '%s' "
3761                          "because it is an invalid path name."),
3762                        uri);
3763           error_selecting_dragged_file_dialog (impl, NULL, error);
3764         }
3765
3766       if (impl->select_multiple)
3767         file_list_drag_data_select_uris (impl, uris);
3768     }
3769
3770   g_strfreev (uris);
3771
3772 out:
3773   g_signal_stop_emission_by_name (widget, "drag_data_received");
3774 }
3775
3776 /* Don't do anything with the drag_drop signal */
3777 static gboolean
3778 file_list_drag_drop_cb (GtkWidget             *widget,
3779                         GdkDragContext        *context,
3780                         gint                   x,
3781                         gint                   y,
3782                         guint                  time_,
3783                         GtkFileChooserDefault *impl)
3784 {
3785   g_signal_stop_emission_by_name (widget, "drag_drop");
3786   return TRUE;
3787 }
3788
3789 /* Disable the normal tree drag motion handler, it makes it look like you're
3790    dropping the dragged item onto a tree item */
3791 static gboolean
3792 file_list_drag_motion_cb (GtkWidget             *widget,
3793                           GdkDragContext        *context,
3794                           gint                   x,
3795                           gint                   y,
3796                           guint                  time_,
3797                           GtkFileChooserDefault *impl)
3798 {
3799   g_signal_stop_emission_by_name (widget, "drag_motion");
3800   return TRUE;
3801 }
3802
3803 /* Constructs the popup menu for the file list if needed */
3804 static void
3805 file_list_build_popup_menu (GtkFileChooserDefault *impl)
3806 {
3807   GtkWidget *item;
3808
3809   if (impl->browse_files_popup_menu)
3810     return;
3811
3812   impl->browse_files_popup_menu = gtk_menu_new ();
3813   gtk_menu_attach_to_widget (GTK_MENU (impl->browse_files_popup_menu),
3814                              impl->browse_files_tree_view,
3815                              popup_menu_detach_cb);
3816
3817   item = gtk_image_menu_item_new_with_mnemonic (_("_Add to Bookmarks"));
3818   impl->browse_files_popup_menu_add_shortcut_item = item;
3819   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
3820                                  gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU));
3821   gtk_widget_set_sensitive (item, FALSE);
3822   g_signal_connect (item, "activate",
3823                     G_CALLBACK (add_to_shortcuts_cb), impl);
3824   gtk_widget_show (item);
3825   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
3826
3827   item = gtk_separator_menu_item_new ();
3828   gtk_widget_show (item);
3829   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
3830
3831   item = gtk_check_menu_item_new_with_mnemonic (_("Show _Hidden Files"));
3832   impl->browse_files_popup_menu_hidden_files_item = item;
3833   g_signal_connect (item, "toggled",
3834                     G_CALLBACK (show_hidden_toggled_cb), impl);
3835   gtk_widget_show (item);
3836   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
3837 }
3838
3839 /* Updates the popup menu for the file list, creating it if necessary */
3840 static void
3841 file_list_update_popup_menu (GtkFileChooserDefault *impl)
3842 {
3843   file_list_build_popup_menu (impl);
3844
3845   /* The sensitivity of the Add to Bookmarks item is set in
3846    * bookmarks_check_add_sensitivity()
3847    */
3848
3849   g_signal_handlers_block_by_func (impl->browse_files_popup_menu_hidden_files_item,
3850                                    G_CALLBACK (show_hidden_toggled_cb), impl);
3851   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (impl->browse_files_popup_menu_hidden_files_item),
3852                                   impl->show_hidden);
3853   g_signal_handlers_unblock_by_func (impl->browse_files_popup_menu_hidden_files_item,
3854                                      G_CALLBACK (show_hidden_toggled_cb), impl);
3855 }
3856
3857 static void
3858 popup_position_func (GtkMenu   *menu,
3859                      gint      *x,
3860                      gint      *y,
3861                      gboolean  *push_in,
3862                      gpointer   user_data)
3863 {
3864   GtkWidget *widget = GTK_WIDGET (user_data);
3865   GdkScreen *screen = gtk_widget_get_screen (widget);
3866   GtkRequisition req;
3867   gint monitor_num;
3868   GdkRectangle monitor;
3869
3870   g_return_if_fail (GTK_WIDGET_REALIZED (widget));
3871
3872   gdk_window_get_origin (widget->window, x, y);
3873
3874   gtk_widget_size_request (GTK_WIDGET (menu), &req);
3875
3876   *x += (widget->allocation.width - req.width) / 2;
3877   *y += (widget->allocation.height - req.height) / 2;
3878
3879   monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
3880   gtk_menu_set_monitor (menu, monitor_num);
3881   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
3882
3883   *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
3884   *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
3885
3886   *push_in = FALSE;
3887 }
3888
3889 static void
3890 file_list_popup_menu (GtkFileChooserDefault *impl,
3891                       GdkEventButton        *event)
3892 {
3893   file_list_update_popup_menu (impl);
3894   if (event)
3895     gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
3896                     NULL, NULL, NULL, NULL,
3897                     event->button, event->time);
3898   else
3899     {
3900       gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
3901                       NULL, NULL,
3902                       popup_position_func, impl->browse_files_tree_view,
3903                       0, GDK_CURRENT_TIME);
3904       gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->browse_files_popup_menu),
3905                                    FALSE);
3906     }
3907
3908 }
3909
3910 /* Callback used for the GtkWidget::popup-menu signal of the file list */
3911 static gboolean
3912 list_popup_menu_cb (GtkWidget *widget,
3913                     GtkFileChooserDefault *impl)
3914 {
3915   file_list_popup_menu (impl, NULL);
3916   return TRUE;
3917 }
3918
3919 /* Callback used when a button is pressed on the file list.  We trap button 3 to
3920  * bring up a popup menu.
3921  */
3922 static gboolean
3923 list_button_press_event_cb (GtkWidget             *widget,
3924                             GdkEventButton        *event,
3925                             GtkFileChooserDefault *impl)
3926 {
3927   static gboolean in_press = FALSE;
3928   gboolean handled;
3929
3930   if (in_press)
3931     return FALSE;
3932
3933   if (event->button != 3)
3934     return FALSE;
3935
3936   in_press = TRUE;
3937   handled = gtk_widget_event (impl->browse_files_tree_view, (GdkEvent *) event);
3938   in_press = FALSE;
3939
3940   file_list_popup_menu (impl, event);
3941   return TRUE;
3942 }
3943
3944 /* Creates the widgets for the file list */
3945 static GtkWidget *
3946 create_file_list (GtkFileChooserDefault *impl)
3947 {
3948   GtkWidget *swin;
3949   GtkTreeSelection *selection;
3950   GtkTreeViewColumn *column;
3951   GtkCellRenderer *renderer;
3952
3953   /* Scrolled window */
3954
3955   swin = gtk_scrolled_window_new (NULL, NULL);
3956   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
3957                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3958   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
3959                                        GTK_SHADOW_IN);
3960
3961   /* Tree/list view */
3962
3963   impl->browse_files_tree_view = gtk_tree_view_new ();
3964 #ifdef PROFILE_FILE_CHOOSER
3965   g_object_set_data (G_OBJECT (impl->browse_files_tree_view), "fmq-name", "file_list");
3966 #endif
3967   g_object_set_data (G_OBJECT (impl->browse_files_tree_view), I_("GtkFileChooserDefault"), impl);
3968   atk_object_set_name (gtk_widget_get_accessible (impl->browse_files_tree_view), _("Files"));
3969
3970   gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->browse_files_tree_view), TRUE);
3971   gtk_container_add (GTK_CONTAINER (swin), impl->browse_files_tree_view);
3972
3973   gtk_drag_dest_set (impl->browse_files_tree_view,
3974                      GTK_DEST_DEFAULT_ALL,
3975                      file_list_dest_targets,
3976                      num_file_list_dest_targets,
3977                      GDK_ACTION_COPY | GDK_ACTION_MOVE);
3978   
3979   g_signal_connect (impl->browse_files_tree_view, "row_activated",
3980                     G_CALLBACK (list_row_activated), impl);
3981   g_signal_connect (impl->browse_files_tree_view, "key_press_event",
3982                     G_CALLBACK (trap_activate_cb), impl);
3983   g_signal_connect (impl->browse_files_tree_view, "popup_menu",
3984                     G_CALLBACK (list_popup_menu_cb), impl);
3985   g_signal_connect (impl->browse_files_tree_view, "button_press_event",
3986                     G_CALLBACK (list_button_press_event_cb), impl);
3987
3988   g_signal_connect (impl->browse_files_tree_view, "drag_data_received",
3989                     G_CALLBACK (file_list_drag_data_received_cb), impl);
3990   g_signal_connect (impl->browse_files_tree_view, "drag_drop",
3991                     G_CALLBACK (file_list_drag_drop_cb), impl);
3992   g_signal_connect (impl->browse_files_tree_view, "drag_motion",
3993                     G_CALLBACK (file_list_drag_motion_cb), impl);
3994
3995   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3996   gtk_tree_selection_set_select_function (selection,
3997                                           list_select_func,
3998                                           impl, NULL);
3999   gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_files_tree_view),
4000                                           GDK_BUTTON1_MASK,
4001                                           file_list_source_targets,
4002                                           num_file_list_source_targets,
4003                                           GDK_ACTION_COPY);
4004
4005   g_signal_connect (selection, "changed",
4006                     G_CALLBACK (list_selection_changed), impl);
4007
4008   /* Filename column */
4009
4010   impl->list_name_column = gtk_tree_view_column_new ();
4011   gtk_tree_view_column_set_expand (impl->list_name_column, TRUE);
4012   gtk_tree_view_column_set_resizable (impl->list_name_column, TRUE);
4013   gtk_tree_view_column_set_title (impl->list_name_column, _("Name"));
4014   gtk_tree_view_column_set_sort_column_id (impl->list_name_column, FILE_LIST_COL_NAME);
4015
4016   renderer = gtk_cell_renderer_pixbuf_new ();
4017   gtk_tree_view_column_pack_start (impl->list_name_column, renderer, FALSE);
4018   gtk_tree_view_column_set_cell_data_func (impl->list_name_column, renderer,
4019                                            list_icon_data_func, impl, NULL);
4020
4021   impl->list_name_renderer = gtk_cell_renderer_text_new ();
4022   g_object_set (impl->list_name_renderer,
4023                 "ellipsize", PANGO_ELLIPSIZE_END,
4024                 NULL);
4025   g_signal_connect (impl->list_name_renderer, "edited",
4026                     G_CALLBACK (renderer_edited_cb), impl);
4027   g_signal_connect (impl->list_name_renderer, "editing_canceled",
4028                     G_CALLBACK (renderer_editing_canceled_cb), impl);
4029   gtk_tree_view_column_pack_start (impl->list_name_column, impl->list_name_renderer, TRUE);
4030   gtk_tree_view_column_set_cell_data_func (impl->list_name_column, impl->list_name_renderer,
4031                                            list_name_data_func, impl, NULL);
4032
4033   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), impl->list_name_column);
4034 #if 0
4035   /* Size column */
4036
4037   column = gtk_tree_view_column_new ();
4038   gtk_tree_view_column_set_title (column, _("Size"));
4039
4040   renderer = gtk_cell_renderer_text_new ();
4041   gtk_tree_view_column_pack_start (column, renderer, TRUE); /* bug: it doesn't expand */
4042   gtk_tree_view_column_set_cell_data_func (column, renderer,
4043                                            list_size_data_func, impl, NULL);
4044   gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_SIZE);
4045   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
4046 #endif
4047   /* Modification time column */
4048
4049   column = gtk_tree_view_column_new ();
4050   gtk_tree_view_column_set_resizable (column, TRUE);
4051   gtk_tree_view_column_set_title (column, _("Modified"));
4052
4053   renderer = gtk_cell_renderer_text_new ();
4054   gtk_tree_view_column_pack_start (column, renderer, TRUE);
4055   gtk_tree_view_column_set_cell_data_func (column, renderer,
4056                                            list_mtime_data_func, impl, NULL);
4057   gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_MTIME);
4058   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
4059   gtk_widget_show_all (swin);
4060
4061   return swin;
4062 }
4063
4064 static GtkWidget *
4065 create_path_bar (GtkFileChooserDefault *impl)
4066 {
4067   GtkWidget *path_bar;
4068
4069   path_bar = g_object_new (GTK_TYPE_PATH_BAR, NULL);
4070   _gtk_path_bar_set_file_system (GTK_PATH_BAR (path_bar), impl->file_system);
4071
4072   return path_bar;
4073 }
4074
4075 static void
4076 set_filter_tooltip (GtkWidget *widget, 
4077                     gpointer   data)
4078 {
4079   GtkTooltips *tooltips = (GtkTooltips *)data;
4080
4081   if (GTK_IS_BUTTON (widget))
4082     gtk_tooltips_set_tip (tooltips, widget,
4083                           _("Select which types of files are shown"), 
4084                           NULL);
4085 }
4086
4087 static void
4088 realize_filter_combo (GtkWidget *combo,
4089                       gpointer   data)
4090 {
4091   GtkFileChooserDefault *impl = (GtkFileChooserDefault *)data;
4092
4093   gtk_container_forall (GTK_CONTAINER (combo),
4094                         set_filter_tooltip,
4095                         impl->tooltips);
4096 }
4097
4098 /* Creates the widgets for the files/folders pane */
4099 static GtkWidget *
4100 file_pane_create (GtkFileChooserDefault *impl,
4101                   GtkSizeGroup          *size_group)
4102 {
4103   GtkWidget *vbox;
4104   GtkWidget *hbox;
4105   GtkWidget *widget;
4106
4107   vbox = gtk_vbox_new (FALSE, 6);
4108   gtk_widget_show (vbox);
4109
4110   /* Box for lists and preview */
4111
4112   hbox = gtk_hbox_new (FALSE, PREVIEW_HBOX_SPACING);
4113   gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
4114   gtk_widget_show (hbox);
4115
4116   /* File list */
4117
4118   widget = create_file_list (impl);
4119   gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
4120
4121   /* Preview */
4122
4123   impl->preview_box = gtk_vbox_new (FALSE, 12);
4124   gtk_box_pack_start (GTK_BOX (hbox), impl->preview_box, FALSE, FALSE, 0);
4125   /* Don't show preview box initially */
4126
4127   /* Filter combo */
4128
4129   impl->filter_combo_hbox = gtk_hbox_new (FALSE, 12);
4130
4131   widget = filter_create (impl);
4132
4133   g_signal_connect (widget, "realize",
4134                     G_CALLBACK (realize_filter_combo), impl);
4135
4136   gtk_widget_show (widget);
4137   gtk_box_pack_end (GTK_BOX (impl->filter_combo_hbox), widget, FALSE, FALSE, 0);
4138
4139   gtk_size_group_add_widget (size_group, impl->filter_combo_hbox);
4140   gtk_box_pack_end (GTK_BOX (vbox), impl->filter_combo_hbox, FALSE, FALSE, 0);
4141
4142   return vbox;
4143 }
4144
4145 /* Callback used when the "Browse for more folders" expander is toggled */
4146 static void
4147 expander_changed_cb (GtkExpander           *expander,
4148                      GParamSpec            *pspec,
4149                      GtkFileChooserDefault *impl)
4150 {
4151   update_appearance (impl);
4152 }
4153
4154 /* Callback used when the selection changes in the save folder combo box */
4155 static void
4156 save_folder_combo_changed_cb (GtkComboBox           *combo,
4157                               GtkFileChooserDefault *impl)
4158 {
4159   GtkTreeIter iter;
4160
4161   if (impl->changing_folder)
4162     return;
4163
4164   if (gtk_combo_box_get_active_iter (combo, &iter))
4165     shortcuts_activate_iter (impl, &iter);
4166 }
4167
4168 /* Creates the combo box with the save folders */
4169 static GtkWidget *
4170 save_folder_combo_create (GtkFileChooserDefault *impl)
4171 {
4172   GtkWidget *combo;
4173   GtkCellRenderer *cell;
4174
4175   combo = g_object_new (GTK_TYPE_COMBO_BOX,
4176                         "model", impl->shortcuts_model,
4177                         "focus-on-click", FALSE,
4178                         NULL);
4179   gtk_widget_show (combo);
4180
4181   cell = gtk_cell_renderer_pixbuf_new ();
4182   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE);
4183   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
4184                                   "pixbuf", SHORTCUTS_COL_PIXBUF,
4185                                   "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
4186                                   "sensitive", SHORTCUTS_COL_PIXBUF_VISIBLE,
4187                                   NULL);
4188
4189   cell = gtk_cell_renderer_text_new ();
4190   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
4191   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
4192                                   "text", SHORTCUTS_COL_NAME,
4193                                   "sensitive", SHORTCUTS_COL_PIXBUF_VISIBLE,
4194                                   NULL);
4195
4196   gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo),
4197                                         shortcuts_row_separator_func,
4198                                         GINT_TO_POINTER (SHORTCUTS_COL_NAME),
4199                                         NULL);
4200
4201   g_signal_connect (combo, "changed",
4202                     G_CALLBACK (save_folder_combo_changed_cb), impl);
4203
4204   return combo;
4205 }
4206
4207 /* Creates the widgets specific to Save mode */
4208 static void
4209 save_widgets_create (GtkFileChooserDefault *impl)
4210 {
4211   GtkWidget *vbox;
4212   GtkWidget *table;
4213   GtkWidget *widget;
4214   GtkWidget *alignment;
4215
4216   if (impl->save_widgets != NULL)
4217     return;
4218
4219   location_switch_to_path_bar (impl);
4220
4221   vbox = gtk_vbox_new (FALSE, 12);
4222
4223   table = gtk_table_new (2, 2, FALSE);
4224   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
4225   gtk_widget_show (table);
4226   gtk_table_set_row_spacings (GTK_TABLE (table), 12);
4227   gtk_table_set_col_spacings (GTK_TABLE (table), 12);
4228
4229   /* Label */
4230
4231   widget = gtk_label_new_with_mnemonic (_("_Name:"));
4232   gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
4233   gtk_table_attach (GTK_TABLE (table), widget,
4234                     0, 1, 0, 1,
4235                     GTK_FILL, GTK_FILL,
4236                     0, 0);
4237   gtk_widget_show (widget);
4238
4239   /* Location entry */
4240
4241   impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
4242   _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
4243                                            impl->file_system);
4244   gtk_entry_set_width_chars (GTK_ENTRY (impl->location_entry), 45);
4245   gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
4246   gtk_table_attach (GTK_TABLE (table), impl->location_entry,
4247                     1, 2, 0, 1,
4248                     GTK_EXPAND | GTK_FILL, 0,
4249                     0, 0);
4250   gtk_widget_show (impl->location_entry);
4251   gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->location_entry);
4252
4253   /* Folder combo */
4254   impl->save_folder_label = gtk_label_new (NULL);
4255   gtk_misc_set_alignment (GTK_MISC (impl->save_folder_label), 0.0, 0.5);
4256   gtk_table_attach (GTK_TABLE (table), impl->save_folder_label,
4257                     0, 1, 1, 2,
4258                     GTK_FILL, GTK_FILL,
4259                     0, 0);
4260   gtk_widget_show (impl->save_folder_label);
4261
4262   impl->save_folder_combo = save_folder_combo_create (impl);
4263   gtk_table_attach (GTK_TABLE (table), impl->save_folder_combo,
4264                     1, 2, 1, 2,
4265                     GTK_EXPAND | GTK_FILL, GTK_FILL,
4266                     0, 0);
4267   gtk_label_set_mnemonic_widget (GTK_LABEL (impl->save_folder_label), impl->save_folder_combo);
4268
4269   /* Expander */
4270   alignment = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
4271   gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
4272
4273   impl->save_expander = gtk_expander_new_with_mnemonic (_("_Browse for other folders"));
4274   gtk_container_add (GTK_CONTAINER (alignment), impl->save_expander);
4275   g_signal_connect (impl->save_expander, "notify::expanded",
4276                     G_CALLBACK (expander_changed_cb),
4277                     impl);
4278   gtk_widget_show_all (alignment);
4279
4280   impl->save_widgets = vbox;
4281   gtk_box_pack_start (GTK_BOX (impl), impl->save_widgets, FALSE, FALSE, 0);
4282   gtk_box_reorder_child (GTK_BOX (impl), impl->save_widgets, 0);
4283   gtk_widget_show (impl->save_widgets);
4284 }
4285
4286 /* Destroys the widgets specific to Save mode */
4287 static void
4288 save_widgets_destroy (GtkFileChooserDefault *impl)
4289 {
4290   if (impl->save_widgets == NULL)
4291     return;
4292
4293   gtk_widget_destroy (impl->save_widgets);
4294   impl->save_widgets = NULL;
4295   impl->location_entry = NULL;
4296   impl->save_folder_label = NULL;
4297   impl->save_folder_combo = NULL;
4298   impl->save_expander = NULL;
4299 }
4300
4301 /* Turns on the path bar widget.  Can be called even if we are already in that
4302  * mode.
4303  */
4304 static void
4305 location_switch_to_path_bar (GtkFileChooserDefault *impl)
4306 {
4307   if (impl->location_entry)
4308     {
4309       gtk_widget_destroy (impl->location_entry);
4310       impl->location_entry = NULL;
4311     }
4312
4313   gtk_widget_hide (impl->location_entry_box);
4314 }
4315
4316 /* Sets the full path of the current folder as the text in the location entry. */
4317 static void
4318 location_entry_set_initial_text (GtkFileChooserDefault *impl)
4319 {
4320   char *text;
4321
4322   if (!impl->current_folder)
4323     return;
4324
4325   if (gtk_file_system_path_is_local (impl->file_system, impl->current_folder))
4326     {
4327       char *filename;
4328
4329       filename = gtk_file_system_path_to_filename (impl->file_system, impl->current_folder);
4330       if (filename)
4331         {
4332           text = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
4333           g_free (filename);
4334         }
4335       else
4336         text = NULL;
4337     }
4338   else
4339     text = gtk_file_system_path_to_uri (impl->file_system, impl->current_folder);
4340
4341   if (text)
4342     {
4343       gboolean need_slash;
4344       int len;
4345
4346       len = strlen (text);
4347       need_slash = (text[len - 1] != G_DIR_SEPARATOR);
4348
4349       if (need_slash)
4350         {
4351           char *slash_text;
4352
4353           slash_text = g_new (char, len + 2);
4354           strcpy (slash_text, text);
4355           slash_text[len] = G_DIR_SEPARATOR;
4356           slash_text[len + 1] = 0;
4357
4358           g_free (text);
4359           text = slash_text;
4360         }
4361
4362       _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), text);
4363       g_free (text);
4364     }
4365 }
4366
4367 /* Turns on the location entry.  Can be called even if we are already in that
4368  * mode.
4369  */
4370 static void
4371 location_switch_to_filename_entry (GtkFileChooserDefault *impl)
4372 {
4373   if (impl->location_entry)
4374     gtk_widget_destroy (impl->location_entry);
4375
4376   /* Box */
4377
4378   gtk_widget_show (impl->location_entry_box);
4379
4380   /* Entry */
4381
4382   impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
4383   _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
4384                                            impl->file_system);
4385   gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
4386   _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action);
4387
4388   gtk_box_pack_start (GTK_BOX (impl->location_entry_box), impl->location_entry, TRUE, TRUE, 0);
4389   gtk_label_set_mnemonic_widget (GTK_LABEL (impl->location_label), impl->location_entry);
4390
4391   /* Configure the entry */
4392
4393   _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->current_folder);
4394   location_entry_set_initial_text (impl);
4395
4396   /* Done */
4397
4398   gtk_widget_show (impl->location_entry);
4399   gtk_widget_grab_focus (impl->location_entry);
4400 }
4401
4402 /* Sets a new location mode.  set_buttons determines whether the toggle button
4403  * for the mode will also be changed.
4404  */
4405 static void
4406 location_mode_set (GtkFileChooserDefault *impl,
4407                    LocationMode new_mode,
4408                    gboolean set_button)
4409 {
4410   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
4411       || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
4412     {
4413       GtkWindow *toplevel;
4414       GtkWidget *current_focus;
4415       gboolean button_active;
4416       gboolean switch_to_file_list;
4417
4418       switch (new_mode)
4419         {
4420         case LOCATION_MODE_PATH_BAR:
4421           button_active = FALSE;
4422
4423           /* The location_entry will disappear when we switch to path bar mode.  So,
4424            * we'll focus the file list in that case, to avoid having a window with
4425            * no focused widget.
4426            */
4427           toplevel = get_toplevel (GTK_WIDGET (impl));
4428           switch_to_file_list = FALSE;
4429           if (toplevel)
4430             {
4431               current_focus = gtk_window_get_focus (toplevel);
4432               if (!current_focus || current_focus == impl->location_entry)
4433                 switch_to_file_list = TRUE;
4434             }
4435
4436           location_switch_to_path_bar (impl);
4437
4438           if (switch_to_file_list)
4439             gtk_widget_grab_focus (impl->browse_files_tree_view);
4440
4441           break;
4442
4443         case LOCATION_MODE_FILENAME_ENTRY:
4444           button_active = TRUE;
4445           location_switch_to_filename_entry (impl);
4446           break;
4447
4448         default:
4449           g_assert_not_reached ();
4450           return;
4451         }
4452
4453       if (set_button)
4454         {
4455           g_signal_handlers_block_by_func (impl->location_button,
4456                                            G_CALLBACK (location_button_toggled_cb), impl);
4457
4458           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (impl->location_button), button_active);
4459
4460           g_signal_handlers_unblock_by_func (impl->location_button,
4461                                              G_CALLBACK (location_button_toggled_cb), impl);
4462         }
4463     }
4464
4465   impl->location_mode = new_mode;
4466 }
4467
4468 /* Callback used when one of the location mode buttons is toggled */
4469 static void
4470 location_button_toggled_cb (GtkToggleButton *toggle,
4471                             GtkFileChooserDefault *impl)
4472 {
4473   gboolean is_active;
4474   LocationMode new_mode;
4475
4476   is_active = gtk_toggle_button_get_active (toggle);
4477
4478   if (is_active)
4479     {
4480       g_assert (impl->location_mode == LOCATION_MODE_PATH_BAR);
4481       new_mode = LOCATION_MODE_FILENAME_ENTRY;
4482     }
4483   else
4484     {
4485       g_assert (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY);
4486       new_mode = LOCATION_MODE_PATH_BAR;
4487     }
4488
4489   location_mode_set (impl, new_mode, FALSE);
4490 }
4491
4492 /* Creates a toggle button for the location entry. */
4493 static void
4494 location_button_create (GtkFileChooserDefault *impl)
4495 {
4496   GtkWidget *image;
4497   const char *str;
4498
4499   image = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON);
4500   gtk_widget_show (image);
4501
4502   impl->location_button = g_object_new (GTK_TYPE_TOGGLE_BUTTON,
4503                                         "image", image,
4504                                         NULL);
4505
4506   g_signal_connect (impl->location_button, "toggled",
4507                     G_CALLBACK (location_button_toggled_cb), impl);
4508
4509   str = _("Type a file name");
4510
4511   gtk_tooltips_set_tip (impl->tooltips, impl->location_button, str, NULL);
4512   atk_object_set_name (gtk_widget_get_accessible (impl->location_button), str);
4513 }
4514
4515 /* Creates the main hpaned with the widgets shared by Open and Save mode */
4516 static GtkWidget *
4517 browse_widgets_create (GtkFileChooserDefault *impl)
4518 {
4519   GtkWidget *vbox;
4520   GtkWidget *hbox;
4521   GtkWidget *hpaned;
4522   GtkWidget *widget;
4523   GtkSizeGroup *size_group;
4524   gchar *text;
4525
4526   /* size group is used by the [+][-] buttons and the filter combo */
4527   size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
4528   vbox = gtk_vbox_new (FALSE, 12);
4529
4530   /* Location widgets */
4531   hbox = gtk_hbox_new (FALSE, 12);
4532   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
4533   gtk_widget_show (hbox);
4534
4535   location_button_create (impl);
4536   gtk_box_pack_start (GTK_BOX (hbox), impl->location_button, FALSE, FALSE, 0);
4537
4538   /* Path bar */
4539
4540   impl->browse_path_bar = create_path_bar (impl);
4541   g_signal_connect (impl->browse_path_bar, "path-clicked", G_CALLBACK (path_bar_clicked), impl);
4542   gtk_widget_show_all (impl->browse_path_bar);
4543   gtk_box_pack_start (GTK_BOX (hbox), impl->browse_path_bar, TRUE, TRUE, 0);
4544
4545   /* Create Folder */
4546   impl->browse_new_folder_button = gtk_button_new_with_mnemonic (_("Create Fo_lder"));
4547   g_signal_connect (impl->browse_new_folder_button, "clicked",
4548                     G_CALLBACK (new_folder_button_clicked), impl);
4549   gtk_box_pack_end (GTK_BOX (hbox), impl->browse_new_folder_button, FALSE, FALSE, 0);
4550
4551   /* Box for the location label and entry */
4552
4553   impl->location_entry_box = gtk_hbox_new (FALSE, 12);
4554   gtk_box_pack_start (GTK_BOX (vbox), impl->location_entry_box, FALSE, FALSE, 0);
4555   
4556   text = g_strconcat ("<b>", _("_Location:"), "</b>", NULL);
4557   impl->location_label = gtk_label_new_with_mnemonic (text);
4558   g_free (text);
4559   gtk_label_set_use_markup (GTK_LABEL (impl->location_label), TRUE);
4560   gtk_widget_show (impl->location_label);
4561   gtk_box_pack_start (GTK_BOX (impl->location_entry_box), impl->location_label, FALSE, FALSE, 0);
4562
4563   /* Paned widget */
4564   hpaned = gtk_hpaned_new ();
4565   gtk_widget_show (hpaned);
4566   gtk_paned_set_position (GTK_PANED (hpaned), 200); /* FIXME: this sucks */
4567   gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
4568
4569   widget = shortcuts_pane_create (impl, size_group);
4570   gtk_paned_pack1 (GTK_PANED (hpaned), widget, FALSE, FALSE);
4571   widget = file_pane_create (impl, size_group);
4572   gtk_paned_pack2 (GTK_PANED (hpaned), widget, TRUE, FALSE);
4573
4574   g_object_unref (size_group);
4575
4576   return vbox;
4577 }
4578
4579 static GObject*
4580 gtk_file_chooser_default_constructor (GType                  type,
4581                                       guint                  n_construct_properties,
4582                                       GObjectConstructParam *construct_params)
4583 {
4584   GtkFileChooserDefault *impl;
4585   GObject *object;
4586
4587   profile_start ("start", NULL);
4588
4589   object = G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->constructor (type,
4590                                                                                 n_construct_properties,
4591                                                                                 construct_params);
4592   impl = GTK_FILE_CHOOSER_DEFAULT (object);
4593
4594   g_assert (impl->file_system);
4595
4596   gtk_widget_push_composite_child ();
4597
4598   /* Shortcuts model */
4599
4600   shortcuts_model_create (impl);
4601
4602   /* The browse widgets */
4603   impl->browse_widgets = browse_widgets_create (impl);
4604   gtk_box_pack_start (GTK_BOX (impl), impl->browse_widgets, TRUE, TRUE, 0);
4605
4606   /* Alignment to hold extra widget */
4607   impl->extra_align = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
4608   gtk_box_pack_start (GTK_BOX (impl), impl->extra_align, FALSE, FALSE, 0);
4609
4610   gtk_widget_pop_composite_child ();
4611   update_appearance (impl);
4612
4613   profile_end ("end", NULL);
4614
4615   return object;
4616 }
4617
4618 /* Sets the extra_widget by packing it in the appropriate place */
4619 static void
4620 set_extra_widget (GtkFileChooserDefault *impl,
4621                   GtkWidget             *extra_widget)
4622 {
4623   if (extra_widget)
4624     {
4625       g_object_ref (extra_widget);
4626       /* FIXME: is this right ? */
4627       gtk_widget_show (extra_widget);
4628     }
4629
4630   if (impl->extra_widget)
4631     {
4632       gtk_container_remove (GTK_CONTAINER (impl->extra_align), impl->extra_widget);
4633       g_object_unref (impl->extra_widget);
4634     }
4635
4636   impl->extra_widget = extra_widget;
4637   if (impl->extra_widget)
4638     {
4639       gtk_container_add (GTK_CONTAINER (impl->extra_align), impl->extra_widget);
4640       gtk_widget_show (impl->extra_align);
4641     }
4642   else
4643     gtk_widget_hide (impl->extra_align);
4644 }
4645
4646 static void
4647 set_local_only (GtkFileChooserDefault *impl,
4648                 gboolean               local_only)
4649 {
4650   if (local_only != impl->local_only)
4651     {
4652       impl->local_only = local_only;
4653
4654       if (impl->shortcuts_model && impl->file_system)
4655         {
4656           shortcuts_add_volumes (impl);
4657           shortcuts_add_bookmarks (impl);
4658         }
4659
4660       if (local_only &&
4661           !gtk_file_system_path_is_local (impl->file_system, impl->current_folder))
4662         {
4663           /* If we are pointing to a non-local folder, make an effort to change
4664            * back to a local folder, but it's really up to the app to not cause
4665            * such a situation, so we ignore errors.
4666            */
4667           const gchar *home = g_get_home_dir ();
4668           GtkFilePath *home_path;
4669
4670           if (home == NULL)
4671             return;
4672
4673           home_path = gtk_file_system_filename_to_path (impl->file_system, home);
4674
4675           _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), home_path, NULL);
4676
4677           gtk_file_path_free (home_path);
4678         }
4679     }
4680 }
4681
4682 static void
4683 volumes_changed_cb (GtkFileSystem         *file_system,
4684                     GtkFileChooserDefault *impl)
4685 {
4686   shortcuts_add_volumes (impl);
4687 }
4688
4689 /* Callback used when the set of bookmarks changes in the file system */
4690 static void
4691 bookmarks_changed_cb (GtkFileSystem         *file_system,
4692                       GtkFileChooserDefault *impl)
4693 {
4694   shortcuts_add_bookmarks (impl);
4695
4696   bookmarks_check_add_sensitivity (impl);
4697   bookmarks_check_remove_sensitivity (impl);
4698   shortcuts_check_popup_sensitivity (impl);
4699 }
4700
4701 /* Sets the file chooser to multiple selection mode */
4702 static void
4703 set_select_multiple (GtkFileChooserDefault *impl,
4704                      gboolean               select_multiple,
4705                      gboolean               property_notify)
4706 {
4707   GtkTreeSelection *selection;
4708   GtkSelectionMode mode;
4709
4710   if (select_multiple == impl->select_multiple)
4711     return;
4712
4713   mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE;
4714
4715   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4716   gtk_tree_selection_set_mode (selection, mode);
4717
4718   impl->select_multiple = select_multiple;
4719   g_object_notify (G_OBJECT (impl), "select-multiple");
4720
4721   check_preview_change (impl);
4722 }
4723
4724 static void
4725 set_file_system_backend (GtkFileChooserDefault *impl,
4726                          const char *backend)
4727 {
4728   profile_start ("start for backend", backend ? backend : "default");
4729
4730   if (impl->file_system)
4731     {
4732       g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
4733       impl->volumes_changed_id = 0;
4734       g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
4735       impl->bookmarks_changed_id = 0;
4736       g_object_unref (impl->file_system);
4737     }
4738
4739   impl->file_system = NULL;
4740   if (backend)
4741     impl->file_system = _gtk_file_system_create (backend);
4742   else
4743     {
4744       GtkSettings *settings = gtk_settings_get_default ();
4745       gchar *default_backend = NULL;
4746
4747       g_object_get (settings, "gtk-file-chooser-backend", &default_backend, NULL);
4748       if (default_backend)
4749         {
4750           impl->file_system = _gtk_file_system_create (default_backend);
4751           g_free (default_backend);
4752         }
4753     }
4754
4755   if (!impl->file_system)
4756     {
4757 #if defined (G_OS_UNIX)
4758       impl->file_system = gtk_file_system_unix_new ();
4759 #elif defined (G_OS_WIN32)
4760       impl->file_system = gtk_file_system_win32_new ();
4761 #else
4762 #error "No default filesystem implementation on the platform"
4763 #endif
4764     }
4765
4766   if (impl->file_system)
4767     {
4768       impl->volumes_changed_id = g_signal_connect (impl->file_system, "volumes-changed",
4769                                                    G_CALLBACK (volumes_changed_cb),
4770                                                    impl);
4771       impl->bookmarks_changed_id = g_signal_connect (impl->file_system, "bookmarks-changed",
4772                                                      G_CALLBACK (bookmarks_changed_cb),
4773                                                      impl);
4774     }
4775
4776   profile_end ("end", NULL);
4777 }
4778
4779 /* This function is basically a do_all function.
4780  *
4781  * It sets the visibility on all the widgets based on the current state, and
4782  * moves the custom_widget if needed.
4783  */
4784 static void
4785 update_appearance (GtkFileChooserDefault *impl)
4786 {
4787   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
4788       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4789     {
4790       const char *text;
4791
4792       gtk_widget_hide (impl->location_button);
4793       save_widgets_create (impl);
4794
4795       if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
4796         text = _("Save in _folder:");
4797       else
4798         text = _("Create in _folder:");
4799
4800       gtk_label_set_text_with_mnemonic (GTK_LABEL (impl->save_folder_label), text);
4801
4802       if (gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
4803         {
4804           gtk_widget_set_sensitive (impl->save_folder_label, FALSE);
4805           gtk_widget_set_sensitive (impl->save_folder_combo, FALSE);
4806           gtk_widget_show (impl->browse_widgets);
4807         }
4808       else
4809         {
4810           gtk_widget_set_sensitive (impl->save_folder_label, TRUE);
4811           gtk_widget_set_sensitive (impl->save_folder_combo, TRUE);
4812           gtk_widget_hide (impl->browse_widgets);
4813         }
4814
4815       gtk_widget_show (impl->browse_new_folder_button);
4816
4817       if (impl->select_multiple)
4818         {
4819           g_warning ("Save mode cannot be set in conjunction with multiple selection mode.  "
4820                      "Re-setting to single selection mode.");
4821           set_select_multiple (impl, FALSE, TRUE);
4822         }
4823     }
4824   else if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
4825            impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
4826     {
4827       gtk_widget_show (impl->location_button);
4828       save_widgets_destroy (impl);
4829       gtk_widget_show (impl->browse_widgets);
4830       location_mode_set (impl, impl->location_mode, TRUE);
4831     }
4832
4833   if (impl->location_entry)
4834     _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action);
4835
4836   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
4837     gtk_widget_hide (impl->browse_new_folder_button);
4838   else
4839     gtk_widget_show (impl->browse_new_folder_button);
4840
4841   /* This *is* needed; we need to redraw the file list because the "sensitivity"
4842    * of files may change depending whether we are in a file or folder-only mode.
4843    */
4844   gtk_widget_queue_draw (impl->browse_files_tree_view);
4845
4846   g_signal_emit_by_name (impl, "default-size-changed");
4847 }
4848
4849 static void
4850 gtk_file_chooser_default_set_property (GObject      *object,
4851                                        guint         prop_id,
4852                                        const GValue *value,
4853                                        GParamSpec   *pspec)
4854
4855 {
4856   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
4857
4858   switch (prop_id)
4859     {
4860     case GTK_FILE_CHOOSER_PROP_ACTION:
4861       {
4862         GtkFileChooserAction action = g_value_get_enum (value);
4863
4864         if (action != impl->action)
4865           {
4866             gtk_file_chooser_default_unselect_all (GTK_FILE_CHOOSER (impl));
4867             
4868             if ((action == GTK_FILE_CHOOSER_ACTION_SAVE || action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4869                 && impl->select_multiple)
4870               {
4871                 g_warning ("Tried to change the file chooser action to SAVE or CREATE_FOLDER, but "
4872                            "this is not allowed in multiple selection mode.  Resetting the file chooser "
4873                            "to single selection mode.");
4874                 set_select_multiple (impl, FALSE, TRUE);
4875               }
4876             impl->action = action;
4877             update_appearance (impl);
4878           }
4879       }
4880       break;
4881
4882     case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
4883       set_file_system_backend (impl, g_value_get_string (value));
4884       break;
4885
4886     case GTK_FILE_CHOOSER_PROP_FILTER:
4887       set_current_filter (impl, g_value_get_object (value));
4888       break;
4889
4890     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
4891       set_local_only (impl, g_value_get_boolean (value));
4892       break;
4893
4894     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
4895       set_preview_widget (impl, g_value_get_object (value));
4896       break;
4897
4898     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
4899       impl->preview_widget_active = g_value_get_boolean (value);
4900       update_preview_widget_visibility (impl);
4901       break;
4902
4903     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
4904       impl->use_preview_label = g_value_get_boolean (value);
4905       update_preview_widget_visibility (impl);
4906       break;
4907
4908     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
4909       set_extra_widget (impl, g_value_get_object (value));
4910       break;
4911
4912     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
4913       {
4914         gboolean select_multiple = g_value_get_boolean (value);
4915         if ((impl->action == GTK_FILE_CHOOSER_ACTION_SAVE || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4916             && select_multiple)
4917           {
4918             g_warning ("Tried to set the file chooser to multiple selection mode, but this is "
4919                        "not allowed in SAVE or CREATE_FOLDER modes.  Ignoring the change and "
4920                        "leaving the file chooser in single selection mode.");
4921             return;
4922           }
4923
4924         set_select_multiple (impl, select_multiple, FALSE);
4925       }
4926       break;
4927
4928     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
4929       {
4930         gboolean show_hidden = g_value_get_boolean (value);
4931         if (show_hidden != impl->show_hidden)
4932           {
4933             impl->show_hidden = show_hidden;
4934
4935             if (impl->browse_files_model)
4936               _gtk_file_system_model_set_show_hidden (impl->browse_files_model, show_hidden);
4937           }
4938       }
4939       break;
4940
4941     case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
4942       {
4943         gboolean do_overwrite_confirmation = g_value_get_boolean (value);
4944         impl->do_overwrite_confirmation = do_overwrite_confirmation;
4945       }
4946       break;
4947
4948     default:
4949       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4950       break;
4951     }
4952 }
4953
4954 static void
4955 gtk_file_chooser_default_get_property (GObject    *object,
4956                                        guint       prop_id,
4957                                        GValue     *value,
4958                                        GParamSpec *pspec)
4959 {
4960   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
4961
4962   switch (prop_id)
4963     {
4964     case GTK_FILE_CHOOSER_PROP_ACTION:
4965       g_value_set_enum (value, impl->action);
4966       break;
4967
4968     case GTK_FILE_CHOOSER_PROP_FILTER:
4969       g_value_set_object (value, impl->current_filter);
4970       break;
4971
4972     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
4973       g_value_set_boolean (value, impl->local_only);
4974       break;
4975
4976     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
4977       g_value_set_object (value, impl->preview_widget);
4978       break;
4979
4980     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
4981       g_value_set_boolean (value, impl->preview_widget_active);
4982       break;
4983
4984     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
4985       g_value_set_boolean (value, impl->use_preview_label);
4986       break;
4987
4988     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
4989       g_value_set_object (value, impl->extra_widget);
4990       break;
4991
4992     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
4993       g_value_set_boolean (value, impl->select_multiple);
4994       break;
4995
4996     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
4997       g_value_set_boolean (value, impl->show_hidden);
4998       break;
4999
5000     case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
5001       g_value_set_boolean (value, impl->do_overwrite_confirmation);
5002       break;
5003
5004     default:
5005       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5006       break;
5007     }
5008 }
5009
5010 /* Removes the settings signal handler.  It's safe to call multiple times */
5011 static void
5012 remove_settings_signal (GtkFileChooserDefault *impl,
5013                         GdkScreen             *screen)
5014 {
5015   if (impl->settings_signal_id)
5016     {
5017       GtkSettings *settings;
5018
5019       settings = gtk_settings_get_for_screen (screen);
5020       g_signal_handler_disconnect (settings,
5021                                    impl->settings_signal_id);
5022       impl->settings_signal_id = 0;
5023     }
5024 }
5025
5026 static void
5027 gtk_file_chooser_default_dispose (GObject *object)
5028 {
5029   GSList *l;
5030   GtkFileChooserDefault *impl = (GtkFileChooserDefault *) object;
5031
5032   if (impl->extra_widget)
5033     {
5034       g_object_unref (impl->extra_widget);
5035       impl->extra_widget = NULL;
5036     }
5037
5038   if (impl->volumes_changed_id > 0)
5039     {
5040       g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
5041       impl->volumes_changed_id = 0;
5042     }
5043
5044   if (impl->bookmarks_changed_id > 0)
5045     {
5046       g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
5047       impl->bookmarks_changed_id = 0;
5048     }
5049
5050   pending_select_paths_free (impl);
5051
5052   /* cancel all pending operations */
5053   if (impl->pending_handles)
5054     {
5055       for (l = impl->pending_handles; l; l = l->next)
5056         {
5057           GtkFileSystemHandle *handle =l->data;
5058           gtk_file_system_cancel_operation (handle);
5059         }
5060       g_slist_free (impl->pending_handles);
5061       impl->pending_handles = NULL;
5062     }
5063
5064   if (impl->reload_icon_handles)
5065     {
5066       for (l = impl->reload_icon_handles; l; l = l->next)
5067         {
5068           GtkFileSystemHandle *handle =l->data;
5069           gtk_file_system_cancel_operation (handle);
5070         }
5071       g_slist_free (impl->reload_icon_handles);
5072       impl->reload_icon_handles = NULL;
5073     }
5074
5075   if (impl->loading_shortcuts)
5076     {
5077       for (l = impl->loading_shortcuts; l; l = l->next)
5078         {
5079           GtkFileSystemHandle *handle =l->data;
5080           gtk_file_system_cancel_operation (handle);
5081         }
5082       g_slist_free (impl->loading_shortcuts);
5083       impl->loading_shortcuts = NULL;
5084     }
5085
5086   if (impl->file_list_drag_data_received_handle)
5087     {
5088       gtk_file_system_cancel_operation (impl->file_list_drag_data_received_handle);
5089       impl->file_list_drag_data_received_handle = NULL;
5090     }
5091
5092   if (impl->update_current_folder_handle)
5093     {
5094       gtk_file_system_cancel_operation (impl->update_current_folder_handle);
5095       impl->update_current_folder_handle = NULL;
5096     }
5097
5098   if (impl->show_and_select_paths_handle)
5099     {
5100       gtk_file_system_cancel_operation (impl->show_and_select_paths_handle);
5101       impl->show_and_select_paths_handle = NULL;
5102     }
5103
5104   if (impl->should_respond_get_info_handle)
5105     {
5106       gtk_file_system_cancel_operation (impl->should_respond_get_info_handle);
5107       impl->should_respond_get_info_handle = NULL;
5108     }
5109
5110   if (impl->update_from_entry_handle)
5111     {
5112       gtk_file_system_cancel_operation (impl->update_from_entry_handle);
5113       impl->update_from_entry_handle = NULL;
5114     }
5115
5116   if (impl->shortcuts_activate_iter_handle)
5117     {
5118       gtk_file_system_cancel_operation (impl->shortcuts_activate_iter_handle);
5119       impl->shortcuts_activate_iter_handle = NULL;
5120     }
5121
5122   remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl)));
5123
5124   G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->dispose (object);
5125 }
5126
5127 /* We override show-all since we have internal widgets that
5128  * shouldn't be shown when you call show_all(), like the filter
5129  * combo box.
5130  */
5131 static void
5132 gtk_file_chooser_default_show_all (GtkWidget *widget)
5133 {
5134   GtkFileChooserDefault *impl = (GtkFileChooserDefault *) widget;
5135
5136   gtk_widget_show (widget);
5137
5138   if (impl->extra_widget)
5139     gtk_widget_show_all (impl->extra_widget);
5140 }
5141
5142 /* Handler for GtkWindow::set-focus; this is where we save the last-focused
5143  * widget on our toplevel.  See gtk_file_chooser_default_hierarchy_changed()
5144  */
5145 static void
5146 toplevel_set_focus_cb (GtkWindow             *window,
5147                        GtkWidget             *focus,
5148                        GtkFileChooserDefault *impl)
5149 {
5150   impl->toplevel_last_focus_widget = gtk_window_get_focus (window);
5151 }
5152
5153 /* We monitor the focus widget on our toplevel to be able to know which widget
5154  * was last focused at the time our "should_respond" method gets called.
5155  */
5156 static void
5157 gtk_file_chooser_default_hierarchy_changed (GtkWidget *widget,
5158                                             GtkWidget *previous_toplevel)
5159 {
5160   GtkFileChooserDefault *impl;
5161   GtkWidget *toplevel;
5162
5163   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5164
5165   if (previous_toplevel)
5166     {
5167       g_assert (impl->toplevel_set_focus_id != 0);
5168       g_signal_handler_disconnect (previous_toplevel, impl->toplevel_set_focus_id);
5169       impl->toplevel_set_focus_id = 0;
5170       impl->toplevel_last_focus_widget = NULL;
5171     }
5172   else
5173     g_assert (impl->toplevel_set_focus_id == 0);
5174
5175   toplevel = gtk_widget_get_toplevel (widget);
5176   if (GTK_IS_WINDOW (toplevel))
5177     {
5178       impl->toplevel_set_focus_id = g_signal_connect (toplevel, "set_focus",
5179                                                       G_CALLBACK (toplevel_set_focus_cb), impl);
5180       impl->toplevel_last_focus_widget = gtk_window_get_focus (GTK_WINDOW (toplevel));
5181     }
5182 }
5183
5184 /* Changes the icons wherever it is needed */
5185 static void
5186 change_icon_theme (GtkFileChooserDefault *impl)
5187 {
5188   GtkSettings *settings;
5189   gint width, height;
5190
5191   profile_start ("start", NULL);
5192
5193   settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
5194
5195   if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU, &width, &height))
5196     impl->icon_size = MAX (width, height);
5197   else
5198     impl->icon_size = FALLBACK_ICON_SIZE;
5199
5200   shortcuts_reload_icons (impl);
5201   gtk_widget_queue_resize (impl->browse_files_tree_view);
5202
5203   profile_end ("end", NULL);
5204 }
5205
5206 /* Callback used when a GtkSettings value changes */
5207 static void
5208 settings_notify_cb (GObject               *object,
5209                     GParamSpec            *pspec,
5210                     GtkFileChooserDefault *impl)
5211 {
5212   const char *name;
5213
5214   profile_start ("start", NULL);
5215
5216   name = g_param_spec_get_name (pspec);
5217
5218   if (strcmp (name, "gtk-icon-theme-name") == 0
5219       || strcmp (name, "gtk-icon-sizes") == 0)
5220     change_icon_theme (impl);
5221
5222   profile_end ("end", NULL);
5223 }
5224
5225 /* Installs a signal handler for GtkSettings so that we can monitor changes in
5226  * the icon theme.
5227  */
5228 static void
5229 check_icon_theme (GtkFileChooserDefault *impl)
5230 {
5231   GtkSettings *settings;
5232
5233   profile_start ("start", NULL);
5234
5235   if (impl->settings_signal_id)
5236     {
5237       profile_end ("end", NULL);
5238       return;
5239     }
5240
5241   if (gtk_widget_has_screen (GTK_WIDGET (impl)))
5242     {
5243       settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
5244       impl->settings_signal_id = g_signal_connect (settings, "notify",
5245                                                    G_CALLBACK (settings_notify_cb), impl);
5246
5247       change_icon_theme (impl);
5248     }
5249
5250   profile_end ("end", NULL);
5251 }
5252
5253 static void
5254 gtk_file_chooser_default_style_set (GtkWidget *widget,
5255                                     GtkStyle  *previous_style)
5256 {
5257   GtkFileChooserDefault *impl;
5258
5259   profile_start ("start", NULL);
5260
5261   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5262
5263   profile_msg ("    parent class style_set start", NULL);
5264   if (GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->style_set)
5265     GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->style_set (widget, previous_style);
5266   profile_msg ("    parent class style_set end", NULL);
5267
5268   if (gtk_widget_has_screen (GTK_WIDGET (impl)))
5269     change_icon_theme (impl);
5270
5271   profile_msg ("    emit default-size-changed start", NULL);
5272   g_signal_emit_by_name (widget, "default-size-changed");
5273   profile_msg ("    emit default-size-changed end", NULL);
5274
5275   profile_end ("end", NULL);
5276 }
5277
5278 static void
5279 gtk_file_chooser_default_screen_changed (GtkWidget *widget,
5280                                          GdkScreen *previous_screen)
5281 {
5282   GtkFileChooserDefault *impl;
5283
5284   profile_start ("start", NULL);
5285
5286   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5287
5288   if (GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->screen_changed)
5289     GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->screen_changed (widget, previous_screen);
5290
5291   remove_settings_signal (impl, previous_screen);
5292   check_icon_theme (impl);
5293
5294   g_signal_emit_by_name (widget, "default-size-changed");
5295
5296   profile_end ("end", NULL);
5297 }
5298
5299 static gboolean
5300 get_is_file_filtered (GtkFileChooserDefault *impl,
5301                       const GtkFilePath     *path,
5302                       GtkFileInfo           *file_info)
5303 {
5304   GtkFileFilterInfo filter_info;
5305   GtkFileFilterFlags needed;
5306   gboolean result;
5307
5308   if (!impl->current_filter)
5309     return FALSE;
5310
5311   filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
5312
5313   needed = gtk_file_filter_get_needed (impl->current_filter);
5314
5315   filter_info.display_name = gtk_file_info_get_display_name (file_info);
5316   filter_info.mime_type = gtk_file_info_get_mime_type (file_info);
5317
5318   if (needed & GTK_FILE_FILTER_FILENAME)
5319     {
5320       filter_info.filename = gtk_file_system_path_to_filename (impl->file_system, path);
5321       if (filter_info.filename)
5322         filter_info.contains |= GTK_FILE_FILTER_FILENAME;
5323     }
5324   else
5325     filter_info.filename = NULL;
5326
5327   if (needed & GTK_FILE_FILTER_URI)
5328     {
5329       filter_info.uri = gtk_file_system_path_to_uri (impl->file_system, path);
5330       if (filter_info.uri)
5331         filter_info.contains |= GTK_FILE_FILTER_URI;
5332     }
5333   else
5334     filter_info.uri = NULL;
5335
5336   result = gtk_file_filter_filter (impl->current_filter, &filter_info);
5337
5338   if (filter_info.filename)
5339     g_free ((gchar *)filter_info.filename);
5340   if (filter_info.uri)
5341     g_free ((gchar *)filter_info.uri);
5342
5343   return !result;
5344 }
5345
5346 static void
5347 settings_load (GtkFileChooserDefault *impl)
5348 {
5349   GtkFileChooserSettings *settings;
5350   LocationMode location_mode;
5351   gboolean show_hidden;
5352
5353   settings = _gtk_file_chooser_settings_new ();
5354
5355   location_mode = _gtk_file_chooser_settings_get_location_mode (settings);
5356   show_hidden = _gtk_file_chooser_settings_get_show_hidden (settings);
5357
5358   g_object_unref (settings);
5359
5360   location_mode_set (impl, location_mode, TRUE);
5361   gtk_file_chooser_set_show_hidden (GTK_FILE_CHOOSER (impl), show_hidden);
5362 }
5363
5364 static void
5365 settings_save (GtkFileChooserDefault *impl)
5366 {
5367   GtkFileChooserSettings *settings;
5368
5369   settings = _gtk_file_chooser_settings_new ();
5370
5371   _gtk_file_chooser_settings_set_location_mode (settings, impl->location_mode);
5372   _gtk_file_chooser_settings_set_show_hidden (settings, gtk_file_chooser_get_show_hidden (GTK_FILE_CHOOSER (impl)));
5373
5374   /* NULL GError */
5375   _gtk_file_chooser_settings_save (settings, NULL);
5376
5377   g_object_unref (settings);
5378 }
5379
5380 /* GtkWidget::map method */
5381 static void
5382 gtk_file_chooser_default_map (GtkWidget *widget)
5383 {
5384   GtkFileChooserDefault *impl;
5385   char *current_working_dir;
5386
5387   profile_start ("start", NULL);
5388
5389   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5390
5391   GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->map (widget);
5392
5393   switch (impl->reload_state)
5394     {
5395     case RELOAD_EMPTY:
5396       /* The user didn't explicitly give us a folder to display, so we'll use the cwd */
5397       current_working_dir = g_get_current_dir ();
5398       gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (impl), current_working_dir);
5399       g_free (current_working_dir);
5400       break;
5401
5402     case RELOAD_HAS_FOLDER:
5403       /* Nothing; we are already loading or loaded, so we don't need to reload */
5404       break;
5405
5406     case RELOAD_WAS_UNMAPPED:
5407       /* Just reload the current folder; else continue the pending load. */
5408       if (impl->current_folder)
5409         {
5410           pending_select_paths_store_selection (impl);
5411           change_folder_and_display_error (impl, impl->current_folder);
5412         }
5413       break;
5414
5415     default:
5416       g_assert_not_reached ();
5417     }
5418
5419   bookmarks_changed_cb (impl->file_system, impl);
5420
5421   settings_load (impl);
5422
5423   profile_end ("end", NULL);
5424 }
5425
5426 /* GtkWidget::unmap method */
5427 static void
5428 gtk_file_chooser_default_unmap (GtkWidget *widget)
5429 {
5430   GtkFileChooserDefault *impl;
5431
5432   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5433
5434   settings_save (impl);
5435
5436   GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->unmap (widget);
5437
5438   impl->reload_state = RELOAD_WAS_UNMAPPED;
5439 }
5440
5441 static gboolean
5442 list_model_filter_func (GtkFileSystemModel *model,
5443                         GtkFilePath        *path,
5444                         const GtkFileInfo  *file_info,
5445                         gpointer            user_data)
5446 {
5447   GtkFileChooserDefault *impl = user_data;
5448
5449   if (!impl->current_filter)
5450     return TRUE;
5451
5452   if (gtk_file_info_get_is_folder (file_info))
5453     return TRUE;
5454
5455   return !get_is_file_filtered (impl, path, (GtkFileInfo *) file_info);
5456 }
5457
5458 static void
5459 install_list_model_filter (GtkFileChooserDefault *impl)
5460 {
5461   GtkFileSystemModelFilter filter;
5462   gpointer data;
5463
5464   g_assert (impl->browse_files_model != NULL);
5465
5466   if (impl->current_filter)
5467     {
5468       filter = list_model_filter_func;
5469       data   = impl;
5470     }
5471   else
5472     {
5473       filter = NULL;
5474       data   = NULL;
5475     }
5476   
5477   _gtk_file_system_model_set_filter (impl->browse_files_model,
5478                                      filter,
5479                                      data);
5480 }
5481
5482 #define COMPARE_DIRECTORIES                                                                                    \
5483   GtkFileChooserDefault *impl = user_data;                                                                     \
5484   const GtkFileInfo *info_a = _gtk_file_system_model_get_info (impl->browse_files_model, a);                           \
5485   const GtkFileInfo *info_b = _gtk_file_system_model_get_info (impl->browse_files_model, b);                           \
5486   gboolean dir_a, dir_b;                                                                                       \
5487                                                                                                                \
5488   if (info_a)                                                                                                  \
5489     dir_a = gtk_file_info_get_is_folder (info_a);                                                              \
5490   else                                                                                                         \
5491     return impl->list_sort_ascending ? -1 : 1;                                                                 \
5492                                                                                                                \
5493   if (info_b)                                                                                                  \
5494     dir_b = gtk_file_info_get_is_folder (info_b);                                                              \
5495   else                                                                                                         \
5496     return impl->list_sort_ascending ? 1 : -1;                                                                 \
5497                                                                                                                \
5498   if (dir_a != dir_b)                                                                                          \
5499     return impl->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */
5500
5501 /* Sort callback for the filename column */
5502 static gint
5503 name_sort_func (GtkTreeModel *model,
5504                 GtkTreeIter  *a,
5505                 GtkTreeIter  *b,
5506                 gpointer      user_data)
5507 {
5508   COMPARE_DIRECTORIES;
5509   else
5510     return strcmp (gtk_file_info_get_display_key (info_a), gtk_file_info_get_display_key (info_b));
5511 }
5512
5513 /* Sort callback for the size column */
5514 static gint
5515 size_sort_func (GtkTreeModel *model,
5516                 GtkTreeIter  *a,
5517                 GtkTreeIter  *b,
5518                 gpointer      user_data)
5519 {
5520   COMPARE_DIRECTORIES;
5521   else
5522     {
5523       gint64 size_a = gtk_file_info_get_size (info_a);
5524       gint64 size_b = gtk_file_info_get_size (info_b);
5525
5526       return size_a > size_b ? -1 : (size_a == size_b ? 0 : 1);
5527     }
5528 }
5529
5530 /* Sort callback for the mtime column */
5531 static gint
5532 mtime_sort_func (GtkTreeModel *model,
5533                  GtkTreeIter  *a,
5534                  GtkTreeIter  *b,
5535                  gpointer      user_data)
5536 {
5537   COMPARE_DIRECTORIES;
5538   else
5539     {
5540       GtkFileTime ta = gtk_file_info_get_modification_time (info_a);
5541       GtkFileTime tb = gtk_file_info_get_modification_time (info_b);
5542
5543       return ta > tb ? -1 : (ta == tb ? 0 : 1);
5544     }
5545 }
5546
5547 /* Callback used when the sort column changes.  We cache the sort order for use
5548  * in name_sort_func().
5549  */
5550 static void
5551 list_sort_column_changed_cb (GtkTreeSortable       *sortable,
5552                              GtkFileChooserDefault *impl)
5553 {
5554   GtkSortType sort_type;
5555
5556   if (gtk_tree_sortable_get_sort_column_id (sortable, NULL, &sort_type))
5557     impl->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
5558 }
5559
5560 static void
5561 set_busy_cursor (GtkFileChooserDefault *impl,
5562                  gboolean               busy)
5563 {
5564   GtkWindow *toplevel;
5565   GdkDisplay *display;
5566   GdkCursor *cursor;
5567
5568   toplevel = get_toplevel (GTK_WIDGET (impl));
5569   if (!toplevel || !GTK_WIDGET_REALIZED (toplevel))
5570     return;
5571
5572   display = gtk_widget_get_display (GTK_WIDGET (toplevel));
5573
5574   if (busy)
5575     cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
5576   else
5577     cursor = NULL;
5578
5579   gdk_window_set_cursor (GTK_WIDGET (toplevel)->window, cursor);
5580   gdk_display_flush (display);
5581
5582   if (cursor)
5583     gdk_cursor_unref (cursor);
5584 }
5585
5586 /* Creates a sort model to wrap the file system model and sets it on the tree view */
5587 static void
5588 load_set_model (GtkFileChooserDefault *impl)
5589 {
5590   profile_start ("start", NULL);
5591
5592   g_assert (impl->browse_files_model != NULL);
5593   g_assert (impl->sort_model == NULL);
5594
5595   profile_msg ("    gtk_tree_model_sort_new_with_model start", NULL);
5596   impl->sort_model = (GtkTreeModelSort *)gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (impl->browse_files_model));
5597   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, name_sort_func, impl, NULL);
5598   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_SIZE, size_sort_func, impl, NULL);
5599   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_MTIME, mtime_sort_func, impl, NULL);
5600   gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->sort_model), NULL, NULL, NULL);
5601   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, GTK_SORT_ASCENDING);
5602   impl->list_sort_ascending = TRUE;
5603   profile_msg ("    gtk_tree_model_sort_new_with_model end", NULL);
5604
5605   g_signal_connect (impl->sort_model, "sort_column_changed",
5606                     G_CALLBACK (list_sort_column_changed_cb), impl);
5607
5608   profile_msg ("    gtk_tree_view_set_model start", NULL);
5609   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
5610                            GTK_TREE_MODEL (impl->sort_model));
5611   gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->browse_files_tree_view));
5612   gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_files_tree_view),
5613                                    GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
5614   profile_msg ("    gtk_tree_view_set_model end", NULL);
5615
5616   profile_end ("end", NULL);
5617 }
5618
5619 /* Timeout callback used when the loading timer expires */
5620 static gboolean
5621 load_timeout_cb (gpointer data)
5622 {
5623   GtkFileChooserDefault *impl;
5624
5625   profile_start ("start", NULL);
5626
5627   GDK_THREADS_ENTER ();
5628
5629   impl = GTK_FILE_CHOOSER_DEFAULT (data);
5630   g_assert (impl->load_state == LOAD_PRELOAD);
5631   g_assert (impl->load_timeout_id != 0);
5632   g_assert (impl->browse_files_model != NULL);
5633
5634   impl->load_timeout_id = 0;
5635   impl->load_state = LOAD_LOADING;
5636
5637   load_set_model (impl);
5638
5639   GDK_THREADS_LEAVE ();
5640
5641   profile_end ("end", NULL);
5642
5643   return FALSE;
5644 }
5645
5646 /* Sets up a new load timer for the model and switches to the LOAD_LOADING state */
5647 static void
5648 load_setup_timer (GtkFileChooserDefault *impl)
5649 {
5650   g_assert (impl->load_timeout_id == 0);
5651   g_assert (impl->load_state != LOAD_PRELOAD);
5652
5653   impl->load_timeout_id = g_timeout_add (MAX_LOADING_TIME, load_timeout_cb, impl);
5654   impl->load_state = LOAD_PRELOAD;
5655 }
5656
5657 /* Removes the load timeout and switches to the LOAD_FINISHED state */
5658 static void
5659 load_remove_timer (GtkFileChooserDefault *impl)
5660 {
5661   if (impl->load_timeout_id != 0)
5662     {
5663       g_assert (impl->load_state == LOAD_PRELOAD);
5664
5665       g_source_remove (impl->load_timeout_id);
5666       impl->load_timeout_id = 0;
5667       impl->load_state = LOAD_EMPTY;
5668     }
5669   else
5670     g_assert (impl->load_state == LOAD_EMPTY ||
5671               impl->load_state == LOAD_LOADING ||
5672               impl->load_state == LOAD_FINISHED);
5673 }
5674
5675 /* Selects the first row in the file list */
5676 static void
5677 browse_files_select_first_row (GtkFileChooserDefault *impl)
5678 {
5679   GtkTreePath *path;
5680
5681   if (!impl->sort_model)
5682     return;
5683
5684   path = gtk_tree_path_new_from_indices (0, -1);
5685   gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), path, NULL, FALSE);
5686   gtk_tree_path_free (path);
5687 }
5688
5689 struct center_selected_row_closure {
5690   GtkFileChooserDefault *impl;
5691   gboolean already_centered;
5692 };
5693
5694 /* Callback used from gtk_tree_selection_selected_foreach(); centers the
5695  * selected row in the tree view.
5696  */
5697 static void
5698 center_selected_row_foreach_cb (GtkTreeModel      *model,
5699                                 GtkTreePath       *path,
5700                                 GtkTreeIter       *iter,
5701                                 gpointer           data)
5702 {
5703   struct center_selected_row_closure *closure;
5704
5705   closure = data;
5706   if (closure->already_centered)
5707     return;
5708
5709   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (closure->impl->browse_files_tree_view), path, NULL, TRUE, 0.5, 0.0);
5710   closure->already_centered = TRUE;
5711 }
5712
5713 /* Centers the selected row in the tree view */
5714 static void
5715 browse_files_center_selected_row (GtkFileChooserDefault *impl)
5716 {
5717   struct center_selected_row_closure closure;
5718   GtkTreeSelection *selection;
5719
5720   closure.impl = impl;
5721   closure.already_centered = FALSE;
5722
5723   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5724   gtk_tree_selection_selected_foreach (selection, center_selected_row_foreach_cb, &closure);
5725 }
5726
5727 struct ShowAndSelectPathsData
5728 {
5729   GtkFileChooserDefault *impl;
5730   GSList *paths;
5731 };
5732
5733 static void
5734 show_and_select_paths_finished_loading (GtkFileFolder *folder,
5735                                         gpointer user_data)
5736 {
5737   gboolean have_hidden;
5738   gboolean have_filtered;
5739   GSList *l;
5740   struct ShowAndSelectPathsData *data = user_data;
5741
5742   have_hidden = FALSE;
5743   have_filtered = FALSE;
5744
5745   for (l = data->paths; l; l = l->next)
5746     {
5747       const GtkFilePath *path;
5748       GtkFileInfo *info;
5749
5750       path = l->data;
5751
5752       /* NULL GError */
5753       info = gtk_file_folder_get_info (folder, path, NULL);
5754       if (info)
5755         {
5756           if (!have_hidden)
5757             have_hidden = gtk_file_info_get_is_hidden (info);
5758
5759           if (!have_filtered)
5760             have_filtered = !gtk_file_info_get_is_folder (info) && get_is_file_filtered (data->impl, path, info);
5761
5762           gtk_file_info_free (info);
5763
5764           if (have_hidden && have_filtered)
5765             break; /* we now have all the information we need */
5766         }
5767     }
5768
5769   g_signal_handlers_disconnect_by_func (folder,
5770                                         show_and_select_paths_finished_loading,
5771                                         user_data);
5772
5773   g_object_unref (folder);
5774
5775   if (have_hidden)
5776     g_object_set (data->impl, "show-hidden", TRUE, NULL);
5777
5778   if (have_filtered)
5779     set_current_filter (data->impl, NULL);
5780
5781   for (l = data->paths; l; l = l->next)
5782     {
5783       const GtkFilePath *path;
5784
5785       path = l->data;
5786       _gtk_file_system_model_path_do (data->impl->browse_files_model, path,
5787                                       select_func, data->impl);
5788     }
5789
5790   browse_files_center_selected_row (data->impl);
5791
5792   gtk_file_paths_free (data->paths);
5793   g_free (data);
5794 }
5795
5796 static void
5797 show_and_select_paths_get_folder_cb (GtkFileSystemHandle   *handle,
5798                                      GtkFileFolder         *folder,
5799                                      const GError          *error,
5800                                      gpointer               user_data)
5801 {
5802   gboolean cancelled = handle->cancelled;
5803   struct ShowAndSelectPathsData *data = user_data;
5804
5805   if (data->impl->show_and_select_paths_handle != handle)
5806     goto out;
5807
5808   data->impl->show_and_select_paths_handle = NULL;
5809
5810   if (cancelled || error)
5811     goto out;
5812
5813   g_object_unref (handle);
5814
5815   if (gtk_file_folder_is_finished_loading (folder))
5816     show_and_select_paths_finished_loading (folder, user_data);
5817   else
5818     g_signal_connect (folder, "finished-loading",
5819                       G_CALLBACK (show_and_select_paths_finished_loading),
5820                       user_data);
5821
5822   return;
5823
5824 out:
5825   g_object_unref (data->impl);
5826   gtk_file_paths_free (data->paths);
5827   g_free (data);
5828
5829   g_object_unref (handle);
5830 }
5831
5832 static gboolean
5833 show_and_select_paths (GtkFileChooserDefault *impl,
5834                        const GtkFilePath     *parent_path,
5835                        GSList                *paths,
5836                        GError                **error)
5837 {
5838   struct ShowAndSelectPathsData *info;
5839
5840   profile_start ("start", NULL);
5841
5842   if (!paths)
5843     {
5844       profile_end ("end", NULL);
5845       return TRUE;
5846     }
5847
5848   info = g_new (struct ShowAndSelectPathsData, 1);
5849   info->impl = g_object_ref (impl);
5850   info->paths = gtk_file_paths_copy (paths);
5851
5852   if (impl->show_and_select_paths_handle)
5853     gtk_file_system_cancel_operation (impl->show_and_select_paths_handle);
5854
5855   impl->show_and_select_paths_handle =
5856     gtk_file_system_get_folder (impl->file_system, parent_path,
5857                                 GTK_FILE_INFO_IS_FOLDER | GTK_FILE_INFO_IS_HIDDEN,
5858                                 show_and_select_paths_get_folder_cb, info);
5859
5860   profile_end ("end", NULL);
5861   return TRUE;
5862 }
5863
5864 /* Processes the pending operation when a folder is finished loading */
5865 static void
5866 pending_select_paths_process (GtkFileChooserDefault *impl)
5867 {
5868   g_assert (impl->load_state == LOAD_FINISHED);
5869   g_assert (impl->browse_files_model != NULL);
5870   g_assert (impl->sort_model != NULL);
5871
5872   impl->processing_pending_selections = TRUE;
5873
5874   if (impl->pending_select_paths)
5875     {
5876       /* NULL GError */
5877       show_and_select_paths (impl, impl->current_folder, impl->pending_select_paths, NULL);
5878       pending_select_paths_free (impl);
5879       browse_files_center_selected_row (impl);
5880     }
5881   else
5882     {
5883       /* We only select the first row if the chooser is actually mapped ---
5884        * selecting the first row is to help the user when he is interacting with
5885        * the chooser, but sometimes a chooser works not on behalf of the user,
5886        * but rather on behalf of something else like GtkFileChooserButton.  In
5887        * that case, the chooser's selection should be what the caller expects,
5888        * as the user can't see that something else got selected.  See bug #165264.
5889        *
5890        * Also, we don't select the first file if we are not in OPEN mode.  Doing
5891        * so would change the contents of the filename entry for SAVE or
5892        * CREATE_FOLDER, which is undesired; in SELECT_FOLDER, we don't want to
5893        * select a *different* folder from the one into which the user just
5894        * navigated.
5895        */
5896       if (GTK_WIDGET_MAPPED (impl) && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
5897         browse_files_select_first_row (impl);
5898     }
5899
5900   impl->processing_pending_selections = FALSE;
5901
5902   g_assert (impl->pending_select_paths == NULL);
5903 }
5904
5905 /* Callback used when the file system model finishes loading */
5906 static void
5907 browse_files_model_finished_loading_cb (GtkFileSystemModel    *model,
5908                                         GtkFileChooserDefault *impl)
5909 {
5910   profile_start ("start", NULL);
5911
5912   if (impl->load_state == LOAD_PRELOAD)
5913     {
5914       load_remove_timer (impl);
5915       load_set_model (impl);
5916     }
5917   else if (impl->load_state == LOAD_LOADING)
5918     {
5919       /* Nothing */
5920     }
5921   else
5922     {
5923       /* We can't g_assert_not_reached(), as something other than us may have
5924        *  initiated a folder reload.  See #165556.
5925        */
5926       profile_end ("end", NULL);
5927       return;
5928     }
5929
5930   g_assert (impl->load_timeout_id == 0);
5931
5932   impl->load_state = LOAD_FINISHED;
5933
5934   pending_select_paths_process (impl);
5935   set_busy_cursor (impl, FALSE);
5936 #ifdef PROFILE_FILE_CHOOSER
5937   access ("MARK: *** FINISHED LOADING", F_OK);
5938 #endif
5939
5940   profile_end ("end", NULL);
5941 }
5942
5943 /* Gets rid of the old list model and creates a new one for the current folder */
5944 static gboolean
5945 set_list_model (GtkFileChooserDefault *impl,
5946                 GError               **error)
5947 {
5948   g_assert (impl->current_folder != NULL);
5949
5950   profile_start ("start", NULL);
5951
5952   load_remove_timer (impl); /* This changes the state to LOAD_EMPTY */
5953
5954   if (impl->browse_files_model)
5955     {
5956       g_object_unref (impl->browse_files_model);
5957       impl->browse_files_model = NULL;
5958     }
5959
5960   if (impl->sort_model)
5961     {
5962       g_object_unref (impl->sort_model);
5963       impl->sort_model = NULL;
5964     }
5965
5966   set_busy_cursor (impl, TRUE);
5967   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
5968
5969   impl->browse_files_model = _gtk_file_system_model_new (impl->file_system,
5970                                                          impl->current_folder, 0,
5971                                                          GTK_FILE_INFO_ALL,
5972                                                          error);
5973   if (!impl->browse_files_model)
5974     {
5975       set_busy_cursor (impl, FALSE);
5976       profile_end ("end", NULL);
5977       return FALSE;
5978     }
5979
5980   load_setup_timer (impl); /* This changes the state to LOAD_PRELOAD */
5981
5982   g_signal_connect (impl->browse_files_model, "finished-loading",
5983                     G_CALLBACK (browse_files_model_finished_loading_cb), impl);
5984
5985   _gtk_file_system_model_set_show_hidden (impl->browse_files_model, impl->show_hidden);
5986
5987   install_list_model_filter (impl);
5988
5989   profile_end ("end", NULL);
5990
5991   return TRUE;
5992 }
5993
5994 struct update_chooser_entry_selected_foreach_closure {
5995   int num_selected;
5996   GtkTreeIter first_selected_iter;
5997 };
5998
5999 static void
6000 update_chooser_entry_selected_foreach (GtkTreeModel *model,
6001                                        GtkTreePath *path,
6002                                        GtkTreeIter *iter,
6003                                        gpointer data)
6004 {
6005   struct update_chooser_entry_selected_foreach_closure *closure;
6006
6007   closure = data;
6008   closure->num_selected++;
6009
6010   if (closure->num_selected == 1)
6011     closure->first_selected_iter = *iter;
6012 }
6013
6014 static void
6015 update_chooser_entry (GtkFileChooserDefault *impl)
6016 {
6017   GtkTreeSelection *selection;
6018   struct update_chooser_entry_selected_foreach_closure closure;
6019   const char *file_part;
6020
6021   if (impl->processing_pending_selections)
6022     return;
6023
6024   if (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6025         || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
6026         || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
6027              || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
6028             && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)))
6029     return;
6030
6031   g_assert (impl->location_entry != NULL);
6032
6033   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6034
6035   closure.num_selected = 0;
6036   gtk_tree_selection_selected_foreach (selection, update_chooser_entry_selected_foreach, &closure);
6037
6038   file_part = NULL;
6039
6040   if (closure.num_selected == 0)
6041     {
6042       /* If nothing is selected, we only reset the file name entry if we are in
6043        * CREATE_FOLDER mode.  In SAVE mode, nothing will be selected when the
6044        * user starts typeahead in the treeview, and we don't want to clear the
6045        * file name entry in that case --- the user could be typing-ahead to look
6046        * for a folder name.  See http://bugzilla.gnome.org/show_bug.cgi?id=308332
6047        */
6048       if (impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6049         file_part = "";
6050     }
6051   else if (closure.num_selected == 1)
6052     {
6053       GtkTreeIter child_iter;
6054       const GtkFileInfo *info;
6055       gboolean change_entry;
6056
6057       gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
6058                                                       &child_iter,
6059                                                       &closure.first_selected_iter);
6060
6061       info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
6062
6063       if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
6064           || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6065           || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
6066         change_entry = !gtk_file_info_get_is_folder (info); /* We don't want the name to change when clicking on a folder... */
6067       else
6068         change_entry = TRUE;                                /* ... unless we are in CREATE_FOLDER mode */
6069
6070       if (change_entry)
6071         file_part = gtk_file_info_get_display_name (info);
6072     }
6073   else
6074     {
6075       /* Multiple selection, so just clear the entry. */
6076       file_part = "";
6077     }
6078
6079   if (file_part)
6080     _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), file_part);
6081 }
6082
6083 static gboolean
6084 gtk_file_chooser_default_set_current_folder (GtkFileChooser    *chooser,
6085                                              const GtkFilePath *path,
6086                                              GError           **error)
6087 {
6088   return gtk_file_chooser_default_update_current_folder (chooser, path, FALSE, error);
6089 }
6090
6091
6092 struct UpdateCurrentFolderData
6093 {
6094   GtkFileChooserDefault *impl;
6095   GtkFilePath *path;
6096   gboolean keep_trail;
6097 };
6098
6099 static void
6100 update_current_folder_get_info_cb (GtkFileSystemHandle *handle,
6101                                    const GtkFileInfo   *info,
6102                                    const GError        *error,
6103                                    gpointer             user_data)
6104 {
6105   gboolean cancelled = handle->cancelled;
6106   struct UpdateCurrentFolderData *data = user_data;
6107   GtkFileChooserDefault *impl = data->impl;
6108
6109   if (handle != impl->update_current_folder_handle)
6110     goto out;
6111
6112   impl->update_current_folder_handle = NULL;
6113   impl->reload_state = RELOAD_EMPTY;
6114
6115   set_busy_cursor (impl, FALSE);
6116
6117   if (cancelled)
6118     goto out;
6119
6120   if (error)
6121     {
6122       error_changing_folder_dialog (impl, data->path, g_error_copy (error));
6123       goto out;
6124     }
6125
6126   if (!gtk_file_info_get_is_folder (info))
6127     goto out;
6128
6129   if (!_gtk_path_bar_set_path (GTK_PATH_BAR (impl->browse_path_bar), data->path, data->keep_trail, NULL))
6130     goto out;
6131
6132   if (impl->current_folder != data->path)
6133     {
6134       if (impl->current_folder)
6135         gtk_file_path_free (impl->current_folder);
6136
6137       impl->current_folder = gtk_file_path_copy (data->path);
6138
6139       impl->reload_state = RELOAD_HAS_FOLDER;
6140     }
6141
6142   /* Update the widgets that may trigger a folder change themselves.  */
6143
6144   if (!impl->changing_folder)
6145     {
6146       impl->changing_folder = TRUE;
6147
6148       shortcuts_update_current_folder (impl);
6149
6150       impl->changing_folder = FALSE;
6151     }
6152
6153   /* Set the folder on the save entry */
6154
6155   if (impl->location_entry)
6156     _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
6157                                              impl->current_folder);
6158
6159   /* Create a new list model.  This is slightly evil; we store the result value
6160    * but perform more actions rather than returning immediately even if it
6161    * generates an error.
6162    */
6163   set_list_model (impl, NULL);
6164
6165   /* Refresh controls */
6166
6167   shortcuts_find_current_folder (impl);
6168
6169   g_signal_emit_by_name (impl, "current-folder-changed", 0);
6170
6171   check_preview_change (impl);
6172   bookmarks_check_add_sensitivity (impl);
6173
6174   g_signal_emit_by_name (impl, "selection-changed", 0);
6175
6176 out:
6177   gtk_file_path_free (data->path);
6178   g_free (data);
6179
6180   g_object_unref (handle);
6181 }
6182
6183 static gboolean
6184 gtk_file_chooser_default_update_current_folder (GtkFileChooser    *chooser,
6185                                                 const GtkFilePath *path,
6186                                                 gboolean           keep_trail,
6187                                                 GError           **error)
6188 {
6189   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
6190   struct UpdateCurrentFolderData *data;
6191
6192   profile_start ("start", (char *) path);
6193
6194   g_assert (path != NULL);
6195
6196   if (impl->local_only &&
6197       !gtk_file_system_path_is_local (impl->file_system, path))
6198     {
6199       g_set_error (error,
6200                    GTK_FILE_CHOOSER_ERROR,
6201                    GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
6202                    _("Cannot change to folder because it is not local"));
6203
6204       profile_end ("end - not local", (char *) path);
6205       return FALSE;
6206     }
6207
6208   if (impl->update_current_folder_handle)
6209     gtk_file_system_cancel_operation (impl->update_current_folder_handle);
6210
6211   /* Test validity of path here.  */
6212   data = g_new0 (struct UpdateCurrentFolderData, 1);
6213   data->impl = impl;
6214   data->path = gtk_file_path_copy (path);
6215   data->keep_trail = keep_trail;
6216
6217   impl->reload_state = RELOAD_HAS_FOLDER;
6218
6219   impl->update_current_folder_handle =
6220     gtk_file_system_get_info (impl->file_system, path, GTK_FILE_INFO_IS_FOLDER,
6221                               update_current_folder_get_info_cb,
6222                               data);
6223
6224   set_busy_cursor (impl, TRUE);
6225
6226   profile_end ("end", NULL);
6227   return TRUE;
6228 }
6229
6230 static GtkFilePath *
6231 gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser)
6232 {
6233   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
6234
6235   if (impl->reload_state == RELOAD_EMPTY)
6236     {
6237       char *current_working_dir;
6238       GtkFilePath *path;
6239
6240       /* We are unmapped, or we had an error while loading the last folder.  We'll return
6241        * the $cwd since once we get (re)mapped, we'll load $cwd anyway unless the caller
6242        * explicitly calls set_current_folder() on us.
6243        */
6244       current_working_dir = g_get_current_dir ();
6245       path = gtk_file_system_filename_to_path (impl->file_system, current_working_dir);
6246       g_free (current_working_dir);
6247       return path;
6248     }
6249
6250   return gtk_file_path_copy (impl->current_folder);
6251 }
6252
6253 static void
6254 gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
6255                                            const gchar    *name)
6256 {
6257   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
6258
6259   g_return_if_fail (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6260                     || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
6261
6262   pending_select_paths_free (impl);
6263   _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), name);
6264 }
6265
6266 static void
6267 select_func (GtkFileSystemModel *model,
6268              GtkTreePath        *path,
6269              GtkTreeIter        *iter,
6270              gpointer            user_data)
6271 {
6272   GtkFileChooserDefault *impl = user_data;
6273   GtkTreeSelection *selection;
6274   GtkTreeIter sorted_iter;
6275
6276   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6277
6278   gtk_tree_model_sort_convert_child_iter_to_iter (impl->sort_model, &sorted_iter, iter);
6279   gtk_tree_selection_select_iter (selection, &sorted_iter);
6280 }
6281
6282 static gboolean
6283 gtk_file_chooser_default_select_path (GtkFileChooser    *chooser,
6284                                       const GtkFilePath *path,
6285                                       GError           **error)
6286 {
6287   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
6288   GtkFilePath *parent_path;
6289   gboolean same_path;
6290
6291   if (!gtk_file_system_get_parent (impl->file_system, path, &parent_path, error))
6292     return FALSE;
6293
6294   if (!parent_path)
6295     return _gtk_file_chooser_set_current_folder_path (chooser, path, error);
6296
6297   if (impl->load_state == LOAD_EMPTY)
6298     same_path = FALSE;
6299   else
6300     {
6301       g_assert (impl->current_folder != NULL);
6302
6303       same_path = gtk_file_path_compare (parent_path, impl->current_folder) == 0;
6304     }
6305
6306   if (same_path && impl->load_state == LOAD_FINISHED)
6307     {
6308       gboolean result;
6309       GSList paths;
6310
6311       paths.data = (gpointer) path;
6312       paths.next = NULL;
6313
6314       result = show_and_select_paths (impl, parent_path, &paths, error);
6315       gtk_file_path_free (parent_path);
6316       return result;
6317     }
6318
6319   pending_select_paths_add (impl, path);
6320
6321   if (!same_path)
6322     {
6323       gboolean result;
6324
6325       result = _gtk_file_chooser_set_current_folder_path (chooser, parent_path, error);
6326       gtk_file_path_free (parent_path);
6327       return result;
6328     }
6329
6330   gtk_file_path_free (parent_path);
6331   return TRUE;
6332 }
6333
6334 static void
6335 unselect_func (GtkFileSystemModel *model,
6336                GtkTreePath        *path,
6337                GtkTreeIter        *iter,
6338                gpointer            user_data)
6339 {
6340   GtkFileChooserDefault *impl = user_data;
6341   GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
6342   GtkTreePath *sorted_path;
6343
6344   sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model,
6345                                                                 path);
6346   gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (tree_view),
6347                                     sorted_path);
6348   gtk_tree_path_free (sorted_path);
6349 }
6350
6351 static void
6352 gtk_file_chooser_default_unselect_path (GtkFileChooser    *chooser,
6353                                         const GtkFilePath *path)
6354 {
6355   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
6356
6357   if (!impl->browse_files_model)
6358     return;
6359
6360   _gtk_file_system_model_path_do (impl->browse_files_model, path,
6361                                   unselect_func, impl);
6362 }
6363
6364 static gboolean
6365 maybe_select (GtkTreeModel *model, 
6366               GtkTreePath  *path, 
6367               GtkTreeIter  *iter, 
6368               gpointer     data)
6369 {
6370   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (data);
6371   GtkTreeSelection *selection;
6372   const GtkFileInfo *info;
6373   gboolean is_folder;
6374   
6375   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6376   
6377   info = get_list_file_info (impl, iter);
6378   is_folder = gtk_file_info_get_is_folder (info);
6379
6380   if ((is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) ||
6381       (!is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN))
6382     gtk_tree_selection_select_iter (selection, iter);
6383   else
6384     gtk_tree_selection_unselect_iter (selection, iter);
6385     
6386   return FALSE;
6387 }
6388
6389 static void
6390 gtk_file_chooser_default_select_all (GtkFileChooser *chooser)
6391 {
6392   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
6393   if (impl->select_multiple)
6394     gtk_tree_model_foreach (GTK_TREE_MODEL (impl->sort_model), 
6395                             maybe_select, impl);
6396 }
6397
6398 static void
6399 gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser)
6400 {
6401   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
6402   GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6403
6404   gtk_tree_selection_unselect_all (selection);
6405   pending_select_paths_free (impl);
6406 }
6407
6408 /* Checks whether the filename entry for the Save modes contains a well-formed filename.
6409  *
6410  * is_well_formed_ret - whether what the user typed passes gkt_file_system_make_path()
6411  *
6412  * is_empty_ret - whether the file entry is totally empty
6413  *
6414  * is_file_part_empty_ret - whether the file part is empty (will be if user types "foobar/", and
6415  *                          the path will be "$cwd/foobar")
6416  */
6417 static void
6418 check_save_entry (GtkFileChooserDefault *impl,
6419                   GtkFilePath          **path_ret,
6420                   gboolean              *is_well_formed_ret,
6421                   gboolean              *is_empty_ret,
6422                   gboolean              *is_file_part_empty_ret,
6423                   gboolean              *is_folder)
6424 {
6425   GtkFileChooserEntry *chooser_entry;
6426   const GtkFilePath *current_folder;
6427   const char *file_part;
6428   GtkFilePath *path;
6429   GError *error;
6430
6431   g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6432             || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
6433             || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
6434                  || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
6435                 && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY));
6436
6437   chooser_entry = GTK_FILE_CHOOSER_ENTRY (impl->location_entry);
6438
6439   if (strlen (gtk_entry_get_text (GTK_ENTRY (chooser_entry))) == 0)
6440     {
6441       *path_ret = NULL;
6442       *is_well_formed_ret = TRUE;
6443       *is_empty_ret = TRUE;
6444       *is_file_part_empty_ret = TRUE;
6445       *is_folder = FALSE;
6446
6447       return;
6448     }
6449
6450   *is_empty_ret = FALSE;
6451
6452   current_folder = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
6453   file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
6454
6455   if (!file_part || file_part[0] == '\0')
6456     {
6457       *path_ret = gtk_file_path_copy (current_folder);
6458       *is_well_formed_ret = TRUE;
6459       *is_file_part_empty_ret = TRUE;
6460       *is_folder = TRUE;
6461
6462       return;
6463     }
6464
6465   *is_file_part_empty_ret = FALSE;
6466
6467   error = NULL;
6468   path = gtk_file_system_make_path (impl->file_system, current_folder, file_part, &error);
6469
6470   if (!path)
6471     {
6472       error_building_filename_dialog (impl, current_folder, file_part, error);
6473       *path_ret = NULL;
6474       *is_well_formed_ret = FALSE;
6475       *is_folder = FALSE;
6476
6477       return;
6478     }
6479
6480   *path_ret = path;
6481   *is_well_formed_ret = TRUE;
6482   *is_folder = _gtk_file_chooser_entry_get_is_folder (chooser_entry, path);
6483 }
6484
6485 struct get_paths_closure {
6486   GtkFileChooserDefault *impl;
6487   GSList *result;
6488   GtkFilePath *path_from_entry;
6489 };
6490
6491 static void
6492 get_paths_foreach (GtkTreeModel *model,
6493                    GtkTreePath  *path,
6494                    GtkTreeIter  *iter,
6495                    gpointer      data)
6496 {
6497   struct get_paths_closure *info;
6498   const GtkFilePath *file_path;
6499   GtkFileSystemModel *fs_model;
6500   GtkTreeIter sel_iter;
6501
6502   info = data;
6503   fs_model = info->impl->browse_files_model;
6504   gtk_tree_model_sort_convert_iter_to_child_iter (info->impl->sort_model, &sel_iter, iter);
6505
6506   file_path = _gtk_file_system_model_get_path (fs_model, &sel_iter);
6507   if (!file_path)
6508     return; /* We are on the editable row */
6509
6510   if (!info->path_from_entry
6511       || gtk_file_path_compare (info->path_from_entry, file_path) != 0)
6512     info->result = g_slist_prepend (info->result, gtk_file_path_copy (file_path));
6513 }
6514
6515 static GSList *
6516 gtk_file_chooser_default_get_paths (GtkFileChooser *chooser)
6517 {
6518   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
6519   struct get_paths_closure info;
6520   GtkWindow *toplevel;
6521   GtkWidget *current_focus;
6522
6523   info.impl = impl;
6524   info.result = NULL;
6525   info.path_from_entry = NULL;
6526
6527   toplevel = get_toplevel (GTK_WIDGET (impl));
6528   if (toplevel)
6529     current_focus = gtk_window_get_focus (toplevel);
6530   else
6531     current_focus = NULL;
6532
6533   if (current_focus == impl->browse_files_tree_view)
6534     {
6535       GtkTreeSelection *selection;
6536
6537     file_list:
6538
6539       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6540       gtk_tree_selection_selected_foreach (selection, get_paths_foreach, &info);
6541
6542       /* If there is no selection in the file list, we probably have this situation:
6543        *
6544        * 1. The user typed a filename in the SAVE filename entry ("foo.txt").
6545        * 2. He then double-clicked on a folder ("bar") in the file list
6546        *
6547        * So we want the selection to be "bar/foo.txt".  Jump to the case for the
6548        * filename entry to see if that is the case.
6549        */
6550       if (info.result == NULL && impl->location_entry)
6551         goto file_entry;
6552     }
6553   else if (impl->location_entry && current_focus == impl->location_entry)
6554     {
6555       gboolean is_well_formed, is_empty, is_file_part_empty, is_folder;
6556
6557     file_entry:
6558
6559       check_save_entry (impl, &info.path_from_entry, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder);
6560
6561       if (!is_well_formed)
6562         return NULL;
6563
6564       if (!is_empty)
6565         {
6566           if (is_file_part_empty && impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
6567             {
6568               gtk_file_path_free (info.path_from_entry);
6569               return NULL;
6570             }
6571         }
6572
6573       info.result = g_slist_prepend (info.result, info.path_from_entry);
6574     }
6575   else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
6576     goto file_list;
6577   else if (impl->location_entry && impl->toplevel_last_focus_widget == impl->location_entry)
6578     goto file_entry;
6579   else
6580     {
6581     /* The focus is on a dialog's action area button or something else */
6582       if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6583           || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6584         goto file_entry;
6585       else
6586         goto file_list; 
6587     }
6588
6589   /* If there's no folder selected, and we're in SELECT_FOLDER mode, then we
6590    * fall back to the current directory */
6591   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
6592       info.result == NULL)
6593     {
6594       info.result = g_slist_prepend (info.result, _gtk_file_chooser_get_current_folder_path (chooser));
6595     }
6596
6597   return g_slist_reverse (info.result);
6598 }
6599
6600 static GtkFilePath *
6601 gtk_file_chooser_default_get_preview_path (GtkFileChooser *chooser)
6602 {
6603   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
6604
6605   if (impl->preview_path)
6606     return gtk_file_path_copy (impl->preview_path);
6607   else
6608     return NULL;
6609 }
6610
6611 static GtkFileSystem *
6612 gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser)
6613 {
6614   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
6615
6616   return impl->file_system;
6617 }
6618
6619 /* Shows or hides the filter widgets */
6620 static void
6621 show_filters (GtkFileChooserDefault *impl,
6622               gboolean               show)
6623 {
6624   if (show)
6625     gtk_widget_show (impl->filter_combo_hbox);
6626   else
6627     gtk_widget_hide (impl->filter_combo_hbox);
6628 }
6629
6630 static void
6631 gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
6632                                      GtkFileFilter  *filter)
6633 {
6634   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
6635   const gchar *name;
6636
6637   if (g_slist_find (impl->filters, filter))
6638     {
6639       g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
6640       return;
6641     }
6642
6643   g_object_ref_sink (filter);
6644   impl->filters = g_slist_append (impl->filters, filter);
6645
6646   name = gtk_file_filter_get_name (filter);
6647   if (!name)
6648     name = "Untitled filter";   /* Place-holder, doesn't need to be marked for translation */
6649
6650   gtk_combo_box_append_text (GTK_COMBO_BOX (impl->filter_combo), name);
6651
6652   if (!g_slist_find (impl->filters, impl->current_filter))
6653     set_current_filter (impl, filter);
6654
6655   show_filters (impl, TRUE);
6656 }
6657
6658 static void
6659 gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
6660                                         GtkFileFilter  *filter)
6661 {
6662   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
6663   GtkTreeModel *model;
6664   GtkTreeIter iter;
6665   gint filter_index;
6666
6667   filter_index = g_slist_index (impl->filters, filter);
6668
6669   if (filter_index < 0)
6670     {
6671       g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
6672       return;
6673     }
6674
6675   impl->filters = g_slist_remove (impl->filters, filter);
6676
6677   if (filter == impl->current_filter)
6678     {
6679       if (impl->filters)
6680         set_current_filter (impl, impl->filters->data);
6681       else
6682         set_current_filter (impl, NULL);
6683     }
6684
6685   /* Remove row from the combo box */
6686   model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->filter_combo));
6687   if (!gtk_tree_model_iter_nth_child  (model, &iter, NULL, filter_index))
6688     g_assert_not_reached ();
6689
6690   gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
6691
6692   g_object_unref (filter);
6693
6694   if (!impl->filters)
6695     show_filters (impl, FALSE);
6696 }
6697
6698 static GSList *
6699 gtk_file_chooser_default_list_filters (GtkFileChooser *chooser)
6700 {
6701   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
6702
6703   return g_slist_copy (impl->filters);
6704 }
6705
6706 /* Returns the position in the shortcuts tree where the nth specified shortcut would appear */
6707 static int
6708 shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl,
6709                                        int                    pos)
6710 {
6711   return pos + shortcuts_get_index (impl, SHORTCUTS_SHORTCUTS);
6712 }
6713
6714 struct AddShortcutData
6715 {
6716   GtkFileChooserDefault *impl;
6717   GtkFilePath *path;
6718 };
6719
6720 static void
6721 add_shortcut_get_info_cb (GtkFileSystemHandle *handle,
6722                           const GtkFileInfo   *info,
6723                           const GError        *error,
6724                           gpointer             user_data)
6725 {
6726   int pos;
6727   gboolean cancelled = handle->cancelled;
6728   struct AddShortcutData *data = user_data;
6729
6730   if (!g_slist_find (data->impl->loading_shortcuts, handle))
6731     goto out;
6732
6733   data->impl->loading_shortcuts = g_slist_remove (data->impl->loading_shortcuts, handle);
6734
6735   if (cancelled || error || !gtk_file_info_get_is_folder (info))
6736     goto out;
6737
6738   pos = shortcuts_get_pos_for_shortcut_folder (data->impl, data->impl->num_shortcuts);
6739
6740   shortcuts_insert_path (data->impl, pos, FALSE, NULL, data->path, NULL, FALSE, SHORTCUTS_SHORTCUTS);
6741
6742 out:
6743   g_object_unref (data->impl);
6744   gtk_file_path_free (data->path);
6745   g_free (data);
6746
6747   g_object_unref (handle);
6748 }
6749
6750 static gboolean
6751 gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser    *chooser,
6752                                               const GtkFilePath *path,
6753                                               GError           **error)
6754 {
6755   GtkFileSystemHandle *handle;
6756   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
6757   struct AddShortcutData *data;
6758   GSList *l;
6759   int pos;
6760
6761   /* Avoid adding duplicates */
6762   pos = shortcut_find_position (impl, path);
6763   if (pos >= 0 && pos < shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR))
6764     {
6765       gchar *uri;
6766
6767       uri = gtk_file_system_path_to_uri (impl->file_system, path);
6768       /* translators, "Shortcut" means "Bookmark" here */
6769       g_set_error (error,
6770                    GTK_FILE_CHOOSER_ERROR,
6771                    GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
6772                    _("Shortcut %s already exists"),
6773                    uri);
6774       g_free (uri);
6775
6776       return FALSE;
6777     }
6778
6779   for (l = impl->loading_shortcuts; l; l = l->next)
6780     {
6781       GtkFileSystemHandle *h = l->data;
6782       GtkFilePath *p;
6783
6784       p = g_object_get_data (G_OBJECT (h), "add-shortcut-path-key");
6785       if (p && !gtk_file_path_compare (path, p))
6786         {
6787           gchar *uri;
6788
6789           uri = gtk_file_system_path_to_uri (impl->file_system, path);
6790           g_set_error (error,
6791                        GTK_FILE_CHOOSER_ERROR,
6792                        GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
6793                        _("Shortcut %s already exists"),
6794                        uri);
6795           g_free (uri);
6796
6797           return FALSE;
6798         }
6799     }
6800
6801   data = g_new0 (struct AddShortcutData, 1);
6802   data->impl = g_object_ref (impl);
6803   data->path = gtk_file_path_copy (path);
6804
6805   handle = gtk_file_system_get_info (impl->file_system, path,
6806                                      GTK_FILE_INFO_IS_FOLDER,
6807                                      add_shortcut_get_info_cb, data);
6808
6809   if (!handle)
6810     return FALSE;
6811
6812   impl->loading_shortcuts = g_slist_append (impl->loading_shortcuts, handle);
6813   g_object_set_data (G_OBJECT (handle), "add-shortcut-path-key", data->path);
6814
6815   return TRUE;
6816 }
6817
6818 static gboolean
6819 gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
6820                                                  const GtkFilePath *path,
6821                                                  GError           **error)
6822 {
6823   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
6824   int pos;
6825   GtkTreeIter iter;
6826   GSList *l;
6827   char *uri;
6828   int i;
6829
6830   for (l = impl->loading_shortcuts; l; l = l->next)
6831     {
6832       GtkFileSystemHandle *h = l->data;
6833       GtkFilePath *p;
6834
6835       p = g_object_get_data (G_OBJECT (h), "add-shortcut-path-key");
6836       if (p && !gtk_file_path_compare (path, p))
6837         {
6838           impl->loading_shortcuts = g_slist_remove (impl->loading_shortcuts, h);
6839           gtk_file_system_cancel_operation (h);
6840           return TRUE;
6841         }
6842     }
6843
6844   if (impl->num_shortcuts == 0)
6845     goto out;
6846
6847   pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
6848   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
6849     g_assert_not_reached ();
6850
6851   for (i = 0; i < impl->num_shortcuts; i++)
6852     {
6853       gpointer col_data;
6854       gboolean is_volume;
6855       GtkFilePath *shortcut;
6856
6857       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
6858                           SHORTCUTS_COL_DATA, &col_data,
6859                           SHORTCUTS_COL_IS_VOLUME, &is_volume,
6860                           -1);
6861       g_assert (col_data != NULL);
6862       g_assert (!is_volume);
6863
6864       shortcut = col_data;
6865       if (gtk_file_path_compare (shortcut, path) == 0)
6866         {
6867           shortcuts_remove_rows (impl, pos + i, 1);
6868           impl->num_shortcuts--;
6869           return TRUE;
6870         }
6871
6872       if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
6873         g_assert_not_reached ();
6874     }
6875
6876  out:
6877
6878   uri = gtk_file_system_path_to_uri (impl->file_system, path);
6879   /* translators, "Shortcut" means "Bookmark" here */
6880   g_set_error (error,
6881                GTK_FILE_CHOOSER_ERROR,
6882                GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
6883                _("Shortcut %s does not exist"),
6884                uri);
6885   g_free (uri);
6886
6887   return FALSE;
6888 }
6889
6890 static GSList *
6891 gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser)
6892 {
6893   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
6894   int pos;
6895   GtkTreeIter iter;
6896   int i;
6897   GSList *list;
6898
6899   if (impl->num_shortcuts == 0)
6900     return NULL;
6901
6902   pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
6903   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
6904     g_assert_not_reached ();
6905
6906   list = NULL;
6907
6908   for (i = 0; i < impl->num_shortcuts; i++)
6909     {
6910       gpointer col_data;
6911       gboolean is_volume;
6912       GtkFilePath *shortcut;
6913
6914       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
6915                           SHORTCUTS_COL_DATA, &col_data,
6916                           SHORTCUTS_COL_IS_VOLUME, &is_volume,
6917                           -1);
6918       g_assert (col_data != NULL);
6919       g_assert (!is_volume);
6920
6921       shortcut = col_data;
6922       list = g_slist_prepend (list, gtk_file_path_copy (shortcut));
6923
6924       if (i != impl->num_shortcuts - 1)
6925         {
6926           if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
6927             g_assert_not_reached ();
6928         }
6929     }
6930
6931   return g_slist_reverse (list);
6932 }
6933
6934 /* Guesses a size based upon font sizes */
6935 static void
6936 find_good_size_from_style (GtkWidget *widget,
6937                            gint      *width,
6938                            gint      *height)
6939 {
6940   GtkFileChooserDefault *impl;
6941   gint default_width, default_height;
6942   int font_size;
6943   GtkRequisition req;
6944   GtkRequisition preview_req;
6945
6946   g_assert (widget->style != NULL);
6947   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
6948
6949   font_size = pango_font_description_get_size (widget->style->font_desc);
6950   font_size = PANGO_PIXELS (font_size);
6951
6952   default_width = font_size * NUM_CHARS;
6953   default_height = font_size * NUM_LINES;
6954
6955   /* Use at least the requisition size not including the preview widget */
6956   gtk_widget_size_request (widget, &req);
6957
6958   if (impl->preview_widget_active && impl->preview_widget)
6959     gtk_widget_size_request (impl->preview_box, &preview_req);
6960   else
6961     preview_req.width = 0;
6962
6963   default_width = MAX (default_width, (req.width - (preview_req.width + PREVIEW_HBOX_SPACING)));
6964   default_height = MAX (default_height, req.height);
6965
6966   *width = default_width;
6967   *height = default_height;
6968 }
6969
6970 static void
6971 gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
6972                                            gint                *default_width,
6973                                            gint                *default_height)
6974 {
6975   GtkFileChooserDefault *impl;
6976
6977   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
6978
6979   find_good_size_from_style (GTK_WIDGET (chooser_embed), default_width, default_height);
6980
6981   if (impl->preview_widget_active && impl->preview_widget)
6982     *default_width += impl->preview_box->requisition.width + PREVIEW_HBOX_SPACING;
6983 }
6984
6985 static void
6986 gtk_file_chooser_default_get_resizable_hints (GtkFileChooserEmbed *chooser_embed,
6987                                               gboolean            *resize_horizontally,
6988                                               gboolean            *resize_vertically)
6989 {
6990   GtkFileChooserDefault *impl;
6991
6992   g_return_if_fail (resize_horizontally != NULL);
6993   g_return_if_fail (resize_vertically != NULL);
6994
6995   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
6996
6997   *resize_horizontally = TRUE;
6998   *resize_vertically = TRUE;
6999
7000   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
7001       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
7002     {
7003       if (! gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
7004         {
7005           *resize_horizontally = FALSE;
7006           *resize_vertically = FALSE;
7007         }
7008     }
7009 }
7010
7011 struct switch_folder_closure {
7012   GtkFileChooserDefault *impl;
7013   const GtkFilePath *path;
7014   int num_selected;
7015 };
7016
7017 /* Used from gtk_tree_selection_selected_foreach() in switch_to_selected_folder() */
7018 static void
7019 switch_folder_foreach_cb (GtkTreeModel      *model,
7020                           GtkTreePath       *path,
7021                           GtkTreeIter       *iter,
7022                           gpointer           data)
7023 {
7024   struct switch_folder_closure *closure;
7025   GtkTreeIter child_iter;
7026
7027   closure = data;
7028
7029   gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
7030
7031   closure->path = _gtk_file_system_model_get_path (closure->impl->browse_files_model, &child_iter);
7032   closure->num_selected++;
7033 }
7034
7035 /* Changes to the selected folder in the list view */
7036 static void
7037 switch_to_selected_folder (GtkFileChooserDefault *impl)
7038 {
7039   GtkTreeSelection *selection;
7040   struct switch_folder_closure closure;
7041
7042   /* We do this with foreach() rather than get_selected() as we may be in
7043    * multiple selection mode
7044    */
7045
7046   closure.impl = impl;
7047   closure.path = NULL;
7048   closure.num_selected = 0;
7049
7050   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7051   gtk_tree_selection_selected_foreach (selection, switch_folder_foreach_cb, &closure);
7052
7053   g_assert (closure.path && closure.num_selected == 1);
7054
7055   change_folder_and_display_error (impl, closure.path);
7056 }
7057
7058 /* Gets the GtkFileInfo for the selected row in the file list; assumes single
7059  * selection mode.
7060  */
7061 static const GtkFileInfo *
7062 get_selected_file_info_from_file_list (GtkFileChooserDefault *impl,
7063                                        gboolean              *had_selection)
7064 {
7065   GtkTreeSelection *selection;
7066   GtkTreeIter iter, child_iter;
7067   const GtkFileInfo *info;
7068
7069   g_assert (!impl->select_multiple);
7070   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7071   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
7072     {
7073       *had_selection = FALSE;
7074       return NULL;
7075     }
7076
7077   *had_selection = TRUE;
7078
7079   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
7080                                                   &child_iter,
7081                                                   &iter);
7082
7083   info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
7084   return info;
7085 }
7086
7087 /* Gets the display name of the selected file in the file list; assumes single
7088  * selection mode and that something is selected.
7089  */
7090 static const gchar *
7091 get_display_name_from_file_list (GtkFileChooserDefault *impl)
7092 {
7093   const GtkFileInfo *info;
7094   gboolean had_selection;
7095
7096   info = get_selected_file_info_from_file_list (impl, &had_selection);
7097   g_assert (had_selection);
7098   g_assert (info != NULL);
7099
7100   return gtk_file_info_get_display_name (info);
7101 }
7102
7103 static void
7104 add_custom_button_to_dialog (GtkDialog   *dialog,
7105                              const gchar *mnemonic_label,
7106                              const gchar *stock_id,
7107                              gint         response_id)
7108 {
7109   GtkWidget *button;
7110
7111   button = gtk_button_new_with_mnemonic (mnemonic_label);
7112   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
7113   gtk_button_set_image (GTK_BUTTON (button),
7114                         gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON));
7115   gtk_widget_show (button);
7116
7117   gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, response_id);
7118 }
7119
7120 /* Presents an overwrite confirmation dialog; returns whether we should accept
7121  * the filename.
7122  */
7123 static gboolean
7124 confirm_dialog_should_accept_filename (GtkFileChooserDefault *impl,
7125                                        const gchar           *file_part,
7126                                        const gchar           *folder_display_name)
7127 {
7128   GtkWindow *toplevel;
7129   GtkWidget *dialog;
7130   int response;
7131
7132   toplevel = get_toplevel (GTK_WIDGET (impl));
7133
7134   dialog = gtk_message_dialog_new (toplevel,
7135                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
7136                                    GTK_MESSAGE_QUESTION,
7137                                    GTK_BUTTONS_NONE,
7138                                    _("A file named \"%s\" already exists.  Do you want to replace it?"),
7139                                    file_part);
7140   gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
7141                                             _("The file already exists in \"%s\".  Replacing it will "
7142                                               "overwrite its contents."),
7143                                             folder_display_name);
7144
7145   gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
7146   add_custom_button_to_dialog (GTK_DIALOG (dialog), _("_Replace"), GTK_STOCK_SAVE_AS, GTK_RESPONSE_ACCEPT);
7147   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
7148   
7149   if (toplevel->group)
7150     gtk_window_group_add_window (toplevel->group, GTK_WINDOW (dialog));
7151
7152   response = gtk_dialog_run (GTK_DIALOG (dialog));
7153
7154   gtk_widget_destroy (dialog);
7155
7156   return (response == GTK_RESPONSE_ACCEPT);
7157 }
7158
7159 struct GetDisplayNameData
7160 {
7161   GtkFileChooserDefault *impl;
7162   gchar *file_part;
7163 };
7164
7165 static void
7166 confirmation_confirm_get_info_cb (GtkFileSystemHandle *handle,
7167                                   const GtkFileInfo   *info,
7168                                   const GError        *error,
7169                                   gpointer             user_data)
7170 {
7171   gboolean cancelled = handle->cancelled;
7172   gboolean should_respond = FALSE;
7173   struct GetDisplayNameData *data = user_data;
7174
7175   if (handle != data->impl->should_respond_get_info_handle)
7176     goto out;
7177
7178   data->impl->should_respond_get_info_handle = NULL;
7179
7180   if (cancelled)
7181     goto out;
7182
7183   if (error)
7184     /* Huh?  Did the folder disappear?  Let the caller deal with it */
7185     should_respond = TRUE;
7186   else
7187     should_respond = confirm_dialog_should_accept_filename (data->impl, data->file_part, gtk_file_info_get_display_name (info));
7188
7189   set_busy_cursor (data->impl, FALSE);
7190   if (should_respond)
7191     g_signal_emit_by_name (data->impl, "response-requested");
7192
7193 out:
7194   g_object_unref (data->impl);
7195   g_free (data->file_part);
7196   g_free (data);
7197
7198   g_object_unref (handle);
7199 }
7200
7201 /* Does overwrite confirmation if appropriate, and returns whether the dialog
7202  * should respond.  Can get the file part from the file list or the save entry.
7203  */
7204 static gboolean
7205 should_respond_after_confirm_overwrite (GtkFileChooserDefault *impl,
7206                                         const gchar           *file_part,
7207                                         const GtkFilePath     *parent_path)
7208 {
7209   GtkFileChooserConfirmation conf;
7210
7211   if (!impl->do_overwrite_confirmation)
7212     return TRUE;
7213
7214   conf = GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM;
7215
7216   g_signal_emit_by_name (impl, "confirm-overwrite", &conf);
7217
7218   switch (conf)
7219     {
7220     case GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM:
7221       {
7222         struct GetDisplayNameData *data;
7223
7224         g_assert (file_part != NULL);
7225
7226         data = g_new0 (struct GetDisplayNameData, 1);
7227         data->impl = g_object_ref (impl);
7228         data->file_part = g_strdup (file_part);
7229
7230         if (impl->should_respond_get_info_handle)
7231           gtk_file_system_cancel_operation (impl->should_respond_get_info_handle);
7232
7233         impl->should_respond_get_info_handle =
7234           gtk_file_system_get_info (impl->file_system, parent_path,
7235                                     GTK_FILE_INFO_DISPLAY_NAME,
7236                                     confirmation_confirm_get_info_cb,
7237                                     data);
7238         set_busy_cursor (data->impl, TRUE);
7239         return FALSE;
7240       }
7241
7242     case GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME:
7243       return TRUE;
7244
7245     case GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN:
7246       return FALSE;
7247
7248     default:
7249       g_assert_not_reached ();
7250       return FALSE;
7251     }
7252 }
7253
7254 static void
7255 action_create_folder_cb (GtkFileSystemHandle *handle,
7256                          const GtkFilePath   *path,
7257                          const GError        *error,
7258                          gpointer             user_data)
7259 {
7260   gboolean cancelled = handle->cancelled;
7261   GtkFileChooserDefault *impl = user_data;
7262
7263   if (!g_slist_find (impl->pending_handles, handle))
7264     goto out;
7265
7266   impl->pending_handles = g_slist_remove (impl->pending_handles, handle);
7267
7268   set_busy_cursor (impl, FALSE);
7269
7270   if (cancelled)
7271     goto out;
7272
7273   if (error)
7274     error_creating_folder_dialog (impl, path, g_error_copy (error));
7275   else
7276     g_signal_emit_by_name (impl, "response-requested");
7277
7278 out:
7279   g_object_unref (impl);
7280   g_object_unref (handle);
7281 }
7282
7283 struct SaveEntryData
7284 {
7285   GtkFileChooserDefault *impl;
7286   gboolean file_exists_and_is_not_folder;
7287   GtkFilePath *parent_path;
7288   GtkFilePath *path;
7289 };
7290
7291 static void
7292 save_entry_get_info_cb (GtkFileSystemHandle *handle,
7293                         const GtkFileInfo   *info,
7294                         const GError        *error,
7295                         gpointer             user_data)
7296 {
7297   gboolean parent_is_folder;
7298   gboolean cancelled = handle->cancelled;
7299   struct SaveEntryData *data = user_data;
7300
7301   if (handle != data->impl->should_respond_get_info_handle)
7302     goto out;
7303
7304   data->impl->should_respond_get_info_handle = NULL;
7305
7306   set_busy_cursor (data->impl, FALSE);
7307
7308   if (cancelled)
7309     goto out;
7310
7311   if (!info)
7312     parent_is_folder = FALSE;
7313   else
7314     parent_is_folder = gtk_file_info_get_is_folder (info);
7315
7316   if (parent_is_folder)
7317     {
7318       if (data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
7319         {
7320           if (data->file_exists_and_is_not_folder)
7321             {
7322               gboolean retval;
7323               const char *file_part;
7324
7325               file_part = _gtk_file_chooser_entry_get_file_part (GTK_FILE_CHOOSER_ENTRY (data->impl->location_entry));
7326               retval = should_respond_after_confirm_overwrite (data->impl, file_part, data->parent_path);
7327
7328               if (retval)
7329                 g_signal_emit_by_name (data->impl, "response-requested");
7330             }
7331           else
7332             g_signal_emit_by_name (data->impl, "response-requested");
7333         }
7334       else /* GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER */
7335         {
7336           GtkFileSystemHandle *handle;
7337
7338           g_object_ref (data->impl);
7339           handle = gtk_file_system_create_folder (data->impl->file_system,
7340                                                   data->path,
7341                                                   action_create_folder_cb,
7342                                                   data->impl);
7343           data->impl->pending_handles = g_slist_append (data->impl->pending_handles, handle);
7344           set_busy_cursor (data->impl, TRUE);
7345         }
7346     }
7347   else
7348     {
7349       /* This will display an error, which is what we want */
7350       change_folder_and_display_error (data->impl, data->parent_path);
7351     }
7352
7353 out:
7354   g_object_unref (data->impl);
7355   gtk_file_path_free (data->path);
7356   gtk_file_path_free (data->parent_path);
7357   g_free (data);
7358
7359   g_object_unref (handle);
7360 }
7361
7362 static void
7363 paste_text_received (GtkClipboard          *clipboard,
7364                      const gchar           *text,
7365                      GtkFileChooserDefault *impl)
7366 {
7367   GtkFilePath *path;
7368
7369   if (!text)
7370     return;
7371
7372   path = gtk_file_system_uri_to_path (impl->file_system, text);
7373   if (!path) 
7374     {
7375       if (!g_path_is_absolute (text)) 
7376         {
7377           location_popup_handler (impl, text);
7378           return;
7379         }
7380
7381       path = gtk_file_system_filename_to_path (impl->file_system, text);
7382       if (!path) 
7383         {
7384           location_popup_handler (impl, text);
7385           return;
7386         }
7387     }
7388
7389   if (!gtk_file_chooser_default_select_path (GTK_FILE_CHOOSER (impl), path, NULL))
7390     location_popup_handler (impl, text);
7391
7392   gtk_file_path_free (path);
7393 }
7394
7395 /* Handler for the "location-popup-on-paste" keybinding signal */
7396 static void
7397 location_popup_on_paste_handler (GtkFileChooserDefault *impl)
7398 {
7399   GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (impl),
7400                                                       GDK_SELECTION_CLIPBOARD);
7401   gtk_clipboard_request_text (clipboard,
7402                               (GtkClipboardTextReceivedFunc) paste_text_received,
7403                               impl);
7404 }
7405
7406
7407 /* Implementation for GtkFileChooserEmbed::should_respond() */
7408 static gboolean
7409 gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
7410 {
7411   GtkFileChooserDefault *impl;
7412   GtkWidget *toplevel;
7413   GtkWidget *current_focus;
7414
7415   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
7416
7417   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
7418   g_assert (GTK_IS_WINDOW (toplevel));
7419
7420   current_focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
7421
7422   if (current_focus == impl->browse_files_tree_view)
7423     {
7424       /* The following array encodes what we do based on the impl->action and the
7425        * number of files selected.
7426        */
7427       typedef enum {
7428         NOOP,                   /* Do nothing (don't respond) */
7429         RESPOND,                /* Respond immediately */
7430         RESPOND_OR_SWITCH,      /* Respond immediately if the selected item is a file; switch to it if it is a folder */
7431         ALL_FILES,              /* Respond only if everything selected is a file */
7432         ALL_FOLDERS,            /* Respond only if everything selected is a folder */
7433         SAVE_ENTRY,             /* Go to the code for handling the save entry */
7434         NOT_REACHED             /* Sanity check */
7435       } ActionToTake;
7436       static const ActionToTake what_to_do[4][3] = {
7437         /*                                0 selected            1 selected              many selected */
7438         /* ACTION_OPEN */               { NOOP,                 RESPOND_OR_SWITCH,      ALL_FILES   },
7439         /* ACTION_SAVE */               { SAVE_ENTRY,           RESPOND_OR_SWITCH,      NOT_REACHED },
7440         /* ACTION_SELECT_FOLDER */      { RESPOND,              ALL_FOLDERS,            ALL_FOLDERS },
7441         /* ACTION_CREATE_FOLDER */      { SAVE_ENTRY,           ALL_FOLDERS,            NOT_REACHED }
7442       };
7443
7444       int num_selected;
7445       gboolean all_files, all_folders;
7446       int k;
7447       ActionToTake action;
7448
7449     file_list:
7450
7451       g_assert (impl->action >= GTK_FILE_CHOOSER_ACTION_OPEN && impl->action <= GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
7452
7453       selection_check (impl, &num_selected, &all_files, &all_folders);
7454
7455       if (num_selected > 2)
7456         k = 2;
7457       else
7458         k = num_selected;
7459
7460       action = what_to_do [impl->action] [k];
7461
7462       switch (action)
7463         {
7464         case NOOP:
7465           return FALSE;
7466
7467         case RESPOND:
7468           return TRUE;
7469
7470         case RESPOND_OR_SWITCH:
7471           g_assert (num_selected == 1);
7472
7473           if (all_folders)
7474             {
7475               switch_to_selected_folder (impl);
7476               return FALSE;
7477             }
7478           else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
7479             return should_respond_after_confirm_overwrite (impl,
7480                                                            get_display_name_from_file_list (impl),
7481                                                            impl->current_folder);
7482           else
7483             return TRUE;
7484
7485         case ALL_FILES:
7486           return all_files;
7487
7488         case ALL_FOLDERS:
7489           return all_folders;
7490
7491         case SAVE_ENTRY:
7492           goto save_entry;
7493
7494         default:
7495           g_assert_not_reached ();
7496         }
7497     }
7498   else if ((impl->location_entry != NULL) && (current_focus == impl->location_entry))
7499     {
7500       GtkFilePath *path;
7501       gboolean is_well_formed, is_empty, is_file_part_empty;
7502       gboolean is_folder;
7503       gboolean retval;
7504       GtkFileChooserEntry *entry;
7505       GError *error;
7506
7507     save_entry:
7508
7509       g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
7510                 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
7511                 || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
7512                      || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
7513                     && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY));
7514
7515       entry = GTK_FILE_CHOOSER_ENTRY (impl->location_entry);
7516       check_save_entry (impl, &path, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder);
7517
7518       if (is_empty || !is_well_formed)
7519         return FALSE;
7520
7521       g_assert (path != NULL);
7522
7523       error = NULL;
7524       if (is_folder)
7525         {
7526           if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
7527               || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
7528             {
7529               _gtk_file_chooser_entry_set_file_part (entry, "");
7530               change_folder_and_display_error (impl, path);
7531               retval = FALSE;
7532             }
7533           else if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
7534                    || GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
7535             {
7536               /* The folder already exists, so we do not need to create it.
7537                * Just respond to terminate the dialog.
7538                */
7539               retval = TRUE;
7540             }
7541           else
7542             {
7543               g_assert_not_reached ();
7544               retval = FALSE;
7545             }
7546         }
7547       else
7548         {
7549           gboolean file_exists_and_is_not_folder;
7550
7551           file_exists_and_is_not_folder = g_error_matches (error, GTK_FILE_SYSTEM_ERROR, GTK_FILE_SYSTEM_ERROR_NOT_FOLDER);
7552
7553           if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
7554             retval = TRUE; /* user typed a filename; we are done */
7555           else if (impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER && file_exists_and_is_not_folder)
7556             {
7557               /* Oops, the user typed the name of an existing path which is not a folder */
7558               error_creating_folder_over_existing_file_dialog (impl, path, error);
7559               error = NULL; /* as it will be freed below for the general case */
7560               retval = FALSE;
7561             }
7562           else
7563             {
7564               GtkFilePath *parent_path;
7565               struct SaveEntryData *data;
7566
7567               /* check that everything up to the last component exists */
7568
7569               parent_path = gtk_file_path_copy (_gtk_file_chooser_entry_get_current_folder (entry));
7570
7571               data = g_new0 (struct SaveEntryData, 1);
7572               data->impl = g_object_ref (impl);
7573               data->file_exists_and_is_not_folder = file_exists_and_is_not_folder;
7574               data->parent_path = parent_path; /* Takes ownership */
7575               data->path = gtk_file_path_copy (path);
7576
7577               if (impl->should_respond_get_info_handle)
7578                 gtk_file_system_cancel_operation (impl->should_respond_get_info_handle);
7579
7580               impl->should_respond_get_info_handle =
7581                 gtk_file_system_get_info (impl->file_system, parent_path,
7582                                           GTK_FILE_INFO_IS_FOLDER,
7583                                           save_entry_get_info_cb,
7584                                           data);
7585               set_busy_cursor (impl, TRUE);
7586
7587               retval = FALSE;
7588             }
7589
7590           if (error != NULL)
7591             g_error_free (error);
7592         }
7593
7594       gtk_file_path_free (path);
7595       return retval;
7596     }
7597   else if (impl->toplevel_last_focus_widget == impl->browse_shortcuts_tree_view)
7598     {
7599       /* The focus is on a dialog's action area button, *and* the widget that
7600        * was focused immediately before it is the shortcuts list.  Switch to the
7601        * selected shortcut and tell the caller not to respond.
7602        */
7603       GtkTreeIter iter;
7604
7605       if (shortcuts_get_selected (impl, &iter))
7606         {
7607           shortcuts_activate_iter (impl, &iter);
7608           
7609           gtk_widget_grab_focus (impl->browse_files_tree_view);
7610         }
7611       else
7612         goto file_list;
7613
7614       return FALSE;
7615     }
7616   else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
7617     {
7618       /* The focus is on a dialog's action area button, *and* the widget that
7619        * was focused immediately before it is the file list.  
7620        */
7621       goto file_list;
7622     }
7623   else if (impl->location_entry && impl->toplevel_last_focus_widget == impl->location_entry)
7624     {
7625       /* The focus is on a dialog's action area button, *and* the widget that
7626        * was focused immediately before it is the location entry.
7627        */
7628       goto save_entry;
7629     }
7630   else
7631     /* The focus is on a dialog's action area button or something else */
7632     if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
7633         || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
7634       goto save_entry;
7635     else
7636       goto file_list; 
7637   
7638   g_assert_not_reached ();
7639   return FALSE;
7640 }
7641
7642 /* Implementation for GtkFileChooserEmbed::initial_focus() */
7643 static void
7644 gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed)
7645 {
7646   GtkFileChooserDefault *impl;
7647   GtkWidget *widget;
7648
7649   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
7650
7651   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
7652       || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
7653     {
7654       if (impl->location_mode == LOCATION_MODE_PATH_BAR)
7655         widget = impl->browse_files_tree_view;
7656       else
7657         widget = impl->location_entry;
7658     }
7659   else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
7660            || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
7661     widget = impl->location_entry;
7662   else
7663     {
7664       g_assert_not_reached ();
7665       widget = NULL;
7666     }
7667
7668   g_assert (widget != NULL);
7669   gtk_widget_grab_focus (widget);
7670 }
7671
7672 static void
7673 set_current_filter (GtkFileChooserDefault *impl,
7674                     GtkFileFilter         *filter)
7675 {
7676   if (impl->current_filter != filter)
7677     {
7678       int filter_index;
7679
7680       /* NULL filters are allowed to reset to non-filtered status
7681        */
7682       filter_index = g_slist_index (impl->filters, filter);
7683       if (impl->filters && filter && filter_index < 0)
7684         return;
7685
7686       if (impl->current_filter)
7687         g_object_unref (impl->current_filter);
7688       impl->current_filter = filter;
7689       if (impl->current_filter)
7690         {
7691           g_object_ref_sink (impl->current_filter);
7692         }
7693
7694       if (impl->filters)
7695         gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
7696                                   filter_index);
7697
7698       if (impl->browse_files_model)
7699         install_list_model_filter (impl);
7700
7701       g_object_notify (G_OBJECT (impl), "filter");
7702     }
7703 }
7704
7705 static void
7706 filter_combo_changed (GtkComboBox           *combo_box,
7707                       GtkFileChooserDefault *impl)
7708 {
7709   gint new_index = gtk_combo_box_get_active (combo_box);
7710   GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
7711
7712   set_current_filter (impl, new_filter);
7713 }
7714
7715 static void
7716 check_preview_change (GtkFileChooserDefault *impl)
7717 {
7718   GtkTreePath *cursor_path;
7719   const GtkFilePath *new_path;
7720   const GtkFileInfo *new_info;
7721
7722   gtk_tree_view_get_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), &cursor_path, NULL);
7723   if (cursor_path && impl->sort_model)
7724     {
7725       GtkTreeIter iter;
7726       GtkTreeIter child_iter;
7727
7728       if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, cursor_path))
7729         g_assert_not_reached ();
7730
7731       gtk_tree_path_free (cursor_path);
7732
7733       gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
7734
7735       new_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
7736       new_info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
7737     }
7738   else
7739     {
7740       new_path = NULL;
7741       new_info = NULL;
7742     }
7743
7744   if (new_path != impl->preview_path &&
7745       !(new_path && impl->preview_path &&
7746         gtk_file_path_compare (new_path, impl->preview_path) == 0))
7747     {
7748       if (impl->preview_path)
7749         {
7750           gtk_file_path_free (impl->preview_path);
7751           g_free (impl->preview_display_name);
7752         }
7753
7754       if (new_path)
7755         {
7756           impl->preview_path = gtk_file_path_copy (new_path);
7757           impl->preview_display_name = g_strdup (gtk_file_info_get_display_name (new_info));
7758         }
7759       else
7760         {
7761           impl->preview_path = NULL;
7762           impl->preview_display_name = NULL;
7763         }
7764
7765       if (impl->use_preview_label && impl->preview_label)
7766         gtk_label_set_text (GTK_LABEL (impl->preview_label), impl->preview_display_name);
7767
7768       g_signal_emit_by_name (impl, "update-preview");
7769     }
7770 }
7771
7772 static void
7773 shortcuts_activate_volume_mount_cb (GtkFileSystemHandle *handle,
7774                                     GtkFileSystemVolume *volume,
7775                                     const GError        *error,
7776                                     gpointer             data)
7777 {
7778   GtkFilePath *path;
7779   gboolean cancelled = handle->cancelled;
7780   GtkFileChooserDefault *impl = data;
7781
7782   if (handle != impl->shortcuts_activate_iter_handle)
7783     goto out;
7784
7785   impl->shortcuts_activate_iter_handle = NULL;
7786
7787   set_busy_cursor (impl, FALSE);
7788
7789   if (cancelled)
7790     goto out;
7791
7792   if (error)
7793     {
7794       char *msg;
7795
7796       msg = g_strdup_printf (_("Could not mount %s"),
7797                              gtk_file_system_volume_get_display_name (impl->file_system, volume));
7798       error_message (impl, msg, error->message);
7799       g_free (msg);
7800
7801       goto out;
7802     }
7803
7804   path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
7805   if (path != NULL)
7806     {
7807       change_folder_and_display_error (impl, path);
7808       gtk_file_path_free (path);
7809     }
7810
7811 out:
7812   g_object_unref (impl);
7813   g_object_unref (handle);
7814 }
7815
7816
7817 /* Activates a volume by mounting it if necessary and then switching to its
7818  * base path.
7819  */
7820 static void
7821 shortcuts_activate_volume (GtkFileChooserDefault *impl,
7822                            GtkFileSystemVolume   *volume)
7823 {
7824   GtkFilePath *path;
7825
7826   /* We ref the file chooser since volume_mount() may run a main loop, and the
7827    * user could close the file chooser window in the meantime.
7828    */
7829   g_object_ref (impl);
7830
7831   if (!gtk_file_system_volume_get_is_mounted (impl->file_system, volume))
7832     {
7833       set_busy_cursor (impl, TRUE);
7834
7835       impl->shortcuts_activate_iter_handle =
7836         gtk_file_system_volume_mount (impl->file_system, volume,
7837                                       shortcuts_activate_volume_mount_cb,
7838                                       g_object_ref (impl));
7839     }
7840   else
7841     {
7842       path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
7843       if (path != NULL)
7844         {
7845           change_folder_and_display_error (impl, path);
7846           gtk_file_path_free (path);
7847         }
7848     }
7849
7850   g_object_unref (impl);
7851 }
7852
7853 /* Opens the folder or volume at the specified iter in the shortcuts model */
7854 struct ShortcutsActivateData
7855 {
7856   GtkFileChooserDefault *impl;
7857   GtkFilePath *path;
7858 };
7859
7860 static void
7861 shortcuts_activate_get_info_cb (GtkFileSystemHandle *handle,
7862                                 const GtkFileInfo   *info,
7863                                 const GError        *error,
7864                                 gpointer             user_data)
7865 {
7866   gboolean cancelled = handle->cancelled;
7867   struct ShortcutsActivateData *data = user_data;
7868
7869   if (handle != data->impl->shortcuts_activate_iter_handle)
7870     goto out;
7871
7872   data->impl->shortcuts_activate_iter_handle = NULL;
7873
7874   if (cancelled)
7875     goto out;
7876
7877   if (!error && gtk_file_info_get_is_folder (info))
7878     change_folder_and_display_error (data->impl, data->path);
7879   else
7880     gtk_file_chooser_default_select_path (GTK_FILE_CHOOSER (data->impl), data->path, NULL);
7881
7882 out:
7883   g_object_unref (data->impl);
7884   gtk_file_path_free (data->path);
7885   g_free (data);
7886
7887   g_object_unref (handle);
7888 }
7889
7890 static void
7891 shortcuts_activate_iter (GtkFileChooserDefault *impl,
7892                          GtkTreeIter           *iter)
7893 {
7894   gpointer col_data;
7895   gboolean is_volume;
7896
7897   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
7898                       SHORTCUTS_COL_DATA, &col_data,
7899                       SHORTCUTS_COL_IS_VOLUME, &is_volume,
7900                       -1);
7901
7902   if (!col_data)
7903     return; /* We are on a separator */
7904
7905   if (impl->shortcuts_activate_iter_handle)
7906     {
7907       gtk_file_system_cancel_operation (impl->shortcuts_activate_iter_handle);
7908       impl->shortcuts_activate_iter_handle = NULL;
7909     }
7910
7911   if (is_volume)
7912     {
7913       GtkFileSystemVolume *volume;
7914
7915       volume = col_data;
7916
7917       shortcuts_activate_volume (impl, volume);
7918     }
7919   else
7920     {
7921       struct ShortcutsActivateData *data;
7922
7923       data = g_new0 (struct ShortcutsActivateData, 1);
7924       data->impl = g_object_ref (impl);
7925       data->path = gtk_file_path_copy (col_data);
7926
7927       impl->shortcuts_activate_iter_handle =
7928         gtk_file_system_get_info (impl->file_system, data->path,
7929                                   GTK_FILE_INFO_IS_FOLDER,
7930                                   shortcuts_activate_get_info_cb, data);
7931     }
7932 }
7933
7934 /* Callback used when a row in the shortcuts list is activated */
7935 static void
7936 shortcuts_row_activated_cb (GtkTreeView           *tree_view,
7937                             GtkTreePath           *path,
7938                             GtkTreeViewColumn     *column,
7939                             GtkFileChooserDefault *impl)
7940 {
7941   GtkTreeIter iter;
7942   GtkTreeIter child_iter;
7943
7944   if (!gtk_tree_model_get_iter (impl->shortcuts_filter_model, &iter, path))
7945     return;
7946
7947   gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
7948                                                     &child_iter,
7949                                                     &iter);
7950   shortcuts_activate_iter (impl, &child_iter);
7951
7952   gtk_widget_grab_focus (impl->browse_files_tree_view);
7953 }
7954
7955 /* Handler for GtkWidget::key-press-event on the shortcuts list */
7956 static gboolean
7957 shortcuts_key_press_event_cb (GtkWidget             *widget,
7958                               GdkEventKey           *event,
7959                               GtkFileChooserDefault *impl)
7960 {
7961   guint modifiers;
7962
7963   modifiers = gtk_accelerator_get_default_mod_mask ();
7964
7965   if ((event->keyval == GDK_BackSpace
7966       || event->keyval == GDK_Delete
7967       || event->keyval == GDK_KP_Delete)
7968       && (event->state & modifiers) == 0)
7969     {
7970       remove_selected_bookmarks (impl);
7971       return TRUE;
7972     }
7973
7974   if ((event->keyval == GDK_F2)
7975       && (event->state & modifiers) == 0)
7976     {
7977       rename_selected_bookmark (impl);
7978       return TRUE;
7979     }
7980
7981   return FALSE;
7982 }
7983
7984 static gboolean
7985 shortcuts_select_func  (GtkTreeSelection  *selection,
7986                         GtkTreeModel      *model,
7987                         GtkTreePath       *path,
7988                         gboolean           path_currently_selected,
7989                         gpointer           data)
7990 {
7991   GtkFileChooserDefault *impl = data;
7992
7993   return (*gtk_tree_path_get_indices (path) != shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR));
7994 }
7995
7996 static gboolean
7997 list_select_func  (GtkTreeSelection  *selection,
7998                    GtkTreeModel      *model,
7999                    GtkTreePath       *path,
8000                    gboolean           path_currently_selected,
8001                    gpointer           data)
8002 {
8003   GtkFileChooserDefault *impl = data;
8004
8005   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
8006       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8007     {
8008       GtkTreeIter iter, child_iter;
8009       const GtkFileInfo *info;
8010
8011       if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
8012         return FALSE;
8013       
8014       gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
8015
8016       info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
8017
8018       if (info && !gtk_file_info_get_is_folder (info))
8019         return FALSE;
8020     }
8021     
8022   return TRUE;
8023 }
8024
8025 static void
8026 list_selection_changed (GtkTreeSelection      *selection,
8027                         GtkFileChooserDefault *impl)
8028 {
8029   /* See if we are in the new folder editable row for Save mode */
8030   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8031     {
8032       const GtkFileInfo *info;
8033       gboolean had_selection;
8034
8035       info = get_selected_file_info_from_file_list (impl, &had_selection);
8036       if (!had_selection)
8037         goto out; /* normal processing */
8038
8039       if (!info)
8040         return; /* We are on the editable row for New Folder */
8041     }
8042
8043  out:
8044
8045   update_chooser_entry (impl);
8046   check_preview_change (impl);
8047   bookmarks_check_add_sensitivity (impl);
8048
8049   g_signal_emit_by_name (impl, "selection-changed", 0);
8050 }
8051
8052 /* Callback used when a row in the file list is activated */
8053 static void
8054 list_row_activated (GtkTreeView           *tree_view,
8055                     GtkTreePath           *path,
8056                     GtkTreeViewColumn     *column,
8057                     GtkFileChooserDefault *impl)
8058 {
8059   GtkTreeIter iter, child_iter;
8060   const GtkFileInfo *info;
8061
8062   if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
8063     return;
8064
8065   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
8066
8067   info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
8068
8069   if (gtk_file_info_get_is_folder (info))
8070     {
8071       const GtkFilePath *file_path;
8072
8073       file_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
8074       change_folder_and_display_error (impl, file_path);
8075
8076       return;
8077     }
8078
8079   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
8080       impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8081     g_signal_emit_by_name (impl, "file-activated");
8082 }
8083
8084 static void
8085 path_bar_clicked (GtkPathBar            *path_bar,
8086                   GtkFilePath           *file_path,
8087                   GtkFilePath           *child_path,
8088                   gboolean               child_is_hidden,
8089                   GtkFileChooserDefault *impl)
8090 {
8091   if (child_path)
8092     pending_select_paths_add (impl, child_path);
8093
8094   if (!change_folder_and_display_error (impl, file_path))
8095     return;
8096
8097   /* Say we have "/foo/bar/[.baz]" and the user clicks on "bar".  We should then
8098    * show hidden files so that ".baz" appears in the file list, as it will still
8099    * be shown in the path bar: "/foo/[bar]/.baz"
8100    */
8101   if (child_is_hidden)
8102     g_object_set (impl, "show-hidden", TRUE, NULL);
8103 }
8104
8105 static const GtkFileInfo *
8106 get_list_file_info (GtkFileChooserDefault *impl,
8107                     GtkTreeIter           *iter)
8108 {
8109   GtkTreeIter child_iter;
8110
8111   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
8112                                                   &child_iter,
8113                                                   iter);
8114
8115   return _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
8116 }
8117
8118 static void
8119 list_icon_data_func (GtkTreeViewColumn *tree_column,
8120                      GtkCellRenderer   *cell,
8121                      GtkTreeModel      *tree_model,
8122                      GtkTreeIter       *iter,
8123                      gpointer           data)
8124 {
8125   GtkFileChooserDefault *impl = data;
8126   GtkTreeIter child_iter;
8127   const GtkFilePath *path;
8128   GdkPixbuf *pixbuf;
8129   const GtkFileInfo *info; 
8130   gboolean sensitive = TRUE;
8131
8132   profile_start ("start", NULL);
8133   
8134   info = get_list_file_info (impl, iter);
8135
8136   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
8137                                                   &child_iter,
8138                                                   iter);
8139   path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
8140
8141   if (path)
8142     {
8143       pixbuf = NULL;
8144
8145       if (info)
8146         {
8147           /* FIXME: NULL GError */
8148           pixbuf = gtk_file_info_render_icon (info, GTK_WIDGET (impl),
8149                                               impl->icon_size, NULL);
8150         }
8151     }
8152   else
8153     {
8154       /* We are on the editable row */
8155       pixbuf = NULL;
8156     }
8157
8158   if (info && (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
8159                impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
8160     sensitive =  gtk_file_info_get_is_folder (info);    
8161     
8162   g_object_set (cell,
8163                 "pixbuf", pixbuf,
8164                 "sensitive", sensitive,
8165                 NULL);
8166     
8167   if (pixbuf)
8168     g_object_unref (pixbuf);
8169
8170   profile_end ("end", NULL);
8171 }
8172
8173 static void
8174 list_name_data_func (GtkTreeViewColumn *tree_column,
8175                      GtkCellRenderer   *cell,
8176                      GtkTreeModel      *tree_model,
8177                      GtkTreeIter       *iter,
8178                      gpointer           data)
8179 {
8180   GtkFileChooserDefault *impl = data;
8181   const GtkFileInfo *info = get_list_file_info (impl, iter);
8182   gboolean sensitive = TRUE;
8183
8184   if (!info)
8185     {
8186       g_object_set (cell,
8187                     "text", _("Type name of new folder"),
8188                     NULL);
8189
8190       return;
8191     }
8192
8193
8194   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
8195          || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8196     {
8197       sensitive = gtk_file_info_get_is_folder (info);
8198     } 
8199     
8200   g_object_set (cell,
8201                 "text", gtk_file_info_get_display_name (info),
8202                 "sensitive", sensitive,
8203                 NULL);
8204 }
8205
8206 #if 0
8207 static void
8208 list_size_data_func (GtkTreeViewColumn *tree_column,
8209                      GtkCellRenderer   *cell,
8210                      GtkTreeModel      *tree_model,
8211                      GtkTreeIter       *iter,
8212                      gpointer           data)
8213 {
8214   GtkFileChooserDefault *impl = data;
8215   const GtkFileInfo *info = get_list_file_info (impl, iter);
8216   gint64 size;
8217   gchar *str;
8218   gboolean sensitive = TRUE;
8219
8220   if (!info || gtk_file_info_get_is_folder (info)) 
8221     {
8222       g_object_set (cell,
8223                     "text", NULL,
8224                     "sensitive", sensitive,
8225                     NULL);
8226       return;
8227     }
8228
8229   size = gtk_file_info_get_size (info);
8230 #if 0
8231   if (size < (gint64)1024)
8232     str = g_strdup_printf (ngettext ("%d byte", "%d bytes", (gint)size), (gint)size);
8233   else if (size < (gint64)1024*1024)
8234     str = g_strdup_printf (_("%.1f KB"), size / (1024.));
8235   else if (size < (gint64)1024*1024*1024)
8236     str = g_strdup_printf (_("%.1f MB"), size / (1024.*1024.));
8237   else
8238     str = g_strdup_printf (_("%.1f GB"), size / (1024.*1024.*1024.));
8239 #endif
8240   str = g_strdup_printf ("%" G_GINT64_FORMAT, size);
8241   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
8242       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8243     sensitive = FALSE;
8244
8245   g_object_set (cell,
8246                 "text", str,
8247                 "sensitive", sensitive,
8248                 "alignment", PANGO_ALIGN_RIGHT,
8249                 NULL);
8250
8251   g_free (str);
8252 }
8253 #endif
8254
8255 /* Tree column data callback for the file list; fetches the mtime of a file */
8256 static void
8257 list_mtime_data_func (GtkTreeViewColumn *tree_column,
8258                       GtkCellRenderer   *cell,
8259                       GtkTreeModel      *tree_model,
8260                       GtkTreeIter       *iter,
8261                       gpointer           data)
8262 {
8263   GtkFileChooserDefault *impl;
8264   const GtkFileInfo *info;
8265   GtkFileTime time_mtime;
8266   GDate mtime, now;
8267   int days_diff;
8268   char buf[256];
8269   gboolean sensitive = TRUE;
8270
8271   impl = data;
8272
8273   info = get_list_file_info (impl, iter);
8274   if (!info)
8275     {
8276       g_object_set (cell,
8277                     "text", "",
8278                     "sensitive", TRUE,
8279                     NULL);
8280       return;
8281     }
8282
8283   time_mtime = gtk_file_info_get_modification_time (info);
8284
8285   if (time_mtime == 0)
8286     strcpy (buf, _("Unknown"));
8287   else
8288     {
8289       time_t time_now;
8290       g_date_set_time_t (&mtime, time_mtime);
8291       time_now = time (NULL);
8292       g_date_set_time_t (&now, time_now);
8293
8294       days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
8295
8296       if (days_diff == 0)
8297         strcpy (buf, _("Today"));
8298       else if (days_diff == 1)
8299         strcpy (buf, _("Yesterday"));
8300       else
8301         {
8302           char *format;
8303
8304           if (days_diff > 1 && days_diff < 7)
8305             format = "%A"; /* Days from last week */
8306           else
8307             format = "%x"; /* Any other date */
8308
8309           if (g_date_strftime (buf, sizeof (buf), format, &mtime) == 0)
8310             strcpy (buf, _("Unknown"));
8311         }
8312     }
8313
8314   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
8315       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8316     sensitive = gtk_file_info_get_is_folder (info);
8317
8318   g_object_set (cell,
8319                 "text", buf,
8320                 "sensitive", sensitive,
8321                 NULL);
8322 }
8323
8324 GtkWidget *
8325 _gtk_file_chooser_default_new (const char *file_system)
8326 {
8327   return  g_object_new (GTK_TYPE_FILE_CHOOSER_DEFAULT,
8328                         "file-system-backend", file_system,
8329                         NULL);
8330 }
8331
8332 static void
8333 location_popup_handler (GtkFileChooserDefault *impl,
8334                         const gchar           *path)
8335 {
8336   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
8337       || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8338     {
8339       LocationMode new_mode;
8340
8341       if (impl->location_mode == LOCATION_MODE_PATH_BAR)
8342         new_mode = LOCATION_MODE_FILENAME_ENTRY;
8343       else if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
8344         new_mode = LOCATION_MODE_PATH_BAR;
8345       else
8346         {
8347           g_assert_not_reached ();
8348           return;
8349         }
8350
8351       location_mode_set (impl, new_mode, TRUE);
8352     }
8353   else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
8354            || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8355     gtk_widget_grab_focus (impl->location_entry);
8356   else
8357     g_assert_not_reached ();
8358 }
8359
8360 /* Handler for the "up-folder" keybinding signal */
8361 static void
8362 up_folder_handler (GtkFileChooserDefault *impl)
8363 {
8364   _gtk_path_bar_up (GTK_PATH_BAR (impl->browse_path_bar));
8365 }
8366
8367 /* Handler for the "down-folder" keybinding signal */
8368 static void
8369 down_folder_handler (GtkFileChooserDefault *impl)
8370 {
8371   _gtk_path_bar_down (GTK_PATH_BAR (impl->browse_path_bar));
8372 }
8373
8374 /* Switches to the shortcut in the specified index */
8375 static void
8376 switch_to_shortcut (GtkFileChooserDefault *impl,
8377                     int pos)
8378 {
8379   GtkTreeIter iter;
8380
8381   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
8382     g_assert_not_reached ();
8383
8384   shortcuts_activate_iter (impl, &iter);
8385 }
8386
8387 /* Handler for the "home-folder" keybinding signal */
8388 static void
8389 home_folder_handler (GtkFileChooserDefault *impl)
8390 {
8391   if (impl->has_home)
8392     switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_HOME));
8393 }
8394
8395 /* Handler for the "desktop-folder" keybinding signal */
8396 static void
8397 desktop_folder_handler (GtkFileChooserDefault *impl)
8398 {
8399   if (impl->has_desktop)
8400     switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_DESKTOP));
8401 }
8402
8403 static void
8404 quick_bookmark_handler (GtkFileChooserDefault *impl,
8405                         gint bookmark_index)
8406 {
8407   int bookmark_pos;
8408   GtkTreePath *path;
8409
8410   if (bookmark_index < 0 || bookmark_index >= impl->num_bookmarks)
8411     return;
8412
8413   bookmark_pos = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS) + bookmark_index;
8414
8415   path = gtk_tree_path_new_from_indices (bookmark_pos, -1);
8416   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
8417                                 path, NULL,
8418                                 FALSE, 0.0, 0.0);
8419   gtk_tree_path_free (path);
8420
8421   switch_to_shortcut (impl, bookmark_pos);
8422 }
8423
8424 \f
8425
8426 /* Drag and drop interfaces */
8427
8428 static void
8429 _shortcuts_model_filter_class_init (ShortcutsModelFilterClass *class)
8430 {
8431 }
8432
8433 static void
8434 _shortcuts_model_filter_init (ShortcutsModelFilter *model)
8435 {
8436   model->impl = NULL;
8437 }
8438
8439 /* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */
8440 static gboolean
8441 shortcuts_model_filter_row_draggable (GtkTreeDragSource *drag_source,
8442                                       GtkTreePath       *path)
8443 {
8444   ShortcutsModelFilter *model;
8445   int pos;
8446   int bookmarks_pos;
8447
8448   model = SHORTCUTS_MODEL_FILTER (drag_source);
8449
8450   pos = *gtk_tree_path_get_indices (path);
8451   bookmarks_pos = shortcuts_get_index (model->impl, SHORTCUTS_BOOKMARKS);
8452
8453   return (pos >= bookmarks_pos && pos < bookmarks_pos + model->impl->num_bookmarks);
8454 }
8455
8456 /* GtkTreeDragSource::drag_data_get implementation for the shortcuts filter model */
8457 static gboolean
8458 shortcuts_model_filter_drag_data_get (GtkTreeDragSource *drag_source,
8459                                       GtkTreePath       *path,
8460                                       GtkSelectionData  *selection_data)
8461 {
8462   ShortcutsModelFilter *model;
8463
8464   model = SHORTCUTS_MODEL_FILTER (drag_source);
8465
8466   /* FIXME */
8467
8468   return FALSE;
8469 }
8470
8471 /* Fill the GtkTreeDragSourceIface vtable */
8472 static void
8473 shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface)
8474 {
8475   iface->row_draggable = shortcuts_model_filter_row_draggable;
8476   iface->drag_data_get = shortcuts_model_filter_drag_data_get;
8477 }
8478
8479 #if 0
8480 /* Fill the GtkTreeDragDestIface vtable */
8481 static void
8482 shortcuts_model_filter_drag_dest_iface_init (GtkTreeDragDestIface *iface)
8483 {
8484   iface->drag_data_received = shortcuts_model_filter_drag_data_received;
8485   iface->row_drop_possible = shortcuts_model_filter_row_drop_possible;
8486 }
8487 #endif
8488
8489 static GtkTreeModel *
8490 shortcuts_model_filter_new (GtkFileChooserDefault *impl,
8491                             GtkTreeModel          *child_model,
8492                             GtkTreePath           *root)
8493 {
8494   ShortcutsModelFilter *model;
8495
8496   model = g_object_new (SHORTCUTS_MODEL_FILTER_TYPE,
8497                         "child-model", child_model,
8498                         "virtual-root", root,
8499                         NULL);
8500
8501   model->impl = impl;
8502
8503   return GTK_TREE_MODEL (model);
8504 }
8505
8506 #define __GTK_FILE_CHOOSER_DEFAULT_C__
8507 #include "gtkaliasdef.c"