]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserdefault.c
bd1b8c9fc8c91123b438d575614c76d4e6756eda
[~andy/gtk] / gtk / gtkfilechooserdefault.c
1 /* GTK - The GIMP Toolkit
2  * gtkfilechooserdefault.c: Default implementation of GtkFileChooser
3  * Copyright (C) 2003, Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include <config.h>
22 #include "gdk/gdkkeysyms.h"
23 #include "gtkalignment.h"
24 #include "gtkbindings.h"
25 #include "gtkbutton.h"
26 #include "gtkcelllayout.h"
27 #include "gtkcellrendererpixbuf.h"
28 #include "gtkcellrenderertext.h"
29 #include "gtkcellrenderertext.h"
30 #include "gtkcheckmenuitem.h"
31 #include "gtkcombobox.h"
32 #include "gtkentry.h"
33 #include "gtkeventbox.h"
34 #include "gtkexpander.h"
35 #include "gtkfilechooserdefault.h"
36 #include "gtkfilechooserembed.h"
37 #include "gtkfilechooserentry.h"
38 #include "gtkfilechooserutils.h"
39 #include "gtkfilechooser.h"
40 #include "gtkfilesystemmodel.h"
41 #include "gtkframe.h"
42 #include "gtkhbox.h"
43 #include "gtkhpaned.h"
44 #include "gtkiconfactory.h"
45 #include "gtkicontheme.h"
46 #include "gtkimage.h"
47 #include "gtkimagemenuitem.h"
48 #include "gtkintl.h"
49 #include "gtklabel.h"
50 #include "gtkmarshalers.h"
51 #include "gtkmenuitem.h"
52 #include "gtkmessagedialog.h"
53 #include "gtkpathbar.h"
54 #include "gtkprivate.h"
55 #include "gtkscrolledwindow.h"
56 #include "gtkseparatormenuitem.h"
57 #include "gtksizegroup.h"
58 #include "gtkstock.h"
59 #include "gtktable.h"
60 #include "gtktreednd.h"
61 #include "gtktreeprivate.h"
62 #include "gtktreeview.h"
63 #include "gtktreemodelsort.h"
64 #include "gtktreeselection.h"
65 #include "gtktreestore.h"
66 #include "gtktooltips.h"
67 #include "gtktypebuiltins.h"
68 #include "gtkvbox.h"
69
70 #if defined (G_OS_UNIX)
71 #include "gtkfilesystemunix.h"
72 #elif defined (G_OS_WIN32)
73 #include "gtkfilesystemwin32.h"
74 #endif
75
76 #include "gtkalias.h"
77
78 #include <errno.h>
79 #include <string.h>
80 #include <time.h>
81
82 \f
83
84 /* Profiling stuff */
85
86 #define PROFILE_FILE_CHOOSER
87 #ifdef PROFILE_FILE_CHOOSER
88
89 #ifdef HAVE_UNISTD_H
90 #include <unistd.h>
91 #endif
92 #ifdef G_OS_WIN32
93 #include <io.h>
94 #endif
95
96 #ifndef F_OK 
97 #define F_OK 0
98 #endif
99
100 #define PROFILE_INDENT 4
101 static int profile_indent;
102
103 static void
104 profile_add_indent (int indent)
105 {
106   profile_indent += indent;
107   if (profile_indent < 0)
108     g_error ("You screwed up your indentation");
109 }
110
111 void
112 _gtk_file_chooser_profile_log (const char *func, int indent, const char *msg1, const char *msg2)
113 {
114   char *str;
115
116   if (indent < 0)
117     profile_add_indent (indent);
118
119   if (profile_indent == 0)
120     str = g_strdup_printf ("MARK: %s %s %s", func, msg1 ? msg1 : "", msg2 ? msg2 : "");
121   else
122     str = g_strdup_printf ("MARK: %*c %s %s %s", profile_indent - 1, ' ', func, msg1 ? msg1 : "", msg2 ? msg2 : "");
123
124   access (str, F_OK);
125   g_free (str);
126
127   if (indent > 0)
128     profile_add_indent (indent);
129 }
130
131 #define profile_start(x, y) _gtk_file_chooser_profile_log (G_STRFUNC, PROFILE_INDENT, x, y)
132 #define profile_end(x, y) _gtk_file_chooser_profile_log (G_STRFUNC, -PROFILE_INDENT, x, y)
133 #define profile_msg(x, y) _gtk_file_chooser_profile_log (NULL, 0, x, y)
134 #else
135 #define profile_start(x, y)
136 #define profile_end(x, y)
137 #define profile_msg(x, y)
138 #endif
139
140 \f
141
142 typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
143
144 #define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
145 #define GTK_IS_FILE_CHOOSER_DEFAULT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT))
146 #define GTK_FILE_CHOOSER_DEFAULT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
147
148 typedef enum {
149   LOAD_EMPTY,                   /* There is no model */
150   LOAD_PRELOAD,                 /* Model is loading and a timer is running; model isn't inserted into the tree yet */
151   LOAD_LOADING,                 /* Timeout expired, model is inserted into the tree, but not fully loaded yet */
152   LOAD_FINISHED                 /* Model is fully loaded and inserted into the tree */
153 } LoadState;
154
155 #define MAX_LOADING_TIME 500
156
157 struct _GtkFileChooserDefaultClass
158 {
159   GtkVBoxClass parent_class;
160 };
161
162 struct _GtkFileChooserDefault
163 {
164   GtkVBox parent_instance;
165
166   GtkFileChooserAction action;
167
168   GtkFileSystem *file_system;
169
170   /* Save mode widgets */
171   GtkWidget *save_widgets;
172
173   GtkWidget *save_file_name_entry;
174   GtkWidget *save_folder_label;
175   GtkWidget *save_folder_combo;
176   GtkWidget *save_expander;
177
178   /* The file browsing widgets */
179   GtkWidget *browse_widgets;
180   GtkWidget *browse_shortcuts_tree_view;
181   GtkWidget *browse_shortcuts_add_button;
182   GtkWidget *browse_shortcuts_remove_button;
183   GtkWidget *browse_shortcuts_popup_menu;
184   GtkWidget *browse_shortcuts_popup_menu_remove_item;
185   GtkWidget *browse_shortcuts_popup_menu_rename_item;
186   GtkWidget *browse_files_tree_view;
187   GtkWidget *browse_files_popup_menu;
188   GtkWidget *browse_files_popup_menu_add_shortcut_item;
189   GtkWidget *browse_files_popup_menu_hidden_files_item;
190   GtkWidget *browse_new_folder_button;
191   GtkWidget *browse_path_bar;
192
193   GtkFileSystemModel *browse_files_model;
194
195   GtkWidget *filter_combo_hbox;
196   GtkWidget *filter_combo;
197   GtkWidget *preview_box;
198   GtkWidget *preview_label;
199   GtkWidget *preview_widget;
200   GtkWidget *extra_align;
201   GtkWidget *extra_widget;
202
203   GtkListStore *shortcuts_model;
204   GtkTreeModel *shortcuts_filter_model;
205
206   GtkTreeModelSort *sort_model;
207
208   LoadState load_state;
209   guint load_timeout_id;
210
211   GSList *pending_select_paths;
212
213   GtkFileFilter *current_filter;
214   GSList *filters;
215
216   GtkTooltips *tooltips;
217
218   gboolean has_home;
219   gboolean has_desktop;
220
221   int num_volumes;
222   int num_shortcuts;
223   int num_bookmarks;
224
225   gulong volumes_changed_id;
226   gulong bookmarks_changed_id;
227
228   GtkFilePath *current_volume_path;
229   GtkFilePath *current_folder;
230   GtkFilePath *preview_path;
231   char *preview_display_name;
232
233   GtkTreeViewColumn *list_name_column;
234   GtkCellRenderer *list_name_renderer;
235
236   GSource *edited_idle;
237   char *edited_new_text;
238
239   gulong settings_signal_id;
240   int icon_size;
241
242   gulong toplevel_set_focus_id;
243   GtkWidget *toplevel_last_focus_widget;
244
245 #if 0
246   GdkDragContext *shortcuts_drag_context;
247   GSource *shortcuts_drag_outside_idle;
248 #endif
249
250   /* Flags */
251
252   guint local_only : 1;
253   guint preview_widget_active : 1;
254   guint use_preview_label : 1;
255   guint select_multiple : 1;
256   guint show_hidden : 1;
257   guint do_overwrite_confirmation : 1;
258   guint list_sort_ascending : 1;
259   guint changing_folder : 1;
260   guint shortcuts_current_folder_active : 1;
261
262 #if 0
263   guint shortcuts_drag_outside : 1;
264 #endif
265 };
266
267 /* Signal IDs */
268 enum {
269   LOCATION_POPUP,
270   UP_FOLDER,
271   DOWN_FOLDER,
272   HOME_FOLDER,
273   LAST_SIGNAL
274 };
275
276 static guint signals[LAST_SIGNAL] = { 0 };
277
278 /* Column numbers for the shortcuts tree.  Keep these in sync with shortcuts_model_create() */
279 enum {
280   SHORTCUTS_COL_PIXBUF,
281   SHORTCUTS_COL_NAME,
282   SHORTCUTS_COL_DATA,
283   SHORTCUTS_COL_IS_VOLUME,
284   SHORTCUTS_COL_REMOVABLE,
285   SHORTCUTS_COL_PIXBUF_VISIBLE,
286   SHORTCUTS_COL_NUM_COLUMNS
287 };
288
289 /* Column numbers for the file list */
290 enum {
291   FILE_LIST_COL_NAME,
292   FILE_LIST_COL_SIZE,
293   FILE_LIST_COL_MTIME,
294   FILE_LIST_COL_NUM_COLUMNS
295 };
296
297 /* Identifiers for target types */
298 enum {
299   GTK_TREE_MODEL_ROW,
300   TEXT_URI_LIST
301 };
302
303 /* Target types for dragging from the shortcuts list */
304 static const GtkTargetEntry shortcuts_source_targets[] = {
305   { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }
306 };
307
308 static const int num_shortcuts_source_targets = (sizeof (shortcuts_source_targets)
309                                                  / sizeof (shortcuts_source_targets[0]));
310
311 /* Target types for dropping into the shortcuts list */
312 static const GtkTargetEntry shortcuts_dest_targets[] = {
313   { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW },
314   { "text/uri-list", 0, TEXT_URI_LIST }
315 };
316
317 static const int num_shortcuts_dest_targets = (sizeof (shortcuts_dest_targets)
318                                                / sizeof (shortcuts_dest_targets[0]));
319
320 /* Target types for DnD from the file list */
321 static const GtkTargetEntry file_list_source_targets[] = {
322   { "text/uri-list", 0, TEXT_URI_LIST }
323 };
324
325 static const int num_file_list_source_targets = (sizeof (file_list_source_targets)
326                                                  / sizeof (file_list_source_targets[0]));
327
328 /* Target types for dropping into the file list */
329 static const GtkTargetEntry file_list_dest_targets[] = {
330   { "text/uri-list", 0, TEXT_URI_LIST }
331 };
332
333 static const int num_file_list_dest_targets = (sizeof (file_list_dest_targets)
334                                                / sizeof (file_list_dest_targets[0]));
335
336
337 /* Interesting places in the shortcuts bar */
338 typedef enum {
339   SHORTCUTS_HOME,
340   SHORTCUTS_DESKTOP,
341   SHORTCUTS_VOLUMES,
342   SHORTCUTS_SHORTCUTS,
343   SHORTCUTS_BOOKMARKS_SEPARATOR,
344   SHORTCUTS_BOOKMARKS,
345   SHORTCUTS_CURRENT_FOLDER_SEPARATOR,
346   SHORTCUTS_CURRENT_FOLDER
347 } ShortcutsIndex;
348
349 /* Icon size for if we can't get it from the theme */
350 #define FALLBACK_ICON_SIZE 16
351
352 #define PREVIEW_HBOX_SPACING 12
353 #define NUM_LINES 40
354 #define NUM_CHARS 60
355
356 static void gtk_file_chooser_default_class_init       (GtkFileChooserDefaultClass *class);
357 static void gtk_file_chooser_default_iface_init       (GtkFileChooserIface        *iface);
358 static void gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface   *iface);
359 static void gtk_file_chooser_default_init             (GtkFileChooserDefault      *impl);
360
361 static GObject* gtk_file_chooser_default_constructor  (GType                  type,
362                                                        guint                  n_construct_properties,
363                                                        GObjectConstructParam *construct_params);
364 static void     gtk_file_chooser_default_finalize     (GObject               *object);
365 static void     gtk_file_chooser_default_set_property (GObject               *object,
366                                                        guint                  prop_id,
367                                                        const GValue          *value,
368                                                        GParamSpec            *pspec);
369 static void     gtk_file_chooser_default_get_property (GObject               *object,
370                                                        guint                  prop_id,
371                                                        GValue                *value,
372                                                        GParamSpec            *pspec);
373 static void     gtk_file_chooser_default_dispose      (GObject               *object);
374 static void     gtk_file_chooser_default_show_all       (GtkWidget             *widget);
375 static void     gtk_file_chooser_default_map            (GtkWidget             *widget);
376 static void     gtk_file_chooser_default_hierarchy_changed (GtkWidget          *widget,
377                                                             GtkWidget          *previous_toplevel);
378 static void     gtk_file_chooser_default_style_set      (GtkWidget             *widget,
379                                                          GtkStyle              *previous_style);
380 static void     gtk_file_chooser_default_screen_changed (GtkWidget             *widget,
381                                                          GdkScreen             *previous_screen);
382
383 static gboolean       gtk_file_chooser_default_set_current_folder          (GtkFileChooser    *chooser,
384                                                                             const GtkFilePath *path,
385                                                                             GError           **error);
386 static gboolean       gtk_file_chooser_default_update_current_folder       (GtkFileChooser    *chooser,
387                                                                             const GtkFilePath *path,
388                                                                             gboolean           keep_trail,
389                                                                             GError           **error);
390 static GtkFilePath *  gtk_file_chooser_default_get_current_folder          (GtkFileChooser    *chooser);
391 static void           gtk_file_chooser_default_set_current_name            (GtkFileChooser    *chooser,
392                                                                             const gchar       *name);
393 static gboolean       gtk_file_chooser_default_select_path                 (GtkFileChooser    *chooser,
394                                                                             const GtkFilePath *path,
395                                                                             GError           **error);
396 static void           gtk_file_chooser_default_unselect_path               (GtkFileChooser    *chooser,
397                                                                             const GtkFilePath *path);
398 static void           gtk_file_chooser_default_select_all                  (GtkFileChooser    *chooser);
399 static void           gtk_file_chooser_default_unselect_all                (GtkFileChooser    *chooser);
400 static GSList *       gtk_file_chooser_default_get_paths                   (GtkFileChooser    *chooser);
401 static GtkFilePath *  gtk_file_chooser_default_get_preview_path            (GtkFileChooser    *chooser);
402 static GtkFileSystem *gtk_file_chooser_default_get_file_system             (GtkFileChooser    *chooser);
403 static void           gtk_file_chooser_default_add_filter                  (GtkFileChooser    *chooser,
404                                                                             GtkFileFilter     *filter);
405 static void           gtk_file_chooser_default_remove_filter               (GtkFileChooser    *chooser,
406                                                                             GtkFileFilter     *filter);
407 static GSList *       gtk_file_chooser_default_list_filters                (GtkFileChooser    *chooser);
408 static gboolean       gtk_file_chooser_default_add_shortcut_folder    (GtkFileChooser    *chooser,
409                                                                        const GtkFilePath *path,
410                                                                        GError           **error);
411 static gboolean       gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
412                                                                        const GtkFilePath *path,
413                                                                        GError           **error);
414 static GSList *       gtk_file_chooser_default_list_shortcut_folders  (GtkFileChooser    *chooser);
415
416 static void           gtk_file_chooser_default_get_default_size       (GtkFileChooserEmbed *chooser_embed,
417                                                                        gint                *default_width,
418                                                                        gint                *default_height);
419 static void           gtk_file_chooser_default_get_resizable_hints    (GtkFileChooserEmbed *chooser_embed,
420                                                                        gboolean            *resize_horizontally,
421                                                                        gboolean            *resize_vertically);
422 static gboolean       gtk_file_chooser_default_should_respond         (GtkFileChooserEmbed *chooser_embed);
423 static void           gtk_file_chooser_default_initial_focus          (GtkFileChooserEmbed *chooser_embed);
424
425 static void location_popup_handler (GtkFileChooserDefault *impl,
426                                     const gchar           *path);
427 static void up_folder_handler      (GtkFileChooserDefault *impl);
428 static void down_folder_handler    (GtkFileChooserDefault *impl);
429 static void home_folder_handler    (GtkFileChooserDefault *impl);
430 static void update_appearance      (GtkFileChooserDefault *impl);
431
432 static void set_current_filter   (GtkFileChooserDefault *impl,
433                                   GtkFileFilter         *filter);
434 static void check_preview_change (GtkFileChooserDefault *impl);
435
436 static void filter_combo_changed       (GtkComboBox           *combo_box,
437                                         GtkFileChooserDefault *impl);
438 static void     shortcuts_row_activated_cb (GtkTreeView           *tree_view,
439                                             GtkTreePath           *path,
440                                             GtkTreeViewColumn     *column,
441                                             GtkFileChooserDefault *impl);
442
443 static gboolean shortcuts_key_press_event_cb (GtkWidget             *widget,
444                                               GdkEventKey           *event,
445                                               GtkFileChooserDefault *impl);
446
447 static gboolean shortcuts_select_func   (GtkTreeSelection      *selection,
448                                          GtkTreeModel          *model,
449                                          GtkTreePath           *path,
450                                          gboolean               path_currently_selected,
451                                          gpointer               data);
452 static gboolean shortcuts_get_selected  (GtkFileChooserDefault *impl,
453                                          GtkTreeIter           *iter);
454 static void shortcuts_activate_iter (GtkFileChooserDefault *impl,
455                                      GtkTreeIter           *iter);
456 static int shortcuts_get_index (GtkFileChooserDefault *impl,
457                                 ShortcutsIndex         where);
458 static int shortcut_find_position (GtkFileChooserDefault *impl,
459                                    const GtkFilePath     *path);
460
461 static void bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl);
462
463 static gboolean list_select_func   (GtkTreeSelection      *selection,
464                                     GtkTreeModel          *model,
465                                     GtkTreePath           *path,
466                                     gboolean               path_currently_selected,
467                                     gpointer               data);
468
469 static void list_selection_changed     (GtkTreeSelection      *tree_selection,
470                                         GtkFileChooserDefault *impl);
471 static void list_row_activated         (GtkTreeView           *tree_view,
472                                         GtkTreePath           *path,
473                                         GtkTreeViewColumn     *column,
474                                         GtkFileChooserDefault *impl);
475
476 static void select_func (GtkFileSystemModel *model,
477                          GtkTreePath        *path,
478                          GtkTreeIter        *iter,
479                          gpointer            user_data);
480
481 static void path_bar_clicked           (GtkPathBar            *path_bar,
482                                         GtkFilePath           *file_path,
483                                         gboolean               child_is_hidden,
484                                         GtkFileChooserDefault *impl);
485
486 static void add_bookmark_button_clicked_cb    (GtkButton             *button,
487                                                GtkFileChooserDefault *impl);
488 static void remove_bookmark_button_clicked_cb (GtkButton             *button,
489                                                GtkFileChooserDefault *impl);
490
491 static void list_icon_data_func (GtkTreeViewColumn *tree_column,
492                                  GtkCellRenderer   *cell,
493                                  GtkTreeModel      *tree_model,
494                                  GtkTreeIter       *iter,
495                                  gpointer           data);
496 static void list_name_data_func (GtkTreeViewColumn *tree_column,
497                                  GtkCellRenderer   *cell,
498                                  GtkTreeModel      *tree_model,
499                                  GtkTreeIter       *iter,
500                                  gpointer           data);
501 #if 0
502 static void list_size_data_func (GtkTreeViewColumn *tree_column,
503                                  GtkCellRenderer   *cell,
504                                  GtkTreeModel      *tree_model,
505                                  GtkTreeIter       *iter,
506                                  gpointer           data);
507 #endif
508 static void list_mtime_data_func (GtkTreeViewColumn *tree_column,
509                                   GtkCellRenderer   *cell,
510                                   GtkTreeModel      *tree_model,
511                                   GtkTreeIter       *iter,
512                                   gpointer           data);
513
514 static const GtkFileInfo *get_list_file_info (GtkFileChooserDefault *impl,
515                                               GtkTreeIter           *iter);
516
517 static void load_remove_timer (GtkFileChooserDefault *impl);
518 static void browse_files_center_selected_row (GtkFileChooserDefault *impl);
519
520 static GObjectClass *parent_class;
521
522 \f
523
524 /* Drag and drop interface declarations */
525
526 typedef struct {
527   GtkTreeModelFilter parent;
528
529   GtkFileChooserDefault *impl;
530 } ShortcutsModelFilter;
531
532 typedef struct {
533   GtkTreeModelFilterClass parent_class;
534 } ShortcutsModelFilterClass;
535
536 #define SHORTCUTS_MODEL_FILTER_TYPE (_shortcuts_model_filter_get_type ())
537 #define SHORTCUTS_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_MODEL_FILTER_TYPE, ShortcutsModelFilter))
538
539 static void shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface);
540
541 G_DEFINE_TYPE_WITH_CODE (ShortcutsModelFilter,
542                          _shortcuts_model_filter,
543                          GTK_TYPE_TREE_MODEL_FILTER,
544                          G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
545                                                 shortcuts_model_filter_drag_source_iface_init));
546
547 static GtkTreeModel *shortcuts_model_filter_new (GtkFileChooserDefault *impl,
548                                                  GtkTreeModel          *child_model,
549                                                  GtkTreePath           *root);
550
551 \f
552
553 GType
554 _gtk_file_chooser_default_get_type (void)
555 {
556   static GType file_chooser_default_type = 0;
557
558   if (!file_chooser_default_type)
559     {
560       static const GTypeInfo file_chooser_default_info =
561       {
562         sizeof (GtkFileChooserDefaultClass),
563         NULL,           /* base_init */
564         NULL,           /* base_finalize */
565         (GClassInitFunc) gtk_file_chooser_default_class_init,
566         NULL,           /* class_finalize */
567         NULL,           /* class_data */
568         sizeof (GtkFileChooserDefault),
569         0,              /* n_preallocs */
570         (GInstanceInitFunc) gtk_file_chooser_default_init,
571       };
572
573       static const GInterfaceInfo file_chooser_info =
574       {
575         (GInterfaceInitFunc) gtk_file_chooser_default_iface_init, /* interface_init */
576         NULL,                                                          /* interface_finalize */
577         NULL                                                           /* interface_data */
578       };
579
580       static const GInterfaceInfo file_chooser_embed_info =
581       {
582         (GInterfaceInitFunc) gtk_file_chooser_embed_default_iface_init, /* interface_init */
583         NULL,                                                          /* interface_finalize */
584         NULL                                                           /* interface_data */
585       };
586
587       file_chooser_default_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserDefault",
588                                                          &file_chooser_default_info, 0);
589
590       g_type_add_interface_static (file_chooser_default_type,
591                                    GTK_TYPE_FILE_CHOOSER,
592                                    &file_chooser_info);
593       g_type_add_interface_static (file_chooser_default_type,
594                                    GTK_TYPE_FILE_CHOOSER_EMBED,
595                                    &file_chooser_embed_info);
596     }
597
598   return file_chooser_default_type;
599 }
600
601 static void
602 gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
603 {
604   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
605   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
606   GtkBindingSet *binding_set;
607
608   parent_class = g_type_class_peek_parent (class);
609
610   gobject_class->finalize = gtk_file_chooser_default_finalize;
611   gobject_class->constructor = gtk_file_chooser_default_constructor;
612   gobject_class->set_property = gtk_file_chooser_default_set_property;
613   gobject_class->get_property = gtk_file_chooser_default_get_property;
614   gobject_class->dispose = gtk_file_chooser_default_dispose;
615
616   widget_class->show_all = gtk_file_chooser_default_show_all;
617   widget_class->map = gtk_file_chooser_default_map;
618   widget_class->hierarchy_changed = gtk_file_chooser_default_hierarchy_changed;
619   widget_class->style_set = gtk_file_chooser_default_style_set;
620   widget_class->screen_changed = gtk_file_chooser_default_screen_changed;
621
622   signals[LOCATION_POPUP] =
623     _gtk_binding_signal_new ("location-popup",
624                              G_OBJECT_CLASS_TYPE (class),
625                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
626                              G_CALLBACK (location_popup_handler),
627                              NULL, NULL,
628                              _gtk_marshal_VOID__STRING,
629                              G_TYPE_NONE, 1, G_TYPE_STRING);
630   signals[UP_FOLDER] =
631     _gtk_binding_signal_new ("up-folder",
632                              G_OBJECT_CLASS_TYPE (class),
633                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
634                              G_CALLBACK (up_folder_handler),
635                              NULL, NULL,
636                              _gtk_marshal_VOID__VOID,
637                              G_TYPE_NONE, 0);
638   signals[DOWN_FOLDER] =
639     _gtk_binding_signal_new ("down-folder",
640                              G_OBJECT_CLASS_TYPE (class),
641                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
642                              G_CALLBACK (down_folder_handler),
643                              NULL, NULL,
644                              _gtk_marshal_VOID__VOID,
645                              G_TYPE_NONE, 0);
646   signals[HOME_FOLDER] =
647     _gtk_binding_signal_new ("home-folder",
648                              G_OBJECT_CLASS_TYPE (class),
649                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
650                              G_CALLBACK (home_folder_handler),
651                              NULL, NULL,
652                              _gtk_marshal_VOID__VOID,
653                              G_TYPE_NONE, 0);
654
655   binding_set = gtk_binding_set_by_class (class);
656
657   gtk_binding_entry_add_signal (binding_set,
658                                 GDK_l, GDK_CONTROL_MASK,
659                                 "location-popup",
660                                 1, G_TYPE_STRING, "");
661
662   gtk_binding_entry_add_signal (binding_set,
663                                 GDK_slash, 0,
664                                 "location-popup",
665                                 1, G_TYPE_STRING, "/");
666
667   gtk_binding_entry_add_signal (binding_set,
668                                 GDK_Up, GDK_MOD1_MASK,
669                                 "up-folder",
670                                 0);
671   gtk_binding_entry_add_signal (binding_set,
672                                 GDK_BackSpace, 0,
673                                 "up-folder",
674                                 0);
675   gtk_binding_entry_add_signal (binding_set,
676                                 GDK_KP_Up, GDK_MOD1_MASK,
677                                 "up-folder",
678                                 0);
679
680   gtk_binding_entry_add_signal (binding_set,
681                                 GDK_Down, GDK_MOD1_MASK,
682                                 "down-folder",
683                                 0);
684   gtk_binding_entry_add_signal (binding_set,
685                                 GDK_KP_Down, GDK_MOD1_MASK,
686                                 "down-folder",
687                                 0);
688
689   gtk_binding_entry_add_signal (binding_set,
690                                 GDK_Home, GDK_MOD1_MASK,
691                                 "home-folder",
692                                 0);
693   gtk_binding_entry_add_signal (binding_set,
694                                 GDK_KP_Home, GDK_MOD1_MASK,
695                                 "home-folder",
696                                 0);
697
698   _gtk_file_chooser_install_properties (gobject_class);
699
700   gtk_settings_install_property (g_param_spec_string ("gtk-file-chooser-backend",
701                                                       P_("Default file chooser backend"),
702                                                       P_("Name of the GtkFileChooser backend to use by default"),
703                                                       NULL,
704                                                       GTK_PARAM_READWRITE));
705 }
706
707 static void
708 gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
709 {
710   iface->select_path = gtk_file_chooser_default_select_path;
711   iface->unselect_path = gtk_file_chooser_default_unselect_path;
712   iface->select_all = gtk_file_chooser_default_select_all;
713   iface->unselect_all = gtk_file_chooser_default_unselect_all;
714   iface->get_paths = gtk_file_chooser_default_get_paths;
715   iface->get_preview_path = gtk_file_chooser_default_get_preview_path;
716   iface->get_file_system = gtk_file_chooser_default_get_file_system;
717   iface->set_current_folder = gtk_file_chooser_default_set_current_folder;
718   iface->get_current_folder = gtk_file_chooser_default_get_current_folder;
719   iface->set_current_name = gtk_file_chooser_default_set_current_name;
720   iface->add_filter = gtk_file_chooser_default_add_filter;
721   iface->remove_filter = gtk_file_chooser_default_remove_filter;
722   iface->list_filters = gtk_file_chooser_default_list_filters;
723   iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder;
724   iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder;
725   iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders;
726 }
727
728 static void
729 gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
730 {
731   iface->get_default_size = gtk_file_chooser_default_get_default_size;
732   iface->get_resizable_hints = gtk_file_chooser_default_get_resizable_hints;
733   iface->should_respond = gtk_file_chooser_default_should_respond;
734   iface->initial_focus = gtk_file_chooser_default_initial_focus;
735 }
736 static void
737 gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
738 {
739   profile_start ("start", NULL);
740
741   impl->local_only = TRUE;
742   impl->preview_widget_active = TRUE;
743   impl->use_preview_label = TRUE;
744   impl->select_multiple = FALSE;
745   impl->show_hidden = FALSE;
746   impl->icon_size = FALLBACK_ICON_SIZE;
747   impl->load_state = LOAD_EMPTY;
748   impl->pending_select_paths = NULL;
749
750   gtk_box_set_spacing (GTK_BOX (impl), 12);
751
752   impl->tooltips = gtk_tooltips_new ();
753   g_object_ref (impl->tooltips);
754   gtk_object_sink (GTK_OBJECT (impl->tooltips));
755
756   profile_end ("end", NULL);
757 }
758
759 /* Frees the data columns for the specified iter in the shortcuts model*/
760 static void
761 shortcuts_free_row_data (GtkFileChooserDefault *impl,
762                          GtkTreeIter           *iter)
763 {
764   gpointer col_data;
765   gboolean is_volume;
766
767   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
768                       SHORTCUTS_COL_DATA, &col_data,
769                       SHORTCUTS_COL_IS_VOLUME, &is_volume,
770                       -1);
771   if (!col_data)
772     return;
773
774   if (is_volume)
775     {
776       GtkFileSystemVolume *volume;
777
778       volume = col_data;
779       gtk_file_system_volume_free (impl->file_system, volume);
780     }
781   else
782     {
783       GtkFilePath *path;
784
785       path = col_data;
786       gtk_file_path_free (path);
787     }
788 }
789
790 /* Frees all the data columns in the shortcuts model */
791 static void
792 shortcuts_free (GtkFileChooserDefault *impl)
793 {
794   GtkTreeIter iter;
795
796   if (!impl->shortcuts_model)
797     return;
798
799   if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
800     do
801       {
802         shortcuts_free_row_data (impl, &iter);
803       }
804     while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter));
805
806   g_object_unref (impl->shortcuts_model);
807   impl->shortcuts_model = NULL;
808 }
809
810 static void
811 pending_select_paths_free (GtkFileChooserDefault *impl)
812 {
813   GSList *l;
814
815   for (l = impl->pending_select_paths; l; l = l->next)
816     {
817       GtkFilePath *path;
818
819       path = l->data;
820       gtk_file_path_free (path);
821     }
822
823   g_slist_free (impl->pending_select_paths);
824   impl->pending_select_paths = NULL;
825 }
826
827 static void
828 pending_select_paths_add (GtkFileChooserDefault *impl,
829                           const GtkFilePath     *path)
830 {
831   impl->pending_select_paths = g_slist_prepend (impl->pending_select_paths, gtk_file_path_copy (path));
832 }
833
834 /* Used from gtk_tree_selection_selected_foreach() */
835 static void
836 store_selection_foreach (GtkTreeModel *model,
837                          GtkTreePath  *path,
838                          GtkTreeIter  *iter,
839                          gpointer      data)
840 {
841   GtkFileChooserDefault *impl;
842   GtkTreeIter child_iter;
843   const GtkFilePath *file_path;
844
845   impl = GTK_FILE_CHOOSER_DEFAULT (data);
846
847   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter);
848
849   file_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
850   pending_select_paths_add (impl, file_path);
851 }
852
853 /* Stores the current selection in the list of paths to select; this is used to
854  * preserve the selection when reloading the current folder.
855  */
856 static void
857 pending_select_paths_store_selection (GtkFileChooserDefault *impl)
858 {
859   GtkTreeSelection *selection;
860
861   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
862   gtk_tree_selection_selected_foreach (selection, store_selection_foreach, impl);
863 }
864
865 static void
866 gtk_file_chooser_default_finalize (GObject *object)
867 {
868   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
869   GSList *l;
870
871   if (impl->shortcuts_filter_model)
872     g_object_unref (impl->shortcuts_filter_model);
873
874   shortcuts_free (impl);
875
876   g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
877   impl->volumes_changed_id = 0;
878   g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
879   impl->bookmarks_changed_id = 0;
880   g_object_unref (impl->file_system);
881
882   for (l = impl->filters; l; l = l->next)
883     {
884       GtkFileFilter *filter;
885
886       filter = GTK_FILE_FILTER (l->data);
887       g_object_unref (filter);
888     }
889   g_slist_free (impl->filters);
890
891   if (impl->current_filter)
892     g_object_unref (impl->current_filter);
893
894   if (impl->current_volume_path)
895     gtk_file_path_free (impl->current_volume_path);
896
897   if (impl->current_folder)
898     gtk_file_path_free (impl->current_folder);
899
900   if (impl->preview_path)
901     gtk_file_path_free (impl->preview_path);
902
903   pending_select_paths_free (impl);
904
905   load_remove_timer (impl);
906
907   /* Free all the Models we have */
908   if (impl->browse_files_model)
909     g_object_unref (impl->browse_files_model);
910
911   if (impl->sort_model)
912     g_object_unref (impl->sort_model);
913
914   g_free (impl->preview_display_name);
915
916   g_free (impl->edited_new_text);
917
918   g_object_unref (impl->tooltips);
919
920   G_OBJECT_CLASS (parent_class)->finalize (object);
921 }
922
923 /* Shows an error dialog set as transient for the specified window */
924 static void
925 error_message_with_parent (GtkWindow  *parent,
926                            const char *msg,
927                            const char *detail)
928 {
929   GtkWidget *dialog;
930
931   dialog = gtk_message_dialog_new (parent,
932                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
933                                    GTK_MESSAGE_ERROR,
934                                    GTK_BUTTONS_OK,
935                                    "%s",
936                                    msg);
937   gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
938                                             "%s", detail);
939   gtk_dialog_run (GTK_DIALOG (dialog));
940   gtk_widget_destroy (dialog);
941 }
942
943 /* Returns a toplevel GtkWindow, or NULL if none */
944 static GtkWindow *
945 get_toplevel (GtkWidget *widget)
946 {
947   GtkWidget *toplevel;
948
949   toplevel = gtk_widget_get_toplevel (widget);
950   if (!GTK_WIDGET_TOPLEVEL (toplevel))
951     return NULL;
952   else
953     return GTK_WINDOW (toplevel);
954 }
955
956 /* Shows an error dialog for the file chooser */
957 static void
958 error_message (GtkFileChooserDefault *impl,
959                const char            *msg,
960                const char            *detail)
961 {
962   error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail);
963 }
964
965 /* Shows a simple error dialog relative to a path.  Frees the GError as well. */
966 static void
967 error_dialog (GtkFileChooserDefault *impl,
968               const char            *msg,
969               const GtkFilePath     *path,
970               GError                *error)
971 {
972   if (error)
973     {
974       char *uri = NULL;
975       char *text;
976
977       if (path)
978         uri = gtk_file_system_path_to_uri (impl->file_system, path);
979       text = g_strdup_printf (msg, uri);
980       error_message (impl, text, error->message);
981       g_free (text);
982       g_free (uri);
983       g_error_free (error);
984     }
985 }
986
987 /* Displays an error message about not being able to get information for a file.
988  * Frees the GError as well.
989  */
990 static void
991 error_getting_info_dialog (GtkFileChooserDefault *impl,
992                            const GtkFilePath     *path,
993                            GError                *error)
994 {
995   error_dialog (impl,
996                 _("Could not retrieve information about the file"),
997                 path, error);
998 }
999
1000 /* Shows an error dialog about not being able to add a bookmark */
1001 static void
1002 error_adding_bookmark_dialog (GtkFileChooserDefault *impl,
1003                               const GtkFilePath     *path,
1004                               GError                *error)
1005 {
1006   error_dialog (impl,
1007                 _("Could not add a bookmark"),
1008                 path, error);
1009 }
1010
1011 /* Shows an error dialog about not being able to remove a bookmark */
1012 static void
1013 error_removing_bookmark_dialog (GtkFileChooserDefault *impl,
1014                                 const GtkFilePath     *path,
1015                                 GError                *error)
1016 {
1017   error_dialog (impl,
1018                 _("Could not remove bookmark"),
1019                 path, error);
1020 }
1021
1022 /* Shows an error dialog about not being able to create a folder */
1023 static void
1024 error_creating_folder_dialog (GtkFileChooserDefault *impl,
1025                               const GtkFilePath     *path,
1026                               GError                *error)
1027 {
1028   error_dialog (impl, 
1029                 _("The folder could not be created"), 
1030                 path, error);
1031 }
1032
1033 /* Shows an error about not being able to create a folder because a file with
1034  * the same name is already there.
1035  */
1036 static void
1037 error_creating_folder_over_existing_file_dialog (GtkFileChooserDefault *impl,
1038                                                  const GtkFilePath     *path,
1039                                                  GError                *error)
1040 {
1041   error_dialog (impl,
1042                 _("The folder could not be created, as a file with the same name "
1043                   "already exists.  Try using a different name for the folder, "
1044                   "or rename the file first."),
1045                 path, error);
1046 }
1047
1048 /* Shows an error dialog about not being able to create a filename */
1049 static void
1050 error_building_filename_dialog (GtkFileChooserDefault *impl,
1051                                 const GtkFilePath     *folder_part,
1052                                 const char            *file_part,
1053                                 GError                *error)
1054 {
1055   error_dialog (impl, _("Invalid file name"), 
1056                 NULL, error);
1057 }
1058
1059 /* Shows an error dialog when we cannot switch to a folder */
1060 static void
1061 error_changing_folder_dialog (GtkFileChooserDefault *impl,
1062                               const GtkFilePath     *path,
1063                               GError                *error)
1064 {
1065   error_dialog (impl, _("The folder contents could not be displayed"),
1066                 path, error);
1067 }
1068
1069 /* Changes folders, displaying an error dialog if this fails */
1070 static gboolean
1071 change_folder_and_display_error (GtkFileChooserDefault *impl,
1072                                  const GtkFilePath     *path)
1073 {
1074   GError *error;
1075   gboolean result;
1076   GtkFilePath *path_copy;
1077
1078   g_return_val_if_fail (path != NULL, FALSE);
1079
1080   profile_start ("start", (char *) path);
1081
1082   /* We copy the path because of this case:
1083    *
1084    * list_row_activated()
1085    *   fetches path from model; path belongs to the model (*)
1086    *   calls change_folder_and_display_error()
1087    *     calls _gtk_file_chooser_set_current_folder_path()
1088    *       changing folders fails, sets model to NULL, thus freeing the path in (*)
1089    */
1090
1091   path_copy = gtk_file_path_copy (path);
1092
1093   error = NULL;
1094   result = gtk_file_chooser_default_update_current_folder (GTK_FILE_CHOOSER (impl), path_copy, TRUE, &error);
1095
1096   if (!result)
1097     error_changing_folder_dialog (impl, path_copy, error);
1098
1099   gtk_file_path_free (path_copy);
1100
1101   profile_end ("end", (char *) path);
1102
1103   return result;
1104 }
1105
1106 static void
1107 update_preview_widget_visibility (GtkFileChooserDefault *impl)
1108 {
1109   if (impl->use_preview_label)
1110     {
1111       if (!impl->preview_label)
1112         {
1113           impl->preview_label = gtk_label_new (impl->preview_display_name);
1114           gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_label, FALSE, FALSE, 0);
1115           gtk_box_reorder_child (GTK_BOX (impl->preview_box), impl->preview_label, 0);
1116           gtk_label_set_ellipsize (GTK_LABEL (impl->preview_label), PANGO_ELLIPSIZE_MIDDLE);
1117           gtk_widget_show (impl->preview_label);
1118         }
1119     }
1120   else
1121     {
1122       if (impl->preview_label)
1123         {
1124           gtk_widget_destroy (impl->preview_label);
1125           impl->preview_label = NULL;
1126         }
1127     }
1128
1129   if (impl->preview_widget_active && impl->preview_widget)
1130     gtk_widget_show (impl->preview_box);
1131   else
1132     gtk_widget_hide (impl->preview_box);
1133
1134   g_signal_emit_by_name (impl, "default-size-changed");
1135 }
1136
1137 static void
1138 set_preview_widget (GtkFileChooserDefault *impl,
1139                     GtkWidget             *preview_widget)
1140 {
1141   if (preview_widget == impl->preview_widget)
1142     return;
1143
1144   if (impl->preview_widget)
1145     gtk_container_remove (GTK_CONTAINER (impl->preview_box),
1146                           impl->preview_widget);
1147
1148   impl->preview_widget = preview_widget;
1149   if (impl->preview_widget)
1150     {
1151       gtk_widget_show (impl->preview_widget);
1152       gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_widget, TRUE, TRUE, 0);
1153       gtk_box_reorder_child (GTK_BOX (impl->preview_box),
1154                              impl->preview_widget,
1155                              (impl->use_preview_label && impl->preview_label) ? 1 : 0);
1156     }
1157
1158   update_preview_widget_visibility (impl);
1159 }
1160
1161 /* Re-reads all the icons for the shortcuts, used when the theme changes */
1162 static void
1163 shortcuts_reload_icons (GtkFileChooserDefault *impl)
1164 {
1165   GtkTreeIter iter;
1166
1167   profile_start ("start", NULL);
1168
1169   if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
1170     goto out;
1171
1172   do {
1173     gpointer data;
1174     gboolean is_volume;
1175     gboolean pixbuf_visible;
1176     GdkPixbuf *pixbuf;
1177
1178     gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
1179                         SHORTCUTS_COL_DATA, &data,
1180                         SHORTCUTS_COL_IS_VOLUME, &is_volume,
1181                         SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible,
1182                         -1);
1183
1184     if (pixbuf_visible && data)
1185       {
1186         if (is_volume)
1187           {
1188             GtkFileSystemVolume *volume;
1189
1190             volume = data;
1191             pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl),
1192                                                          impl->icon_size, NULL);
1193           }
1194         else
1195           {
1196             const GtkFilePath *path;
1197
1198             path = data;
1199             pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
1200                                                   impl->icon_size, NULL);
1201           }
1202
1203         gtk_list_store_set (impl->shortcuts_model, &iter,
1204                             SHORTCUTS_COL_PIXBUF, pixbuf,
1205                             -1);
1206         if (pixbuf)
1207           g_object_unref (pixbuf);
1208       }
1209   } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
1210
1211  out:
1212
1213   profile_end ("end", NULL);
1214 }
1215
1216 static void 
1217 shortcuts_find_folder (GtkFileChooserDefault *impl,
1218                        GtkFilePath           *folder)
1219 {
1220   GtkTreeSelection *selection;
1221   int pos;
1222   GtkTreePath *path;
1223
1224   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1225
1226   g_assert (folder != NULL);
1227   pos = shortcut_find_position (impl, folder);
1228   if (pos == -1)
1229     {
1230       gtk_tree_selection_unselect_all (selection);
1231       return;
1232     }
1233
1234   path = gtk_tree_path_new_from_indices (pos, -1);
1235   gtk_tree_selection_select_path (selection, path);
1236   gtk_tree_path_free (path);
1237 }
1238
1239 /* If a shortcut corresponds to the current folder, selects it */
1240 static void
1241 shortcuts_find_current_folder (GtkFileChooserDefault *impl)
1242 {
1243   shortcuts_find_folder (impl, impl->current_folder);
1244 }
1245
1246 /* Convenience function to get the display name and icon info for a path */
1247 static GtkFileInfo *
1248 get_file_info (GtkFileSystem      *file_system, 
1249                const GtkFilePath  *path, 
1250                gboolean            name_only, 
1251                GError            **error)
1252 {
1253   GtkFilePath *parent_path;
1254   GtkFileFolder *parent_folder;
1255   GtkFileInfo *info;
1256   GError *tmp = NULL;
1257
1258   profile_start ("start", (char *) path);
1259
1260   parent_path = NULL;
1261   info = NULL;
1262
1263   if (!gtk_file_system_get_parent (file_system, path, &parent_path, &tmp))
1264     goto out;
1265
1266   parent_folder = gtk_file_system_get_folder (file_system, parent_path ? parent_path : path,
1267                                               GTK_FILE_INFO_DISPLAY_NAME
1268                                               | (name_only ? 0 : GTK_FILE_INFO_IS_FOLDER),
1269                                               &tmp);
1270   if (!parent_folder)
1271     goto out;
1272
1273   info = gtk_file_folder_get_info (parent_folder, parent_path ? path : NULL, &tmp);
1274   g_object_unref (parent_folder);
1275
1276  out:
1277   if (parent_path)
1278     gtk_file_path_free (parent_path);
1279
1280   if (tmp)
1281     {
1282       g_set_error (error,
1283                    GTK_FILE_CHOOSER_ERROR,
1284                    GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
1285                    _("Could not get information about '%s': %s"), 
1286                    gtk_file_path_get_string (path),
1287                    tmp->message);
1288       g_error_free (tmp);
1289     }
1290
1291   profile_end ("end", (char *) path);
1292
1293   return info;
1294 }
1295
1296 /* Returns whether a path is a folder */
1297 static gboolean
1298 check_is_folder (GtkFileSystem      *file_system, 
1299                  const GtkFilePath  *path, 
1300                  GError            **error)
1301 {
1302   GtkFileFolder *folder;
1303
1304   profile_start ("start", (char *) path);
1305
1306   folder = gtk_file_system_get_folder (file_system, path, 0, error);
1307   if (!folder)
1308     {
1309       profile_end ("end - is not folder", (char *) path);
1310       return FALSE;
1311     }
1312
1313   g_object_unref (folder);
1314
1315   profile_end ("end", (char *) path);
1316   return TRUE;
1317 }
1318
1319 /* Inserts a path in the shortcuts tree, making a copy of it; alternatively,
1320  * inserts a volume.  A position of -1 indicates the end of the tree.
1321  */
1322 static gboolean
1323 shortcuts_insert_path (GtkFileChooserDefault *impl,
1324                        int                    pos,
1325                        gboolean               is_volume,
1326                        GtkFileSystemVolume   *volume,
1327                        const GtkFilePath     *path,
1328                        const char            *label,
1329                        gboolean               removable,
1330                        GError               **error)
1331 {
1332   char *label_copy;
1333   GdkPixbuf *pixbuf;
1334   gpointer data;
1335   GtkTreeIter iter;
1336
1337   profile_start ("start", is_volume ? "volume" : (char *) path);
1338
1339   if (is_volume)
1340     {
1341       data = volume;
1342       label_copy = gtk_file_system_volume_get_display_name (impl->file_system, volume);
1343       pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl),
1344                                                    impl->icon_size, NULL);
1345     }
1346   else
1347     {
1348       if (!check_is_folder (impl->file_system, path, error))
1349         {
1350           profile_end ("end - is not folder", NULL);
1351           return FALSE;
1352         }
1353
1354       if (label)
1355         label_copy = g_strdup (label);
1356       else
1357         {
1358           GtkFileInfo *info = get_file_info (impl->file_system, path, TRUE, error);
1359
1360           if (!info)
1361             {
1362               profile_end ("end - could not get info", (char *) path);
1363               return FALSE;
1364             }
1365
1366           label_copy = g_strdup (gtk_file_info_get_display_name (info));
1367           gtk_file_info_free (info);
1368         }
1369
1370       data = gtk_file_path_copy (path);
1371       pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
1372                                             impl->icon_size, NULL);
1373     }
1374
1375   if (pos == -1)
1376     gtk_list_store_append (impl->shortcuts_model, &iter);
1377   else
1378     gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
1379
1380   gtk_list_store_set (impl->shortcuts_model, &iter,
1381                       SHORTCUTS_COL_PIXBUF, pixbuf,
1382                       SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1383                       SHORTCUTS_COL_NAME, label_copy,
1384                       SHORTCUTS_COL_DATA, data,
1385                       SHORTCUTS_COL_IS_VOLUME, is_volume,
1386                       SHORTCUTS_COL_REMOVABLE, removable,
1387                       -1);
1388
1389   g_free (label_copy);
1390
1391   if (pixbuf)
1392     g_object_unref (pixbuf);
1393
1394   profile_end ("end", NULL);
1395
1396   return TRUE;
1397 }
1398
1399 /* Appends an item for the user's home directory to the shortcuts model */
1400 static void
1401 shortcuts_append_home (GtkFileChooserDefault *impl)
1402 {
1403   const char *home;
1404   GtkFilePath *home_path;
1405   GError *error;
1406
1407   profile_start ("start", NULL);
1408
1409   home = g_get_home_dir ();
1410   if (home == NULL)
1411     {
1412       profile_end ("end - no home directory!?", NULL);
1413       return;
1414     }
1415
1416   home_path = gtk_file_system_filename_to_path (impl->file_system, home);
1417
1418   error = NULL;
1419   impl->has_home = shortcuts_insert_path (impl, -1, FALSE, NULL, home_path, _("Home"), FALSE, &error);
1420   if (!impl->has_home)
1421     error_getting_info_dialog (impl, home_path, error);
1422
1423   gtk_file_path_free (home_path);
1424
1425   profile_end ("end", NULL);
1426 }
1427
1428 /* Appends the ~/Desktop directory to the shortcuts model */
1429 static void
1430 shortcuts_append_desktop (GtkFileChooserDefault *impl)
1431 {
1432   char *name;
1433   GtkFilePath *path;
1434
1435   profile_start ("start", NULL);
1436
1437 #ifdef G_OS_WIN32
1438   name = _gtk_file_system_win32_get_desktop ();
1439 #else
1440   const char *home = g_get_home_dir ();
1441   if (home == NULL)
1442     {
1443       profile_end ("end - no home directory!?", NULL);
1444       return;
1445     }
1446
1447   name = g_build_filename (home, "Desktop", NULL);
1448 #endif
1449
1450   path = gtk_file_system_filename_to_path (impl->file_system, name);
1451   g_free (name);
1452
1453   impl->has_desktop = shortcuts_insert_path (impl, -1, FALSE, NULL, path, _("Desktop"), FALSE, NULL);
1454   /* We do not actually pop up an error dialog if there is no desktop directory
1455    * because some people may really not want to have one.
1456    */
1457
1458   gtk_file_path_free (path);
1459
1460   profile_end ("end", NULL);
1461 }
1462
1463 /* Appends a list of GtkFilePath to the shortcuts model; returns how many were inserted */
1464 static int
1465 shortcuts_append_paths (GtkFileChooserDefault *impl,
1466                         GSList                *paths)
1467 {
1468   int start_row;
1469   int num_inserted;
1470   gchar *label;
1471
1472   profile_start ("start", NULL);
1473
1474   /* As there is no separator now, we want to start there.
1475    */
1476   start_row = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
1477   num_inserted = 0;
1478
1479   for (; paths; paths = paths->next)
1480     {
1481       GtkFilePath *path;
1482       GError *error;
1483
1484       path = paths->data;
1485       error = NULL;
1486
1487       if (impl->local_only &&
1488           !gtk_file_system_path_is_local (impl->file_system, path))
1489         continue;
1490
1491       label = gtk_file_system_get_bookmark_label (impl->file_system, path);
1492
1493       /* NULL GError, but we don't really want to show error boxes here */
1494       if (shortcuts_insert_path (impl, start_row + num_inserted, FALSE, NULL, path, label, TRUE, NULL))
1495         num_inserted++;
1496
1497       g_free (label);
1498     }
1499
1500   profile_end ("end", NULL);
1501
1502   return num_inserted;
1503 }
1504
1505 /* Returns the index for the corresponding item in the shortcuts bar */
1506 static int
1507 shortcuts_get_index (GtkFileChooserDefault *impl,
1508                      ShortcutsIndex         where)
1509 {
1510   int n;
1511
1512   n = 0;
1513
1514   if (where == SHORTCUTS_HOME)
1515     goto out;
1516
1517   n += impl->has_home ? 1 : 0;
1518
1519   if (where == SHORTCUTS_DESKTOP)
1520     goto out;
1521
1522   n += impl->has_desktop ? 1 : 0;
1523
1524   if (where == SHORTCUTS_VOLUMES)
1525     goto out;
1526
1527   n += impl->num_volumes;
1528
1529   if (where == SHORTCUTS_SHORTCUTS)
1530     goto out;
1531
1532   n += impl->num_shortcuts;
1533
1534   if (where == SHORTCUTS_BOOKMARKS_SEPARATOR)
1535     goto out;
1536
1537   /* If there are no bookmarks there won't be a separator */
1538   n += (impl->num_bookmarks > 0) ? 1 : 0;
1539
1540   if (where == SHORTCUTS_BOOKMARKS)
1541     goto out;
1542
1543   n += impl->num_bookmarks;
1544
1545   if (where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR)
1546     goto out;
1547
1548   n += 1;
1549
1550   if (where == SHORTCUTS_CURRENT_FOLDER)
1551     goto out;
1552
1553   g_assert_not_reached ();
1554
1555  out:
1556
1557   return n;
1558 }
1559
1560 /* Removes the specified number of rows from the shortcuts list */
1561 static void
1562 shortcuts_remove_rows (GtkFileChooserDefault *impl,
1563                        int                    start_row,
1564                        int                    n_rows)
1565 {
1566   GtkTreePath *path;
1567
1568   path = gtk_tree_path_new_from_indices (start_row, -1);
1569
1570   for (; n_rows; n_rows--)
1571     {
1572       GtkTreeIter iter;
1573
1574       if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
1575         g_assert_not_reached ();
1576
1577       shortcuts_free_row_data (impl, &iter);
1578       gtk_list_store_remove (impl->shortcuts_model, &iter);
1579     }
1580
1581   gtk_tree_path_free (path);
1582 }
1583
1584 /* Adds all the file system volumes to the shortcuts model */
1585 static void
1586 shortcuts_add_volumes (GtkFileChooserDefault *impl)
1587 {
1588   int start_row;
1589   GSList *list, *l;
1590   int n;
1591   gboolean old_changing_folders;
1592
1593   profile_start ("start", NULL);
1594
1595   old_changing_folders = impl->changing_folder;
1596   impl->changing_folder = TRUE;
1597
1598   start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
1599   shortcuts_remove_rows (impl, start_row, impl->num_volumes);
1600   impl->num_volumes = 0;
1601
1602   list = gtk_file_system_list_volumes (impl->file_system);
1603
1604   n = 0;
1605
1606   for (l = list; l; l = l->next)
1607     {
1608       GtkFileSystemVolume *volume;
1609
1610       volume = l->data;
1611
1612       if (impl->local_only)
1613         {
1614           GtkFilePath *base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
1615           gboolean is_local = gtk_file_system_path_is_local (impl->file_system, base_path);
1616           gtk_file_path_free (base_path);
1617
1618           if (!is_local)
1619             {
1620               gtk_file_system_volume_free (impl->file_system, volume);
1621               continue;
1622             }
1623         }
1624
1625       if (shortcuts_insert_path (impl, start_row + n, TRUE, volume, NULL, NULL, FALSE, NULL))
1626         n++;
1627       else
1628         gtk_file_system_volume_free (impl->file_system, volume);
1629     }
1630
1631   impl->num_volumes = n;
1632   g_slist_free (list);
1633
1634   if (impl->shortcuts_filter_model)
1635     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
1636
1637   impl->changing_folder = old_changing_folders;
1638
1639   profile_end ("end", NULL);
1640 }
1641
1642 /* Inserts a separator node in the shortcuts list */
1643 static void
1644 shortcuts_insert_separator (GtkFileChooserDefault *impl,
1645                             ShortcutsIndex where)
1646 {
1647   GtkTreeIter iter;
1648
1649   g_assert (where == SHORTCUTS_BOOKMARKS_SEPARATOR || where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1650
1651   gtk_list_store_insert (impl->shortcuts_model, &iter,
1652                          shortcuts_get_index (impl, where));
1653   gtk_list_store_set (impl->shortcuts_model, &iter,
1654                       SHORTCUTS_COL_PIXBUF, NULL,
1655                       SHORTCUTS_COL_PIXBUF_VISIBLE, FALSE,
1656                       SHORTCUTS_COL_NAME, NULL,
1657                       SHORTCUTS_COL_DATA, NULL,
1658                       -1);
1659 }
1660
1661 /* Updates the list of bookmarks */
1662 static void
1663 shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
1664 {
1665   GSList *bookmarks;
1666   gboolean old_changing_folders;
1667   GtkTreeIter iter;
1668   GtkFilePath *list_selected = NULL;
1669   GtkFilePath *combo_selected = NULL;
1670   gboolean is_volume;
1671   gpointer col_data;
1672
1673   profile_start ("start", NULL);
1674         
1675   old_changing_folders = impl->changing_folder;
1676   impl->changing_folder = TRUE;
1677
1678   if (shortcuts_get_selected (impl, &iter))
1679     {
1680       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), 
1681                           &iter, 
1682                           SHORTCUTS_COL_DATA, &col_data,
1683                           SHORTCUTS_COL_IS_VOLUME, &is_volume,
1684                           -1);
1685
1686       if (col_data && !is_volume)
1687         list_selected = gtk_file_path_copy (col_data);
1688     }
1689
1690   if (impl->save_folder_combo &&
1691       gtk_combo_box_get_active_iter (GTK_COMBO_BOX (impl->save_folder_combo), 
1692                                      &iter))
1693     {
1694       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), 
1695                           &iter, 
1696                           SHORTCUTS_COL_DATA, &col_data,
1697                           SHORTCUTS_COL_IS_VOLUME, &is_volume,
1698                           -1);
1699       
1700       if (col_data && !is_volume)
1701         combo_selected = gtk_file_path_copy (col_data);
1702     }
1703
1704   if (impl->num_bookmarks > 0)
1705     shortcuts_remove_rows (impl,
1706                            shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR),
1707                            impl->num_bookmarks + 1);
1708
1709   bookmarks = gtk_file_system_list_bookmarks (impl->file_system);
1710   impl->num_bookmarks = shortcuts_append_paths (impl, bookmarks);
1711   gtk_file_paths_free (bookmarks);
1712
1713   if (impl->num_bookmarks > 0)
1714     shortcuts_insert_separator (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
1715
1716   if (impl->shortcuts_filter_model)
1717     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
1718
1719   if (list_selected)
1720     {
1721       shortcuts_find_folder (impl, list_selected);
1722       gtk_file_path_free (list_selected);
1723     }
1724
1725   if (combo_selected)
1726     {
1727       gint pos;
1728
1729       pos = shortcut_find_position (impl, combo_selected);
1730       if (pos != -1)
1731         gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), 
1732                                   pos);
1733       gtk_file_path_free (combo_selected);
1734     }
1735   
1736   impl->changing_folder = old_changing_folders;
1737
1738   profile_end ("end", NULL);
1739 }
1740
1741 /* Appends a separator and a row to the shortcuts list for the current folder */
1742 static void
1743 shortcuts_add_current_folder (GtkFileChooserDefault *impl)
1744 {
1745   int pos;
1746   gboolean success;
1747
1748   g_assert (!impl->shortcuts_current_folder_active);
1749
1750   success = TRUE;
1751
1752   g_assert (impl->current_folder != NULL);
1753
1754   pos = shortcut_find_position (impl, impl->current_folder);
1755   if (pos == -1)
1756     {
1757       GtkFileSystemVolume *volume;
1758       GtkFilePath *base_path;
1759
1760       /* Separator */
1761
1762       shortcuts_insert_separator (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1763
1764       /* Item */
1765
1766       pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER);
1767
1768       volume = gtk_file_system_get_volume_for_path (impl->file_system, impl->current_folder);
1769       if (volume)
1770         base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
1771       else
1772         base_path = NULL;
1773
1774       if (base_path &&
1775           strcmp (gtk_file_path_get_string (base_path), gtk_file_path_get_string (impl->current_folder)) == 0)
1776         {
1777           success = shortcuts_insert_path (impl, pos, TRUE, volume, NULL, NULL, FALSE, NULL);
1778           if (success)
1779             volume = NULL;
1780         }
1781       else
1782         success = shortcuts_insert_path (impl, pos, FALSE, NULL, impl->current_folder, NULL, FALSE, NULL);
1783
1784       if (volume)
1785         gtk_file_system_volume_free (impl->file_system, volume);
1786
1787       if (base_path)
1788         gtk_file_path_free (base_path);
1789
1790       if (!success)
1791         shortcuts_remove_rows (impl, pos - 1, 1); /* remove the separator */
1792
1793       impl->shortcuts_current_folder_active = success;
1794     }
1795
1796   if (success)
1797     gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), pos);
1798 }
1799
1800 /* Updates the current folder row in the shortcuts model */
1801 static void
1802 shortcuts_update_current_folder (GtkFileChooserDefault *impl)
1803 {
1804   int pos;
1805
1806   pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1807
1808   if (impl->shortcuts_current_folder_active)
1809     {
1810       shortcuts_remove_rows (impl, pos, 2);
1811       impl->shortcuts_current_folder_active = FALSE;
1812     }
1813
1814   shortcuts_add_current_folder (impl);
1815 }
1816
1817 /* Filter function used for the shortcuts filter model */
1818 static gboolean
1819 shortcuts_filter_cb (GtkTreeModel          *model,
1820                      GtkTreeIter           *iter,
1821                      gpointer               data)
1822 {
1823   GtkFileChooserDefault *impl;
1824   GtkTreePath *path;
1825   int pos;
1826
1827   impl = GTK_FILE_CHOOSER_DEFAULT (data);
1828
1829   path = gtk_tree_model_get_path (model, iter);
1830   if (!path)
1831     return FALSE;
1832
1833   pos = *gtk_tree_path_get_indices (path);
1834   gtk_tree_path_free (path);
1835
1836   return (pos < shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR));
1837 }
1838
1839 /* Creates the list model for shortcuts */
1840 static void
1841 shortcuts_model_create (GtkFileChooserDefault *impl)
1842 {
1843   /* Keep this order in sync with the SHORCUTS_COL_* enum values */
1844   impl->shortcuts_model = gtk_list_store_new (SHORTCUTS_COL_NUM_COLUMNS,
1845                                               GDK_TYPE_PIXBUF,  /* pixbuf */
1846                                               G_TYPE_STRING,    /* name */
1847                                               G_TYPE_POINTER,   /* path or volume */
1848                                               G_TYPE_BOOLEAN,   /* is the previous column a volume? */
1849                                               G_TYPE_BOOLEAN,   /* removable */
1850                                               G_TYPE_BOOLEAN);  /* pixbuf cell visibility */
1851
1852   if (impl->file_system)
1853     {
1854       shortcuts_append_home (impl);
1855       shortcuts_append_desktop (impl);
1856       shortcuts_add_volumes (impl);
1857       shortcuts_add_bookmarks (impl);
1858     }
1859
1860   impl->shortcuts_filter_model = shortcuts_model_filter_new (impl,
1861                                                              GTK_TREE_MODEL (impl->shortcuts_model),
1862                                                              NULL);
1863
1864   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
1865                                           shortcuts_filter_cb,
1866                                           impl,
1867                                           NULL);
1868 }
1869
1870 /* Callback used when the "New Folder" button is clicked */
1871 static void
1872 new_folder_button_clicked (GtkButton             *button,
1873                            GtkFileChooserDefault *impl)
1874 {
1875   GtkTreeIter iter;
1876   GtkTreePath *path;
1877
1878   if (!impl->browse_files_model)
1879     return; /* FIXME: this sucks.  Disable the New Folder button or something. */
1880
1881   /* Prevent button from being clicked twice */
1882   gtk_widget_set_sensitive (impl->browse_new_folder_button, FALSE);
1883
1884   _gtk_file_system_model_add_editable (impl->browse_files_model, &iter);
1885
1886   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->browse_files_model), &iter);
1887   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_files_tree_view),
1888                                 path, impl->list_name_column,
1889                                 FALSE, 0.0, 0.0);
1890
1891   g_object_set (impl->list_name_renderer, "editable", TRUE, NULL);
1892   gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view),
1893                             path,
1894                             impl->list_name_column,
1895                             TRUE);
1896
1897   gtk_tree_path_free (path);
1898 }
1899
1900 /* Idle handler for creating a new folder after editing its name cell, or for
1901  * canceling the editing.
1902  */
1903 static gboolean
1904 edited_idle_cb (GtkFileChooserDefault *impl)
1905 {
1906   GDK_THREADS_ENTER ();
1907   
1908   g_source_destroy (impl->edited_idle);
1909   impl->edited_idle = NULL;
1910
1911   _gtk_file_system_model_remove_editable (impl->browse_files_model);
1912   g_object_set (impl->list_name_renderer, "editable", FALSE, NULL);
1913
1914   gtk_widget_set_sensitive (impl->browse_new_folder_button, TRUE);
1915
1916   if (impl->edited_new_text) /* not cancelled? */
1917     {
1918       GError *error;
1919       GtkFilePath *file_path;
1920
1921       error = NULL;
1922       file_path = gtk_file_system_make_path (impl->file_system, impl->current_folder, impl->edited_new_text,
1923                                              &error);
1924       if (file_path)
1925         {
1926           error = NULL;
1927           if (gtk_file_system_create_folder (impl->file_system, file_path, &error))
1928             change_folder_and_display_error (impl, file_path);
1929           else
1930             error_creating_folder_dialog (impl, file_path, error);
1931
1932           gtk_file_path_free (file_path);
1933         }
1934       else
1935         error_creating_folder_dialog (impl, file_path, error);
1936
1937       g_free (impl->edited_new_text);
1938       impl->edited_new_text = NULL;
1939     }
1940
1941   GDK_THREADS_LEAVE ();
1942
1943   return FALSE;
1944 }
1945
1946 static void
1947 queue_edited_idle (GtkFileChooserDefault *impl,
1948                    const gchar           *new_text)
1949 {
1950   /* We create the folder in an idle handler so that we don't modify the tree
1951    * just now.
1952    */
1953
1954   g_assert (!impl->edited_idle);
1955   g_assert (!impl->edited_new_text);
1956
1957   impl->edited_idle = g_idle_source_new ();
1958   g_source_set_closure (impl->edited_idle,
1959                         g_cclosure_new_object (G_CALLBACK (edited_idle_cb),
1960                                                G_OBJECT (impl)));
1961   g_source_attach (impl->edited_idle, NULL);
1962
1963   if (new_text)
1964     impl->edited_new_text = g_strdup (new_text);
1965 }
1966
1967 /* Callback used from the text cell renderer when the new folder is named */
1968 static void
1969 renderer_edited_cb (GtkCellRendererText   *cell_renderer_text,
1970                     const gchar           *path,
1971                     const gchar           *new_text,
1972                     GtkFileChooserDefault *impl)
1973 {
1974  /* work around bug #154921 */
1975   g_object_set (cell_renderer_text, 
1976                 "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
1977    queue_edited_idle (impl, new_text);
1978 }
1979
1980 /* Callback used from the text cell renderer when the new folder edition gets
1981  * canceled.
1982  */
1983 static void
1984 renderer_editing_canceled_cb (GtkCellRendererText   *cell_renderer_text,
1985                               GtkFileChooserDefault *impl)
1986 {
1987  /* work around bug #154921 */
1988   g_object_set (cell_renderer_text, 
1989                 "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
1990    queue_edited_idle (impl, NULL);
1991 }
1992
1993 /* Creates the widgets for the filter combo box */
1994 static GtkWidget *
1995 filter_create (GtkFileChooserDefault *impl)
1996 {
1997   impl->filter_combo = gtk_combo_box_new_text ();
1998   gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (impl->filter_combo), FALSE);
1999
2000   g_signal_connect (impl->filter_combo, "changed",
2001                     G_CALLBACK (filter_combo_changed), impl);
2002
2003   return impl->filter_combo;
2004 }
2005
2006 static GtkWidget *
2007 button_new (GtkFileChooserDefault *impl,
2008             const char *text,
2009             const char *stock_id,
2010             gboolean    sensitive,
2011             gboolean    show,
2012             GCallback   callback)
2013 {
2014   GtkWidget *button;
2015   GtkWidget *image;
2016
2017   button = gtk_button_new_with_mnemonic (text);
2018   image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
2019   gtk_button_set_image (GTK_BUTTON (button), image);
2020
2021   gtk_widget_set_sensitive (button, sensitive);
2022   g_signal_connect (button, "clicked", callback, impl);
2023
2024   if (show)
2025     gtk_widget_show (button);
2026
2027   return button;
2028 }
2029
2030 /* Looks for a path among the shortcuts; returns its index or -1 if it doesn't exist */
2031 static int
2032 shortcut_find_position (GtkFileChooserDefault *impl,
2033                         const GtkFilePath     *path)
2034 {
2035   GtkTreeIter iter;
2036   int i;
2037   int current_folder_separator_idx;
2038
2039   if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
2040     return -1;
2041
2042   current_folder_separator_idx = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2043
2044   for (i = 0; i < current_folder_separator_idx; i++)
2045     {
2046       gpointer col_data;
2047       gboolean is_volume;
2048
2049       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2050                           SHORTCUTS_COL_DATA, &col_data,
2051                           SHORTCUTS_COL_IS_VOLUME, &is_volume,
2052                           -1);
2053
2054       if (col_data)
2055         {
2056           if (is_volume)
2057             {
2058               GtkFileSystemVolume *volume;
2059               GtkFilePath *base_path;
2060               gboolean exists;
2061
2062               volume = col_data;
2063               base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
2064
2065               exists = base_path && strcmp (gtk_file_path_get_string (path),
2066                                             gtk_file_path_get_string (base_path)) == 0;
2067               g_free (base_path);
2068
2069               if (exists)
2070                 return i;
2071             }
2072           else
2073             {
2074               GtkFilePath *model_path;
2075
2076               model_path = col_data;
2077
2078               if (model_path && gtk_file_path_compare (model_path, path) == 0)
2079                 return i;
2080             }
2081         }
2082
2083       gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
2084     }
2085
2086   return -1;
2087 }
2088
2089 /* Tries to add a bookmark from a path name */
2090 static gboolean
2091 shortcuts_add_bookmark_from_path (GtkFileChooserDefault *impl,
2092                                   const GtkFilePath     *path,
2093                                   int                    pos)
2094 {
2095   GError *error;
2096
2097   g_return_val_if_fail (path != NULL, FALSE);
2098  
2099   if (shortcut_find_position (impl, path) != -1)
2100     return FALSE;
2101
2102   /* FIXME: this check really belongs in gtk_file_system_insert_bookmark.  */
2103   error = NULL;
2104   if (!check_is_folder (impl->file_system, path, &error))
2105     {
2106       error_adding_bookmark_dialog (impl, path, error);
2107       return FALSE;
2108     }
2109
2110   error = NULL;
2111   if (!gtk_file_system_insert_bookmark (impl->file_system, path, pos, &error))
2112     {
2113       error_adding_bookmark_dialog (impl, path, error);
2114       return FALSE;
2115     }
2116
2117   return TRUE;
2118 }
2119
2120 static void
2121 add_bookmark_foreach_cb (GtkTreeModel *model,
2122                          GtkTreePath  *path,
2123                          GtkTreeIter  *iter,
2124                          gpointer      data)
2125 {
2126   GtkFileChooserDefault *impl;
2127   GtkFileSystemModel *fs_model;
2128   GtkTreeIter child_iter;
2129   const GtkFilePath *file_path;
2130
2131   impl = (GtkFileChooserDefault *) data;
2132
2133   fs_model = impl->browse_files_model;
2134   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter);
2135
2136   file_path = _gtk_file_system_model_get_path (fs_model, &child_iter);
2137   shortcuts_add_bookmark_from_path (impl, file_path, -1);
2138 }
2139
2140 /* Adds a bookmark from the currently selected item in the file list */
2141 static void
2142 bookmarks_add_selected_folder (GtkFileChooserDefault *impl)
2143 {
2144   GtkTreeSelection *selection;
2145
2146   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2147
2148   if (gtk_tree_selection_count_selected_rows (selection) == 0)
2149     shortcuts_add_bookmark_from_path (impl, impl->current_folder, -1);
2150   else
2151     gtk_tree_selection_selected_foreach (selection,
2152                                          add_bookmark_foreach_cb,
2153                                          impl);
2154 }
2155
2156 /* Callback used when the "Add bookmark" button is clicked */
2157 static void
2158 add_bookmark_button_clicked_cb (GtkButton *button,
2159                                 GtkFileChooserDefault *impl)
2160 {
2161   bookmarks_add_selected_folder (impl);
2162 }
2163
2164 /* Returns TRUE plus an iter in the shortcuts_model if a row is selected;
2165  * returns FALSE if no shortcut is selected.
2166  */
2167 static gboolean
2168 shortcuts_get_selected (GtkFileChooserDefault *impl,
2169                         GtkTreeIter           *iter)
2170 {
2171   GtkTreeSelection *selection;
2172   GtkTreeIter parent_iter;
2173
2174   if (!impl->browse_shortcuts_tree_view)
2175     return FALSE;
2176
2177   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
2178
2179   if (!gtk_tree_selection_get_selected (selection, NULL, &parent_iter))
2180     return FALSE;
2181
2182   gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
2183                                                     iter,
2184                                                     &parent_iter);
2185   return TRUE;
2186 }
2187
2188 /* Removes the selected bookmarks */
2189 static void
2190 remove_selected_bookmarks (GtkFileChooserDefault *impl)
2191 {
2192   GtkTreeIter iter;
2193   gpointer col_data;
2194   GtkFilePath *path;
2195   gboolean removable;
2196   GError *error;
2197
2198   if (!shortcuts_get_selected (impl, &iter))
2199     return;
2200
2201   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2202                       SHORTCUTS_COL_DATA, &col_data,
2203                       SHORTCUTS_COL_REMOVABLE, &removable,
2204                       -1);
2205   g_assert (col_data != NULL);
2206
2207   if (!removable)
2208     return;
2209
2210   path = col_data;
2211
2212   error = NULL;
2213   if (!gtk_file_system_remove_bookmark (impl->file_system, path, &error))
2214     error_removing_bookmark_dialog (impl, path, error);
2215 }
2216
2217 /* Callback used when the "Remove bookmark" button is clicked */
2218 static void
2219 remove_bookmark_button_clicked_cb (GtkButton *button,
2220                                    GtkFileChooserDefault *impl)
2221 {
2222   remove_selected_bookmarks (impl);
2223 }
2224
2225 struct selection_check_closure {
2226   GtkFileChooserDefault *impl;
2227   int num_selected;
2228   gboolean all_files;
2229   gboolean all_folders;
2230 };
2231
2232 /* Used from gtk_tree_selection_selected_foreach() */
2233 static void
2234 selection_check_foreach_cb (GtkTreeModel *model,
2235                             GtkTreePath  *path,
2236                             GtkTreeIter  *iter,
2237                             gpointer      data)
2238 {
2239   struct selection_check_closure *closure;
2240   GtkTreeIter child_iter;
2241   const GtkFileInfo *info;
2242   gboolean is_folder;
2243
2244   closure = data;
2245   closure->num_selected++;
2246
2247   gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
2248
2249   info = _gtk_file_system_model_get_info (closure->impl->browse_files_model, &child_iter);
2250   is_folder = info ? gtk_file_info_get_is_folder (info) : FALSE;
2251
2252   closure->all_folders = closure->all_folders && is_folder;
2253   closure->all_files = closure->all_files && !is_folder;
2254 }
2255
2256 /* Checks whether the selected items in the file list are all files or all folders */
2257 static void
2258 selection_check (GtkFileChooserDefault *impl,
2259                  gint                  *num_selected,
2260                  gboolean              *all_files,
2261                  gboolean              *all_folders)
2262 {
2263   struct selection_check_closure closure;
2264   GtkTreeSelection *selection;
2265
2266   closure.impl = impl;
2267   closure.num_selected = 0;
2268   closure.all_files = TRUE;
2269   closure.all_folders = TRUE;
2270
2271   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2272   gtk_tree_selection_selected_foreach (selection,
2273                                        selection_check_foreach_cb,
2274                                        &closure);
2275
2276   g_assert (closure.num_selected == 0 || !(closure.all_files && closure.all_folders));
2277
2278   if (num_selected)
2279     *num_selected = closure.num_selected;
2280
2281   if (all_files)
2282     *all_files = closure.all_files;
2283
2284   if (all_folders)
2285     *all_folders = closure.all_folders;
2286 }
2287
2288 struct get_selected_path_closure {
2289   GtkFileChooserDefault *impl;
2290   const GtkFilePath *path;
2291 };
2292
2293 static void
2294 get_selected_path_foreach_cb (GtkTreeModel *model,
2295                               GtkTreePath  *path,
2296                               GtkTreeIter  *iter,
2297                               gpointer      data)
2298 {
2299   struct get_selected_path_closure *closure;
2300   GtkTreeIter child_iter;
2301
2302   closure = data;
2303
2304   gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
2305   closure->path = _gtk_file_system_model_get_path (closure->impl->browse_files_model, &child_iter);
2306 }
2307
2308 /* Returns a selected path from the file list */
2309 static const GtkFilePath *
2310 get_selected_path (GtkFileChooserDefault *impl)
2311 {
2312   struct get_selected_path_closure closure;
2313   GtkTreeSelection *selection;
2314
2315   closure.impl = impl;
2316   closure.path = NULL;
2317
2318   selection =  gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2319   gtk_tree_selection_selected_foreach (selection,
2320                                        get_selected_path_foreach_cb,
2321                                        &closure);
2322
2323   return closure.path;
2324 }
2325
2326 typedef struct {
2327   GtkFileChooserDefault *impl;
2328   gchar *tip;
2329 } UpdateTooltipData;
2330
2331 static void 
2332 update_tooltip (GtkTreeModel      *model,
2333                 GtkTreePath       *path,
2334                 GtkTreeIter       *iter,
2335                 gpointer           data)
2336 {
2337   UpdateTooltipData *udata = data;
2338   GtkTreeIter child_iter;
2339   const GtkFileInfo *info;
2340
2341   if (udata->tip == NULL)
2342     {
2343       gtk_tree_model_sort_convert_iter_to_child_iter (udata->impl->sort_model,
2344                                                       &child_iter,
2345                                                       iter);
2346   
2347       info = _gtk_file_system_model_get_info (udata->impl->browse_files_model, &child_iter);
2348       udata->tip = g_strdup_printf (_("Add the folder '%s' to the bookmarks"),
2349                                     gtk_file_info_get_display_name (info));
2350     }
2351 }
2352
2353
2354 /* Sensitize the "add bookmark" button if all the selected items are folders, or
2355  * if there are no selected items *and* the current folder is not in the
2356  * bookmarks list.  De-sensitize the button otherwise.
2357  */
2358 static void
2359 bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl)
2360 {
2361   gint num_selected;
2362   gboolean all_folders;
2363   gboolean active;
2364   gchar *tip;
2365
2366   selection_check (impl, &num_selected, NULL, &all_folders);
2367
2368   if (num_selected == 0)
2369     active = (impl->current_folder != NULL) && (shortcut_find_position (impl, impl->current_folder) == -1);
2370   else if (num_selected == 1)
2371     {
2372       const GtkFilePath *path;
2373
2374       path = get_selected_path (impl);
2375       active = all_folders && (shortcut_find_position (impl, path) == -1);
2376     }
2377   else
2378     active = all_folders;
2379
2380   gtk_widget_set_sensitive (impl->browse_shortcuts_add_button, active);
2381
2382   if (impl->browse_files_popup_menu_add_shortcut_item)
2383     gtk_widget_set_sensitive (impl->browse_files_popup_menu_add_shortcut_item,
2384                               (num_selected == 0) ? FALSE : active);
2385
2386   if (active)
2387     {
2388       if (num_selected == 0)
2389         tip = g_strdup_printf (_("Add the current folder to the bookmarks"));    
2390       else if (num_selected > 1)
2391         tip = g_strdup_printf (_("Add the selected folders to the bookmarks"));
2392       else
2393         {
2394           GtkTreeSelection *selection;
2395           UpdateTooltipData data;
2396           
2397           selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2398           data.impl = impl;
2399           data.tip = NULL;
2400           gtk_tree_selection_selected_foreach (selection, update_tooltip, &data);
2401           tip = data.tip;
2402           
2403         }
2404       gtk_tooltips_set_tip (impl->tooltips, impl->browse_shortcuts_add_button, tip, NULL);
2405       g_free (tip);
2406     }
2407 }
2408
2409 /* Sets the sensitivity of the "remove bookmark" button depending on whether a
2410  * bookmark row is selected in the shortcuts tree.
2411  */
2412 static void
2413 bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl)
2414 {
2415   GtkTreeIter iter;
2416   gboolean removable = FALSE;
2417   gchar *name = NULL;
2418   
2419   if (shortcuts_get_selected (impl, &iter))
2420     gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2421                         SHORTCUTS_COL_REMOVABLE, &removable,
2422                         SHORTCUTS_COL_NAME, &name,
2423                         -1);
2424
2425   gtk_widget_set_sensitive (impl->browse_shortcuts_remove_button, removable);
2426
2427   if (removable)
2428     {
2429       gchar *tip;
2430
2431       tip = g_strdup_printf (_("Remove the bookmark '%s'"), name);
2432       gtk_tooltips_set_tip (impl->tooltips, impl->browse_shortcuts_remove_button,
2433                             tip, NULL);
2434       g_free (tip);
2435     }
2436
2437   g_free (name);
2438 }
2439
2440 static void
2441 shortcuts_check_popup_sensitivity (GtkFileChooserDefault *impl)
2442 {
2443   GtkTreeIter iter;
2444   gboolean removable = FALSE;
2445
2446   if (impl->browse_shortcuts_popup_menu == NULL)
2447     return;
2448
2449   if (shortcuts_get_selected (impl, &iter))
2450     gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2451                         SHORTCUTS_COL_REMOVABLE, &removable,
2452                         -1);
2453
2454   gtk_widget_set_sensitive (impl->browse_shortcuts_popup_menu_remove_item, removable);
2455   gtk_widget_set_sensitive (impl->browse_shortcuts_popup_menu_rename_item, removable);
2456 }
2457
2458 /* GtkWidget::drag-begin handler for the shortcuts list. */
2459 static void
2460 shortcuts_drag_begin_cb (GtkWidget             *widget,
2461                          GdkDragContext        *context,
2462                          GtkFileChooserDefault *impl)
2463 {
2464 #if 0
2465   impl->shortcuts_drag_context = g_object_ref (context);
2466 #endif
2467 }
2468
2469 #if 0
2470 /* Removes the idle handler for outside drags */
2471 static void
2472 shortcuts_cancel_drag_outside_idle (GtkFileChooserDefault *impl)
2473 {
2474   if (!impl->shortcuts_drag_outside_idle)
2475     return;
2476
2477   g_source_destroy (impl->shortcuts_drag_outside_idle);
2478   impl->shortcuts_drag_outside_idle = NULL;
2479 }
2480 #endif
2481
2482 /* GtkWidget::drag-end handler for the shortcuts list. */
2483 static void
2484 shortcuts_drag_end_cb (GtkWidget             *widget,
2485                        GdkDragContext        *context,
2486                        GtkFileChooserDefault *impl)
2487 {
2488 #if 0
2489   g_object_unref (impl->shortcuts_drag_context);
2490
2491   shortcuts_cancel_drag_outside_idle (impl);
2492
2493   if (!impl->shortcuts_drag_outside)
2494     return;
2495
2496   gtk_button_clicked (GTK_BUTTON (impl->browse_shortcuts_remove_button));
2497
2498   impl->shortcuts_drag_outside = FALSE;
2499 #endif
2500 }
2501
2502 /* GtkWidget::drag-data-delete handler for the shortcuts list. */
2503 static void
2504 shortcuts_drag_data_delete_cb (GtkWidget             *widget,
2505                                GdkDragContext        *context,
2506                                GtkFileChooserDefault *impl)
2507 {
2508   g_signal_stop_emission_by_name (widget, "drag-data-delete");
2509 }
2510
2511 #if 0
2512 /* Creates a suitable drag cursor to indicate that the selected bookmark will be
2513  * deleted or not.
2514  */
2515 static void
2516 shortcuts_drag_set_delete_cursor (GtkFileChooserDefault *impl,
2517                                   gboolean               delete)
2518 {
2519   GtkTreeView *tree_view;
2520   GtkTreeIter iter;
2521   GtkTreePath *path;
2522   GdkPixmap *row_pixmap;
2523   GdkBitmap *mask;
2524   int row_pixmap_y;
2525   int cell_y;
2526
2527   tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
2528
2529   /* Find the selected path and get its drag pixmap */
2530
2531   if (!shortcuts_get_selected (impl, &iter))
2532     g_assert_not_reached ();
2533
2534   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
2535
2536   row_pixmap = gtk_tree_view_create_row_drag_icon (tree_view, path);
2537   gtk_tree_path_free (path);
2538
2539   mask = NULL;
2540   row_pixmap_y = 0;
2541
2542   if (delete)
2543     {
2544       GdkPixbuf *pixbuf;
2545
2546       pixbuf = gtk_widget_render_icon (impl->browse_shortcuts_tree_view,
2547                                        GTK_STOCK_DELETE,
2548                                        GTK_ICON_SIZE_DND,
2549                                        NULL);
2550       if (pixbuf)
2551         {
2552           GdkPixmap *composite;
2553           int row_pixmap_width, row_pixmap_height;
2554           int pixbuf_width, pixbuf_height;
2555           int composite_width, composite_height;
2556           int pixbuf_x, pixbuf_y;
2557           GdkGC *gc, *mask_gc;
2558           GdkColor color;
2559           GdkBitmap *pixbuf_mask;
2560
2561           /* Create pixmap and mask for composite image */
2562
2563           gdk_drawable_get_size (row_pixmap, &row_pixmap_width, &row_pixmap_height);
2564           pixbuf_width = gdk_pixbuf_get_width (pixbuf);
2565           pixbuf_height = gdk_pixbuf_get_height (pixbuf);
2566
2567           composite_width = MAX (row_pixmap_width, pixbuf_width);
2568           composite_height = MAX (row_pixmap_height, pixbuf_height);
2569
2570           row_pixmap_y = (composite_height - row_pixmap_height) / 2;
2571
2572           if (gtk_widget_get_direction (impl->browse_shortcuts_tree_view) == GTK_TEXT_DIR_RTL)
2573             pixbuf_x = 0;
2574           else
2575             pixbuf_x = composite_width - pixbuf_width;
2576
2577           pixbuf_y = (composite_height - pixbuf_height) / 2;
2578
2579           composite = gdk_pixmap_new (row_pixmap, composite_width, composite_height, -1);
2580           gc = gdk_gc_new (composite);
2581
2582           mask = gdk_pixmap_new (row_pixmap, composite_width, composite_height, 1);
2583           mask_gc = gdk_gc_new (mask);
2584           color.pixel = 0;
2585           gdk_gc_set_foreground (mask_gc, &color);
2586           gdk_draw_rectangle (mask, mask_gc, TRUE, 0, 0, composite_width, composite_height);
2587
2588           color.red = 0xffff;
2589           color.green = 0xffff;
2590           color.blue = 0xffff;
2591           gdk_gc_set_rgb_fg_color (gc, &color);
2592           gdk_draw_rectangle (composite, gc, TRUE, 0, 0, composite_width, composite_height);
2593
2594           /* Composite the row pixmap and the pixbuf */
2595
2596           gdk_pixbuf_render_pixmap_and_mask_for_colormap
2597             (pixbuf,
2598              gtk_widget_get_colormap (impl->browse_shortcuts_tree_view),
2599              NULL, &pixbuf_mask, 128);
2600           gdk_draw_drawable (mask, mask_gc, pixbuf_mask,
2601                              0, 0,
2602                              pixbuf_x, pixbuf_y,
2603                              pixbuf_width, pixbuf_height);
2604           g_object_unref (pixbuf_mask);
2605
2606           gdk_draw_drawable (composite, gc, row_pixmap,
2607                              0, 0,
2608                              0, row_pixmap_y,
2609                              row_pixmap_width, row_pixmap_height);
2610           color.pixel = 1;
2611           gdk_gc_set_foreground (mask_gc, &color);
2612           gdk_draw_rectangle (mask, mask_gc, TRUE, 0, row_pixmap_y, row_pixmap_width, row_pixmap_height);
2613
2614           gdk_draw_pixbuf (composite, gc, pixbuf,
2615                            0, 0,
2616                            pixbuf_x, pixbuf_y,
2617                            pixbuf_width, pixbuf_height,
2618                            GDK_RGB_DITHER_MAX,
2619                            0, 0);
2620
2621           g_object_unref (pixbuf);
2622           g_object_unref (row_pixmap);
2623
2624           row_pixmap = composite;
2625         }
2626     }
2627
2628   /* The hotspot offsets here are copied from gtk_tree_view_drag_begin(), ugh */
2629
2630   gtk_tree_view_get_path_at_pos (tree_view,
2631                                  tree_view->priv->press_start_x,
2632                                  tree_view->priv->press_start_y,
2633                                  NULL,
2634                                  NULL,
2635                                  NULL,
2636                                  &cell_y);
2637
2638   gtk_drag_set_icon_pixmap (impl->shortcuts_drag_context,
2639                             gdk_drawable_get_colormap (row_pixmap),
2640                             row_pixmap,
2641                             mask,
2642                             tree_view->priv->press_start_x + 1,
2643                             row_pixmap_y + cell_y + 1);
2644
2645   g_object_unref (row_pixmap);
2646   if (mask)
2647     g_object_unref (mask);
2648 }
2649
2650 /* We set the delete cursor and the shortcuts_drag_outside flag in an idle
2651  * handler so that we can tell apart the drag_leave event that comes right
2652  * before a drag_drop, from a normal drag_leave.  We don't want to set the
2653  * cursor nor the flag in the latter case.
2654  */
2655 static gboolean
2656 shortcuts_drag_outside_idle_cb (GtkFileChooserDefault *impl)
2657 {
2658   GDK_THREADS_ENTER ();
2659   
2660   shortcuts_drag_set_delete_cursor (impl, TRUE);
2661   impl->shortcuts_drag_outside = TRUE;
2662
2663   shortcuts_cancel_drag_outside_idle (impl);
2664
2665   GDK_THREADS_LEAVE ();
2666
2667   return FALSE;
2668 }
2669 #endif
2670
2671 /* GtkWidget::drag-leave handler for the shortcuts list.  We unhighlight the
2672  * drop position.
2673  */
2674 static void
2675 shortcuts_drag_leave_cb (GtkWidget             *widget,
2676                          GdkDragContext        *context,
2677                          guint                  time_,
2678                          GtkFileChooserDefault *impl)
2679 {
2680 #if 0
2681   if (gtk_drag_get_source_widget (context) == widget && !impl->shortcuts_drag_outside_idle)
2682     {
2683       impl->shortcuts_drag_outside_idle = g_idle_source_new ();
2684       g_source_set_closure (impl->shortcuts_drag_outside_idle,
2685                             g_cclosure_new_object (G_CALLBACK (shortcuts_drag_outside_idle_cb),
2686                                                    G_OBJECT (impl)));
2687       g_source_attach (impl->shortcuts_drag_outside_idle, NULL);
2688     }
2689 #endif
2690
2691   gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
2692                                    NULL,
2693                                    GTK_TREE_VIEW_DROP_BEFORE);
2694
2695   g_signal_stop_emission_by_name (widget, "drag-leave");
2696 }
2697
2698 /* Computes the appropriate row and position for dropping */
2699 static void
2700 shortcuts_compute_drop_position (GtkFileChooserDefault   *impl,
2701                                  int                      x,
2702                                  int                      y,
2703                                  GtkTreePath            **path,
2704                                  GtkTreeViewDropPosition *pos)
2705 {
2706   GtkTreeView *tree_view;
2707   GtkTreeViewColumn *column;
2708   int cell_y;
2709   GdkRectangle cell;
2710   int row;
2711   int bookmarks_index;
2712
2713   tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
2714
2715   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2716
2717   if (!gtk_tree_view_get_path_at_pos (tree_view,
2718                                       x,
2719                                       y - TREE_VIEW_HEADER_HEIGHT (tree_view),
2720                                       path,
2721                                       &column,
2722                                       NULL,
2723                                       &cell_y))
2724     {
2725       row = bookmarks_index + impl->num_bookmarks - 1;
2726       *path = gtk_tree_path_new_from_indices (row, -1);
2727       *pos = GTK_TREE_VIEW_DROP_AFTER;
2728       return;
2729     }
2730
2731   row = *gtk_tree_path_get_indices (*path);
2732   gtk_tree_view_get_background_area (tree_view, *path, column, &cell);
2733   gtk_tree_path_free (*path);
2734
2735   if (row < bookmarks_index)
2736     {
2737       row = bookmarks_index;
2738       *pos = GTK_TREE_VIEW_DROP_BEFORE;
2739     }
2740   else if (row > bookmarks_index + impl->num_bookmarks - 1)
2741     {
2742       row = bookmarks_index + impl->num_bookmarks - 1;
2743       *pos = GTK_TREE_VIEW_DROP_AFTER;
2744     }
2745   else
2746     {
2747       if (cell_y < cell.height / 2)
2748         *pos = GTK_TREE_VIEW_DROP_BEFORE;
2749       else
2750         *pos = GTK_TREE_VIEW_DROP_AFTER;
2751     }
2752
2753   *path = gtk_tree_path_new_from_indices (row, -1);
2754 }
2755
2756 /* GtkWidget::drag-motion handler for the shortcuts list.  We basically
2757  * implement the destination side of DnD by hand, due to limitations in
2758  * GtkTreeView's DnD API.
2759  */
2760 static gboolean
2761 shortcuts_drag_motion_cb (GtkWidget             *widget,
2762                           GdkDragContext        *context,
2763                           gint                   x,
2764                           gint                   y,
2765                           guint                  time_,
2766                           GtkFileChooserDefault *impl)
2767 {
2768   GtkTreePath *path;
2769   GtkTreeViewDropPosition pos;
2770   GdkDragAction action;
2771
2772 #if 0
2773   if (gtk_drag_get_source_widget (context) == widget)
2774     {
2775       shortcuts_cancel_drag_outside_idle (impl);
2776
2777       if (impl->shortcuts_drag_outside)
2778         {
2779           shortcuts_drag_set_delete_cursor (impl, FALSE);
2780           impl->shortcuts_drag_outside = FALSE;
2781         }
2782     }
2783 #endif
2784
2785   if (context->suggested_action == GDK_ACTION_COPY || (context->actions & GDK_ACTION_COPY) != 0)
2786     action = GDK_ACTION_COPY;
2787   else if (context->suggested_action == GDK_ACTION_MOVE || (context->actions & GDK_ACTION_MOVE) != 0)
2788     action = GDK_ACTION_MOVE;
2789   else
2790     {
2791       action = 0;
2792       goto out;
2793     }
2794
2795   shortcuts_compute_drop_position (impl, x, y, &path, &pos);
2796   gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), path, pos);
2797   gtk_tree_path_free (path);
2798
2799  out:
2800
2801   g_signal_stop_emission_by_name (widget, "drag-motion");
2802
2803   if (action != 0)
2804     {
2805       gdk_drag_status (context, action, time_);
2806       return TRUE;
2807     }
2808   else
2809     return FALSE;
2810 }
2811
2812 /* GtkWidget::drag-drop handler for the shortcuts list. */
2813 static gboolean
2814 shortcuts_drag_drop_cb (GtkWidget             *widget,
2815                         GdkDragContext        *context,
2816                         gint                   x,
2817                         gint                   y,
2818                         guint                  time_,
2819                         GtkFileChooserDefault *impl)
2820 {
2821 #if 0
2822   shortcuts_cancel_drag_outside_idle (impl);
2823 #endif
2824
2825   g_signal_stop_emission_by_name (widget, "drag-drop");
2826   return TRUE;
2827 }
2828
2829 /* Parses a "text/uri-list" string and inserts its URIs as bookmarks */
2830 static void
2831 shortcuts_drop_uris (GtkFileChooserDefault *impl,
2832                      const char            *data,
2833                      int                    position)
2834 {
2835   gchar **uris;
2836   gint i;
2837
2838   uris = g_uri_list_extract_uris (data);
2839
2840   for (i = 0; uris[i]; i++)
2841     {
2842       char *uri;
2843       GtkFilePath *path;
2844
2845       uri = uris[i];
2846       path = gtk_file_system_uri_to_path (impl->file_system, uri);
2847
2848       if (path)
2849         {
2850           if (shortcuts_add_bookmark_from_path (impl, path, position))
2851             position++;
2852
2853           gtk_file_path_free (path);
2854         }
2855       else
2856         {
2857           GError *error;
2858
2859           g_set_error (&error,
2860                        GTK_FILE_CHOOSER_ERROR,
2861                        GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
2862                        _("Could not add a bookmark for '%s' "
2863                          "because it is an invalid path name."),
2864                        uri);
2865           error_adding_bookmark_dialog (impl, path, error);
2866         }
2867     }
2868
2869   g_strfreev (uris);
2870 }
2871
2872 /* Reorders the selected bookmark to the specified position */
2873 static void
2874 shortcuts_reorder (GtkFileChooserDefault *impl,
2875                    int                    new_position)
2876 {
2877   GtkTreeIter iter;
2878   gpointer col_data;
2879   gboolean is_volume;
2880   GtkTreePath *path;
2881   int old_position;
2882   int bookmarks_index;
2883   const GtkFilePath *file_path;
2884   GtkFilePath *file_path_copy;
2885   GError *error;
2886
2887   /* Get the selected path */
2888
2889   if (!shortcuts_get_selected (impl, &iter))
2890     g_assert_not_reached ();
2891
2892   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
2893   old_position = *gtk_tree_path_get_indices (path);
2894   gtk_tree_path_free (path);
2895
2896   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2897   old_position -= bookmarks_index;
2898   g_assert (old_position >= 0 && old_position < impl->num_bookmarks);
2899
2900   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2901                       SHORTCUTS_COL_DATA, &col_data,
2902                       SHORTCUTS_COL_IS_VOLUME, &is_volume,
2903                       -1);
2904   g_assert (col_data != NULL);
2905   g_assert (!is_volume);
2906
2907   file_path = col_data;
2908   file_path_copy = gtk_file_path_copy (file_path); /* removal below will free file_path, so we need a copy */
2909
2910   /* Remove the path from the old position and insert it in the new one */
2911
2912   if (new_position > old_position)
2913     new_position--;
2914
2915   if (old_position == new_position)
2916     goto out;
2917
2918   error = NULL;
2919   if (gtk_file_system_remove_bookmark (impl->file_system, file_path_copy, &error))
2920     shortcuts_add_bookmark_from_path (impl, file_path_copy, new_position);
2921   else
2922     error_adding_bookmark_dialog (impl, file_path_copy, error);
2923
2924  out:
2925
2926   gtk_file_path_free (file_path_copy);
2927 }
2928
2929 /* Callback used when we get the drag data for the bookmarks list.  We add the
2930  * received URIs as bookmarks if they are folders.
2931  */
2932 static void
2933 shortcuts_drag_data_received_cb (GtkWidget          *widget,
2934                                  GdkDragContext     *context,
2935                                  gint                x,
2936                                  gint                y,
2937                                  GtkSelectionData   *selection_data,
2938                                  guint               info,
2939                                  guint               time_,
2940                                  gpointer            data)
2941 {
2942   GtkFileChooserDefault *impl;
2943   GtkTreePath *tree_path;
2944   GtkTreeViewDropPosition tree_pos;
2945   int position;
2946   int bookmarks_index;
2947
2948   impl = GTK_FILE_CHOOSER_DEFAULT (data);
2949
2950   /* Compute position */
2951
2952   bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2953
2954   shortcuts_compute_drop_position (impl, x, y, &tree_path, &tree_pos);
2955   position = *gtk_tree_path_get_indices (tree_path);
2956   gtk_tree_path_free (tree_path);
2957
2958   if (tree_pos == GTK_TREE_VIEW_DROP_AFTER)
2959     position++;
2960
2961   g_assert (position >= bookmarks_index);
2962   position -= bookmarks_index;
2963
2964   if (selection_data->target == gdk_atom_intern ("text/uri-list", FALSE))
2965     shortcuts_drop_uris (impl, selection_data->data, position);
2966   else if (selection_data->target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE))
2967     shortcuts_reorder (impl, position);
2968
2969   g_signal_stop_emission_by_name (widget, "drag-data-received");
2970 }
2971
2972 /* Callback used when the selection in the shortcuts tree changes */
2973 static void
2974 shortcuts_selection_changed_cb (GtkTreeSelection      *selection,
2975                                 GtkFileChooserDefault *impl)
2976 {
2977   bookmarks_check_remove_sensitivity (impl);
2978   shortcuts_check_popup_sensitivity (impl);
2979 }
2980
2981 static gboolean
2982 shortcuts_row_separator_func (GtkTreeModel *model,
2983                               GtkTreeIter  *iter,
2984                               gpointer      data)
2985 {
2986   gint column = GPOINTER_TO_INT (data);
2987   gchar *text;
2988
2989   gtk_tree_model_get (model, iter, column, &text, -1);
2990   
2991   if (!text)
2992     return TRUE;
2993
2994   g_free (text);
2995
2996   return FALSE;
2997 }
2998
2999 /* Since GtkTreeView has a keybinding attached to '/', we need to catch
3000  * keypresses before the TreeView gets them.
3001  */
3002 static gboolean
3003 tree_view_keybinding_cb (GtkWidget             *tree_view,
3004                          GdkEventKey           *event,
3005                          GtkFileChooserDefault *impl)
3006 {
3007   if (event->keyval == GDK_slash &&
3008       ! (event->state & (~GDK_SHIFT_MASK & gtk_accelerator_get_default_mod_mask ())))
3009     {
3010       location_popup_handler (impl, "/");
3011       return TRUE;
3012     }
3013   
3014   return FALSE;
3015 }
3016
3017 /* Callback used when the file list's popup menu is detached */
3018 static void
3019 shortcuts_popup_menu_detach_cb (GtkWidget *attach_widget,
3020                                 GtkMenu   *menu)
3021 {
3022   GtkFileChooserDefault *impl;
3023   
3024   impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
3025   g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
3026
3027   impl->browse_shortcuts_popup_menu = NULL;
3028   impl->browse_shortcuts_popup_menu_remove_item = NULL;
3029   impl->browse_shortcuts_popup_menu_rename_item = NULL;
3030 }
3031
3032 static void
3033 remove_shortcut_cb (GtkMenuItem           *item,
3034                     GtkFileChooserDefault *impl)
3035 {
3036   remove_selected_bookmarks (impl);
3037 }
3038
3039 static void
3040 rename_shortcut_cb (GtkMenuItem           *item,
3041                     GtkFileChooserDefault *impl)
3042 {
3043   GtkTreeIter iter;
3044   GtkTreePath *path;
3045   GtkTreeViewColumn *column;
3046   GtkCellRenderer *cell;
3047   GList *renderers;
3048
3049   if (shortcuts_get_selected (impl, &iter))
3050     {
3051       path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
3052       column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), 0);
3053       renderers = gtk_tree_view_column_get_cell_renderers (column);
3054       cell = g_list_nth_data (renderers, 1);
3055       g_list_free (renderers);
3056       g_object_set (cell, "editable", TRUE, NULL);
3057       gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3058                                         path, column, cell, TRUE);
3059       gtk_tree_path_free (path);
3060     }
3061 }
3062
3063 /* Constructs the popup menu for the file list if needed */
3064 static void
3065 shortcuts_build_popup_menu (GtkFileChooserDefault *impl)
3066 {
3067   GtkWidget *item;
3068
3069   if (impl->browse_shortcuts_popup_menu)
3070     return;
3071
3072   impl->browse_shortcuts_popup_menu = gtk_menu_new ();
3073   gtk_menu_attach_to_widget (GTK_MENU (impl->browse_shortcuts_popup_menu),
3074                              impl->browse_shortcuts_tree_view,
3075                              shortcuts_popup_menu_detach_cb);
3076
3077   item = gtk_image_menu_item_new_with_label (_("Remove"));
3078   impl->browse_shortcuts_popup_menu_remove_item = item;
3079   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
3080                                  gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU));
3081   g_signal_connect (item, "activate",
3082                     G_CALLBACK (remove_shortcut_cb), impl);
3083   gtk_widget_show (item);
3084   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu), item);
3085
3086   item = gtk_menu_item_new_with_label (_("Rename..."));
3087   impl->browse_shortcuts_popup_menu_rename_item = item;
3088   g_signal_connect (item, "activate",
3089                     G_CALLBACK (rename_shortcut_cb), impl);
3090   gtk_widget_show (item);
3091   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu), item);
3092
3093   shortcuts_check_popup_sensitivity (impl);
3094 }
3095
3096 static void
3097 shortcuts_update_popup_menu (GtkFileChooserDefault *impl)
3098 {
3099   shortcuts_build_popup_menu (impl);  
3100 }
3101
3102 static void
3103 popup_position_func (GtkMenu   *menu,
3104                      gint      *x,
3105                      gint      *y,
3106                      gboolean  *push_in,
3107                      gpointer   user_data);
3108
3109 static void
3110 shortcuts_popup_menu (GtkFileChooserDefault *impl,
3111                       GdkEventButton        *event)
3112 {
3113   shortcuts_update_popup_menu (impl);
3114   if (event)
3115     gtk_menu_popup (GTK_MENU (impl->browse_shortcuts_popup_menu),
3116                     NULL, NULL, NULL, NULL,
3117                     event->button, event->time);
3118   else
3119     {
3120       gtk_menu_popup (GTK_MENU (impl->browse_shortcuts_popup_menu),
3121                       NULL, NULL,
3122                       popup_position_func, impl->browse_shortcuts_tree_view,
3123                       0, GDK_CURRENT_TIME);
3124       gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu),
3125                                    FALSE);
3126     }
3127 }
3128
3129 /* Callback used for the GtkWidget::popup-menu signal of the shortcuts list */
3130 static gboolean
3131 shortcuts_popup_menu_cb (GtkWidget *widget,
3132                          GtkFileChooserDefault *impl)
3133 {
3134   shortcuts_popup_menu (impl, NULL);
3135   return TRUE;
3136 }
3137
3138 /* Callback used when a button is pressed on the shortcuts list.  
3139  * We trap button 3 to bring up a popup menu.
3140  */
3141 static gboolean
3142 shortcuts_button_press_event_cb (GtkWidget             *widget,
3143                                  GdkEventButton        *event,
3144                                  GtkFileChooserDefault *impl)
3145 {
3146   if (event->button != 3)
3147     return FALSE;
3148
3149   shortcuts_popup_menu (impl, event);
3150   return TRUE;
3151 }
3152
3153 static void
3154 shortcuts_edited (GtkCellRenderer       *cell,
3155                   gchar                 *path_string,
3156                   gchar                 *new_text,
3157                   GtkFileChooserDefault *impl)
3158 {
3159   GtkTreePath *path;
3160   GtkTreeIter iter;
3161   GtkFilePath *shortcut;
3162
3163   g_object_set (cell, "editable", FALSE, NULL);
3164
3165   path = gtk_tree_path_new_from_string (path_string);
3166   gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path);
3167   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
3168                       SHORTCUTS_COL_DATA, &shortcut,
3169                       -1);
3170   gtk_tree_path_free (path);
3171   
3172   gtk_file_system_set_bookmark_label (impl->file_system, shortcut, new_text);
3173 }
3174
3175 static void
3176 shortcuts_editing_canceled (GtkCellRenderer       *cell,
3177                             GtkFileChooserDefault *impl)
3178 {
3179   g_object_set (cell, "editable", FALSE, NULL);
3180 }
3181
3182 /* Creates the widgets for the shortcuts and bookmarks tree */
3183 static GtkWidget *
3184 shortcuts_list_create (GtkFileChooserDefault *impl)
3185 {
3186   GtkWidget *swin;
3187   GtkTreeSelection *selection;
3188   GtkTreeViewColumn *column;
3189   GtkCellRenderer *renderer;
3190
3191   /* Scrolled window */
3192
3193   swin = gtk_scrolled_window_new (NULL, NULL);
3194   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
3195                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3196   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
3197                                        GTK_SHADOW_IN);
3198   gtk_widget_show (swin);
3199
3200   /* Tree */
3201
3202   impl->browse_shortcuts_tree_view = gtk_tree_view_new ();
3203   g_signal_connect (impl->browse_shortcuts_tree_view, "key-press-event",
3204                     G_CALLBACK (tree_view_keybinding_cb), impl);
3205   g_signal_connect (impl->browse_shortcuts_tree_view, "popup-menu",
3206                     G_CALLBACK (shortcuts_popup_menu_cb), impl);
3207   g_signal_connect (impl->browse_shortcuts_tree_view, "button-press-event",
3208                     G_CALLBACK (shortcuts_button_press_event_cb), impl);
3209   atk_object_set_name (gtk_widget_get_accessible (impl->browse_shortcuts_tree_view), _("Shortcuts"));
3210   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), FALSE);
3211
3212   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), impl->shortcuts_filter_model);
3213
3214   gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3215                                           GDK_BUTTON1_MASK,
3216                                           shortcuts_source_targets,
3217                                           num_shortcuts_source_targets,
3218                                           GDK_ACTION_MOVE);
3219
3220   gtk_drag_dest_set (impl->browse_shortcuts_tree_view,
3221                      GTK_DEST_DEFAULT_ALL,
3222                      shortcuts_dest_targets,
3223                      num_shortcuts_dest_targets,
3224                      GDK_ACTION_COPY | GDK_ACTION_MOVE);
3225
3226   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
3227   gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
3228   gtk_tree_selection_set_select_function (selection,
3229                                           shortcuts_select_func,
3230                                           impl, NULL);
3231
3232   g_signal_connect (selection, "changed",
3233                     G_CALLBACK (shortcuts_selection_changed_cb), impl);
3234
3235   g_signal_connect (impl->browse_shortcuts_tree_view, "row-activated",
3236                     G_CALLBACK (shortcuts_row_activated_cb), impl);
3237
3238   g_signal_connect (impl->browse_shortcuts_tree_view, "key-press-event",
3239                     G_CALLBACK (shortcuts_key_press_event_cb), impl);
3240
3241   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-begin",
3242                     G_CALLBACK (shortcuts_drag_begin_cb), impl);
3243   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-end",
3244                     G_CALLBACK (shortcuts_drag_end_cb), impl);
3245   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-delete",
3246                     G_CALLBACK (shortcuts_drag_data_delete_cb), impl);
3247
3248   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-leave",
3249                     G_CALLBACK (shortcuts_drag_leave_cb), impl);
3250   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-motion",
3251                     G_CALLBACK (shortcuts_drag_motion_cb), impl);
3252   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-drop",
3253                     G_CALLBACK (shortcuts_drag_drop_cb), impl);
3254   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-received",
3255                     G_CALLBACK (shortcuts_drag_data_received_cb), impl);
3256
3257   gtk_container_add (GTK_CONTAINER (swin), impl->browse_shortcuts_tree_view);
3258   gtk_widget_show (impl->browse_shortcuts_tree_view);
3259
3260   /* Column */
3261
3262   column = gtk_tree_view_column_new ();
3263   gtk_tree_view_column_set_title (column, _("Folder"));
3264
3265   renderer = gtk_cell_renderer_pixbuf_new ();
3266   gtk_tree_view_column_pack_start (column, renderer, FALSE);
3267   gtk_tree_view_column_set_attributes (column, renderer,
3268                                        "pixbuf", SHORTCUTS_COL_PIXBUF,
3269                                        "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
3270                                        NULL);
3271
3272   renderer = gtk_cell_renderer_text_new ();
3273   g_signal_connect (renderer, "edited", 
3274                     G_CALLBACK (shortcuts_edited), impl);
3275   g_signal_connect (renderer, "editing-canceled", 
3276                     G_CALLBACK (shortcuts_editing_canceled), impl);
3277   gtk_tree_view_column_pack_start (column, renderer, TRUE);
3278   gtk_tree_view_column_set_attributes (column, renderer,
3279                                        "text", SHORTCUTS_COL_NAME,
3280                                        NULL);
3281
3282   gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3283                                         shortcuts_row_separator_func,
3284                                         GINT_TO_POINTER (SHORTCUTS_COL_NAME),
3285                                         NULL);
3286
3287   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), column);
3288
3289   return swin;
3290 }
3291
3292 /* Creates the widgets for the shortcuts/bookmarks pane */
3293 static GtkWidget *
3294 shortcuts_pane_create (GtkFileChooserDefault *impl,
3295                        GtkSizeGroup          *size_group)
3296 {
3297   GtkWidget *vbox;
3298   GtkWidget *hbox;
3299   GtkWidget *widget;
3300
3301   vbox = gtk_vbox_new (FALSE, 6);
3302   gtk_widget_show (vbox);
3303
3304   /* Shortcuts tree */
3305
3306   widget = shortcuts_list_create (impl);
3307   gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
3308
3309   /* Box for buttons */
3310
3311   hbox = gtk_hbox_new (TRUE, 6);
3312   gtk_size_group_add_widget (size_group, hbox);
3313   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
3314   gtk_widget_show (hbox);
3315
3316   /* Add bookmark button */
3317
3318   impl->browse_shortcuts_add_button = button_new (impl,
3319                                                   _("_Add"),
3320                                                   GTK_STOCK_ADD,
3321                                                   FALSE,
3322                                                   TRUE,
3323                                                   G_CALLBACK (add_bookmark_button_clicked_cb));
3324   gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_add_button, TRUE, TRUE, 0);
3325   gtk_tooltips_set_tip (impl->tooltips, impl->browse_shortcuts_add_button,
3326                         _("Add the selected folder to the Bookmarks"), NULL);
3327
3328   /* Remove bookmark button */
3329
3330   impl->browse_shortcuts_remove_button = button_new (impl,
3331                                                      _("_Remove"),
3332                                                      GTK_STOCK_REMOVE,
3333                                                      FALSE,
3334                                                      TRUE,
3335                                                      G_CALLBACK (remove_bookmark_button_clicked_cb));
3336   gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_remove_button, TRUE, TRUE, 0);
3337   gtk_tooltips_set_tip (impl->tooltips, impl->browse_shortcuts_remove_button,
3338                         _("Remove the selected bookmark"), NULL);
3339
3340   return vbox;
3341 }
3342
3343 /* Handles key press events on the file list, so that we can trap Enter to
3344  * activate the default button on our own.  Also, checks to see if '/' has been
3345  * pressed.  See comment by tree_view_keybinding_cb() for more details.
3346  */
3347 static gboolean
3348 trap_activate_cb (GtkWidget   *widget,
3349                   GdkEventKey *event,
3350                   gpointer     data)
3351 {
3352   GtkFileChooserDefault *impl;
3353   int modifiers;
3354
3355   impl = (GtkFileChooserDefault *) data;
3356
3357   modifiers = gtk_accelerator_get_default_mod_mask ();
3358   
3359   if (event->keyval == GDK_slash &&
3360       ! (event->state & (~GDK_SHIFT_MASK & modifiers)))
3361     {
3362       location_popup_handler (impl, "/");
3363       return TRUE;
3364     }
3365
3366   if ((event->keyval == GDK_Return
3367        || event->keyval == GDK_ISO_Enter
3368        || event->keyval == GDK_KP_Enter
3369        || event->keyval == GDK_space)
3370       && ((event->state && modifiers) == 0)
3371       && !(impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
3372            impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
3373     {
3374       GtkWindow *window;
3375
3376       window = get_toplevel (widget);
3377       if (window
3378           && widget != window->default_widget
3379           && !(widget == window->focus_widget &&
3380                (!window->default_widget || !GTK_WIDGET_SENSITIVE (window->default_widget))))
3381         {
3382           gtk_window_activate_default (window);
3383           return TRUE;
3384         }
3385     }
3386
3387   return FALSE;
3388 }
3389
3390 /* Callback used when the file list's popup menu is detached */
3391 static void
3392 popup_menu_detach_cb (GtkWidget *attach_widget,
3393                       GtkMenu   *menu)
3394 {
3395   GtkFileChooserDefault *impl;
3396
3397   impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
3398   g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
3399
3400   impl->browse_files_popup_menu = NULL;
3401   impl->browse_files_popup_menu_add_shortcut_item = NULL;
3402   impl->browse_files_popup_menu_hidden_files_item = NULL;
3403 }
3404
3405 /* Callback used when the "Add to Bookmarks" menu item is activated */
3406 static void
3407 add_to_shortcuts_cb (GtkMenuItem           *item,
3408                      GtkFileChooserDefault *impl)
3409 {
3410   bookmarks_add_selected_folder (impl);
3411 }
3412
3413 /* Callback used when the "Open Location" menu item is activated */
3414 static void
3415 open_location_cb (GtkMenuItem           *item,
3416                   GtkFileChooserDefault *impl)
3417 {
3418   location_popup_handler (impl, "");
3419 }
3420
3421 /* Callback used when the "Show Hidden Files" menu item is toggled */
3422 static void
3423 show_hidden_toggled_cb (GtkCheckMenuItem      *item,
3424                         GtkFileChooserDefault *impl)
3425 {
3426   g_object_set (impl,
3427                 "show-hidden", gtk_check_menu_item_get_active (item),
3428                 NULL);
3429 }
3430
3431 /* Shows an error dialog about not being able to select a dragged file */
3432 static void
3433 error_selecting_dragged_file_dialog (GtkFileChooserDefault *impl,
3434                                      const GtkFilePath     *path,
3435                                      GError                *error)
3436 {
3437   error_dialog (impl,
3438                 _("Could not select file"),
3439                 path, error);
3440 }
3441
3442 static void
3443 file_list_drag_data_received_cb (GtkWidget          *widget,
3444                                  GdkDragContext     *context,
3445                                  gint                x,
3446                                  gint                y,
3447                                  GtkSelectionData   *selection_data,
3448                                  guint               info,
3449                                  guint               time_,
3450                                  gpointer            data)
3451 {
3452   GtkFileChooserDefault *impl;
3453   GtkFileChooser *chooser;
3454   gchar **uris;
3455   char *uri;
3456   GtkFilePath *path;
3457   GError *error = NULL;
3458   gint i;
3459  
3460   impl = GTK_FILE_CHOOSER_DEFAULT (data);
3461   chooser = GTK_FILE_CHOOSER (data);
3462   
3463   /* Parse the text/uri-list string, navigate to the first one */
3464   uris = g_uri_list_extract_uris (selection_data->data);
3465   if (uris[0]) 
3466     {
3467       uri = uris[0];
3468       path = gtk_file_system_uri_to_path (impl->file_system, uri);
3469       
3470       if (path)
3471         {
3472           if ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
3473                impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) &&
3474               uris[1] == 0 &&
3475               check_is_folder (impl->file_system, path, NULL))
3476             change_folder_and_display_error (impl, path);
3477           else
3478             {
3479               gtk_file_chooser_default_unselect_all (chooser);
3480               gtk_file_chooser_default_select_path (chooser, path, &error);
3481               if (error)
3482                 error_selecting_dragged_file_dialog (impl, path, error);
3483               else
3484                 browse_files_center_selected_row (impl);
3485             }
3486
3487           gtk_file_path_free (path);
3488         }
3489       else
3490         {
3491           g_set_error (&error,
3492                        GTK_FILE_CHOOSER_ERROR,
3493                        GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
3494                        _("Could not select file '%s' "
3495                          "because it is an invalid path name."),
3496                        uri);
3497           error_selecting_dragged_file_dialog (impl, NULL, error);
3498         }
3499
3500       
3501       if (impl->select_multiple)
3502         {
3503           for (i = 1; uris[i]; i++)
3504             {
3505               uri = uris[i];
3506               path = gtk_file_system_uri_to_path (impl->file_system, uri);
3507               
3508               if (path)
3509                 {
3510                   gtk_file_chooser_default_select_path (chooser, path, &error);
3511                   if (error)
3512                     error_selecting_dragged_file_dialog (impl, path, error);
3513
3514                   gtk_file_path_free (path);
3515                 }
3516             }
3517         }
3518     }
3519
3520   g_strfreev (uris);
3521
3522   g_signal_stop_emission_by_name (widget, "drag-data-received");
3523 }
3524
3525 /* Don't do anything with the drag_drop signal */
3526 static gboolean
3527 file_list_drag_drop_cb (GtkWidget             *widget,
3528                         GdkDragContext        *context,
3529                         gint                   x,
3530                         gint                   y,
3531                         guint                  time_,
3532                         GtkFileChooserDefault *impl)
3533 {
3534   g_signal_stop_emission_by_name (widget, "drag-drop");
3535   return TRUE;
3536 }
3537
3538 /* Disable the normal tree drag motion handler, it makes it look like you're
3539    dropping the dragged item onto a tree item */
3540 static gboolean
3541 file_list_drag_motion_cb (GtkWidget             *widget,
3542                           GdkDragContext        *context,
3543                           gint                   x,
3544                           gint                   y,
3545                           guint                  time_,
3546                           GtkFileChooserDefault *impl)
3547 {
3548   g_signal_stop_emission_by_name (widget, "drag-motion");
3549   return TRUE;
3550 }
3551
3552 /* Constructs the popup menu for the file list if needed */
3553 static void
3554 file_list_build_popup_menu (GtkFileChooserDefault *impl)
3555 {
3556   GtkWidget *item;
3557
3558   if (impl->browse_files_popup_menu)
3559     return;
3560
3561   impl->browse_files_popup_menu = gtk_menu_new ();
3562   gtk_menu_attach_to_widget (GTK_MENU (impl->browse_files_popup_menu),
3563                              impl->browse_files_tree_view,
3564                              popup_menu_detach_cb);
3565
3566   item = gtk_image_menu_item_new_with_mnemonic (_("_Add to Bookmarks"));
3567   impl->browse_files_popup_menu_add_shortcut_item = item;
3568   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
3569                                  gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU));
3570   gtk_widget_set_sensitive (item, FALSE);
3571   g_signal_connect (item, "activate",
3572                     G_CALLBACK (add_to_shortcuts_cb), impl);
3573   gtk_widget_show (item);
3574   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
3575
3576   item = gtk_image_menu_item_new_with_mnemonic (_("Open _Location"));
3577   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
3578                                  gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU));
3579   g_signal_connect (item, "activate",
3580                     G_CALLBACK (open_location_cb), impl);
3581   gtk_widget_show (item);
3582   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
3583
3584   item = gtk_separator_menu_item_new ();
3585   gtk_widget_show (item);
3586   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
3587
3588   item = gtk_check_menu_item_new_with_mnemonic (_("Show _Hidden Files"));
3589   impl->browse_files_popup_menu_hidden_files_item = item;
3590   g_signal_connect (item, "toggled",
3591                     G_CALLBACK (show_hidden_toggled_cb), impl);
3592   gtk_widget_show (item);
3593   gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
3594 }
3595
3596 /* Updates the popup menu for the file list, creating it if necessary */
3597 static void
3598 file_list_update_popup_menu (GtkFileChooserDefault *impl)
3599 {
3600   file_list_build_popup_menu (impl);
3601
3602   /* The sensitivity of the Add to Bookmarks item is set in
3603    * bookmarks_check_add_sensitivity()
3604    */
3605
3606   g_signal_handlers_block_by_func (impl->browse_files_popup_menu_hidden_files_item,
3607                                    G_CALLBACK (show_hidden_toggled_cb), impl);
3608   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (impl->browse_files_popup_menu_hidden_files_item),
3609                                   impl->show_hidden);
3610   g_signal_handlers_unblock_by_func (impl->browse_files_popup_menu_hidden_files_item,
3611                                      G_CALLBACK (show_hidden_toggled_cb), impl);
3612 }
3613
3614 static void
3615 popup_position_func (GtkMenu   *menu,
3616                      gint      *x,
3617                      gint      *y,
3618                      gboolean  *push_in,
3619                      gpointer   user_data)
3620 {
3621   GtkWidget *widget = GTK_WIDGET (user_data);
3622   GdkScreen *screen = gtk_widget_get_screen (widget);
3623   GtkRequisition req;
3624   gint monitor_num;
3625   GdkRectangle monitor;
3626
3627   g_return_if_fail (GTK_WIDGET_REALIZED (widget));
3628
3629   gdk_window_get_origin (widget->window, x, y);
3630
3631   gtk_widget_size_request (GTK_WIDGET (menu), &req);
3632
3633   *x += (widget->allocation.width - req.width) / 2;
3634   *y += (widget->allocation.height - req.height) / 2;
3635
3636   monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
3637   gtk_menu_set_monitor (menu, monitor_num);
3638   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
3639
3640   *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
3641   *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
3642
3643   *push_in = FALSE;
3644 }
3645
3646 static void
3647 file_list_popup_menu (GtkFileChooserDefault *impl,
3648                       GdkEventButton        *event)
3649 {
3650   file_list_update_popup_menu (impl);
3651   if (event)
3652     gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
3653                     NULL, NULL, NULL, NULL,
3654                     event->button, event->time);
3655   else
3656     {
3657       gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
3658                       NULL, NULL,
3659                       popup_position_func, impl->browse_files_tree_view,
3660                       0, GDK_CURRENT_TIME);
3661       gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->browse_files_popup_menu),
3662                                    FALSE);
3663     }
3664
3665 }
3666
3667 /* Callback used for the GtkWidget::popup-menu signal of the file list */
3668 static gboolean
3669 list_popup_menu_cb (GtkWidget *widget,
3670                     GtkFileChooserDefault *impl)
3671 {
3672   file_list_popup_menu (impl, NULL);
3673   return TRUE;
3674 }
3675
3676 /* Callback used when a button is pressed on the file list.  We trap button 3 to
3677  * bring up a popup menu.
3678  */
3679 static gboolean
3680 list_button_press_event_cb (GtkWidget             *widget,
3681                             GdkEventButton        *event,
3682                             GtkFileChooserDefault *impl)
3683 {
3684   if (event->button != 3)
3685     return FALSE;
3686
3687   file_list_popup_menu (impl, event);
3688   return TRUE;
3689 }
3690
3691 /* Creates the widgets for the file list */
3692 static GtkWidget *
3693 create_file_list (GtkFileChooserDefault *impl)
3694 {
3695   GtkWidget *swin;
3696   GtkTreeSelection *selection;
3697   GtkTreeViewColumn *column;
3698   GtkCellRenderer *renderer;
3699
3700   /* Scrolled window */
3701
3702   swin = gtk_scrolled_window_new (NULL, NULL);
3703   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
3704                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3705   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
3706                                        GTK_SHADOW_IN);
3707
3708   /* Tree/list view */
3709
3710   impl->browse_files_tree_view = gtk_tree_view_new ();
3711   g_object_set_data (G_OBJECT (impl->browse_files_tree_view), "GtkFileChooserDefault", impl);
3712   atk_object_set_name (gtk_widget_get_accessible (impl->browse_files_tree_view), _("Files"));
3713
3714   gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->browse_files_tree_view), TRUE);
3715   gtk_container_add (GTK_CONTAINER (swin), impl->browse_files_tree_view);
3716
3717   gtk_drag_dest_set (impl->browse_files_tree_view,
3718                      GTK_DEST_DEFAULT_ALL,
3719                      file_list_dest_targets,
3720                      num_file_list_dest_targets,
3721                      GDK_ACTION_COPY | GDK_ACTION_MOVE);
3722   
3723   g_signal_connect (impl->browse_files_tree_view, "row-activated",
3724                     G_CALLBACK (list_row_activated), impl);
3725   g_signal_connect (impl->browse_files_tree_view, "key-press-event",
3726                     G_CALLBACK (trap_activate_cb), impl);
3727   g_signal_connect (impl->browse_files_tree_view, "popup-menu",
3728                     G_CALLBACK (list_popup_menu_cb), impl);
3729   g_signal_connect (impl->browse_files_tree_view, "button-press-event",
3730                     G_CALLBACK (list_button_press_event_cb), impl);
3731
3732   g_signal_connect (impl->browse_files_tree_view, "drag-data-received",
3733                     G_CALLBACK (file_list_drag_data_received_cb), impl);
3734   g_signal_connect (impl->browse_files_tree_view, "drag-drop",
3735                     G_CALLBACK (file_list_drag_drop_cb), impl);
3736   g_signal_connect (impl->browse_files_tree_view, "drag-motion",
3737                     G_CALLBACK (file_list_drag_motion_cb), impl);
3738
3739   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3740   gtk_tree_selection_set_select_function (selection,
3741                                           list_select_func,
3742                                           impl, NULL);
3743   gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_files_tree_view),
3744                                           GDK_BUTTON1_MASK,
3745                                           file_list_source_targets,
3746                                           num_file_list_source_targets,
3747                                           GDK_ACTION_COPY);
3748
3749   g_signal_connect (selection, "changed",
3750                     G_CALLBACK (list_selection_changed), impl);
3751
3752   /* Filename column */
3753
3754   impl->list_name_column = gtk_tree_view_column_new ();
3755   gtk_tree_view_column_set_expand (impl->list_name_column, TRUE);
3756   gtk_tree_view_column_set_resizable (impl->list_name_column, TRUE);
3757   gtk_tree_view_column_set_title (impl->list_name_column, _("Name"));
3758   gtk_tree_view_column_set_sort_column_id (impl->list_name_column, FILE_LIST_COL_NAME);
3759
3760   renderer = gtk_cell_renderer_pixbuf_new ();
3761   gtk_tree_view_column_pack_start (impl->list_name_column, renderer, FALSE);
3762   gtk_tree_view_column_set_cell_data_func (impl->list_name_column, renderer,
3763                                            list_icon_data_func, impl, NULL);
3764
3765   impl->list_name_renderer = gtk_cell_renderer_text_new ();
3766   g_object_set (impl->list_name_renderer,
3767                 "ellipsize", PANGO_ELLIPSIZE_END,
3768                 NULL);
3769   g_signal_connect (impl->list_name_renderer, "edited",
3770                     G_CALLBACK (renderer_edited_cb), impl);
3771   g_signal_connect (impl->list_name_renderer, "editing-canceled",
3772                     G_CALLBACK (renderer_editing_canceled_cb), impl);
3773   gtk_tree_view_column_pack_start (impl->list_name_column, impl->list_name_renderer, TRUE);
3774   gtk_tree_view_column_set_cell_data_func (impl->list_name_column, impl->list_name_renderer,
3775                                            list_name_data_func, impl, NULL);
3776
3777   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), impl->list_name_column);
3778 #if 0
3779   /* Size column */
3780
3781   column = gtk_tree_view_column_new ();
3782   gtk_tree_view_column_set_title (column, _("Size"));
3783
3784   renderer = gtk_cell_renderer_text_new ();
3785   gtk_tree_view_column_pack_start (column, renderer, TRUE);
3786   gtk_tree_view_column_set_cell_data_func (column, renderer,
3787                                            list_size_data_func, impl, NULL);
3788   gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_SIZE);
3789   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
3790 #endif
3791   /* Modification time column */
3792
3793   column = gtk_tree_view_column_new ();
3794   gtk_tree_view_column_set_resizable (column, TRUE);
3795   gtk_tree_view_column_set_title (column, _("Modified"));
3796
3797   renderer = gtk_cell_renderer_text_new ();
3798   gtk_tree_view_column_pack_start (column, renderer, TRUE);
3799   gtk_tree_view_column_set_cell_data_func (column, renderer,
3800                                            list_mtime_data_func, impl, NULL);
3801   gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_MTIME);
3802   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
3803   gtk_widget_show_all (swin);
3804
3805   return swin;
3806 }
3807
3808 static GtkWidget *
3809 create_path_bar (GtkFileChooserDefault *impl)
3810 {
3811   GtkWidget *path_bar;
3812
3813   path_bar = g_object_new (GTK_TYPE_PATH_BAR, NULL);
3814   _gtk_path_bar_set_file_system (GTK_PATH_BAR (path_bar), impl->file_system);
3815
3816   return path_bar;
3817 }
3818
3819 static void
3820 set_filter_tooltip (GtkWidget *widget, 
3821                     gpointer   data)
3822 {
3823   GtkTooltips *tooltips = (GtkTooltips *)data;
3824
3825   if (GTK_IS_BUTTON (widget))
3826     gtk_tooltips_set_tip (tooltips, widget,
3827                           _("Select which types of files are shown"), 
3828                           NULL);
3829 }
3830
3831 static void
3832 realize_filter_combo (GtkWidget *combo,
3833                       gpointer   data)
3834 {
3835   GtkFileChooserDefault *impl = (GtkFileChooserDefault *)data;
3836
3837   gtk_container_forall (GTK_CONTAINER (combo),
3838                         set_filter_tooltip,
3839                         impl->tooltips);
3840 }
3841
3842 /* Creates the widgets for the files/folders pane */
3843 static GtkWidget *
3844 file_pane_create (GtkFileChooserDefault *impl,
3845                   GtkSizeGroup          *size_group)
3846 {
3847   GtkWidget *vbox;
3848   GtkWidget *hbox;
3849   GtkWidget *widget;
3850
3851   vbox = gtk_vbox_new (FALSE, 6);
3852   gtk_widget_show (vbox);
3853
3854   /* The path bar and 'Create Folder' button */
3855   hbox = gtk_hbox_new (FALSE, 12);
3856   gtk_widget_show (hbox);
3857   impl->browse_path_bar = create_path_bar (impl);
3858   g_signal_connect (impl->browse_path_bar, "path-clicked", G_CALLBACK (path_bar_clicked), impl);
3859   gtk_widget_show_all (impl->browse_path_bar);
3860   gtk_box_pack_start (GTK_BOX (hbox), impl->browse_path_bar, TRUE, TRUE, 0);
3861
3862   /* Create Folder */
3863   impl->browse_new_folder_button = gtk_button_new_with_mnemonic (_("Create Fo_lder"));
3864   g_signal_connect (impl->browse_new_folder_button, "clicked",
3865                     G_CALLBACK (new_folder_button_clicked), impl);
3866   gtk_box_pack_end (GTK_BOX (hbox), impl->browse_new_folder_button, FALSE, FALSE, 0);
3867   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
3868
3869   /* Box for lists and preview */
3870
3871   hbox = gtk_hbox_new (FALSE, PREVIEW_HBOX_SPACING);
3872   gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
3873   gtk_widget_show (hbox);
3874
3875   /* File list */
3876
3877   widget = create_file_list (impl);
3878   gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
3879
3880   /* Preview */
3881
3882   impl->preview_box = gtk_vbox_new (FALSE, 12);
3883   gtk_box_pack_start (GTK_BOX (hbox), impl->preview_box, FALSE, FALSE, 0);
3884   /* Don't show preview box initially */
3885
3886   /* Filter combo */
3887
3888   impl->filter_combo_hbox = gtk_hbox_new (FALSE, 12);
3889
3890   widget = filter_create (impl);
3891
3892   g_signal_connect (widget, "realize",
3893                     G_CALLBACK (realize_filter_combo), impl);
3894
3895   gtk_widget_show (widget);
3896   gtk_box_pack_end (GTK_BOX (impl->filter_combo_hbox), widget, FALSE, FALSE, 0);
3897
3898   gtk_size_group_add_widget (size_group, impl->filter_combo_hbox);
3899   gtk_box_pack_end (GTK_BOX (vbox), impl->filter_combo_hbox, FALSE, FALSE, 0);
3900
3901   return vbox;
3902 }
3903 /* Callback used when the "Browse for more folders" expander is toggled */
3904 static void
3905 expander_changed_cb (GtkExpander           *expander,
3906                      GParamSpec            *pspec,
3907                      GtkFileChooserDefault *impl)
3908 {
3909   update_appearance (impl);
3910 }
3911
3912 /* Callback used when the selection changes in the save folder combo box */
3913 static void
3914 save_folder_combo_changed_cb (GtkComboBox           *combo,
3915                               GtkFileChooserDefault *impl)
3916 {
3917   GtkTreeIter iter;
3918
3919   if (impl->changing_folder)
3920     return;
3921
3922   if (gtk_combo_box_get_active_iter (combo, &iter))
3923     shortcuts_activate_iter (impl, &iter);
3924 }
3925
3926 /* Creates the combo box with the save folders */
3927 static GtkWidget *
3928 save_folder_combo_create (GtkFileChooserDefault *impl)
3929 {
3930   GtkWidget *combo;
3931   GtkCellRenderer *cell;
3932
3933   combo = g_object_new (GTK_TYPE_COMBO_BOX,
3934                         "model", impl->shortcuts_model,
3935                         "focus-on-click", FALSE,
3936                         NULL);
3937   gtk_widget_show (combo);
3938
3939   cell = gtk_cell_renderer_pixbuf_new ();
3940   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE);
3941   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
3942                                   "pixbuf", SHORTCUTS_COL_PIXBUF,
3943                                   "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
3944                                   "sensitive", SHORTCUTS_COL_PIXBUF_VISIBLE,
3945                                   NULL);
3946
3947   cell = gtk_cell_renderer_text_new ();
3948   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
3949   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
3950                                   "text", SHORTCUTS_COL_NAME,
3951                                   "sensitive", SHORTCUTS_COL_PIXBUF_VISIBLE,
3952                                   NULL);
3953
3954   gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo),
3955                                         shortcuts_row_separator_func,
3956                                         GINT_TO_POINTER (SHORTCUTS_COL_NAME),
3957                                         NULL);
3958
3959   g_signal_connect (combo, "changed",
3960                     G_CALLBACK (save_folder_combo_changed_cb), impl);
3961
3962   return combo;
3963 }
3964
3965 /* Creates the widgets specific to Save mode */
3966 static GtkWidget *
3967 save_widgets_create (GtkFileChooserDefault *impl)
3968 {
3969   GtkWidget *vbox;
3970   GtkWidget *table;
3971   GtkWidget *widget;
3972   GtkWidget *alignment;
3973
3974   vbox = gtk_vbox_new (FALSE, 12);
3975
3976   table = gtk_table_new (2, 2, FALSE);
3977   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
3978   gtk_widget_show (table);
3979   gtk_table_set_row_spacings (GTK_TABLE (table), 12);
3980   gtk_table_set_col_spacings (GTK_TABLE (table), 12);
3981
3982   /* Name entry */
3983
3984   widget = gtk_label_new_with_mnemonic (_("_Name:"));
3985   gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
3986   gtk_table_attach (GTK_TABLE (table), widget,
3987                     0, 1, 0, 1,
3988                     GTK_FILL, GTK_FILL,
3989                     0, 0);
3990   gtk_widget_show (widget);
3991
3992   impl->save_file_name_entry = _gtk_file_chooser_entry_new (TRUE);
3993   _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry),
3994                                            impl->file_system);
3995   gtk_entry_set_width_chars (GTK_ENTRY (impl->save_file_name_entry), 45);
3996   gtk_entry_set_activates_default (GTK_ENTRY (impl->save_file_name_entry), TRUE);
3997   gtk_table_attach (GTK_TABLE (table), impl->save_file_name_entry,
3998                     1, 2, 0, 1,
3999                     GTK_EXPAND | GTK_FILL, 0,
4000                     0, 0);
4001   gtk_widget_show (impl->save_file_name_entry);
4002   gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->save_file_name_entry);
4003
4004   /* Folder combo */
4005   impl->save_folder_label = gtk_label_new (NULL);
4006   gtk_misc_set_alignment (GTK_MISC (impl->save_folder_label), 0.0, 0.5);
4007   gtk_table_attach (GTK_TABLE (table), impl->save_folder_label,
4008                     0, 1, 1, 2,
4009                     GTK_FILL, GTK_FILL,
4010                     0, 0);
4011   gtk_widget_show (impl->save_folder_label);
4012
4013   impl->save_folder_combo = save_folder_combo_create (impl);
4014   gtk_table_attach (GTK_TABLE (table), impl->save_folder_combo,
4015                     1, 2, 1, 2,
4016                     GTK_EXPAND | GTK_FILL, GTK_FILL,
4017                     0, 0);
4018   gtk_label_set_mnemonic_widget (GTK_LABEL (impl->save_folder_label), impl->save_folder_combo);
4019
4020   /* Expander */
4021   alignment = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
4022   gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
4023
4024   impl->save_expander = gtk_expander_new_with_mnemonic (_("_Browse for other folders"));
4025   gtk_container_add (GTK_CONTAINER (alignment), impl->save_expander);
4026   g_signal_connect (impl->save_expander, "notify::expanded",
4027                     G_CALLBACK (expander_changed_cb),
4028                     impl);
4029   gtk_widget_show_all (alignment);
4030
4031   return vbox;
4032 }
4033
4034 /* Creates the main hpaned with the widgets shared by Open and Save mode */
4035 static GtkWidget *
4036 browse_widgets_create (GtkFileChooserDefault *impl)
4037 {
4038   GtkWidget *vbox;
4039   GtkWidget *hpaned;
4040   GtkWidget *widget;
4041   GtkSizeGroup *size_group;
4042
4043   /* size group is used by the [+][-] buttons and the filter combo */
4044   size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
4045   vbox = gtk_vbox_new (FALSE, 12);
4046
4047   /* Paned widget */
4048   hpaned = gtk_hpaned_new ();
4049   gtk_widget_show (hpaned);
4050   gtk_paned_set_position (GTK_PANED (hpaned), 200); /* FIXME: this sucks */
4051   gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
4052
4053   widget = shortcuts_pane_create (impl, size_group);
4054   gtk_paned_pack1 (GTK_PANED (hpaned), widget, FALSE, FALSE);
4055   widget = file_pane_create (impl, size_group);
4056   gtk_paned_pack2 (GTK_PANED (hpaned), widget, TRUE, FALSE);
4057
4058   g_object_unref (size_group);
4059
4060   return vbox;
4061 }
4062
4063 static GObject*
4064 gtk_file_chooser_default_constructor (GType                  type,
4065                                       guint                  n_construct_properties,
4066                                       GObjectConstructParam *construct_params)
4067 {
4068   GtkFileChooserDefault *impl;
4069   GObject *object;
4070
4071   object = parent_class->constructor (type,
4072                                       n_construct_properties,
4073                                       construct_params);
4074   impl = GTK_FILE_CHOOSER_DEFAULT (object);
4075
4076   g_assert (impl->file_system);
4077
4078   gtk_widget_push_composite_child ();
4079
4080   /* Shortcuts model */
4081
4082   shortcuts_model_create (impl);
4083
4084   /* Widgets for Save mode */
4085   impl->save_widgets = save_widgets_create (impl);
4086   gtk_box_pack_start (GTK_BOX (impl), impl->save_widgets, FALSE, FALSE, 0);
4087
4088   /* The browse widgets */
4089   impl->browse_widgets = browse_widgets_create (impl);
4090   gtk_box_pack_start (GTK_BOX (impl), impl->browse_widgets, TRUE, TRUE, 0);
4091
4092   /* Alignment to hold extra widget */
4093   impl->extra_align = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
4094   gtk_box_pack_start (GTK_BOX (impl), impl->extra_align, FALSE, FALSE, 0);
4095
4096   gtk_widget_pop_composite_child ();
4097   update_appearance (impl);
4098
4099   return object;
4100 }
4101
4102 /* Sets the extra_widget by packing it in the appropriate place */
4103 static void
4104 set_extra_widget (GtkFileChooserDefault *impl,
4105                   GtkWidget             *extra_widget)
4106 {
4107   if (extra_widget)
4108     {
4109       g_object_ref (extra_widget);
4110       /* FIXME: is this right ? */
4111       gtk_widget_show (extra_widget);
4112     }
4113
4114   if (impl->extra_widget)
4115     {
4116       gtk_container_remove (GTK_CONTAINER (impl->extra_align), impl->extra_widget);
4117       g_object_unref (impl->extra_widget);
4118     }
4119
4120   impl->extra_widget = extra_widget;
4121   if (impl->extra_widget)
4122     {
4123       gtk_container_add (GTK_CONTAINER (impl->extra_align), impl->extra_widget);
4124       gtk_widget_show (impl->extra_align);
4125     }
4126   else
4127     gtk_widget_hide (impl->extra_align);
4128 }
4129
4130 static void
4131 set_local_only (GtkFileChooserDefault *impl,
4132                 gboolean               local_only)
4133 {
4134   if (local_only != impl->local_only)
4135     {
4136       impl->local_only = local_only;
4137
4138       if (impl->shortcuts_model && impl->file_system)
4139         {
4140           shortcuts_add_volumes (impl);
4141           shortcuts_add_bookmarks (impl);
4142         }
4143
4144       if (local_only &&
4145           !gtk_file_system_path_is_local (impl->file_system, impl->current_folder))
4146         {
4147           /* If we are pointing to a non-local folder, make an effort to change
4148            * back to a local folder, but it's really up to the app to not cause
4149            * such a situation, so we ignore errors.
4150            */
4151           const gchar *home = g_get_home_dir ();
4152           GtkFilePath *home_path;
4153
4154           if (home == NULL)
4155             return;
4156
4157           home_path = gtk_file_system_filename_to_path (impl->file_system, home);
4158
4159           _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), home_path, NULL);
4160
4161           gtk_file_path_free (home_path);
4162         }
4163     }
4164 }
4165
4166 static void
4167 volumes_changed_cb (GtkFileSystem         *file_system,
4168                     GtkFileChooserDefault *impl)
4169 {
4170   shortcuts_add_volumes (impl);
4171 }
4172
4173 /* Callback used when the set of bookmarks changes in the file system */
4174 static void
4175 bookmarks_changed_cb (GtkFileSystem         *file_system,
4176                       GtkFileChooserDefault *impl)
4177 {
4178   shortcuts_add_bookmarks (impl);
4179
4180   bookmarks_check_add_sensitivity (impl);
4181   bookmarks_check_remove_sensitivity (impl);
4182   shortcuts_check_popup_sensitivity (impl);
4183 }
4184
4185 /* Sets the file chooser to multiple selection mode */
4186 static void
4187 set_select_multiple (GtkFileChooserDefault *impl,
4188                      gboolean               select_multiple,
4189                      gboolean               property_notify)
4190 {
4191   GtkTreeSelection *selection;
4192   GtkSelectionMode mode;
4193
4194   if (select_multiple == impl->select_multiple)
4195     return;
4196
4197   mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE;
4198
4199   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4200   gtk_tree_selection_set_mode (selection, mode);
4201
4202   impl->select_multiple = select_multiple;
4203   g_object_notify (G_OBJECT (impl), "select-multiple");
4204
4205   check_preview_change (impl);
4206 }
4207
4208 static void
4209 set_file_system_backend (GtkFileChooserDefault *impl,
4210                          const char *backend)
4211 {
4212   profile_start ("start for backend", backend ? backend : "default");
4213
4214   if (impl->file_system)
4215     {
4216       g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
4217       impl->volumes_changed_id = 0;
4218       g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
4219       impl->bookmarks_changed_id = 0;
4220       g_object_unref (impl->file_system);
4221     }
4222
4223   impl->file_system = NULL;
4224   if (backend)
4225     impl->file_system = _gtk_file_system_create (backend);
4226   else
4227     {
4228       GtkSettings *settings = gtk_settings_get_default ();
4229       gchar *default_backend = NULL;
4230
4231       g_object_get (settings, "gtk-file-chooser-backend", &default_backend, NULL);
4232       if (default_backend)
4233         {
4234           impl->file_system = _gtk_file_system_create (default_backend);
4235           g_free (default_backend);
4236         }
4237     }
4238
4239   if (!impl->file_system)
4240     {
4241 #if defined (G_OS_UNIX)
4242       impl->file_system = gtk_file_system_unix_new ();
4243 #elif defined (G_OS_WIN32)
4244       impl->file_system = gtk_file_system_win32_new ();
4245 #else
4246 #error "No default filesystem implementation on the platform"
4247 #endif
4248     }
4249
4250   if (impl->file_system)
4251     {
4252       impl->volumes_changed_id = g_signal_connect (impl->file_system, "volumes-changed",
4253                                                    G_CALLBACK (volumes_changed_cb),
4254                                                    impl);
4255       impl->bookmarks_changed_id = g_signal_connect (impl->file_system, "bookmarks-changed",
4256                                                      G_CALLBACK (bookmarks_changed_cb),
4257                                                      impl);
4258     }
4259
4260   profile_end ("end", NULL);
4261 }
4262
4263 /* This function is basically a do_all function.
4264  *
4265  * It sets the visibility on all the widgets based on the current state, and
4266  * moves the custom_widget if needed.
4267  */
4268 static void
4269 update_appearance (GtkFileChooserDefault *impl)
4270 {
4271   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
4272       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4273     {
4274       const char *text;
4275
4276       gtk_widget_show (impl->save_widgets);
4277
4278       if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
4279         text = _("Save in _folder:");
4280       else
4281         text = _("Create in _folder:");
4282
4283       gtk_label_set_text_with_mnemonic (GTK_LABEL (impl->save_folder_label), text);
4284
4285       if (gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
4286         {
4287           gtk_widget_set_sensitive (impl->save_folder_label, FALSE);
4288           gtk_widget_set_sensitive (impl->save_folder_combo, FALSE);
4289           gtk_widget_show (impl->browse_widgets);
4290         }
4291       else
4292         {
4293           gtk_widget_set_sensitive (impl->save_folder_label, TRUE);
4294           gtk_widget_set_sensitive (impl->save_folder_combo, TRUE);
4295           gtk_widget_hide (impl->browse_widgets);
4296         }
4297
4298       gtk_widget_show (impl->browse_new_folder_button);
4299
4300       if (impl->select_multiple)
4301         {
4302           g_warning ("Save mode cannot be set in conjunction with multiple selection mode.  "
4303                      "Re-setting to single selection mode.");
4304           set_select_multiple (impl, FALSE, TRUE);
4305         }
4306     }
4307   else if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
4308            impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
4309     {
4310       gtk_widget_hide (impl->save_widgets);
4311       gtk_widget_show (impl->browse_widgets);
4312     }
4313
4314   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
4315     gtk_widget_hide (impl->browse_new_folder_button);
4316   else
4317     gtk_widget_show (impl->browse_new_folder_button);
4318
4319   gtk_widget_queue_draw (impl->browse_files_tree_view);
4320
4321   g_signal_emit_by_name (impl, "default-size-changed");
4322 }
4323
4324 static void
4325 gtk_file_chooser_default_set_property (GObject      *object,
4326                                        guint         prop_id,
4327                                        const GValue *value,
4328                                        GParamSpec   *pspec)
4329
4330 {
4331   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
4332
4333   switch (prop_id)
4334     {
4335     case GTK_FILE_CHOOSER_PROP_ACTION:
4336       {
4337         GtkFileChooserAction action = g_value_get_enum (value);
4338
4339         if (action != impl->action)
4340           {
4341             gtk_file_chooser_default_unselect_all (GTK_FILE_CHOOSER (impl));
4342             
4343             if ((action == GTK_FILE_CHOOSER_ACTION_SAVE || action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4344                 && impl->select_multiple)
4345               {
4346                 g_warning ("Tried to change the file chooser action to SAVE or CREATE_FOLDER, but "
4347                            "this is not allowed in multiple selection mode.  Resetting the file chooser "
4348                            "to single selection mode.");
4349                 set_select_multiple (impl, FALSE, TRUE);
4350               }
4351             impl->action = action;
4352             update_appearance (impl);
4353           }
4354         
4355         if (impl->save_file_name_entry)
4356           _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry),
4357                                               action);
4358       }
4359       break;
4360
4361     case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
4362       set_file_system_backend (impl, g_value_get_string (value));
4363       break;
4364
4365     case GTK_FILE_CHOOSER_PROP_FILTER:
4366       set_current_filter (impl, g_value_get_object (value));
4367       break;
4368
4369     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
4370       set_local_only (impl, g_value_get_boolean (value));
4371       break;
4372
4373     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
4374       set_preview_widget (impl, g_value_get_object (value));
4375       break;
4376
4377     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
4378       impl->preview_widget_active = g_value_get_boolean (value);
4379       update_preview_widget_visibility (impl);
4380       break;
4381
4382     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
4383       impl->use_preview_label = g_value_get_boolean (value);
4384       update_preview_widget_visibility (impl);
4385       break;
4386
4387     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
4388       set_extra_widget (impl, g_value_get_object (value));
4389       break;
4390
4391     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
4392       {
4393         gboolean select_multiple = g_value_get_boolean (value);
4394         if ((impl->action == GTK_FILE_CHOOSER_ACTION_SAVE || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4395             && select_multiple)
4396           {
4397             g_warning ("Tried to set the file chooser to multiple selection mode, but this is "
4398                        "not allowed in SAVE or CREATE_FOLDER modes.  Ignoring the change and "
4399                        "leaving the file chooser in single selection mode.");
4400             return;
4401           }
4402
4403         set_select_multiple (impl, select_multiple, FALSE);
4404       }
4405       break;
4406
4407     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
4408       {
4409         gboolean show_hidden = g_value_get_boolean (value);
4410         if (show_hidden != impl->show_hidden)
4411           {
4412             impl->show_hidden = show_hidden;
4413
4414             if (impl->browse_files_model)
4415               _gtk_file_system_model_set_show_hidden (impl->browse_files_model, show_hidden);
4416           }
4417       }
4418       break;
4419
4420     case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
4421       {
4422         gboolean do_overwrite_confirmation = g_value_get_boolean (value);
4423         impl->do_overwrite_confirmation = do_overwrite_confirmation;
4424       }
4425       break;
4426
4427     default:
4428       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4429       break;
4430     }
4431 }
4432
4433 static void
4434 gtk_file_chooser_default_get_property (GObject    *object,
4435                                        guint       prop_id,
4436                                        GValue     *value,
4437                                        GParamSpec *pspec)
4438 {
4439   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
4440
4441   switch (prop_id)
4442     {
4443     case GTK_FILE_CHOOSER_PROP_ACTION:
4444       g_value_set_enum (value, impl->action);
4445       break;
4446
4447     case GTK_FILE_CHOOSER_PROP_FILTER:
4448       g_value_set_object (value, impl->current_filter);
4449       break;
4450
4451     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
4452       g_value_set_boolean (value, impl->local_only);
4453       break;
4454
4455     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
4456       g_value_set_object (value, impl->preview_widget);
4457       break;
4458
4459     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
4460       g_value_set_boolean (value, impl->preview_widget_active);
4461       break;
4462
4463     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
4464       g_value_set_boolean (value, impl->use_preview_label);
4465       break;
4466
4467     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
4468       g_value_set_object (value, impl->extra_widget);
4469       break;
4470
4471     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
4472       g_value_set_boolean (value, impl->select_multiple);
4473       break;
4474
4475     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
4476       g_value_set_boolean (value, impl->show_hidden);
4477       break;
4478
4479     case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
4480       g_value_set_boolean (value, impl->do_overwrite_confirmation);
4481       break;
4482
4483     default:
4484       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4485       break;
4486     }
4487 }
4488
4489 /* Removes the settings signal handler.  It's safe to call multiple times */
4490 static void
4491 remove_settings_signal (GtkFileChooserDefault *impl,
4492                         GdkScreen             *screen)
4493 {
4494   if (impl->settings_signal_id)
4495     {
4496       GtkSettings *settings;
4497
4498       settings = gtk_settings_get_for_screen (screen);
4499       g_signal_handler_disconnect (settings,
4500                                    impl->settings_signal_id);
4501       impl->settings_signal_id = 0;
4502     }
4503 }
4504
4505 static void
4506 gtk_file_chooser_default_dispose (GObject *object)
4507 {
4508   GtkFileChooserDefault *impl = (GtkFileChooserDefault *) object;
4509
4510   if (impl->extra_widget)
4511     {
4512       g_object_unref (impl->extra_widget);
4513       impl->extra_widget = NULL;
4514     }
4515
4516   remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl)));
4517
4518   G_OBJECT_CLASS (parent_class)->dispose (object);
4519 }
4520
4521 /* We override show-all since we have internal widgets that
4522  * shouldn't be shown when you call show_all(), like the filter
4523  * combo box.
4524  */
4525 static void
4526 gtk_file_chooser_default_show_all (GtkWidget *widget)
4527 {
4528   GtkFileChooserDefault *impl = (GtkFileChooserDefault *) widget;
4529
4530   gtk_widget_show (widget);
4531
4532   if (impl->extra_widget)
4533     gtk_widget_show_all (impl->extra_widget);
4534 }
4535
4536 /* Handler for GtkWindow::set-focus; this is where we save the last-focused
4537  * widget on our toplevel.  See gtk_file_chooser_default_hierarchy_changed()
4538  */
4539 static void
4540 toplevel_set_focus_cb (GtkWindow             *window,
4541                        GtkWidget             *focus,
4542                        GtkFileChooserDefault *impl)
4543 {
4544   impl->toplevel_last_focus_widget = gtk_window_get_focus (window);
4545 }
4546
4547 /* We monitor the focus widget on our toplevel to be able to know which widget
4548  * was last focused at the time our "should_respond" method gets called.
4549  */
4550 static void
4551 gtk_file_chooser_default_hierarchy_changed (GtkWidget *widget,
4552                                             GtkWidget *previous_toplevel)
4553 {
4554   GtkFileChooserDefault *impl;
4555   GtkWidget *toplevel;
4556
4557   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
4558
4559   if (previous_toplevel)
4560     {
4561       g_assert (impl->toplevel_set_focus_id != 0);
4562       g_signal_handler_disconnect (previous_toplevel, impl->toplevel_set_focus_id);
4563       impl->toplevel_set_focus_id = 0;
4564       impl->toplevel_last_focus_widget = NULL;
4565     }
4566   else
4567     g_assert (impl->toplevel_set_focus_id == 0);
4568
4569   toplevel = gtk_widget_get_toplevel (widget);
4570   if (GTK_IS_WINDOW (toplevel))
4571     {
4572       impl->toplevel_set_focus_id = g_signal_connect (toplevel, "set-focus",
4573                                                       G_CALLBACK (toplevel_set_focus_cb), impl);
4574       impl->toplevel_last_focus_widget = gtk_window_get_focus (GTK_WINDOW (toplevel));
4575     }
4576 }
4577
4578 /* Changes the icons wherever it is needed */
4579 static void
4580 change_icon_theme (GtkFileChooserDefault *impl)
4581 {
4582   GtkSettings *settings;
4583   gint width, height;
4584
4585   settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
4586
4587   if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU, &width, &height))
4588     impl->icon_size = MAX (width, height);
4589   else
4590     impl->icon_size = FALLBACK_ICON_SIZE;
4591
4592   shortcuts_reload_icons (impl);
4593   gtk_widget_queue_resize (impl->browse_files_tree_view);
4594 }
4595
4596 /* Callback used when a GtkSettings value changes */
4597 static void
4598 settings_notify_cb (GObject               *object,
4599                     GParamSpec            *pspec,
4600                     GtkFileChooserDefault *impl)
4601 {
4602   const char *name;
4603
4604   name = g_param_spec_get_name (pspec);
4605
4606   if (strcmp (name, "gtk-icon-theme-name") == 0
4607       || strcmp (name, "gtk-icon-sizes") == 0)
4608     change_icon_theme (impl);
4609 }
4610
4611 /* Installs a signal handler for GtkSettings so that we can monitor changes in
4612  * the icon theme.
4613  */
4614 static void
4615 check_icon_theme (GtkFileChooserDefault *impl)
4616 {
4617   GtkSettings *settings;
4618
4619   if (impl->settings_signal_id)
4620     return;
4621
4622   if (gtk_widget_has_screen (GTK_WIDGET (impl)))
4623     {
4624       settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
4625       impl->settings_signal_id = g_signal_connect (settings, "notify",
4626                                                    G_CALLBACK (settings_notify_cb), impl);
4627
4628       change_icon_theme (impl);
4629     }
4630 }
4631
4632 static void
4633 gtk_file_chooser_default_style_set (GtkWidget *widget,
4634                                     GtkStyle  *previous_style)
4635 {
4636   GtkFileChooserDefault *impl;
4637
4638   profile_start ("start", NULL);
4639
4640   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
4641
4642   if (GTK_WIDGET_CLASS (parent_class)->style_set)
4643     GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
4644
4645   if (gtk_widget_has_screen (GTK_WIDGET (impl)))
4646     change_icon_theme (impl);
4647
4648   g_signal_emit_by_name (widget, "default-size-changed");
4649
4650   profile_end ("end", NULL);
4651 }
4652
4653 static void
4654 gtk_file_chooser_default_screen_changed (GtkWidget *widget,
4655                                          GdkScreen *previous_screen)
4656 {
4657   GtkFileChooserDefault *impl;
4658
4659   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
4660
4661   if (GTK_WIDGET_CLASS (parent_class)->screen_changed)
4662     GTK_WIDGET_CLASS (parent_class)->screen_changed (widget, previous_screen);
4663
4664   remove_settings_signal (impl, previous_screen);
4665   check_icon_theme (impl);
4666
4667   g_signal_emit_by_name (widget, "default-size-changed");
4668 }
4669
4670 static gboolean
4671 get_is_file_filtered (GtkFileChooserDefault *impl,
4672                       const GtkFilePath     *path,
4673                       GtkFileInfo           *file_info)
4674 {
4675   GtkFileFilterInfo filter_info;
4676   GtkFileFilterFlags needed;
4677   gboolean result;
4678
4679   if (!impl->current_filter)
4680     return FALSE;
4681
4682   filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
4683
4684   needed = gtk_file_filter_get_needed (impl->current_filter);
4685
4686   filter_info.display_name = gtk_file_info_get_display_name (file_info);
4687   filter_info.mime_type = gtk_file_info_get_mime_type (file_info);
4688
4689   if (needed & GTK_FILE_FILTER_FILENAME)
4690     {
4691       filter_info.filename = gtk_file_system_path_to_filename (impl->file_system, path);
4692       if (filter_info.filename)
4693         filter_info.contains |= GTK_FILE_FILTER_FILENAME;
4694     }
4695   else
4696     filter_info.filename = NULL;
4697
4698   if (needed & GTK_FILE_FILTER_URI)
4699     {
4700       filter_info.uri = gtk_file_system_path_to_uri (impl->file_system, path);
4701       if (filter_info.uri)
4702         filter_info.contains |= GTK_FILE_FILTER_URI;
4703     }
4704   else
4705     filter_info.uri = NULL;
4706
4707   result = gtk_file_filter_filter (impl->current_filter, &filter_info);
4708
4709   if (filter_info.filename)
4710     g_free ((gchar *)filter_info.filename);
4711   if (filter_info.uri)
4712     g_free ((gchar *)filter_info.uri);
4713
4714   return !result;
4715 }
4716
4717 /* GtkWidget::map method */
4718 static void
4719 gtk_file_chooser_default_map (GtkWidget *widget)
4720 {
4721   GtkFileChooserDefault *impl;
4722
4723   profile_start ("start", NULL);
4724
4725   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
4726
4727   GTK_WIDGET_CLASS (parent_class)->map (widget);
4728
4729   if (impl->current_folder)
4730     {
4731       pending_select_paths_store_selection (impl);
4732       change_folder_and_display_error (impl, impl->current_folder);
4733     }
4734
4735   bookmarks_changed_cb (impl->file_system, impl);
4736
4737   profile_end ("end", NULL);
4738 }
4739
4740 static gboolean
4741 list_model_filter_func (GtkFileSystemModel *model,
4742                         GtkFilePath        *path,
4743                         const GtkFileInfo  *file_info,
4744                         gpointer            user_data)
4745 {
4746   GtkFileChooserDefault *impl = user_data;
4747
4748   if (!impl->current_filter)
4749     return TRUE;
4750
4751   if (gtk_file_info_get_is_folder (file_info))
4752     return TRUE;
4753
4754   return !get_is_file_filtered (impl, path, (GtkFileInfo *) file_info);
4755 }
4756
4757 static void
4758 install_list_model_filter (GtkFileChooserDefault *impl)
4759 {
4760   GtkFileSystemModelFilter filter;
4761   gpointer data;
4762
4763   g_assert (impl->browse_files_model != NULL);
4764
4765   if (impl->current_filter)
4766     {
4767       filter = list_model_filter_func;
4768       data   = impl;
4769     }
4770   else
4771     {
4772       filter = NULL;
4773       data   = NULL;
4774     }
4775   
4776   _gtk_file_system_model_set_filter (impl->browse_files_model,
4777                                      filter,
4778                                      data);
4779 }
4780
4781 #define COMPARE_DIRECTORIES                                                                                    \
4782   GtkFileChooserDefault *impl = user_data;                                                                     \
4783   const GtkFileInfo *info_a = _gtk_file_system_model_get_info (impl->browse_files_model, a);                           \
4784   const GtkFileInfo *info_b = _gtk_file_system_model_get_info (impl->browse_files_model, b);                           \
4785   gboolean dir_a, dir_b;                                                                                       \
4786                                                                                                                \
4787   if (info_a)                                                                                                  \
4788     dir_a = gtk_file_info_get_is_folder (info_a);                                                              \
4789   else                                                                                                         \
4790     return impl->list_sort_ascending ? -1 : 1;                                                                 \
4791                                                                                                                \
4792   if (info_b)                                                                                                  \
4793     dir_b = gtk_file_info_get_is_folder (info_b);                                                              \
4794   else                                                                                                         \
4795     return impl->list_sort_ascending ? 1 : -1;                                                                 \
4796                                                                                                                \
4797   if (dir_a != dir_b)                                                                                          \
4798     return impl->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */
4799
4800 /* Sort callback for the filename column */
4801 static gint
4802 name_sort_func (GtkTreeModel *model,
4803                 GtkTreeIter  *a,
4804                 GtkTreeIter  *b,
4805                 gpointer      user_data)
4806 {
4807   COMPARE_DIRECTORIES;
4808   else
4809     return strcmp (gtk_file_info_get_display_key (info_a), gtk_file_info_get_display_key (info_b));
4810 }
4811
4812 /* Sort callback for the size column */
4813 static gint
4814 size_sort_func (GtkTreeModel *model,
4815                 GtkTreeIter  *a,
4816                 GtkTreeIter  *b,
4817                 gpointer      user_data)
4818 {
4819   COMPARE_DIRECTORIES;
4820   else
4821     {
4822       gint64 size_a = gtk_file_info_get_size (info_a);
4823       gint64 size_b = gtk_file_info_get_size (info_b);
4824
4825       return size_a > size_b ? -1 : (size_a == size_b ? 0 : 1);
4826     }
4827 }
4828
4829 /* Sort callback for the mtime column */
4830 static gint
4831 mtime_sort_func (GtkTreeModel *model,
4832                  GtkTreeIter  *a,
4833                  GtkTreeIter  *b,
4834                  gpointer      user_data)
4835 {
4836   COMPARE_DIRECTORIES;
4837   else
4838     {
4839       GtkFileTime ta = gtk_file_info_get_modification_time (info_a);
4840       GtkFileTime tb = gtk_file_info_get_modification_time (info_b);
4841
4842       return ta > tb ? -1 : (ta == tb ? 0 : 1);
4843     }
4844 }
4845
4846 /* Callback used when the sort column changes.  We cache the sort order for use
4847  * in name_sort_func().
4848  */
4849 static void
4850 list_sort_column_changed_cb (GtkTreeSortable       *sortable,
4851                              GtkFileChooserDefault *impl)
4852 {
4853   GtkSortType sort_type;
4854
4855   if (gtk_tree_sortable_get_sort_column_id (sortable, NULL, &sort_type))
4856     impl->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
4857 }
4858
4859 static void
4860 set_busy_cursor (GtkFileChooserDefault *impl,
4861                  gboolean               busy)
4862 {
4863   GtkWindow *toplevel;
4864   GdkDisplay *display;
4865   GdkCursor *cursor;
4866
4867   toplevel = get_toplevel (GTK_WIDGET (impl));
4868   if (!toplevel || !GTK_WIDGET_REALIZED (toplevel))
4869     return;
4870
4871   display = gtk_widget_get_display (GTK_WIDGET (toplevel));
4872
4873   if (busy)
4874     cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
4875   else
4876     cursor = NULL;
4877
4878   gdk_window_set_cursor (GTK_WIDGET (toplevel)->window, cursor);
4879   gdk_display_flush (display);
4880
4881   if (cursor)
4882     gdk_cursor_unref (cursor);
4883 }
4884
4885 /* Creates a sort model to wrap the file system model and sets it on the tree view */
4886 static void
4887 load_set_model (GtkFileChooserDefault *impl)
4888 {
4889   g_assert (impl->browse_files_model != NULL);
4890   g_assert (impl->sort_model == NULL);
4891
4892   impl->sort_model = (GtkTreeModelSort *)gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (impl->browse_files_model));
4893   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, name_sort_func, impl, NULL);
4894   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_SIZE, size_sort_func, impl, NULL);
4895   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_MTIME, mtime_sort_func, impl, NULL);
4896   gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->sort_model), NULL, NULL, NULL);
4897   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, GTK_SORT_ASCENDING);
4898   impl->list_sort_ascending = TRUE;
4899
4900   g_signal_connect (impl->sort_model, "sort-column-changed",
4901                     G_CALLBACK (list_sort_column_changed_cb), impl);
4902
4903   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
4904                            GTK_TREE_MODEL (impl->sort_model));
4905   gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->browse_files_tree_view));
4906   gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_files_tree_view),
4907                                    GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
4908 }
4909
4910 /* Timeout callback used when the loading timer expires */
4911 static gboolean
4912 load_timeout_cb (gpointer data)
4913 {
4914   GtkFileChooserDefault *impl;
4915
4916   profile_start ("start", NULL);
4917
4918   GDK_THREADS_ENTER ();
4919
4920   impl = GTK_FILE_CHOOSER_DEFAULT (data);
4921   g_assert (impl->load_state == LOAD_PRELOAD);
4922   g_assert (impl->load_timeout_id != 0);
4923   g_assert (impl->browse_files_model != NULL);
4924
4925   impl->load_timeout_id = 0;
4926   impl->load_state = LOAD_LOADING;
4927
4928   load_set_model (impl);
4929
4930   GDK_THREADS_LEAVE ();
4931
4932   profile_end ("end", NULL);
4933
4934   return FALSE;
4935 }
4936
4937 /* Sets up a new load timer for the model and switches to the LOAD_LOADING state */
4938 static void
4939 load_setup_timer (GtkFileChooserDefault *impl)
4940 {
4941   g_assert (impl->load_timeout_id == 0);
4942   g_assert (impl->load_state != LOAD_PRELOAD);
4943
4944   impl->load_timeout_id = g_timeout_add (MAX_LOADING_TIME, load_timeout_cb, impl);
4945   impl->load_state = LOAD_PRELOAD;
4946 }
4947
4948 /* Removes the load timeout and switches to the LOAD_FINISHED state */
4949 static void
4950 load_remove_timer (GtkFileChooserDefault *impl)
4951 {
4952   if (impl->load_timeout_id != 0)
4953     {
4954       g_assert (impl->load_state == LOAD_PRELOAD);
4955
4956       g_source_remove (impl->load_timeout_id);
4957       impl->load_timeout_id = 0;
4958       impl->load_state = LOAD_EMPTY;
4959     }
4960   else
4961     g_assert (impl->load_state == LOAD_EMPTY ||
4962               impl->load_state == LOAD_LOADING ||
4963               impl->load_state == LOAD_FINISHED);
4964 }
4965
4966 /* Selects the first row in the file list */
4967 static void
4968 browse_files_select_first_row (GtkFileChooserDefault *impl)
4969 {
4970   GtkTreePath *path;
4971
4972   if (!impl->sort_model)
4973     return;
4974
4975   path = gtk_tree_path_new_from_indices (0, -1);
4976   gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), path, NULL, FALSE);
4977   gtk_tree_path_free (path);
4978 }
4979
4980 struct center_selected_row_closure {
4981   GtkFileChooserDefault *impl;
4982   gboolean already_centered;
4983 };
4984
4985 /* Callback used from gtk_tree_selection_selected_foreach(); centers the
4986  * selected row in the tree view.
4987  */
4988 static void
4989 center_selected_row_foreach_cb (GtkTreeModel      *model,
4990                                 GtkTreePath       *path,
4991                                 GtkTreeIter       *iter,
4992                                 gpointer           data)
4993 {
4994   struct center_selected_row_closure *closure;
4995
4996   closure = data;
4997   if (closure->already_centered)
4998     return;
4999
5000   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (closure->impl->browse_files_tree_view), path, NULL, TRUE, 0.5, 0.0);
5001   closure->already_centered = TRUE;
5002 }
5003
5004 /* Centers the selected row in the tree view */
5005 static void
5006 browse_files_center_selected_row (GtkFileChooserDefault *impl)
5007 {
5008   struct center_selected_row_closure closure;
5009   GtkTreeSelection *selection;
5010
5011   closure.impl = impl;
5012   closure.already_centered = FALSE;
5013
5014   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5015   gtk_tree_selection_selected_foreach (selection, center_selected_row_foreach_cb, &closure);
5016 }
5017
5018 static gboolean
5019 show_and_select_paths (GtkFileChooserDefault *impl,
5020                        const GtkFilePath     *parent_path,
5021                        const GtkFilePath     *only_one_path,
5022                        GSList                *paths,
5023                        GError               **error)
5024 {
5025   GtkFileFolder *folder;
5026   gboolean success;
5027   gboolean have_hidden;
5028   gboolean have_filtered;
5029
5030   profile_start ("start", NULL);
5031
5032   if (!only_one_path && !paths)
5033     {
5034       profile_end ("end", NULL);
5035       return TRUE;
5036     }
5037
5038   folder = gtk_file_system_get_folder (impl->file_system, parent_path, GTK_FILE_INFO_IS_HIDDEN, error);
5039   if (!folder)
5040     {
5041       profile_end ("end", NULL);
5042       return FALSE;
5043     }
5044
5045   success = FALSE;
5046   have_hidden = FALSE;
5047   have_filtered = FALSE;
5048
5049   if (only_one_path)
5050     {
5051       GtkFileInfo *info;
5052
5053       info = gtk_file_folder_get_info (folder, only_one_path, error);
5054       if (info)
5055         {
5056           success = TRUE;
5057           have_hidden = gtk_file_info_get_is_hidden (info);
5058           have_filtered = get_is_file_filtered (impl, only_one_path, info);
5059           gtk_file_info_free (info);
5060         }
5061     }
5062   else
5063     {
5064       GSList *l;
5065
5066       for (l = paths; l; l = l->next)
5067         {
5068           const GtkFilePath *path;
5069           GtkFileInfo *info;
5070
5071           path = l->data;
5072
5073           /* NULL GError */
5074           info = gtk_file_folder_get_info (folder, path, NULL);
5075           if (info)
5076             {
5077               if (!have_hidden)
5078                 have_hidden = gtk_file_info_get_is_hidden (info);
5079
5080               if (!have_filtered)
5081                 have_filtered = get_is_file_filtered (impl, path, info);
5082
5083               gtk_file_info_free (info);
5084
5085               if (have_hidden && have_filtered)
5086                 break; /* we now have all the information we need */
5087             }
5088         }
5089
5090       success = TRUE;
5091     }
5092
5093   g_object_unref (folder);
5094
5095   if (!success)
5096     {
5097       profile_end ("end", NULL);
5098       return FALSE;
5099     }
5100
5101   if (have_hidden)
5102     g_object_set (impl, "show-hidden", TRUE, NULL);
5103
5104   if (have_filtered)
5105     set_current_filter (impl, NULL);
5106
5107   if (only_one_path)
5108     _gtk_file_system_model_path_do (impl->browse_files_model, only_one_path, select_func, impl);
5109   else
5110     {
5111       GSList *l;
5112
5113       for (l = paths; l; l = l->next)
5114         {
5115           const GtkFilePath *path;
5116
5117           path = l->data;
5118           _gtk_file_system_model_path_do (impl->browse_files_model, path, select_func, impl);
5119         }
5120     }
5121
5122   profile_end ("end", NULL);
5123   return TRUE;
5124 }
5125
5126 /* Processes the pending operation when a folder is finished loading */
5127 static void
5128 pending_select_paths_process (GtkFileChooserDefault *impl)
5129 {
5130   g_assert (impl->load_state == LOAD_FINISHED);
5131   g_assert (impl->browse_files_model != NULL);
5132   g_assert (impl->sort_model != NULL);
5133
5134   if (impl->pending_select_paths)
5135     {
5136       /* NULL GError */
5137       show_and_select_paths (impl, impl->current_folder, NULL, impl->pending_select_paths, NULL);
5138       pending_select_paths_free (impl);
5139       browse_files_center_selected_row (impl);
5140     }
5141   else
5142     {
5143       /* We only select the first row if the chooser is actually mapped ---
5144        * selecting the first row is to help the user when he is interacting with
5145        * the chooser, but sometimes a chooser works not on behalf of the user,
5146        * but rather on behalf of something else like GtkFileChooserButton.  In
5147        * that case, the chooser's selection should be what the caller expects,
5148        * as the user can't see that something else got selected.  See bug #165264.
5149        *
5150        * Also, we don't select the first file if we are not in OPEN mode.  Doing
5151        * so would change the contents of the filename entry for SAVE or
5152        * CREATE_FOLDER, which is undesired; in SELECT_FOLDER, we don't want to
5153        * select a *different* folder from the one into which the user just
5154        * navigated.
5155        */
5156       if (GTK_WIDGET_MAPPED (impl) && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
5157         browse_files_select_first_row (impl);
5158     }
5159
5160   g_assert (impl->pending_select_paths == NULL);
5161 }
5162
5163 /* Callback used when the file system model finishes loading */
5164 static void
5165 browse_files_model_finished_loading_cb (GtkFileSystemModel    *model,
5166                                         GtkFileChooserDefault *impl)
5167 {
5168   profile_start ("start", NULL);
5169
5170   if (impl->load_state == LOAD_PRELOAD)
5171     {
5172       load_remove_timer (impl);
5173       load_set_model (impl);
5174     }
5175   else if (impl->load_state == LOAD_LOADING)
5176     {
5177       /* Nothing */
5178     }
5179   else
5180     {
5181       /* We can't g_assert_not_reached(), as something other than us may have
5182        *  initiated a folder reload.  See #165556.
5183        */
5184       profile_end ("end", NULL);
5185       return;
5186     }
5187
5188   g_assert (impl->load_timeout_id == 0);
5189
5190   impl->load_state = LOAD_FINISHED;
5191
5192   pending_select_paths_process (impl);
5193   set_busy_cursor (impl, FALSE);
5194
5195   profile_end ("end", NULL);
5196 }
5197
5198 /* Gets rid of the old list model and creates a new one for the current folder */
5199 static gboolean
5200 set_list_model (GtkFileChooserDefault *impl,
5201                 GError               **error)
5202 {
5203   g_assert (impl->current_folder != NULL);
5204
5205   profile_start ("start", NULL);
5206
5207   load_remove_timer (impl); /* This changes the state to LOAD_EMPTY */
5208
5209   if (impl->browse_files_model)
5210     {
5211       g_object_unref (impl->browse_files_model);
5212       impl->browse_files_model = NULL;
5213     }
5214
5215   if (impl->sort_model)
5216     {
5217       g_object_unref (impl->sort_model);
5218       impl->sort_model = NULL;
5219     }
5220
5221   set_busy_cursor (impl, TRUE);
5222   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
5223
5224   impl->browse_files_model = _gtk_file_system_model_new (impl->file_system,
5225                                                          impl->current_folder, 0,
5226                                                          GTK_FILE_INFO_ALL,
5227                                                          error);
5228   if (!impl->browse_files_model)
5229     {
5230       set_busy_cursor (impl, FALSE);
5231       profile_end ("end", NULL);
5232       return FALSE;
5233     }
5234
5235   load_setup_timer (impl); /* This changes the state to LOAD_PRELOAD */
5236
5237   g_signal_connect (impl->browse_files_model, "finished-loading",
5238                     G_CALLBACK (browse_files_model_finished_loading_cb), impl);
5239
5240   _gtk_file_system_model_set_show_hidden (impl->browse_files_model, impl->show_hidden);
5241
5242   install_list_model_filter (impl);
5243
5244   profile_end ("end", NULL);
5245
5246   return TRUE;
5247 }
5248
5249 static void
5250 update_chooser_entry (GtkFileChooserDefault *impl)
5251 {
5252   GtkTreeSelection *selection;
5253   const GtkFileInfo *info;
5254   GtkTreeIter iter;
5255   GtkTreeIter child_iter;
5256   gboolean change_entry;
5257
5258   if (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
5259     return;
5260
5261   g_assert (!impl->select_multiple);
5262   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5263
5264   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
5265     {
5266       _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry), "");
5267       return;
5268     }
5269
5270   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
5271                                                   &child_iter,
5272                                                   &iter);
5273
5274   info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
5275
5276   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
5277     change_entry = !gtk_file_info_get_is_folder (info); /* We don't want the name to change when clicking on a folder... */
5278   else
5279     change_entry = TRUE;                                /* ... unless we are in CREATE_FOLDER mode */
5280
5281   if (change_entry)
5282     _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry),
5283                                            gtk_file_info_get_display_name (info));
5284 }
5285
5286 static gboolean
5287 gtk_file_chooser_default_set_current_folder (GtkFileChooser    *chooser,
5288                                              const GtkFilePath *path,
5289                                              GError           **error)
5290 {
5291   return gtk_file_chooser_default_update_current_folder (chooser, path, FALSE, error);
5292 }
5293
5294 static gboolean
5295 gtk_file_chooser_default_update_current_folder (GtkFileChooser    *chooser,
5296                                                 const GtkFilePath *path,
5297                                                 gboolean           keep_trail,
5298                                                 GError           **error)
5299 {
5300   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5301   gboolean result;
5302
5303   profile_start ("start", (char *) path);
5304
5305   g_assert (path != NULL);
5306
5307   if (impl->local_only &&
5308       !gtk_file_system_path_is_local (impl->file_system, path))
5309     {
5310       g_set_error (error,
5311                    GTK_FILE_CHOOSER_ERROR,
5312                    GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
5313                    _("Cannot change to folder because it is not local"));
5314
5315       profile_end ("end - not local", (char *) path);
5316       return FALSE;
5317     }
5318
5319   /* Test validity of path here.  */
5320   if (!check_is_folder (impl->file_system, path, error))
5321     {
5322       profile_end ("end - not a folder", (char *) path);
5323       return FALSE;
5324     }
5325
5326   if (!_gtk_path_bar_set_path (GTK_PATH_BAR (impl->browse_path_bar), path, keep_trail, error))
5327     {
5328       profile_end ("end - could not set path bar", (char *) path);
5329       return FALSE;
5330     }
5331
5332   if (impl->current_folder != path)
5333     {
5334       if (impl->current_folder)
5335         gtk_file_path_free (impl->current_folder);
5336
5337       impl->current_folder = gtk_file_path_copy (path);
5338     }
5339
5340   /* Update the widgets that may trigger a folder change themselves.  */
5341
5342   if (!impl->changing_folder)
5343     {
5344       impl->changing_folder = TRUE;
5345
5346       shortcuts_update_current_folder (impl);
5347
5348       impl->changing_folder = FALSE;
5349     }
5350
5351   /* Set the folder on the save entry */
5352
5353   _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry),
5354                                            impl->current_folder);
5355
5356   /* Create a new list model.  This is slightly evil; we store the result value
5357    * but perform more actions rather than returning immediately even if it
5358    * generates an error.
5359    */
5360   result = set_list_model (impl, error);
5361
5362   /* Refresh controls */
5363
5364   shortcuts_find_current_folder (impl);
5365
5366   g_signal_emit_by_name (impl, "current-folder-changed", 0);
5367
5368   check_preview_change (impl);
5369   bookmarks_check_add_sensitivity (impl);
5370
5371   g_signal_emit_by_name (impl, "selection-changed", 0);
5372
5373   profile_end ("end", NULL);
5374   return result;
5375 }
5376
5377 static GtkFilePath *
5378 gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser)
5379 {
5380   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5381
5382   return gtk_file_path_copy (impl->current_folder);
5383 }
5384
5385 static void
5386 gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
5387                                            const gchar    *name)
5388 {
5389   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5390
5391   g_return_if_fail (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5392                     || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
5393
5394   pending_select_paths_free (impl);
5395   _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry), name);
5396 }
5397
5398 static void
5399 select_func (GtkFileSystemModel *model,
5400              GtkTreePath        *path,
5401              GtkTreeIter        *iter,
5402              gpointer            user_data)
5403 {
5404   GtkFileChooserDefault *impl = user_data;
5405   GtkTreeSelection *selection;
5406   GtkTreeIter sorted_iter;
5407
5408   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5409
5410   gtk_tree_model_sort_convert_child_iter_to_iter (impl->sort_model, &sorted_iter, iter);
5411   gtk_tree_selection_select_iter (selection, &sorted_iter);
5412 }
5413
5414 static gboolean
5415 gtk_file_chooser_default_select_path (GtkFileChooser    *chooser,
5416                                       const GtkFilePath *path,
5417                                       GError           **error)
5418 {
5419   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5420   GtkFilePath *parent_path;
5421   gboolean same_path;
5422
5423   if (!gtk_file_system_get_parent (impl->file_system, path, &parent_path, error))
5424     return FALSE;
5425
5426   if (!parent_path)
5427     return _gtk_file_chooser_set_current_folder_path (chooser, path, error);
5428
5429   if (impl->load_state == LOAD_EMPTY)
5430     same_path = FALSE;
5431   else
5432     {
5433       g_assert (impl->current_folder != NULL);
5434
5435       same_path = gtk_file_path_compare (parent_path, impl->current_folder) == 0;
5436     }
5437
5438   if (same_path && impl->load_state == LOAD_FINISHED)
5439     {
5440       gboolean result;
5441
5442       result = show_and_select_paths (impl, parent_path, path, NULL, error);
5443       gtk_file_path_free (parent_path);
5444       return result;
5445     }
5446
5447   pending_select_paths_add (impl, path);
5448
5449   if (!same_path)
5450     {
5451       gboolean result;
5452
5453       result = _gtk_file_chooser_set_current_folder_path (chooser, parent_path, error);
5454       gtk_file_path_free (parent_path);
5455       return result;
5456     }
5457
5458   gtk_file_path_free (parent_path);
5459   return TRUE;
5460 }
5461
5462 static void
5463 unselect_func (GtkFileSystemModel *model,
5464                GtkTreePath        *path,
5465                GtkTreeIter        *iter,
5466                gpointer            user_data)
5467 {
5468   GtkFileChooserDefault *impl = user_data;
5469   GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
5470   GtkTreePath *sorted_path;
5471
5472   sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model,
5473                                                                 path);
5474   gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (tree_view),
5475                                     sorted_path);
5476   gtk_tree_path_free (sorted_path);
5477 }
5478
5479 static void
5480 gtk_file_chooser_default_unselect_path (GtkFileChooser    *chooser,
5481                                         const GtkFilePath *path)
5482 {
5483   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5484
5485   if (!impl->browse_files_model)
5486     return;
5487
5488   _gtk_file_system_model_path_do (impl->browse_files_model, path,
5489                                   unselect_func, impl);
5490 }
5491
5492 static gboolean
5493 maybe_select (GtkTreeModel *model, 
5494               GtkTreePath  *path, 
5495               GtkTreeIter  *iter, 
5496               gpointer     data)
5497 {
5498   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (data);
5499   GtkTreeSelection *selection;
5500   const GtkFileInfo *info;
5501   gboolean is_folder;
5502   
5503   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5504   
5505   info = get_list_file_info (impl, iter);
5506   is_folder = gtk_file_info_get_is_folder (info);
5507
5508   if ((is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) ||
5509       (!is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN))
5510     gtk_tree_selection_select_iter (selection, iter);
5511   else
5512     gtk_tree_selection_unselect_iter (selection, iter);
5513     
5514   return FALSE;
5515 }
5516
5517 static void
5518 gtk_file_chooser_default_select_all (GtkFileChooser *chooser)
5519 {
5520   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5521   if (impl->select_multiple)
5522     gtk_tree_model_foreach (GTK_TREE_MODEL (impl->sort_model), 
5523                             maybe_select, impl);
5524 }
5525
5526 static void
5527 gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser)
5528 {
5529   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5530   GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5531
5532   gtk_tree_selection_unselect_all (selection);
5533   pending_select_paths_free (impl);
5534 }
5535
5536 /* Checks whether the filename entry for the Save modes contains a well-formed filename.
5537  *
5538  * is_well_formed_ret - whether what the user typed passes gkt_file_system_make_path()
5539  *
5540  * is_empty_ret - whether the file entry is totally empty
5541  *
5542  * is_file_part_empty_ret - whether the file part is empty (will be if user types "foobar/", and
5543  *                          the path will be "$cwd/foobar")
5544  */
5545 static void
5546 check_save_entry (GtkFileChooserDefault *impl,
5547                   GtkFilePath          **path_ret,
5548                   gboolean              *is_well_formed_ret,
5549                   gboolean              *is_empty_ret,
5550                   gboolean              *is_file_part_empty_ret)
5551 {
5552   GtkFileChooserEntry *chooser_entry;
5553   const GtkFilePath *current_folder;
5554   const char *file_part;
5555   GtkFilePath *path;
5556   GError *error;
5557
5558   g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5559             || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
5560
5561   chooser_entry = GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry);
5562
5563   if (strlen (gtk_entry_get_text (GTK_ENTRY (chooser_entry))) == 0)
5564     {
5565       *path_ret = NULL;
5566       *is_well_formed_ret = TRUE;
5567       *is_empty_ret = TRUE;
5568       *is_file_part_empty_ret = TRUE;
5569
5570       return;
5571     }
5572
5573   *is_empty_ret = FALSE;
5574
5575   current_folder = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
5576   file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
5577
5578   if (!file_part || file_part[0] == '\0')
5579     {
5580       *path_ret = gtk_file_path_copy (current_folder);
5581       *is_well_formed_ret = TRUE;
5582       *is_file_part_empty_ret = TRUE;
5583
5584       return;
5585     }
5586
5587   *is_file_part_empty_ret = FALSE;
5588
5589   error = NULL;
5590   path = gtk_file_system_make_path (impl->file_system, current_folder, file_part, &error);
5591
5592   if (!path)
5593     {
5594       error_building_filename_dialog (impl, current_folder, file_part, error);
5595       *path_ret = NULL;
5596       *is_well_formed_ret = FALSE;
5597
5598       return;
5599     }
5600
5601   *path_ret = path;
5602   *is_well_formed_ret = TRUE;
5603 }
5604
5605 struct get_paths_closure {
5606   GtkFileChooserDefault *impl;
5607   GSList *result;
5608   GtkFilePath *path_from_entry;
5609 };
5610
5611 static void
5612 get_paths_foreach (GtkTreeModel *model,
5613                    GtkTreePath  *path,
5614                    GtkTreeIter  *iter,
5615                    gpointer      data)
5616 {
5617   struct get_paths_closure *info;
5618   const GtkFilePath *file_path;
5619   GtkFileSystemModel *fs_model;
5620   GtkTreeIter sel_iter;
5621
5622   info = data;
5623   fs_model = info->impl->browse_files_model;
5624   gtk_tree_model_sort_convert_iter_to_child_iter (info->impl->sort_model, &sel_iter, iter);
5625
5626   file_path = _gtk_file_system_model_get_path (fs_model, &sel_iter);
5627   if (!file_path)
5628     return; /* We are on the editable row */
5629
5630   if (!info->path_from_entry
5631       || gtk_file_path_compare (info->path_from_entry, file_path) != 0)
5632     info->result = g_slist_prepend (info->result, gtk_file_path_copy (file_path));
5633 }
5634
5635 static GSList *
5636 gtk_file_chooser_default_get_paths (GtkFileChooser *chooser)
5637 {
5638   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5639   struct get_paths_closure info;
5640
5641   info.impl = impl;
5642   info.result = NULL;
5643   info.path_from_entry = NULL;
5644
5645   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5646       || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5647     {
5648       gboolean is_well_formed, is_empty, is_file_part_empty;
5649
5650       check_save_entry (impl, &info.path_from_entry, &is_well_formed, &is_empty, &is_file_part_empty);
5651
5652       if (!is_well_formed)
5653         return NULL;
5654
5655       if (!is_empty)
5656         {
5657           if (is_file_part_empty && impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
5658             {
5659               gtk_file_path_free (info.path_from_entry);
5660               return NULL;
5661             }
5662         }
5663     }
5664
5665   if (!info.path_from_entry || impl->select_multiple)
5666     {
5667       GtkTreeSelection *selection;
5668
5669       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5670       gtk_tree_selection_selected_foreach (selection, get_paths_foreach, &info);
5671     }
5672
5673   if (info.path_from_entry)
5674     info.result = g_slist_prepend (info.result, info.path_from_entry);
5675
5676   /* If there's no folder selected, and we're in SELECT_FOLDER mode, then we
5677    * fall back to the current directory */
5678   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
5679       info.result == NULL)
5680     {
5681       info.result = g_slist_prepend (info.result, gtk_file_path_copy (impl->current_folder));
5682     }
5683
5684   return g_slist_reverse (info.result);
5685 }
5686
5687 static GtkFilePath *
5688 gtk_file_chooser_default_get_preview_path (GtkFileChooser *chooser)
5689 {
5690   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5691
5692   if (impl->preview_path)
5693     return gtk_file_path_copy (impl->preview_path);
5694   else
5695     return NULL;
5696 }
5697
5698 static GtkFileSystem *
5699 gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser)
5700 {
5701   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5702
5703   return impl->file_system;
5704 }
5705
5706 /* Shows or hides the filter widgets */
5707 static void
5708 show_filters (GtkFileChooserDefault *impl,
5709               gboolean               show)
5710 {
5711   if (show)
5712     gtk_widget_show (impl->filter_combo_hbox);
5713   else
5714     gtk_widget_hide (impl->filter_combo_hbox);
5715 }
5716
5717 static void
5718 gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
5719                                      GtkFileFilter  *filter)
5720 {
5721   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5722   const gchar *name;
5723
5724   if (g_slist_find (impl->filters, filter))
5725     {
5726       g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
5727       return;
5728     }
5729
5730   g_object_ref (filter);
5731   gtk_object_sink (GTK_OBJECT (filter));
5732   impl->filters = g_slist_append (impl->filters, filter);
5733
5734   name = gtk_file_filter_get_name (filter);
5735   if (!name)
5736     name = "Untitled filter";   /* Place-holder, doesn't need to be marked for translation */
5737
5738   gtk_combo_box_append_text (GTK_COMBO_BOX (impl->filter_combo), name);
5739
5740   if (!g_slist_find (impl->filters, impl->current_filter))
5741     set_current_filter (impl, filter);
5742
5743   show_filters (impl, TRUE);
5744 }
5745
5746 static void
5747 gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
5748                                         GtkFileFilter  *filter)
5749 {
5750   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5751   GtkTreeModel *model;
5752   GtkTreeIter iter;
5753   gint filter_index;
5754
5755   filter_index = g_slist_index (impl->filters, filter);
5756
5757   if (filter_index < 0)
5758     {
5759       g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
5760       return;
5761     }
5762
5763   impl->filters = g_slist_remove (impl->filters, filter);
5764
5765   if (filter == impl->current_filter)
5766     {
5767       if (impl->filters)
5768         set_current_filter (impl, impl->filters->data);
5769       else
5770         set_current_filter (impl, NULL);
5771     }
5772
5773   /* Remove row from the combo box */
5774   model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->filter_combo));
5775   gtk_tree_model_iter_nth_child  (model, &iter, NULL, filter_index);
5776   gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
5777
5778   g_object_unref (filter);
5779
5780   if (!impl->filters)
5781     show_filters (impl, FALSE);
5782 }
5783
5784 static GSList *
5785 gtk_file_chooser_default_list_filters (GtkFileChooser *chooser)
5786 {
5787   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5788
5789   return g_slist_copy (impl->filters);
5790 }
5791
5792 /* Returns the position in the shortcuts tree where the nth specified shortcut would appear */
5793 static int
5794 shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl,
5795                                        int                    pos)
5796 {
5797   return pos + shortcuts_get_index (impl, SHORTCUTS_SHORTCUTS);
5798 }
5799
5800 static gboolean
5801 gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser    *chooser,
5802                                               const GtkFilePath *path,
5803                                               GError           **error)
5804 {
5805   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5806   gboolean result;
5807   int pos;
5808
5809   /* Test validity of path here.  */
5810   if (!check_is_folder (impl->file_system, path, error))
5811     return FALSE;
5812
5813   pos = shortcuts_get_pos_for_shortcut_folder (impl, impl->num_shortcuts);
5814
5815   result = shortcuts_insert_path (impl, pos, FALSE, NULL, path, NULL, FALSE, error);
5816
5817   if (result)
5818     impl->num_shortcuts++;
5819
5820   if (impl->shortcuts_filter_model)
5821     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
5822
5823   return result;
5824 }
5825
5826 static gboolean
5827 gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
5828                                                  const GtkFilePath *path,
5829                                                  GError           **error)
5830 {
5831   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5832   int pos;
5833   GtkTreeIter iter;
5834   char *uri;
5835   int i;
5836
5837   if (impl->num_shortcuts == 0)
5838     goto out;
5839
5840   pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
5841   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
5842     g_assert_not_reached ();
5843
5844   for (i = 0; i < impl->num_shortcuts; i++)
5845     {
5846       gpointer col_data;
5847       gboolean is_volume;
5848       GtkFilePath *shortcut;
5849
5850       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
5851                           SHORTCUTS_COL_DATA, &col_data,
5852                           SHORTCUTS_COL_IS_VOLUME, &is_volume,
5853                           -1);
5854       g_assert (col_data != NULL);
5855       g_assert (!is_volume);
5856
5857       shortcut = col_data;
5858       if (gtk_file_path_compare (shortcut, path) == 0)
5859         {
5860           shortcuts_remove_rows (impl, pos + i, 1);
5861           impl->num_shortcuts--;
5862           return TRUE;
5863         }
5864
5865       if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
5866         g_assert_not_reached ();
5867     }
5868
5869  out:
5870
5871   uri = gtk_file_system_path_to_uri (impl->file_system, path);
5872   g_set_error (error,
5873                GTK_FILE_CHOOSER_ERROR,
5874                GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
5875                _("Shortcut %s does not exist"),
5876                uri);
5877   g_free (uri);
5878
5879   return FALSE;
5880 }
5881
5882 static GSList *
5883 gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser)
5884 {
5885   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
5886   int pos;
5887   GtkTreeIter iter;
5888   int i;
5889   GSList *list;
5890
5891   if (impl->num_shortcuts == 0)
5892     return NULL;
5893
5894   pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
5895   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
5896     g_assert_not_reached ();
5897
5898   list = NULL;
5899
5900   for (i = 0; i < impl->num_shortcuts; i++)
5901     {
5902       gpointer col_data;
5903       gboolean is_volume;
5904       GtkFilePath *shortcut;
5905
5906       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
5907                           SHORTCUTS_COL_DATA, &col_data,
5908                           SHORTCUTS_COL_IS_VOLUME, &is_volume,
5909                           -1);
5910       g_assert (col_data != NULL);
5911       g_assert (!is_volume);
5912
5913       shortcut = col_data;
5914       list = g_slist_prepend (list, gtk_file_path_copy (shortcut));
5915
5916       if (i != impl->num_shortcuts - 1)
5917         {
5918           if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
5919             g_assert_not_reached ();
5920         }
5921     }
5922
5923   return g_slist_reverse (list);
5924 }
5925
5926 /* Guesses a size based upon font sizes */
5927 static void
5928 find_good_size_from_style (GtkWidget *widget,
5929                            gint      *width,
5930                            gint      *height)
5931 {
5932   GtkFileChooserDefault *impl;
5933   gint default_width, default_height;
5934   int font_size;
5935   GtkRequisition req;
5936   GtkRequisition preview_req;
5937
5938   g_assert (widget->style != NULL);
5939   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5940
5941   font_size = pango_font_description_get_size (widget->style->font_desc);
5942   font_size = PANGO_PIXELS (font_size);
5943
5944   default_width = font_size * NUM_CHARS;
5945   default_height = font_size * NUM_LINES;
5946
5947   /* Use at least the requisition size not including the preview widget */
5948   gtk_widget_size_request (widget, &req);
5949
5950   if (impl->preview_widget_active && impl->preview_widget)
5951     gtk_widget_size_request (impl->preview_box, &preview_req);
5952   else
5953     preview_req.width = 0;
5954
5955   default_width = MAX (default_width, (req.width - (preview_req.width + PREVIEW_HBOX_SPACING)));
5956   default_height = MAX (default_height, req.height);
5957
5958   *width = default_width;
5959   *height = default_height;
5960 }
5961
5962 static void
5963 gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
5964                                            gint                *default_width,
5965                                            gint                *default_height)
5966 {
5967   GtkFileChooserDefault *impl;
5968
5969   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
5970
5971   find_good_size_from_style (GTK_WIDGET (chooser_embed), default_width, default_height);
5972
5973   if (impl->preview_widget_active && impl->preview_widget)
5974     *default_width += impl->preview_box->requisition.width + PREVIEW_HBOX_SPACING;
5975 }
5976
5977 static void
5978 gtk_file_chooser_default_get_resizable_hints (GtkFileChooserEmbed *chooser_embed,
5979                                               gboolean            *resize_horizontally,
5980                                               gboolean            *resize_vertically)
5981 {
5982   GtkFileChooserDefault *impl;
5983
5984   g_return_if_fail (resize_horizontally != NULL);
5985   g_return_if_fail (resize_vertically != NULL);
5986
5987   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
5988
5989   *resize_horizontally = TRUE;
5990   *resize_vertically = TRUE;
5991
5992   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
5993       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5994     {
5995       if (! gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
5996         {
5997           *resize_horizontally = FALSE;
5998           *resize_vertically = FALSE;
5999         }
6000     }
6001 }
6002
6003 struct switch_folder_closure {
6004   GtkFileChooserDefault *impl;
6005   const GtkFilePath *path;
6006   int num_selected;
6007 };
6008
6009 /* Used from gtk_tree_selection_selected_foreach() in switch_to_selected_folder() */
6010 static void
6011 switch_folder_foreach_cb (GtkTreeModel      *model,
6012                           GtkTreePath       *path,
6013                           GtkTreeIter       *iter,
6014                           gpointer           data)
6015 {
6016   struct switch_folder_closure *closure;
6017   GtkTreeIter child_iter;
6018
6019   closure = data;
6020
6021   gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
6022
6023   closure->path = _gtk_file_system_model_get_path (closure->impl->browse_files_model, &child_iter);
6024   closure->num_selected++;
6025 }
6026
6027 /* Changes to the selected folder in the list view */
6028 static void
6029 switch_to_selected_folder (GtkFileChooserDefault *impl)
6030 {
6031   GtkTreeSelection *selection;
6032   struct switch_folder_closure closure;
6033
6034   /* We do this with foreach() rather than get_selected() as we may be in
6035    * multiple selection mode
6036    */
6037
6038   closure.impl = impl;
6039   closure.path = NULL;
6040   closure.num_selected = 0;
6041
6042   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6043   gtk_tree_selection_selected_foreach (selection, switch_folder_foreach_cb, &closure);
6044
6045   g_assert (closure.path && closure.num_selected == 1);
6046
6047   change_folder_and_display_error (impl, closure.path);
6048 }
6049
6050 /* Gets the GtkFileInfo for the selected row in the file list; assumes single
6051  * selection mode.
6052  */
6053 static const GtkFileInfo *
6054 get_selected_file_info_from_file_list (GtkFileChooserDefault *impl,
6055                                        gboolean              *had_selection)
6056 {
6057   GtkTreeSelection *selection;
6058   GtkTreeIter iter, child_iter;
6059   const GtkFileInfo *info;
6060
6061   g_assert (!impl->select_multiple);
6062   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6063   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
6064     {
6065       *had_selection = FALSE;
6066       return NULL;
6067     }
6068
6069   *had_selection = TRUE;
6070
6071   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
6072                                                   &child_iter,
6073                                                   &iter);
6074
6075   info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
6076   return info;
6077 }
6078
6079 /* Gets the display name of the selected file in the file list; assumes single
6080  * selection mode and that something is selected.
6081  */
6082 static const gchar *
6083 get_display_name_from_file_list (GtkFileChooserDefault *impl)
6084 {
6085   const GtkFileInfo *info;
6086   gboolean had_selection;
6087
6088   info = get_selected_file_info_from_file_list (impl, &had_selection);
6089   g_assert (had_selection);
6090   g_assert (info != NULL);
6091
6092   return gtk_file_info_get_display_name (info);
6093 }
6094
6095 static void
6096 add_custom_button_to_dialog (GtkDialog   *dialog,
6097                              const gchar *mnemonic_label,
6098                              const gchar *stock_id,
6099                              gint         response_id)
6100 {
6101   GtkWidget *button;
6102
6103   button = gtk_button_new_with_mnemonic (mnemonic_label);
6104   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
6105   gtk_button_set_image (GTK_BUTTON (button),
6106                         gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON));
6107   gtk_widget_show (button);
6108
6109   gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, response_id);
6110 }
6111
6112 /* Presents an overwrite confirmation dialog; returns whether we should accept
6113  * the filename.
6114  */
6115 static gboolean
6116 confirm_dialog_should_accept_filename (GtkFileChooserDefault *impl,
6117                                        const gchar           *file_part,
6118                                        const gchar           *folder_display_name)
6119 {
6120   GtkWindow *toplevel;
6121   GtkWidget *dialog;
6122   int response;
6123
6124   toplevel = get_toplevel (GTK_WIDGET (impl));
6125
6126   dialog = gtk_message_dialog_new (toplevel,
6127                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
6128                                    GTK_MESSAGE_QUESTION,
6129                                    GTK_BUTTONS_NONE,
6130                                    _("A file named \"%s\" already exists.  Do you want to replace it?"),
6131                                    file_part);
6132   gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
6133                                             _("The file already exists in \"%s\".  Replacing it will "
6134                                               "overwrite its contents."),
6135                                             folder_display_name);
6136
6137   gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
6138   add_custom_button_to_dialog (GTK_DIALOG (dialog), _("_Replace"), GTK_STOCK_SAVE_AS, GTK_RESPONSE_ACCEPT);
6139   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
6140   
6141   response = gtk_dialog_run (GTK_DIALOG (dialog));
6142
6143   gtk_widget_destroy (dialog);
6144
6145   return (response == GTK_RESPONSE_ACCEPT);
6146 }
6147
6148 static char *
6149 get_display_name_for_folder (GtkFileChooserDefault *impl,
6150                              const GtkFilePath     *path)
6151 {
6152   char *display_name;
6153   GtkFilePath *parent_path;
6154   GtkFileFolder *parent_folder;
6155   GtkFileInfo *info;
6156
6157   display_name = NULL;
6158   parent_path = NULL;
6159   parent_folder = NULL;
6160   info = NULL;
6161
6162   if (!gtk_file_system_get_parent (impl->file_system, path, &parent_path, NULL))
6163     goto out;
6164
6165   parent_folder = gtk_file_system_get_folder (impl->file_system,
6166                                               parent_path ? parent_path : path,
6167                                               GTK_FILE_INFO_DISPLAY_NAME,
6168                                               NULL);
6169   if (!parent_folder)
6170     goto out;
6171
6172   info = gtk_file_folder_get_info (parent_folder, parent_path ? path : NULL, NULL);
6173   if (!info)
6174     goto out;
6175
6176   display_name = g_strdup (gtk_file_info_get_display_name (info));
6177
6178  out:
6179
6180   if (parent_path)
6181     gtk_file_path_free (parent_path);
6182
6183   if (parent_folder)
6184     g_object_unref (parent_folder);
6185
6186   if (info)
6187     gtk_file_info_free (info);
6188
6189   return display_name;
6190 }
6191
6192 /* Does overwrite confirmation if appropriate, and returns whether the dialog
6193  * should respond.  Can get the file part from the file list or the save entry.
6194  */
6195 static gboolean
6196 should_respond_after_confirm_overwrite (GtkFileChooserDefault *impl,
6197                                         const gchar           *file_part,
6198                                         const GtkFilePath     *parent_path)
6199 {
6200   GtkFileChooserConfirmation conf;
6201
6202   if (!impl->do_overwrite_confirmation)
6203     return TRUE;
6204
6205   conf = GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM;
6206
6207   g_signal_emit_by_name (impl, "confirm-overwrite", &conf);
6208
6209   switch (conf)
6210     {
6211     case GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM:
6212       {
6213         char *parent_display_name;
6214         gboolean retval;
6215
6216         g_assert (file_part != NULL);
6217
6218         parent_display_name = get_display_name_for_folder (impl, parent_path);
6219         if (!parent_display_name)
6220           return TRUE; /* Huh?  Did the folder disappear?  Let the caller deal with it */
6221
6222         retval = confirm_dialog_should_accept_filename (impl, file_part, parent_display_name);
6223         g_free (parent_display_name);
6224         return retval;
6225       }
6226
6227     case GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME:
6228       return TRUE;
6229
6230     case GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN:
6231       return FALSE;
6232
6233     default:
6234       g_assert_not_reached ();
6235       return FALSE;
6236     }
6237 }
6238
6239 /* Implementation for GtkFileChooserEmbed::should_respond() */
6240 static gboolean
6241 gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
6242 {
6243   GtkFileChooserDefault *impl;
6244   GtkWidget *toplevel;
6245   GtkWidget *current_focus;
6246
6247   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
6248
6249   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
6250   g_assert (GTK_IS_WINDOW (toplevel));
6251
6252   current_focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
6253
6254   if (current_focus == impl->browse_files_tree_view)
6255     {
6256       /* The following array encodes what we do based on the impl->action and the
6257        * number of files selected.
6258        */
6259       typedef enum {
6260         NOOP,                   /* Do nothing (don't respond) */
6261         RESPOND,                /* Respond immediately */
6262         RESPOND_OR_SWITCH,      /* Respond immediately if the selected item is a file; switch to it if it is a folder */
6263         ALL_FILES,              /* Respond only if everything selected is a file */
6264         ALL_FOLDERS,            /* Respond only if everything selected is a folder */
6265         SAVE_ENTRY,             /* Go to the code for handling the save entry */
6266         NOT_REACHED             /* Sanity check */
6267       } ActionToTake;
6268       static const ActionToTake what_to_do[4][3] = {
6269         /*                                0 selected            1 selected              many selected */
6270         /* ACTION_OPEN */               { NOOP,                 RESPOND_OR_SWITCH,      ALL_FILES   },
6271         /* ACTION_SAVE */               { SAVE_ENTRY,           RESPOND_OR_SWITCH,      NOT_REACHED },
6272         /* ACTION_SELECT_FOLDER */      { RESPOND,              ALL_FOLDERS,            ALL_FOLDERS },
6273         /* ACTION_CREATE_FOLDER */      { SAVE_ENTRY,           ALL_FOLDERS,            NOT_REACHED }
6274       };
6275
6276       int num_selected;
6277       gboolean all_files, all_folders;
6278       int k;
6279       ActionToTake action;
6280
6281     file_list:
6282
6283       g_assert (impl->action >= GTK_FILE_CHOOSER_ACTION_OPEN && impl->action <= GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
6284
6285       selection_check (impl, &num_selected, &all_files, &all_folders);
6286
6287       if (num_selected > 2)
6288         k = 2;
6289       else
6290         k = num_selected;
6291
6292       action = what_to_do [impl->action] [k];
6293
6294       switch (action)
6295         {
6296         case NOOP:
6297           return FALSE;
6298
6299         case RESPOND:
6300           return TRUE;
6301
6302         case RESPOND_OR_SWITCH:
6303           g_assert (num_selected == 1);
6304
6305           if (all_folders)
6306             {
6307               switch_to_selected_folder (impl);
6308               return FALSE;
6309             }
6310           else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
6311             return should_respond_after_confirm_overwrite (impl,
6312                                                            get_display_name_from_file_list (impl),
6313                                                            impl->current_folder);
6314           else
6315             return TRUE;
6316
6317         case ALL_FILES:
6318           return all_files;
6319
6320         case ALL_FOLDERS:
6321           return all_folders;
6322
6323         case SAVE_ENTRY:
6324           goto save_entry;
6325
6326         default:
6327           g_assert_not_reached ();
6328         }
6329     }
6330   else if (current_focus == impl->save_file_name_entry)
6331     {
6332       GtkFilePath *path;
6333       gboolean is_well_formed, is_empty, is_file_part_empty;
6334       gboolean is_folder;
6335       gboolean retval;
6336       GtkFileChooserEntry *entry;
6337       GError *error;
6338
6339     save_entry:
6340
6341       g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6342                 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
6343
6344       entry = GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry);
6345       check_save_entry (impl, &path, &is_well_formed, &is_empty, &is_file_part_empty);
6346
6347       if (is_empty || !is_well_formed)
6348         return FALSE;
6349
6350       g_assert (path != NULL);
6351
6352       error = NULL;
6353       is_folder = check_is_folder (impl->file_system, path, &error);
6354       if (is_folder)
6355         {
6356           if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
6357             {
6358               _gtk_file_chooser_entry_set_file_part (entry, "");
6359               change_folder_and_display_error (impl, path);
6360               retval = FALSE;
6361             }
6362           else /* GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER */
6363             {
6364               /* The folder already exists, so we do not need to create it.
6365                * Just respond to terminate the dialog.
6366                */
6367               retval = TRUE;
6368             }
6369         }
6370       else
6371         {
6372           gboolean file_exists_and_is_not_folder;
6373
6374           file_exists_and_is_not_folder = g_error_matches (error, GTK_FILE_SYSTEM_ERROR, GTK_FILE_SYSTEM_ERROR_NOT_FOLDER);
6375
6376           if (impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER && file_exists_and_is_not_folder)
6377             {
6378               /* Oops, the user typed the name of an existing path which is not a folder */
6379               error_creating_folder_over_existing_file_dialog (impl, path, error);
6380               error = NULL; /* as it will be freed below for the general case */
6381               retval = FALSE;
6382             }
6383           else
6384             {
6385               GtkFilePath *parent_path;
6386               gboolean parent_is_folder;
6387
6388               /* check that everything up to the last component exists */
6389
6390               parent_path = gtk_file_path_copy (_gtk_file_chooser_entry_get_current_folder (entry));
6391               parent_is_folder = check_is_folder (impl->file_system, parent_path, NULL);
6392               if (parent_is_folder)
6393                 {
6394                   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
6395                     {
6396                       if (file_exists_and_is_not_folder)
6397                         {
6398                           const char *file_part;
6399
6400                           file_part = _gtk_file_chooser_entry_get_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry));
6401                           retval = should_respond_after_confirm_overwrite (impl, file_part, parent_path);
6402                         }
6403                       else
6404                         retval = TRUE;
6405                     }
6406                   else /* GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER */
6407                     {
6408                       GError *create_error;
6409
6410                       create_error = NULL;
6411                       if (gtk_file_system_create_folder (impl->file_system, path, &create_error))
6412                         retval = TRUE;
6413                       else
6414                         {
6415                           error_creating_folder_dialog (impl, path, create_error);
6416                           retval = FALSE;
6417                         }
6418                     }
6419                 }
6420               else
6421                 {
6422                   /* This will display an error, which is what we want */
6423                   change_folder_and_display_error (impl, parent_path);
6424                   retval = FALSE;
6425                 }
6426
6427               gtk_file_path_free (parent_path);
6428             }
6429
6430           if (error != NULL)
6431             g_error_free (error);
6432         }
6433
6434       gtk_file_path_free (path);
6435       return retval;
6436     }
6437   else if (impl->toplevel_last_focus_widget == impl->browse_shortcuts_tree_view)
6438     {
6439       /* The focus is on a dialog's action area button, *and* the widget that
6440        * was focused immediately before it is the shortcuts list.  Switch to the
6441        * selected shortcut and tell the caller not to respond.
6442        */
6443       GtkTreeIter iter;
6444
6445       if (shortcuts_get_selected (impl, &iter))
6446         {
6447           shortcuts_activate_iter (impl, &iter);
6448           
6449           gtk_widget_grab_focus (impl->browse_files_tree_view);
6450         }
6451       else
6452         goto file_list;
6453
6454       return FALSE;
6455     }
6456   else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
6457     {
6458       /* The focus is on a dialog's action area button, *and* the widget that
6459        * was focused immediately before it is the file list.  
6460        */
6461       goto file_list;
6462     }
6463   else
6464     /* The focus is on a dialog's action area button or something else */
6465     if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6466         || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6467       goto save_entry;
6468     else
6469       goto file_list; 
6470   
6471   g_assert_not_reached ();
6472   return FALSE;
6473 }
6474
6475 /* Implementation for GtkFileChooserEmbed::initial_focus() */
6476 static void
6477 gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed)
6478 {
6479   GtkFileChooserDefault *impl;
6480   GtkWidget *widget;
6481
6482   impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
6483
6484   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
6485       || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
6486     widget = impl->browse_files_tree_view;
6487   else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6488            || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6489     widget = impl->save_file_name_entry;
6490   else
6491     {
6492       g_assert_not_reached ();
6493       widget = NULL;
6494     }
6495
6496   gtk_widget_grab_focus (widget);
6497 }
6498
6499 static void
6500 set_current_filter (GtkFileChooserDefault *impl,
6501                     GtkFileFilter         *filter)
6502 {
6503   if (impl->current_filter != filter)
6504     {
6505       int filter_index;
6506
6507       /* NULL filters are allowed to reset to non-filtered status
6508        */
6509       filter_index = g_slist_index (impl->filters, filter);
6510       if (impl->filters && filter && filter_index < 0)
6511         return;
6512
6513       if (impl->current_filter)
6514         g_object_unref (impl->current_filter);
6515       impl->current_filter = filter;
6516       if (impl->current_filter)
6517         {
6518           g_object_ref (impl->current_filter);
6519           gtk_object_sink (GTK_OBJECT (filter));
6520         }
6521
6522       if (impl->filters)
6523         gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
6524                                   filter_index);
6525
6526       if (impl->browse_files_model)
6527         install_list_model_filter (impl);
6528
6529       g_object_notify (G_OBJECT (impl), "filter");
6530     }
6531 }
6532
6533 static void
6534 filter_combo_changed (GtkComboBox           *combo_box,
6535                       GtkFileChooserDefault *impl)
6536 {
6537   gint new_index = gtk_combo_box_get_active (combo_box);
6538   GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
6539
6540   set_current_filter (impl, new_filter);
6541 }
6542
6543 static void
6544 check_preview_change (GtkFileChooserDefault *impl)
6545 {
6546   GtkTreePath *cursor_path;
6547   const GtkFilePath *new_path;
6548   const GtkFileInfo *new_info;
6549
6550   gtk_tree_view_get_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), &cursor_path, NULL);
6551   if (cursor_path && impl->sort_model)
6552     {
6553       GtkTreeIter iter;
6554       GtkTreeIter child_iter;
6555
6556       gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, cursor_path);
6557       gtk_tree_path_free (cursor_path);
6558
6559       gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
6560
6561       new_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
6562       new_info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
6563     }
6564   else
6565     {
6566       new_path = NULL;
6567       new_info = NULL;
6568     }
6569
6570   if (new_path != impl->preview_path &&
6571       !(new_path && impl->preview_path &&
6572         gtk_file_path_compare (new_path, impl->preview_path) == 0))
6573     {
6574       if (impl->preview_path)
6575         {
6576           gtk_file_path_free (impl->preview_path);
6577           g_free (impl->preview_display_name);
6578         }
6579
6580       if (new_path)
6581         {
6582           impl->preview_path = gtk_file_path_copy (new_path);
6583           impl->preview_display_name = g_strdup (gtk_file_info_get_display_name (new_info));
6584         }
6585       else
6586         {
6587           impl->preview_path = NULL;
6588           impl->preview_display_name = NULL;
6589         }
6590
6591       if (impl->use_preview_label && impl->preview_label)
6592         gtk_label_set_text (GTK_LABEL (impl->preview_label), impl->preview_display_name);
6593
6594       g_signal_emit_by_name (impl, "update-preview");
6595     }
6596 }
6597
6598 /* Activates a volume by mounting it if necessary and then switching to its
6599  * base path.
6600  */
6601 static void
6602 shortcuts_activate_volume (GtkFileChooserDefault *impl,
6603                            GtkFileSystemVolume   *volume)
6604 {
6605   GtkFilePath *path;
6606
6607   /* We ref the file chooser since volume_mount() may run a main loop, and the
6608    * user could close the file chooser window in the meantime.
6609    */
6610   g_object_ref (impl);
6611
6612   if (!gtk_file_system_volume_get_is_mounted (impl->file_system, volume))
6613     {
6614       GError *error;
6615       gboolean result;
6616
6617       set_busy_cursor (impl, TRUE);
6618
6619       error = NULL;
6620       result = gtk_file_system_volume_mount (impl->file_system, volume, &error);
6621
6622       if (!result)
6623         {
6624           char *msg;
6625
6626           msg = g_strdup_printf (_("Could not mount %s"),
6627                                  gtk_file_system_volume_get_display_name (impl->file_system, volume));
6628           error_message (impl, msg, error->message);
6629           g_free (msg);
6630           g_error_free (error);
6631         }
6632
6633       set_busy_cursor (impl, FALSE);
6634
6635       if (!result)
6636         goto out;
6637     }
6638
6639   path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
6640   change_folder_and_display_error (impl, path);
6641   gtk_file_path_free (path);
6642
6643  out:
6644
6645   g_object_unref (impl);
6646 }
6647
6648 /* Opens the folder or volume at the specified iter in the shortcuts model */
6649 static void
6650 shortcuts_activate_iter (GtkFileChooserDefault *impl,
6651                          GtkTreeIter           *iter)
6652 {
6653   gpointer col_data;
6654   gboolean is_volume;
6655
6656   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
6657                       SHORTCUTS_COL_DATA, &col_data,
6658                       SHORTCUTS_COL_IS_VOLUME, &is_volume,
6659                       -1);
6660
6661   if (!col_data)
6662     return; /* We are on a separator */
6663
6664   if (is_volume)
6665     {
6666       GtkFileSystemVolume *volume;
6667
6668       volume = col_data;
6669
6670       shortcuts_activate_volume (impl, volume);
6671     }
6672   else
6673     {
6674       const GtkFilePath *file_path;
6675
6676       file_path = col_data;
6677       change_folder_and_display_error (impl, file_path);
6678     }
6679 }
6680
6681 /* Callback used when a row in the shortcuts list is activated */
6682 static void
6683 shortcuts_row_activated_cb (GtkTreeView           *tree_view,
6684                             GtkTreePath           *path,
6685                             GtkTreeViewColumn     *column,
6686                             GtkFileChooserDefault *impl)
6687 {
6688   GtkTreeIter iter;
6689   GtkTreeIter child_iter;
6690
6691   if (!gtk_tree_model_get_iter (impl->shortcuts_filter_model, &iter, path))
6692     return;
6693
6694   gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
6695                                                     &child_iter,
6696                                                     &iter);
6697   shortcuts_activate_iter (impl, &child_iter);
6698
6699   gtk_widget_grab_focus (impl->browse_files_tree_view);
6700 }
6701
6702 /* Handler for GtkWidget::key-press-event on the shortcuts list */
6703 static gboolean
6704 shortcuts_key_press_event_cb (GtkWidget             *widget,
6705                               GdkEventKey           *event,
6706                               GtkFileChooserDefault *impl)
6707 {
6708   guint modifiers;
6709
6710   modifiers = gtk_accelerator_get_default_mod_mask ();
6711
6712   if ((event->keyval == GDK_BackSpace
6713       || event->keyval == GDK_Delete
6714       || event->keyval == GDK_KP_Delete)
6715       && (event->state & modifiers) == 0)
6716     {
6717       remove_selected_bookmarks (impl);
6718       return TRUE;
6719     }
6720
6721   return FALSE;
6722 }
6723
6724 static gboolean
6725 shortcuts_select_func  (GtkTreeSelection  *selection,
6726                         GtkTreeModel      *model,
6727                         GtkTreePath       *path,
6728                         gboolean           path_currently_selected,
6729                         gpointer           data)
6730 {
6731   GtkFileChooserDefault *impl = data;
6732
6733   return (*gtk_tree_path_get_indices (path) != shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR));
6734 }
6735
6736 static gboolean
6737 list_select_func  (GtkTreeSelection  *selection,
6738                    GtkTreeModel      *model,
6739                    GtkTreePath       *path,
6740                    gboolean           path_currently_selected,
6741                    gpointer           data)
6742 {
6743   GtkFileChooserDefault *impl = data;
6744
6745   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
6746       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6747     {
6748       GtkTreeIter iter, child_iter;
6749       const GtkFileInfo *info;
6750
6751       if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
6752         return FALSE;
6753       
6754       gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
6755
6756       info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
6757
6758       if (info && !gtk_file_info_get_is_folder (info))
6759         return FALSE;
6760     }
6761     
6762   return TRUE;
6763 }
6764
6765 static void
6766 list_selection_changed (GtkTreeSelection      *selection,
6767                         GtkFileChooserDefault *impl)
6768 {
6769   /* See if we are in the new folder editable row for Save mode */
6770   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
6771     {
6772       const GtkFileInfo *info;
6773       gboolean had_selection;
6774
6775       info = get_selected_file_info_from_file_list (impl, &had_selection);
6776       if (!had_selection)
6777         goto out; /* normal processing */
6778
6779       if (!info)
6780         return; /* We are on the editable row for New Folder */
6781     }
6782
6783  out:
6784
6785   update_chooser_entry (impl);
6786   check_preview_change (impl);
6787   bookmarks_check_add_sensitivity (impl);
6788
6789   g_signal_emit_by_name (impl, "selection-changed", 0);
6790 }
6791
6792 /* Callback used when a row in the file list is activated */
6793 static void
6794 list_row_activated (GtkTreeView           *tree_view,
6795                     GtkTreePath           *path,
6796                     GtkTreeViewColumn     *column,
6797                     GtkFileChooserDefault *impl)
6798 {
6799   GtkTreeIter iter, child_iter;
6800   const GtkFileInfo *info;
6801
6802   if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
6803     return;
6804
6805   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
6806
6807   info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
6808
6809   if (gtk_file_info_get_is_folder (info))
6810     {
6811       const GtkFilePath *file_path;
6812
6813       file_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
6814       change_folder_and_display_error (impl, file_path);
6815
6816       return;
6817     }
6818
6819   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
6820       impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
6821     g_signal_emit_by_name (impl, "file-activated");
6822 }
6823
6824 static void
6825 path_bar_clicked (GtkPathBar            *path_bar,
6826                   GtkFilePath           *file_path,
6827                   gboolean               child_is_hidden,
6828                   GtkFileChooserDefault *impl)
6829 {
6830   if (!change_folder_and_display_error (impl, file_path))
6831     return;
6832
6833   /* Say we have "/foo/bar/[.baz]" and the user clicks on "bar".  We should then
6834    * show hidden files so that ".baz" appears in the file list, as it will still
6835    * be shown in the path bar: "/foo/[bar]/.baz"
6836    */
6837   if (child_is_hidden)
6838     g_object_set (impl, "show-hidden", TRUE, NULL);
6839 }
6840
6841 static const GtkFileInfo *
6842 get_list_file_info (GtkFileChooserDefault *impl,
6843                     GtkTreeIter           *iter)
6844 {
6845   GtkTreeIter child_iter;
6846
6847   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
6848                                                   &child_iter,
6849                                                   iter);
6850
6851   return _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
6852 }
6853
6854 static void
6855 list_icon_data_func (GtkTreeViewColumn *tree_column,
6856                      GtkCellRenderer   *cell,
6857                      GtkTreeModel      *tree_model,
6858                      GtkTreeIter       *iter,
6859                      gpointer           data)
6860 {
6861   GtkFileChooserDefault *impl = data;
6862   GtkTreeIter child_iter;
6863   const GtkFilePath *path;
6864   GdkPixbuf *pixbuf;
6865   const GtkFileInfo *info; 
6866   gboolean sensitive = TRUE;
6867
6868   profile_start ("start", NULL);
6869   
6870   info = get_list_file_info (impl, iter);
6871
6872   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
6873                                                   &child_iter,
6874                                                   iter);
6875   path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
6876
6877   if (path)
6878     {
6879       /* FIXME: NULL GError */
6880       pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
6881                                             impl->icon_size, NULL);
6882     }
6883   else
6884     {
6885       /* We are on the editable row */
6886       pixbuf = NULL;
6887     }
6888
6889   if (info && (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
6890                impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
6891     sensitive =  gtk_file_info_get_is_folder (info);    
6892     
6893   g_object_set (cell,
6894                 "pixbuf", pixbuf,
6895                 "sensitive", sensitive,
6896                 NULL);
6897     
6898   if (pixbuf)
6899     g_object_unref (pixbuf);
6900
6901   profile_end ("end", NULL);
6902 }
6903
6904 static void
6905 list_name_data_func (GtkTreeViewColumn *tree_column,
6906                      GtkCellRenderer   *cell,
6907                      GtkTreeModel      *tree_model,
6908                      GtkTreeIter       *iter,
6909                      gpointer           data)
6910 {
6911   GtkFileChooserDefault *impl = data;
6912   const GtkFileInfo *info = get_list_file_info (impl, iter);
6913   gboolean sensitive = TRUE;
6914
6915   if (!info)
6916     {
6917       g_object_set (cell,
6918                     "text", _("Type name of new folder"),
6919                     NULL);
6920
6921       return;
6922     }
6923
6924
6925   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
6926          || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6927     {
6928       sensitive = gtk_file_info_get_is_folder (info);
6929     } 
6930     
6931   g_object_set (cell,
6932                 "text", gtk_file_info_get_display_name (info),
6933                 "sensitive", sensitive,
6934                 NULL);
6935 }
6936
6937 #if 0
6938 static void
6939 list_size_data_func (GtkTreeViewColumn *tree_column,
6940                      GtkCellRenderer   *cell,
6941                      GtkTreeModel      *tree_model,
6942                      GtkTreeIter       *iter,
6943                      gpointer           data)
6944 {
6945   GtkFileChooserDefault *impl = data;
6946   const GtkFileInfo *info = get_list_file_info (impl, iter);
6947   gint64 size;
6948   gchar *str;
6949   gboolean sensitive = TRUE;
6950
6951   if (!info || gtk_file_info_get_is_folder (info)) 
6952     {
6953       g_object_set (cell,"sensitive", sensitive, NULL);
6954       return;
6955     }
6956
6957   size = gtk_file_info_get_size (info);
6958
6959   if (size < (gint64)1024)
6960     str = g_strdup_printf (ngettext ("%d byte", "%d bytes", (gint)size), (gint)size);
6961   else if (size < (gint64)1024*1024)
6962     str = g_strdup_printf (_("%.1f K"), size / (1024.));
6963   else if (size < (gint64)1024*1024*1024)
6964     str = g_strdup_printf (_("%.1f M"), size / (1024.*1024.));
6965   else
6966     str = g_strdup_printf (_("%.1f G"), size / (1024.*1024.*1024.));
6967
6968   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
6969       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6970     sensitive = FALSE;
6971
6972   g_object_set (cell,
6973                 "text", str,
6974                 "sensitive", sensitive,
6975                 NULL);
6976
6977   g_free (str);
6978 }
6979 #endif
6980
6981 /* Tree column data callback for the file list; fetches the mtime of a file */
6982 static void
6983 list_mtime_data_func (GtkTreeViewColumn *tree_column,
6984                       GtkCellRenderer   *cell,
6985                       GtkTreeModel      *tree_model,
6986                       GtkTreeIter       *iter,
6987                       gpointer           data)
6988 {
6989   GtkFileChooserDefault *impl;
6990   const GtkFileInfo *info;
6991   GtkFileTime time_mtime, time_now;
6992   GDate mtime, now;
6993   int days_diff;
6994   char buf[256];
6995   gboolean sensitive = TRUE;
6996
6997   impl = data;
6998
6999   info = get_list_file_info (impl, iter);
7000   if (!info)
7001     {
7002       g_object_set (cell,
7003                     "text", "",
7004                     "sensitive", TRUE,
7005                     NULL);
7006       return;
7007     }
7008
7009   time_mtime = gtk_file_info_get_modification_time (info);
7010
7011   if (time_mtime == 0)
7012     strcpy (buf, _("Unknown"));
7013   else
7014     {
7015       g_date_set_time (&mtime, (GTime) time_mtime);
7016
7017       time_now = (GTime ) time (NULL);
7018       g_date_set_time (&now, (GTime) time_now);
7019
7020       days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
7021
7022       if (days_diff == 0)
7023         strcpy (buf, _("Today"));
7024       else if (days_diff == 1)
7025         strcpy (buf, _("Yesterday"));
7026       else
7027         {
7028           char *format;
7029
7030           if (days_diff > 1 && days_diff < 7)
7031             format = "%A"; /* Days from last week */
7032           else
7033             format = "%x"; /* Any other date */
7034
7035           if (g_date_strftime (buf, sizeof (buf), format, &mtime) == 0)
7036             strcpy (buf, _("Unknown"));
7037         }
7038     }
7039
7040   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
7041       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
7042     sensitive = gtk_file_info_get_is_folder (info);
7043
7044   g_object_set (cell,
7045                 "text", buf,
7046                 "sensitive", sensitive,
7047                 NULL);
7048 }
7049
7050 GtkWidget *
7051 _gtk_file_chooser_default_new (const char *file_system)
7052 {
7053   return  g_object_new (GTK_TYPE_FILE_CHOOSER_DEFAULT,
7054                         "file-system-backend", file_system,
7055                         NULL);
7056 }
7057
7058 static GtkWidget *
7059 location_entry_create (GtkFileChooserDefault *impl,
7060                        const gchar           *path)
7061 {
7062   GtkWidget *entry;
7063
7064   entry = _gtk_file_chooser_entry_new (TRUE);
7065   /* Pick a good width for the entry */
7066   gtk_entry_set_width_chars (GTK_ENTRY (entry), 30);
7067   gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
7068   _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (entry), impl->file_system);
7069   _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (entry), impl->action);
7070   if (path[0])
7071     {
7072       _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (entry), 
7073                                                gtk_file_path_new_steal ((gchar *)path));
7074       _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (entry), path);
7075     }
7076   else
7077     {
7078       _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (entry), impl->current_folder);
7079       if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
7080           || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
7081         _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (entry), "");
7082       else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
7083                || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
7084         _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (entry),
7085                                                gtk_entry_get_text (GTK_ENTRY (impl->save_file_name_entry)));
7086       else
7087         g_assert_not_reached ();
7088     }
7089
7090   return GTK_WIDGET (entry);
7091 }
7092
7093 static gboolean
7094 update_from_entry (GtkFileChooserDefault *impl,
7095                    GtkWindow             *parent,
7096                    GtkFileChooserEntry   *chooser_entry)
7097 {
7098   const GtkFilePath *folder_path;
7099   const char *file_part;
7100
7101   folder_path = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
7102   file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
7103
7104   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN && !folder_path)
7105     {
7106       error_message_with_parent (parent,
7107                                  _("Cannot change folder"),
7108                                  _("The folder you specified is an invalid path."));
7109       return FALSE;
7110     }
7111
7112   if (file_part[0] == '\0')
7113     return change_folder_and_display_error (impl, folder_path);
7114   else
7115     {
7116       GtkFileFolder *folder = NULL;
7117       GtkFilePath *subfolder_path = NULL;
7118       GtkFileInfo *info = NULL;
7119       GError *error;
7120       gboolean result;
7121
7122       result = FALSE;
7123
7124       /* If the file part is non-empty, we need to figure out if it refers to a
7125        * folder within folder. We could optimize the case here where the folder
7126        * is already loaded for one of our tree models.
7127        */
7128
7129       error = NULL;
7130       folder = gtk_file_system_get_folder (impl->file_system, folder_path, GTK_FILE_INFO_IS_FOLDER, &error);
7131
7132       if (!folder)
7133         {
7134           error_getting_info_dialog (impl, folder_path, error);
7135           goto out;
7136         }
7137
7138       error = NULL;
7139       subfolder_path = gtk_file_system_make_path (impl->file_system, folder_path, file_part, &error);
7140
7141       if (!subfolder_path)
7142         {
7143           char *msg;
7144           char *uri;
7145
7146           uri = gtk_file_system_path_to_uri (impl->file_system, folder_path);
7147           msg = g_strdup_printf (_("Could not build file name from '%s' and '%s'"),
7148                                  uri, file_part);
7149           error_message (impl, msg, error->message);
7150           g_free (uri);
7151           g_free (msg);
7152           goto out;
7153         }
7154
7155       error = NULL;
7156       info = gtk_file_folder_get_info (folder, subfolder_path, &error);
7157
7158       if (!info)
7159         {
7160           if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
7161               || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
7162             {
7163               if (!change_folder_and_display_error (impl, folder_path))
7164                 goto out;
7165
7166               gtk_file_chooser_default_set_current_name (GTK_FILE_CHOOSER (impl), file_part);
7167             }
7168           else
7169             error_getting_info_dialog (impl, subfolder_path, error);
7170
7171           goto out;
7172         }
7173
7174       if (gtk_file_info_get_is_folder (info))
7175         result = change_folder_and_display_error (impl, subfolder_path);
7176       else
7177         {
7178           GError *error;
7179
7180           error = NULL;
7181           result = _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (impl), subfolder_path, &error);
7182           if (!result)
7183             error_dialog (impl, _("Could not select item"),
7184                           subfolder_path, error);
7185         }
7186
7187     out:
7188
7189       if (folder)
7190         g_object_unref (folder);
7191
7192       gtk_file_path_free (subfolder_path);
7193
7194       if (info)
7195         gtk_file_info_free (info);
7196
7197       return result;
7198     }
7199
7200   g_assert_not_reached ();
7201 }
7202
7203 static void
7204 location_popup_handler (GtkFileChooserDefault *impl,
7205                         const gchar           *path)
7206 {
7207   GtkWidget *dialog;
7208   GtkWindow *toplevel;
7209   GtkWidget *hbox;
7210   GtkWidget *label;
7211   GtkWidget *entry;
7212   gboolean refocus;
7213   const char *title;
7214   const char *accept_stock;
7215
7216   /* Create dialog */
7217
7218   toplevel = get_toplevel (GTK_WIDGET (impl));
7219
7220   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
7221       || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
7222     {
7223       title = _("Open Location");
7224       accept_stock = GTK_STOCK_OPEN;
7225     }
7226   else
7227     {
7228       g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
7229                 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
7230       title = _("Save in Location");
7231       accept_stock = GTK_STOCK_SAVE;
7232     }
7233
7234   dialog = gtk_dialog_new_with_buttons (title,
7235                                         toplevel,
7236                                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
7237                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
7238                                         accept_stock, GTK_RESPONSE_ACCEPT,
7239                                         NULL);
7240   gtk_window_set_default_size (GTK_WINDOW (dialog), 300, -1);
7241   gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
7242   gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2);
7243   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
7244
7245   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
7246                                            GTK_RESPONSE_ACCEPT,
7247                                            GTK_RESPONSE_CANCEL,
7248                                            -1);
7249
7250   hbox = gtk_hbox_new (FALSE, 12);
7251   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0);
7252   gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
7253
7254   label = gtk_label_new_with_mnemonic (_("_Location:"));
7255   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
7256
7257   entry = location_entry_create (impl, path);
7258
7259   gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
7260   gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
7261
7262   /* Run */
7263
7264   gtk_widget_show_all (dialog);
7265   /* If the dialog is brought up by typing the first characters
7266    * of a path, unselect the text in the entry, so that you can
7267    * just type on without erasing the initial part.
7268    */
7269   if (path[0])
7270     gtk_editable_select_region (GTK_EDITABLE (entry), -1, -1);
7271
7272   refocus = TRUE;
7273
7274   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
7275     {
7276       if (update_from_entry (impl, GTK_WINDOW (dialog), GTK_FILE_CHOOSER_ENTRY (entry)))
7277         {
7278           if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
7279               || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
7280             {
7281               gtk_widget_grab_focus (impl->browse_files_tree_view);
7282             }
7283           else
7284             {
7285               g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
7286                         || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
7287               gtk_widget_grab_focus (impl->save_file_name_entry);
7288             }
7289           refocus = FALSE;
7290         }
7291     }
7292
7293   if (refocus)
7294     {
7295       GtkWindow *toplevel;
7296
7297       toplevel = get_toplevel (GTK_WIDGET (impl));
7298       if (toplevel && toplevel->focus_widget)
7299         gtk_widget_grab_focus (toplevel->focus_widget);
7300     }
7301
7302   gtk_widget_destroy (dialog);
7303 }
7304
7305 /* Handler for the "up-folder" keybinding signal */
7306 static void
7307 up_folder_handler (GtkFileChooserDefault *impl)
7308 {
7309   pending_select_paths_add (impl, impl->current_folder);
7310   _gtk_path_bar_up (GTK_PATH_BAR (impl->browse_path_bar));
7311 }
7312
7313 /* Handler for the "down-folder" keybinding signal */
7314 static void
7315 down_folder_handler (GtkFileChooserDefault *impl)
7316 {
7317   _gtk_path_bar_down (GTK_PATH_BAR (impl->browse_path_bar));
7318 }
7319
7320 /* Handler for the "home-folder" keybinding signal */
7321 static void
7322 home_folder_handler (GtkFileChooserDefault *impl)
7323 {
7324   int pos;
7325   GtkTreeIter iter;
7326
7327   if (!impl->has_home)
7328     return; /* Should we put up an error dialog? */
7329
7330   pos = shortcuts_get_index (impl, SHORTCUTS_HOME);
7331   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
7332     g_assert_not_reached ();
7333
7334   shortcuts_activate_iter (impl, &iter);
7335 }
7336
7337 \f
7338
7339 /* Drag and drop interfaces */
7340
7341 static void
7342 _shortcuts_model_filter_class_init (ShortcutsModelFilterClass *class)
7343 {
7344 }
7345
7346 static void
7347 _shortcuts_model_filter_init (ShortcutsModelFilter *model)
7348 {
7349   model->impl = NULL;
7350 }
7351
7352 /* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */
7353 static gboolean
7354 shortcuts_model_filter_row_draggable (GtkTreeDragSource *drag_source,
7355                                       GtkTreePath       *path)
7356 {
7357   ShortcutsModelFilter *model;
7358   int pos;
7359   int bookmarks_pos;
7360
7361   model = SHORTCUTS_MODEL_FILTER (drag_source);
7362
7363   pos = *gtk_tree_path_get_indices (path);
7364   bookmarks_pos = shortcuts_get_index (model->impl, SHORTCUTS_BOOKMARKS);
7365
7366   return (pos >= bookmarks_pos && pos < bookmarks_pos + model->impl->num_bookmarks);
7367 }
7368
7369 /* GtkTreeDragSource::drag_data_get implementation for the shortcuts filter model */
7370 static gboolean
7371 shortcuts_model_filter_drag_data_get (GtkTreeDragSource *drag_source,
7372                                       GtkTreePath       *path,
7373                                       GtkSelectionData  *selection_data)
7374 {
7375   ShortcutsModelFilter *model;
7376
7377   model = SHORTCUTS_MODEL_FILTER (drag_source);
7378
7379   /* FIXME */
7380
7381   return FALSE;
7382 }
7383
7384 /* Fill the GtkTreeDragSourceIface vtable */
7385 static void
7386 shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface)
7387 {
7388   iface->row_draggable = shortcuts_model_filter_row_draggable;
7389   iface->drag_data_get = shortcuts_model_filter_drag_data_get;
7390 }
7391
7392 #if 0
7393 /* Fill the GtkTreeDragDestIface vtable */
7394 static void
7395 shortcuts_model_filter_drag_dest_iface_init (GtkTreeDragDestIface *iface)
7396 {
7397   iface->drag_data_received = shortcuts_model_filter_drag_data_received;
7398   iface->row_drop_possible = shortcuts_model_filter_row_drop_possible;
7399 }
7400 #endif
7401
7402 static GtkTreeModel *
7403 shortcuts_model_filter_new (GtkFileChooserDefault *impl,
7404                             GtkTreeModel          *child_model,
7405                             GtkTreePath           *root)
7406 {
7407   ShortcutsModelFilter *model;
7408
7409   model = g_object_new (SHORTCUTS_MODEL_FILTER_TYPE,
7410                         "child-model", child_model,
7411                         "virtual-root", root,
7412                         NULL);
7413
7414   model->impl = impl;
7415
7416   return GTK_TREE_MODEL (model);
7417 }
7418
7419 #define __GTK_FILE_CHOOSER_DEFAULT_C__
7420 #include "gtkaliasdef.c"