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