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