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