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