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