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