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