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