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