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