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