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