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