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