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