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