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