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