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