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