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