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