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