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