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