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