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