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