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