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