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