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