]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserdefault.c
bgo#652045 - Initialize local_only in GtkFileChooserEntry
[~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 "gtkbindings.h"
27 #include "gtkcelllayout.h"
28 #include "gtkcellrendererpixbuf.h"
29 #include "gtkcellrenderertext.h"
30 #include "gtkcheckmenuitem.h"
31 #include "gtkclipboard.h"
32 #include "gtkcomboboxtext.h"
33 #include "gtkentry.h"
34 #include "gtkexpander.h"
35 #include "gtkfilechooserprivate.h"
36 #include "gtkfilechooserdialog.h"
37 #include "gtkfilechooserembed.h"
38 #include "gtkfilechooserentry.h"
39 #include "gtkfilechooserutils.h"
40 #include "gtkfilechooser.h"
41 #include "gtkfilesystem.h"
42 #include "gtkfilesystemmodel.h"
43 #include "gtkframe.h"
44 #include "gtkpaned.h"
45 #include "gtkiconfactory.h"
46 #include "gtkicontheme.h"
47 #include "gtkimage.h"
48 #include "gtkimagemenuitem.h"
49 #include "gtklabel.h"
50 #include "gtkmarshalers.h"
51 #include "gtkmessagedialog.h"
52 #include "gtkmountoperation.h"
53 #include "gtkpathbar.h"
54 #include "gtkprivate.h"
55 #include "gtkradiobutton.h"
56 #include "gtkrecentfilter.h"
57 #include "gtkrecentmanager.h"
58 #include "gtkscrolledwindow.h"
59 #include "gtkseparatormenuitem.h"
60 #include "gtksizegroup.h"
61 #include "gtksizerequest.h"
62 #include "gtkstock.h"
63 #include "gtktable.h"
64 #include "gtktoolbar.h"
65 #include "gtktoolbutton.h"
66 #include "gtktooltip.h"
67 #include "gtktreednd.h"
68 #include "gtktreeprivate.h"
69 #include "gtktreeselection.h"
70 #include "gtkbox.h"
71 #include "gtkorientable.h"
72 #include "gtkintl.h"
73
74 #include <errno.h>
75 #include <string.h>
76 #include <time.h>
77 #include <sys/stat.h>
78 #include <sys/types.h>
79 #include <locale.h>
80
81 #ifdef HAVE_UNISTD_H
82 #include <unistd.h>
83 #endif
84 #ifdef G_OS_WIN32
85 #include <io.h>
86 #endif
87
88 /* Profiling stuff */
89 #undef PROFILE_FILE_CHOOSER
90 #ifdef PROFILE_FILE_CHOOSER
91
92
93 #ifndef F_OK 
94 #define F_OK 0
95 #endif
96
97 #define PROFILE_INDENT 4
98 static int profile_indent;
99
100 static void
101 profile_add_indent (int indent)
102 {
103   profile_indent += indent;
104   if (profile_indent < 0)
105     g_error ("You screwed up your indentation");
106 }
107
108 void
109 _gtk_file_chooser_profile_log (const char *func, int indent, const char *msg1, const char *msg2)
110 {
111   char *str;
112
113   if (indent < 0)
114     profile_add_indent (indent);
115
116   if (profile_indent == 0)
117     str = g_strdup_printf ("MARK: %s %s %s", func ? func : "", msg1 ? msg1 : "", msg2 ? msg2 : "");
118   else
119     str = g_strdup_printf ("MARK: %*c %s %s %s", profile_indent - 1, ' ', func ? func : "", msg1 ? msg1 : "", msg2 ? msg2 : "");
120
121   access (str, F_OK);
122   g_free (str);
123
124   if (indent > 0)
125     profile_add_indent (indent);
126 }
127
128 #define profile_start(x, y) _gtk_file_chooser_profile_log (G_STRFUNC, PROFILE_INDENT, x, y)
129 #define profile_end(x, y) _gtk_file_chooser_profile_log (G_STRFUNC, -PROFILE_INDENT, x, y)
130 #define profile_msg(x, y) _gtk_file_chooser_profile_log (NULL, 0, x, y)
131 #else
132 #define profile_start(x, y)
133 #define profile_end(x, y)
134 #define profile_msg(x, y)
135 #endif
136
137
138
139 typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
140
141 #define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
142 #define GTK_IS_FILE_CHOOSER_DEFAULT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT))
143 #define GTK_FILE_CHOOSER_DEFAULT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
144
145 #define MAX_LOADING_TIME 500
146
147 #define DEFAULT_NEW_FOLDER_NAME _("Type name of new folder")
148
149 struct _GtkFileChooserDefaultClass
150 {
151   GtkBoxClass parent_class;
152 };
153
154 /* Signal IDs */
155 enum {
156   LOCATION_POPUP,
157   LOCATION_POPUP_ON_PASTE,
158   UP_FOLDER,
159   DOWN_FOLDER,
160   HOME_FOLDER,
161   DESKTOP_FOLDER,
162   QUICK_BOOKMARK,
163   LOCATION_TOGGLE_POPUP,
164   SHOW_HIDDEN,
165   SEARCH_SHORTCUT,
166   RECENT_SHORTCUT,
167
168   LAST_SIGNAL
169 };
170
171 static guint signals[LAST_SIGNAL] = { 0 };
172
173 /* Column numbers for the shortcuts tree.  Keep these in sync with shortcuts_model_create() */
174 enum {
175   SHORTCUTS_COL_PIXBUF,
176   SHORTCUTS_COL_NAME,
177   SHORTCUTS_COL_DATA,
178   SHORTCUTS_COL_TYPE,
179   SHORTCUTS_COL_REMOVABLE,
180   SHORTCUTS_COL_PIXBUF_VISIBLE,
181   SHORTCUTS_COL_CANCELLABLE,
182   SHORTCUTS_COL_NUM_COLUMNS
183 };
184
185 typedef enum {
186   SHORTCUT_TYPE_FILE,
187   SHORTCUT_TYPE_VOLUME,
188   SHORTCUT_TYPE_SEPARATOR,
189   SHORTCUT_TYPE_SEARCH,
190   SHORTCUT_TYPE_RECENT
191 } ShortcutType;
192
193 #define MODEL_ATTRIBUTES "standard::name,standard::type,standard::display-name," \
194                          "standard::is-hidden,standard::is-backup,standard::size," \
195                          "standard::content-type,time::modified"
196 enum {
197   /* the first 3 must be these due to settings caching sort column */
198   MODEL_COL_NAME,
199   MODEL_COL_SIZE,
200   MODEL_COL_MTIME,
201   MODEL_COL_FILE,
202   MODEL_COL_NAME_COLLATED,
203   MODEL_COL_IS_FOLDER,
204   MODEL_COL_PIXBUF,
205   MODEL_COL_SIZE_TEXT,
206   MODEL_COL_MTIME_TEXT,
207   MODEL_COL_ELLIPSIZE,
208   MODEL_COL_NUM_COLUMNS
209 };
210
211 /* This list of types is passed to _gtk_file_system_model_new*() */
212 #define MODEL_COLUMN_TYPES                                      \
213         MODEL_COL_NUM_COLUMNS,                                  \
214         G_TYPE_STRING,            /* MODEL_COL_NAME */          \
215         G_TYPE_INT64,             /* MODEL_COL_SIZE */          \
216         G_TYPE_LONG,              /* MODEL_COL_MTIME */         \
217         G_TYPE_FILE,              /* MODEL_COL_FILE */          \
218         G_TYPE_STRING,            /* MODEL_COL_NAME_COLLATED */ \
219         G_TYPE_BOOLEAN,           /* MODEL_COL_IS_FOLDER */     \
220         GDK_TYPE_PIXBUF,          /* MODEL_COL_PIXBUF */        \
221         G_TYPE_STRING,            /* MODEL_COL_SIZE_TEXT */     \
222         G_TYPE_STRING,            /* MODEL_COL_MTIME_TEXT */    \
223         PANGO_TYPE_ELLIPSIZE_MODE /* MODEL_COL_ELLIPSIZE */
224
225 /* Identifiers for target types */
226 enum {
227   GTK_TREE_MODEL_ROW,
228 };
229
230 /* Interesting places in the shortcuts bar */
231 typedef enum {
232   SHORTCUTS_SEARCH,
233   SHORTCUTS_RECENT,
234   SHORTCUTS_RECENT_SEPARATOR,
235   SHORTCUTS_HOME,
236   SHORTCUTS_DESKTOP,
237   SHORTCUTS_VOLUMES,
238   SHORTCUTS_SHORTCUTS,
239   SHORTCUTS_BOOKMARKS_SEPARATOR,
240   SHORTCUTS_BOOKMARKS,
241   SHORTCUTS_CURRENT_FOLDER_SEPARATOR,
242   SHORTCUTS_CURRENT_FOLDER
243 } ShortcutsIndex;
244
245 /* Icon size for if we can't get it from the theme */
246 #define FALLBACK_ICON_SIZE 16
247
248 #define PREVIEW_HBOX_SPACING 12
249 #define NUM_LINES 45
250 #define NUM_CHARS 60
251
252 #define SETTINGS_KEY_LAST_FOLDER_URI     "last-folder-uri"
253 #define SETTINGS_KEY_LOCATION_MODE       "location-mode"
254 #define SETTINGS_KEY_SHOW_HIDDEN         "show-hidden"
255 #define SETTINGS_KEY_EXPAND_FOLDERS      "expand-folders"
256 #define SETTINGS_KEY_SHOW_SIZE_COLUMN    "show-size-column"
257 #define SETTINGS_KEY_SORT_COLUMN         "sort-column"
258 #define SETTINGS_KEY_SORT_ORDER          "sort-order"
259 #define SETTINGS_KEY_WINDOW_POSITION     "window-position"
260 #define SETTINGS_KEY_WINDOW_SIZE         "window-size"
261
262 static void gtk_file_chooser_default_iface_init       (GtkFileChooserIface        *iface);
263 static void gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface   *iface);
264
265 static GObject* gtk_file_chooser_default_constructor  (GType                  type,
266                                                        guint                  n_construct_properties,
267                                                        GObjectConstructParam *construct_params);
268 static void     gtk_file_chooser_default_finalize     (GObject               *object);
269 static void     gtk_file_chooser_default_set_property (GObject               *object,
270                                                        guint                  prop_id,
271                                                        const GValue          *value,
272                                                        GParamSpec            *pspec);
273 static void     gtk_file_chooser_default_get_property (GObject               *object,
274                                                        guint                  prop_id,
275                                                        GValue                *value,
276                                                        GParamSpec            *pspec);
277 static void     gtk_file_chooser_default_dispose      (GObject               *object);
278 static void     gtk_file_chooser_default_show_all       (GtkWidget             *widget);
279 static void     gtk_file_chooser_default_realize        (GtkWidget             *widget);
280 static void     gtk_file_chooser_default_map            (GtkWidget             *widget);
281 static void     gtk_file_chooser_default_unmap          (GtkWidget             *widget);
282 static void     gtk_file_chooser_default_hierarchy_changed (GtkWidget          *widget,
283                                                             GtkWidget          *previous_toplevel);
284 static void     gtk_file_chooser_default_style_updated  (GtkWidget             *widget);
285 static void     gtk_file_chooser_default_screen_changed (GtkWidget             *widget,
286                                                          GdkScreen             *previous_screen);
287 static void     gtk_file_chooser_default_size_allocate  (GtkWidget             *widget,
288                                                          GtkAllocation         *allocation);
289
290 static gboolean       gtk_file_chooser_default_set_current_folder          (GtkFileChooser    *chooser,
291                                                                             GFile             *folder,
292                                                                             GError           **error);
293 static gboolean       gtk_file_chooser_default_update_current_folder       (GtkFileChooser    *chooser,
294                                                                             GFile             *folder,
295                                                                             gboolean           keep_trail,
296                                                                             gboolean           clear_entry,
297                                                                             GError           **error);
298 static GFile *        gtk_file_chooser_default_get_current_folder          (GtkFileChooser    *chooser);
299 static void           gtk_file_chooser_default_set_current_name            (GtkFileChooser    *chooser,
300                                                                             const gchar       *name);
301 static gboolean       gtk_file_chooser_default_select_file                 (GtkFileChooser    *chooser,
302                                                                             GFile             *file,
303                                                                             GError           **error);
304 static void           gtk_file_chooser_default_unselect_file               (GtkFileChooser    *chooser,
305                                                                             GFile             *file);
306 static void           gtk_file_chooser_default_select_all                  (GtkFileChooser    *chooser);
307 static void           gtk_file_chooser_default_unselect_all                (GtkFileChooser    *chooser);
308 static GSList *       gtk_file_chooser_default_get_files                   (GtkFileChooser    *chooser);
309 static GFile *        gtk_file_chooser_default_get_preview_file            (GtkFileChooser    *chooser);
310 static GtkFileSystem *gtk_file_chooser_default_get_file_system             (GtkFileChooser    *chooser);
311 static void           gtk_file_chooser_default_add_filter                  (GtkFileChooser    *chooser,
312                                                                             GtkFileFilter     *filter);
313 static void           gtk_file_chooser_default_remove_filter               (GtkFileChooser    *chooser,
314                                                                             GtkFileFilter     *filter);
315 static GSList *       gtk_file_chooser_default_list_filters                (GtkFileChooser    *chooser);
316 static gboolean       gtk_file_chooser_default_add_shortcut_folder    (GtkFileChooser    *chooser,
317                                                                        GFile             *file,
318                                                                        GError           **error);
319 static gboolean       gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
320                                                                        GFile             *file,
321                                                                        GError           **error);
322 static GSList *       gtk_file_chooser_default_list_shortcut_folders  (GtkFileChooser    *chooser);
323
324 static void           gtk_file_chooser_default_get_default_size       (GtkFileChooserEmbed *chooser_embed,
325                                                                        gint                *default_width,
326                                                                        gint                *default_height);
327 static gboolean       gtk_file_chooser_default_should_respond         (GtkFileChooserEmbed *chooser_embed);
328 static void           gtk_file_chooser_default_initial_focus          (GtkFileChooserEmbed *chooser_embed);
329
330 static void location_popup_handler  (GtkFileChooserDefault *impl,
331                                      const gchar           *path);
332 static void location_popup_on_paste_handler (GtkFileChooserDefault *impl);
333 static void location_toggle_popup_handler   (GtkFileChooserDefault *impl);
334 static void up_folder_handler       (GtkFileChooserDefault *impl);
335 static void down_folder_handler     (GtkFileChooserDefault *impl);
336 static void home_folder_handler     (GtkFileChooserDefault *impl);
337 static void desktop_folder_handler  (GtkFileChooserDefault *impl);
338 static void quick_bookmark_handler  (GtkFileChooserDefault *impl,
339                                      gint                   bookmark_index);
340 static void show_hidden_handler     (GtkFileChooserDefault *impl);
341 static void search_shortcut_handler (GtkFileChooserDefault *impl);
342 static void recent_shortcut_handler (GtkFileChooserDefault *impl);
343 static void update_appearance       (GtkFileChooserDefault *impl);
344
345 static void set_current_filter   (GtkFileChooserDefault *impl,
346                                   GtkFileFilter         *filter);
347 static void check_preview_change (GtkFileChooserDefault *impl);
348
349 static void filter_combo_changed       (GtkComboBox           *combo_box,
350                                         GtkFileChooserDefault *impl);
351
352 static gboolean shortcuts_key_press_event_cb (GtkWidget             *widget,
353                                               GdkEventKey           *event,
354                                               GtkFileChooserDefault *impl);
355
356 static gboolean shortcuts_select_func   (GtkTreeSelection      *selection,
357                                          GtkTreeModel          *model,
358                                          GtkTreePath           *path,
359                                          gboolean               path_currently_selected,
360                                          gpointer               data);
361 static gboolean shortcuts_get_selected  (GtkFileChooserDefault *impl,
362                                          GtkTreeIter           *iter);
363 static void shortcuts_activate_iter (GtkFileChooserDefault *impl,
364                                      GtkTreeIter           *iter);
365 static int shortcuts_get_index (GtkFileChooserDefault *impl,
366                                 ShortcutsIndex         where);
367 static int shortcut_find_position (GtkFileChooserDefault *impl,
368                                    GFile                 *file);
369
370 static void bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl);
371
372 static gboolean list_select_func   (GtkTreeSelection      *selection,
373                                     GtkTreeModel          *model,
374                                     GtkTreePath           *path,
375                                     gboolean               path_currently_selected,
376                                     gpointer               data);
377
378 static void list_selection_changed     (GtkTreeSelection      *tree_selection,
379                                         GtkFileChooserDefault *impl);
380 static void list_row_activated         (GtkTreeView           *tree_view,
381                                         GtkTreePath           *path,
382                                         GtkTreeViewColumn     *column,
383                                         GtkFileChooserDefault *impl);
384
385 static void path_bar_clicked (GtkPathBar            *path_bar,
386                               GFile                 *file,
387                               GFile                 *child,
388                               gboolean               child_is_hidden,
389                               GtkFileChooserDefault *impl);
390
391 static void add_bookmark_button_clicked_cb    (GtkButton             *button,
392                                                GtkFileChooserDefault *impl);
393 static void remove_bookmark_button_clicked_cb (GtkButton             *button,
394                                                GtkFileChooserDefault *impl);
395 static void save_folder_combo_changed_cb      (GtkComboBox           *combo,
396                                                GtkFileChooserDefault *impl);
397
398 static void update_cell_renderer_attributes (GtkFileChooserDefault *impl);
399
400 static void load_remove_timer (GtkFileChooserDefault *impl);
401 static void browse_files_center_selected_row (GtkFileChooserDefault *impl);
402
403 static void location_button_toggled_cb (GtkToggleButton *toggle,
404                                         GtkFileChooserDefault *impl);
405 static void location_switch_to_path_bar (GtkFileChooserDefault *impl);
406
407 static void stop_loading_and_clear_list_model (GtkFileChooserDefault *impl,
408                                                gboolean remove_from_treeview);
409 static void     search_stop_searching        (GtkFileChooserDefault *impl,
410                                               gboolean               remove_query);
411 static void     search_clear_model           (GtkFileChooserDefault *impl, 
412                                               gboolean               remove_from_treeview);
413 static gboolean search_should_respond        (GtkFileChooserDefault *impl);
414 static void     search_switch_to_browse_mode (GtkFileChooserDefault *impl);
415 static GSList  *search_get_selected_files    (GtkFileChooserDefault *impl);
416 static void     search_entry_activate_cb     (GtkEntry              *entry, 
417                                               gpointer               data);
418 static void     settings_load                (GtkFileChooserDefault *impl);
419
420 static void     recent_stop_loading          (GtkFileChooserDefault *impl);
421 static void     recent_clear_model           (GtkFileChooserDefault *impl,
422                                               gboolean               remove_from_treeview);
423 static gboolean recent_should_respond        (GtkFileChooserDefault *impl);
424 static void     recent_switch_to_browse_mode (GtkFileChooserDefault *impl);
425 static GSList * recent_get_selected_files    (GtkFileChooserDefault *impl);
426 static void     set_file_system_backend      (GtkFileChooserDefault *impl);
427 static void     unset_file_system_backend    (GtkFileChooserDefault *impl);
428
429
430
431 \f
432
433 /* Drag and drop interface declarations */
434
435 typedef struct {
436   GtkTreeModelFilter parent;
437
438   GtkFileChooserDefault *impl;
439 } ShortcutsPaneModelFilter;
440
441 typedef struct {
442   GtkTreeModelFilterClass parent_class;
443 } ShortcutsPaneModelFilterClass;
444
445 #define SHORTCUTS_PANE_MODEL_FILTER_TYPE (_shortcuts_pane_model_filter_get_type ())
446 #define SHORTCUTS_PANE_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_PANE_MODEL_FILTER_TYPE, ShortcutsPaneModelFilter))
447
448 static void shortcuts_pane_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface);
449
450 G_DEFINE_TYPE_WITH_CODE (ShortcutsPaneModelFilter,
451                          _shortcuts_pane_model_filter,
452                          GTK_TYPE_TREE_MODEL_FILTER,
453                          G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
454                                                 shortcuts_pane_model_filter_drag_source_iface_init))
455
456 static GtkTreeModel *shortcuts_pane_model_filter_new (GtkFileChooserDefault *impl,
457                                                       GtkTreeModel          *child_model,
458                                                       GtkTreePath           *root);
459
460 \f
461
462 G_DEFINE_TYPE_WITH_CODE (GtkFileChooserDefault, _gtk_file_chooser_default, GTK_TYPE_BOX,
463                          G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER,
464                                                 gtk_file_chooser_default_iface_init)
465                          G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER_EMBED,
466                                                 gtk_file_chooser_embed_default_iface_init));                                            
467
468
469 static void
470 add_normal_and_shifted_binding (GtkBindingSet  *binding_set,
471                                 guint           keyval,
472                                 GdkModifierType modifiers,
473                                 const gchar    *signal_name)
474 {
475   gtk_binding_entry_add_signal (binding_set,
476                                 keyval, modifiers,
477                                 signal_name, 0);
478
479   gtk_binding_entry_add_signal (binding_set,
480                                 keyval, modifiers | GDK_SHIFT_MASK,
481                                 signal_name, 0);
482 }
483
484 static void
485 _gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
486 {
487   static const guint quick_bookmark_keyvals[10] = {
488     GDK_KEY_1, GDK_KEY_2, GDK_KEY_3, GDK_KEY_4, GDK_KEY_5, GDK_KEY_6, GDK_KEY_7, GDK_KEY_8, GDK_KEY_9, GDK_KEY_0
489   };
490   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
491   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
492   GtkBindingSet *binding_set;
493   int i;
494
495   gobject_class->finalize = gtk_file_chooser_default_finalize;
496   gobject_class->constructor = gtk_file_chooser_default_constructor;
497   gobject_class->set_property = gtk_file_chooser_default_set_property;
498   gobject_class->get_property = gtk_file_chooser_default_get_property;
499   gobject_class->dispose = gtk_file_chooser_default_dispose;
500
501   widget_class->show_all = gtk_file_chooser_default_show_all;
502   widget_class->realize = gtk_file_chooser_default_realize;
503   widget_class->map = gtk_file_chooser_default_map;
504   widget_class->unmap = gtk_file_chooser_default_unmap;
505   widget_class->hierarchy_changed = gtk_file_chooser_default_hierarchy_changed;
506   widget_class->style_updated = gtk_file_chooser_default_style_updated;
507   widget_class->screen_changed = gtk_file_chooser_default_screen_changed;
508   widget_class->size_allocate = gtk_file_chooser_default_size_allocate;
509
510   signals[LOCATION_POPUP] =
511     g_signal_new_class_handler (I_("location-popup"),
512                                 G_OBJECT_CLASS_TYPE (class),
513                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
514                                 G_CALLBACK (location_popup_handler),
515                                 NULL, NULL,
516                                 _gtk_marshal_VOID__STRING,
517                                 G_TYPE_NONE, 1, G_TYPE_STRING);
518
519   signals[LOCATION_POPUP_ON_PASTE] =
520     g_signal_new_class_handler (I_("location-popup-on-paste"),
521                                 G_OBJECT_CLASS_TYPE (class),
522                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
523                                 G_CALLBACK (location_popup_on_paste_handler),
524                                 NULL, NULL,
525                                 _gtk_marshal_VOID__VOID,
526                                 G_TYPE_NONE, 0);
527
528   signals[LOCATION_TOGGLE_POPUP] =
529     g_signal_new_class_handler (I_("location-toggle-popup"),
530                                 G_OBJECT_CLASS_TYPE (class),
531                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
532                                 G_CALLBACK (location_toggle_popup_handler),
533                                 NULL, NULL,
534                                 _gtk_marshal_VOID__VOID,
535                                 G_TYPE_NONE, 0);
536
537   signals[UP_FOLDER] =
538     g_signal_new_class_handler (I_("up-folder"),
539                                 G_OBJECT_CLASS_TYPE (class),
540                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
541                                 G_CALLBACK (up_folder_handler),
542                                 NULL, NULL,
543                                 _gtk_marshal_VOID__VOID,
544                                 G_TYPE_NONE, 0);
545
546   signals[DOWN_FOLDER] =
547     g_signal_new_class_handler (I_("down-folder"),
548                                 G_OBJECT_CLASS_TYPE (class),
549                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
550                                 G_CALLBACK (down_folder_handler),
551                                 NULL, NULL,
552                                 _gtk_marshal_VOID__VOID,
553                                 G_TYPE_NONE, 0);
554
555   signals[HOME_FOLDER] =
556     g_signal_new_class_handler (I_("home-folder"),
557                                 G_OBJECT_CLASS_TYPE (class),
558                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
559                                 G_CALLBACK (home_folder_handler),
560                                 NULL, NULL,
561                                 _gtk_marshal_VOID__VOID,
562                                 G_TYPE_NONE, 0);
563
564   signals[DESKTOP_FOLDER] =
565     g_signal_new_class_handler (I_("desktop-folder"),
566                                 G_OBJECT_CLASS_TYPE (class),
567                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
568                                 G_CALLBACK (desktop_folder_handler),
569                                 NULL, NULL,
570                                 _gtk_marshal_VOID__VOID,
571                                 G_TYPE_NONE, 0);
572
573   signals[QUICK_BOOKMARK] =
574     g_signal_new_class_handler (I_("quick-bookmark"),
575                                 G_OBJECT_CLASS_TYPE (class),
576                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
577                                 G_CALLBACK (quick_bookmark_handler),
578                                 NULL, NULL,
579                                 _gtk_marshal_VOID__INT,
580                                 G_TYPE_NONE, 1, G_TYPE_INT);
581
582   signals[SHOW_HIDDEN] =
583     g_signal_new_class_handler (I_("show-hidden"),
584                                 G_OBJECT_CLASS_TYPE (class),
585                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
586                                 G_CALLBACK (show_hidden_handler),
587                                 NULL, NULL,
588                                 _gtk_marshal_VOID__VOID,
589                                 G_TYPE_NONE, 0);
590
591   signals[SEARCH_SHORTCUT] =
592     g_signal_new_class_handler (I_("search-shortcut"),
593                                 G_OBJECT_CLASS_TYPE (class),
594                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
595                                 G_CALLBACK (search_shortcut_handler),
596                                 NULL, NULL,
597                                 _gtk_marshal_VOID__VOID,
598                                 G_TYPE_NONE, 0);
599
600   signals[RECENT_SHORTCUT] =
601     g_signal_new_class_handler (I_("recent-shortcut"),
602                                 G_OBJECT_CLASS_TYPE (class),
603                                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
604                                 G_CALLBACK (recent_shortcut_handler),
605                                 NULL, NULL,
606                                 _gtk_marshal_VOID__VOID,
607                                 G_TYPE_NONE, 0);
608
609   binding_set = gtk_binding_set_by_class (class);
610
611   gtk_binding_entry_add_signal (binding_set,
612                                 GDK_KEY_l, GDK_CONTROL_MASK,
613                                 "location-toggle-popup",
614                                 0);
615
616   gtk_binding_entry_add_signal (binding_set,
617                                 GDK_KEY_slash, 0,
618                                 "location-popup",
619                                 1, G_TYPE_STRING, "/");
620   gtk_binding_entry_add_signal (binding_set,
621                                 GDK_KEY_KP_Divide, 0,
622                                 "location-popup",
623                                 1, G_TYPE_STRING, "/");
624
625 #ifdef G_OS_UNIX
626   gtk_binding_entry_add_signal (binding_set,
627                                 GDK_KEY_asciitilde, 0,
628                                 "location-popup",
629                                 1, G_TYPE_STRING, "~");
630 #endif
631
632   gtk_binding_entry_add_signal (binding_set,
633                                 GDK_KEY_v, GDK_CONTROL_MASK,
634                                 "location-popup-on-paste",
635                                 0);
636   gtk_binding_entry_add_signal (binding_set,
637                                 GDK_KEY_BackSpace, 0,
638                                 "up-folder",
639                                 0);
640
641   add_normal_and_shifted_binding (binding_set,
642                                   GDK_KEY_Up, GDK_MOD1_MASK,
643                                   "up-folder");
644
645   add_normal_and_shifted_binding (binding_set,
646                                   GDK_KEY_KP_Up, GDK_MOD1_MASK,
647                                   "up-folder");
648
649   add_normal_and_shifted_binding (binding_set,
650                                   GDK_KEY_Down, GDK_MOD1_MASK,
651                                   "down-folder");
652   add_normal_and_shifted_binding (binding_set,
653                                   GDK_KEY_KP_Down, GDK_MOD1_MASK,
654                                   "down-folder");
655
656   gtk_binding_entry_add_signal (binding_set,
657                                 GDK_KEY_Home, GDK_MOD1_MASK,
658                                 "home-folder",
659                                 0);
660   gtk_binding_entry_add_signal (binding_set,
661                                 GDK_KEY_KP_Home, GDK_MOD1_MASK,
662                                 "home-folder",
663                                 0);
664   gtk_binding_entry_add_signal (binding_set,
665                                 GDK_KEY_d, GDK_MOD1_MASK,
666                                 "desktop-folder",
667                                 0);
668   gtk_binding_entry_add_signal (binding_set,
669                                 GDK_KEY_h, GDK_CONTROL_MASK,
670                                 "show-hidden",
671                                 0);
672   gtk_binding_entry_add_signal (binding_set,
673                                 GDK_KEY_s, GDK_MOD1_MASK,
674                                 "search-shortcut",
675                                 0);
676   gtk_binding_entry_add_signal (binding_set,
677                                 GDK_KEY_r, GDK_MOD1_MASK,
678                                 "recent-shortcut",
679                                 0);
680
681   for (i = 0; i < 10; i++)
682     gtk_binding_entry_add_signal (binding_set,
683                                   quick_bookmark_keyvals[i], GDK_MOD1_MASK,
684                                   "quick-bookmark",
685                                   1, G_TYPE_INT, i);
686
687   _gtk_file_chooser_install_properties (gobject_class);
688 }
689
690 static void
691 gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
692 {
693   iface->select_file = gtk_file_chooser_default_select_file;
694   iface->unselect_file = gtk_file_chooser_default_unselect_file;
695   iface->select_all = gtk_file_chooser_default_select_all;
696   iface->unselect_all = gtk_file_chooser_default_unselect_all;
697   iface->get_files = gtk_file_chooser_default_get_files;
698   iface->get_preview_file = gtk_file_chooser_default_get_preview_file;
699   iface->get_file_system = gtk_file_chooser_default_get_file_system;
700   iface->set_current_folder = gtk_file_chooser_default_set_current_folder;
701   iface->get_current_folder = gtk_file_chooser_default_get_current_folder;
702   iface->set_current_name = gtk_file_chooser_default_set_current_name;
703   iface->add_filter = gtk_file_chooser_default_add_filter;
704   iface->remove_filter = gtk_file_chooser_default_remove_filter;
705   iface->list_filters = gtk_file_chooser_default_list_filters;
706   iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder;
707   iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder;
708   iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders;
709 }
710
711 static void
712 gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
713 {
714   iface->get_default_size = gtk_file_chooser_default_get_default_size;
715   iface->should_respond = gtk_file_chooser_default_should_respond;
716   iface->initial_focus = gtk_file_chooser_default_initial_focus;
717 }
718
719 static void
720 _gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
721 {
722   profile_start ("start", NULL);
723 #ifdef PROFILE_FILE_CHOOSER
724   access ("MARK: *** CREATE FILE CHOOSER", F_OK);
725 #endif
726   impl->local_only = TRUE;
727   impl->preview_widget_active = TRUE;
728   impl->use_preview_label = TRUE;
729   impl->select_multiple = FALSE;
730   impl->show_hidden = FALSE;
731   impl->show_size_column = TRUE;
732   impl->icon_size = FALLBACK_ICON_SIZE;
733   impl->load_state = LOAD_EMPTY;
734   impl->reload_state = RELOAD_EMPTY;
735   impl->pending_select_files = NULL;
736   impl->location_mode = LOCATION_MODE_PATH_BAR;
737   impl->operation_mode = OPERATION_MODE_BROWSE;
738   impl->sort_column = MODEL_COL_NAME;
739   impl->sort_order = GTK_SORT_ASCENDING;
740   impl->recent_manager = gtk_recent_manager_get_default ();
741   impl->create_folders = TRUE;
742
743   gtk_orientable_set_orientation (GTK_ORIENTABLE (impl),
744                                   GTK_ORIENTATION_VERTICAL);
745   gtk_box_set_spacing (GTK_BOX (impl), 12);
746
747   set_file_system_backend (impl);
748
749   profile_end ("end", NULL);
750 }
751
752 /* Frees the data columns for the specified iter in the shortcuts model*/
753 static void
754 shortcuts_free_row_data (GtkFileChooserDefault *impl,
755                          GtkTreeIter           *iter)
756 {
757   gpointer col_data;
758   ShortcutType shortcut_type;
759   GCancellable *cancellable;
760
761   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
762                       SHORTCUTS_COL_DATA, &col_data,
763                       SHORTCUTS_COL_TYPE, &shortcut_type,
764                       SHORTCUTS_COL_CANCELLABLE, &cancellable,
765                       -1);
766
767   if (cancellable)
768     {
769       g_cancellable_cancel (cancellable);
770       g_object_unref (cancellable);
771     }
772
773   if (!(shortcut_type == SHORTCUT_TYPE_FILE || 
774         shortcut_type == SHORTCUT_TYPE_VOLUME) ||
775       !col_data)
776     return;
777
778   if (shortcut_type == SHORTCUT_TYPE_VOLUME)
779     {
780       GtkFileSystemVolume *volume;
781
782       volume = col_data;
783       _gtk_file_system_volume_unref (volume);
784     }
785   if (shortcut_type == SHORTCUT_TYPE_FILE)
786     {
787       GFile *file;
788
789       file = col_data;
790       g_object_unref (file);
791     }
792 }
793
794 /* Frees all the data columns in the shortcuts model */
795 static void
796 shortcuts_free (GtkFileChooserDefault *impl)
797 {
798   GtkTreeIter iter;
799
800   if (!impl->shortcuts_model)
801     return;
802
803   if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
804     do
805       {
806         shortcuts_free_row_data (impl, &iter);
807       }
808     while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter));
809
810   g_object_unref (impl->shortcuts_model);
811   impl->shortcuts_model = NULL;
812 }
813
814 static void
815 pending_select_files_free (GtkFileChooserDefault *impl)
816 {
817   g_slist_foreach (impl->pending_select_files, (GFunc) g_object_unref, NULL);
818   g_slist_free (impl->pending_select_files);
819   impl->pending_select_files = NULL;
820 }
821
822 static void
823 pending_select_files_add (GtkFileChooserDefault *impl,
824                           GFile                 *file)
825 {
826   impl->pending_select_files =
827     g_slist_prepend (impl->pending_select_files, g_object_ref (file));
828 }
829
830 static void
831 gtk_file_chooser_default_finalize (GObject *object)
832 {
833   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
834   GSList *l;
835
836   unset_file_system_backend (impl);
837
838   if (impl->shortcuts_pane_filter_model)
839     g_object_unref (impl->shortcuts_pane_filter_model);
840
841   if (impl->shortcuts_combo_filter_model)
842     g_object_unref (impl->shortcuts_combo_filter_model);
843
844   shortcuts_free (impl);
845
846   g_free (impl->browse_files_last_selected_name);
847
848   for (l = impl->filters; l; l = l->next)
849     {
850       GtkFileFilter *filter;
851
852       filter = GTK_FILE_FILTER (l->data);
853       g_object_unref (filter);
854     }
855   g_slist_free (impl->filters);
856
857   if (impl->current_filter)
858     g_object_unref (impl->current_filter);
859
860   if (impl->current_volume_file)
861     g_object_unref (impl->current_volume_file);
862
863   if (impl->current_folder)
864     g_object_unref (impl->current_folder);
865
866   if (impl->preview_file)
867     g_object_unref (impl->preview_file);
868
869   if (impl->browse_path_bar_size_group)
870     g_object_unref (impl->browse_path_bar_size_group);
871
872   /* Free all the Models we have */
873   stop_loading_and_clear_list_model (impl, FALSE);
874   search_clear_model (impl, FALSE);
875   recent_clear_model (impl, FALSE);
876
877   /* stopping the load above should have cleared this */
878   g_assert (impl->load_timeout_id == 0);
879
880   g_free (impl->preview_display_name);
881
882   g_free (impl->edited_new_text);
883
884   G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->finalize (object);
885 }
886
887 /* Shows an error dialog set as transient for the specified window */
888 static void
889 error_message_with_parent (GtkWindow  *parent,
890                            const char *msg,
891                            const char *detail)
892 {
893   GtkWidget *dialog;
894
895   dialog = gtk_message_dialog_new (parent,
896                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
897                                    GTK_MESSAGE_ERROR,
898                                    GTK_BUTTONS_OK,
899                                    "%s",
900                                    msg);
901   gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
902                                             "%s", detail);
903
904   if (parent && gtk_window_has_group (parent))
905     gtk_window_group_add_window (gtk_window_get_group (parent),
906                                  GTK_WINDOW (dialog));
907
908   gtk_dialog_run (GTK_DIALOG (dialog));
909   gtk_widget_destroy (dialog);
910 }
911
912 /* Returns a toplevel GtkWindow, or NULL if none */
913 static GtkWindow *
914 get_toplevel (GtkWidget *widget)
915 {
916   GtkWidget *toplevel;
917
918   toplevel = gtk_widget_get_toplevel (widget);
919   if (!gtk_widget_is_toplevel (toplevel))
920     return NULL;
921   else
922     return GTK_WINDOW (toplevel);
923 }
924
925 /* Shows an error dialog for the file chooser */
926 static void
927 error_message (GtkFileChooserDefault *impl,
928                const char            *msg,
929                const char            *detail)
930 {
931   error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail);
932 }
933
934 /* Shows a simple error dialog relative to a path.  Frees the GError as well. */
935 static void
936 error_dialog (GtkFileChooserDefault *impl,
937               const char            *msg,
938               GFile                 *file,
939               GError                *error)
940 {
941   if (error)
942     {
943       char *uri = NULL;
944       char *text;
945
946       if (file)
947         uri = g_file_get_uri (file);
948       text = g_strdup_printf (msg, uri);
949       error_message (impl, text, error->message);
950       g_free (text);
951       g_free (uri);
952       g_error_free (error);
953     }
954 }
955
956 /* Displays an error message about not being able to get information for a file.
957  * Frees the GError as well.
958  */
959 static void
960 error_getting_info_dialog (GtkFileChooserDefault *impl,
961                            GFile                 *file,
962                            GError                *error)
963 {
964   error_dialog (impl,
965                 _("Could not retrieve information about the file"),
966                 file, error);
967 }
968
969 /* Shows an error dialog about not being able to add a bookmark */
970 static void
971 error_adding_bookmark_dialog (GtkFileChooserDefault *impl,
972                               GFile                 *file,
973                               GError                *error)
974 {
975   error_dialog (impl,
976                 _("Could not add a bookmark"),
977                 file, error);
978 }
979
980 /* Shows an error dialog about not being able to remove a bookmark */
981 static void
982 error_removing_bookmark_dialog (GtkFileChooserDefault *impl,
983                                 GFile                 *file,
984                                 GError                *error)
985 {
986   error_dialog (impl,
987                 _("Could not remove bookmark"),
988                 file, error);
989 }
990
991 /* Shows an error dialog about not being able to create a folder */
992 static void
993 error_creating_folder_dialog (GtkFileChooserDefault *impl,
994                               GFile                 *file,
995                               GError                *error)
996 {
997   error_dialog (impl, 
998                 _("The folder could not be created"), 
999                 file, error);
1000 }
1001
1002 /* Shows an error about not being able to create a folder because a file with
1003  * the same name is already there.
1004  */
1005 static void
1006 error_creating_folder_over_existing_file_dialog (GtkFileChooserDefault *impl,
1007                                                  GFile                 *file,
1008                                                  GError                *error)
1009 {
1010   error_dialog (impl,
1011                 _("The folder could not be created, as a file with the same "
1012                   "name already exists.  Try using a different name for the "
1013                   "folder, or rename the file first."),
1014                 file, error);
1015 }
1016
1017 static void
1018 error_with_file_under_nonfolder (GtkFileChooserDefault *impl,
1019                                  GFile *parent_file)
1020 {
1021   GError *error;
1022
1023   error = NULL;
1024   g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY,
1025                        _("You need to choose a valid filename."));
1026
1027   error_dialog (impl,
1028                 _("Cannot create a file under %s as it is not a folder"),
1029                 parent_file, error);
1030 }
1031
1032 /* Shows an error about not being able to select a folder because a file with
1033  * the same name is already there.
1034  */
1035 static void
1036 error_selecting_folder_over_existing_file_dialog (GtkFileChooserDefault *impl,
1037                                                   GFile                 *file)
1038 {
1039   error_dialog (impl,
1040                 _("You may only select folders.  The item that you selected is not a folder; "
1041                   "try using a different item."),
1042                 file, NULL);
1043 }
1044
1045 /* Shows an error dialog about not being able to create a filename */
1046 static void
1047 error_building_filename_dialog (GtkFileChooserDefault *impl,
1048                                 GError                *error)
1049 {
1050   error_dialog (impl, _("Invalid file name"), 
1051                 NULL, error);
1052 }
1053
1054 /* Shows an error dialog when we cannot switch to a folder */
1055 static void
1056 error_changing_folder_dialog (GtkFileChooserDefault *impl,
1057                               GFile                 *file,
1058                               GError                *error)
1059 {
1060   error_dialog (impl, _("The folder contents could not be displayed"),
1061                 file, error);
1062 }
1063
1064 /* Changes folders, displaying an error dialog if this fails */
1065 static gboolean
1066 change_folder_and_display_error (GtkFileChooserDefault *impl,
1067                                  GFile                 *file,
1068                                  gboolean               clear_entry)
1069 {
1070   GError *error;
1071   gboolean result;
1072
1073   g_return_val_if_fail (G_IS_FILE (file), FALSE);
1074
1075   /* We copy the path because of this case:
1076    *
1077    * list_row_activated()
1078    *   fetches path from model; path belongs to the model (*)
1079    *   calls change_folder_and_display_error()
1080    *     calls gtk_file_chooser_set_current_folder_file()
1081    *       changing folders fails, sets model to NULL, thus freeing the path in (*)
1082    */
1083
1084   error = NULL;
1085   result = gtk_file_chooser_default_update_current_folder (GTK_FILE_CHOOSER (impl), file, TRUE, clear_entry, &error);
1086
1087   if (!result)
1088     error_changing_folder_dialog (impl, file, error);
1089
1090   return result;
1091 }
1092
1093 static void
1094 emit_default_size_changed (GtkFileChooserDefault *impl)
1095 {
1096   profile_msg ("    emit default-size-changed start", NULL);
1097   g_signal_emit_by_name (impl, "default-size-changed");
1098   profile_msg ("    emit default-size-changed end", NULL);
1099 }
1100
1101 static void
1102 update_preview_widget_visibility (GtkFileChooserDefault *impl)
1103 {
1104   if (impl->use_preview_label)
1105     {
1106       if (!impl->preview_label)
1107         {
1108           impl->preview_label = gtk_label_new (impl->preview_display_name);
1109           gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_label, FALSE, FALSE, 0);
1110           gtk_box_reorder_child (GTK_BOX (impl->preview_box), impl->preview_label, 0);
1111           gtk_label_set_ellipsize (GTK_LABEL (impl->preview_label), PANGO_ELLIPSIZE_MIDDLE);
1112           gtk_widget_show (impl->preview_label);
1113         }
1114     }
1115   else
1116     {
1117       if (impl->preview_label)
1118         {
1119           gtk_widget_destroy (impl->preview_label);
1120           impl->preview_label = NULL;
1121         }
1122     }
1123
1124   if (impl->preview_widget_active && impl->preview_widget)
1125     gtk_widget_show (impl->preview_box);
1126   else
1127     gtk_widget_hide (impl->preview_box);
1128
1129   if (!gtk_widget_get_mapped (GTK_WIDGET (impl)))
1130     emit_default_size_changed (impl);
1131 }
1132
1133 static void
1134 set_preview_widget (GtkFileChooserDefault *impl,
1135                     GtkWidget             *preview_widget)
1136 {
1137   if (preview_widget == impl->preview_widget)
1138     return;
1139
1140   if (impl->preview_widget)
1141     gtk_container_remove (GTK_CONTAINER (impl->preview_box),
1142                           impl->preview_widget);
1143
1144   impl->preview_widget = preview_widget;
1145   if (impl->preview_widget)
1146     {
1147       gtk_widget_show (impl->preview_widget);
1148       gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_widget, TRUE, TRUE, 0);
1149       gtk_box_reorder_child (GTK_BOX (impl->preview_box),
1150                              impl->preview_widget,
1151                              (impl->use_preview_label && impl->preview_label) ? 1 : 0);
1152     }
1153
1154   update_preview_widget_visibility (impl);
1155 }
1156
1157 /* Renders a "Search" icon at an appropriate size for a tree view */
1158 static GdkPixbuf *
1159 render_search_icon (GtkFileChooserDefault *impl)
1160 {
1161   return gtk_widget_render_icon_pixbuf (GTK_WIDGET (impl), GTK_STOCK_FIND, GTK_ICON_SIZE_MENU);
1162 }
1163
1164 static GdkPixbuf *
1165 render_recent_icon (GtkFileChooserDefault *impl)
1166 {
1167   GtkIconTheme *theme;
1168   GdkPixbuf *retval;
1169
1170   if (gtk_widget_has_screen (GTK_WIDGET (impl)))
1171     theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
1172   else
1173     theme = gtk_icon_theme_get_default ();
1174
1175   retval = gtk_icon_theme_load_icon (theme, "document-open-recent",
1176                                      impl->icon_size, 0,
1177                                      NULL);
1178
1179   /* fallback */
1180   if (!retval)
1181     retval = gtk_widget_render_icon_pixbuf (GTK_WIDGET (impl), GTK_STOCK_FILE, GTK_ICON_SIZE_MENU);
1182
1183   return retval;
1184 }
1185
1186
1187 /* Re-reads all the icons for the shortcuts, used when the theme changes */
1188 struct ReloadIconsData
1189 {
1190   GtkFileChooserDefault *impl;
1191   GtkTreeRowReference *row_ref;
1192 };
1193
1194 static void
1195 shortcuts_reload_icons_get_info_cb (GCancellable *cancellable,
1196                                     GFileInfo    *info,
1197                                     const GError *error,
1198                                     gpointer      user_data)
1199 {
1200   GdkPixbuf *pixbuf;
1201   GtkTreeIter iter;
1202   GtkTreePath *path;
1203   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1204   struct ReloadIconsData *data = user_data;
1205
1206   if (!g_slist_find (data->impl->reload_icon_cancellables, cancellable))
1207     goto out;
1208
1209   data->impl->reload_icon_cancellables = g_slist_remove (data->impl->reload_icon_cancellables, cancellable);
1210
1211   if (cancelled || error)
1212     goto out;
1213
1214   pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->impl), data->impl->icon_size);
1215
1216   path = gtk_tree_row_reference_get_path (data->row_ref);
1217   if (path)
1218     {
1219       gtk_tree_model_get_iter (GTK_TREE_MODEL (data->impl->shortcuts_model), &iter, path);
1220       gtk_list_store_set (data->impl->shortcuts_model, &iter,
1221                           SHORTCUTS_COL_PIXBUF, pixbuf,
1222                           -1);
1223       gtk_tree_path_free (path);
1224     }
1225
1226   if (pixbuf)
1227     g_object_unref (pixbuf);
1228
1229 out:
1230   gtk_tree_row_reference_free (data->row_ref);
1231   g_object_unref (data->impl);
1232   g_free (data);
1233
1234   g_object_unref (cancellable);
1235 }
1236
1237 static void
1238 shortcuts_reload_icons (GtkFileChooserDefault *impl)
1239 {
1240   GSList *l;
1241   GtkTreeIter iter;
1242
1243   profile_start ("start", NULL);
1244
1245   if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
1246     goto out;
1247
1248   for (l = impl->reload_icon_cancellables; l; l = l->next)
1249     {
1250       GCancellable *cancellable = G_CANCELLABLE (l->data);
1251       g_cancellable_cancel (cancellable);
1252     }
1253   g_slist_free (impl->reload_icon_cancellables);
1254   impl->reload_icon_cancellables = NULL;
1255
1256   do
1257     {
1258       gpointer data;
1259       ShortcutType shortcut_type;
1260       gboolean pixbuf_visible;
1261       GdkPixbuf *pixbuf;
1262
1263       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
1264                           SHORTCUTS_COL_DATA, &data,
1265                           SHORTCUTS_COL_TYPE, &shortcut_type,
1266                           SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible,
1267                           -1);
1268
1269       pixbuf = NULL;
1270       if (pixbuf_visible)
1271         {
1272           if (shortcut_type == SHORTCUT_TYPE_VOLUME)
1273             {
1274               GtkFileSystemVolume *volume;
1275
1276               volume = data;
1277               pixbuf = _gtk_file_system_volume_render_icon (volume, GTK_WIDGET (impl),
1278                                                             impl->icon_size, NULL);
1279             }
1280           else if (shortcut_type == SHORTCUT_TYPE_FILE)
1281             {
1282               if (g_file_is_native (G_FILE (data)))
1283                 {
1284                   GFile *file;
1285                   struct ReloadIconsData *info;
1286                   GtkTreePath *tree_path;
1287                   GCancellable *cancellable;
1288
1289                   file = data;
1290
1291                   info = g_new0 (struct ReloadIconsData, 1);
1292                   info->impl = g_object_ref (impl);
1293                   tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
1294                   info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->shortcuts_model), tree_path);
1295                   gtk_tree_path_free (tree_path);
1296
1297                   cancellable = _gtk_file_system_get_info (impl->file_system, file,
1298                                                            "standard::icon",
1299                                                            shortcuts_reload_icons_get_info_cb,
1300                                                            info);
1301                   impl->reload_icon_cancellables = g_slist_append (impl->reload_icon_cancellables, cancellable);
1302                 }
1303               else
1304                 {
1305                   GtkIconTheme *icon_theme;
1306
1307                   /* Don't call get_info for remote paths to avoid latency and
1308                    * auth dialogs.
1309                    * If we switch to a better bookmarks file format (XBEL), we
1310                    * should use mime info to get a better icon.
1311                    */
1312                   icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
1313                   pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote", 
1314                                                      impl->icon_size, 0, NULL);
1315                 }
1316             }
1317           else if (shortcut_type == SHORTCUT_TYPE_SEARCH)
1318             {
1319               pixbuf = render_search_icon (impl);
1320             }
1321           else if (shortcut_type == SHORTCUT_TYPE_RECENT)
1322             {
1323               pixbuf = render_recent_icon (impl);
1324             }
1325
1326           gtk_list_store_set (impl->shortcuts_model, &iter,
1327                               SHORTCUTS_COL_PIXBUF, pixbuf,
1328                               -1);
1329
1330           if (pixbuf)
1331             g_object_unref (pixbuf);
1332
1333         }
1334     }
1335   while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
1336
1337  out:
1338
1339   profile_end ("end", NULL);
1340 }
1341
1342 static void 
1343 shortcuts_find_folder (GtkFileChooserDefault *impl,
1344                        GFile                 *folder)
1345 {
1346   GtkTreeSelection *selection;
1347   int pos;
1348   GtkTreePath *path;
1349
1350   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1351
1352   g_assert (folder != NULL);
1353   pos = shortcut_find_position (impl, folder);
1354   if (pos == -1)
1355     {
1356       gtk_tree_selection_unselect_all (selection);
1357       return;
1358     }
1359
1360   path = gtk_tree_path_new_from_indices (pos, -1);
1361   gtk_tree_selection_select_path (selection, path);
1362   gtk_tree_path_free (path);
1363 }
1364
1365 /* If a shortcut corresponds to the current folder, selects it */
1366 static void
1367 shortcuts_find_current_folder (GtkFileChooserDefault *impl)
1368 {
1369   shortcuts_find_folder (impl, impl->current_folder);
1370 }
1371
1372 /* Removes the specified number of rows from the shortcuts list */
1373 static void
1374 shortcuts_remove_rows (GtkFileChooserDefault *impl,
1375                        int                    start_row,
1376                        int                    n_rows)
1377 {
1378   GtkTreePath *path;
1379
1380   path = gtk_tree_path_new_from_indices (start_row, -1);
1381
1382   for (; n_rows; n_rows--)
1383     {
1384       GtkTreeIter iter;
1385
1386       if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
1387         g_assert_not_reached ();
1388
1389       shortcuts_free_row_data (impl, &iter);
1390       gtk_list_store_remove (impl->shortcuts_model, &iter);
1391     }
1392
1393   gtk_tree_path_free (path);
1394 }
1395
1396 static void
1397 shortcuts_update_count (GtkFileChooserDefault *impl,
1398                         ShortcutsIndex         type,
1399                         gint                   value)
1400 {
1401   switch (type)
1402     {
1403       case SHORTCUTS_HOME:
1404         if (value < 0)
1405           impl->has_home = FALSE;
1406         else
1407           impl->has_home = TRUE;
1408         break;
1409
1410       case SHORTCUTS_DESKTOP:
1411         if (value < 0)
1412           impl->has_desktop = FALSE;
1413         else
1414           impl->has_desktop = TRUE;
1415         break;
1416
1417       case SHORTCUTS_VOLUMES:
1418         impl->num_volumes += value;
1419         break;
1420
1421       case SHORTCUTS_SHORTCUTS:
1422         impl->num_shortcuts += value;
1423         break;
1424
1425       case SHORTCUTS_BOOKMARKS:
1426         impl->num_bookmarks += value;
1427         break;
1428
1429       case SHORTCUTS_CURRENT_FOLDER:
1430         if (value < 0)
1431           impl->shortcuts_current_folder_active = FALSE;
1432         else
1433           impl->shortcuts_current_folder_active = TRUE;
1434         break;
1435
1436       default:
1437         /* nothing */
1438         break;
1439     }
1440 }
1441
1442 struct ShortcutsInsertRequest
1443 {
1444   GtkFileChooserDefault *impl;
1445   GFile *file;
1446   int pos;
1447   char *label_copy;
1448   GtkTreeRowReference *row_ref;
1449   ShortcutsIndex type;
1450   gboolean name_only;
1451   gboolean removable;
1452 };
1453
1454 static void
1455 get_file_info_finished (GCancellable *cancellable,
1456                         GFileInfo    *info,
1457                         const GError *error,
1458                         gpointer      data)
1459 {
1460   gint pos = -1;
1461   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1462   GdkPixbuf *pixbuf;
1463   GtkTreePath *path;
1464   GtkTreeIter iter;
1465   GCancellable *model_cancellable = NULL;
1466   struct ShortcutsInsertRequest *request = data;
1467
1468   path = gtk_tree_row_reference_get_path (request->row_ref);
1469   if (!path)
1470     /* Handle doesn't exist anymore in the model */
1471     goto out;
1472
1473   pos = gtk_tree_path_get_indices (path)[0];
1474   gtk_tree_model_get_iter (GTK_TREE_MODEL (request->impl->shortcuts_model),
1475                            &iter, path);
1476   gtk_tree_path_free (path);
1477
1478   /* validate cancellable, else goto out */
1479   gtk_tree_model_get (GTK_TREE_MODEL (request->impl->shortcuts_model), &iter,
1480                       SHORTCUTS_COL_CANCELLABLE, &model_cancellable,
1481                       -1);
1482   if (cancellable != model_cancellable)
1483     goto out;
1484
1485   /* set the cancellable to NULL in the model (we unref later on) */
1486   gtk_list_store_set (request->impl->shortcuts_model, &iter,
1487                       SHORTCUTS_COL_CANCELLABLE, NULL,
1488                       -1);
1489
1490   if (cancelled)
1491     goto out;
1492
1493   if (!info)
1494     {
1495       gtk_list_store_remove (request->impl->shortcuts_model, &iter);
1496       shortcuts_update_count (request->impl, request->type, -1);
1497
1498       if (request->type == SHORTCUTS_HOME)
1499         {
1500           GFile *home;
1501
1502           home = g_file_new_for_path (g_get_home_dir ());
1503           error_getting_info_dialog (request->impl, home, g_error_copy (error));
1504           g_object_unref (home);
1505         }
1506       else if (request->type == SHORTCUTS_CURRENT_FOLDER)
1507         {
1508           /* Remove the current folder separator */
1509           gint separator_pos = shortcuts_get_index (request->impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1510           shortcuts_remove_rows (request->impl, separator_pos, 1);
1511         }
1512
1513       goto out;
1514     }
1515   
1516   if (!request->label_copy)
1517     request->label_copy = g_strdup (g_file_info_get_display_name (info));
1518   pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (request->impl),
1519                                        request->impl->icon_size);
1520
1521   gtk_list_store_set (request->impl->shortcuts_model, &iter,
1522                       SHORTCUTS_COL_PIXBUF, pixbuf,
1523                       SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1524                       SHORTCUTS_COL_NAME, request->label_copy,
1525                       SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_FILE,
1526                       SHORTCUTS_COL_REMOVABLE, request->removable,
1527                       -1);
1528
1529   if (request->impl->shortcuts_pane_filter_model)
1530     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (request->impl->shortcuts_pane_filter_model));
1531
1532   if (request->impl->shortcuts_combo_filter_model)
1533     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (request->impl->shortcuts_combo_filter_model));
1534
1535   if (request->type == SHORTCUTS_CURRENT_FOLDER &&
1536       request->impl->save_folder_combo != NULL)
1537     {
1538       /* The current folder is updated via _activate_iter(), don't
1539        * have save_folder_combo_changed_cb() call _activate_iter()
1540        * again.
1541        */
1542       g_signal_handlers_block_by_func (request->impl->save_folder_combo,
1543                                        G_CALLBACK (save_folder_combo_changed_cb),
1544                                        request->impl);
1545       
1546       if (request->impl->has_search)
1547         pos -= 1;
1548
1549       if (request->impl->has_recent)
1550         pos -= 2;
1551
1552       gtk_combo_box_set_active (GTK_COMBO_BOX (request->impl->save_folder_combo), pos);
1553       g_signal_handlers_unblock_by_func (request->impl->save_folder_combo,
1554                                          G_CALLBACK (save_folder_combo_changed_cb),
1555                                          request->impl);
1556     }
1557
1558   if (pixbuf)
1559     g_object_unref (pixbuf);
1560
1561 out:
1562   g_object_unref (request->impl);
1563   g_object_unref (request->file);
1564   gtk_tree_row_reference_free (request->row_ref);
1565   g_free (request->label_copy);
1566   g_free (request);
1567
1568   if (model_cancellable)
1569     g_object_unref (model_cancellable);
1570 }
1571
1572 /* FIXME: GtkFileSystem needs a function to split a remote path
1573  * into hostname and path components, or maybe just have a 
1574  * gtk_file_system_path_get_display_name().
1575  *
1576  * This function is also used in gtkfilechooserbutton.c
1577  */
1578 gchar *
1579 _gtk_file_chooser_label_for_file (GFile *file)
1580 {
1581   const gchar *path, *start, *end, *p;
1582   gchar *uri, *host, *label;
1583
1584   uri = g_file_get_uri (file);
1585
1586   start = strstr (uri, "://");
1587   if (start)
1588     {
1589       start += 3;
1590       path = strchr (start, '/');
1591       if (path)
1592         end = path;
1593       else
1594         {
1595           end = uri + strlen (uri);
1596           path = "/";
1597         }
1598
1599       /* strip username */
1600       p = strchr (start, '@');
1601       if (p && p < end)
1602         start = p + 1;
1603   
1604       p = strchr (start, ':');
1605       if (p && p < end)
1606         end = p;
1607   
1608       host = g_strndup (start, end - start);
1609   
1610       /* Translators: the first string is a path and the second string 
1611        * is a hostname. Nautilus and the panel contain the same string 
1612        * to translate. 
1613        */
1614       label = g_strdup_printf (_("%1$s on %2$s"), path, host);
1615   
1616       g_free (host);
1617     }
1618   else
1619     {
1620       label = g_strdup (uri);
1621     }
1622   
1623   g_free (uri);
1624
1625   return label;
1626 }
1627
1628 /* Inserts a path in the shortcuts tree, making a copy of it; alternatively,
1629  * inserts a volume.  A position of -1 indicates the end of the tree.
1630  */
1631 static void
1632 shortcuts_insert_file (GtkFileChooserDefault *impl,
1633                        int                    pos,
1634                        ShortcutType           shortcut_type,
1635                        GtkFileSystemVolume   *volume,
1636                        GFile                 *file,
1637                        const char            *label,
1638                        gboolean               removable,
1639                        ShortcutsIndex         type)
1640 {
1641   char *label_copy;
1642   GdkPixbuf *pixbuf = NULL;
1643   gpointer data = NULL;
1644   GtkTreeIter iter;
1645   GtkIconTheme *icon_theme;
1646
1647   profile_start ("start shortcut", NULL);
1648
1649   if (shortcut_type == SHORTCUT_TYPE_VOLUME)
1650     {
1651       data = volume;
1652       label_copy = _gtk_file_system_volume_get_display_name (volume);
1653       pixbuf = _gtk_file_system_volume_render_icon (volume, GTK_WIDGET (impl),
1654                                                     impl->icon_size, NULL);
1655     }
1656   else if (shortcut_type == SHORTCUT_TYPE_FILE)
1657     {
1658       if (g_file_is_native (file))
1659         {
1660           struct ShortcutsInsertRequest *request;
1661           GCancellable *cancellable;
1662           GtkTreePath *p;
1663
1664           request = g_new0 (struct ShortcutsInsertRequest, 1);
1665           request->impl = g_object_ref (impl);
1666           request->file = g_object_ref (file);
1667           request->name_only = TRUE;
1668           request->removable = removable;
1669           request->pos = pos;
1670           request->type = type;
1671           if (label)
1672             request->label_copy = g_strdup (label);
1673
1674           if (pos == -1)
1675             gtk_list_store_append (impl->shortcuts_model, &iter);
1676           else
1677             gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
1678
1679           p = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
1680           request->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->shortcuts_model), p);
1681           gtk_tree_path_free (p);
1682
1683           cancellable = _gtk_file_system_get_info (request->impl->file_system, request->file,
1684                                                    "standard::is-hidden,standard::is-backup,standard::display-name,standard::icon",
1685                                                    get_file_info_finished, request);
1686
1687           gtk_list_store_set (impl->shortcuts_model, &iter,
1688                               SHORTCUTS_COL_DATA, g_object_ref (file),
1689                               SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_FILE,
1690                               SHORTCUTS_COL_CANCELLABLE, cancellable,
1691                               -1);
1692
1693           shortcuts_update_count (impl, type, 1);
1694
1695           return;
1696         }
1697       else
1698         {
1699           /* Don't call get_info for remote paths to avoid latency and
1700            * auth dialogs.
1701            */
1702           data = g_object_ref (file);
1703           if (label)
1704             label_copy = g_strdup (label);
1705           else
1706             label_copy = _gtk_file_chooser_label_for_file (file);
1707
1708           /* If we switch to a better bookmarks file format (XBEL), we
1709            * should use mime info to get a better icon.
1710            */
1711           icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
1712           pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote", 
1713                                              impl->icon_size, 0, NULL);
1714         }
1715     }
1716    else
1717     {
1718       g_assert_not_reached ();
1719
1720       return;
1721     }
1722
1723   if (pos == -1)
1724     gtk_list_store_append (impl->shortcuts_model, &iter);
1725   else
1726     gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
1727
1728   shortcuts_update_count (impl, type, 1);
1729
1730   gtk_list_store_set (impl->shortcuts_model, &iter,
1731                       SHORTCUTS_COL_PIXBUF, pixbuf,
1732                       SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1733                       SHORTCUTS_COL_NAME, label_copy,
1734                       SHORTCUTS_COL_DATA, data,
1735                       SHORTCUTS_COL_TYPE, shortcut_type,
1736                       SHORTCUTS_COL_REMOVABLE, removable,
1737                       SHORTCUTS_COL_CANCELLABLE, NULL,
1738                       -1);
1739
1740   if (impl->shortcuts_pane_filter_model)
1741     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model));
1742
1743   if (impl->shortcuts_combo_filter_model)
1744     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model));
1745
1746   if (type == SHORTCUTS_CURRENT_FOLDER && impl->save_folder_combo != NULL)
1747     {
1748       /* The current folder is updated via _activate_iter(), don't
1749        * have save_folder_combo_changed_cb() call _activate_iter()
1750        * again.
1751        */
1752       gint combo_pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER);
1753
1754       if (impl->has_search)
1755         combo_pos -= 1;
1756
1757       if (impl->has_recent)
1758         combo_pos -= 2;
1759       
1760       g_signal_handlers_block_by_func (impl->save_folder_combo,
1761                                        G_CALLBACK (save_folder_combo_changed_cb),
1762                                        impl);
1763       
1764       gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), combo_pos);
1765       g_signal_handlers_unblock_by_func (impl->save_folder_combo,
1766                                          G_CALLBACK (save_folder_combo_changed_cb),
1767                                          impl);
1768     }
1769
1770   g_free (label_copy);
1771
1772   if (pixbuf)
1773     g_object_unref (pixbuf);
1774
1775   profile_end ("end", NULL);
1776 }
1777
1778 static void
1779 shortcuts_append_search (GtkFileChooserDefault *impl)
1780 {
1781   GdkPixbuf *pixbuf;
1782   GtkTreeIter iter;
1783
1784   pixbuf = render_search_icon (impl);
1785
1786   gtk_list_store_append (impl->shortcuts_model, &iter);
1787   gtk_list_store_set (impl->shortcuts_model, &iter,
1788                       SHORTCUTS_COL_PIXBUF, pixbuf,
1789                       SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1790                       SHORTCUTS_COL_NAME, _("Search"),
1791                       SHORTCUTS_COL_DATA, NULL,
1792                       SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_SEARCH,
1793                       SHORTCUTS_COL_REMOVABLE, FALSE,
1794                       -1);
1795
1796   if (pixbuf)
1797     g_object_unref (pixbuf);
1798
1799   impl->has_search = TRUE;
1800 }
1801
1802 static void
1803 shortcuts_append_recent (GtkFileChooserDefault *impl)
1804 {
1805   GdkPixbuf *pixbuf;
1806   GtkTreeIter iter;
1807
1808   pixbuf = render_recent_icon (impl);
1809
1810   gtk_list_store_append (impl->shortcuts_model, &iter);
1811   gtk_list_store_set (impl->shortcuts_model, &iter,
1812                       SHORTCUTS_COL_PIXBUF, pixbuf,
1813                       SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1814                       SHORTCUTS_COL_NAME, _("Recently Used"),
1815                       SHORTCUTS_COL_DATA, NULL,
1816                       SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_RECENT,
1817                       SHORTCUTS_COL_REMOVABLE, FALSE,
1818                       -1);
1819   
1820   if (pixbuf)
1821     g_object_unref (pixbuf);
1822
1823   impl->has_recent = TRUE;
1824 }
1825
1826 /* Appends an item for the user's home directory to the shortcuts model */
1827 static void
1828 shortcuts_append_home (GtkFileChooserDefault *impl)
1829 {
1830   const char *home_path;
1831   GFile *home;
1832
1833   profile_start ("start", NULL);
1834
1835   home_path = g_get_home_dir ();
1836   if (home_path == NULL)
1837     {
1838       profile_end ("end - no home directory!?", NULL);
1839       return;
1840     }
1841
1842   home = g_file_new_for_path (home_path);
1843   shortcuts_insert_file (impl, -1, SHORTCUT_TYPE_FILE, NULL, home, NULL, FALSE, SHORTCUTS_HOME);
1844   impl->has_home = TRUE;
1845
1846   g_object_unref (home);
1847
1848   profile_end ("end", NULL);
1849 }
1850
1851 /* Appends the ~/Desktop directory to the shortcuts model */
1852 static void
1853 shortcuts_append_desktop (GtkFileChooserDefault *impl)
1854 {
1855   const char *name;
1856   GFile *file;
1857
1858   profile_start ("start", NULL);
1859
1860   name = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
1861   /* "To disable a directory, point it to the homedir."
1862    * See http://freedesktop.org/wiki/Software/xdg-user-dirs
1863    **/
1864   if (!g_strcmp0 (name, g_get_home_dir ()))
1865     {
1866       profile_end ("end", NULL);
1867       return;
1868     }
1869
1870   file = g_file_new_for_path (name);
1871   shortcuts_insert_file (impl, -1, SHORTCUT_TYPE_FILE, NULL, file, _("Desktop"), FALSE, SHORTCUTS_DESKTOP);
1872   impl->has_desktop = TRUE;
1873
1874   /* We do not actually pop up an error dialog if there is no desktop directory
1875    * because some people may really not want to have one.
1876    */
1877
1878   g_object_unref (file);
1879
1880   profile_end ("end", NULL);
1881 }
1882
1883 /* Appends a list of GFile to the shortcuts model; returns how many were inserted */
1884 static int
1885 shortcuts_append_bookmarks (GtkFileChooserDefault *impl,
1886                             GSList                *bookmarks)
1887 {
1888   int start_row;
1889   int num_inserted;
1890   gchar *label;
1891
1892   profile_start ("start", NULL);
1893
1894   start_row = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR) + 1;
1895   num_inserted = 0;
1896
1897   for (; bookmarks; bookmarks = bookmarks->next)
1898     {
1899       GFile *file;
1900
1901       file = bookmarks->data;
1902
1903       if (impl->local_only && !g_file_is_native (file))
1904         continue;
1905
1906       if (shortcut_find_position (impl, file) != -1)
1907         continue;
1908
1909       label = _gtk_file_system_get_bookmark_label (impl->file_system, file);
1910
1911       shortcuts_insert_file (impl, start_row + num_inserted, SHORTCUT_TYPE_FILE, NULL, file, label, TRUE, SHORTCUTS_BOOKMARKS);
1912       g_free (label);
1913
1914       num_inserted++;
1915     }
1916
1917   profile_end ("end", NULL);
1918
1919   return num_inserted;
1920 }
1921
1922 /* Returns the index for the corresponding item in the shortcuts bar */
1923 static int
1924 shortcuts_get_index (GtkFileChooserDefault *impl,
1925                      ShortcutsIndex         where)
1926 {
1927   int n;
1928
1929   n = 0;
1930   
1931   if (where == SHORTCUTS_SEARCH)
1932     goto out;
1933
1934   n += impl->has_search ? 1 : 0;
1935
1936   if (where == SHORTCUTS_RECENT)
1937     goto out;
1938
1939   n += impl->has_recent ? 1 : 0;
1940
1941   if (where == SHORTCUTS_RECENT_SEPARATOR)
1942     goto out;
1943
1944   n += impl->has_recent ? 1 : 0;
1945
1946   if (where == SHORTCUTS_HOME)
1947     goto out;
1948
1949   n += impl->has_home ? 1 : 0;
1950
1951   if (where == SHORTCUTS_DESKTOP)
1952     goto out;
1953
1954   n += impl->has_desktop ? 1 : 0;
1955
1956   if (where == SHORTCUTS_VOLUMES)
1957     goto out;
1958
1959   n += impl->num_volumes;
1960
1961   if (where == SHORTCUTS_SHORTCUTS)
1962     goto out;
1963
1964   n += impl->num_shortcuts;
1965
1966   if (where == SHORTCUTS_BOOKMARKS_SEPARATOR)
1967     goto out;
1968
1969   /* If there are no bookmarks there won't be a separator */
1970   n += (impl->num_bookmarks > 0) ? 1 : 0;
1971
1972   if (where == SHORTCUTS_BOOKMARKS)
1973     goto out;
1974
1975   n += impl->num_bookmarks;
1976
1977   if (where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR)
1978     goto out;
1979
1980   n += 1;
1981
1982   if (where == SHORTCUTS_CURRENT_FOLDER)
1983     goto out;
1984
1985   g_assert_not_reached ();
1986
1987  out:
1988
1989   return n;
1990 }
1991
1992 /* Adds all the file system volumes to the shortcuts model */
1993 static void
1994 shortcuts_add_volumes (GtkFileChooserDefault *impl)
1995 {
1996   int start_row;
1997   GSList *list, *l;
1998   int n;
1999   gboolean old_changing_folders;
2000
2001   profile_start ("start", NULL);
2002
2003   old_changing_folders = impl->changing_folder;
2004   impl->changing_folder = TRUE;
2005
2006   start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
2007   shortcuts_remove_rows (impl, start_row, impl->num_volumes);
2008   impl->num_volumes = 0;
2009
2010   list = _gtk_file_system_list_volumes (impl->file_system);
2011
2012   n = 0;
2013
2014   for (l = list; l; l = l->next)
2015     {
2016       GtkFileSystemVolume *volume;
2017
2018       volume = l->data;
2019
2020       if (impl->local_only)
2021         {
2022           if (_gtk_file_system_volume_is_mounted (volume))
2023             {
2024               GFile *base_file;
2025               gboolean base_is_native = TRUE;
2026
2027               base_file = _gtk_file_system_volume_get_root (volume);
2028               if (base_file != NULL)
2029                 {
2030                   base_is_native = g_file_is_native (base_file);
2031                   g_object_unref (base_file);
2032                 }
2033
2034               if (!base_is_native)
2035                 continue;
2036             }
2037         }
2038
2039       shortcuts_insert_file (impl,
2040                              start_row + n,
2041                              SHORTCUT_TYPE_VOLUME,
2042                              _gtk_file_system_volume_ref (volume),
2043                              NULL,
2044                              NULL,
2045                              FALSE,
2046                              SHORTCUTS_VOLUMES);
2047       n++;
2048     }
2049
2050   impl->num_volumes = n;
2051   g_slist_free (list);
2052
2053   if (impl->shortcuts_pane_filter_model)
2054     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model));
2055
2056   if (impl->shortcuts_combo_filter_model)
2057     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model));
2058
2059   impl->changing_folder = old_changing_folders;
2060
2061   profile_end ("end", NULL);
2062 }
2063
2064 /* Inserts a separator node in the shortcuts list */
2065 static void
2066 shortcuts_insert_separator (GtkFileChooserDefault *impl,
2067                             ShortcutsIndex         where)
2068 {
2069   GtkTreeIter iter;
2070
2071   g_assert (where == SHORTCUTS_RECENT_SEPARATOR || 
2072             where == SHORTCUTS_BOOKMARKS_SEPARATOR || 
2073             where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2074
2075   gtk_list_store_insert (impl->shortcuts_model, &iter,
2076                          shortcuts_get_index (impl, where));
2077   gtk_list_store_set (impl->shortcuts_model, &iter,
2078                       SHORTCUTS_COL_PIXBUF, NULL,
2079                       SHORTCUTS_COL_PIXBUF_VISIBLE, FALSE,
2080                       SHORTCUTS_COL_NAME, NULL,
2081                       SHORTCUTS_COL_DATA, NULL,
2082                       SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_SEPARATOR,
2083                       -1);
2084 }
2085
2086 /* Updates the list of bookmarks */
2087 static void
2088 shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
2089 {
2090   GSList *bookmarks;
2091   gboolean old_changing_folders;
2092   GtkTreeIter iter;
2093   GFile *list_selected = NULL;
2094   GFile *combo_selected = NULL;
2095   ShortcutType shortcut_type;
2096   gpointer col_data;
2097
2098   profile_start ("start", NULL);
2099         
2100   old_changing_folders = impl->changing_folder;
2101   impl->changing_folder = TRUE;
2102
2103   if (shortcuts_get_selected (impl, &iter))
2104     {
2105       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), 
2106                           &iter, 
2107                           SHORTCUTS_COL_DATA, &col_data,
2108                           SHORTCUTS_COL_TYPE, &shortcut_type,
2109                           -1);
2110
2111       if (col_data && shortcut_type == SHORTCUT_TYPE_FILE)
2112         list_selected = g_object_ref (col_data);
2113     }
2114
2115   if (impl->save_folder_combo &&
2116       gtk_combo_box_get_active_iter (GTK_COMBO_BOX (impl->save_folder_combo), 
2117                                      &iter))
2118     {
2119       GtkTreeIter child_iter;
2120
2121       gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER  (impl->shortcuts_combo_filter_model),
2122                                                         &child_iter,
2123                                                         &iter);
2124       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), 
2125                           &child_iter, 
2126                           SHORTCUTS_COL_DATA, &col_data,
2127                           SHORTCUTS_COL_TYPE, &shortcut_type,
2128                           -1);
2129       
2130       if (col_data && shortcut_type == SHORTCUT_TYPE_FILE)
2131         combo_selected = g_object_ref (col_data);
2132     }
2133
2134   if (impl->num_bookmarks > 0)
2135     shortcuts_remove_rows (impl,
2136                            shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR),
2137                            impl->num_bookmarks + 1);
2138
2139   impl->num_bookmarks = 0;
2140   shortcuts_insert_separator (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
2141
2142   bookmarks = _gtk_file_system_list_bookmarks (impl->file_system);
2143   shortcuts_append_bookmarks (impl, bookmarks);
2144   g_slist_foreach (bookmarks, (GFunc) g_object_unref, NULL);
2145   g_slist_free (bookmarks);
2146
2147   if (impl->num_bookmarks == 0)
2148     shortcuts_remove_rows (impl, shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR), 1);
2149
2150   if (impl->shortcuts_pane_filter_model)
2151     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model));
2152
2153   if (impl->shortcuts_combo_filter_model)
2154     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model));
2155
2156   if (list_selected)
2157     {
2158       shortcuts_find_folder (impl, list_selected);
2159       g_object_unref (list_selected);
2160     }
2161
2162   if (combo_selected)
2163     {
2164       gint pos;
2165
2166       pos = shortcut_find_position (impl, combo_selected);
2167       if (pos != -1)
2168         {
2169           if (impl->has_search)
2170             pos -= 1;
2171
2172           if (impl->has_recent)
2173             pos -= 2;
2174
2175           gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), pos);
2176         }
2177
2178       g_object_unref (combo_selected);
2179     }
2180
2181   impl->changing_folder = old_changing_folders;
2182
2183   profile_end ("end", NULL);
2184 }
2185
2186 /* Appends a separator and a row to the shortcuts list for the current folder */
2187 static void
2188 shortcuts_add_current_folder (GtkFileChooserDefault *impl)
2189 {
2190   int pos;
2191
2192   g_assert (!impl->shortcuts_current_folder_active);
2193
2194   g_assert (impl->current_folder != NULL);
2195
2196   pos = shortcut_find_position (impl, impl->current_folder);
2197   if (pos == -1)
2198     {
2199       GtkFileSystemVolume *volume;
2200       GFile *base_file;
2201
2202       /* Separator */
2203       shortcuts_insert_separator (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2204
2205       /* Item */
2206       pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER);
2207
2208       volume = _gtk_file_system_get_volume_for_file (impl->file_system, impl->current_folder);
2209       if (volume)
2210         base_file = _gtk_file_system_volume_get_root (volume);
2211       else
2212         base_file = NULL;
2213
2214       if (base_file && g_file_equal (base_file, impl->current_folder))
2215         shortcuts_insert_file (impl, pos, SHORTCUT_TYPE_VOLUME, volume, NULL, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER);
2216       else
2217         shortcuts_insert_file (impl, pos, SHORTCUT_TYPE_FILE, NULL, impl->current_folder, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER);
2218
2219       if (base_file)
2220         g_object_unref (base_file);
2221     }
2222   else if (impl->save_folder_combo != NULL)
2223     {
2224       if (impl->has_search)
2225         pos -= 1;
2226
2227       if (impl->has_recent)
2228         pos -= 2; /* + separator */
2229
2230       gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), pos);
2231     }
2232 }
2233
2234 /* Updates the current folder row in the shortcuts model */
2235 static void
2236 shortcuts_update_current_folder (GtkFileChooserDefault *impl)
2237 {
2238   int pos;
2239
2240   pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2241
2242   if (impl->shortcuts_current_folder_active)
2243     {
2244       shortcuts_remove_rows (impl, pos, 2);
2245       impl->shortcuts_current_folder_active = FALSE;
2246     }
2247
2248   shortcuts_add_current_folder (impl);
2249 }
2250
2251 /* Filter function used for the shortcuts filter model */
2252 static gboolean
2253 shortcuts_pane_filter_cb (GtkTreeModel *model,
2254                           GtkTreeIter  *iter,
2255                           gpointer      data)
2256 {
2257   GtkFileChooserDefault *impl;
2258   GtkTreePath *path;
2259   int pos;
2260
2261   impl = GTK_FILE_CHOOSER_DEFAULT (data);
2262
2263   path = gtk_tree_model_get_path (model, iter);
2264   if (!path)
2265     return FALSE;
2266
2267   pos = *gtk_tree_path_get_indices (path);
2268   gtk_tree_path_free (path);
2269
2270   return (pos < shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR));
2271 }
2272
2273 /* Creates the list model for shortcuts */
2274 static void
2275 shortcuts_model_create (GtkFileChooserDefault *impl)
2276 {
2277   /* Keep this order in sync with the SHORCUTS_COL_* enum values */
2278   impl->shortcuts_model = gtk_list_store_new (SHORTCUTS_COL_NUM_COLUMNS,
2279                                               GDK_TYPE_PIXBUF,  /* pixbuf */
2280                                               G_TYPE_STRING,    /* name */
2281                                               G_TYPE_POINTER,   /* path or volume */
2282                                               G_TYPE_INT,       /* ShortcutType */
2283                                               G_TYPE_BOOLEAN,   /* removable */
2284                                               G_TYPE_BOOLEAN,   /* pixbuf cell visibility */
2285                                               G_TYPE_POINTER);  /* GCancellable */
2286
2287   shortcuts_append_search (impl);
2288
2289   if (impl->recent_manager)
2290     {
2291       shortcuts_append_recent (impl);
2292       shortcuts_insert_separator (impl, SHORTCUTS_RECENT_SEPARATOR);
2293     }
2294   
2295   if (impl->file_system)
2296     {
2297       shortcuts_append_home (impl);
2298       shortcuts_append_desktop (impl);
2299       shortcuts_add_volumes (impl);
2300     }
2301
2302   impl->shortcuts_pane_filter_model = shortcuts_pane_model_filter_new (impl,
2303                                                                        GTK_TREE_MODEL (impl->shortcuts_model),
2304                                                                        NULL);
2305
2306   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
2307                                           shortcuts_pane_filter_cb,
2308                                           impl,
2309                                           NULL);
2310 }
2311
2312 /* Callback used when the "New Folder" button is clicked */
2313 static void
2314 new_folder_button_clicked (GtkButton             *button,
2315                            GtkFileChooserDefault *impl)
2316 {
2317   GtkTreeIter iter;
2318   GtkTreePath *path;
2319
2320   if (!impl->browse_files_model)
2321     return; /* FIXME: this sucks.  Disable the New Folder button or something. */
2322
2323   /* Prevent button from being clicked twice */
2324   gtk_widget_set_sensitive (impl->browse_new_folder_button, FALSE);
2325
2326   _gtk_file_system_model_add_editable (impl->browse_files_model, &iter);
2327
2328   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->browse_files_model), &iter);
2329   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_files_tree_view),
2330                                 path, impl->list_name_column,
2331                                 FALSE, 0.0, 0.0);
2332
2333   g_object_set (impl->list_name_renderer, "editable", TRUE, NULL);
2334   gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view),
2335                             path,
2336                             impl->list_name_column,
2337                             TRUE);
2338
2339   gtk_tree_path_free (path);
2340 }
2341
2342 static GSource *
2343 add_idle_while_impl_is_alive (GtkFileChooserDefault *impl, GCallback callback)
2344 {
2345   GSource *source;
2346
2347   source = g_idle_source_new ();
2348   g_source_set_closure (source,
2349                         g_cclosure_new_object (callback, G_OBJECT (impl)));
2350   g_source_attach (source, NULL);
2351
2352   return source;
2353 }
2354
2355 /* Idle handler for creating a new folder after editing its name cell, or for
2356  * canceling the editing.
2357  */
2358 static gboolean
2359 edited_idle_cb (GtkFileChooserDefault *impl)
2360 {
2361   GDK_THREADS_ENTER ();
2362   
2363   g_source_destroy (impl->edited_idle);
2364   impl->edited_idle = NULL;
2365
2366   _gtk_file_system_model_remove_editable (impl->browse_files_model);
2367   g_object_set (impl->list_name_renderer, "editable", FALSE, NULL);
2368
2369   gtk_widget_set_sensitive (impl->browse_new_folder_button, TRUE);
2370
2371   if (impl->edited_new_text /* not cancelled? */
2372       && (strlen (impl->edited_new_text) != 0)
2373       && (strcmp (impl->edited_new_text, DEFAULT_NEW_FOLDER_NAME) != 0)) /* Don't create folder if name is empty or has not been edited */
2374     {
2375       GError *error = NULL;
2376       GFile *file;
2377
2378       file = g_file_get_child_for_display_name (impl->current_folder,
2379                                                 impl->edited_new_text,
2380                                                 &error);
2381       if (file)
2382         {
2383           GError *error = NULL;
2384
2385           if (g_file_make_directory (file, NULL, &error))
2386             change_folder_and_display_error (impl, file, FALSE);
2387           else
2388             error_creating_folder_dialog (impl, file, error);
2389
2390           g_object_unref (file);
2391         }
2392       else
2393         error_creating_folder_dialog (impl, file, error);
2394
2395       g_free (impl->edited_new_text);
2396       impl->edited_new_text = NULL;
2397     }
2398
2399   GDK_THREADS_LEAVE ();
2400
2401   return FALSE;
2402 }
2403
2404 static void
2405 queue_edited_idle (GtkFileChooserDefault *impl,
2406                    const gchar           *new_text)
2407 {
2408   /* We create the folder in an idle handler so that we don't modify the tree
2409    * just now.
2410    */
2411
2412   if (!impl->edited_idle)
2413     impl->edited_idle = add_idle_while_impl_is_alive (impl, G_CALLBACK (edited_idle_cb));
2414
2415   g_free (impl->edited_new_text);
2416   impl->edited_new_text = g_strdup (new_text);
2417 }
2418
2419 /* Callback used from the text cell renderer when the new folder is named */
2420 static void
2421 renderer_edited_cb (GtkCellRendererText   *cell_renderer_text,
2422                     const gchar           *path,
2423                     const gchar           *new_text,
2424                     GtkFileChooserDefault *impl)
2425 {
2426   /* work around bug #154921 */
2427   g_object_set (cell_renderer_text, 
2428                 "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
2429   queue_edited_idle (impl, new_text);
2430 }
2431
2432 /* Callback used from the text cell renderer when the new folder edition gets
2433  * canceled.
2434  */
2435 static void
2436 renderer_editing_canceled_cb (GtkCellRendererText   *cell_renderer_text,
2437                               GtkFileChooserDefault *impl)
2438 {
2439   /* work around bug #154921 */
2440   g_object_set (cell_renderer_text, 
2441                 "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
2442   queue_edited_idle (impl, NULL);
2443 }
2444
2445 /* Creates the widgets for the filter combo box */
2446 static GtkWidget *
2447 filter_create (GtkFileChooserDefault *impl)
2448 {
2449   GtkCellRenderer *cell;
2450   GList           *cells;
2451
2452   impl->filter_combo = gtk_combo_box_text_new ();
2453   gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (impl->filter_combo), FALSE);
2454
2455   /* Get the combo's text renderer and set ellipsize parameters */
2456   cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (impl->filter_combo));
2457   g_assert (cells);
2458   cell = cells->data;
2459
2460   g_object_set (G_OBJECT (cell),
2461                 "ellipsize", PANGO_ELLIPSIZE_END,
2462                 NULL);
2463
2464   g_list_free (cells);
2465
2466   g_signal_connect (impl->filter_combo, "changed",
2467                     G_CALLBACK (filter_combo_changed), impl);
2468
2469   gtk_widget_set_tooltip_text (impl->filter_combo,
2470                                _("Select which types of files are shown"));
2471
2472   return impl->filter_combo;
2473 }
2474
2475 static GtkWidget *
2476 toolbutton_new (GtkFileChooserDefault *impl,
2477                 GIcon                 *icon,
2478                 gboolean               sensitive,
2479                 gboolean               show,
2480                 GCallback              callback)
2481 {
2482   GtkToolItem *item;
2483   GtkWidget *image;
2484
2485   item = gtk_tool_button_new (NULL, NULL);
2486   image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_SMALL_TOOLBAR);
2487   gtk_widget_show (image);
2488   gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (item), image);
2489
2490   gtk_widget_set_sensitive (GTK_WIDGET (item), sensitive);
2491   g_signal_connect (item, "clicked", callback, impl);
2492
2493   if (show)
2494     gtk_widget_show (GTK_WIDGET (item));
2495
2496   return GTK_WIDGET (item);
2497 }
2498
2499 /* Looks for a path among the shortcuts; returns its index or -1 if it doesn't exist */
2500 static int
2501 shortcut_find_position (GtkFileChooserDefault *impl,
2502                         GFile                 *file)
2503 {
2504   GtkTreeIter iter;
2505   int i;
2506   int current_folder_separator_idx;
2507
2508   if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
2509     return -1;
2510
2511   current_folder_separator_idx = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2512
2513 #if 0
2514   /* FIXME: is this still needed? */
2515   if (current_folder_separator_idx >= impl->shortcuts_model->length)
2516     return -1;
2517 #endif
2518
2519   for (i = 0; i < current_folder_separator_idx; i++)
2520     {
2521       gpointer col_data;
2522       ShortcutType shortcut_type;
2523
2524       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2525                           SHORTCUTS_COL_DATA, &col_data,
2526                           SHORTCUTS_COL_TYPE, &shortcut_type,
2527                           -1);
2528
2529       if (col_data)
2530         {
2531           if (shortcut_type == SHORTCUT_TYPE_VOLUME)
2532             {
2533               GtkFileSystemVolume *volume;
2534               GFile *base_file;
2535               gboolean exists;
2536
2537               volume = col_data;
2538               base_file = _gtk_file_system_volume_get_root (volume);
2539
2540               exists = base_file && g_file_equal (file, base_file);
2541
2542               if (base_file)
2543                 g_object_unref (base_file);
2544
2545               if (exists)
2546                 return i;
2547             }
2548           else if (shortcut_type == SHORTCUT_TYPE_FILE)
2549             {
2550               GFile *model_file;
2551
2552               model_file = col_data;
2553
2554               if (model_file && g_file_equal (model_file, file))
2555                 return i;
2556             }
2557         }
2558
2559       if (i < current_folder_separator_idx - 1)
2560         {
2561           if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
2562             g_assert_not_reached ();
2563         }
2564     }
2565
2566   return -1;
2567 }
2568
2569 /* Tries to add a bookmark from a path name */
2570 static gboolean
2571 shortcuts_add_bookmark_from_file (GtkFileChooserDefault *impl,
2572                                   GFile                 *file,
2573                                   int                    pos)
2574 {
2575   GError *error;
2576
2577   g_return_val_if_fail (G_IS_FILE (file), FALSE);
2578
2579   if (shortcut_find_position (impl, file) != -1)
2580     return FALSE;
2581
2582   error = NULL;
2583   if (!_gtk_file_system_insert_bookmark (impl->file_system, file, pos, &error))
2584     {
2585       error_adding_bookmark_dialog (impl, file, error);
2586       return FALSE;
2587     }
2588
2589   return TRUE;
2590 }
2591
2592 static void
2593 add_bookmark_foreach_cb (GtkTreeModel *model,
2594                          GtkTreePath  *path,
2595                          GtkTreeIter  *iter,
2596                          gpointer      data)
2597 {
2598   GtkFileChooserDefault *impl;
2599   GFile *file;
2600
2601   impl = (GtkFileChooserDefault *) data;
2602
2603   gtk_tree_model_get (model, iter,
2604                       MODEL_COL_FILE, &file,
2605                       -1);
2606
2607   shortcuts_add_bookmark_from_file (impl, file, -1);
2608
2609   g_object_unref (file);
2610 }
2611
2612 /* Adds a bookmark from the currently selected item in the file list */
2613 static void
2614 bookmarks_add_selected_folder (GtkFileChooserDefault *impl)
2615 {
2616   GtkTreeSelection *selection;
2617
2618   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2619
2620   if (gtk_tree_selection_count_selected_rows (selection) == 0)
2621     shortcuts_add_bookmark_from_file (impl, impl->current_folder, -1);
2622   else
2623     gtk_tree_selection_selected_foreach (selection,
2624                                          add_bookmark_foreach_cb,
2625                                          impl);
2626 }
2627
2628 /* Callback used when the "Add bookmark" button is clicked */
2629 static void
2630 add_bookmark_button_clicked_cb (GtkButton *button,
2631                                 GtkFileChooserDefault *impl)
2632 {
2633   bookmarks_add_selected_folder (impl);
2634 }
2635
2636 /* Returns TRUE plus an iter in the shortcuts_model if a row is selected;
2637  * returns FALSE if no shortcut is selected.
2638  */
2639 static gboolean
2640 shortcuts_get_selected (GtkFileChooserDefault *impl,
2641                         GtkTreeIter           *iter)
2642 {
2643   GtkTreeSelection *selection;
2644   GtkTreeIter parent_iter;
2645
2646   if (!impl->browse_shortcuts_tree_view)
2647     return FALSE;
2648
2649   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
2650
2651   if (!gtk_tree_selection_get_selected (selection, NULL, &parent_iter))
2652     return FALSE;
2653
2654   gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
2655                                                     iter,
2656                                                     &parent_iter);
2657   return TRUE;
2658 }
2659
2660 /* Removes the selected bookmarks */
2661 static void
2662 remove_selected_bookmarks (GtkFileChooserDefault *impl)
2663 {
2664   GtkTreeIter iter;
2665   gpointer col_data;
2666   GFile *file;
2667   gboolean removable;
2668   GError *error;
2669
2670   if (!shortcuts_get_selected (impl, &iter))
2671     return;
2672
2673   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2674                       SHORTCUTS_COL_DATA, &col_data,
2675                       SHORTCUTS_COL_REMOVABLE, &removable,
2676                       -1);
2677
2678   if (!removable)
2679     return;
2680
2681   g_assert (col_data != NULL);
2682
2683   file = col_data;
2684
2685   error = NULL;
2686   if (!_gtk_file_system_remove_bookmark (impl->file_system, file, &error))
2687     error_removing_bookmark_dialog (impl, file, error);
2688 }
2689
2690 /* Callback used when the "Remove bookmark" button is clicked */
2691 static void
2692 remove_bookmark_button_clicked_cb (GtkButton *button,
2693                                    GtkFileChooserDefault *impl)
2694 {
2695   remove_selected_bookmarks (impl);
2696 }
2697
2698 struct selection_check_closure {
2699   GtkFileChooserDefault *impl;
2700   int num_selected;
2701   gboolean all_files;
2702   gboolean all_folders;
2703 };
2704
2705 /* Used from gtk_tree_selection_selected_foreach() */
2706 static void
2707 selection_check_foreach_cb (GtkTreeModel *model,
2708                             GtkTreePath  *path,
2709                             GtkTreeIter  *iter,
2710                             gpointer      data)
2711 {
2712   struct selection_check_closure *closure;
2713   gboolean is_folder;
2714   GFile *file;
2715
2716   gtk_tree_model_get (model, iter,
2717                       MODEL_COL_FILE, &file,
2718                       MODEL_COL_IS_FOLDER, &is_folder,
2719                       -1);
2720
2721   if (file == NULL)
2722     return;
2723
2724   g_object_unref (file);
2725
2726   closure = data;
2727   closure->num_selected++;
2728
2729   closure->all_folders = closure->all_folders && is_folder;
2730   closure->all_files = closure->all_files && !is_folder;
2731 }
2732
2733 /* Checks whether the selected items in the file list are all files or all folders */
2734 static void
2735 selection_check (GtkFileChooserDefault *impl,
2736                  gint                  *num_selected,
2737                  gboolean              *all_files,
2738                  gboolean              *all_folders)
2739 {
2740   struct selection_check_closure closure;
2741   GtkTreeSelection *selection;
2742
2743   closure.impl = impl;
2744   closure.num_selected = 0;
2745   closure.all_files = TRUE;
2746   closure.all_folders = TRUE;
2747
2748   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2749   gtk_tree_selection_selected_foreach (selection,
2750                                        selection_check_foreach_cb,
2751                                        &closure);
2752
2753   g_assert (closure.num_selected == 0 || !(closure.all_files && closure.all_folders));
2754
2755   if (num_selected)
2756     *num_selected = closure.num_selected;
2757
2758   if (all_files)
2759     *all_files = closure.all_files;
2760
2761   if (all_folders)
2762     *all_folders = closure.all_folders;
2763 }
2764
2765 struct get_selected_file_closure {
2766   GtkFileChooserDefault *impl;
2767   GFile *file;
2768 };
2769
2770 static void
2771 get_selected_file_foreach_cb (GtkTreeModel *model,
2772                               GtkTreePath  *path,
2773                               GtkTreeIter  *iter,
2774                               gpointer      data)
2775 {
2776   struct get_selected_file_closure *closure = data;
2777
2778   if (closure->file)
2779     {
2780       /* Just in case this function gets run more than once with a multiple selection; we only care about one file */
2781       g_object_unref (closure->file);
2782       closure->file = NULL;
2783     }
2784
2785   gtk_tree_model_get (model, iter,
2786                       MODEL_COL_FILE, &closure->file, /* this will give us a reffed file */
2787                       -1);
2788 }
2789
2790 /* Returns a selected path from the file list */
2791 static GFile *
2792 get_selected_file (GtkFileChooserDefault *impl)
2793 {
2794   struct get_selected_file_closure closure;
2795   GtkTreeSelection *selection;
2796
2797   closure.impl = impl;
2798   closure.file = NULL;
2799
2800   selection =  gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2801   gtk_tree_selection_selected_foreach (selection,
2802                                        get_selected_file_foreach_cb,
2803                                        &closure);
2804
2805   return closure.file;
2806 }
2807
2808 typedef struct {
2809   GtkFileChooserDefault *impl;
2810   gchar *tip;
2811 } UpdateTooltipData;
2812
2813 static void 
2814 update_tooltip (GtkTreeModel      *model,
2815                 GtkTreePath       *path,
2816                 GtkTreeIter       *iter,
2817                 gpointer           data)
2818 {
2819   UpdateTooltipData *udata = data;
2820
2821   if (udata->tip == NULL)
2822     {
2823       gchar *display_name;
2824
2825       gtk_tree_model_get (model, iter,
2826                           MODEL_COL_NAME, &display_name,
2827                           -1);
2828
2829       udata->tip = g_strdup_printf (_("Add the folder '%s' to the bookmarks"),
2830                                     display_name);
2831       g_free (display_name);
2832     }
2833 }
2834
2835
2836 /* Sensitize the "add bookmark" button if all the selected items are folders, or
2837  * if there are no selected items *and* the current folder is not in the
2838  * bookmarks list.  De-sensitize the button otherwise.
2839  */
2840 static void
2841 bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl)
2842 {
2843   gint num_selected;
2844   gboolean all_folders;
2845   gboolean active;
2846   gchar *tip;
2847
2848   selection_check (impl, &num_selected, NULL, &all_folders);
2849
2850   if (num_selected == 0)
2851     active = (impl->current_folder != NULL) && (shortcut_find_position (impl, impl->current_folder) == -1);
2852   else if (num_selected == 1)
2853     {
2854       GFile *file;
2855
2856       file = get_selected_file (impl);
2857       active = file && all_folders && (shortcut_find_position (impl, file) == -1);
2858       if (file)
2859         g_object_unref (file);
2860     }
2861   else
2862     active = all_folders;
2863
2864   gtk_widget_set_sensitive (impl->browse_shortcuts_add_button, active);
2865
2866   if (impl->browse_files_popup_menu_add_shortcut_item)
2867     gtk_widget_set_sensitive (impl->browse_files_popup_menu_add_shortcut_item,
2868                               (num_selected == 0) ? FALSE : active);
2869
2870   if (active)
2871     {
2872       if (num_selected == 0)
2873         tip = g_strdup_printf (_("Add the current folder to the bookmarks"));
2874       else if (num_selected > 1)
2875         tip = g_strdup_printf (_("Add the selected folders to the bookmarks"));
2876       else
2877         {
2878           GtkTreeSelection *selection;
2879           UpdateTooltipData data;
2880
2881           selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2882           data.impl = impl;
2883           data.tip = NULL;
2884           gtk_tree_selection_selected_foreach (selection, update_tooltip, &data);
2885           tip = data.tip;
2886         }
2887
2888       gtk_widget_set_tooltip_text (impl->browse_shortcuts_add_button, tip);
2889       g_free (tip);
2890     }
2891 }
2892
2893 /* Sets the sensitivity of the "remove bookmark" button depending on whether a
2894  * bookmark row is selected in the shortcuts tree.
2895  */
2896 static void
2897 bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl)
2898 {
2899   GtkTreeIter iter;
2900   gboolean removable = FALSE;
2901   gchar *name = NULL;
2902   gchar *tip;
2903   
2904   if (shortcuts_get_selected (impl, &iter))
2905     {
2906       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2907                           SHORTCUTS_COL_REMOVABLE, &removable,
2908                           SHORTCUTS_COL_NAME, &name,
2909                           -1);
2910       gtk_widget_set_sensitive (impl->browse_shortcuts_remove_button, removable);
2911
2912       if (removable)
2913         tip = g_strdup_printf (_("Remove the bookmark '%s'"), name);
2914       else
2915         tip = g_strdup_printf (_("Bookmark '%s' cannot be removed"), name);
2916
2917       gtk_widget_set_tooltip_text (impl->browse_shortcuts_remove_button, tip);
2918       g_free (tip);
2919     }
2920   else
2921     gtk_widget_set_tooltip_text (impl->browse_shortcuts_remove_button,
2922                                  _("Remove the selected bookmark"));
2923   g_free (name);
2924 }
2925
2926 static void
2927 shortcuts_check_popup_sensitivity (GtkFileChooserDefault *impl)
2928 {
2929   GtkTreeIter iter;
2930   gboolean removable = FALSE;
2931
2932   if (impl->browse_shortcuts_popup_menu == NULL)
2933     return;
2934
2935   if (shortcuts_get_selected (impl, &iter))
2936     gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2937                         SHORTCUTS_COL_REMOVABLE, &removable,
2938                         -1);
2939
2940   gtk_widget_set_sensitive (impl->browse_shortcuts_popup_menu_remove_item, removable);
2941   gtk_widget_set_sensitive (impl->browse_shortcuts_popup_menu_rename_item, removable);
2942 }
2943
2944 /* GtkWidget::drag-begin handler for the shortcuts list. */
2945 static void
2946 shortcuts_drag_begin_cb (GtkWidget             *widget,
2947                          GdkDragContext        *context,
2948                          GtkFileChooserDefault *impl)
2949 {
2950 #if 0
2951   impl->shortcuts_drag_context = g_object_ref (context);
2952 #endif
2953 }
2954
2955 #if 0
2956 /* Removes the idle handler for outside drags */
2957 static void
2958 shortcuts_cancel_drag_outside_idle (GtkFileChooserDefault *impl)
2959 {
2960   if (!impl->shortcuts_drag_outside_idle)
2961     return;
2962
2963   g_source_destroy (impl->shortcuts_drag_outside_idle);
2964   impl->shortcuts_drag_outside_idle = NULL;
2965 }
2966 #endif
2967
2968 /* GtkWidget::drag-end handler for the shortcuts list. */
2969 static void
2970 shortcuts_drag_end_cb (GtkWidget             *widget,
2971                        GdkDragContext        *context,
2972                        GtkFileChooserDefault *impl)
2973 {
2974 #if 0
2975   g_object_unref (impl->shortcuts_drag_context);
2976
2977   shortcuts_cancel_drag_outside_idle (impl);
2978
2979   if (!impl->shortcuts_drag_outside)
2980     return;
2981
2982   gtk_button_clicked (GTK_BUTTON (impl->browse_shortcuts_remove_button));
2983
2984   impl->shortcuts_drag_outside = FALSE;
2985 #endif
2986 }
2987
2988 /* GtkWidget::drag-data-delete handler for the shortcuts list. */
2989 static void
2990 shortcuts_drag_data_delete_cb (GtkWidget             *widget,
2991                                GdkDragContext        *context,
2992                                GtkFileChooserDefault *impl)
2993 {
2994   g_signal_stop_emission_by_name (widget, "drag-data-delete");
2995 }
2996
2997 /* GtkWidget::drag-leave handler for the shortcuts list.  We unhighlight the
2998  * drop position.
2999  */
3000 static void
3001 shortcuts_drag_leave_cb (GtkWidget             *widget,
3002                          GdkDragContext        *context,
3003                          guint                  time_,
3004                          GtkFileChooserDefault *impl)
3005 {
3006 #if 0
3007   if (gtk_drag_get_source_widget (context) == widget && !impl->shortcuts_drag_outside_idle)
3008     {
3009       impl->shortcuts_drag_outside_idle = add_idle_while_impl_is_alive (impl, G_CALLBACK (shortcuts_drag_outside_idle_cb));
3010     }
3011 #endif
3012
3013   gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3014                                    NULL,
3015                                    GTK_TREE_VIEW_DROP_BEFORE);
3016
3017   g_signal_stop_emission_by_name (widget, "drag-leave");
3018 }
3019
3020 /* Computes the appropriate row and position for dropping */
3021 static void
3022 shortcuts_compute_drop_position (GtkFileChooserDefault   *impl,
3023                                  int                      x,
3024                                  int                      y,
3025                                  GtkTreePath            **path,
3026                                  GtkTreeViewDropPosition *pos)
3027 {
3028   GtkTreeView *tree_view;
3029   GtkTreeViewColumn *column;
3030   int cell_y;
3031   GdkRectangle cell;
3032   int row;
3033   int bookmarks_index;
3034   int header_height = 0;
3035
3036   tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
3037
3038   if (gtk_tree_view_get_headers_visible (tree_view))
3039     header_height = _gtk_tree_view_get_header_height (tree_view);
3040
3041   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
3042
3043   if (!gtk_tree_view_get_path_at_pos (tree_view,
3044                                       x,
3045                                       y - header_height,
3046                                       path,
3047                                       &column,
3048                                       NULL,
3049                                       &cell_y))
3050     {
3051       row = bookmarks_index + impl->num_bookmarks - 1;
3052       *path = gtk_tree_path_new_from_indices (row, -1);
3053       *pos = GTK_TREE_VIEW_DROP_AFTER;
3054       return;
3055     }
3056
3057   row = *gtk_tree_path_get_indices (*path);
3058   gtk_tree_view_get_background_area (tree_view, *path, column, &cell);
3059   gtk_tree_path_free (*path);
3060
3061   if (row < bookmarks_index)
3062     {
3063       row = bookmarks_index;
3064       *pos = GTK_TREE_VIEW_DROP_BEFORE;
3065     }
3066   else if (row > bookmarks_index + impl->num_bookmarks - 1)
3067     {
3068       row = bookmarks_index + impl->num_bookmarks - 1;
3069       *pos = GTK_TREE_VIEW_DROP_AFTER;
3070     }
3071   else
3072     {
3073       if (cell_y < cell.height / 2)
3074         *pos = GTK_TREE_VIEW_DROP_BEFORE;
3075       else
3076         *pos = GTK_TREE_VIEW_DROP_AFTER;
3077     }
3078
3079   *path = gtk_tree_path_new_from_indices (row, -1);
3080 }
3081
3082 /* GtkWidget::drag-motion handler for the shortcuts list.  We basically
3083  * implement the destination side of DnD by hand, due to limitations in
3084  * GtkTreeView's DnD API.
3085  */
3086 static gboolean
3087 shortcuts_drag_motion_cb (GtkWidget             *widget,
3088                           GdkDragContext        *context,
3089                           gint                   x,
3090                           gint                   y,
3091                           guint                  time_,
3092                           GtkFileChooserDefault *impl)
3093 {
3094   GtkTreePath *path;
3095   GtkTreeViewDropPosition pos;
3096   GdkDragAction action;
3097
3098 #if 0
3099   if (gtk_drag_get_source_widget (context) == widget)
3100     {
3101       shortcuts_cancel_drag_outside_idle (impl);
3102
3103       if (impl->shortcuts_drag_outside)
3104         {
3105           shortcuts_drag_set_delete_cursor (impl, FALSE);
3106           impl->shortcuts_drag_outside = FALSE;
3107         }
3108     }
3109 #endif
3110
3111   if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_COPY ||
3112       (gdk_drag_context_get_actions (context) & GDK_ACTION_COPY) != 0)
3113     action = GDK_ACTION_COPY;
3114   else if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_MOVE ||
3115            (gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
3116     action = GDK_ACTION_MOVE;
3117   else
3118     {
3119       action = 0;
3120       goto out;
3121     }
3122
3123   shortcuts_compute_drop_position (impl, x, y, &path, &pos);
3124   gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), path, pos);
3125   gtk_tree_path_free (path);
3126
3127  out:
3128
3129   g_signal_stop_emission_by_name (widget, "drag-motion");
3130
3131   if (action != 0)
3132     {
3133       gdk_drag_status (context, action, time_);
3134       return TRUE;
3135     }
3136   else
3137     return FALSE;
3138 }
3139
3140 /* GtkWidget::drag-drop handler for the shortcuts list. */
3141 static gboolean
3142 shortcuts_drag_drop_cb (GtkWidget             *widget,
3143                         GdkDragContext        *context,
3144                         gint                   x,
3145                         gint                   y,
3146                         guint                  time_,
3147                         GtkFileChooserDefault *impl)
3148 {
3149 #if 0
3150   shortcuts_cancel_drag_outside_idle (impl);
3151 #endif
3152
3153   g_signal_stop_emission_by_name (widget, "drag-drop");
3154   return TRUE;
3155 }
3156
3157 /* Parses a "text/uri-list" string and inserts its URIs as bookmarks */
3158 static void
3159 shortcuts_drop_uris (GtkFileChooserDefault *impl,
3160                      GtkSelectionData      *selection_data,
3161                      int                    position)
3162 {
3163   gchar **uris;
3164   gint i;
3165
3166   uris = gtk_selection_data_get_uris (selection_data);
3167   if (!uris)
3168     return;
3169
3170   for (i = 0; uris[i]; i++)
3171     {
3172       char *uri;
3173       GFile *file;
3174
3175       uri = uris[i];
3176       file = g_file_new_for_uri (uri);
3177
3178       if (shortcuts_add_bookmark_from_file (impl, file, position))
3179         position++;
3180
3181       g_object_unref (file);
3182     }
3183
3184   g_strfreev (uris);
3185 }
3186
3187 /* Reorders the selected bookmark to the specified position */
3188 static void
3189 shortcuts_reorder (GtkFileChooserDefault *impl,
3190                    int                    new_position)
3191 {
3192   GtkTreeIter iter;
3193   gpointer col_data;
3194   ShortcutType shortcut_type;
3195   GtkTreePath *path;
3196   int old_position;
3197   int bookmarks_index;
3198   GFile *file;
3199   GError *error;
3200   gchar *name = NULL;
3201
3202   /* Get the selected path */
3203
3204   if (!shortcuts_get_selected (impl, &iter))
3205     g_assert_not_reached ();
3206
3207   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
3208   old_position = *gtk_tree_path_get_indices (path);
3209   gtk_tree_path_free (path);
3210
3211   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
3212   old_position -= bookmarks_index;
3213   g_assert (old_position >= 0 && old_position < impl->num_bookmarks);
3214
3215   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
3216                       SHORTCUTS_COL_NAME, &name,
3217                       SHORTCUTS_COL_DATA, &col_data,
3218                       SHORTCUTS_COL_TYPE, &shortcut_type,
3219                       -1);
3220   g_assert (col_data != NULL);
3221   g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
3222   
3223   file = col_data;
3224   g_object_ref (file); /* removal below will free file, so we need a new ref */
3225
3226   /* Remove the path from the old position and insert it in the new one */
3227
3228   if (new_position > old_position)
3229     new_position--;
3230
3231   if (old_position == new_position)
3232     goto out;
3233
3234   error = NULL;
3235   if (_gtk_file_system_remove_bookmark (impl->file_system, file, &error))
3236     {
3237       shortcuts_add_bookmark_from_file (impl, file, new_position);
3238       _gtk_file_system_set_bookmark_label (impl->file_system, file, name);
3239     }
3240   else
3241     error_adding_bookmark_dialog (impl, file, error);
3242
3243  out:
3244
3245   g_object_unref (file);
3246   g_free (name);
3247 }
3248
3249 /* Callback used when we get the drag data for the bookmarks list.  We add the
3250  * received URIs as bookmarks if they are folders.
3251  */
3252 static void
3253 shortcuts_drag_data_received_cb (GtkWidget          *widget,
3254                                  GdkDragContext     *context,
3255                                  gint                x,
3256                                  gint                y,
3257                                  GtkSelectionData   *selection_data,
3258                                  guint               info,
3259                                  guint               time_,
3260                                  gpointer            data)
3261 {
3262   GtkFileChooserDefault *impl;
3263   GtkTreePath *tree_path;
3264   GtkTreeViewDropPosition tree_pos;
3265   GdkAtom target;
3266   int position;
3267   int bookmarks_index;
3268
3269   impl = GTK_FILE_CHOOSER_DEFAULT (data);
3270
3271   /* Compute position */
3272
3273   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
3274
3275   shortcuts_compute_drop_position (impl, x, y, &tree_path, &tree_pos);
3276   position = *gtk_tree_path_get_indices (tree_path);
3277   gtk_tree_path_free (tree_path);
3278
3279   if (tree_pos == GTK_TREE_VIEW_DROP_AFTER)
3280     position++;
3281
3282   g_assert (position >= bookmarks_index);
3283   position -= bookmarks_index;
3284
3285   target = gtk_selection_data_get_target (selection_data);
3286
3287   if (gtk_targets_include_uri (&target, 1))
3288     shortcuts_drop_uris (impl, selection_data, position);
3289   else if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
3290     shortcuts_reorder (impl, position);
3291
3292   g_signal_stop_emission_by_name (widget, "drag-data-received");
3293 }
3294
3295 /* Callback used to display a tooltip in the shortcuts tree */
3296 static gboolean
3297 shortcuts_query_tooltip_cb (GtkWidget             *widget,
3298                             gint                   x,
3299                             gint                   y,
3300                             gboolean               keyboard_mode,
3301                             GtkTooltip            *tooltip,
3302                             GtkFileChooserDefault *impl)
3303 {
3304   GtkTreeModel *model;
3305   GtkTreeIter iter;
3306
3307   if (gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget),
3308                                          &x, &y,
3309                                          keyboard_mode,
3310                                          &model,
3311                                          NULL,
3312                                          &iter))
3313     {
3314       gpointer col_data;
3315       ShortcutType shortcut_type;
3316
3317       gtk_tree_model_get (model, &iter,
3318                           SHORTCUTS_COL_DATA, &col_data,
3319                           SHORTCUTS_COL_TYPE, &shortcut_type,
3320                           -1);
3321
3322       if (shortcut_type == SHORTCUT_TYPE_SEPARATOR)
3323         return FALSE;
3324       else if (shortcut_type == SHORTCUT_TYPE_VOLUME)
3325         return FALSE;
3326       else if (shortcut_type == SHORTCUT_TYPE_FILE)
3327         {
3328           GFile *file;
3329           char *parse_name;
3330
3331           file = G_FILE (col_data);
3332           parse_name = g_file_get_parse_name (file);
3333
3334           gtk_tooltip_set_text (tooltip, parse_name);
3335
3336           g_free (parse_name);
3337
3338           return TRUE;
3339         }
3340       else if (shortcut_type == SHORTCUT_TYPE_SEARCH)
3341         {
3342           return FALSE;
3343         }
3344       else if (shortcut_type == SHORTCUT_TYPE_RECENT)
3345         {
3346           return FALSE;
3347         }
3348     }
3349
3350   return FALSE;
3351 }
3352
3353
3354 /* Callback used when the selection in the shortcuts tree changes */
3355 static void
3356 shortcuts_selection_changed_cb (GtkTreeSelection      *selection,
3357                                 GtkFileChooserDefault *impl)
3358 {
3359   GtkTreeIter iter;
3360   GtkTreeIter child_iter;
3361
3362   bookmarks_check_remove_sensitivity (impl);
3363   shortcuts_check_popup_sensitivity (impl);
3364
3365   if (impl->changing_folder)
3366     return;
3367
3368   if (gtk_tree_selection_get_selected(selection, NULL, &iter))
3369     {
3370       gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
3371                                                         &child_iter,
3372                                                         &iter);
3373       shortcuts_activate_iter (impl, &child_iter);
3374     }
3375 }
3376
3377 static gboolean
3378 shortcuts_row_separator_func (GtkTreeModel *model,
3379                               GtkTreeIter  *iter,
3380                               gpointer      data)
3381 {
3382   ShortcutType shortcut_type;
3383
3384   gtk_tree_model_get (model, iter, SHORTCUTS_COL_TYPE, &shortcut_type, -1);
3385   
3386   return shortcut_type == SHORTCUT_TYPE_SEPARATOR;
3387 }
3388
3389 static gboolean
3390 shortcuts_key_press_event_after_cb (GtkWidget             *tree_view,
3391                                     GdkEventKey           *event,
3392                                     GtkFileChooserDefault *impl)
3393 {
3394   GtkWidget *entry;
3395
3396   /* don't screw up focus switching with Tab */
3397   if (event->keyval == GDK_KEY_Tab
3398       || event->keyval == GDK_KEY_KP_Tab
3399       || event->keyval == GDK_KEY_ISO_Left_Tab
3400       || event->length < 1)
3401     return FALSE;
3402
3403   if (impl->location_entry)
3404     entry = impl->location_entry;
3405   else if (impl->search_entry)
3406     entry = impl->search_entry;
3407   else
3408     entry = NULL;
3409
3410   if (entry)
3411     {
3412       gtk_widget_grab_focus (entry);
3413       return gtk_widget_event (entry, (GdkEvent *) event);
3414     }
3415   else
3416     return FALSE;
3417 }
3418
3419 /* Callback used when the file list's popup menu is detached */
3420 static void
3421 shortcuts_popup_menu_detach_cb (GtkWidget *attach_widget,
3422                                 GtkMenu   *menu)
3423 {
3424   GtkFileChooserDefault *impl;
3425   
3426   impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
3427   g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
3428
3429   impl->browse_shortcuts_popup_menu = NULL;
3430   impl->browse_shortcuts_popup_menu_remove_item = NULL;
3431   impl->browse_shortcuts_popup_menu_rename_item = NULL;
3432 }
3433
3434 static void
3435 remove_shortcut_cb (GtkMenuItem           *item,
3436                     GtkFileChooserDefault *impl)
3437 {
3438   remove_selected_bookmarks (impl);
3439 }
3440
3441 /* Rename the selected bookmark */
3442 static void
3443 rename_selected_bookmark (GtkFileChooserDefault *impl)
3444 {
3445   GtkTreeIter iter;
3446   GtkTreePath *path;
3447   GtkTreeViewColumn *column;
3448   GtkCellRenderer *cell;
3449   GList *renderers;
3450
3451   if (shortcuts_get_selected (impl, &iter))
3452     {
3453       path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
3454       column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), 0);
3455       renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
3456       cell = g_list_nth_data (renderers, 1);
3457       g_list_free (renderers);
3458       g_object_set (cell, "editable", TRUE, NULL);
3459       gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3460                                         path, column, cell, TRUE);
3461       gtk_tree_path_free (path);
3462     }
3463 }
3464
3465 static void
3466 rename_shortcut_cb (GtkMenuItem           *item,
3467                     GtkFileChooserDefault *impl)
3468 {
3469   rename_selected_bookmark (impl);
3470 }
3471
3472 /* Constructs the popup menu for the file list if needed */
3473 static void
3474 shortcuts_build_popup_menu (GtkFileChooserDefault *impl)
3475 {
3476   GtkWidget *item;
3477
3478   if (impl->browse_shortcuts_popup_menu)
3479     return;
3480
3481   impl->browse_shortcuts_popup_menu = gtk_menu_new ();
3482   gtk_menu_attach_to_widget (GTK_MENU (impl->browse_shortcuts_popup_menu),
3483                              impl->browse_shortcuts_tree_view,
3484                              shortcuts_popup_menu_detach_cb);
3485
3486   item = gtk_image_menu_item_new_with_label (_("Remove"));
3487   impl->browse_shortcuts_popup_menu_remove_item = item;
3488   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
3489                                  gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU));
3490   g_signal_connect (item, "activate",
3491                     G_CALLBACK (remove_shortcut_cb), impl);
3492   gtk_widget_show (item);
3493   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu), item);
3494
3495   item = gtk_menu_item_new_with_label (_("Rename..."));
3496   impl->browse_shortcuts_popup_menu_rename_item = item;
3497   g_signal_connect (item, "activate",
3498                     G_CALLBACK (rename_shortcut_cb), impl);
3499   gtk_widget_show (item);
3500   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu), item);
3501 }
3502
3503 static void
3504 shortcuts_update_popup_menu (GtkFileChooserDefault *impl)
3505 {
3506   shortcuts_build_popup_menu (impl);  
3507   shortcuts_check_popup_sensitivity (impl);
3508 }
3509
3510 static void
3511 popup_position_func (GtkMenu   *menu,
3512                      gint      *x,
3513                      gint      *y,
3514                      gboolean  *push_in,
3515                      gpointer   user_data);
3516
3517 static void
3518 shortcuts_popup_menu (GtkFileChooserDefault *impl,
3519                       GdkEventButton        *event)
3520 {
3521   shortcuts_update_popup_menu (impl);
3522   if (event)
3523     gtk_menu_popup (GTK_MENU (impl->browse_shortcuts_popup_menu),
3524                     NULL, NULL, NULL, NULL,
3525                     event->button, event->time);
3526   else
3527     {
3528       gtk_menu_popup (GTK_MENU (impl->browse_shortcuts_popup_menu),
3529                       NULL, NULL,
3530                       popup_position_func, impl->browse_shortcuts_tree_view,
3531                       0, GDK_CURRENT_TIME);
3532       gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu),
3533                                    FALSE);
3534     }
3535 }
3536
3537 /* Callback used for the GtkWidget::popup-menu signal of the shortcuts list */
3538 static gboolean
3539 shortcuts_popup_menu_cb (GtkWidget *widget,
3540                          GtkFileChooserDefault *impl)
3541 {
3542   shortcuts_popup_menu (impl, NULL);
3543   return TRUE;
3544 }
3545
3546 /* Callback used when a button is pressed on the shortcuts list.  
3547  * We trap button 3 to bring up a popup menu.
3548  */
3549 static gboolean
3550 shortcuts_button_press_event_cb (GtkWidget             *widget,
3551                                  GdkEventButton        *event,
3552                                  GtkFileChooserDefault *impl)
3553 {
3554   static gboolean in_press = FALSE;
3555   gboolean handled;
3556
3557   if (in_press)
3558     return FALSE;
3559
3560   if (event->button != 3)
3561     return FALSE;
3562
3563   in_press = TRUE;
3564   handled = gtk_widget_event (impl->browse_shortcuts_tree_view, (GdkEvent *) event);
3565   in_press = FALSE;
3566
3567   if (!handled)
3568     return FALSE;
3569
3570   shortcuts_popup_menu (impl, event);
3571   return TRUE;
3572 }
3573
3574 static void
3575 shortcuts_edited (GtkCellRenderer       *cell,
3576                   gchar                 *path_string,
3577                   gchar                 *new_text,
3578                   GtkFileChooserDefault *impl)
3579 {
3580   GtkTreePath *path;
3581   GtkTreeIter iter;
3582   GFile *shortcut;
3583
3584   g_object_set (cell, "editable", FALSE, NULL);
3585
3586   path = gtk_tree_path_new_from_string (path_string);
3587   if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
3588     g_assert_not_reached ();
3589
3590   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
3591                       SHORTCUTS_COL_DATA, &shortcut,
3592                       -1);
3593   gtk_tree_path_free (path);
3594   
3595   _gtk_file_system_set_bookmark_label (impl->file_system, shortcut, new_text);
3596 }
3597
3598 static void
3599 shortcuts_editing_canceled (GtkCellRenderer       *cell,
3600                             GtkFileChooserDefault *impl)
3601 {
3602   g_object_set (cell, "editable", FALSE, NULL);
3603 }
3604
3605 /* Creates the widgets for the shortcuts and bookmarks tree */
3606 static GtkWidget *
3607 shortcuts_list_create (GtkFileChooserDefault *impl)
3608 {
3609   GtkWidget *swin;
3610   GtkTreeSelection *selection;
3611   GtkTreeViewColumn *column;
3612   GtkCellRenderer *renderer;
3613
3614   /* Target types for dragging a row to/from the shortcuts list */
3615   const GtkTargetEntry tree_model_row_targets[] = {
3616     { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }
3617   };
3618
3619   /* Scrolled window */
3620
3621   swin = gtk_scrolled_window_new (NULL, NULL);
3622   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
3623                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
3624   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
3625                                        GTK_SHADOW_IN);
3626   gtk_widget_show (swin);
3627
3628   /* Tree */
3629   impl->browse_shortcuts_tree_view = gtk_tree_view_new ();
3630   gtk_style_context_add_class (gtk_widget_get_style_context (impl->browse_shortcuts_tree_view),
3631                                GTK_STYLE_CLASS_SIDEBAR);
3632   gtk_tree_view_set_enable_search (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), FALSE);
3633 #ifdef PROFILE_FILE_CHOOSER
3634   g_object_set_data (G_OBJECT (impl->browse_shortcuts_tree_view), "fmq-name", "shortcuts");
3635 #endif
3636
3637   /* Connect "after" to key-press-event on the shortcuts pane.  We want this action to be possible:
3638    *
3639    *   1. user brings up a SAVE dialog
3640    *   2. user clicks on a shortcut in the shortcuts pane
3641    *   3. user starts typing a filename
3642    *
3643    * Normally, the user's typing would be ignored, as the shortcuts treeview doesn't
3644    * support interactive search.  However, we'd rather focus the location entry
3645    * so that the user can type *there*.
3646    *
3647    * To preserve keyboard navigation in the shortcuts pane, we don't focus the
3648    * filename entry if one clicks on a shortcut; rather, we focus the entry only
3649    * if the user starts typing while the focus is in the shortcuts pane.
3650    */
3651   g_signal_connect_after (impl->browse_shortcuts_tree_view, "key-press-event",
3652                           G_CALLBACK (shortcuts_key_press_event_after_cb), impl);
3653
3654   g_signal_connect (impl->browse_shortcuts_tree_view, "popup-menu",
3655                     G_CALLBACK (shortcuts_popup_menu_cb), impl);
3656   g_signal_connect (impl->browse_shortcuts_tree_view, "button-press-event",
3657                     G_CALLBACK (shortcuts_button_press_event_cb), impl);
3658   /* Accessible object name for the file chooser's shortcuts pane */
3659   atk_object_set_name (gtk_widget_get_accessible (impl->browse_shortcuts_tree_view), _("Places"));
3660
3661   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), impl->shortcuts_pane_filter_model);
3662
3663   gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3664                                           GDK_BUTTON1_MASK,
3665                                           tree_model_row_targets,
3666                                           G_N_ELEMENTS (tree_model_row_targets),
3667                                           GDK_ACTION_MOVE);
3668
3669   gtk_drag_dest_set (impl->browse_shortcuts_tree_view,
3670                      GTK_DEST_DEFAULT_ALL,
3671                      tree_model_row_targets,
3672                      G_N_ELEMENTS (tree_model_row_targets),
3673                      GDK_ACTION_COPY | GDK_ACTION_MOVE);
3674   gtk_drag_dest_add_uri_targets (impl->browse_shortcuts_tree_view);
3675
3676   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
3677   gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
3678   gtk_tree_selection_set_select_function (selection,
3679                                           shortcuts_select_func,
3680                                           impl, NULL);
3681
3682   g_signal_connect (selection, "changed",
3683                     G_CALLBACK (shortcuts_selection_changed_cb), impl);
3684
3685   g_signal_connect (impl->browse_shortcuts_tree_view, "key-press-event",
3686                     G_CALLBACK (shortcuts_key_press_event_cb), impl);
3687
3688   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-begin",
3689                     G_CALLBACK (shortcuts_drag_begin_cb), impl);
3690   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-end",
3691                     G_CALLBACK (shortcuts_drag_end_cb), impl);
3692   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-delete",
3693                     G_CALLBACK (shortcuts_drag_data_delete_cb), impl);
3694
3695   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-leave",
3696                     G_CALLBACK (shortcuts_drag_leave_cb), impl);
3697   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-motion",
3698                     G_CALLBACK (shortcuts_drag_motion_cb), impl);
3699   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-drop",
3700                     G_CALLBACK (shortcuts_drag_drop_cb), impl);
3701   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-received",
3702                     G_CALLBACK (shortcuts_drag_data_received_cb), impl);
3703
3704   /* Support tooltips */
3705   gtk_widget_set_has_tooltip (impl->browse_shortcuts_tree_view, TRUE);
3706   g_signal_connect (impl->browse_shortcuts_tree_view, "query-tooltip",
3707                     G_CALLBACK (shortcuts_query_tooltip_cb), impl);
3708
3709   gtk_container_add (GTK_CONTAINER (swin), impl->browse_shortcuts_tree_view);
3710   gtk_widget_show (impl->browse_shortcuts_tree_view);
3711
3712   /* Column */
3713
3714   column = gtk_tree_view_column_new ();
3715   /* Column header for the file chooser's shortcuts pane */
3716   gtk_tree_view_column_set_title (column, _("_Places"));
3717
3718   renderer = gtk_cell_renderer_pixbuf_new ();
3719   gtk_tree_view_column_pack_start (column, renderer, FALSE);
3720   gtk_tree_view_column_set_attributes (column, renderer,
3721                                        "pixbuf", SHORTCUTS_COL_PIXBUF,
3722                                        "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
3723                                        NULL);
3724
3725   renderer = gtk_cell_renderer_text_new ();
3726   g_object_set (renderer,
3727                 "width-chars", 12,
3728                 "ellipsize", PANGO_ELLIPSIZE_END,
3729                 NULL);
3730   g_signal_connect (renderer, "edited",
3731                     G_CALLBACK (shortcuts_edited), impl);
3732   g_signal_connect (renderer, "editing-canceled",
3733                     G_CALLBACK (shortcuts_editing_canceled), impl);
3734   gtk_tree_view_column_pack_start (column, renderer, TRUE);
3735   gtk_tree_view_column_set_attributes (column, renderer,
3736                                        "text", SHORTCUTS_COL_NAME,
3737                                        NULL);
3738
3739   gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3740                                         shortcuts_row_separator_func,
3741                                         NULL, NULL);
3742
3743   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), column);
3744
3745   return swin;
3746 }
3747
3748 /* Creates the widgets for the shortcuts/bookmarks pane */
3749 static GtkWidget *
3750 shortcuts_pane_create (GtkFileChooserDefault *impl,
3751                        GtkSizeGroup          *size_group)
3752 {
3753   GtkWidget *vbox;
3754   GtkWidget *toolbar;
3755   GtkWidget *widget;
3756   GtkStyleContext *context;
3757   GIcon *icon;
3758
3759   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3760   gtk_widget_show (vbox);
3761
3762   /* Shortcuts tree */
3763
3764   widget = shortcuts_list_create (impl);
3765
3766   gtk_size_group_add_widget (size_group, widget);
3767   context = gtk_widget_get_style_context (widget);
3768   gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
3769
3770   gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
3771
3772   /* Box for buttons */
3773
3774   toolbar = gtk_toolbar_new ();
3775   gtk_toolbar_set_icon_size (GTK_TOOLBAR (toolbar), GTK_ICON_SIZE_MENU);
3776
3777   context = gtk_widget_get_style_context (toolbar);
3778   gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);
3779   gtk_style_context_add_class (context, GTK_STYLE_CLASS_INLINE_TOOLBAR);
3780   
3781   gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0);
3782   gtk_widget_show (toolbar);
3783
3784   /* Add bookmark button */
3785   icon = g_themed_icon_new_with_default_fallbacks ("list-add-symbolic");
3786   impl->browse_shortcuts_add_button = toolbutton_new (impl,
3787                                                       icon,
3788                                                       FALSE,
3789                                                       TRUE,
3790                                                       G_CALLBACK (add_bookmark_button_clicked_cb));
3791   g_object_unref (icon);
3792
3793   gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (impl->browse_shortcuts_add_button), 0);
3794   gtk_widget_set_tooltip_text (impl->browse_shortcuts_add_button,
3795                                _("Add the selected folder to the Bookmarks"));
3796
3797   /* Remove bookmark button */
3798   icon = g_themed_icon_new_with_default_fallbacks ("list-remove-symbolic");
3799   impl->browse_shortcuts_remove_button = toolbutton_new (impl,
3800                                                          icon,
3801                                                          FALSE,
3802                                                          TRUE,
3803                                                          G_CALLBACK (remove_bookmark_button_clicked_cb));
3804   g_object_unref (icon);
3805   gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (impl->browse_shortcuts_remove_button), 1);
3806   gtk_widget_set_tooltip_text (impl->browse_shortcuts_remove_button,
3807                                _("Remove the selected bookmark"));
3808
3809   return vbox;
3810 }
3811
3812 static gboolean
3813 key_is_left_or_right (GdkEventKey *event)
3814 {
3815   guint modifiers;
3816
3817   modifiers = gtk_accelerator_get_default_mod_mask ();
3818
3819   return ((event->keyval == GDK_KEY_Right
3820            || event->keyval == GDK_KEY_KP_Right
3821            || event->keyval == GDK_KEY_Left
3822            || event->keyval == GDK_KEY_KP_Left)
3823           && (event->state & modifiers) == 0);
3824 }
3825
3826 /* Handles key press events on the file list, so that we can trap Enter to
3827  * activate the default button on our own.  Also, checks to see if '/' has been
3828  * pressed.  See comment by tree_view_keybinding_cb() for more details.
3829  */
3830 static gboolean
3831 browse_files_key_press_event_cb (GtkWidget   *widget,
3832                                  GdkEventKey *event,
3833                                  gpointer     data)
3834 {
3835   GtkFileChooserDefault *impl;
3836   int modifiers;
3837
3838   impl = (GtkFileChooserDefault *) data;
3839
3840   modifiers = gtk_accelerator_get_default_mod_mask ();
3841
3842   if ((event->keyval == GDK_KEY_slash
3843        || event->keyval == GDK_KEY_KP_Divide
3844 #ifdef G_OS_UNIX
3845        || event->keyval == GDK_KEY_asciitilde
3846 #endif
3847        ) && ! (event->state & (~GDK_SHIFT_MASK & modifiers)))
3848     {
3849       location_popup_handler (impl, event->string);
3850       return TRUE;
3851     }
3852
3853   if (key_is_left_or_right (event))
3854     {
3855       gtk_widget_grab_focus (impl->browse_shortcuts_tree_view);
3856       return TRUE;
3857     }
3858
3859   if ((event->keyval == GDK_KEY_Return
3860        || event->keyval == GDK_KEY_ISO_Enter
3861        || event->keyval == GDK_KEY_KP_Enter
3862        || event->keyval == GDK_KEY_space
3863        || event->keyval == GDK_KEY_KP_Space)
3864       && ((event->state & modifiers) == 0)
3865       && !(impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
3866            impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
3867     {
3868       GtkWindow *window;
3869
3870       window = get_toplevel (widget);
3871       if (window)
3872         {
3873           GtkWidget *default_widget, *focus_widget;
3874
3875           default_widget = gtk_window_get_default_widget (window);
3876           focus_widget = gtk_window_get_focus (window);
3877
3878           if (widget != default_widget &&
3879               !(widget == focus_widget && (!default_widget || !gtk_widget_get_sensitive (default_widget))))
3880             {
3881               gtk_window_activate_default (window);
3882
3883               return TRUE;
3884             }
3885         }
3886     }
3887
3888   return FALSE;
3889 }
3890
3891 /* Callback used when the file list's popup menu is detached */
3892 static void
3893 popup_menu_detach_cb (GtkWidget *attach_widget,
3894                       GtkMenu   *menu)
3895 {
3896   GtkFileChooserDefault *impl;
3897
3898   impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
3899   g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
3900
3901   impl->browse_files_popup_menu = NULL;
3902   impl->browse_files_popup_menu_add_shortcut_item = NULL;
3903   impl->browse_files_popup_menu_hidden_files_item = NULL;
3904 }
3905
3906 /* Callback used when the "Add to Bookmarks" menu item is activated */
3907 static void
3908 add_to_shortcuts_cb (GtkMenuItem           *item,
3909                      GtkFileChooserDefault *impl)
3910 {
3911   bookmarks_add_selected_folder (impl);
3912 }
3913
3914 /* Callback used when the "Show Hidden Files" menu item is toggled */
3915 static void
3916 show_hidden_toggled_cb (GtkCheckMenuItem      *item,
3917                         GtkFileChooserDefault *impl)
3918 {
3919   g_object_set (impl,
3920                 "show-hidden", gtk_check_menu_item_get_active (item),
3921                 NULL);
3922 }
3923
3924 /* Callback used when the "Show Size Column" menu item is toggled */
3925 static void
3926 show_size_column_toggled_cb (GtkCheckMenuItem *item,
3927                              GtkFileChooserDefault *impl)
3928 {
3929   impl->show_size_column = gtk_check_menu_item_get_active (item);
3930
3931   gtk_tree_view_column_set_visible (impl->list_size_column,
3932                                     impl->show_size_column);
3933 }
3934
3935 /* Shows an error dialog about not being able to select a dragged file */
3936 static void
3937 error_selecting_dragged_file_dialog (GtkFileChooserDefault *impl,
3938                                      GFile                 *file,
3939                                      GError                *error)
3940 {
3941   error_dialog (impl,
3942                 _("Could not select file"),
3943                 file, error);
3944 }
3945
3946 static void
3947 file_list_drag_data_select_uris (GtkFileChooserDefault  *impl,
3948                                  gchar                 **uris)
3949 {
3950   int i;
3951   char *uri;
3952   GtkFileChooser *chooser = GTK_FILE_CHOOSER (impl);
3953
3954   for (i = 1; uris[i]; i++)
3955     {
3956       GFile *file;
3957       GError *error = NULL;
3958
3959       uri = uris[i];
3960       file = g_file_new_for_uri (uri);
3961
3962       gtk_file_chooser_default_select_file (chooser, file, &error);
3963       if (error)
3964         error_selecting_dragged_file_dialog (impl, file, error);
3965
3966       g_object_unref (file);
3967     }
3968 }
3969
3970 struct FileListDragData
3971 {
3972   GtkFileChooserDefault *impl;
3973   gchar **uris;
3974   GFile *file;
3975 };
3976
3977 static void
3978 file_list_drag_data_received_get_info_cb (GCancellable *cancellable,
3979                                           GFileInfo    *info,
3980                                           const GError *error,
3981                                           gpointer      user_data)
3982 {
3983   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
3984   struct FileListDragData *data = user_data;
3985   GtkFileChooser *chooser = GTK_FILE_CHOOSER (data->impl);
3986
3987   if (cancellable != data->impl->file_list_drag_data_received_cancellable)
3988     goto out;
3989
3990   data->impl->file_list_drag_data_received_cancellable = NULL;
3991
3992   if (cancelled || error)
3993     goto out;
3994
3995   if ((data->impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
3996        data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) &&
3997       data->uris[1] == 0 && !error && _gtk_file_info_consider_as_directory (info))
3998     change_folder_and_display_error (data->impl, data->file, FALSE);
3999   else
4000     {
4001       GError *error = NULL;
4002
4003       gtk_file_chooser_default_unselect_all (chooser);
4004       gtk_file_chooser_default_select_file (chooser, data->file, &error);
4005       if (error)
4006         error_selecting_dragged_file_dialog (data->impl, data->file, error);
4007       else
4008         browse_files_center_selected_row (data->impl);
4009     }
4010
4011   if (data->impl->select_multiple)
4012     file_list_drag_data_select_uris (data->impl, data->uris);
4013
4014 out:
4015   g_object_unref (data->impl);
4016   g_strfreev (data->uris);
4017   g_object_unref (data->file);
4018   g_free (data);
4019
4020   g_object_unref (cancellable);
4021 }
4022
4023 static void
4024 file_list_drag_data_received_cb (GtkWidget        *widget,
4025                                  GdkDragContext   *context,
4026                                  gint              x,
4027                                  gint              y,
4028                                  GtkSelectionData *selection_data,
4029                                  guint             info,
4030                                  guint             time_,
4031                                  gpointer          data)
4032 {
4033   GtkFileChooserDefault *impl;
4034   gchar **uris;
4035   char *uri;
4036   GFile *file;
4037
4038   impl = GTK_FILE_CHOOSER_DEFAULT (data);
4039
4040   /* Allow only drags from other widgets; see bug #533891. */
4041   if (gtk_drag_get_source_widget (context) == widget)
4042     {
4043       g_signal_stop_emission_by_name (widget, "drag-data-received");
4044       return;
4045     }
4046
4047   /* Parse the text/uri-list string, navigate to the first one */
4048   uris = gtk_selection_data_get_uris (selection_data);
4049   if (uris && uris[0])
4050     {
4051       struct FileListDragData *data;
4052
4053       uri = uris[0];
4054       file = g_file_new_for_uri (uri);
4055
4056       data = g_new0 (struct FileListDragData, 1);
4057       data->impl = g_object_ref (impl);
4058       data->uris = uris;
4059       data->file = file;
4060
4061       if (impl->file_list_drag_data_received_cancellable)
4062         g_cancellable_cancel (impl->file_list_drag_data_received_cancellable);
4063
4064       impl->file_list_drag_data_received_cancellable =
4065         _gtk_file_system_get_info (impl->file_system, file,
4066                                    "standard::type",
4067                                    file_list_drag_data_received_get_info_cb,
4068                                    data);
4069     }
4070
4071   g_signal_stop_emission_by_name (widget, "drag-data-received");
4072 }
4073
4074 /* Don't do anything with the drag_drop signal */
4075 static gboolean
4076 file_list_drag_drop_cb (GtkWidget             *widget,
4077                         GdkDragContext        *context,
4078                         gint                   x,
4079                         gint                   y,
4080                         guint                  time_,
4081                         GtkFileChooserDefault *impl)
4082 {
4083   g_signal_stop_emission_by_name (widget, "drag-drop");
4084   return TRUE;
4085 }
4086
4087 /* Disable the normal tree drag motion handler, it makes it look like you're
4088    dropping the dragged item onto a tree item */
4089 static gboolean
4090 file_list_drag_motion_cb (GtkWidget             *widget,
4091                           GdkDragContext        *context,
4092                           gint                   x,
4093                           gint                   y,
4094                           guint                  time_,
4095                           GtkFileChooserDefault *impl)
4096 {
4097   g_signal_stop_emission_by_name (widget, "drag-motion");
4098   return TRUE;
4099 }
4100
4101 /* Constructs the popup menu for the file list if needed */
4102 static void
4103 file_list_build_popup_menu (GtkFileChooserDefault *impl)
4104 {
4105   GtkWidget *item;
4106
4107   if (impl->browse_files_popup_menu)
4108     return;
4109
4110   impl->browse_files_popup_menu = gtk_menu_new ();
4111   gtk_menu_attach_to_widget (GTK_MENU (impl->browse_files_popup_menu),
4112                              impl->browse_files_tree_view,
4113                              popup_menu_detach_cb);
4114
4115   item = gtk_image_menu_item_new_with_mnemonic (_("_Add to Bookmarks"));
4116   impl->browse_files_popup_menu_add_shortcut_item = item;
4117   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
4118                                  gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU));
4119   g_signal_connect (item, "activate",
4120                     G_CALLBACK (add_to_shortcuts_cb), impl);
4121   gtk_widget_show (item);
4122   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4123
4124   item = gtk_separator_menu_item_new ();
4125   gtk_widget_show (item);
4126   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4127
4128   item = gtk_check_menu_item_new_with_mnemonic (_("Show _Hidden Files"));
4129   impl->browse_files_popup_menu_hidden_files_item = item;
4130   g_signal_connect (item, "toggled",
4131                     G_CALLBACK (show_hidden_toggled_cb), impl);
4132   gtk_widget_show (item);
4133   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4134
4135   item = gtk_check_menu_item_new_with_mnemonic (_("Show _Size Column"));
4136   impl->browse_files_popup_menu_size_column_item = item;
4137   g_signal_connect (item, "toggled",
4138                     G_CALLBACK (show_size_column_toggled_cb), impl);
4139   gtk_widget_show (item);
4140   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4141
4142   bookmarks_check_add_sensitivity (impl);
4143 }
4144
4145 /* Updates the popup menu for the file list, creating it if necessary */
4146 static void
4147 file_list_update_popup_menu (GtkFileChooserDefault *impl)
4148 {
4149   file_list_build_popup_menu (impl);
4150
4151   /* FIXME - handle OPERATION_MODE_SEARCH and OPERATION_MODE_RECENT */
4152
4153   /* The sensitivity of the Add to Bookmarks item is set in
4154    * bookmarks_check_add_sensitivity()
4155    */
4156
4157   /* 'Show Hidden Files' */
4158   g_signal_handlers_block_by_func (impl->browse_files_popup_menu_hidden_files_item,
4159                                    G_CALLBACK (show_hidden_toggled_cb), impl);
4160   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (impl->browse_files_popup_menu_hidden_files_item),
4161                                   impl->show_hidden);
4162   g_signal_handlers_unblock_by_func (impl->browse_files_popup_menu_hidden_files_item,
4163                                      G_CALLBACK (show_hidden_toggled_cb), impl);
4164
4165   /* 'Show Size Column' */
4166   g_signal_handlers_block_by_func (impl->browse_files_popup_menu_size_column_item,
4167                                    G_CALLBACK (show_size_column_toggled_cb), impl);
4168   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (impl->browse_files_popup_menu_size_column_item),
4169                                   impl->show_size_column);
4170   g_signal_handlers_unblock_by_func (impl->browse_files_popup_menu_size_column_item,
4171                                      G_CALLBACK (show_size_column_toggled_cb), impl);
4172 }
4173
4174 static void
4175 popup_position_func (GtkMenu   *menu,
4176                      gint      *x,
4177                      gint      *y,
4178                      gboolean  *push_in,
4179                      gpointer   user_data)
4180 {
4181   GtkAllocation allocation;
4182   GtkWidget *widget = GTK_WIDGET (user_data);
4183   GdkScreen *screen = gtk_widget_get_screen (widget);
4184   GtkRequisition req;
4185   gint monitor_num;
4186   GdkRectangle monitor;
4187
4188   g_return_if_fail (gtk_widget_get_realized (widget));
4189
4190   gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
4191
4192   gtk_widget_get_preferred_size (GTK_WIDGET (menu),
4193                                  &req, NULL);
4194
4195   gtk_widget_get_allocation (widget, &allocation);
4196   *x += (allocation.width - req.width) / 2;
4197   *y += (allocation.height - req.height) / 2;
4198
4199   monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
4200   gtk_menu_set_monitor (menu, monitor_num);
4201   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
4202
4203   *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
4204   *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
4205
4206   *push_in = FALSE;
4207 }
4208
4209 static void
4210 file_list_popup_menu (GtkFileChooserDefault *impl,
4211                       GdkEventButton        *event)
4212 {
4213   file_list_update_popup_menu (impl);
4214   if (event)
4215     gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
4216                     NULL, NULL, NULL, NULL,
4217                     event->button, event->time);
4218   else
4219     {
4220       gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
4221                       NULL, NULL,
4222                       popup_position_func, impl->browse_files_tree_view,
4223                       0, GDK_CURRENT_TIME);
4224       gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->browse_files_popup_menu),
4225                                    FALSE);
4226     }
4227
4228 }
4229
4230 /* Callback used for the GtkWidget::popup-menu signal of the file list */
4231 static gboolean
4232 list_popup_menu_cb (GtkWidget *widget,
4233                     GtkFileChooserDefault *impl)
4234 {
4235   file_list_popup_menu (impl, NULL);
4236   return TRUE;
4237 }
4238
4239 /* Callback used when a button is pressed on the file list.  We trap button 3 to
4240  * bring up a popup menu.
4241  */
4242 static gboolean
4243 list_button_press_event_cb (GtkWidget             *widget,
4244                             GdkEventButton        *event,
4245                             GtkFileChooserDefault *impl)
4246 {
4247   static gboolean in_press = FALSE;
4248
4249   if (in_press)
4250     return FALSE;
4251
4252   if (event->button != 3)
4253     return FALSE;
4254
4255   in_press = TRUE;
4256   gtk_widget_event (impl->browse_files_tree_view, (GdkEvent *) event);
4257   in_press = FALSE;
4258
4259   file_list_popup_menu (impl, event);
4260   return TRUE;
4261 }
4262
4263 typedef struct {
4264   OperationMode operation_mode;
4265   gint general_column;
4266   gint model_column;
4267 } ColumnMap;
4268
4269 /* Sets the sort column IDs for the file list based on the operation mode */
4270 static void
4271 file_list_set_sort_column_ids (GtkFileChooserDefault *impl)
4272 {
4273   gtk_tree_view_column_set_sort_column_id (impl->list_name_column, MODEL_COL_NAME);
4274   gtk_tree_view_column_set_sort_column_id (impl->list_mtime_column, MODEL_COL_MTIME);
4275   gtk_tree_view_column_set_sort_column_id (impl->list_size_column, MODEL_COL_SIZE);
4276 }
4277
4278 static gboolean
4279 file_list_query_tooltip_cb (GtkWidget  *widget,
4280                             gint        x,
4281                             gint        y,
4282                             gboolean    keyboard_tip,
4283                             GtkTooltip *tooltip,
4284                             gpointer    user_data)
4285 {
4286   GtkFileChooserDefault *impl = user_data;
4287   GtkTreeModel *model;
4288   GtkTreePath *path;
4289   GtkTreeIter iter;
4290   GFile *file;
4291   gchar *filename;
4292
4293   if (impl->operation_mode == OPERATION_MODE_BROWSE)
4294     return FALSE;
4295
4296
4297   if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (impl->browse_files_tree_view),
4298                                           &x, &y,
4299                                           keyboard_tip,
4300                                           &model, &path, &iter))
4301     return FALSE;
4302                                        
4303   gtk_tree_model_get (model, &iter,
4304                       MODEL_COL_FILE, &file,
4305                       -1);
4306
4307   if (file == NULL)
4308     {
4309       gtk_tree_path_free (path);
4310       return FALSE;
4311     }
4312
4313   filename = g_file_get_path (file);
4314   gtk_tooltip_set_text (tooltip, filename);
4315   gtk_tree_view_set_tooltip_row (GTK_TREE_VIEW (impl->browse_files_tree_view),
4316                                  tooltip,
4317                                  path);
4318
4319   g_free (filename);
4320   g_object_unref (file);
4321   gtk_tree_path_free (path);
4322
4323   return TRUE;
4324 }
4325
4326 static void
4327 set_icon_cell_renderer_fixed_size (GtkFileChooserDefault *impl, GtkCellRenderer *renderer)
4328 {
4329   gint xpad, ypad;
4330
4331   gtk_cell_renderer_get_padding (renderer, &xpad, &ypad);
4332   gtk_cell_renderer_set_fixed_size (renderer, 
4333                                     xpad * 2 + impl->icon_size,
4334                                     ypad * 2 + impl->icon_size);
4335 }
4336
4337 /* Creates the widgets for the file list */
4338 static GtkWidget *
4339 create_file_list (GtkFileChooserDefault *impl)
4340 {
4341   GtkWidget *swin;
4342   GtkTreeSelection *selection;
4343   GtkTreeViewColumn *column;
4344   GtkCellRenderer *renderer;
4345
4346   /* Scrolled window */
4347   swin = gtk_scrolled_window_new (NULL, NULL);
4348   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
4349                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
4350   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
4351                                        GTK_SHADOW_IN);
4352
4353   /* Tree/list view */
4354
4355   impl->browse_files_tree_view = gtk_tree_view_new ();
4356 #ifdef PROFILE_FILE_CHOOSER
4357   g_object_set_data (G_OBJECT (impl->browse_files_tree_view), "fmq-name", "file_list");
4358 #endif
4359   g_object_set_data (G_OBJECT (impl->browse_files_tree_view), I_("GtkFileChooserDefault"), impl);
4360   atk_object_set_name (gtk_widget_get_accessible (impl->browse_files_tree_view), _("Files"));
4361
4362   gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->browse_files_tree_view), TRUE);
4363   gtk_container_add (GTK_CONTAINER (swin), impl->browse_files_tree_view);
4364
4365   gtk_drag_dest_set (impl->browse_files_tree_view,
4366                      GTK_DEST_DEFAULT_ALL,
4367                      NULL, 0,
4368                      GDK_ACTION_COPY | GDK_ACTION_MOVE);
4369   gtk_drag_dest_add_uri_targets (impl->browse_files_tree_view);
4370   
4371   g_signal_connect (impl->browse_files_tree_view, "row-activated",
4372                     G_CALLBACK (list_row_activated), impl);
4373   g_signal_connect (impl->browse_files_tree_view, "key-press-event",
4374                     G_CALLBACK (browse_files_key_press_event_cb), impl);
4375   g_signal_connect (impl->browse_files_tree_view, "popup-menu",
4376                     G_CALLBACK (list_popup_menu_cb), impl);
4377   g_signal_connect (impl->browse_files_tree_view, "button-press-event",
4378                     G_CALLBACK (list_button_press_event_cb), impl);
4379
4380   g_signal_connect (impl->browse_files_tree_view, "drag-data-received",
4381                     G_CALLBACK (file_list_drag_data_received_cb), impl);
4382   g_signal_connect (impl->browse_files_tree_view, "drag-drop",
4383                     G_CALLBACK (file_list_drag_drop_cb), impl);
4384   g_signal_connect (impl->browse_files_tree_view, "drag-motion",
4385                     G_CALLBACK (file_list_drag_motion_cb), impl);
4386
4387   g_object_set (impl->browse_files_tree_view, "has-tooltip", TRUE, NULL);
4388   g_signal_connect (impl->browse_files_tree_view, "query-tooltip",
4389                     G_CALLBACK (file_list_query_tooltip_cb), impl);
4390
4391   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4392   gtk_tree_selection_set_select_function (selection,
4393                                           list_select_func,
4394                                           impl, NULL);
4395   gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_files_tree_view),
4396                                           GDK_BUTTON1_MASK,
4397                                           NULL, 0,
4398                                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
4399   gtk_drag_source_add_uri_targets (impl->browse_files_tree_view);
4400
4401   g_signal_connect (selection, "changed",
4402                     G_CALLBACK (list_selection_changed), impl);
4403
4404   /* Keep the column order in sync with update_cell_renderer_attributes() */
4405
4406   /* Filename column */
4407
4408   impl->list_name_column = gtk_tree_view_column_new ();
4409   gtk_tree_view_column_set_expand (impl->list_name_column, TRUE);
4410   gtk_tree_view_column_set_resizable (impl->list_name_column, TRUE);
4411   gtk_tree_view_column_set_title (impl->list_name_column, _("Name"));
4412
4413   renderer = gtk_cell_renderer_pixbuf_new ();
4414   /* We set a fixed size so that we get an empty slot even if no icons are loaded yet */
4415   set_icon_cell_renderer_fixed_size (impl, renderer);
4416   gtk_tree_view_column_pack_start (impl->list_name_column, renderer, FALSE);
4417
4418   impl->list_name_renderer = gtk_cell_renderer_text_new ();
4419   g_object_set (impl->list_name_renderer,
4420                 "ellipsize", PANGO_ELLIPSIZE_END,
4421                 NULL);
4422   g_signal_connect (impl->list_name_renderer, "edited",
4423                     G_CALLBACK (renderer_edited_cb), impl);
4424   g_signal_connect (impl->list_name_renderer, "editing-canceled",
4425                     G_CALLBACK (renderer_editing_canceled_cb), impl);
4426   gtk_tree_view_column_pack_start (impl->list_name_column, impl->list_name_renderer, TRUE);
4427
4428   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), impl->list_name_column);
4429
4430   /* Size column */
4431
4432   column = gtk_tree_view_column_new ();
4433   gtk_tree_view_column_set_resizable (column, TRUE);
4434   gtk_tree_view_column_set_title (column, _("Size"));
4435
4436   renderer = gtk_cell_renderer_text_new ();
4437   g_object_set (renderer, 
4438                 "alignment", PANGO_ALIGN_RIGHT,
4439                 NULL);
4440   gtk_tree_view_column_pack_start (column, renderer, TRUE); /* bug: it doesn't expand */
4441   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
4442   impl->list_size_column = column;
4443
4444   /* Modification time column */
4445
4446   column = gtk_tree_view_column_new ();
4447   gtk_tree_view_column_set_resizable (column, TRUE);
4448   gtk_tree_view_column_set_title (column, _("Modified"));
4449
4450   renderer = gtk_cell_renderer_text_new ();
4451   gtk_tree_view_column_pack_start (column, renderer, TRUE);
4452   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
4453   impl->list_mtime_column = column;
4454   
4455   file_list_set_sort_column_ids (impl);
4456   update_cell_renderer_attributes (impl);
4457
4458   gtk_widget_show_all (swin);
4459
4460   return swin;
4461 }
4462
4463 static GtkWidget *
4464 create_path_bar (GtkFileChooserDefault *impl)
4465 {
4466   GtkWidget *path_bar;
4467
4468   path_bar = g_object_new (GTK_TYPE_PATH_BAR, NULL);
4469   _gtk_path_bar_set_file_system (GTK_PATH_BAR (path_bar), impl->file_system);
4470
4471   return path_bar;
4472 }
4473
4474 /* Creates the widgets for the files/folders pane */
4475 static GtkWidget *
4476 file_pane_create (GtkFileChooserDefault *impl,
4477                   GtkSizeGroup          *size_group)
4478 {
4479   GtkWidget *vbox;
4480   GtkWidget *hbox;
4481   GtkWidget *widget;
4482
4483   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
4484   gtk_widget_show (vbox);
4485
4486   /* Box for lists and preview */
4487
4488   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, PREVIEW_HBOX_SPACING);
4489   gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
4490   gtk_widget_show (hbox);
4491
4492   /* File list */
4493
4494   widget = create_file_list (impl);
4495   gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
4496   gtk_size_group_add_widget (size_group, widget);
4497
4498   /* Preview */
4499
4500   impl->preview_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
4501   gtk_box_pack_start (GTK_BOX (hbox), impl->preview_box, FALSE, FALSE, 0);
4502   /* Don't show preview box initially */
4503
4504   /* Filter combo */
4505
4506   impl->filter_combo_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
4507
4508   widget = filter_create (impl);
4509
4510   gtk_widget_show (widget);
4511   gtk_box_pack_end (GTK_BOX (impl->filter_combo_hbox), widget, FALSE, FALSE, 0);
4512
4513   gtk_box_pack_end (GTK_BOX (vbox), impl->filter_combo_hbox, FALSE, FALSE, 0);
4514
4515   return vbox;
4516 }
4517
4518 /* Callback used when the "Browse for more folders" expander is toggled */
4519 static void
4520 expander_changed_cb (GtkExpander           *expander,
4521                      GParamSpec            *pspec,
4522                      GtkFileChooserDefault *impl)
4523 {
4524   impl->expand_folders = gtk_expander_get_expanded(GTK_EXPANDER (impl->save_expander));
4525   update_appearance (impl);
4526 }
4527
4528 /* Callback used when the selection changes in the save folder combo box */
4529 static void
4530 save_folder_combo_changed_cb (GtkComboBox           *combo,
4531                               GtkFileChooserDefault *impl)
4532 {
4533   GtkTreeIter iter;
4534
4535   if (impl->changing_folder)
4536     return;
4537
4538   if (gtk_combo_box_get_active_iter (combo, &iter))
4539     {
4540       GtkTreeIter child_iter;
4541       
4542       gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model),
4543                                                         &child_iter,
4544                                                         &iter);
4545       shortcuts_activate_iter (impl, &child_iter);
4546     }
4547 }
4548
4549 static void
4550 save_folder_update_tooltip (GtkComboBox           *combo,
4551                             GtkFileChooserDefault *impl)
4552 {
4553   GtkTreeIter iter;
4554   gchar *tooltip;
4555
4556   tooltip = NULL;
4557
4558   if (gtk_combo_box_get_active_iter (combo, &iter))
4559     {
4560       GtkTreeIter child_iter;
4561       gpointer col_data;
4562       ShortcutType shortcut_type;
4563
4564       gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model),
4565                                                         &child_iter,
4566                                                         &iter);
4567       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &child_iter,
4568                           SHORTCUTS_COL_DATA, &col_data,
4569                           SHORTCUTS_COL_TYPE, &shortcut_type,
4570                           -1);
4571
4572       if (shortcut_type == SHORTCUT_TYPE_FILE)
4573         tooltip = g_file_get_parse_name (G_FILE (col_data));
4574    }
4575
4576   gtk_widget_set_tooltip_text (GTK_WIDGET (combo), tooltip);
4577   gtk_widget_set_has_tooltip (GTK_WIDGET (combo),
4578                               gtk_widget_get_sensitive (GTK_WIDGET (combo)));
4579   g_free (tooltip);
4580 }
4581
4582 /* Filter function used to filter out the Search item and its separator.  
4583  * Used for the "Save in folder" combo box, so that these items do not appear in it.
4584  */
4585 static gboolean
4586 shortcuts_combo_filter_func (GtkTreeModel *model,
4587                              GtkTreeIter  *iter,
4588                              gpointer      data)
4589 {
4590   GtkFileChooserDefault *impl;
4591   GtkTreePath *tree_path;
4592   gint *indices;
4593   int idx;
4594   gboolean retval;
4595
4596   impl = GTK_FILE_CHOOSER_DEFAULT (data);
4597
4598   g_assert (model == GTK_TREE_MODEL (impl->shortcuts_model));
4599
4600   tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), iter);
4601   g_assert (tree_path != NULL);
4602
4603   indices = gtk_tree_path_get_indices (tree_path);
4604
4605   retval = TRUE;
4606
4607   if (impl->has_search)
4608     {
4609       idx = shortcuts_get_index (impl, SHORTCUTS_SEARCH);
4610       if (idx == indices[0])
4611         retval = FALSE;
4612     }
4613   
4614   if (impl->has_recent)
4615     {
4616       idx = shortcuts_get_index (impl, SHORTCUTS_RECENT);
4617       if (idx == indices[0])
4618         retval = FALSE;
4619       else
4620         {
4621           idx = shortcuts_get_index (impl, SHORTCUTS_RECENT_SEPARATOR);
4622           if (idx == indices[0])
4623             retval = FALSE;
4624         }
4625      }
4626
4627   gtk_tree_path_free (tree_path);
4628
4629   return retval;
4630  }
4631
4632 /* Creates the combo box with the save folders */
4633 static GtkWidget *
4634 save_folder_combo_create (GtkFileChooserDefault *impl)
4635 {
4636   GtkWidget *combo;
4637   GtkCellRenderer *cell;
4638
4639   impl->shortcuts_combo_filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (impl->shortcuts_model), NULL);
4640   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model),
4641                                           shortcuts_combo_filter_func,
4642                                           impl,
4643                                           NULL);
4644
4645   combo = g_object_new (GTK_TYPE_COMBO_BOX,
4646                         "model", impl->shortcuts_combo_filter_model,
4647                         "focus-on-click", FALSE,
4648                         NULL);
4649   gtk_widget_show (combo);
4650
4651   cell = gtk_cell_renderer_pixbuf_new ();
4652   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE);
4653   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
4654                                   "pixbuf", SHORTCUTS_COL_PIXBUF,
4655                                   "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
4656                                   "sensitive", SHORTCUTS_COL_PIXBUF_VISIBLE,
4657                                   NULL);
4658
4659   cell = gtk_cell_renderer_text_new ();
4660   g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
4661   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
4662   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
4663                                   "text", SHORTCUTS_COL_NAME,
4664                                   "sensitive", SHORTCUTS_COL_PIXBUF_VISIBLE,
4665                                   NULL);
4666
4667   gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo),
4668                                         shortcuts_row_separator_func,
4669                                         NULL, NULL);
4670
4671   g_signal_connect (combo, "changed",
4672                     G_CALLBACK (save_folder_combo_changed_cb), impl);
4673   g_signal_connect (combo, "changed",
4674                     G_CALLBACK (save_folder_update_tooltip), impl);
4675
4676   return combo;
4677 }
4678
4679 /* Creates the widgets specific to Save mode */
4680 static void
4681 save_widgets_create (GtkFileChooserDefault *impl)
4682 {
4683   GtkWidget *vbox;
4684   GtkWidget *table;
4685   GtkWidget *widget;
4686
4687   if (impl->save_widgets != NULL)
4688     return;
4689
4690   location_switch_to_path_bar (impl);
4691
4692   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
4693
4694   table = gtk_table_new (2, 2, FALSE);
4695   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
4696   gtk_widget_show (table);
4697   gtk_table_set_row_spacings (GTK_TABLE (table), 12);
4698   gtk_table_set_col_spacings (GTK_TABLE (table), 12);
4699
4700   /* Label */
4701
4702   widget = gtk_label_new_with_mnemonic (_("_Name:"));
4703   gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
4704   gtk_table_attach (GTK_TABLE (table), widget,
4705                     0, 1, 0, 1,
4706                     GTK_FILL, GTK_FILL,
4707                     0, 0);
4708   gtk_widget_show (widget);
4709
4710   /* Location entry */
4711
4712   impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
4713   _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
4714                                            impl->file_system);
4715   _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->local_only);
4716   gtk_entry_set_width_chars (GTK_ENTRY (impl->location_entry), 45);
4717   gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
4718   gtk_table_attach (GTK_TABLE (table), impl->location_entry,
4719                     1, 2, 0, 1,
4720                     GTK_EXPAND | GTK_FILL, 0,
4721                     0, 0);
4722   gtk_widget_show (impl->location_entry);
4723   gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->location_entry);
4724
4725   /* Folder combo */
4726   impl->save_folder_label = gtk_label_new (NULL);
4727   gtk_misc_set_alignment (GTK_MISC (impl->save_folder_label), 0.0, 0.5);
4728   gtk_table_attach (GTK_TABLE (table), impl->save_folder_label,
4729                     0, 1, 1, 2,
4730                     GTK_FILL, GTK_FILL,
4731                     0, 0);
4732   gtk_widget_show (impl->save_folder_label);
4733
4734   impl->save_folder_combo = save_folder_combo_create (impl);
4735   gtk_table_attach (GTK_TABLE (table), impl->save_folder_combo,
4736                     1, 2, 1, 2,
4737                     GTK_EXPAND | GTK_FILL, GTK_FILL,
4738                     0, 0);
4739   gtk_label_set_mnemonic_widget (GTK_LABEL (impl->save_folder_label), impl->save_folder_combo);
4740
4741   /* Expander */
4742
4743   impl->save_expander = gtk_expander_new_with_mnemonic (_("_Browse for other folders"));
4744   gtk_widget_set_halign (impl->save_expander, GTK_ALIGN_START);
4745   gtk_widget_set_valign (impl->save_expander, GTK_ALIGN_CENTER);
4746   gtk_box_pack_start (GTK_BOX (vbox), impl->save_expander, FALSE, FALSE, 0);
4747   g_signal_connect (impl->save_expander, "notify::expanded",
4748                     G_CALLBACK (expander_changed_cb),
4749                     impl);
4750   gtk_widget_show (impl->save_expander);
4751
4752   impl->save_widgets = vbox;
4753   gtk_box_pack_start (GTK_BOX (impl), impl->save_widgets, FALSE, FALSE, 0);
4754   gtk_box_reorder_child (GTK_BOX (impl), impl->save_widgets, 0);
4755   gtk_widget_show (impl->save_widgets);
4756 }
4757
4758 /* Destroys the widgets specific to Save mode */
4759 static void
4760 save_widgets_destroy (GtkFileChooserDefault *impl)
4761 {
4762   if (impl->save_widgets == NULL)
4763     return;
4764
4765   gtk_widget_destroy (impl->save_widgets);
4766   impl->save_widgets = NULL;
4767   impl->location_entry = NULL;
4768   impl->save_folder_label = NULL;
4769   impl->save_folder_combo = NULL;
4770   impl->save_expander = NULL;
4771 }
4772
4773 /* Turns on the path bar widget.  Can be called even if we are already in that
4774  * mode.
4775  */
4776 static void
4777 location_switch_to_path_bar (GtkFileChooserDefault *impl)
4778 {
4779   if (impl->location_entry)
4780     {
4781       gtk_widget_destroy (impl->location_entry);
4782       impl->location_entry = NULL;
4783     }
4784
4785   gtk_widget_hide (impl->location_entry_box);
4786 }
4787
4788 /* Sets the full path of the current folder as the text in the location entry. */
4789 static void
4790 location_entry_set_initial_text (GtkFileChooserDefault *impl)
4791 {
4792   gchar *text, *filename;
4793
4794   if (!impl->current_folder)
4795     return;
4796
4797   filename = g_file_get_path (impl->current_folder);
4798
4799   if (filename)
4800     {
4801       text = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
4802       g_free (filename);
4803     }
4804   else
4805     text = g_file_get_uri (impl->current_folder);
4806
4807   if (text)
4808     {
4809       gboolean need_slash;
4810       int len;
4811
4812       len = strlen (text);
4813       need_slash = (text[len - 1] != G_DIR_SEPARATOR);
4814
4815       if (need_slash)
4816         {
4817           char *slash_text;
4818
4819           slash_text = g_new (char, len + 2);
4820           strcpy (slash_text, text);
4821           slash_text[len] = G_DIR_SEPARATOR;
4822           slash_text[len + 1] = 0;
4823
4824           g_free (text);
4825           text = slash_text;
4826         }
4827
4828       _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), text);
4829       g_free (text);
4830     }
4831
4832   g_free (filename);
4833 }
4834
4835 /* Turns on the location entry.  Can be called even if we are already in that
4836  * mode.
4837  */
4838 static void
4839 location_switch_to_filename_entry (GtkFileChooserDefault *impl)
4840 {
4841   /* when in search or recent files mode, we are not showing the
4842    * location_entry_box container, so there's no point in switching
4843    * to it.
4844    */
4845   if (impl->operation_mode == OPERATION_MODE_SEARCH ||
4846       impl->operation_mode == OPERATION_MODE_RECENT)
4847     return;
4848
4849   if (impl->location_entry)
4850     gtk_widget_destroy (impl->location_entry);
4851
4852   /* Box */
4853
4854   gtk_widget_show (impl->location_entry_box);
4855
4856   /* Entry */
4857
4858   impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
4859   _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
4860                                            impl->file_system);
4861   gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
4862   _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action);
4863
4864   gtk_box_pack_start (GTK_BOX (impl->location_entry_box), impl->location_entry, TRUE, TRUE, 0);
4865   gtk_label_set_mnemonic_widget (GTK_LABEL (impl->location_label), impl->location_entry);
4866
4867   /* Configure the entry */
4868
4869   _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->current_folder);
4870   _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->local_only);
4871
4872   /* Done */
4873
4874   gtk_widget_show (impl->location_entry);
4875   gtk_widget_grab_focus (impl->location_entry);
4876 }
4877
4878 /* Sets a new location mode.  set_buttons determines whether the toggle button
4879  * for the mode will also be changed.
4880  */
4881 static void
4882 location_mode_set (GtkFileChooserDefault *impl,
4883                    LocationMode new_mode,
4884                    gboolean set_button)
4885 {
4886   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
4887       impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
4888     {
4889       GtkWindow *toplevel;
4890       GtkWidget *current_focus;
4891       gboolean button_active;
4892       gboolean switch_to_file_list;
4893
4894       switch (new_mode)
4895         {
4896         case LOCATION_MODE_PATH_BAR:
4897           button_active = FALSE;
4898
4899           /* The location_entry will disappear when we switch to path bar mode.  So,
4900            * we'll focus the file list in that case, to avoid having a window with
4901            * no focused widget.
4902            */
4903           toplevel = get_toplevel (GTK_WIDGET (impl));
4904           switch_to_file_list = FALSE;
4905           if (toplevel)
4906             {
4907               current_focus = gtk_window_get_focus (toplevel);
4908               if (!current_focus || current_focus == impl->location_entry)
4909                 switch_to_file_list = TRUE;
4910             }
4911
4912           location_switch_to_path_bar (impl);
4913
4914           if (switch_to_file_list)
4915             gtk_widget_grab_focus (impl->browse_files_tree_view);
4916
4917           break;
4918
4919         case LOCATION_MODE_FILENAME_ENTRY:
4920           button_active = TRUE;
4921           location_switch_to_filename_entry (impl);
4922           break;
4923
4924         default:
4925           g_assert_not_reached ();
4926           return;
4927         }
4928
4929       if (set_button)
4930         {
4931           g_signal_handlers_block_by_func (impl->location_button,
4932                                            G_CALLBACK (location_button_toggled_cb), impl);
4933
4934           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (impl->location_button), button_active);
4935
4936           g_signal_handlers_unblock_by_func (impl->location_button,
4937                                              G_CALLBACK (location_button_toggled_cb), impl);
4938         }
4939     }
4940
4941   impl->location_mode = new_mode;
4942 }
4943
4944 static void
4945 location_toggle_popup_handler (GtkFileChooserDefault *impl)
4946 {
4947   /* when in search or recent files mode, we are not showing the
4948    * location_entry_box container, so there's no point in switching
4949    * to it.
4950    */
4951   if (impl->operation_mode == OPERATION_MODE_SEARCH ||
4952       impl->operation_mode == OPERATION_MODE_RECENT)
4953     return;
4954
4955   /* If the file entry is not visible, show it.
4956    * If it is visible, turn it off only if it is focused.  Otherwise, switch to the entry.
4957    */
4958   if (impl->location_mode == LOCATION_MODE_PATH_BAR)
4959     {
4960       location_mode_set (impl, LOCATION_MODE_FILENAME_ENTRY, TRUE);
4961     }
4962   else if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
4963     {
4964       if (gtk_widget_has_focus (impl->location_entry))
4965         {
4966           location_mode_set (impl, LOCATION_MODE_PATH_BAR, TRUE);
4967         }
4968       else
4969         {
4970           gtk_widget_grab_focus (impl->location_entry);
4971         }
4972     }
4973 }
4974
4975 /* Callback used when one of the location mode buttons is toggled */
4976 static void
4977 location_button_toggled_cb (GtkToggleButton *toggle,
4978                             GtkFileChooserDefault *impl)
4979 {
4980   gboolean is_active;
4981   LocationMode new_mode;
4982
4983   is_active = gtk_toggle_button_get_active (toggle);
4984
4985   if (is_active)
4986     {
4987       g_assert (impl->location_mode == LOCATION_MODE_PATH_BAR);
4988       new_mode = LOCATION_MODE_FILENAME_ENTRY;
4989     }
4990   else
4991     {
4992       g_assert (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY);
4993       new_mode = LOCATION_MODE_PATH_BAR;
4994     }
4995
4996   location_mode_set (impl, new_mode, FALSE);
4997 }
4998
4999 /* Creates a toggle button for the location entry. */
5000 static void
5001 location_button_create (GtkFileChooserDefault *impl)
5002 {
5003   GtkWidget *image;
5004   const char *str;
5005
5006   image = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON);
5007   gtk_widget_show (image);
5008
5009   impl->location_button = g_object_new (GTK_TYPE_TOGGLE_BUTTON,
5010                                         "image", image,
5011                                         NULL);
5012
5013   g_signal_connect (impl->location_button, "toggled",
5014                     G_CALLBACK (location_button_toggled_cb), impl);
5015
5016   str = _("Type a file name");
5017
5018   gtk_widget_set_tooltip_text (impl->location_button, str);
5019   atk_object_set_name (gtk_widget_get_accessible (impl->location_button), str);
5020 }
5021
5022 /* Creates the main hpaned with the widgets shared by Open and Save mode */
5023 static GtkWidget *
5024 browse_widgets_create (GtkFileChooserDefault *impl)
5025 {
5026   GtkWidget *vbox;
5027   GtkWidget *hpaned;
5028   GtkWidget *widget;
5029   GtkSizeGroup *size_group;
5030
5031   /* size group is used by the scrolled windows of the panes */
5032   size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
5033   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
5034
5035   /* Location widgets */
5036   impl->browse_path_bar_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
5037   gtk_box_pack_start (GTK_BOX (vbox), impl->browse_path_bar_hbox, FALSE, FALSE, 0);
5038   gtk_widget_show (impl->browse_path_bar_hbox);
5039
5040   /* Size group that allows the path bar to be the same size between modes */
5041   impl->browse_path_bar_size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
5042   gtk_size_group_set_ignore_hidden (impl->browse_path_bar_size_group, FALSE);
5043
5044   /* Location button */
5045
5046   location_button_create (impl);
5047   gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->location_button, FALSE, FALSE, 0);
5048   gtk_size_group_add_widget (impl->browse_path_bar_size_group, impl->location_button);
5049
5050   /* Path bar */
5051
5052   impl->browse_path_bar = create_path_bar (impl);
5053   g_signal_connect (impl->browse_path_bar, "path-clicked", G_CALLBACK (path_bar_clicked), impl);
5054   gtk_widget_show_all (impl->browse_path_bar);
5055   gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->browse_path_bar, TRUE, TRUE, 0);
5056   gtk_size_group_add_widget (impl->browse_path_bar_size_group, impl->browse_path_bar);
5057
5058   /* Create Folder */
5059   impl->browse_new_folder_button = gtk_button_new_with_mnemonic (_("Create Fo_lder"));
5060   g_signal_connect (impl->browse_new_folder_button, "clicked",
5061                     G_CALLBACK (new_folder_button_clicked), impl);
5062   gtk_box_pack_end (GTK_BOX (impl->browse_path_bar_hbox), impl->browse_new_folder_button, FALSE, FALSE, 0);
5063
5064   /* Box for the location label and entry */
5065
5066   impl->location_entry_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
5067   gtk_box_pack_start (GTK_BOX (vbox), impl->location_entry_box, FALSE, FALSE, 0);
5068
5069   impl->location_label = gtk_label_new_with_mnemonic (_("_Location:"));
5070   gtk_widget_show (impl->location_label);
5071   gtk_box_pack_start (GTK_BOX (impl->location_entry_box), impl->location_label, FALSE, FALSE, 0);
5072
5073   /* Paned widget */
5074
5075   hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
5076   gtk_widget_show (hpaned);
5077   gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
5078
5079   widget = shortcuts_pane_create (impl, size_group);
5080   gtk_paned_pack1 (GTK_PANED (hpaned), widget, FALSE, FALSE);
5081   widget = file_pane_create (impl, size_group);
5082   gtk_paned_pack2 (GTK_PANED (hpaned), widget, TRUE, FALSE);
5083   gtk_paned_set_position (GTK_PANED (hpaned), 148);
5084   g_object_unref (size_group);
5085
5086   return vbox;
5087 }
5088
5089 static GObject*
5090 gtk_file_chooser_default_constructor (GType                  type,
5091                                       guint                  n_construct_properties,
5092                                       GObjectConstructParam *construct_params)
5093 {
5094   GtkFileChooserDefault *impl;
5095   GObject *object;
5096
5097   profile_start ("start", NULL);
5098
5099   object = G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->constructor (type,
5100                                                                                 n_construct_properties,
5101                                                                                 construct_params);
5102   impl = GTK_FILE_CHOOSER_DEFAULT (object);
5103
5104   g_assert (impl->file_system);
5105
5106   gtk_widget_push_composite_child ();
5107
5108   /* Shortcuts model */
5109   shortcuts_model_create (impl);
5110
5111   /* The browse widgets */
5112   impl->browse_widgets = browse_widgets_create (impl);
5113   gtk_box_pack_start (GTK_BOX (impl), impl->browse_widgets, TRUE, TRUE, 0);
5114
5115   /* Alignment to hold extra widget */
5116   impl->extra_align = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
5117   gtk_widget_set_halign (impl->extra_align, GTK_ALIGN_START);
5118   gtk_widget_set_valign (impl->extra_align, GTK_ALIGN_CENTER);
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, &req, NULL);
8012           *default_height += gtk_box_get_spacing (GTK_BOX (chooser_embed)) + req.height;
8013         }
8014     }
8015   else
8016     {
8017       gtk_widget_get_preferred_size (GTK_WIDGET (impl),
8018                                      &req, NULL);
8019       *default_width = req.width;
8020       *default_height = req.height;
8021     }
8022 }
8023
8024 struct switch_folder_closure {
8025   GtkFileChooserDefault *impl;
8026   GFile *file;
8027   int num_selected;
8028 };
8029
8030 /* Used from gtk_tree_selection_selected_foreach() in switch_to_selected_folder() */
8031 static void
8032 switch_folder_foreach_cb (GtkTreeModel      *model,
8033                           GtkTreePath       *path,
8034                           GtkTreeIter       *iter,
8035                           gpointer           data)
8036 {
8037   struct switch_folder_closure *closure;
8038
8039   closure = data;
8040
8041   closure->file = _gtk_file_system_model_get_file (closure->impl->browse_files_model, iter);
8042   closure->num_selected++;
8043 }
8044
8045 /* Changes to the selected folder in the list view */
8046 static void
8047 switch_to_selected_folder (GtkFileChooserDefault *impl)
8048 {
8049   GtkTreeSelection *selection;
8050   struct switch_folder_closure closure;
8051
8052   /* We do this with foreach() rather than get_selected() as we may be in
8053    * multiple selection mode
8054    */
8055
8056   closure.impl = impl;
8057   closure.file = NULL;
8058   closure.num_selected = 0;
8059
8060   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
8061   gtk_tree_selection_selected_foreach (selection, switch_folder_foreach_cb, &closure);
8062
8063   g_assert (closure.file && closure.num_selected == 1);
8064
8065   change_folder_and_display_error (impl, closure.file, FALSE);
8066 }
8067
8068 /* Gets the GFileInfo for the selected row in the file list; assumes single
8069  * selection mode.
8070  */
8071 static GFileInfo *
8072 get_selected_file_info_from_file_list (GtkFileChooserDefault *impl,
8073                                        gboolean              *had_selection)
8074 {
8075   GtkTreeSelection *selection;
8076   GtkTreeIter iter;
8077   GFileInfo *info;
8078
8079   g_assert (!impl->select_multiple);
8080   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
8081   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
8082     {
8083       *had_selection = FALSE;
8084       return NULL;
8085     }
8086
8087   *had_selection = TRUE;
8088
8089   info = _gtk_file_system_model_get_info (impl->browse_files_model, &iter);
8090   return info;
8091 }
8092
8093 /* Gets the display name of the selected file in the file list; assumes single
8094  * selection mode and that something is selected.
8095  */
8096 static const gchar *
8097 get_display_name_from_file_list (GtkFileChooserDefault *impl)
8098 {
8099   GFileInfo *info;
8100   gboolean had_selection;
8101
8102   info = get_selected_file_info_from_file_list (impl, &had_selection);
8103   g_assert (had_selection);
8104   g_assert (info != NULL);
8105
8106   return g_file_info_get_display_name (info);
8107 }
8108
8109 static void
8110 add_custom_button_to_dialog (GtkDialog   *dialog,
8111                              const gchar *mnemonic_label,
8112                              const gchar *stock_id,
8113                              gint         response_id)
8114 {
8115   GtkWidget *button;
8116
8117   button = gtk_button_new_with_mnemonic (mnemonic_label);
8118   gtk_widget_set_can_default (button, TRUE);
8119   gtk_button_set_image (GTK_BUTTON (button),
8120                         gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON));
8121   gtk_widget_show (button);
8122
8123   gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, response_id);
8124 }
8125
8126 /* Presents an overwrite confirmation dialog; returns whether we should accept
8127  * the filename.
8128  */
8129 static gboolean
8130 confirm_dialog_should_accept_filename (GtkFileChooserDefault *impl,
8131                                        const gchar           *file_part,
8132                                        const gchar           *folder_display_name)
8133 {
8134   GtkWindow *toplevel;
8135   GtkWidget *dialog;
8136   int response;
8137
8138   toplevel = get_toplevel (GTK_WIDGET (impl));
8139
8140   dialog = gtk_message_dialog_new (toplevel,
8141                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
8142                                    GTK_MESSAGE_QUESTION,
8143                                    GTK_BUTTONS_NONE,
8144                                    _("A file named \"%s\" already exists.  Do you want to replace it?"),
8145                                    file_part);
8146   gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
8147                                             _("The file already exists in \"%s\".  Replacing it will "
8148                                               "overwrite its contents."),
8149                                             folder_display_name);
8150
8151   gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
8152   add_custom_button_to_dialog (GTK_DIALOG (dialog), _("_Replace"),
8153                                GTK_STOCK_SAVE_AS, GTK_RESPONSE_ACCEPT);
8154   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
8155                                            GTK_RESPONSE_ACCEPT,
8156                                            GTK_RESPONSE_CANCEL,
8157                                            -1);
8158   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
8159
8160   if (gtk_window_has_group (toplevel))
8161     gtk_window_group_add_window (gtk_window_get_group (toplevel),
8162                                  GTK_WINDOW (dialog));
8163
8164   response = gtk_dialog_run (GTK_DIALOG (dialog));
8165
8166   gtk_widget_destroy (dialog);
8167
8168   return (response == GTK_RESPONSE_ACCEPT);
8169 }
8170
8171 struct GetDisplayNameData
8172 {
8173   GtkFileChooserDefault *impl;
8174   gchar *file_part;
8175 };
8176
8177 static void
8178 confirmation_confirm_get_info_cb (GCancellable *cancellable,
8179                                   GFileInfo    *info,
8180                                   const GError *error,
8181                                   gpointer      user_data)
8182 {
8183   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
8184   gboolean should_respond = FALSE;
8185   struct GetDisplayNameData *data = user_data;
8186
8187   if (cancellable != data->impl->should_respond_get_info_cancellable)
8188     goto out;
8189
8190   data->impl->should_respond_get_info_cancellable = NULL;
8191
8192   if (cancelled)
8193     goto out;
8194
8195   if (error)
8196     /* Huh?  Did the folder disappear?  Let the caller deal with it */
8197     should_respond = TRUE;
8198   else
8199     should_respond = confirm_dialog_should_accept_filename (data->impl, data->file_part, g_file_info_get_display_name (info));
8200
8201   set_busy_cursor (data->impl, FALSE);
8202   if (should_respond)
8203     g_signal_emit_by_name (data->impl, "response-requested");
8204
8205 out:
8206   g_object_unref (data->impl);
8207   g_free (data->file_part);
8208   g_free (data);
8209
8210   g_object_unref (cancellable);
8211 }
8212
8213 /* Does overwrite confirmation if appropriate, and returns whether the dialog
8214  * should respond.  Can get the file part from the file list or the save entry.
8215  */
8216 static gboolean
8217 should_respond_after_confirm_overwrite (GtkFileChooserDefault *impl,
8218                                         const gchar           *file_part,
8219                                         GFile                 *parent_file)
8220 {
8221   GtkFileChooserConfirmation conf;
8222
8223   if (!impl->do_overwrite_confirmation)
8224     return TRUE;
8225
8226   conf = GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM;
8227
8228   g_signal_emit_by_name (impl, "confirm-overwrite", &conf);
8229
8230   switch (conf)
8231     {
8232     case GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM:
8233       {
8234         struct GetDisplayNameData *data;
8235
8236         g_assert (file_part != NULL);
8237
8238         data = g_new0 (struct GetDisplayNameData, 1);
8239         data->impl = g_object_ref (impl);
8240         data->file_part = g_strdup (file_part);
8241
8242         if (impl->should_respond_get_info_cancellable)
8243           g_cancellable_cancel (impl->should_respond_get_info_cancellable);
8244
8245         impl->should_respond_get_info_cancellable =
8246           _gtk_file_system_get_info (impl->file_system, parent_file,
8247                                      "standard::display-name",
8248                                      confirmation_confirm_get_info_cb,
8249                                      data);
8250         set_busy_cursor (data->impl, TRUE);
8251         return FALSE;
8252       }
8253
8254     case GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME:
8255       return TRUE;
8256
8257     case GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN:
8258       return FALSE;
8259
8260     default:
8261       g_assert_not_reached ();
8262       return FALSE;
8263     }
8264 }
8265
8266 struct FileExistsData
8267 {
8268   GtkFileChooserDefault *impl;
8269   gboolean file_exists_and_is_not_folder;
8270   GFile *parent_file;
8271   GFile *file;
8272 };
8273
8274 static void
8275 name_entry_get_parent_info_cb (GCancellable *cancellable,
8276                                GFileInfo    *info,
8277                                const GError *error,
8278                                gpointer      user_data)
8279 {
8280   gboolean parent_is_folder;
8281   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
8282   struct FileExistsData *data = user_data;
8283
8284   if (cancellable != data->impl->should_respond_get_info_cancellable)
8285     goto out;
8286
8287   data->impl->should_respond_get_info_cancellable = NULL;
8288
8289   set_busy_cursor (data->impl, FALSE);
8290
8291   if (cancelled)
8292     goto out;
8293
8294   if (!info)
8295     parent_is_folder = FALSE;
8296   else
8297     parent_is_folder = _gtk_file_info_consider_as_directory (info);
8298
8299   if (parent_is_folder)
8300     {
8301       if (data->impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
8302         {
8303           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) */
8304         }
8305       else if (data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8306         {
8307           if (data->file_exists_and_is_not_folder)
8308             {
8309               gboolean retval;
8310               char *file_part;
8311
8312               /* Dup the string because the string may be modified
8313                * depending on what clients do in the confirm-overwrite
8314                * signal and this corrupts the pointer
8315                */
8316               file_part = g_strdup (_gtk_file_chooser_entry_get_file_part (GTK_FILE_CHOOSER_ENTRY (data->impl->location_entry)));
8317               retval = should_respond_after_confirm_overwrite (data->impl, file_part, data->parent_file);
8318               g_free (file_part);
8319
8320               if (retval)
8321                 g_signal_emit_by_name (data->impl, "response-requested");
8322             }
8323           else
8324             g_signal_emit_by_name (data->impl, "response-requested");
8325         }
8326       else if (data->impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
8327                || data->impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8328         {
8329           GError *mkdir_error = NULL;
8330
8331           /* In both cases (SELECT_FOLDER and CREATE_FOLDER), if you type
8332            * "/blah/nonexistent" you *will* want a folder created.
8333            */
8334
8335           set_busy_cursor (data->impl, TRUE);
8336           g_file_make_directory (data->file, NULL, &mkdir_error);
8337           set_busy_cursor (data->impl, FALSE);
8338
8339           if (!mkdir_error)
8340             g_signal_emit_by_name (data->impl, "response-requested");
8341           else
8342             error_creating_folder_dialog (data->impl, data->file, mkdir_error);
8343         }
8344       else
8345         g_assert_not_reached ();
8346     }
8347   else
8348     {
8349       if (info)
8350         {
8351           /* The parent exists, but it's not a folder!  Someone probably typed existing_file.txt/subfile.txt */
8352           error_with_file_under_nonfolder (data->impl, data->parent_file);
8353         }
8354       else
8355         {
8356           GError *error_copy;
8357
8358           /* The parent folder is not readable for some reason */
8359
8360           error_copy = g_error_copy (error);
8361           error_changing_folder_dialog (data->impl, data->parent_file, error_copy);
8362         }
8363     }
8364
8365 out:
8366   g_object_unref (data->impl);
8367   g_object_unref (data->file);
8368   g_object_unref (data->parent_file);
8369   g_free (data);
8370
8371   g_object_unref (cancellable);
8372 }
8373
8374 static void
8375 file_exists_get_info_cb (GCancellable *cancellable,
8376                          GFileInfo    *info,
8377                          const GError *error,
8378                          gpointer      user_data)
8379 {
8380   gboolean data_ownership_taken = FALSE;
8381   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
8382   gboolean file_exists;
8383   gboolean is_folder;
8384   gboolean needs_parent_check = FALSE;
8385   struct FileExistsData *data = user_data;
8386
8387   if (cancellable != data->impl->file_exists_get_info_cancellable)
8388     goto out;
8389
8390   data->impl->file_exists_get_info_cancellable = NULL;
8391
8392   set_busy_cursor (data->impl, FALSE);
8393
8394   if (cancelled)
8395     goto out;
8396
8397   file_exists = (info != NULL);
8398   is_folder = (file_exists && _gtk_file_info_consider_as_directory (info));
8399
8400   if (data->impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
8401     {
8402       if (is_folder)
8403         change_folder_and_display_error (data->impl, data->file, TRUE);
8404       else
8405         {
8406           if (file_exists)
8407             g_signal_emit_by_name (data->impl, "response-requested"); /* user typed an existing filename; we are done */
8408           else
8409             needs_parent_check = TRUE; /* file doesn't exist; see if its parent exists */
8410         }
8411     }
8412   else if (data->impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8413     {
8414       if (file_exists && !is_folder)
8415         {
8416           /* Oops, the user typed the name of an existing path which is not
8417            * a folder
8418            */
8419           error_creating_folder_over_existing_file_dialog (data->impl, data->file,
8420                                                            g_error_copy (error));
8421         }
8422       else
8423         {
8424           needs_parent_check = TRUE;
8425         }
8426     }
8427   else if (data->impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8428     {
8429       if (!file_exists)
8430         {
8431           needs_parent_check = TRUE;
8432         }
8433       else
8434         {
8435           if (is_folder)
8436             {
8437               /* User typed a folder; we are done */
8438               g_signal_emit_by_name (data->impl, "response-requested");
8439             }
8440           else
8441             error_selecting_folder_over_existing_file_dialog (data->impl, data->file);
8442         }
8443     }
8444   else if (data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8445     {
8446       if (is_folder)
8447         change_folder_and_display_error (data->impl, data->file, TRUE);
8448       else
8449         needs_parent_check = TRUE;
8450     }
8451   else
8452     {
8453       g_assert_not_reached();
8454     }
8455
8456   if (needs_parent_check) {
8457     /* check that everything up to the last path component exists (i.e. the parent) */
8458
8459     data->file_exists_and_is_not_folder = file_exists && !is_folder;
8460     data_ownership_taken = TRUE;
8461
8462     if (data->impl->should_respond_get_info_cancellable)
8463       g_cancellable_cancel (data->impl->should_respond_get_info_cancellable);
8464
8465       data->impl->should_respond_get_info_cancellable =
8466         _gtk_file_system_get_info (data->impl->file_system,
8467                                    data->parent_file,
8468                                    "standard::type",
8469                                    name_entry_get_parent_info_cb,
8470                                    data);
8471       set_busy_cursor (data->impl, TRUE);
8472     }
8473
8474 out:
8475   if (!data_ownership_taken)
8476     {
8477       g_object_unref (data->impl);
8478       g_object_unref (data->file);
8479       g_object_unref (data->parent_file);
8480       g_free (data);
8481     }
8482
8483   g_object_unref (cancellable);
8484 }
8485
8486 static void
8487 paste_text_received (GtkClipboard          *clipboard,
8488                      const gchar           *text,
8489                      GtkFileChooserDefault *impl)
8490 {
8491   GFile *file;
8492
8493   if (!text)
8494     return;
8495
8496   file = g_file_new_for_uri (text);
8497
8498   if (!gtk_file_chooser_default_select_file (GTK_FILE_CHOOSER (impl), file, NULL))
8499     location_popup_handler (impl, text);
8500
8501   g_object_unref (file);
8502 }
8503
8504 /* Handler for the "location-popup-on-paste" keybinding signal */
8505 static void
8506 location_popup_on_paste_handler (GtkFileChooserDefault *impl)
8507 {
8508   GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (impl),
8509                                                       GDK_SELECTION_CLIPBOARD);
8510   gtk_clipboard_request_text (clipboard,
8511                               (GtkClipboardTextReceivedFunc) paste_text_received,
8512                               impl);
8513 }
8514
8515
8516 /* Implementation for GtkFileChooserEmbed::should_respond() */
8517 static gboolean
8518 gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
8519 {
8520   GtkFileChooserDefault *impl;
8521   GtkWidget *toplevel;
8522   GtkWidget *current_focus;
8523
8524   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
8525
8526   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
8527   g_assert (GTK_IS_WINDOW (toplevel));
8528
8529   current_focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
8530
8531   if (current_focus == impl->browse_files_tree_view)
8532     {
8533       /* The following array encodes what we do based on the impl->action and the
8534        * number of files selected.
8535        */
8536       typedef enum {
8537         NOOP,                   /* Do nothing (don't respond) */
8538         RESPOND,                /* Respond immediately */
8539         RESPOND_OR_SWITCH,      /* Respond immediately if the selected item is a file; switch to it if it is a folder */
8540         ALL_FILES,              /* Respond only if everything selected is a file */
8541         ALL_FOLDERS,            /* Respond only if everything selected is a folder */
8542         SAVE_ENTRY,             /* Go to the code for handling the save entry */
8543         NOT_REACHED             /* Sanity check */
8544       } ActionToTake;
8545       static const ActionToTake what_to_do[4][3] = {
8546         /*                                0 selected            1 selected              many selected */
8547         /* ACTION_OPEN */               { NOOP,                 RESPOND_OR_SWITCH,      ALL_FILES   },
8548         /* ACTION_SAVE */               { SAVE_ENTRY,           RESPOND_OR_SWITCH,      NOT_REACHED },
8549         /* ACTION_SELECT_FOLDER */      { RESPOND,              ALL_FOLDERS,            ALL_FOLDERS },
8550         /* ACTION_CREATE_FOLDER */      { SAVE_ENTRY,           ALL_FOLDERS,            NOT_REACHED }
8551       };
8552
8553       int num_selected;
8554       gboolean all_files, all_folders;
8555       int k;
8556       ActionToTake action;
8557
8558     file_list:
8559
8560       g_assert (impl->action >= GTK_FILE_CHOOSER_ACTION_OPEN && impl->action <= GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
8561
8562       if (impl->operation_mode == OPERATION_MODE_SEARCH)
8563         return search_should_respond (impl);
8564
8565       if (impl->operation_mode == OPERATION_MODE_RECENT)
8566         return recent_should_respond (impl);
8567
8568       selection_check (impl, &num_selected, &all_files, &all_folders);
8569
8570       if (num_selected > 2)
8571         k = 2;
8572       else
8573         k = num_selected;
8574
8575       action = what_to_do [impl->action] [k];
8576
8577       switch (action)
8578         {
8579         case NOOP:
8580           return FALSE;
8581
8582         case RESPOND:
8583           return TRUE;
8584
8585         case RESPOND_OR_SWITCH:
8586           g_assert (num_selected == 1);
8587
8588           if (all_folders)
8589             {
8590               switch_to_selected_folder (impl);
8591               return FALSE;
8592             }
8593           else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8594             return should_respond_after_confirm_overwrite (impl,
8595                                                            get_display_name_from_file_list (impl),
8596                                                            impl->current_folder);
8597           else
8598             return TRUE;
8599
8600         case ALL_FILES:
8601           return all_files;
8602
8603         case ALL_FOLDERS:
8604           return all_folders;
8605
8606         case SAVE_ENTRY:
8607           goto save_entry;
8608
8609         default:
8610           g_assert_not_reached ();
8611         }
8612     }
8613   else if ((impl->location_entry != NULL) && (current_focus == impl->location_entry))
8614     {
8615       GFile *file;
8616       gboolean is_well_formed, is_empty, is_file_part_empty;
8617       gboolean is_folder;
8618       gboolean retval;
8619       GtkFileChooserEntry *entry;
8620       GError *error;
8621
8622     save_entry:
8623
8624       g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
8625                 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
8626                 || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
8627                      || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8628                     && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY));
8629
8630       entry = GTK_FILE_CHOOSER_ENTRY (impl->location_entry);
8631       check_save_entry (impl, &file, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder);
8632
8633       if (!is_well_formed)
8634         return FALSE;
8635
8636       if (is_empty)
8637         {
8638           if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
8639               || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8640             return FALSE;
8641
8642           goto file_list;
8643         }
8644
8645       g_assert (file != NULL);
8646
8647       error = NULL;
8648       if (is_folder)
8649         {
8650           if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
8651               impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8652             {
8653               change_folder_and_display_error (impl, file, TRUE);
8654               retval = FALSE;
8655             }
8656           else if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
8657                    impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8658             {
8659               /* The folder already exists, so we do not need to create it.
8660                * Just respond to terminate the dialog.
8661                */
8662               retval = TRUE;
8663             }
8664           else
8665             {
8666               g_assert_not_reached ();
8667               retval = FALSE;
8668             }
8669         }
8670       else
8671         {
8672           struct FileExistsData *data;
8673
8674           /* We need to check whether file exists and whether it is a folder -
8675            * the GtkFileChooserEntry *does* report is_folder==FALSE as a false
8676            * negative (it doesn't know yet if your last path component is a
8677            * folder).
8678            */
8679
8680           data = g_new0 (struct FileExistsData, 1);
8681           data->impl = g_object_ref (impl);
8682           data->file = g_object_ref (file);
8683           data->parent_file = g_object_ref (_gtk_file_chooser_entry_get_current_folder (entry));
8684
8685           if (impl->file_exists_get_info_cancellable)
8686             g_cancellable_cancel (impl->file_exists_get_info_cancellable);
8687
8688           impl->file_exists_get_info_cancellable =
8689             _gtk_file_system_get_info (impl->file_system, file,
8690                                        "standard::type",
8691                                        file_exists_get_info_cb,
8692                                        data);
8693
8694           set_busy_cursor (impl, TRUE);
8695           retval = FALSE;
8696
8697           if (error != NULL)
8698             g_error_free (error);
8699         }
8700
8701       g_object_unref (file);
8702       return retval;
8703     }
8704   else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
8705     {
8706       /* The focus is on a dialog's action area button, *and* the widget that
8707        * was focused immediately before it is the file list.  
8708        */
8709       goto file_list;
8710     }
8711   else if (impl->operation_mode == OPERATION_MODE_SEARCH && impl->toplevel_last_focus_widget == impl->search_entry)
8712     {
8713       search_entry_activate_cb (GTK_ENTRY (impl->search_entry), impl);
8714       return FALSE;
8715     }
8716   else if (impl->location_entry && impl->toplevel_last_focus_widget == impl->location_entry)
8717     {
8718       /* The focus is on a dialog's action area button, *and* the widget that
8719        * was focused immediately before it is the location entry.
8720        */
8721       goto save_entry;
8722     }
8723   else
8724     /* The focus is on a dialog's action area button or something else */
8725     if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
8726         || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8727       goto save_entry;
8728     else
8729       goto file_list; 
8730   
8731   g_assert_not_reached ();
8732   return FALSE;
8733 }
8734
8735 /* Implementation for GtkFileChooserEmbed::initial_focus() */
8736 static void
8737 gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed)
8738 {
8739   GtkFileChooserDefault *impl;
8740   GtkWidget *widget;
8741
8742   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
8743
8744   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
8745       impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8746     {
8747       if (impl->location_mode == LOCATION_MODE_PATH_BAR)
8748         widget = impl->browse_files_tree_view;
8749       else
8750         widget = impl->location_entry;
8751     }
8752   else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
8753            impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8754     widget = impl->location_entry;
8755   else
8756     {
8757       g_assert_not_reached ();
8758       widget = NULL;
8759     }
8760
8761   g_assert (widget != NULL);
8762   gtk_widget_grab_focus (widget);
8763 }
8764
8765 /* Callback used from gtk_tree_selection_selected_foreach(); gets the selected GFiles */
8766 static void
8767 search_selected_foreach_get_file_cb (GtkTreeModel *model,
8768                                      GtkTreePath  *path,
8769                                      GtkTreeIter  *iter,
8770                                      gpointer      data)
8771 {
8772   GSList **list;
8773   GFile *file;
8774
8775   list = data;
8776
8777   gtk_tree_model_get (model, iter, MODEL_COL_FILE, &file, -1);
8778   *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 */
8779 }
8780
8781 /* Constructs a list of the selected paths in search mode */
8782 static GSList *
8783 search_get_selected_files (GtkFileChooserDefault *impl)
8784 {
8785   GSList *result;
8786   GtkTreeSelection *selection;
8787
8788   result = NULL;
8789
8790   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
8791   gtk_tree_selection_selected_foreach (selection, search_selected_foreach_get_file_cb, &result);
8792   result = g_slist_reverse (result);
8793
8794   return result;
8795 }
8796
8797 /* Called from ::should_respond().  We return whether there are selected files
8798  * in the search list.
8799  */
8800 static gboolean
8801 search_should_respond (GtkFileChooserDefault *impl)
8802 {
8803   GtkTreeSelection *selection;
8804
8805   g_assert (impl->operation_mode == OPERATION_MODE_SEARCH);
8806
8807   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
8808   return (gtk_tree_selection_count_selected_rows (selection) != 0);
8809 }
8810
8811 /* Adds one hit from the search engine to the search_model */
8812 static void
8813 search_add_hit (GtkFileChooserDefault *impl,
8814                 gchar                 *uri)
8815 {
8816   GFile *file;
8817
8818   file = g_file_new_for_uri (uri);
8819   if (!file)
8820     return;
8821
8822   if (!g_file_is_native (file))
8823     {
8824       g_object_unref (file);
8825       return;
8826     }
8827
8828   _gtk_file_system_model_add_and_query_file (impl->search_model,
8829                                              file,
8830                                              MODEL_ATTRIBUTES);
8831
8832   g_object_unref (file);
8833 }
8834
8835 /* Callback used from GtkSearchEngine when we get new hits */
8836 static void
8837 search_engine_hits_added_cb (GtkSearchEngine *engine,
8838                              GList           *hits,
8839                              gpointer         data)
8840 {
8841   GtkFileChooserDefault *impl;
8842   GList *l;
8843   
8844   impl = GTK_FILE_CHOOSER_DEFAULT (data);
8845
8846   for (l = hits; l; l = l->next)
8847     search_add_hit (impl, (gchar*)l->data);
8848 }
8849
8850 /* Callback used from GtkSearchEngine when the query is done running */
8851 static void
8852 search_engine_finished_cb (GtkSearchEngine *engine,
8853                            gpointer         data)
8854 {
8855   GtkFileChooserDefault *impl;
8856   
8857   impl = GTK_FILE_CHOOSER_DEFAULT (data);
8858   
8859 #if 0
8860   /* EB: setting the model here will avoid loads of row events,
8861    * but it'll make the search look like blocked.
8862    */
8863   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
8864                            GTK_TREE_MODEL (impl->search_model));
8865 #endif
8866
8867   /* FMQ: if search was empty, say that we got no hits */
8868   set_busy_cursor (impl, FALSE);
8869 }
8870
8871 /* Displays a generic error when we cannot create a GtkSearchEngine.  
8872  * It would be better if _gtk_search_engine_new() gave us a GError 
8873  * with a better message, but it doesn't do that right now.
8874  */
8875 static void
8876 search_error_could_not_create_client (GtkFileChooserDefault *impl)
8877 {
8878   error_message (impl,
8879                  _("Could not start the search process"),
8880                  _("The program was not able to create a connection to the indexer "
8881                    "daemon.  Please make sure it is running."));
8882 }
8883
8884 static void
8885 search_engine_error_cb (GtkSearchEngine *engine,
8886                         const gchar     *message,
8887                         gpointer         data)
8888 {
8889   GtkFileChooserDefault *impl;
8890   
8891   impl = GTK_FILE_CHOOSER_DEFAULT (data);
8892
8893   search_stop_searching (impl, TRUE);
8894   error_message (impl, _("Could not send the search request"), message);
8895
8896   set_busy_cursor (impl, FALSE);
8897 }
8898
8899 /* Frees the data in the search_model */
8900 static void
8901 search_clear_model (GtkFileChooserDefault *impl, 
8902                     gboolean               remove_from_treeview)
8903 {
8904   if (!impl->search_model)
8905     return;
8906
8907   g_object_unref (impl->search_model);
8908   impl->search_model = NULL;
8909   
8910   if (remove_from_treeview)
8911     gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
8912 }
8913
8914 /* Stops any ongoing searches; does not touch the search_model */
8915 static void
8916 search_stop_searching (GtkFileChooserDefault *impl,
8917                        gboolean               remove_query)
8918 {
8919   if (remove_query && impl->search_query)
8920     {
8921       g_object_unref (impl->search_query);
8922       impl->search_query = NULL;
8923     }
8924   
8925   if (impl->search_engine)
8926     {
8927       _gtk_search_engine_stop (impl->search_engine);
8928       
8929       g_object_unref (impl->search_engine);
8930       impl->search_engine = NULL;
8931     }
8932 }
8933
8934 /* Stops any pending searches, clears the file list, and switches back to OPERATION_MODE_BROWSE */
8935 static void
8936 search_switch_to_browse_mode (GtkFileChooserDefault *impl)
8937 {
8938   g_assert (impl->operation_mode != OPERATION_MODE_BROWSE);
8939
8940   search_stop_searching (impl, FALSE);
8941   search_clear_model (impl, TRUE);
8942
8943   gtk_widget_destroy (impl->search_hbox);
8944   impl->search_hbox = NULL;
8945   impl->search_entry = NULL;
8946
8947   gtk_widget_show (impl->browse_path_bar);
8948   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN || !impl->create_folders)
8949     gtk_widget_hide (impl->browse_new_folder_button);
8950   else
8951     gtk_widget_show (impl->browse_new_folder_button);
8952
8953
8954   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
8955       impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8956     {
8957       gtk_widget_show (impl->location_button);
8958
8959       if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
8960         gtk_widget_show (impl->location_entry_box);
8961     }
8962
8963   impl->operation_mode = OPERATION_MODE_BROWSE;
8964
8965   file_list_set_sort_column_ids (impl);
8966 }
8967
8968 /* Creates the search_model and puts it in the tree view */
8969 static void
8970 search_setup_model (GtkFileChooserDefault *impl)
8971 {
8972   g_assert (impl->search_model == NULL);
8973
8974   impl->search_model = _gtk_file_system_model_new (file_system_model_set,
8975                                                    impl,
8976                                                    MODEL_COLUMN_TYPES);
8977
8978   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model),
8979                                    MODEL_COL_NAME,
8980                                    name_sort_func,
8981                                    impl, NULL);
8982   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model),
8983                                    MODEL_COL_MTIME,
8984                                    mtime_sort_func,
8985                                    impl, NULL);
8986   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model),
8987                                    MODEL_COL_SIZE,
8988                                    size_sort_func,
8989                                    impl, NULL);
8990   set_sort_column (impl);
8991
8992   /* EB: setting the model here will make the hits list update feel
8993    * more "alive" than setting the model at the end of the search
8994    * run
8995    */
8996   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
8997                            GTK_TREE_MODEL (impl->search_model));
8998 }
8999
9000 /* Creates a new query with the specified text and launches it */
9001 static void
9002 search_start_query (GtkFileChooserDefault *impl,
9003                     const gchar           *query_text)
9004 {
9005   search_stop_searching (impl, FALSE);
9006   search_clear_model (impl, TRUE);
9007   search_setup_model (impl);
9008   set_busy_cursor (impl, TRUE);
9009
9010   if (impl->search_engine == NULL)
9011     impl->search_engine = _gtk_search_engine_new ();
9012
9013   if (!impl->search_engine)
9014     {
9015       set_busy_cursor (impl, FALSE);
9016       search_error_could_not_create_client (impl); /* lame; we don't get an error code or anything */
9017       return;
9018     }
9019
9020   if (!impl->search_query)
9021     {
9022       impl->search_query = _gtk_query_new ();
9023       _gtk_query_set_text (impl->search_query, query_text);
9024     }
9025   
9026   _gtk_search_engine_set_query (impl->search_engine, impl->search_query);
9027
9028   g_signal_connect (impl->search_engine, "hits-added",
9029                     G_CALLBACK (search_engine_hits_added_cb), impl);
9030   g_signal_connect (impl->search_engine, "finished",
9031                     G_CALLBACK (search_engine_finished_cb), impl);
9032   g_signal_connect (impl->search_engine, "error",
9033                     G_CALLBACK (search_engine_error_cb), impl);
9034
9035   _gtk_search_engine_start (impl->search_engine);
9036 }
9037
9038 /* Callback used when the user presses Enter while typing on the search
9039  * entry; starts the query
9040  */
9041 static void
9042 search_entry_activate_cb (GtkEntry *entry,
9043                           gpointer data)
9044 {
9045   GtkFileChooserDefault *impl;
9046   const char *text;
9047
9048   impl = GTK_FILE_CHOOSER_DEFAULT (data);
9049
9050   text = gtk_entry_get_text (GTK_ENTRY (impl->search_entry));
9051   if (strlen (text) == 0)
9052     return;
9053
9054   /* reset any existing query object */
9055   if (impl->search_query)
9056     {
9057       g_object_unref (impl->search_query);
9058       impl->search_query = NULL;
9059     }
9060
9061   search_start_query (impl, text);
9062 }
9063
9064 static gboolean
9065 focus_entry_idle_cb (GtkFileChooserDefault *impl)
9066 {
9067   GDK_THREADS_ENTER ();
9068   
9069   g_source_destroy (impl->focus_entry_idle);
9070   impl->focus_entry_idle = NULL;
9071
9072   if (impl->search_entry)
9073     gtk_widget_grab_focus (impl->search_entry);
9074
9075   GDK_THREADS_LEAVE ();
9076
9077   return FALSE;
9078 }
9079
9080 static void
9081 focus_search_entry_in_idle (GtkFileChooserDefault *impl)
9082 {
9083   /* bgo#634558 - When the user clicks on the Search entry in the shortcuts
9084    * pane, we get a selection-changed signal and we set up the search widgets.
9085    * However, gtk_tree_view_button_press() focuses the treeview *after* making
9086    * the change to the selection.  So, we need to re-focus the search entry
9087    * after the treeview has finished doing its work; we'll do that in an idle
9088    * handler.
9089    */
9090
9091   if (!impl->focus_entry_idle)
9092     impl->focus_entry_idle = add_idle_while_impl_is_alive (impl, G_CALLBACK (focus_entry_idle_cb));
9093 }
9094
9095 /* Hides the path bar and creates the search entry */
9096 static void
9097 search_setup_widgets (GtkFileChooserDefault *impl)
9098 {
9099   GtkWidget *label;
9100   GtkWidget *image;
9101   gchar *tmp;
9102
9103   impl->search_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
9104
9105   /* Image */
9106
9107   image = gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_BUTTON);
9108   gtk_box_pack_start (GTK_BOX (impl->search_hbox), image, FALSE, FALSE, 5);
9109
9110   /* Label */
9111
9112   label = gtk_label_new (NULL);
9113   tmp = g_strdup_printf ("<b>%s</b>", _("Search:"));
9114   gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), tmp);
9115   gtk_box_pack_start (GTK_BOX (impl->search_hbox), label, FALSE, FALSE, 0);
9116   g_free (tmp);
9117
9118   /* Entry */
9119
9120   impl->search_entry = gtk_entry_new ();
9121   gtk_label_set_mnemonic_widget (GTK_LABEL (label), impl->search_entry);
9122   g_signal_connect (impl->search_entry, "activate",
9123                     G_CALLBACK (search_entry_activate_cb),
9124                     impl);
9125   gtk_box_pack_start (GTK_BOX (impl->search_hbox), impl->search_entry, TRUE, TRUE, 0);
9126
9127   /* if there already is a query, restart it */
9128   if (impl->search_query)
9129     {
9130       gchar *query = _gtk_query_get_text (impl->search_query);
9131
9132       if (query)
9133         {
9134           gtk_entry_set_text (GTK_ENTRY (impl->search_entry), query);
9135           search_start_query (impl, query);
9136
9137           g_free (query);
9138         }
9139       else
9140         {
9141           g_object_unref (impl->search_query);
9142           impl->search_query = NULL;
9143         }
9144     }
9145
9146   gtk_widget_hide (impl->browse_path_bar);
9147   gtk_widget_hide (impl->browse_new_folder_button);
9148
9149   /* Box for search widgets */
9150   gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->search_hbox, TRUE, TRUE, 0);
9151   gtk_widget_show_all (impl->search_hbox);
9152   gtk_size_group_add_widget (GTK_SIZE_GROUP (impl->browse_path_bar_size_group), impl->search_hbox);
9153
9154   /* Hide the location widgets temporarily */
9155
9156   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
9157       impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
9158     {
9159       gtk_widget_hide (impl->location_button);
9160       gtk_widget_hide (impl->location_entry_box);
9161     }
9162
9163   focus_search_entry_in_idle (impl);
9164
9165   /* FMQ: hide the filter combo? */
9166 }
9167
9168 /* Stops running operations like populating the browse model, searches, and the recent-files model */
9169 static void
9170 stop_operation (GtkFileChooserDefault *impl, OperationMode mode)
9171 {
9172   switch (mode)
9173     {
9174     case OPERATION_MODE_BROWSE:
9175       stop_loading_and_clear_list_model (impl, TRUE);
9176       break;
9177
9178     case OPERATION_MODE_SEARCH:
9179       search_stop_searching (impl, FALSE);
9180       search_clear_model (impl, TRUE);
9181
9182       gtk_widget_destroy (impl->search_hbox);
9183       impl->search_hbox = NULL;
9184       impl->search_entry = NULL;
9185       break;
9186
9187     case OPERATION_MODE_RECENT:
9188       recent_stop_loading (impl);
9189       recent_clear_model (impl, TRUE);
9190
9191       gtk_widget_destroy (impl->recent_hbox);
9192       impl->recent_hbox = NULL;
9193       break;
9194     }
9195 }
9196
9197 /* Main entry point to the searching functions; this gets called when the user
9198  * activates the Search shortcut.
9199  */
9200 static void
9201 search_activate (GtkFileChooserDefault *impl)
9202 {
9203   OperationMode previous_mode;
9204   
9205   if (impl->operation_mode == OPERATION_MODE_SEARCH)
9206     {
9207       focus_search_entry_in_idle (impl);
9208       return;
9209     }
9210
9211   previous_mode = impl->operation_mode;
9212   impl->operation_mode = OPERATION_MODE_SEARCH;
9213
9214   stop_operation (impl, previous_mode);
9215
9216   g_assert (impl->search_hbox == NULL);
9217   g_assert (impl->search_entry == NULL);
9218   g_assert (impl->search_model == NULL);
9219
9220   search_setup_widgets (impl);
9221   file_list_set_sort_column_ids (impl);
9222 }
9223
9224 /*
9225  * Recent files support
9226  */
9227
9228 /* Frees the data in the recent_model */
9229 static void
9230 recent_clear_model (GtkFileChooserDefault *impl,
9231                     gboolean               remove_from_treeview)
9232 {
9233   if (!impl->recent_model)
9234     return;
9235
9236   if (remove_from_treeview)
9237     gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
9238
9239   g_object_unref (impl->recent_model);
9240   impl->recent_model = NULL;
9241 }
9242
9243 /* Stops any ongoing loading of the recent files list; does
9244  * not touch the recent_model
9245  */
9246 static void
9247 recent_stop_loading (GtkFileChooserDefault *impl)
9248 {
9249   if (impl->load_recent_id)
9250     {
9251       g_source_remove (impl->load_recent_id);
9252       impl->load_recent_id = 0;
9253     }
9254 }
9255
9256 /* Stops any pending load, clears the file list, and switches
9257  * back to OPERATION_MODE_BROWSE
9258  */
9259 static void
9260 recent_switch_to_browse_mode (GtkFileChooserDefault *impl)
9261 {
9262   g_assert (impl->operation_mode != OPERATION_MODE_BROWSE);
9263
9264   recent_stop_loading (impl);
9265   recent_clear_model (impl, TRUE);
9266
9267   gtk_widget_destroy (impl->recent_hbox);
9268   impl->recent_hbox = NULL;
9269
9270   gtk_widget_show (impl->browse_path_bar);
9271   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN || !impl->create_folders)
9272     gtk_widget_hide (impl->browse_new_folder_button);
9273   else
9274     gtk_widget_show (impl->browse_new_folder_button);
9275
9276   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
9277       impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
9278     {
9279       gtk_widget_show (impl->location_button);
9280
9281       if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
9282         gtk_widget_show (impl->location_entry_box);
9283     }
9284
9285   gtk_tree_view_column_set_visible (impl->list_size_column, impl->show_size_column);
9286
9287   impl->operation_mode = OPERATION_MODE_BROWSE;
9288
9289   file_list_set_sort_column_ids (impl);
9290 }
9291
9292 static void
9293 recent_setup_model (GtkFileChooserDefault *impl)
9294 {
9295   g_assert (impl->recent_model == NULL);
9296
9297   impl->recent_model = _gtk_file_system_model_new (file_system_model_set,
9298                                                    impl,
9299                                                    MODEL_COLUMN_TYPES);
9300
9301   _gtk_file_system_model_set_filter (impl->recent_model,
9302                                      impl->current_filter);
9303   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model),
9304                                    MODEL_COL_NAME,
9305                                    name_sort_func,
9306                                    impl, NULL);
9307   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model),
9308                                    MODEL_COL_SIZE,
9309                                    size_sort_func,
9310                                    impl, NULL);
9311   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model),
9312                                    MODEL_COL_MTIME,
9313                                    mtime_sort_func,
9314                                    impl, NULL);
9315   set_sort_column (impl);
9316 }
9317
9318 typedef struct
9319 {
9320   GtkFileChooserDefault *impl;
9321   GList *items;
9322   gint n_items;
9323   gint n_loaded_items;
9324   guint needs_sorting : 1;
9325 } RecentLoadData;
9326
9327 static void
9328 recent_idle_cleanup (gpointer data)
9329 {
9330   RecentLoadData *load_data = data;
9331   GtkFileChooserDefault *impl = load_data->impl;
9332
9333   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
9334                            GTK_TREE_MODEL (impl->recent_model));
9335
9336   set_busy_cursor (impl, FALSE);
9337   
9338   impl->load_recent_id = 0;
9339   
9340   if (load_data->items)
9341     {
9342       g_list_foreach (load_data->items, (GFunc) gtk_recent_info_unref, NULL);
9343       g_list_free (load_data->items);
9344     }
9345
9346   g_free (load_data);
9347 }
9348
9349 static gint
9350 recent_sort_mru (gconstpointer a,
9351                  gconstpointer b)
9352 {
9353   GtkRecentInfo *info_a = (GtkRecentInfo *) a;
9354   GtkRecentInfo *info_b = (GtkRecentInfo *) b;
9355
9356   return (gtk_recent_info_get_modified (info_b) - gtk_recent_info_get_modified (info_a));
9357 }
9358
9359 static gint
9360 get_recent_files_limit (GtkWidget *widget)
9361 {
9362   GtkSettings *settings;
9363   gint limit;
9364
9365   if (gtk_widget_has_screen (widget))
9366     settings = gtk_settings_get_for_screen (gtk_widget_get_screen (widget));
9367   else
9368     settings = gtk_settings_get_default ();
9369
9370   g_object_get (G_OBJECT (settings), "gtk-recent-files-limit", &limit, NULL);
9371
9372   return limit;
9373 }
9374
9375 static gboolean
9376 recent_idle_load (gpointer data)
9377 {
9378   RecentLoadData *load_data = data;
9379   GtkFileChooserDefault *impl = load_data->impl;
9380   GList *walk;
9381   GFile *file;
9382
9383   if (!impl->recent_manager)
9384     return FALSE;
9385
9386   /* first iteration: load all the items */
9387   if (!load_data->items)
9388     {
9389       load_data->items = gtk_recent_manager_get_items (impl->recent_manager);
9390       if (!load_data->items)
9391         return FALSE;
9392
9393       load_data->needs_sorting = TRUE;
9394
9395       return TRUE;
9396     }
9397   
9398   /* second iteration: preliminary MRU sorting and clamping */
9399   if (load_data->needs_sorting)
9400     {
9401       gint limit;
9402
9403       load_data->items = g_list_sort (load_data->items, recent_sort_mru);
9404       load_data->n_items = g_list_length (load_data->items);
9405
9406       limit = get_recent_files_limit (GTK_WIDGET (impl));
9407       
9408       if (limit != -1 && (load_data->n_items > limit))
9409         {
9410           GList *clamp, *l;
9411
9412           clamp = g_list_nth (load_data->items, limit - 1);
9413           if (G_LIKELY (clamp))
9414             {
9415               l = clamp->next;
9416               clamp->next = NULL;
9417
9418               g_list_foreach (l, (GFunc) gtk_recent_info_unref, NULL);
9419               g_list_free (l);
9420
9421               load_data->n_items = limit;
9422             }
9423          }
9424
9425       load_data->n_loaded_items = 0;
9426       load_data->needs_sorting = FALSE;
9427
9428       return TRUE;
9429     }
9430
9431   /* finished loading items */
9432   for (walk = load_data->items; walk; walk = walk->next)
9433     {
9434       GtkRecentInfo *info = walk->data;
9435       file = g_file_new_for_uri (gtk_recent_info_get_uri (info));
9436
9437       _gtk_file_system_model_add_and_query_file (impl->recent_model,
9438                                                  file,
9439                                                  MODEL_ATTRIBUTES);
9440       gtk_recent_info_unref (walk->data);
9441       g_object_unref (file);
9442     }
9443
9444   g_list_free (load_data->items);
9445   load_data->items = NULL;
9446
9447   return FALSE;
9448 }
9449
9450 static void
9451 recent_start_loading (GtkFileChooserDefault *impl)
9452 {
9453   RecentLoadData *load_data;
9454
9455   recent_stop_loading (impl);
9456   recent_clear_model (impl, TRUE);
9457   recent_setup_model (impl);
9458   set_busy_cursor (impl, TRUE);
9459
9460   g_assert (impl->load_recent_id == 0);
9461
9462   load_data = g_new (RecentLoadData, 1);
9463   load_data->impl = impl;
9464   load_data->items = NULL;
9465   load_data->n_items = 0;
9466   load_data->n_loaded_items = 0;
9467   load_data->needs_sorting = TRUE;
9468
9469   /* begin lazy loading the recent files into the model */
9470   impl->load_recent_id = gdk_threads_add_idle_full (G_PRIORITY_HIGH_IDLE + 30,
9471                                                     recent_idle_load,
9472                                                     load_data,
9473                                                     recent_idle_cleanup);
9474 }
9475
9476 static void
9477 recent_selected_foreach_get_file_cb (GtkTreeModel *model,
9478                                      GtkTreePath  *path,
9479                                      GtkTreeIter  *iter,
9480                                      gpointer      data)
9481 {
9482   GSList **list;
9483   GFile *file;
9484
9485   list = data;
9486
9487   gtk_tree_model_get (model, iter, MODEL_COL_FILE, &file, -1);
9488   *list = g_slist_prepend (*list, file);
9489 }
9490
9491 /* Constructs a list of the selected paths in recent files mode */
9492 static GSList *
9493 recent_get_selected_files (GtkFileChooserDefault *impl)
9494 {
9495   GSList *result;
9496   GtkTreeSelection *selection;
9497
9498   result = NULL;
9499
9500   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
9501   gtk_tree_selection_selected_foreach (selection, recent_selected_foreach_get_file_cb, &result);
9502   result = g_slist_reverse (result);
9503
9504   return result;
9505 }
9506
9507 /* Called from ::should_respond().  We return whether there are selected
9508  * files in the recent files list.
9509  */
9510 static gboolean
9511 recent_should_respond (GtkFileChooserDefault *impl)
9512 {
9513   GtkTreeSelection *selection;
9514
9515   g_assert (impl->operation_mode == OPERATION_MODE_RECENT);
9516
9517   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
9518   return (gtk_tree_selection_count_selected_rows (selection) != 0);
9519 }
9520
9521 /* Hide the location widgets temporarily */
9522 static void
9523 recent_hide_entry (GtkFileChooserDefault *impl)
9524 {
9525   GtkWidget *label;
9526   GtkWidget *image;
9527   gchar *tmp;
9528
9529   impl->recent_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
9530
9531   /* Image */
9532   image = gtk_image_new_from_icon_name ("document-open-recent", GTK_ICON_SIZE_BUTTON);
9533   gtk_box_pack_start (GTK_BOX (impl->recent_hbox), image, FALSE, FALSE, 5);
9534
9535   /* Label */
9536   label = gtk_label_new (NULL);
9537   tmp = g_strdup_printf ("<b>%s</b>", _("Recently Used"));
9538   gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), tmp);
9539   gtk_box_pack_start (GTK_BOX (impl->recent_hbox), label, FALSE, FALSE, 0);
9540   g_free (tmp);
9541
9542   gtk_widget_hide (impl->browse_path_bar);
9543   gtk_widget_hide (impl->browse_new_folder_button);
9544   
9545   /* Box for recent widgets */
9546   gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->recent_hbox, TRUE, TRUE, 0);
9547   gtk_size_group_add_widget (impl->browse_path_bar_size_group, impl->recent_hbox);
9548   gtk_widget_show_all (impl->recent_hbox);
9549
9550   /* Hide the location widgets temporarily */
9551   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
9552       impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
9553     {
9554       gtk_widget_hide (impl->location_button);
9555       gtk_widget_hide (impl->location_entry_box);
9556     }
9557 }
9558
9559 /* Main entry point to the recent files functions; this gets called when
9560  * the user activates the Recently Used shortcut.
9561  */
9562 static void
9563 recent_activate (GtkFileChooserDefault *impl)
9564 {
9565   OperationMode previous_mode;
9566
9567   if (impl->operation_mode == OPERATION_MODE_RECENT)
9568     return;
9569
9570   previous_mode = impl->operation_mode;
9571   impl->operation_mode = OPERATION_MODE_RECENT;
9572
9573   stop_operation (impl, previous_mode);
9574
9575   recent_hide_entry (impl);
9576
9577   file_list_set_sort_column_ids (impl);
9578   recent_start_loading (impl);
9579 }
9580
9581 static void
9582 set_current_filter (GtkFileChooserDefault *impl,
9583                     GtkFileFilter         *filter)
9584 {
9585   if (impl->current_filter != filter)
9586     {
9587       int filter_index;
9588
9589       /* NULL filters are allowed to reset to non-filtered status
9590        */
9591       filter_index = g_slist_index (impl->filters, filter);
9592       if (impl->filters && filter && filter_index < 0)
9593         return;
9594
9595       if (impl->current_filter)
9596         g_object_unref (impl->current_filter);
9597       impl->current_filter = filter;
9598       if (impl->current_filter)
9599         {
9600           g_object_ref_sink (impl->current_filter);
9601         }
9602
9603       if (impl->filters)
9604         gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
9605                                   filter_index);
9606
9607       if (impl->browse_files_model)
9608         install_list_model_filter (impl);
9609
9610       if (impl->search_model)
9611         _gtk_file_system_model_set_filter (impl->search_model, filter);
9612
9613       if (impl->recent_model)
9614         _gtk_file_system_model_set_filter (impl->recent_model, filter);
9615
9616       g_object_notify (G_OBJECT (impl), "filter");
9617     }
9618 }
9619
9620 static void
9621 filter_combo_changed (GtkComboBox           *combo_box,
9622                       GtkFileChooserDefault *impl)
9623 {
9624   gint new_index = gtk_combo_box_get_active (combo_box);
9625   GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
9626
9627   set_current_filter (impl, new_filter);
9628 }
9629
9630 static void
9631 check_preview_change (GtkFileChooserDefault *impl)
9632 {
9633   GtkTreePath *cursor_path;
9634   GFile *new_file;
9635   char *new_display_name;
9636   GtkTreeModel *model;
9637
9638   gtk_tree_view_get_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), &cursor_path, NULL);
9639   model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view));
9640   if (cursor_path)
9641     {
9642       GtkTreeIter iter;
9643
9644       gtk_tree_model_get_iter (model, &iter, cursor_path);
9645       gtk_tree_model_get (model, &iter,
9646                           MODEL_COL_FILE, &new_file,
9647                           MODEL_COL_NAME, &new_display_name,
9648                           -1);
9649       
9650       gtk_tree_path_free (cursor_path);
9651     }
9652   else
9653     {
9654       new_file = NULL;
9655       new_display_name = NULL;
9656     }
9657
9658   if (new_file != impl->preview_file &&
9659       !(new_file && impl->preview_file &&
9660         g_file_equal (new_file, impl->preview_file)))
9661     {
9662       if (impl->preview_file)
9663         {
9664           g_object_unref (impl->preview_file);
9665           g_free (impl->preview_display_name);
9666         }
9667
9668       if (new_file)
9669         {
9670           impl->preview_file = new_file;
9671           impl->preview_display_name = new_display_name;
9672         }
9673       else
9674         {
9675           impl->preview_file = NULL;
9676           impl->preview_display_name = NULL;
9677           g_free (new_display_name);
9678         }
9679
9680       if (impl->use_preview_label && impl->preview_label)
9681         gtk_label_set_text (GTK_LABEL (impl->preview_label), impl->preview_display_name);
9682
9683       g_signal_emit_by_name (impl, "update-preview");
9684     }
9685   else
9686     {
9687       if (new_file)
9688         g_object_unref (new_file);
9689
9690       g_free (new_display_name);
9691     }
9692 }
9693
9694 static void
9695 shortcuts_activate_volume_mount_cb (GCancellable        *cancellable,
9696                                     GtkFileSystemVolume *volume,
9697                                     const GError        *error,
9698                                     gpointer             data)
9699 {
9700   GFile *file;
9701   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
9702   GtkFileChooserDefault *impl = data;
9703
9704   if (cancellable != impl->shortcuts_activate_iter_cancellable)
9705     goto out;
9706
9707   impl->shortcuts_activate_iter_cancellable = NULL;
9708
9709   set_busy_cursor (impl, FALSE);
9710
9711   if (cancelled)
9712     goto out;
9713
9714   if (error)
9715     {
9716       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED))
9717         {
9718           char *msg, *name;
9719
9720           name = _gtk_file_system_volume_get_display_name (volume);
9721           msg = g_strdup_printf (_("Could not mount %s"), name);
9722
9723           error_message (impl, msg, error->message);
9724
9725           g_free (msg);
9726           g_free (name);
9727         }
9728
9729       goto out;
9730     }
9731
9732   file = _gtk_file_system_volume_get_root (volume);
9733   if (file != NULL)
9734     {
9735       change_folder_and_display_error (impl, file, FALSE);
9736       g_object_unref (file);
9737     }
9738
9739 out:
9740   g_object_unref (impl);
9741   g_object_unref (cancellable);
9742 }
9743
9744
9745 /* Activates a volume by mounting it if necessary and then switching to its
9746  * base path.
9747  */
9748 static void
9749 shortcuts_activate_volume (GtkFileChooserDefault *impl,
9750                            GtkFileSystemVolume   *volume)
9751 {
9752   GFile *file;
9753
9754   switch (impl->operation_mode)
9755     {
9756     case OPERATION_MODE_BROWSE:
9757       break;
9758     case OPERATION_MODE_SEARCH:
9759       search_switch_to_browse_mode (impl);
9760       break;
9761     case OPERATION_MODE_RECENT:
9762       recent_switch_to_browse_mode (impl);
9763       break;
9764     }
9765
9766   /* We ref the file chooser since volume_mount() may run a main loop, and the
9767    * user could close the file chooser window in the meantime.
9768    */
9769   g_object_ref (impl);
9770
9771   if (!_gtk_file_system_volume_is_mounted (volume))
9772     {
9773       GMountOperation *mount_op;
9774
9775       set_busy_cursor (impl, TRUE);
9776    
9777       mount_op = gtk_mount_operation_new (get_toplevel (GTK_WIDGET (impl)));
9778       impl->shortcuts_activate_iter_cancellable =
9779         _gtk_file_system_mount_volume (impl->file_system, volume, mount_op,
9780                                        shortcuts_activate_volume_mount_cb,
9781                                        g_object_ref (impl));
9782       g_object_unref (mount_op);
9783     }
9784   else
9785     {
9786       file = _gtk_file_system_volume_get_root (volume);
9787       if (file != NULL)
9788         {
9789           change_folder_and_display_error (impl, file, FALSE);
9790           g_object_unref (file);
9791         }
9792     }
9793
9794   g_object_unref (impl);
9795 }
9796
9797 /* Opens the folder or volume at the specified iter in the shortcuts model */
9798 struct ShortcutsActivateData
9799 {
9800   GtkFileChooserDefault *impl;
9801   GFile *file;
9802 };
9803
9804 static void
9805 shortcuts_activate_get_info_cb (GCancellable *cancellable,
9806                                 GFileInfo    *info,
9807                                 const GError *error,
9808                                 gpointer      user_data)
9809 {
9810   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
9811   struct ShortcutsActivateData *data = user_data;
9812
9813   if (cancellable != data->impl->shortcuts_activate_iter_cancellable)
9814     goto out;
9815
9816   data->impl->shortcuts_activate_iter_cancellable = NULL;
9817
9818   if (cancelled)
9819     goto out;
9820
9821   if (!error && _gtk_file_info_consider_as_directory (info))
9822     change_folder_and_display_error (data->impl, data->file, FALSE);
9823   else
9824     gtk_file_chooser_default_select_file (GTK_FILE_CHOOSER (data->impl),
9825                                           data->file,
9826                                           NULL);
9827
9828 out:
9829   g_object_unref (data->impl);
9830   g_object_unref (data->file);
9831   g_free (data);
9832
9833   g_object_unref (cancellable);
9834 }
9835
9836 static void
9837 shortcuts_activate_mount_enclosing_volume (GCancellable        *cancellable,
9838                                            GtkFileSystemVolume *volume,
9839                                            const GError        *error,
9840                                            gpointer             user_data)
9841 {
9842   struct ShortcutsActivateData *data = user_data;
9843
9844   if (error)
9845     {
9846       error_changing_folder_dialog (data->impl, data->file, g_error_copy (error));
9847
9848       g_object_unref (data->impl);
9849       g_object_unref (data->file);
9850       g_free (data);
9851
9852       return;
9853     }
9854
9855   data->impl->shortcuts_activate_iter_cancellable =
9856     _gtk_file_system_get_info (data->impl->file_system, data->file,
9857                                "standard::type",
9858                                shortcuts_activate_get_info_cb, data);
9859
9860   if (volume)
9861     _gtk_file_system_volume_unref (volume);
9862 }
9863
9864 static void
9865 shortcuts_activate_iter (GtkFileChooserDefault *impl,
9866                          GtkTreeIter           *iter)
9867 {
9868   gpointer col_data;
9869   ShortcutType shortcut_type;
9870
9871   if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY
9872       && !(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
9873            || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
9874     _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
9875
9876   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
9877                       SHORTCUTS_COL_DATA, &col_data,
9878                       SHORTCUTS_COL_TYPE, &shortcut_type,
9879                       -1);
9880
9881   if (impl->shortcuts_activate_iter_cancellable)
9882     {
9883       g_cancellable_cancel (impl->shortcuts_activate_iter_cancellable);
9884       impl->shortcuts_activate_iter_cancellable = NULL;
9885     }
9886
9887   if (shortcut_type == SHORTCUT_TYPE_SEPARATOR)
9888     return;
9889   else if (shortcut_type == SHORTCUT_TYPE_VOLUME)
9890     {
9891       GtkFileSystemVolume *volume;
9892
9893       volume = col_data;
9894
9895       shortcuts_activate_volume (impl, volume);
9896     }
9897   else if (shortcut_type == SHORTCUT_TYPE_FILE)
9898     {
9899       struct ShortcutsActivateData *data;
9900       GtkFileSystemVolume *volume;
9901
9902       volume = _gtk_file_system_get_volume_for_file (impl->file_system, col_data);
9903
9904       data = g_new0 (struct ShortcutsActivateData, 1);
9905       data->impl = g_object_ref (impl);
9906       data->file = g_object_ref (col_data);
9907
9908       if (!volume || !_gtk_file_system_volume_is_mounted (volume))
9909         {
9910           GMountOperation *mount_operation;
9911           GtkWidget *toplevel;
9912
9913           toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
9914
9915           mount_operation = gtk_mount_operation_new (GTK_WINDOW (toplevel));
9916
9917           impl->shortcuts_activate_iter_cancellable =
9918             _gtk_file_system_mount_enclosing_volume (impl->file_system, col_data,
9919                                                      mount_operation,
9920                                                      shortcuts_activate_mount_enclosing_volume,
9921                                                      data);
9922         }
9923       else
9924         {
9925           impl->shortcuts_activate_iter_cancellable =
9926             _gtk_file_system_get_info (impl->file_system, data->file,
9927                                        "standard::type",
9928                                        shortcuts_activate_get_info_cb, data);
9929         }
9930     }
9931   else if (shortcut_type == SHORTCUT_TYPE_SEARCH)
9932     {
9933       search_activate (impl);
9934     }
9935   else if (shortcut_type == SHORTCUT_TYPE_RECENT)
9936     {
9937       recent_activate (impl);
9938     }
9939 }
9940
9941 /* Handler for GtkWidget::key-press-event on the shortcuts list */
9942 static gboolean
9943 shortcuts_key_press_event_cb (GtkWidget             *widget,
9944                               GdkEventKey           *event,
9945                               GtkFileChooserDefault *impl)
9946 {
9947   guint modifiers;
9948
9949   modifiers = gtk_accelerator_get_default_mod_mask ();
9950
9951   if (key_is_left_or_right (event))
9952     {
9953       gtk_widget_grab_focus (impl->browse_files_tree_view);
9954       return TRUE;
9955     }
9956
9957   if ((event->keyval == GDK_KEY_BackSpace
9958       || event->keyval == GDK_KEY_Delete
9959       || event->keyval == GDK_KEY_KP_Delete)
9960       && (event->state & modifiers) == 0)
9961     {
9962       remove_selected_bookmarks (impl);
9963       return TRUE;
9964     }
9965
9966   if ((event->keyval == GDK_KEY_F2)
9967       && (event->state & modifiers) == 0)
9968     {
9969       rename_selected_bookmark (impl);
9970       return TRUE;
9971     }
9972
9973   return FALSE;
9974 }
9975
9976 static gboolean
9977 shortcuts_select_func  (GtkTreeSelection  *selection,
9978                         GtkTreeModel      *model,
9979                         GtkTreePath       *path,
9980                         gboolean           path_currently_selected,
9981                         gpointer           data)
9982 {
9983   GtkFileChooserDefault *impl = data;
9984   GtkTreeIter filter_iter;
9985   ShortcutType shortcut_type;
9986
9987   if (!gtk_tree_model_get_iter (impl->shortcuts_pane_filter_model, &filter_iter, path))
9988     g_assert_not_reached ();
9989
9990   gtk_tree_model_get (impl->shortcuts_pane_filter_model, &filter_iter, SHORTCUTS_COL_TYPE, &shortcut_type, -1);
9991
9992   return shortcut_type != SHORTCUT_TYPE_SEPARATOR;
9993 }
9994
9995 static gboolean
9996 list_select_func  (GtkTreeSelection  *selection,
9997                    GtkTreeModel      *model,
9998                    GtkTreePath       *path,
9999                    gboolean           path_currently_selected,
10000                    gpointer           data)
10001 {
10002   GtkFileChooserDefault *impl = data;
10003
10004   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
10005       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
10006     {
10007       GtkTreeIter iter;
10008       gboolean is_folder;
10009
10010       if (!gtk_tree_model_get_iter (model, &iter, path))
10011         return FALSE;
10012       gtk_tree_model_get (model, &iter,
10013                           MODEL_COL_IS_FOLDER, &is_folder,
10014                           -1);
10015       if (!is_folder)
10016         return FALSE;
10017     }
10018     
10019   return TRUE;
10020 }
10021
10022 static void
10023 list_selection_changed (GtkTreeSelection      *selection,
10024                         GtkFileChooserDefault *impl)
10025 {
10026   /* See if we are in the new folder editable row for Save mode */
10027   if (impl->operation_mode == OPERATION_MODE_BROWSE &&
10028       impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
10029     {
10030       GFileInfo *info;
10031       gboolean had_selection;
10032
10033       info = get_selected_file_info_from_file_list (impl, &had_selection);
10034       if (!had_selection)
10035         goto out; /* normal processing */
10036
10037       if (!info)
10038         return; /* We are on the editable row for New Folder */
10039     }
10040
10041  out:
10042
10043   if (impl->location_entry)
10044     update_chooser_entry (impl);
10045
10046   check_preview_change (impl);
10047   bookmarks_check_add_sensitivity (impl);
10048
10049   g_signal_emit_by_name (impl, "selection-changed", 0);
10050 }
10051
10052 /* Callback used when a row in the file list is activated */
10053 static void
10054 list_row_activated (GtkTreeView           *tree_view,
10055                     GtkTreePath           *path,
10056                     GtkTreeViewColumn     *column,
10057                     GtkFileChooserDefault *impl)
10058 {
10059   GFile *file;
10060   GtkTreeIter iter;
10061   GtkTreeModel *model;
10062   gboolean is_folder;
10063
10064   model = gtk_tree_view_get_model (tree_view);
10065
10066   if (!gtk_tree_model_get_iter (model, &iter, path))
10067     return;
10068
10069   gtk_tree_model_get (model, &iter,
10070                       MODEL_COL_FILE, &file,
10071                       MODEL_COL_IS_FOLDER, &is_folder,
10072                       -1);
10073         
10074   if (is_folder && file)
10075     {
10076       change_folder_and_display_error (impl, file, FALSE);
10077       g_object_unref (file);
10078       goto out;
10079     }
10080
10081   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
10082       impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
10083     g_signal_emit_by_name (impl, "file-activated");
10084
10085  out:
10086
10087   if (file)
10088     g_object_unref (file);
10089 }
10090
10091 static void
10092 path_bar_clicked (GtkPathBar            *path_bar,
10093                   GFile                 *file,
10094                   GFile                 *child_file,
10095                   gboolean               child_is_hidden,
10096                   GtkFileChooserDefault *impl)
10097 {
10098   if (child_file)
10099     pending_select_files_add (impl, child_file);
10100
10101   if (!change_folder_and_display_error (impl, file, FALSE))
10102     return;
10103
10104   /* Say we have "/foo/bar/[.baz]" and the user clicks on "bar".  We should then
10105    * show hidden files so that ".baz" appears in the file list, as it will still
10106    * be shown in the path bar: "/foo/[bar]/.baz"
10107    */
10108   if (child_is_hidden)
10109     g_object_set (impl, "show-hidden", TRUE, NULL);
10110 }
10111
10112 static void
10113 update_cell_renderer_attributes (GtkFileChooserDefault *impl)
10114 {
10115   GtkTreeViewColumn *column;
10116   GtkCellRenderer *renderer;
10117   GList *walk, *list;
10118   gboolean always_sensitive;
10119
10120   always_sensitive = impl->action != GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
10121                      impl->action != GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER;
10122
10123   /* Keep the following column numbers in sync with create_file_list() */
10124
10125   /* name */
10126   column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_files_tree_view), 0);
10127   list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
10128   for (walk = list; walk; walk = walk->next)
10129     {
10130       renderer = walk->data;
10131       if (GTK_IS_CELL_RENDERER_PIXBUF (renderer))
10132         {
10133           gtk_tree_view_column_set_attributes (column, renderer, 
10134                                                "pixbuf", MODEL_COL_PIXBUF,
10135                                                NULL);
10136         }
10137       else
10138         {
10139           gtk_tree_view_column_set_attributes (column, renderer, 
10140                                                "text", MODEL_COL_NAME,
10141                                                "ellipsize", MODEL_COL_ELLIPSIZE,
10142                                                NULL);
10143         }
10144       if (always_sensitive)
10145         g_object_set (renderer, "sensitive", TRUE, NULL);
10146       else
10147         gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_FOLDER);
10148     }
10149   g_list_free (list);
10150
10151   /* size */
10152   column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_files_tree_view), 1);
10153   list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
10154   renderer = list->data;
10155   gtk_tree_view_column_set_attributes (column, renderer, 
10156                                        "text", MODEL_COL_SIZE_TEXT,
10157                                        NULL);
10158   if (always_sensitive)
10159     g_object_set (renderer, "sensitive", TRUE, NULL);
10160   else
10161     gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_FOLDER);
10162   g_list_free (list);
10163
10164   /* mtime */
10165   column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_files_tree_view), 2);
10166   list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
10167   renderer = list->data;
10168   gtk_tree_view_column_set_attributes (column, renderer, 
10169                                        "text", MODEL_COL_MTIME_TEXT,
10170                                        NULL);
10171   if (always_sensitive)
10172     g_object_set (renderer, "sensitive", TRUE, NULL);
10173   else
10174     gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_FOLDER);
10175   g_list_free (list);
10176 }
10177
10178 GtkWidget *
10179 _gtk_file_chooser_default_new (void)
10180 {
10181   return g_object_new (GTK_TYPE_FILE_CHOOSER_DEFAULT, NULL);
10182 }
10183
10184 static void
10185 location_set_user_text (GtkFileChooserDefault *impl,
10186                         const gchar           *path)
10187 {
10188   _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), path);
10189   gtk_editable_set_position (GTK_EDITABLE (impl->location_entry), -1);
10190 }
10191
10192 static void
10193 location_popup_handler (GtkFileChooserDefault *impl,
10194                         const gchar           *path)
10195
10196   if (impl->operation_mode != OPERATION_MODE_BROWSE)
10197     {
10198       GtkWidget *widget_to_focus;
10199       
10200       /* This will give us the location widgets back */
10201       switch (impl->operation_mode)
10202         {
10203         case OPERATION_MODE_SEARCH:
10204           search_switch_to_browse_mode (impl);
10205           break;
10206         case OPERATION_MODE_RECENT:
10207           recent_switch_to_browse_mode (impl);
10208           break;
10209         case OPERATION_MODE_BROWSE:
10210           g_assert_not_reached ();
10211           break;
10212         }
10213
10214       if (impl->current_folder)
10215         change_folder_and_display_error (impl, impl->current_folder, FALSE);
10216
10217       if (impl->location_mode == LOCATION_MODE_PATH_BAR)
10218         widget_to_focus = impl->browse_files_tree_view;
10219       else
10220         widget_to_focus = impl->location_entry;
10221
10222       gtk_widget_grab_focus (widget_to_focus);
10223       return; 
10224     }
10225   
10226   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
10227       impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
10228     {
10229       LocationMode new_mode;
10230
10231       if (path != NULL)
10232         {
10233           /* since the user typed something, we unconditionally want to turn on the entry */
10234           new_mode = LOCATION_MODE_FILENAME_ENTRY;
10235         }
10236       else if (impl->location_mode == LOCATION_MODE_PATH_BAR)
10237         new_mode = LOCATION_MODE_FILENAME_ENTRY;
10238       else if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
10239         new_mode = LOCATION_MODE_PATH_BAR;
10240       else
10241         {
10242           g_assert_not_reached ();
10243           return;
10244         }
10245
10246       location_mode_set (impl, new_mode, TRUE);
10247       if (new_mode == LOCATION_MODE_FILENAME_ENTRY)
10248         {
10249           if (path != NULL)
10250             location_set_user_text (impl, path);
10251           else
10252             {
10253               location_entry_set_initial_text (impl);
10254               gtk_editable_select_region (GTK_EDITABLE (impl->location_entry), 0, -1);
10255             }
10256         }
10257     }
10258   else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
10259            impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
10260     {
10261       gtk_widget_grab_focus (impl->location_entry);
10262       if (path != NULL)
10263         location_set_user_text (impl, path);
10264     }
10265   else
10266     g_assert_not_reached ();
10267 }
10268
10269 /* Handler for the "up-folder" keybinding signal */
10270 static void
10271 up_folder_handler (GtkFileChooserDefault *impl)
10272 {
10273   _gtk_path_bar_up (GTK_PATH_BAR (impl->browse_path_bar));
10274 }
10275
10276 /* Handler for the "down-folder" keybinding signal */
10277 static void
10278 down_folder_handler (GtkFileChooserDefault *impl)
10279 {
10280   _gtk_path_bar_down (GTK_PATH_BAR (impl->browse_path_bar));
10281 }
10282
10283 /* Switches to the shortcut in the specified index */
10284 static void
10285 switch_to_shortcut (GtkFileChooserDefault *impl,
10286                     int pos)
10287 {
10288   GtkTreeIter iter;
10289
10290   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
10291     g_assert_not_reached ();
10292
10293   shortcuts_activate_iter (impl, &iter);
10294 }
10295
10296 /* Handler for the "home-folder" keybinding signal */
10297 static void
10298 home_folder_handler (GtkFileChooserDefault *impl)
10299 {
10300   if (impl->has_home)
10301     switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_HOME));
10302 }
10303
10304 /* Handler for the "desktop-folder" keybinding signal */
10305 static void
10306 desktop_folder_handler (GtkFileChooserDefault *impl)
10307 {
10308   if (impl->has_desktop)
10309     switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_DESKTOP));
10310 }
10311
10312 /* Handler for the "search-shortcut" keybinding signal */
10313 static void
10314 search_shortcut_handler (GtkFileChooserDefault *impl)
10315 {
10316   if (impl->has_search)
10317     {
10318       switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_SEARCH));
10319
10320       /* we want the entry widget to grab the focus the first
10321        * time, not the browse_files_tree_view widget.
10322        */
10323       if (impl->search_entry)
10324         gtk_widget_grab_focus (impl->search_entry);
10325     }
10326 }
10327
10328 /* Handler for the "recent-shortcut" keybinding signal */
10329 static void
10330 recent_shortcut_handler (GtkFileChooserDefault *impl)
10331 {
10332   if (impl->has_recent)
10333     switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_RECENT));
10334 }
10335
10336 static void
10337 quick_bookmark_handler (GtkFileChooserDefault *impl,
10338                         gint bookmark_index)
10339 {
10340   int bookmark_pos;
10341   GtkTreePath *path;
10342
10343   if (bookmark_index < 0 || bookmark_index >= impl->num_bookmarks)
10344     return;
10345
10346   bookmark_pos = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS) + bookmark_index;
10347
10348   path = gtk_tree_path_new_from_indices (bookmark_pos, -1);
10349   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
10350                                 path, NULL,
10351                                 FALSE, 0.0, 0.0);
10352   gtk_tree_path_free (path);
10353
10354   switch_to_shortcut (impl, bookmark_pos);
10355 }
10356
10357 static void
10358 show_hidden_handler (GtkFileChooserDefault *impl)
10359 {
10360   g_object_set (impl,
10361                 "show-hidden", !impl->show_hidden,
10362                 NULL);
10363 }
10364
10365
10366 /* Drag and drop interfaces */
10367
10368 static void
10369 _shortcuts_pane_model_filter_class_init (ShortcutsPaneModelFilterClass *class)
10370 {
10371 }
10372
10373 static void
10374 _shortcuts_pane_model_filter_init (ShortcutsPaneModelFilter *model)
10375 {
10376   model->impl = NULL;
10377 }
10378
10379 /* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */
10380 static gboolean
10381 shortcuts_pane_model_filter_row_draggable (GtkTreeDragSource *drag_source,
10382                                            GtkTreePath       *path)
10383 {
10384   ShortcutsPaneModelFilter *model;
10385   int pos;
10386   int bookmarks_pos;
10387
10388   model = SHORTCUTS_PANE_MODEL_FILTER (drag_source);
10389
10390   pos = *gtk_tree_path_get_indices (path);
10391   bookmarks_pos = shortcuts_get_index (model->impl, SHORTCUTS_BOOKMARKS);
10392
10393   return (pos >= bookmarks_pos && pos < bookmarks_pos + model->impl->num_bookmarks);
10394 }
10395
10396 /* GtkTreeDragSource::drag_data_get implementation for the shortcuts
10397  * filter model
10398  */
10399 static gboolean
10400 shortcuts_pane_model_filter_drag_data_get (GtkTreeDragSource *drag_source,
10401                                            GtkTreePath       *path,
10402                                            GtkSelectionData  *selection_data)
10403 {
10404   /* FIXME */
10405
10406   return FALSE;
10407 }
10408
10409 /* Fill the GtkTreeDragSourceIface vtable */
10410 static void
10411 shortcuts_pane_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface)
10412 {
10413   iface->row_draggable = shortcuts_pane_model_filter_row_draggable;
10414   iface->drag_data_get = shortcuts_pane_model_filter_drag_data_get;
10415 }
10416
10417 #if 0
10418 /* Fill the GtkTreeDragDestIface vtable */
10419 static void
10420 shortcuts_pane_model_filter_drag_dest_iface_init (GtkTreeDragDestIface *iface)
10421 {
10422   iface->drag_data_received = shortcuts_pane_model_filter_drag_data_received;
10423   iface->row_drop_possible = shortcuts_pane_model_filter_row_drop_possible;
10424 }
10425 #endif
10426
10427 static GtkTreeModel *
10428 shortcuts_pane_model_filter_new (GtkFileChooserDefault *impl,
10429                                  GtkTreeModel          *child_model,
10430                                  GtkTreePath           *root)
10431 {
10432   ShortcutsPaneModelFilter *model;
10433
10434   model = g_object_new (SHORTCUTS_PANE_MODEL_FILTER_TYPE,
10435                         "child-model", child_model,
10436                         "virtual-root", root,
10437                         NULL);
10438
10439   model->impl = impl;
10440
10441   return GTK_TREE_MODEL (model);
10442 }
10443