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