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