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