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