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