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