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