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