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