]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserdefault.c
Use a plain GtkEntry for the filename, not a GtkFileChooserEntry.
[~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 "gdk/gdkkeysyms.h"
22 #include "gtkalignment.h"
23 #include "gtkbindings.h"
24 #include "gtkbutton.h"
25 #include "gtkcellrendererpixbuf.h"
26 #include "gtkcellrendererseptext.h"
27 #include "gtkcellrenderertext.h"
28 #include "gtkcombobox.h"
29 #include "gtkentry.h"
30 #include "gtkexpander.h"
31 #include "gtkfilechooserdefault.h"
32 #include "gtkfilechooserentry.h"
33 #include "gtkfilechooserutils.h"
34 #include "gtkfilechooser.h"
35 #include "gtkfilesystemmodel.h"
36 #include "gtkframe.h"
37 #include "gtkhbox.h"
38 #include "gtkhpaned.h"
39 #include "gtkicontheme.h"
40 #include "gtkimage.h"
41 #include "gtkintl.h"
42 #include "gtklabel.h"
43 #include "gtkmarshalers.h"
44 #include "gtkmenuitem.h"
45 #include "gtkmessagedialog.h"
46 #include "gtkpathbar.h"
47 #include "gtkprivate.h"
48 #include "gtkscrolledwindow.h"
49 #include "gtksizegroup.h"
50 #include "gtkstock.h"
51 #include "gtktable.h"
52 #include "gtktreeview.h"
53 #include "gtktreemodelsort.h"
54 #include "gtktreeselection.h"
55 #include "gtktreestore.h"
56 #include "gtktypebuiltins.h"
57 #include "gtkvbox.h"
58
59 #if defined (G_OS_UNIX)
60 #include "gtkfilesystemunix.h"
61 #elif defined (G_OS_WIN32)
62 #include "gtkfilesystemwin32.h"
63 #endif
64
65 #include <string.h>
66 #include <time.h>
67
68 typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
69
70 #define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
71 #define GTK_IS_FILE_CHOOSER_DEFAULT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT))
72 #define GTK_FILE_CHOOSER_DEFAULT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
73
74 struct _GtkFileChooserDefaultClass
75 {
76   GtkVBoxClass parent_class;
77 };
78
79 struct _GtkFileChooserDefault
80 {
81   GtkVBox parent_instance;
82
83   GtkFileChooserAction action;
84
85   GtkFileSystem *file_system;
86
87   /* Things common to Open and Save mode */
88
89   GtkWidget *hpaned;
90
91   GtkFileSystemModel *tree_model;
92   GtkListStore *shortcuts_model;
93   GtkFileSystemModel *list_model;
94   GtkTreeModelSort *sort_model;
95
96   GtkFileFilter *current_filter;
97   GSList *filters;
98
99   gboolean has_home;
100   gboolean has_desktop;
101
102   int num_volumes;
103   int num_shortcuts;
104   int num_bookmarks;
105
106   guint volumes_changed_id;
107   guint bookmarks_changed_id;
108
109   GtkFilePath *current_volume_path;
110   GtkFilePath *current_folder;
111   GtkFilePath *preview_path;
112
113   GtkWidget *new_folder_button;
114
115   GtkWidget *preview_frame;
116
117   GtkWidget *filter_combo;
118   GtkWidget *tree_scrollwin;
119   GtkWidget *tree;
120   GtkWidget *shortcuts_scrollwin;
121   GtkWidget *shortcuts_tree;
122   GtkWidget *add_bookmark_button;
123   GtkWidget *remove_bookmark_button;
124   GtkWidget *list_scrollwin;
125   GtkWidget *list;
126   GtkWidget *preview_widget;
127   GtkWidget *extra_widget;
128   GtkWidget *path_bar;
129
130   GtkTreeViewColumn *list_name_column;
131   GtkCellRenderer *list_name_renderer;
132
133   /* Things for Save mode */
134
135   GtkWidget *save_widgets;
136
137   GtkWidget *entry;
138   GtkWidget *save_folder_label;
139   GtkWidget *save_folder_combo;
140
141   /* Flags */
142
143   guint folder_mode : 1;
144   guint local_only : 1;
145   guint preview_widget_active : 1;
146   guint select_multiple : 1;
147   guint show_hidden : 1;
148   guint list_sort_ascending : 1;
149   guint changing_folder : 1;
150 };
151
152 /* Signal IDs */
153 enum {
154   LOCATION_POPUP,
155   UP_FOLDER,
156   HOME_FOLDER,
157   LAST_SIGNAL
158 };
159
160 static guint signals[LAST_SIGNAL] = { 0 };
161
162 /* Column numbers for the shortcuts tree.  Keep these in sync with create_shortcuts_model() */
163 enum {
164   SHORTCUTS_COL_PIXBUF,
165   SHORTCUTS_COL_NAME,
166   SHORTCUTS_COL_PATH,
167   SHORTCUTS_COL_REMOVABLE,
168   SHORTCUTS_COL_PIXBUF_VISIBLE,
169   SHORTCUTS_COL_NUM_COLUMNS
170 };
171
172 /* Column numbers for the file list */
173 enum {
174   FILE_LIST_COL_NAME,
175   FILE_LIST_COL_SIZE,
176   FILE_LIST_COL_MTIME,
177   FILE_LIST_COL_NUM_COLUMNS
178 };
179
180 /* Identifiers for target types */
181 enum {
182   TEXT_URI_LIST
183 };
184
185 /* Target types for DnD in the shortcuts list */
186 static GtkTargetEntry shortcuts_targets[] = {
187   { "text/uri-list", 0, TEXT_URI_LIST }
188 };
189
190 static const int num_shortcuts_targets = sizeof (shortcuts_targets) / sizeof (shortcuts_targets[0]);
191
192 /* Interesting places in the shortcuts bar */
193 typedef enum {
194   SHORTCUTS_HOME,
195   SHORTCUTS_DESKTOP,
196   SHORTCUTS_VOLUMES,
197   SHORTCUTS_SHORTCUTS,
198   SHORTCUTS_SEPARATOR,
199   SHORTCUTS_BOOKMARKS
200 } ShortcutsIndex;
201
202 /* Standard icon size */
203 /* FIXME: maybe this should correspond to the font size in the tree views... */
204 #define ICON_SIZE 20
205
206 static void gtk_file_chooser_default_class_init   (GtkFileChooserDefaultClass *class);
207 static void gtk_file_chooser_default_iface_init   (GtkFileChooserIface        *iface);
208 static void gtk_file_chooser_default_init         (GtkFileChooserDefault      *impl);
209
210 static GObject* gtk_file_chooser_default_constructor  (GType                  type,
211                                                        guint                  n_construct_properties,
212                                                        GObjectConstructParam *construct_params);
213 static void     gtk_file_chooser_default_finalize     (GObject               *object);
214 static void     gtk_file_chooser_default_set_property (GObject               *object,
215                                                        guint                  prop_id,
216                                                        const GValue          *value,
217                                                        GParamSpec            *pspec);
218 static void     gtk_file_chooser_default_get_property (GObject               *object,
219                                                        guint                  prop_id,
220                                                        GValue                *value,
221                                                        GParamSpec            *pspec);
222 static void     gtk_file_chooser_default_show_all     (GtkWidget             *widget);
223
224 static void           gtk_file_chooser_default_set_current_folder          (GtkFileChooser    *chooser,
225                                                                             const GtkFilePath *path);
226 static GtkFilePath *  gtk_file_chooser_default_get_current_folder          (GtkFileChooser    *chooser);
227 static void           gtk_file_chooser_default_set_current_name            (GtkFileChooser    *chooser,
228                                                                             const gchar       *name);
229 static void           gtk_file_chooser_default_select_path                 (GtkFileChooser    *chooser,
230                                                                             const GtkFilePath *path);
231 static void           gtk_file_chooser_default_unselect_path               (GtkFileChooser    *chooser,
232                                                                             const GtkFilePath *path);
233 static void           gtk_file_chooser_default_select_all                  (GtkFileChooser    *chooser);
234 static void           gtk_file_chooser_default_unselect_all                (GtkFileChooser    *chooser);
235 static GSList *       gtk_file_chooser_default_get_paths                   (GtkFileChooser    *chooser);
236 static GtkFilePath *  gtk_file_chooser_default_get_preview_path            (GtkFileChooser    *chooser);
237 static GtkFileSystem *gtk_file_chooser_default_get_file_system             (GtkFileChooser    *chooser);
238 static void           gtk_file_chooser_default_add_filter                  (GtkFileChooser    *chooser,
239                                                                             GtkFileFilter     *filter);
240 static void           gtk_file_chooser_default_remove_filter               (GtkFileChooser    *chooser,
241                                                                             GtkFileFilter     *filter);
242 static GSList *       gtk_file_chooser_default_list_filters                (GtkFileChooser    *chooser);
243 static gboolean       gtk_file_chooser_default_add_shortcut_folder    (GtkFileChooser    *chooser,
244                                                                        const GtkFilePath *path,
245                                                                        GError           **error);
246 static gboolean       gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
247                                                                        const GtkFilePath *path,
248                                                                        GError           **error);
249 static GSList *       gtk_file_chooser_default_list_shortcut_folders  (GtkFileChooser    *chooser);
250
251 static void location_popup_handler (GtkFileChooserDefault *impl);
252 static void up_folder_handler      (GtkFileChooserDefault *impl);
253 static void home_folder_handler    (GtkFileChooserDefault *impl);
254
255 static void set_current_filter   (GtkFileChooserDefault *impl,
256                                   GtkFileFilter         *filter);
257 static void check_preview_change (GtkFileChooserDefault *impl);
258
259 static void filter_combo_changed       (GtkComboBox           *combo_box,
260                                         GtkFileChooserDefault *impl);
261 static void tree_selection_changed     (GtkTreeSelection      *tree_selection,
262                                         GtkFileChooserDefault *impl);
263
264 static void     shortcuts_row_activated_cb (GtkTreeView           *tree_view,
265                                             GtkTreePath           *path,
266                                             GtkTreeViewColumn     *column,
267                                             GtkFileChooserDefault *impl);
268 static gboolean shortcuts_select_func   (GtkTreeSelection      *selection,
269                                          GtkTreeModel          *model,
270                                          GtkTreePath           *path,
271                                          gboolean               path_currently_selected,
272                                          gpointer               data);
273
274 static void list_selection_changed     (GtkTreeSelection      *tree_selection,
275                                         GtkFileChooserDefault *impl);
276 static void list_row_activated         (GtkTreeView           *tree_view,
277                                         GtkTreePath           *path,
278                                         GtkTreeViewColumn     *column,
279                                         GtkFileChooserDefault *impl);
280
281 static void path_bar_clicked           (GtkPathBar            *path_bar,
282                                         GtkFilePath           *file_path,
283                                         GtkFileChooserDefault *impl);
284
285 static void add_bookmark_button_clicked_cb    (GtkButton             *button,
286                                                GtkFileChooserDefault *impl);
287 static void remove_bookmark_button_clicked_cb (GtkButton             *button,
288                                                GtkFileChooserDefault *impl);
289
290 static void tree_name_data_func (GtkTreeViewColumn *tree_column,
291                                  GtkCellRenderer   *cell,
292                                  GtkTreeModel      *tree_model,
293                                  GtkTreeIter       *iter,
294                                  gpointer           data);
295 static void list_icon_data_func (GtkTreeViewColumn *tree_column,
296                                  GtkCellRenderer   *cell,
297                                  GtkTreeModel      *tree_model,
298                                  GtkTreeIter       *iter,
299                                  gpointer           data);
300 static void list_name_data_func (GtkTreeViewColumn *tree_column,
301                                  GtkCellRenderer   *cell,
302                                  GtkTreeModel      *tree_model,
303                                  GtkTreeIter       *iter,
304                                  gpointer           data);
305 #if 0
306 static void list_size_data_func (GtkTreeViewColumn *tree_column,
307                                  GtkCellRenderer   *cell,
308                                  GtkTreeModel      *tree_model,
309                                  GtkTreeIter       *iter,
310                                  gpointer           data);
311 #endif
312 static void list_mtime_data_func (GtkTreeViewColumn *tree_column,
313                                   GtkCellRenderer   *cell,
314                                   GtkTreeModel      *tree_model,
315                                   GtkTreeIter       *iter,
316                                   gpointer           data);
317
318 static GObjectClass *parent_class;
319
320 GType
321 _gtk_file_chooser_default_get_type (void)
322 {
323   static GType file_chooser_default_type = 0;
324
325   if (!file_chooser_default_type)
326     {
327       static const GTypeInfo file_chooser_default_info =
328       {
329         sizeof (GtkFileChooserDefaultClass),
330         NULL,           /* base_init */
331         NULL,           /* base_finalize */
332         (GClassInitFunc) gtk_file_chooser_default_class_init,
333         NULL,           /* class_finalize */
334         NULL,           /* class_data */
335         sizeof (GtkFileChooserDefault),
336         0,              /* n_preallocs */
337         (GInstanceInitFunc) gtk_file_chooser_default_init,
338       };
339
340       static const GInterfaceInfo file_chooser_info =
341       {
342         (GInterfaceInitFunc) gtk_file_chooser_default_iface_init, /* interface_init */
343         NULL,                                                          /* interface_finalize */
344         NULL                                                           /* interface_data */
345       };
346
347       file_chooser_default_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserDefault",
348                                                          &file_chooser_default_info, 0);
349       g_type_add_interface_static (file_chooser_default_type,
350                                    GTK_TYPE_FILE_CHOOSER,
351                                    &file_chooser_info);
352     }
353
354   return file_chooser_default_type;
355 }
356
357 static void
358 gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
359 {
360   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
361   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
362   GtkBindingSet *binding_set;
363
364   parent_class = g_type_class_peek_parent (class);
365
366   gobject_class->finalize = gtk_file_chooser_default_finalize;
367   gobject_class->constructor = gtk_file_chooser_default_constructor;
368   gobject_class->set_property = gtk_file_chooser_default_set_property;
369   gobject_class->get_property = gtk_file_chooser_default_get_property;
370
371   widget_class->show_all = gtk_file_chooser_default_show_all;
372
373   signals[LOCATION_POPUP] =
374     _gtk_binding_signal_new ("location-popup",
375                              G_OBJECT_CLASS_TYPE (class),
376                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
377                              G_CALLBACK (location_popup_handler),
378                              NULL, NULL,
379                              _gtk_marshal_VOID__VOID,
380                              G_TYPE_NONE, 0);
381   signals[UP_FOLDER] =
382     _gtk_binding_signal_new ("up-folder",
383                              G_OBJECT_CLASS_TYPE (class),
384                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
385                              G_CALLBACK (up_folder_handler),
386                              NULL, NULL,
387                              _gtk_marshal_VOID__VOID,
388                              G_TYPE_NONE, 0);
389   signals[HOME_FOLDER] =
390     _gtk_binding_signal_new ("home-folder",
391                              G_OBJECT_CLASS_TYPE (class),
392                              G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
393                              G_CALLBACK (home_folder_handler),
394                              NULL, NULL,
395                              _gtk_marshal_VOID__VOID,
396                              G_TYPE_NONE, 0);
397
398   binding_set = gtk_binding_set_by_class (class);
399
400   gtk_binding_entry_add_signal (binding_set,
401                                 GDK_l, GDK_CONTROL_MASK,
402                                 "location-popup",
403                                 0);
404
405   gtk_binding_entry_add_signal (binding_set,
406                                 GDK_Up, GDK_MOD1_MASK,
407                                 "up-folder",
408                                 0);
409   gtk_binding_entry_add_signal (binding_set,
410                                 GDK_KP_Up, GDK_MOD1_MASK,
411                                 "up-folder",
412                                 0);
413
414   gtk_binding_entry_add_signal (binding_set,
415                                 GDK_Home, GDK_MOD1_MASK,
416                                 "home-folder",
417                                 0);
418   gtk_binding_entry_add_signal (binding_set,
419                                 GDK_KP_Home, GDK_MOD1_MASK,
420                                 "home-folder",
421                                 0);
422
423   _gtk_file_chooser_install_properties (gobject_class);
424 }
425
426 static void
427 gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
428 {
429   iface->select_path = gtk_file_chooser_default_select_path;
430   iface->unselect_path = gtk_file_chooser_default_unselect_path;
431   iface->select_all = gtk_file_chooser_default_select_all;
432   iface->unselect_all = gtk_file_chooser_default_unselect_all;
433   iface->get_paths = gtk_file_chooser_default_get_paths;
434   iface->get_preview_path = gtk_file_chooser_default_get_preview_path;
435   iface->get_file_system = gtk_file_chooser_default_get_file_system;
436   iface->set_current_folder = gtk_file_chooser_default_set_current_folder;
437   iface->get_current_folder = gtk_file_chooser_default_get_current_folder;
438   iface->set_current_name = gtk_file_chooser_default_set_current_name;
439   iface->add_filter = gtk_file_chooser_default_add_filter;
440   iface->remove_filter = gtk_file_chooser_default_remove_filter;
441   iface->list_filters = gtk_file_chooser_default_list_filters;
442   iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder;
443   iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder;
444   iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders;
445 }
446
447 static void
448 gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
449 {
450   impl->folder_mode = FALSE;
451   impl->local_only = TRUE;
452   impl->preview_widget_active = TRUE;
453   impl->select_multiple = FALSE;
454   impl->show_hidden = FALSE;
455
456   gtk_box_set_spacing (GTK_BOX (impl), 12);
457 }
458
459 static void
460 gtk_file_chooser_default_finalize (GObject *object)
461 {
462   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
463   GSList *l;
464
465   g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
466   impl->volumes_changed_id = 0;
467   g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
468   impl->bookmarks_changed_id = 0;
469   g_object_unref (impl->file_system);
470
471   for (l = impl->filters; l; l = l->next)
472     {
473       GtkFileFilter *filter;
474
475       filter = GTK_FILE_FILTER (l->data);
476       g_object_unref (filter);
477     }
478   g_slist_free (impl->filters);
479
480   if (impl->current_filter)
481     g_object_unref (impl->current_filter);
482
483   if (impl->current_volume_path)
484     gtk_file_path_free (impl->current_volume_path);
485
486   if (impl->current_folder)
487     gtk_file_path_free (impl->current_folder);
488
489   if (impl->preview_path)
490     gtk_file_path_free (impl->preview_path);
491
492   G_OBJECT_CLASS (parent_class)->finalize (object);
493 }
494
495 /* Shows an error dialog set as transient for the specified window */
496 static void
497 error_message_with_parent (GtkWindow  *parent,
498                            const char *msg)
499 {
500   GtkWidget *dialog;
501
502   dialog = gtk_message_dialog_new (parent,
503                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
504                                    GTK_MESSAGE_ERROR,
505                                    GTK_BUTTONS_CLOSE,
506                                    "%s",
507                                    msg);
508   gtk_dialog_run (GTK_DIALOG (dialog));
509   gtk_widget_destroy (dialog);
510 }
511
512 /* Shows an error dialog for the file chooser */
513 static void
514 error_message (GtkFileChooserDefault *impl,
515                const char            *msg)
516 {
517   GtkWidget *toplevel;
518
519   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
520   if (!GTK_WIDGET_TOPLEVEL (toplevel))
521     toplevel = NULL;
522
523   error_message_with_parent (toplevel ? GTK_WINDOW (toplevel) : NULL,
524                              msg);
525 }
526
527 /* Shows a simple error dialog relative to a path.  Frees the GError as well. */
528 static void
529 error_dialog (GtkFileChooserDefault *impl,
530               const char            *msg,
531               const GtkFilePath     *path,
532               GError                *error)
533 {
534   char *text;
535
536   text = g_strdup_printf (msg,
537                           gtk_file_path_get_string (path),
538                           error->message);
539   error_message (impl, text);
540   g_free (text);
541   g_error_free (error);
542 }
543
544 /* Displays an error message about not being able to get information for a file.
545  * Frees the GError as well.
546  */
547 static void
548 error_getting_info_dialog (GtkFileChooserDefault *impl,
549                            const GtkFilePath     *path,
550                            GError                *error)
551 {
552   error_dialog (impl,
553                 _("Could not retrieve information about %s:\n%s"),
554                 path, error);
555 }
556
557 /* Shows an error dialog about not being able to add a bookmark */
558 static void
559 error_could_not_add_bookmark_dialog (GtkFileChooserDefault *impl,
560                                      const GtkFilePath     *path,
561                                      GError                *error)
562 {
563   error_dialog (impl,
564                 _("Could not add a bookmark for %s:\n%s"),
565                 path, error);
566 }
567
568 /* Shows an error dialog about not being able to compose a filename */
569 static void
570 error_building_filename_dialog (GtkFileChooserDefault *impl,
571                                 const GtkFilePath     *base_path,
572                                 const char            *file_part,
573                                 GError                *error)
574 {
575   char *msg;
576
577   msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
578                          gtk_file_path_get_string (base_path),
579                          file_part,
580                          error->message);
581   error_message (impl, msg);
582   g_free (msg);
583   g_error_free (error);
584 }
585
586 static void
587 update_preview_widget_visibility (GtkFileChooserDefault *impl)
588 {
589   if (impl->preview_widget_active && impl->preview_widget)
590     gtk_widget_show (impl->preview_frame);
591   else
592     gtk_widget_hide (impl->preview_frame);
593 }
594
595 static void
596 set_preview_widget (GtkFileChooserDefault *impl,
597                     GtkWidget             *preview_widget)
598 {
599   if (preview_widget == impl->preview_widget)
600     return;
601
602   if (impl->preview_widget)
603     gtk_container_remove (GTK_CONTAINER (impl->preview_frame),
604                           impl->preview_widget);
605
606   impl->preview_widget = preview_widget;
607   if (impl->preview_widget)
608     {
609       gtk_widget_show_all (impl->preview_widget);
610       gtk_container_add (GTK_CONTAINER (impl->preview_frame),
611                          impl->preview_widget);
612     }
613
614   update_preview_widget_visibility (impl);
615 }
616
617 /* Clears the selection in the shortcuts tree */
618 static void
619 shortcuts_unselect_all (GtkFileChooserDefault *impl)
620 {
621   GtkTreeSelection *selection;
622
623   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->shortcuts_tree));
624   gtk_tree_selection_unselect_all (selection);
625 }
626
627 /* Convenience function to get the display name and icon info for a path */
628 static GtkFileInfo *
629 get_file_info (GtkFileSystem *file_system, const GtkFilePath *path, GError **error)
630 {
631   GtkFilePath *parent_path;
632   GtkFileFolder *parent_folder;
633   GtkFileInfo *info;
634
635   if (!gtk_file_system_get_parent (file_system, path, &parent_path, error))
636     return NULL;
637
638   parent_folder = gtk_file_system_get_folder (file_system, parent_path,
639                                               GTK_FILE_INFO_DISPLAY_NAME
640 #if 0
641                                               | GTK_FILE_INFO_ICON
642 #endif
643                                               | GTK_FILE_INFO_IS_FOLDER,
644                                               error);
645   gtk_file_path_free (parent_path);
646
647   if (!parent_folder)
648     return NULL;
649
650   info = gtk_file_folder_get_info (parent_folder, path, error);
651   g_object_unref (parent_folder);
652
653   return info;
654 }
655
656 /* Inserts a path in the shortcuts tree, making a copy of it; alternatively,
657  * inserts a volume.  A position of -1 indicates the end of the tree.
658  */
659 static gboolean
660 shortcuts_insert_path (GtkFileChooserDefault *impl,
661                        int                    pos,
662                        gboolean               is_volume,
663                        GtkFileSystemVolume   *volume,
664                        const GtkFilePath     *path,
665                        const char            *label,
666                        gboolean               removable,
667                        GError               **error)
668 {
669   char *label_copy;
670   GdkPixbuf *pixbuf;
671   gpointer data;
672   GtkTreeIter iter;
673
674   if (is_volume)
675     {
676       data = volume;
677       label_copy = gtk_file_system_volume_get_display_name (impl->file_system, volume);
678       pixbuf = gtk_file_system_volume_render_icon (impl->file_system,
679                                                    volume,
680                                                    GTK_WIDGET (impl),
681                                                    ICON_SIZE,
682                                                    NULL);
683     }
684   else
685     {
686       GtkFileInfo *info;
687
688       info = get_file_info (impl->file_system, path, error);
689       if (!info)
690         return FALSE;
691
692       data = gtk_file_path_copy (path);
693
694       if (label)
695         label_copy = g_strdup (label);
696       else
697         label_copy = g_strdup (gtk_file_info_get_display_name (info));
698
699       pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl), ICON_SIZE, NULL);
700
701       gtk_file_info_free (info);
702     }
703
704   if (pos == -1)
705     gtk_list_store_append (impl->shortcuts_model, &iter);
706   else
707     gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
708
709   gtk_list_store_set (impl->shortcuts_model, &iter,
710                       SHORTCUTS_COL_PIXBUF, pixbuf,
711                       SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
712                       SHORTCUTS_COL_NAME, label_copy,
713                       SHORTCUTS_COL_PATH, data,
714                       SHORTCUTS_COL_REMOVABLE, removable,
715                       -1);
716
717   g_free (label_copy);
718
719   if (pixbuf)
720     g_object_unref (pixbuf);
721
722   return TRUE;
723 }
724
725 /* Appends an item for the user's home directory to the shortcuts model */
726 static void
727 shortcuts_append_home (GtkFileChooserDefault *impl)
728 {
729   const char *home;
730   GtkFilePath *home_path;
731   GError *error;
732
733   home = g_get_home_dir ();
734   home_path = gtk_file_system_filename_to_path (impl->file_system, home);
735
736   error = NULL;
737   impl->has_home = shortcuts_insert_path (impl, -1, FALSE, NULL, home_path, _("Home"), FALSE, &error);
738   if (!impl->has_home)
739     error_getting_info_dialog (impl, home_path, error);
740
741   gtk_file_path_free (home_path);
742 }
743
744 /* Appends the ~/Desktop directory to the shortcuts model */
745 static void
746 shortcuts_append_desktop (GtkFileChooserDefault *impl)
747 {
748   char *name;
749   GtkFilePath *path;
750
751   name = g_build_filename (g_get_home_dir (), "Desktop", NULL);
752   path = gtk_file_system_filename_to_path (impl->file_system, name);
753   g_free (name);
754
755   impl->has_desktop = shortcuts_insert_path (impl, -1, FALSE, NULL, path, _("Desktop"), FALSE, NULL);
756   /* We do not actually pop up an error dialog if there is no desktop directory
757    * because some people may really not want to have one.
758    */
759
760   gtk_file_path_free (path);
761 }
762
763 /* Appends a list of GtkFilePath to the shortcuts model; returns how many were inserted */
764 static int
765 shortcuts_append_paths (GtkFileChooserDefault *impl,
766                         GSList                *paths)
767 {
768   int num_inserted;
769
770   num_inserted = 0;
771
772   for (; paths; paths = paths->next)
773     {
774       GtkFilePath *path;
775       GError *error;
776
777       path = paths->data;
778       error = NULL;
779
780       /* NULL GError, but we don't really want to show error boxes here */
781
782       if (shortcuts_insert_path (impl, -1, FALSE, NULL, path, NULL, TRUE, NULL))
783         num_inserted++;
784     }
785
786   return num_inserted;
787 }
788
789 /* Returns the index for the corresponding item in the shortcuts bar */
790 static int
791 shortcuts_get_index (GtkFileChooserDefault *impl,
792                      ShortcutsIndex         where)
793 {
794   int n;
795
796   n = 0;
797
798   if (where == SHORTCUTS_HOME)
799     goto out;
800
801   n += impl->has_home ? 1 : 0;
802
803   if (where == SHORTCUTS_DESKTOP)
804     goto out;
805
806   n += impl->has_desktop ? 1 : 0;
807
808   if (where == SHORTCUTS_VOLUMES)
809     goto out;
810
811   n += impl->num_volumes;
812
813   if (where == SHORTCUTS_SHORTCUTS)
814     goto out;
815
816   n += impl->num_shortcuts;
817
818   if (where == SHORTCUTS_SEPARATOR)
819     goto out;
820
821   /* If there are no bookmarks there won't be a separator */
822   n += impl->num_shortcuts > 0 ? 1 : 0;
823
824   if (where == SHORTCUTS_BOOKMARKS)
825     goto out;
826
827   g_assert_not_reached ();
828
829  out:
830
831   return n;
832 }
833
834 typedef void (* RemoveFunc) (GtkFileChooserDefault *impl, gpointer data);
835
836 /* Removes the specified number of rows from the shortcuts list */
837 static void
838 shortcuts_remove_rows (GtkFileChooserDefault *impl,
839                        int start_row,
840                        int n_rows,
841                        RemoveFunc remove_fn)
842 {
843   GtkTreePath *path;
844
845   path = gtk_tree_path_new_from_indices (start_row, -1);
846
847   for (; n_rows; n_rows--)
848     {
849       GtkTreeIter iter;
850       gpointer data;
851
852       if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
853         g_assert_not_reached ();
854
855       if (remove_fn)
856         {
857           gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &data, -1);
858           (* remove_fn) (impl, data);
859         }
860
861       gtk_list_store_remove (impl->shortcuts_model, &iter);
862     }
863
864   gtk_tree_path_free (path);
865 }
866
867 /* Used from shortcuts_remove_rows() */
868 static void
869 volume_remove_cb (GtkFileChooserDefault *impl, gpointer data)
870 {
871   GtkFileSystemVolume *volume;
872
873   volume = data;
874   gtk_file_system_volume_free (impl->file_system, volume);
875 }
876
877 /* Adds all the file system volumes to the shortcuts model */
878 static void
879 shortcuts_add_volumes (GtkFileChooserDefault *impl)
880 {
881   int start_row;
882   GSList *list, *l;
883   int n;
884
885   start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
886   shortcuts_remove_rows (impl, start_row, impl->num_volumes, volume_remove_cb);
887   impl->num_volumes = 0;
888
889   list = gtk_file_system_list_volumes (impl->file_system);
890
891   n = 0;
892
893   for (l = list; l; l = l->next)
894     {
895       GtkFileSystemVolume *volume;
896
897       volume = l->data;
898
899       shortcuts_insert_path (impl, start_row + n, TRUE, volume, NULL, NULL, FALSE, NULL);
900       n++;
901     }
902
903   impl->num_volumes = n;
904
905   g_slist_free (list);
906 }
907
908 /* Used from shortcuts_remove_rows() */
909 static void
910 remove_bookmark_cb (GtkFileChooserDefault *impl, gpointer data)
911 {
912   GtkFilePath *path;
913
914   path = data;
915   gtk_file_path_free (path);
916 }
917
918 /* Inserts the bookmarks separator node */
919 static void
920 shortcuts_insert_separator (GtkFileChooserDefault *impl)
921 {
922   GtkTreeIter iter;
923
924   gtk_list_store_insert (impl->shortcuts_model, &iter,
925                          shortcuts_get_index (impl, SHORTCUTS_SEPARATOR));
926   gtk_list_store_set (impl->shortcuts_model, &iter,
927                       SHORTCUTS_COL_PIXBUF, NULL,
928                       SHORTCUTS_COL_PIXBUF_VISIBLE, FALSE,
929                       SHORTCUTS_COL_NAME, NULL,
930                       SHORTCUTS_COL_PATH, NULL,
931                       -1);
932 }
933
934 /* Creates the GtkTreeStore used as the shortcuts model */
935 /* Updates the list of bookmarks */
936 static void
937 shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
938 {
939   GSList *bookmarks;
940
941   if (impl->num_bookmarks > 0)
942     {
943       shortcuts_remove_rows (impl,
944                              shortcuts_get_index (impl, SHORTCUTS_SEPARATOR),
945                              impl->num_bookmarks + 1,
946                              remove_bookmark_cb);
947
948     }
949   
950   bookmarks = gtk_file_system_list_bookmarks (impl->file_system);
951   impl->num_bookmarks = shortcuts_append_paths (impl, bookmarks);
952   gtk_file_paths_free (bookmarks);
953
954   if (impl->num_bookmarks > 0)
955     {
956       shortcuts_insert_separator (impl);
957     }
958 }
959
960 static void
961 create_shortcuts_model (GtkFileChooserDefault *impl)
962 {
963   if (impl->shortcuts_model)
964     g_object_unref (impl->shortcuts_model);
965
966   /* Keep this order in sync with the SHORCUTS_COL_* enum values */
967   impl->shortcuts_model = gtk_list_store_new (SHORTCUTS_COL_NUM_COLUMNS,
968                                               GDK_TYPE_PIXBUF,  /* pixbuf */
969                                               G_TYPE_STRING,    /* name */
970                                               G_TYPE_POINTER,   /* path or volume */
971                                               G_TYPE_BOOLEAN,   /* removable */
972                                               G_TYPE_BOOLEAN);  /* pixbuf cell visibility */
973
974   if (impl->file_system)
975     {
976       shortcuts_append_home (impl);
977       shortcuts_append_desktop (impl);
978       shortcuts_add_volumes (impl);
979       shortcuts_add_bookmarks (impl);
980     }
981
982   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->shortcuts_tree), GTK_TREE_MODEL (impl->shortcuts_model));
983 }
984
985 /* Callback used when the "New Folder" toolbar button is clicked */
986 static void
987 new_folder_button_clicked (GtkButton             *button,
988                            GtkFileChooserDefault *impl)
989 {
990   GtkTreeIter iter;
991   GtkTreePath *path;
992
993   /* FIXME: this doesn't work for folder mode, just for file mode */
994
995   _gtk_file_system_model_add_editable (impl->list_model, &iter);
996   g_object_set (impl->list_name_renderer, "editable", TRUE, NULL);
997
998   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->list_model), &iter);
999   gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->list),
1000                             path,
1001                             impl->list_name_column,
1002                             TRUE);
1003 }
1004
1005 /* Callback used from the text cell renderer when the new folder is named */
1006 static void
1007 renderer_edited_cb (GtkCellRendererText   *cell_renderer_text,
1008                     const gchar           *path,
1009                     const gchar           *new_text,
1010                     GtkFileChooserDefault *impl)
1011 {
1012   GError *error;
1013   GtkFilePath *file_path;
1014
1015   _gtk_file_system_model_remove_editable (impl->list_model);
1016   g_object_set (impl->list_name_renderer, "editable", FALSE, NULL);
1017
1018   error = NULL;
1019   file_path = gtk_file_system_make_path (impl->file_system, impl->current_folder, new_text, &error);
1020   if (!file_path)
1021     {
1022       error_building_filename_dialog (impl, impl->current_folder, new_text, error);
1023       return;
1024     }
1025
1026   error = NULL;
1027   if (!gtk_file_system_create_folder (impl->file_system, file_path, &error))
1028     error_dialog (impl,
1029                   _("Could not create folder %s:\n%s"),
1030                   file_path, error);
1031
1032   gtk_file_path_free (file_path);
1033
1034   /* FIXME: scroll to the new folder and select it */
1035 }
1036
1037 /* Callback used from the text cell renderer when the new folder edition gets
1038  * canceled.
1039  */
1040 static void
1041 renderer_editing_canceled_cb (GtkCellRendererText   *cell_renderer_text,
1042                               GtkFileChooserDefault *impl)
1043 {
1044   _gtk_file_system_model_remove_editable (impl->list_model);
1045   g_object_set (impl->list_name_renderer, "editable", FALSE, NULL);
1046 }
1047
1048 /* Creates the widgets for the filter combo box */
1049 static GtkWidget *
1050 filter_create (GtkFileChooserDefault *impl)
1051 {
1052   impl->filter_combo = gtk_combo_box_new_text ();
1053   g_signal_connect (impl->filter_combo, "changed",
1054                     G_CALLBACK (filter_combo_changed), impl);
1055
1056   return impl->filter_combo;
1057 }
1058
1059 static GtkWidget *
1060 button_new (GtkFileChooserDefault *impl,
1061             const char *text,
1062             const char *stock_id,
1063             gboolean    sensitive,
1064             gboolean    show,
1065             GCallback   callback)
1066 {
1067   GtkWidget *button;
1068   GtkWidget *hbox;
1069   GtkWidget *widget;
1070   GtkWidget *align;
1071
1072   button = gtk_button_new ();
1073   hbox = gtk_hbox_new (FALSE, 2);
1074   align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1075
1076   gtk_container_add (GTK_CONTAINER (button), align);
1077   gtk_container_add (GTK_CONTAINER (align), hbox);
1078   widget = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
1079
1080   gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1081
1082   widget = gtk_label_new_with_mnemonic (text);
1083   gtk_label_set_mnemonic_widget (GTK_LABEL (widget), GTK_WIDGET (button));
1084   gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1085
1086   gtk_widget_set_sensitive (button, sensitive);
1087   g_signal_connect (button, "clicked", callback, impl);
1088
1089   gtk_widget_show_all (align);
1090
1091   if (show)
1092     gtk_widget_show (button);
1093
1094   return button;
1095 }
1096
1097 /* Creates the widgets for the folder tree */
1098 static GtkWidget *
1099 create_folder_tree (GtkFileChooserDefault *impl)
1100 {
1101   GtkTreeSelection *selection;
1102
1103   /* Scrolled window */
1104
1105   impl->tree_scrollwin = gtk_scrolled_window_new (NULL, NULL);
1106   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->tree_scrollwin),
1107                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1108   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (impl->tree_scrollwin),
1109                                        GTK_SHADOW_IN);
1110   if (impl->folder_mode)
1111     gtk_widget_show (impl->tree_scrollwin);
1112
1113   /* Tree */
1114
1115   impl->tree = gtk_tree_view_new ();
1116   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->tree), FALSE);
1117
1118   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->tree));
1119   g_signal_connect (selection, "changed",
1120                     G_CALLBACK (tree_selection_changed), impl);
1121
1122   gtk_container_add (GTK_CONTAINER (impl->tree_scrollwin), impl->tree);
1123   gtk_widget_show (impl->tree);
1124
1125   /* Column */
1126
1127   gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (impl->tree), 0,
1128                                               _("File name"),
1129                                               gtk_cell_renderer_text_new (),
1130                                               tree_name_data_func, impl, NULL);
1131   gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->tree),
1132                                    GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
1133
1134   return impl->tree_scrollwin;
1135 }
1136
1137 /* Returns whether a path is already present in the shortcuts list */
1138 static gboolean
1139 shortcut_exists (GtkFileChooserDefault *impl,
1140                  const GtkFilePath     *path)
1141 {
1142   gboolean exists;
1143   GtkTreeIter iter;
1144   int volumes_idx;
1145   int separator_idx;
1146
1147   exists = FALSE;
1148
1149   if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
1150     {
1151       int i;
1152
1153       separator_idx = shortcuts_get_index (impl, SHORTCUTS_SEPARATOR);
1154       volumes_idx = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
1155
1156       i = 0;
1157
1158       do
1159         {
1160           gpointer data;
1161
1162           if (i == separator_idx)
1163             continue;
1164
1165           gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &data, -1);
1166
1167           if (i >= volumes_idx && i < volumes_idx + impl->num_volumes)
1168             {
1169               GtkFileSystemVolume *volume;
1170               GtkFilePath *base_path;
1171
1172               volume = data;
1173               base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
1174
1175               exists = strcmp (gtk_file_path_get_string (path),
1176                                gtk_file_path_get_string (base_path)) == 0;
1177               g_free (base_path);
1178
1179               if (exists)
1180                 break;
1181             }
1182           else
1183             {
1184               GtkFilePath *model_path;
1185
1186               model_path = data;
1187
1188               if (model_path && gtk_file_path_compare (model_path, path) == 0)
1189                 {
1190                   exists = TRUE;
1191                   break;
1192                 }
1193             }
1194         }
1195       while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter));
1196     }
1197
1198   return exists;
1199 }
1200
1201 /* Tries to add a bookmark from a path name */
1202 static void
1203 shortcuts_add_bookmark_from_path (GtkFileChooserDefault *impl,
1204                                   const GtkFilePath     *path)
1205 {
1206   GtkFileInfo *info;
1207   GError *error;
1208
1209   if (shortcut_exists (impl, path))
1210     return;
1211
1212   error = NULL;
1213   info = get_file_info (impl->file_system, path, &error);
1214
1215   if (!info)
1216     error_getting_info_dialog (impl, path, error);
1217   else if (!gtk_file_info_get_is_folder (info))
1218     {
1219       char *msg;
1220
1221       msg = g_strdup_printf (_("Could not add bookmark for %s because it is not a folder."),
1222                              gtk_file_path_get_string (path));
1223       error_message (impl, msg);
1224       g_free (msg);
1225     }
1226   else
1227     {
1228       error = NULL;
1229       if (!gtk_file_system_add_bookmark (impl->file_system, path, &error))
1230         error_could_not_add_bookmark_dialog (impl, path, error);
1231     }
1232 }
1233
1234 static void
1235 add_bookmark_foreach_cb (GtkTreeModel *model,
1236                          GtkTreePath  *path,
1237                          GtkTreeIter  *iter,
1238                          gpointer      data)
1239 {
1240   GtkFileChooserDefault *impl;
1241   GtkFileSystemModel *fs_model;
1242   GtkTreeIter child_iter;
1243   const GtkFilePath *file_path;
1244
1245   impl = GTK_FILE_CHOOSER_DEFAULT (data);
1246
1247   if (impl->folder_mode)
1248     {
1249       fs_model = impl->tree_model;
1250       child_iter = *iter;
1251     }
1252   else
1253     {
1254       fs_model = impl->list_model;
1255       gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter);
1256     }
1257
1258   file_path = _gtk_file_system_model_get_path (fs_model, &child_iter);
1259   shortcuts_add_bookmark_from_path (impl, file_path);
1260 }
1261
1262 /* Callback used when the "Add bookmark" button is clicked */
1263 static void
1264 add_bookmark_button_clicked_cb (GtkButton *button,
1265                                 GtkFileChooserDefault *impl)
1266 {
1267   GtkWidget *tree_view;
1268   GtkTreeSelection *selection;
1269
1270   if (impl->folder_mode)
1271     tree_view = impl->tree;
1272   else
1273     tree_view = impl->list;
1274
1275   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
1276   if (gtk_tree_selection_count_selected_rows (selection) == 0)
1277     shortcuts_add_bookmark_from_path (impl, impl->current_folder);
1278   else
1279     gtk_tree_selection_selected_foreach (selection,
1280                                          add_bookmark_foreach_cb,
1281                                          impl);
1282 }
1283
1284 /* Callback used when the "Remove bookmark" button is clicked */
1285 static void
1286 remove_bookmark_button_clicked_cb (GtkButton *button,
1287                                    GtkFileChooserDefault *impl)
1288 {
1289   GtkTreeSelection *selection;
1290   GtkTreeIter iter;
1291   GtkFilePath *path;
1292   gboolean removable;
1293   GError *error;
1294
1295   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->shortcuts_tree));
1296
1297   if (gtk_tree_selection_get_selected (selection, NULL, &iter))
1298     {
1299       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
1300                           SHORTCUTS_COL_PATH, &path,
1301                           SHORTCUTS_COL_REMOVABLE, &removable, -1);
1302       if (!removable)
1303         {
1304           g_assert_not_reached ();
1305           return;
1306         }
1307
1308       error = NULL;
1309       if (!gtk_file_system_remove_bookmark (impl->file_system, path, &error))
1310         error_dialog (impl,
1311                       _("Could not remove bookmark for %s:\n%s"),
1312                       path,
1313                       error);
1314     }
1315 }
1316
1317 struct is_folders_foreach_closure {
1318   GtkFileChooserDefault *impl;
1319   gboolean all_folders;
1320 };
1321
1322 /* Used from gtk_tree_selection_selected_foreach() */
1323 static void
1324 is_folders_foreach_cb (GtkTreeModel *model,
1325                        GtkTreePath  *path,
1326                        GtkTreeIter  *iter,
1327                        gpointer      data)
1328 {
1329   struct is_folders_foreach_closure *closure;
1330   GtkTreeIter child_iter;
1331   const GtkFileInfo *info;
1332
1333   closure = data;
1334
1335   gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
1336
1337   info = _gtk_file_system_model_get_info (closure->impl->list_model, &child_iter);
1338   closure->all_folders &= gtk_file_info_get_is_folder (info);
1339 }
1340
1341 /* Returns whether the selected items in the file list are all folders */
1342 static gboolean
1343 selection_is_folders (GtkFileChooserDefault *impl)
1344 {
1345   struct is_folders_foreach_closure closure;
1346   GtkTreeSelection *selection;
1347
1348   g_assert (!impl->folder_mode);
1349
1350   closure.impl = impl;
1351   closure.all_folders = TRUE;
1352
1353   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
1354   gtk_tree_selection_selected_foreach (selection,
1355                                        is_folders_foreach_cb,
1356                                        &closure);
1357
1358   return closure.all_folders;
1359 }
1360
1361 /* Sensitize the "add bookmark" button if all the selected items are folders, or
1362  * if there are no selected items *and* the current folder is not in the
1363  * bookmarks list.  De-sensitize the button otherwise.
1364  */
1365 static void
1366 bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl)
1367 {
1368   GtkWidget *tree_view;
1369   GtkTreeSelection *selection;
1370   gboolean active;
1371
1372   /* Check selection */
1373
1374   if (impl->folder_mode)
1375     tree_view = impl->tree;
1376   else
1377     tree_view = impl->list;
1378
1379   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
1380
1381   if (gtk_tree_selection_count_selected_rows (selection) == 0)
1382     active = !shortcut_exists (impl, impl->current_folder);
1383   else
1384     active = (impl->folder_mode || selection_is_folders (impl));
1385
1386   gtk_widget_set_sensitive (impl->add_bookmark_button, active);
1387 }
1388
1389 /* Sets the sensitivity of the "remove bookmark" button depending on whether a
1390  * bookmark row is selected in the shortcuts tree.
1391  */
1392 static void
1393 bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl)
1394 {
1395   GtkTreeSelection *selection;
1396   GtkTreeIter iter;
1397   gboolean removable = FALSE;
1398
1399   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->shortcuts_tree));
1400
1401   if (gtk_tree_selection_get_selected (selection, NULL, &iter))
1402     gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
1403                         SHORTCUTS_COL_REMOVABLE, &removable,
1404                         -1);
1405
1406   gtk_widget_set_sensitive (impl->remove_bookmark_button, removable);
1407 }
1408
1409 /* Converts raw selection data from text/uri-list to a list of strings */
1410 static GSList *
1411 split_uris (const char *data)
1412 {
1413   GSList *uris;
1414   const char *p, *start;
1415
1416   uris = NULL;
1417
1418   start = data;
1419
1420   for (p = start; *p != 0; p++)
1421     if (*p == '\r' && *(p + 1) == '\n')
1422       {
1423         char *name;
1424
1425         name = g_strndup (start, p - start);
1426         uris = g_slist_prepend (uris, name);
1427
1428         start = p + 2;
1429         p = start;
1430       }
1431
1432   uris = g_slist_reverse (uris);
1433   return uris;
1434 }
1435
1436 /* Callback used when we get the drag data for the bookmarks list.  We add the
1437  * received URIs as bookmarks if they are folders.
1438  */
1439 static void
1440 shortcuts_drag_data_received_cb (GtkWidget          *widget,
1441                                  GdkDragContext     *context,
1442                                  gint                x,
1443                                  gint                y,
1444                                  GtkSelectionData   *selection_data,
1445                                  guint               info,
1446                                  guint               time_,
1447                                  gpointer            data)
1448 {
1449   GtkFileChooserDefault *impl;
1450   GSList *uris, *l;
1451
1452   impl = GTK_FILE_CHOOSER_DEFAULT (data);
1453
1454   uris = split_uris (selection_data->data);
1455
1456   for (l = uris; l; l = l->next)
1457     {
1458       char *uri;
1459       GtkFilePath *path;
1460
1461       uri = l->data;
1462       path = gtk_file_system_uri_to_path (impl->file_system, uri);
1463
1464       if (path)
1465         {
1466           shortcuts_add_bookmark_from_path (impl, path);
1467           gtk_file_path_free (path);
1468         }
1469       else
1470         {
1471           char *msg;
1472
1473           msg = g_strdup_printf (_("Could not add a bookmark for %s because it is an invalid path name."),
1474                                  uri);
1475           error_message (impl, msg);
1476           g_free (msg);
1477         }
1478
1479       g_free (uri);
1480     }
1481
1482   g_slist_free (uris);
1483 }
1484
1485 /* Callback used when the selection in the shortcuts tree changes */
1486 static void
1487 shortcuts_selection_changed_cb (GtkTreeSelection      *selection,
1488                                 GtkFileChooserDefault *impl)
1489 {
1490   bookmarks_check_remove_sensitivity (impl);
1491 }
1492
1493 /* Creates the widgets for the shortcuts and bookmarks tree */
1494 static GtkWidget *
1495 shortcuts_tree_create (GtkFileChooserDefault *impl)
1496 {
1497   GtkTreeSelection *selection;
1498   GtkTreeViewColumn *column;
1499   GtkCellRenderer *renderer;
1500
1501   /* Scrolled window */
1502
1503   impl->shortcuts_scrollwin = gtk_scrolled_window_new (NULL, NULL);
1504   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->shortcuts_scrollwin),
1505                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1506   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (impl->shortcuts_scrollwin),
1507                                        GTK_SHADOW_IN);
1508   gtk_widget_show (impl->shortcuts_scrollwin);
1509
1510   /* Tree */
1511
1512   impl->shortcuts_tree = gtk_tree_view_new ();
1513   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->shortcuts_tree), FALSE);
1514   
1515   gtk_drag_dest_set (impl->shortcuts_tree,
1516                      GTK_DEST_DEFAULT_ALL,
1517                      shortcuts_targets,
1518                      num_shortcuts_targets,
1519                      GDK_ACTION_COPY);
1520
1521   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->shortcuts_tree));
1522   gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
1523   gtk_tree_selection_set_select_function (selection,
1524                                           shortcuts_select_func,
1525                                           impl, NULL);
1526
1527   g_signal_connect (selection, "changed",
1528                     G_CALLBACK (shortcuts_selection_changed_cb), impl);
1529
1530   g_signal_connect (impl->shortcuts_tree, "row-activated",
1531                     G_CALLBACK (shortcuts_row_activated_cb), impl);
1532
1533   g_signal_connect (impl->shortcuts_tree, "drag-data-received",
1534                     G_CALLBACK (shortcuts_drag_data_received_cb), impl);
1535
1536   gtk_container_add (GTK_CONTAINER (impl->shortcuts_scrollwin), impl->shortcuts_tree);
1537   gtk_widget_show (impl->shortcuts_tree);
1538
1539   /* Model */
1540
1541   create_shortcuts_model (impl);
1542
1543   /* Column */
1544
1545   column = gtk_tree_view_column_new ();
1546   gtk_tree_view_column_set_title (column, _("Folder"));
1547
1548   renderer = gtk_cell_renderer_pixbuf_new ();
1549   gtk_tree_view_column_pack_start (column, renderer, FALSE);
1550   gtk_tree_view_column_set_attributes (column, renderer,
1551                                        "pixbuf", SHORTCUTS_COL_PIXBUF,
1552                                        "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
1553                                        NULL);
1554
1555   renderer = _gtk_cell_renderer_sep_text_new ();
1556   gtk_tree_view_column_pack_start (column, renderer, TRUE);
1557   gtk_tree_view_column_set_attributes (column, renderer,
1558                                        "text", SHORTCUTS_COL_NAME,
1559                                        NULL);
1560
1561   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->shortcuts_tree), column);
1562
1563   return impl->shortcuts_scrollwin;
1564 }
1565
1566 /* Creates the widgets for the shortcuts/bookmarks pane */
1567 static GtkWidget *
1568 shortcuts_pane_create (GtkFileChooserDefault *impl,
1569                        GtkSizeGroup          *size_group)
1570 {
1571   GtkWidget *vbox;
1572   GtkWidget *hbox;
1573   GtkWidget *widget;
1574
1575   vbox = gtk_vbox_new (FALSE, 6);
1576   gtk_widget_show (vbox);
1577
1578   /* Shortcuts tree */
1579
1580   widget = shortcuts_tree_create (impl);
1581   gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
1582
1583   /* Box for buttons */
1584
1585   hbox = gtk_hbox_new (TRUE, 6);
1586   gtk_size_group_add_widget (size_group, hbox);
1587   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1588   gtk_widget_show (hbox);
1589
1590   /* Add bookmark button */
1591
1592   impl->add_bookmark_button = button_new (impl,
1593                                           _("_Add"),
1594                                           GTK_STOCK_ADD,
1595                                           FALSE,
1596                                           TRUE,
1597                                           G_CALLBACK (add_bookmark_button_clicked_cb));
1598   gtk_box_pack_start (GTK_BOX (hbox), impl->add_bookmark_button, TRUE, TRUE, 0);
1599
1600   /* Remove bookmark button */
1601
1602   impl->remove_bookmark_button = button_new (impl,
1603                                              _("_Remove"),
1604                                              GTK_STOCK_REMOVE,
1605                                              FALSE,
1606                                              TRUE,
1607                                              G_CALLBACK (remove_bookmark_button_clicked_cb));
1608   gtk_box_pack_start (GTK_BOX (hbox), impl->remove_bookmark_button, TRUE, TRUE, 0);
1609
1610   return vbox;
1611 }
1612
1613 /* Creates the widgets for the file list */
1614 static GtkWidget *
1615 create_file_list (GtkFileChooserDefault *impl)
1616 {
1617   GtkTreeSelection *selection;
1618   GtkTreeViewColumn *column;
1619   GtkCellRenderer *renderer;
1620
1621   /* Scrolled window */
1622
1623   impl->list_scrollwin = gtk_scrolled_window_new (NULL, NULL);
1624   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->list_scrollwin),
1625                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1626   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (impl->list_scrollwin),
1627                                        GTK_SHADOW_IN);
1628   if (!impl->folder_mode)
1629     gtk_widget_show (impl->list_scrollwin);
1630
1631   /* Tree/list view */
1632
1633   impl->list = gtk_tree_view_new ();
1634   gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->list), TRUE);
1635   gtk_container_add (GTK_CONTAINER (impl->list_scrollwin), impl->list);
1636   g_signal_connect (impl->list, "row_activated",
1637                     G_CALLBACK (list_row_activated), impl);
1638   gtk_widget_show (impl->list);
1639
1640   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
1641   g_signal_connect (selection, "changed",
1642                     G_CALLBACK (list_selection_changed), impl);
1643
1644   /* Filename column */
1645
1646   impl->list_name_column = gtk_tree_view_column_new ();
1647   gtk_tree_view_column_set_expand (impl->list_name_column, TRUE);
1648   gtk_tree_view_column_set_title (impl->list_name_column, _("File name"));
1649   gtk_tree_view_column_set_sort_column_id (impl->list_name_column, FILE_LIST_COL_NAME);
1650
1651   renderer = gtk_cell_renderer_pixbuf_new ();
1652   gtk_tree_view_column_pack_start (impl->list_name_column, renderer, FALSE);
1653   gtk_tree_view_column_set_cell_data_func (impl->list_name_column, renderer,
1654                                            list_icon_data_func, impl, NULL);
1655
1656   impl->list_name_renderer = gtk_cell_renderer_text_new ();
1657   g_signal_connect (impl->list_name_renderer, "edited",
1658                     G_CALLBACK (renderer_edited_cb), impl);
1659   g_signal_connect (impl->list_name_renderer, "editing-canceled",
1660                     G_CALLBACK (renderer_editing_canceled_cb), impl);
1661   gtk_tree_view_column_pack_start (impl->list_name_column, impl->list_name_renderer, TRUE);
1662   gtk_tree_view_column_set_cell_data_func (impl->list_name_column, impl->list_name_renderer,
1663                                            list_name_data_func, impl, NULL);
1664
1665   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->list), impl->list_name_column);
1666 #if 0
1667   /* Size column */
1668
1669   column = gtk_tree_view_column_new ();
1670   gtk_tree_view_column_set_title (column, _("Size"));
1671
1672   renderer = gtk_cell_renderer_text_new ();
1673   gtk_tree_view_column_pack_start (column, renderer, TRUE);
1674   gtk_tree_view_column_set_cell_data_func (column, renderer,
1675                                            list_size_data_func, impl, NULL);
1676   gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_SIZE);
1677   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->list), column);
1678 #endif
1679   /* Modification time column */
1680
1681   column = gtk_tree_view_column_new ();
1682   gtk_tree_view_column_set_title (column, _("Modified"));
1683
1684   renderer = gtk_cell_renderer_text_new ();
1685   gtk_tree_view_column_pack_start (column, renderer, TRUE);
1686   gtk_tree_view_column_set_cell_data_func (column, renderer,
1687                                            list_mtime_data_func, impl, NULL);
1688   gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_MTIME);
1689   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->list), column);
1690
1691   return impl->list_scrollwin;
1692 }
1693
1694 static GtkWidget *
1695 create_filename_entry_and_filter_combo (GtkFileChooserDefault *impl)
1696 {
1697   GtkWidget *hbox;
1698   GtkWidget *widget;
1699
1700   hbox = gtk_hbox_new (FALSE, 12);
1701   gtk_widget_show (hbox);
1702
1703   /* Filter combo */
1704
1705   widget = filter_create (impl);
1706   gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1707
1708   return hbox;
1709 }
1710
1711 /* Creates the widgets for the files/folders pane */
1712 static GtkWidget *
1713 file_pane_create (GtkFileChooserDefault *impl,
1714                   GtkSizeGroup          *size_group)
1715 {
1716   GtkWidget *vbox;
1717   GtkWidget *hbox;
1718   GtkWidget *widget;
1719
1720   vbox = gtk_vbox_new (FALSE, 6);
1721   gtk_widget_show (vbox);
1722
1723   /* The path bar and 'Create Folder' button */
1724   hbox = gtk_hbox_new (FALSE, 12);
1725   gtk_widget_show (hbox);
1726   impl->path_bar = g_object_new (GTK_TYPE_PATH_BAR, NULL);
1727   g_signal_connect (impl->path_bar, "path_clicked", G_CALLBACK (path_bar_clicked), impl);
1728   gtk_widget_show_all (impl->path_bar);
1729   gtk_box_pack_start (GTK_BOX (hbox), impl->path_bar, TRUE, TRUE, 0);
1730
1731   /* Create Folder */
1732   impl->new_folder_button = gtk_button_new_with_mnemonic (_("Create _Folder"));
1733   g_signal_connect (impl->new_folder_button, "clicked",
1734                     G_CALLBACK (new_folder_button_clicked), impl);
1735   gtk_box_pack_end (GTK_BOX (hbox), impl->new_folder_button, FALSE, FALSE, 0);
1736
1737   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
1738       impl->folder_mode)
1739     gtk_widget_show (impl->new_folder_button);
1740   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1741
1742   /* Box for lists and preview */
1743
1744   hbox = gtk_hbox_new (FALSE, 12);
1745   gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
1746   gtk_widget_show (hbox);
1747
1748   /* Folder tree */
1749
1750   widget = create_folder_tree (impl);
1751   gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
1752
1753   /* File list */
1754
1755   widget = create_file_list (impl);
1756   gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
1757
1758   /* Preview */
1759
1760   impl->preview_frame = gtk_frame_new (_("Preview"));
1761   gtk_box_pack_start (GTK_BOX (hbox), impl->preview_frame, FALSE, FALSE, 0);
1762   /* Don't show preview frame initially */
1763
1764   /* Filename entry and filter combo */
1765   hbox = gtk_hbox_new (FALSE, 0);
1766   gtk_size_group_add_widget (size_group, hbox);
1767   widget = create_filename_entry_and_filter_combo (impl);
1768   gtk_box_pack_end (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1769   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1770   gtk_widget_show (hbox);
1771
1772   return vbox;
1773 }
1774 /* Callback used when the "Browse for more folders" expander is toggled */
1775 static void
1776 expander_changed_cb (GtkExpander           *expander,
1777                      GParamSpec            *pspec,
1778                      GtkFileChooserDefault *impl)
1779 {
1780   gboolean active;
1781
1782   active = gtk_expander_get_expanded (expander);
1783
1784   if (active)
1785     gtk_widget_show (impl->hpaned);
1786   else
1787     gtk_widget_hide (impl->hpaned);
1788
1789   gtk_widget_set_sensitive (impl->save_folder_label, !active);
1790 /*   gtk_widget_set_sensitive (impl->save_folder_combo, !active); */
1791 }
1792
1793 /* Creates the widgets specific to Save mode */
1794 static GtkWidget *
1795 save_widgets_create (GtkFileChooserDefault *impl)
1796 {
1797   GtkWidget *table;
1798   GtkWidget *widget;
1799   GtkWidget *alignment;
1800
1801   table = gtk_table_new (3, 2, FALSE);
1802   gtk_table_set_row_spacings (GTK_TABLE (table), 12);
1803   gtk_table_set_col_spacings (GTK_TABLE (table), 12);
1804
1805   /* Name entry */
1806
1807   widget = gtk_label_new_with_mnemonic (_("_Name:"));
1808   gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
1809   gtk_table_attach (GTK_TABLE (table), widget,
1810                     0, 1, 0, 1,
1811                     GTK_FILL, GTK_FILL,
1812                     0, 0);
1813   gtk_widget_show (widget);
1814
1815   impl->entry = gtk_entry_new ();
1816   gtk_entry_set_activates_default (GTK_ENTRY (impl->entry), TRUE);
1817   gtk_table_attach (GTK_TABLE (table), impl->entry,
1818                     1, 2, 0, 1,
1819                     GTK_EXPAND | GTK_FILL, 0,
1820                     0, 0);
1821   gtk_widget_show (impl->entry);
1822   gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->entry);
1823
1824   /* Folder combo */
1825
1826   impl->save_folder_label = gtk_label_new_with_mnemonic (_("Save in _Folder:"));
1827   gtk_misc_set_alignment (GTK_MISC (impl->save_folder_label), 0.0, 0.5);
1828   gtk_table_attach (GTK_TABLE (table), impl->save_folder_label,
1829                     0, 1, 1, 2,
1830                     GTK_FILL, GTK_FILL,
1831                     0, 0);
1832   gtk_widget_show (impl->save_folder_label);
1833
1834   /* FIXME: create the combo */
1835
1836   /* Expander */
1837
1838   alignment = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
1839   gtk_table_attach (GTK_TABLE (table), alignment,
1840                     0, 2, 2, 3,
1841                     GTK_FILL, GTK_FILL,
1842                     0, 0);
1843   gtk_widget_show (alignment);
1844
1845   widget = gtk_expander_new_with_mnemonic (_("_Browse for other folders"));
1846   gtk_container_add (GTK_CONTAINER (alignment), widget);
1847   gtk_widget_show (widget);
1848   g_signal_connect (widget, "notify::expanded",
1849                     G_CALLBACK (expander_changed_cb),
1850                     impl);
1851
1852   return table;  
1853 }
1854
1855 /* Creates the main hpaned with the widgets shared by Open and Save mode */
1856 static GtkWidget *
1857 main_paned_create (GtkFileChooserDefault *impl)
1858 {
1859   GtkWidget *hpaned;
1860   GtkWidget *widget;
1861   GtkSizeGroup *size_group;
1862
1863   /* size group is used by the [+][-] buttons, as well as the filter. */
1864   size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
1865
1866   /* Paned widget */
1867
1868   hpaned = gtk_hpaned_new ();
1869   gtk_paned_set_position (GTK_PANED (hpaned), 200); /* FIXME: this sucks */
1870
1871   /* Shortcuts pane */
1872
1873   widget = shortcuts_pane_create (impl, size_group);
1874   gtk_paned_pack1 (GTK_PANED (hpaned), widget, FALSE, FALSE);
1875
1876   /* File/folder pane */
1877
1878   widget = file_pane_create (impl, size_group);
1879   gtk_paned_pack2 (GTK_PANED (hpaned), widget, TRUE, FALSE);
1880
1881   return hpaned;
1882 }
1883
1884 static GObject*
1885 gtk_file_chooser_default_constructor (GType                  type,
1886                                       guint                  n_construct_properties,
1887                                       GObjectConstructParam *construct_params)
1888 {
1889   GtkFileChooserDefault *impl;
1890   GObject *object;
1891   GtkWidget *show_widget;
1892
1893   object = parent_class->constructor (type,
1894                                       n_construct_properties,
1895                                       construct_params);
1896   impl = GTK_FILE_CHOOSER_DEFAULT (object);
1897
1898   g_assert (impl->file_system);
1899
1900   gtk_widget_push_composite_child ();
1901
1902   /* Widgets for Save mode */
1903
1904   impl->save_widgets = save_widgets_create (impl);
1905   gtk_box_pack_start (GTK_BOX (impl), impl->save_widgets, FALSE, FALSE, 0);
1906
1907   /* Widgets for Open and Save mode */
1908
1909   impl->hpaned = main_paned_create (impl);
1910   gtk_box_pack_start (GTK_BOX (impl), impl->hpaned, TRUE, TRUE, 0);
1911
1912   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
1913     show_widget = impl->hpaned;
1914   else
1915     show_widget = impl->save_widgets;
1916
1917   gtk_widget_show (show_widget);
1918
1919   gtk_widget_pop_composite_child ();
1920
1921   return object;
1922 }
1923
1924 /* Sets the extra_widget by packing it in the appropriate place */
1925 static void
1926 set_extra_widget (GtkFileChooserDefault *impl,
1927                   GtkWidget             *extra_widget)
1928 {
1929   if (extra_widget == impl->extra_widget)
1930     return;
1931
1932   if (impl->extra_widget)
1933     gtk_container_remove (GTK_CONTAINER (impl), impl->extra_widget);
1934
1935   impl->extra_widget = extra_widget;
1936   if (impl->extra_widget)
1937     {
1938       gtk_widget_show_all (impl->extra_widget);
1939       gtk_box_pack_end (GTK_BOX (impl), impl->extra_widget, FALSE, FALSE, 0);
1940     }
1941 }
1942
1943 static void
1944 volumes_changed_cb (GtkFileSystem         *file_system,
1945                     GtkFileChooserDefault *impl)
1946 {
1947   shortcuts_add_volumes (impl);
1948 }
1949
1950 /* Callback used when the set of bookmarks changes in the file system */
1951 static void
1952 bookmarks_changed_cb (GtkFileSystem         *file_system,
1953                       GtkFileChooserDefault *impl)
1954 {
1955   shortcuts_add_bookmarks (impl);
1956
1957   bookmarks_check_add_sensitivity (impl);
1958   bookmarks_check_remove_sensitivity (impl);
1959 }
1960
1961 /* Sets the file chooser to multiple selection mode */
1962 static void
1963 set_select_multiple (GtkFileChooserDefault *impl,
1964                      gboolean               select_multiple,
1965                      gboolean               property_notify)
1966 {
1967   GtkTreeSelection *selection;
1968   GtkSelectionMode mode;
1969
1970   if (select_multiple == impl->select_multiple)
1971     return;
1972
1973   impl->select_multiple = select_multiple;
1974
1975   mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE;
1976
1977   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->tree));
1978   gtk_tree_selection_set_mode (selection, mode);
1979
1980   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
1981   gtk_tree_selection_set_mode (selection, mode);
1982
1983   g_object_notify (G_OBJECT (impl), "select-multiple");
1984
1985   /* FIXME #132255: See note in check_preview_change() */
1986   check_preview_change (impl);
1987 }
1988
1989 static void
1990 set_file_system_backend (GtkFileChooserDefault *impl,
1991                          const char *backend)
1992 {
1993   if (impl->file_system)
1994     {
1995       g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
1996       impl->volumes_changed_id = 0;
1997       g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
1998       impl->bookmarks_changed_id = 0;
1999       g_object_unref (impl->file_system);
2000     }
2001   
2002   impl->file_system = NULL;
2003   if (backend)
2004     impl->file_system = _gtk_file_system_create (backend);
2005   
2006   if (!impl->file_system)
2007     {
2008 #if defined (G_OS_UNIX)
2009       impl->file_system = gtk_file_system_unix_new ();
2010 #elif defined (G_OS_WIN32)
2011       impl->file_system = gtk_file_system_win32_new ();
2012 #else
2013 #error "No default filesystem implementation on the platform"
2014 #endif
2015     }
2016   
2017   if (impl->file_system)
2018     {
2019       impl->volumes_changed_id = g_signal_connect (impl->file_system, "volumes-changed",
2020                                                    G_CALLBACK (volumes_changed_cb),
2021                                                    impl);
2022       impl->bookmarks_changed_id = g_signal_connect (impl->file_system, "bookmarks-changed",
2023                                                      G_CALLBACK (bookmarks_changed_cb),
2024                                                      impl);
2025     }
2026 }
2027
2028 static void
2029 gtk_file_chooser_default_set_property (GObject      *object,
2030                                        guint         prop_id,
2031                                        const GValue *value,
2032                                        GParamSpec   *pspec)
2033
2034 {
2035   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
2036
2037   switch (prop_id)
2038     {
2039     case GTK_FILE_CHOOSER_PROP_ACTION:
2040       impl->action = g_value_get_enum (value);
2041       if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
2042         {
2043           gtk_widget_hide (impl->hpaned);
2044           gtk_widget_show (impl->save_widgets);
2045           gtk_widget_show (impl->new_folder_button);
2046
2047           if (impl->select_multiple)
2048             {
2049               g_warning ("Save mode cannot be set in conjunction with multiple selection mode.  "
2050                          "Re-setting to single selection mode.");
2051               set_select_multiple (impl, FALSE, TRUE);
2052             }
2053         }
2054       else
2055         {
2056           gtk_widget_hide (impl->save_widgets);
2057           gtk_widget_show (impl->hpaned);
2058
2059           if (impl->folder_mode)
2060             gtk_widget_show (impl->new_folder_button);
2061           else
2062             gtk_widget_hide (impl->new_folder_button);
2063         }
2064
2065       break;
2066     case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
2067       set_file_system_backend (impl, g_value_get_string (value));
2068       break;
2069     case GTK_FILE_CHOOSER_PROP_FILTER:
2070       set_current_filter (impl, g_value_get_object (value));
2071       break;
2072     case GTK_FILE_CHOOSER_PROP_FOLDER_MODE:
2073       {
2074         gboolean folder_mode = g_value_get_boolean (value);
2075         if (folder_mode != impl->folder_mode)
2076           {
2077             impl->folder_mode = folder_mode;
2078             if (impl->folder_mode)
2079               {
2080                 gtk_widget_hide (impl->list_scrollwin);
2081                 gtk_widget_show (impl->tree_scrollwin);
2082                 gtk_widget_show (impl->new_folder_button);
2083               }
2084             else
2085               {
2086                 gtk_widget_hide (impl->tree_scrollwin);
2087                 gtk_widget_show (impl->list_scrollwin);
2088                 gtk_widget_hide (impl->new_folder_button);
2089               }
2090           }
2091       }
2092       break;
2093     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
2094       impl->local_only = g_value_get_boolean (value);
2095       break;
2096     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
2097       set_preview_widget (impl, g_value_get_object (value));
2098       break;
2099     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
2100       impl->preview_widget_active = g_value_get_boolean (value);
2101       update_preview_widget_visibility (impl);
2102       break;
2103     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
2104       set_extra_widget (impl, g_value_get_object (value));
2105       break;
2106     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
2107       {
2108         gboolean select_multiple = g_value_get_boolean (value);
2109         if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE && select_multiple)
2110           {
2111             g_warning ("Multiple selection mode is not allowed in Save mode");
2112             return;
2113           }
2114
2115         set_select_multiple (impl, select_multiple, FALSE);
2116       }
2117       break;
2118     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
2119       {
2120         gboolean show_hidden = g_value_get_boolean (value);
2121         if (show_hidden != impl->show_hidden)
2122           {
2123             impl->show_hidden = show_hidden;
2124             _gtk_file_system_model_set_show_hidden (impl->tree_model, show_hidden);
2125             _gtk_file_system_model_set_show_hidden (impl->list_model, show_hidden);
2126           }
2127       }
2128       break;
2129     default:
2130       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2131       break;
2132     }
2133 }
2134
2135 static void
2136 gtk_file_chooser_default_get_property (GObject    *object,
2137                                        guint       prop_id,
2138                                        GValue     *value,
2139                                        GParamSpec *pspec)
2140 {
2141   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
2142
2143   switch (prop_id)
2144     {
2145     case GTK_FILE_CHOOSER_PROP_ACTION:
2146       g_value_set_enum (value, impl->action);
2147       break;
2148     case GTK_FILE_CHOOSER_PROP_FILTER:
2149       g_value_set_object (value, impl->current_filter);
2150       break;
2151     case GTK_FILE_CHOOSER_PROP_FOLDER_MODE:
2152       g_value_set_boolean (value, impl->folder_mode);
2153       break;
2154     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
2155       g_value_set_boolean (value, impl->local_only);
2156       break;
2157     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
2158       g_value_set_object (value, impl->preview_widget);
2159       break;
2160     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
2161       g_value_set_boolean (value, impl->preview_widget_active);
2162       break;
2163     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
2164       g_value_set_object (value, impl->extra_widget);
2165       break;
2166     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
2167       g_value_set_boolean (value, impl->select_multiple);
2168       break;
2169     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
2170       g_value_set_boolean (value, impl->show_hidden);
2171       break;
2172     default:
2173       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2174       break;
2175     }
2176 }
2177
2178 /* We override show-all since we have internal widgets that
2179  * shouldn't be shown when you call show_all(), like the filter
2180  * combo box.
2181  */
2182 static void
2183 gtk_file_chooser_default_show_all (GtkWidget *widget)
2184 {
2185   gtk_widget_show (widget);
2186 }
2187
2188 static void
2189 expand_and_select_func (GtkFileSystemModel *model,
2190                         GtkTreePath        *path,
2191                         GtkTreeIter        *iter,
2192                         gpointer            user_data)
2193 {
2194   GtkFileChooserDefault *impl = user_data;
2195   GtkTreeView *tree_view;
2196
2197   if (model == impl->tree_model)
2198     tree_view = GTK_TREE_VIEW (impl->tree);
2199   else
2200     tree_view = GTK_TREE_VIEW (impl->list);
2201
2202   gtk_tree_view_expand_to_path (tree_view, path);
2203   gtk_tree_view_expand_row (tree_view, path, FALSE);
2204   gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
2205   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->tree), path, NULL, TRUE, 0.3, 0.5);
2206 }
2207
2208 static gboolean
2209 list_model_filter_func (GtkFileSystemModel *model,
2210                         GtkFilePath        *path,
2211                         const GtkFileInfo  *file_info,
2212                         gpointer            user_data)
2213 {
2214   GtkFileChooserDefault *impl = user_data;
2215   GtkFileFilterInfo filter_info;
2216   GtkFileFilterFlags needed;
2217   gboolean result;
2218
2219   if (!impl->current_filter)
2220     return TRUE;
2221
2222   if (gtk_file_info_get_is_folder (file_info))
2223     return TRUE;
2224
2225   filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
2226
2227   needed = gtk_file_filter_get_needed (impl->current_filter);
2228
2229   filter_info.display_name = gtk_file_info_get_display_name (file_info);
2230   filter_info.mime_type = gtk_file_info_get_mime_type (file_info);
2231
2232   if (needed & GTK_FILE_FILTER_FILENAME)
2233     {
2234       filter_info.filename = gtk_file_system_path_to_filename (impl->file_system, path);
2235       if (filter_info.filename)
2236         filter_info.contains |= GTK_FILE_FILTER_FILENAME;
2237     }
2238   else
2239     filter_info.filename = NULL;
2240
2241   if (needed & GTK_FILE_FILTER_URI)
2242     {
2243       filter_info.uri = gtk_file_system_path_to_uri (impl->file_system, path);
2244       if (filter_info.filename)
2245         filter_info.contains |= GTK_FILE_FILTER_URI;
2246     }
2247   else
2248     filter_info.uri = NULL;
2249
2250   result = gtk_file_filter_filter (impl->current_filter, &filter_info);
2251
2252   if (filter_info.filename)
2253     g_free ((gchar *)filter_info.filename);
2254   if (filter_info.uri)
2255     g_free ((gchar *)filter_info.uri);
2256
2257   return result;
2258 }
2259
2260 static void
2261 install_list_model_filter (GtkFileChooserDefault *impl)
2262 {
2263   if (impl->current_filter)
2264     _gtk_file_system_model_set_filter (impl->list_model,
2265                                        list_model_filter_func,
2266                                        impl);
2267 }
2268
2269 #define COMPARE_DIRECTORIES                                                                                    \
2270   GtkFileChooserDefault *impl = user_data;                                                                     \
2271   const GtkFileInfo *info_a = _gtk_file_system_model_get_info (impl->list_model, a);                           \
2272   const GtkFileInfo *info_b = _gtk_file_system_model_get_info (impl->list_model, b);                           \
2273   gboolean dir_a, dir_b;                                                                                       \
2274                                                                                                                \
2275   if (info_a)                                                                                                  \
2276     dir_a = gtk_file_info_get_is_folder (info_a);                                                              \
2277   else                                                                                                         \
2278     return impl->list_sort_ascending ? -1 : 1;                                                                 \
2279                                                                                                                \
2280   if (info_b)                                                                                                  \
2281     dir_b = gtk_file_info_get_is_folder (info_b);                                                              \
2282   else                                                                                                         \
2283     return impl->list_sort_ascending ? 1 : -1;                                                                 \
2284                                                                                                                \
2285   if (dir_a != dir_b)                                                                                          \
2286     return impl->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */
2287
2288 /* Sort callback for the filename column */
2289 static gint
2290 name_sort_func (GtkTreeModel *model,
2291                 GtkTreeIter  *a,
2292                 GtkTreeIter  *b,
2293                 gpointer      user_data)
2294 {
2295   COMPARE_DIRECTORIES;
2296   else
2297     return strcmp (gtk_file_info_get_display_key (info_a), gtk_file_info_get_display_key (info_b));
2298 }
2299
2300 /* Sort callback for the size column */
2301 static gint
2302 size_sort_func (GtkTreeModel *model,
2303                 GtkTreeIter  *a,
2304                 GtkTreeIter  *b,
2305                 gpointer      user_data)
2306 {
2307   COMPARE_DIRECTORIES;
2308   else
2309     {
2310       gint64 size_a = gtk_file_info_get_size (info_a);
2311       gint64 size_b = gtk_file_info_get_size (info_b);
2312
2313       return size_a > size_b ? -1 : (size_a == size_b ? 0 : 1);
2314     }
2315 }
2316
2317 /* Sort callback for the mtime column */
2318 static gint
2319 mtime_sort_func (GtkTreeModel *model,
2320                  GtkTreeIter  *a,
2321                  GtkTreeIter  *b,
2322                  gpointer      user_data)
2323 {
2324   COMPARE_DIRECTORIES;
2325   else
2326     {
2327       GtkFileTime ta = gtk_file_info_get_modification_time (info_a);
2328       GtkFileTime tb = gtk_file_info_get_modification_time (info_b);
2329
2330       return ta > tb ? -1 : (ta == tb ? 0 : 1);
2331     }
2332 }
2333
2334 /* Callback used when the sort column changes.  We cache the sort order for use
2335  * in name_sort_func().
2336  */
2337 static void
2338 list_sort_column_changed_cb (GtkTreeSortable       *sortable,
2339                              GtkFileChooserDefault *impl)
2340 {
2341   GtkSortType sort_type;
2342
2343   if (gtk_tree_sortable_get_sort_column_id (sortable, NULL, &sort_type))
2344     impl->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
2345 }
2346
2347 /* Gets rid of the old list model and creates a new one for the current folder */
2348 static void
2349 set_list_model (GtkFileChooserDefault *impl)
2350 {
2351   if (impl->list_model)
2352     {
2353       g_object_unref (impl->list_model);
2354       g_object_unref (impl->sort_model);
2355     }
2356
2357   impl->list_model = _gtk_file_system_model_new (impl->file_system,
2358                                                  impl->current_folder, 0,
2359                                                  GTK_FILE_INFO_ALL);
2360   _gtk_file_system_model_set_show_hidden (impl->list_model, impl->show_hidden);
2361   install_list_model_filter (impl);
2362
2363   impl->sort_model = (GtkTreeModelSort *)gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (impl->list_model));
2364   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, name_sort_func, impl, NULL);
2365   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_SIZE, size_sort_func, impl, NULL);
2366   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_MTIME, mtime_sort_func, impl, NULL);
2367   gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->sort_model), NULL, NULL, NULL);
2368   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, GTK_SORT_ASCENDING);
2369   impl->list_sort_ascending = TRUE;
2370
2371   g_signal_connect (impl->sort_model, "sort_column_changed",
2372                     G_CALLBACK (list_sort_column_changed_cb), impl);
2373
2374   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->list),
2375                            GTK_TREE_MODEL (impl->sort_model));
2376   gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->list));
2377   gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->list),
2378                                    GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
2379 }
2380
2381 /* Gets rid of the old folder tree model and creates a new one for the volume
2382  * corresponding to the specified path.
2383  */
2384 static void
2385 set_tree_model (GtkFileChooserDefault *impl, const GtkFilePath *path)
2386 {
2387   GtkFileSystemVolume *volume;
2388   GtkFilePath *base_path, *parent_path;
2389
2390   base_path = NULL;
2391   
2392   volume = gtk_file_system_get_volume_for_path (impl->file_system, path);
2393   
2394   if (volume)
2395     base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
2396   
2397   if (base_path == NULL)
2398     {
2399       base_path = gtk_file_path_copy (path);
2400       while (gtk_file_system_get_parent (impl->file_system,
2401                                          base_path,
2402                                          &parent_path,
2403                                          NULL) &&
2404              parent_path != NULL)
2405         {
2406           gtk_file_path_free (base_path);
2407           base_path = parent_path;
2408         }
2409     }
2410
2411   if (impl->current_volume_path && gtk_file_path_compare (base_path, impl->current_volume_path) == 0)
2412     goto out;
2413
2414   if (impl->tree_model)
2415     g_object_unref (impl->tree_model);
2416
2417   impl->current_volume_path = gtk_file_path_copy (base_path);
2418
2419   impl->tree_model = _gtk_file_system_model_new (impl->file_system, impl->current_volume_path, -1,
2420                                                  GTK_FILE_INFO_DISPLAY_NAME);
2421   _gtk_file_system_model_set_show_files (impl->tree_model, FALSE);
2422   _gtk_file_system_model_set_show_hidden (impl->tree_model, impl->show_hidden);
2423
2424   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->tree),
2425                            GTK_TREE_MODEL (impl->tree_model));
2426
2427  out:
2428
2429   gtk_file_path_free (base_path);
2430   if (volume) 
2431     gtk_file_system_volume_free (impl->file_system, volume);
2432 }
2433
2434 static void
2435 update_chooser_entry (GtkFileChooserDefault *impl)
2436 {
2437   GtkTreeSelection *selection;
2438   const GtkFileInfo *info;
2439   GtkTreeIter iter;
2440   GtkTreeIter child_iter;
2441
2442   if (impl->action != GTK_FILE_CHOOSER_ACTION_SAVE)
2443     return;
2444
2445   g_assert (!impl->select_multiple);
2446   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
2447
2448   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
2449     return;
2450
2451   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
2452                                                   &child_iter,
2453                                                   &iter);
2454
2455   info = _gtk_file_system_model_get_info (impl->list_model, &child_iter);
2456
2457   if (!gtk_file_info_get_is_folder (info))
2458     gtk_entry_set_text (GTK_ENTRY (impl->entry),
2459                         gtk_file_info_get_display_name (info));
2460 }
2461
2462 static void
2463 gtk_file_chooser_default_set_current_folder (GtkFileChooser    *chooser,
2464                                              const GtkFilePath *path)
2465 {
2466   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2467
2468   if (impl->current_folder)
2469     gtk_file_path_free (impl->current_folder);
2470
2471   impl->current_folder = gtk_file_path_copy (path);
2472
2473   /* Change the current folder label */
2474   gtk_path_bar_set_path (GTK_PATH_BAR (impl->path_bar), path, impl->file_system, NULL);
2475
2476   /* Update the folder tree */
2477
2478   if (!impl->changing_folder)
2479     {
2480       impl->changing_folder = TRUE;
2481       set_tree_model (impl, impl->current_folder);
2482       _gtk_file_system_model_path_do (impl->tree_model, path,
2483                                       expand_and_select_func, impl);
2484       impl->changing_folder = FALSE;
2485     }
2486
2487   /* Create a new list model */
2488   set_list_model (impl);
2489
2490   /* Refresh controls */
2491
2492   shortcuts_unselect_all (impl);
2493
2494   g_signal_emit_by_name (impl, "current-folder-changed", 0);
2495
2496   check_preview_change (impl);
2497   bookmarks_check_add_sensitivity (impl);
2498
2499   g_signal_emit_by_name (impl, "selection-changed", 0);
2500 }
2501
2502 static GtkFilePath *
2503 gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser)
2504 {
2505   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2506
2507   return gtk_file_path_copy (impl->current_folder);
2508 }
2509
2510 static void
2511 gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
2512                                            const gchar    *name)
2513 {
2514   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2515
2516   g_return_if_fail (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE);
2517
2518   gtk_entry_set_text (GTK_ENTRY (impl->entry), name);
2519 }
2520
2521 static void
2522 select_func (GtkFileSystemModel *model,
2523              GtkTreePath        *path,
2524              GtkTreeIter        *iter,
2525              gpointer            user_data)
2526 {
2527   GtkFileChooserDefault *impl = user_data;
2528   GtkTreeView *tree_view = GTK_TREE_VIEW (impl->list);
2529   GtkTreePath *sorted_path;
2530
2531   sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model, path);
2532   gtk_tree_view_set_cursor (tree_view, sorted_path, NULL, FALSE);
2533   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->tree), sorted_path, NULL, TRUE, 0.3, 0.0);
2534   gtk_tree_path_free (sorted_path);
2535 }
2536
2537 static void
2538 gtk_file_chooser_default_select_path (GtkFileChooser    *chooser,
2539                                       const GtkFilePath *path)
2540 {
2541   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2542   GtkFilePath *parent_path;
2543   GError *error;
2544
2545   error = NULL;
2546   if (!gtk_file_system_get_parent (impl->file_system, path, &parent_path, &error))
2547     {
2548       error_getting_info_dialog (impl, path, error);
2549       return;
2550     }
2551
2552   if (!parent_path)
2553     {
2554       _gtk_file_chooser_set_current_folder_path (chooser, path);
2555     }
2556   else
2557     {
2558       _gtk_file_chooser_set_current_folder_path (chooser, parent_path);
2559       gtk_file_path_free (parent_path);
2560       _gtk_file_system_model_path_do (impl->list_model, path,
2561                                       select_func, impl);
2562     }
2563 }
2564
2565 static void
2566 unselect_func (GtkFileSystemModel *model,
2567                GtkTreePath        *path,
2568                GtkTreeIter        *iter,
2569                gpointer            user_data)
2570 {
2571   GtkFileChooserDefault *impl = user_data;
2572   GtkTreeView *tree_view = GTK_TREE_VIEW (impl->list);
2573   GtkTreePath *sorted_path;
2574
2575   sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model,
2576                                                                 path);
2577   gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (tree_view),
2578                                     sorted_path);
2579   gtk_tree_path_free (sorted_path);
2580 }
2581
2582 static void
2583 gtk_file_chooser_default_unselect_path (GtkFileChooser    *chooser,
2584                                         const GtkFilePath *path)
2585 {
2586   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2587
2588   _gtk_file_system_model_path_do (impl->list_model, path,
2589                                  unselect_func, impl);
2590 }
2591
2592 static void
2593 gtk_file_chooser_default_select_all (GtkFileChooser *chooser)
2594 {
2595   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2596   if (impl->select_multiple)
2597     {
2598       GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
2599       gtk_tree_selection_select_all (selection);
2600     }
2601 }
2602
2603 static void
2604 gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser)
2605 {
2606   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2607   GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
2608
2609   gtk_tree_selection_unselect_all (selection);
2610 }
2611
2612 struct get_paths_closure {
2613   GtkFileChooserDefault *impl;
2614   GSList *result;
2615   GtkFilePath *path_from_entry;
2616 };
2617
2618 static void
2619 get_paths_foreach (GtkTreeModel *model,
2620                    GtkTreePath  *path,
2621                    GtkTreeIter  *iter,
2622                    gpointer      data)
2623 {
2624   struct get_paths_closure *info;
2625   const GtkFilePath *file_path;
2626   GtkFileSystemModel *fs_model;
2627   GtkTreeIter sel_iter;
2628
2629   info = data;
2630
2631   if (info->impl->folder_mode)
2632     {
2633       fs_model = info->impl->tree_model;
2634       sel_iter = *iter;
2635     }
2636   else
2637     {
2638       fs_model = info->impl->list_model;
2639       gtk_tree_model_sort_convert_iter_to_child_iter (info->impl->sort_model, &sel_iter, iter);
2640     }
2641
2642   file_path = _gtk_file_system_model_get_path (fs_model, &sel_iter);
2643
2644   if (!info->path_from_entry
2645       || gtk_file_path_compare (info->path_from_entry, file_path) != 0)
2646     info->result = g_slist_prepend (info->result, gtk_file_path_copy (file_path));
2647 }
2648
2649 static GSList *
2650 gtk_file_chooser_default_get_paths (GtkFileChooser *chooser)
2651 {
2652   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2653   struct get_paths_closure info;
2654
2655   info.impl = impl;
2656   info.result = NULL;
2657   info.path_from_entry = NULL;
2658
2659   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
2660     {
2661       const char *filename;
2662
2663       filename = gtk_entry_get_text (GTK_ENTRY (impl->entry));
2664
2665       if (filename != NULL && filename[0] != '\0')
2666         {
2667           GtkFilePath *selected;
2668           GError *error = NULL;
2669
2670           selected = gtk_file_system_make_path (impl->file_system, impl->current_folder, filename, &error);
2671
2672           if (!selected)
2673             {
2674               error_building_filename_dialog (impl, impl->current_folder, filename, error);
2675               return NULL;
2676             }
2677
2678           info.path_from_entry = selected;
2679         }
2680     }
2681
2682   if (!info.path_from_entry || impl->select_multiple)
2683     {
2684       GtkTreeSelection *selection;
2685
2686       selection = NULL;
2687
2688       if (impl->folder_mode)
2689         {
2690           if (impl->tree_model)
2691             selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->tree));
2692         }
2693       else
2694         {
2695           if (impl->sort_model)
2696             selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
2697         }
2698
2699       if (selection)
2700         gtk_tree_selection_selected_foreach (selection, get_paths_foreach, &info);
2701     }
2702
2703   if (info.path_from_entry)
2704     info.result = g_slist_prepend (info.result, info.path_from_entry);
2705
2706   return g_slist_reverse (info.result);
2707 }
2708
2709 static GtkFilePath *
2710 gtk_file_chooser_default_get_preview_path (GtkFileChooser *chooser)
2711 {
2712   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2713
2714   if (impl->preview_path)
2715     return gtk_file_path_copy (impl->preview_path);
2716   else
2717     return NULL;
2718 }
2719
2720 static GtkFileSystem *
2721 gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser)
2722 {
2723   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2724
2725   return impl->file_system;
2726 }
2727
2728 /* Shows or hides the filter widgets */
2729 static void
2730 toolbar_show_filters (GtkFileChooserDefault *impl,
2731                       gboolean               show)
2732 {
2733   if (show)
2734     gtk_widget_show (impl->filter_combo);
2735   else
2736     gtk_widget_hide (impl->filter_combo);
2737 }
2738
2739 static void
2740 gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
2741                                      GtkFileFilter  *filter)
2742 {
2743   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2744   const gchar *name;
2745
2746   if (g_slist_find (impl->filters, filter))
2747     {
2748       g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
2749       return;
2750     }
2751
2752   g_object_ref (filter);
2753   gtk_object_sink (GTK_OBJECT (filter));
2754   impl->filters = g_slist_append (impl->filters, filter);
2755
2756   name = gtk_file_filter_get_name (filter);
2757   if (!name)
2758     name = "Untitled filter";   /* Place-holder, doesn't need to be marked for translation */
2759
2760   gtk_combo_box_append_text (GTK_COMBO_BOX (impl->filter_combo), name);
2761
2762   if (!g_slist_find (impl->filters, impl->current_filter))
2763     set_current_filter (impl, filter);
2764
2765   toolbar_show_filters (impl, TRUE);
2766 }
2767
2768 static void
2769 gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
2770                                         GtkFileFilter  *filter)
2771 {
2772   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2773   GtkTreeModel *model;
2774   GtkTreeIter iter;
2775   gint filter_index;
2776
2777   filter_index = g_slist_index (impl->filters, filter);
2778
2779   if (filter_index < 0)
2780     {
2781       g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
2782       return;
2783     }
2784
2785   impl->filters = g_slist_remove (impl->filters, filter);
2786
2787   if (filter == impl->current_filter)
2788     {
2789       if (impl->filters)
2790         set_current_filter (impl, impl->filters->data);
2791       else
2792         set_current_filter (impl, NULL);
2793     }
2794
2795   /* Remove row from the combo box */
2796   model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->filter_combo));
2797   gtk_tree_model_iter_nth_child  (model, &iter, NULL, filter_index);
2798   gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
2799
2800   g_object_unref (filter);
2801
2802   if (!impl->filters)
2803     toolbar_show_filters (impl, FALSE);
2804 }
2805
2806 static GSList *
2807 gtk_file_chooser_default_list_filters (GtkFileChooser *chooser)
2808 {
2809   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2810
2811   return g_slist_copy (impl->filters);
2812 }
2813
2814 /* Returns the position in the shortcuts tree where the nth specified shortcut would appear */
2815 static int
2816 shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl,
2817                                        int                    pos)
2818 {
2819   return pos + shortcuts_get_index (impl, SHORTCUTS_SHORTCUTS);
2820 }
2821
2822 static gboolean
2823 gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser    *chooser,
2824                                               const GtkFilePath *path,
2825                                               GError           **error)
2826 {
2827   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2828   gboolean result;
2829   int pos;
2830
2831   pos = shortcuts_get_pos_for_shortcut_folder (impl, impl->num_shortcuts);
2832
2833   result = shortcuts_insert_path (impl, pos, FALSE, NULL, path, NULL, FALSE, error);
2834
2835   if (result)
2836     impl->num_shortcuts++;
2837
2838   return result;
2839 }
2840
2841 static gboolean
2842 gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
2843                                                  const GtkFilePath *path,
2844                                                  GError           **error)
2845 {
2846   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2847   int pos;
2848   GtkTreeIter iter;
2849   int i;
2850
2851   if (impl->num_shortcuts == 0)
2852     goto out;
2853
2854   pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
2855   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
2856     g_assert_not_reached ();
2857
2858   for (i = 0; i < impl->num_shortcuts; i++)
2859     {
2860       GtkFilePath *shortcut;
2861
2862       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &shortcut, -1);
2863       g_assert (shortcut != NULL);
2864
2865       if (gtk_file_path_compare (shortcut, path) == 0)
2866         {
2867           /* The other columns are freed by the GtkTreeStore */
2868           gtk_file_path_free (shortcut);
2869           gtk_list_store_remove (impl->shortcuts_model, &iter);
2870           impl->num_shortcuts--;
2871           return TRUE;
2872         }
2873
2874       if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
2875         g_assert_not_reached ();
2876     }
2877
2878  out:
2879
2880   g_set_error (error,
2881                GTK_FILE_CHOOSER_ERROR,
2882                GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
2883                _("shortcut %s does not exist"),
2884                gtk_file_path_get_string (path));
2885
2886   return FALSE;
2887 }
2888
2889 static GSList *
2890 gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser)
2891 {
2892   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2893   int pos;
2894   GtkTreeIter iter;
2895   int i;
2896   GSList *list;
2897
2898   pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
2899   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
2900     g_assert_not_reached ();
2901
2902   list = NULL;
2903
2904   for (i = 0; i < impl->num_shortcuts; i++)
2905     {
2906       GtkFilePath *shortcut;
2907
2908       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &shortcut, -1);
2909       g_assert (shortcut != NULL);
2910
2911       list = g_slist_prepend (list, gtk_file_path_copy (shortcut));
2912
2913       if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
2914         g_assert_not_reached ();
2915     }
2916
2917   return g_slist_reverse (list);
2918 }
2919
2920 static void
2921 set_current_filter (GtkFileChooserDefault *impl,
2922                     GtkFileFilter         *filter)
2923 {
2924   if (impl->current_filter != filter)
2925     {
2926       int filter_index;
2927
2928       /* If we have filters, new filter must be one of them
2929        */
2930       filter_index = g_slist_index (impl->filters, filter);
2931       if (impl->filters && filter_index < 0)
2932         return;
2933
2934       if (impl->current_filter)
2935         g_object_unref (impl->current_filter);
2936       impl->current_filter = filter;
2937       if (impl->current_filter)
2938         {
2939           g_object_ref (impl->current_filter);
2940           gtk_object_sink (GTK_OBJECT (filter));
2941         }
2942
2943       if (impl->filters)
2944         gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
2945                                   filter_index);
2946
2947       install_list_model_filter (impl);
2948
2949       g_object_notify (G_OBJECT (impl), "filter");
2950     }
2951 }
2952
2953 static void
2954 open_and_close (GtkTreeView *tree_view,
2955                 GtkTreePath *target_path)
2956 {
2957   GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
2958   GtkTreeIter iter;
2959   GtkTreePath *path;
2960
2961   path = gtk_tree_path_new ();
2962   gtk_tree_path_append_index (path, 0);
2963
2964   gtk_tree_model_get_iter (model, &iter, path);
2965
2966   while (TRUE)
2967     {
2968       if (gtk_tree_path_is_ancestor (path, target_path) ||
2969           gtk_tree_path_compare (path, target_path) == 0)
2970         {
2971           GtkTreeIter child_iter;
2972           gtk_tree_view_expand_row (tree_view, path, FALSE);
2973           if (gtk_tree_model_iter_children (model, &child_iter, &iter))
2974             {
2975               iter = child_iter;
2976               gtk_tree_path_down (path);
2977               goto next;
2978             }
2979         }
2980       else
2981         gtk_tree_view_collapse_row (tree_view, path);
2982
2983       while (TRUE)
2984         {
2985           GtkTreeIter parent_iter;
2986           GtkTreeIter next_iter;
2987
2988           next_iter = iter;
2989           if (gtk_tree_model_iter_next (model, &next_iter))
2990             {
2991               iter = next_iter;
2992               gtk_tree_path_next (path);
2993               goto next;
2994             }
2995
2996           if (!gtk_tree_model_iter_parent (model, &parent_iter, &iter))
2997             goto out;
2998
2999           iter = parent_iter;
3000           gtk_tree_path_up (path);
3001         }
3002     next:
3003       ;
3004     }
3005
3006  out:
3007   gtk_tree_path_free (path);
3008 }
3009
3010 static void
3011 filter_combo_changed (GtkComboBox           *combo_box,
3012                       GtkFileChooserDefault *impl)
3013 {
3014   gint new_index = gtk_combo_box_get_active (combo_box);
3015   GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
3016
3017   set_current_filter (impl, new_filter);
3018 }
3019
3020 static void
3021 check_preview_change (GtkFileChooserDefault *impl)
3022 {
3023   const GtkFilePath *new_path = NULL;
3024
3025   /* FIXME #132255: Fixing preview for multiple selection involves getting the
3026    * full selection and diffing to find out what the most recently selected file
3027    * is; there is logic in GtkFileSelection that probably can be
3028    * copied.
3029    */
3030   if (impl->sort_model && !impl->select_multiple)
3031     {
3032       GtkTreeSelection *selection;
3033       GtkTreeIter iter;
3034
3035       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
3036       if (gtk_tree_selection_get_selected (selection, NULL, &iter))
3037         {
3038           GtkTreeIter child_iter;
3039
3040           gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
3041                                                           &child_iter, &iter);
3042
3043           new_path = _gtk_file_system_model_get_path (impl->list_model, &child_iter);
3044         }
3045     }
3046
3047   if (new_path != impl->preview_path &&
3048       !(new_path && impl->preview_path &&
3049         gtk_file_path_compare (new_path, impl->preview_path) == 0))
3050     {
3051       if (impl->preview_path)
3052         gtk_file_path_free (impl->preview_path);
3053
3054       if (new_path)
3055         impl->preview_path = gtk_file_path_copy (new_path);
3056       else
3057         impl->preview_path = NULL;
3058
3059       g_signal_emit_by_name (impl, "update-preview");
3060     }
3061 }
3062
3063 static void
3064 tree_selection_changed (GtkTreeSelection      *selection,
3065                         GtkFileChooserDefault *impl)
3066 {
3067   GtkTreeIter iter;
3068   const GtkFilePath *file_path;
3069   GtkTreePath *path;
3070
3071   /* FIXME #132255: Fixing this for multiple selection involves getting the full
3072    * selection and diffing to find out what the most recently selected file is;
3073    * there is logic in GtkFileSelection that probably can be copied;
3074    * check_preview_change() is similar.
3075    */
3076   if (impl->select_multiple
3077       || !gtk_tree_selection_get_selected (selection, NULL, &iter))
3078     return;
3079
3080   file_path = _gtk_file_system_model_get_path (impl->tree_model, &iter);
3081   if (impl->current_folder && gtk_file_path_compare (file_path, impl->current_folder) == 0)
3082     return;
3083
3084   /* Close the tree up to only the parents of the newly selected
3085    * node and it's immediate children are visible.
3086    */
3087   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->tree_model), &iter);
3088   open_and_close (GTK_TREE_VIEW (impl->tree), path);
3089   gtk_tree_path_free (path);
3090
3091   if (!impl->changing_folder)
3092     _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), file_path);
3093 }
3094
3095 /* Activates a volume by mounting it if necessary and then switching to its
3096  * base path.
3097  */
3098 static void
3099 shortcuts_activate_volume (GtkFileChooserDefault *impl,
3100                            GtkFileSystemVolume   *volume)
3101 {
3102   GtkFilePath *path;
3103
3104   if (!gtk_file_system_volume_get_is_mounted (impl->file_system, volume))
3105     {
3106       GError *error;
3107
3108       error = NULL;
3109       if (!gtk_file_system_volume_mount (impl->file_system, volume, &error))
3110         {
3111           char *msg;
3112
3113           msg = g_strdup_printf ("Could not mount %s:\n%s",
3114                                  gtk_file_system_volume_get_display_name (impl->file_system, volume),
3115                                  error->message);
3116           error_message (impl, msg);
3117           g_free (msg);
3118           g_error_free (error);
3119
3120           return;
3121         }
3122     }
3123
3124   path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
3125   _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), path);
3126   gtk_file_path_free (path);
3127 }
3128
3129 /* Callback used when a row in the shortcuts list is activated */
3130 static void
3131 shortcuts_row_activated_cb (GtkTreeView           *tree_view,
3132                             GtkTreePath           *path,
3133                             GtkTreeViewColumn     *column,
3134                             GtkFileChooserDefault *impl)
3135 {
3136   GtkTreeIter iter;
3137   int selected, start_row;
3138   gpointer data;
3139
3140   if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
3141     return;
3142
3143   selected = *gtk_tree_path_get_indices (path);
3144
3145   if (selected == shortcuts_get_index (impl, SHORTCUTS_SEPARATOR))
3146     return;
3147
3148   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &data, -1);
3149
3150   start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
3151   if (selected >= start_row && selected < start_row + impl->num_volumes)
3152     {
3153       GtkFileSystemVolume *volume;
3154
3155       volume = data;
3156       shortcuts_activate_volume (impl, volume);
3157     }
3158   else
3159     {
3160       GtkFilePath *file_path;
3161
3162       file_path = data;
3163       _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), file_path);
3164     }
3165 }
3166
3167 static gboolean
3168 shortcuts_select_func  (GtkTreeSelection  *selection,
3169                         GtkTreeModel      *model,
3170                         GtkTreePath       *path,
3171                         gboolean           path_currently_selected,
3172                         gpointer           data)
3173 {
3174   GtkFileChooserDefault *impl = data;
3175
3176   return (*gtk_tree_path_get_indices (path) != shortcuts_get_index (impl, SHORTCUTS_SEPARATOR));
3177 }
3178
3179 static void
3180 list_selection_changed (GtkTreeSelection      *selection,
3181                         GtkFileChooserDefault *impl)
3182 {
3183   /* See if we are in the new folder editable row for Save mode */
3184   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
3185     {
3186       GtkTreeSelection *selection;
3187       GtkTreeIter iter, child_iter;
3188       const GtkFileInfo *info;
3189
3190       g_assert (!impl->select_multiple);
3191       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
3192       if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
3193         return;
3194
3195       gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
3196                                                       &child_iter,
3197                                                       &iter);
3198
3199       info = _gtk_file_system_model_get_info (impl->list_model, &child_iter);
3200       if (!info)
3201         return; /* We are on the editable row for New Folder */
3202     }
3203
3204   update_chooser_entry (impl);
3205   check_preview_change (impl);
3206   bookmarks_check_add_sensitivity (impl);
3207
3208   g_signal_emit_by_name (impl, "selection-changed", 0);
3209 }
3210
3211 /* Callback used when a row in the file list is activated */
3212 static void
3213 list_row_activated (GtkTreeView           *tree_view,
3214                     GtkTreePath           *path,
3215                     GtkTreeViewColumn     *column,
3216                     GtkFileChooserDefault *impl)
3217 {
3218   GtkTreeIter iter, child_iter;
3219   const GtkFileInfo *info;
3220
3221   if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
3222     return;
3223
3224   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
3225
3226   info = _gtk_file_system_model_get_info (impl->list_model, &child_iter);
3227
3228   if (gtk_file_info_get_is_folder (info))
3229     {
3230       const GtkFilePath *file_path;
3231
3232       file_path = _gtk_file_system_model_get_path (impl->list_model, &child_iter);
3233       _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), file_path);
3234
3235       return;
3236     }
3237
3238   g_signal_emit_by_name (impl, "file-activated");
3239 }
3240
3241 static void
3242 path_bar_clicked (GtkPathBar            *path_bar,
3243                   GtkFilePath           *file_path,
3244                   GtkFileChooserDefault *impl)
3245 {
3246   _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), file_path);
3247 }
3248
3249 static const GtkFileInfo *
3250 get_list_file_info (GtkFileChooserDefault *impl,
3251                     GtkTreeIter           *iter)
3252 {
3253   GtkTreeIter child_iter;
3254
3255   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
3256                                                   &child_iter,
3257                                                   iter);
3258
3259   return _gtk_file_system_model_get_info (impl->list_model, &child_iter);
3260 }
3261
3262 static void
3263 tree_name_data_func (GtkTreeViewColumn *tree_column,
3264                      GtkCellRenderer   *cell,
3265                      GtkTreeModel      *tree_model,
3266                      GtkTreeIter       *iter,
3267                      gpointer           data)
3268 {
3269   GtkFileChooserDefault *impl = data;
3270   const GtkFileInfo *info = _gtk_file_system_model_get_info (impl->tree_model, iter);
3271
3272   if (info)
3273     {
3274       g_object_set (cell,
3275                     "text", gtk_file_info_get_display_name (info),
3276                     NULL);
3277     }
3278 }
3279
3280 static void
3281 list_icon_data_func (GtkTreeViewColumn *tree_column,
3282                      GtkCellRenderer   *cell,
3283                      GtkTreeModel      *tree_model,
3284                      GtkTreeIter       *iter,
3285                      gpointer           data)
3286 {
3287   GtkFileChooserDefault *impl = data;
3288   GtkTreeIter child_iter;
3289   const GtkFilePath *path;
3290   GdkPixbuf *pixbuf;
3291
3292   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
3293                                                   &child_iter,
3294                                                   iter);
3295   path = _gtk_file_system_model_get_path (impl->list_model, &child_iter);
3296   if (!path)
3297     return;
3298
3299   /* FIXME: NULL GError */
3300   pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl), ICON_SIZE, NULL);
3301   g_object_set (cell,
3302                 "pixbuf", pixbuf,
3303                 NULL);
3304
3305   if (pixbuf)
3306     g_object_unref (pixbuf);
3307 }
3308
3309 /* Sets a cellrenderer's text, making it bold if the GtkFileInfo is a folder */
3310 static void
3311 set_cell_text_bold_if_folder (const GtkFileInfo *info, GtkCellRenderer *cell, const char *text)
3312 {
3313   g_object_set (cell,
3314                 "text", text,
3315                 "weight", gtk_file_info_get_is_folder (info) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
3316                 NULL);
3317 }
3318
3319 static void
3320 list_name_data_func (GtkTreeViewColumn *tree_column,
3321                      GtkCellRenderer   *cell,
3322                      GtkTreeModel      *tree_model,
3323                      GtkTreeIter       *iter,
3324                      gpointer           data)
3325 {
3326   GtkFileChooserDefault *impl = data;
3327   const GtkFileInfo *info = get_list_file_info (impl, iter);
3328
3329   if (!info)
3330     {
3331       g_object_set (cell,
3332                     "text", _("Type name of new folder"),
3333                     NULL);
3334       return;
3335     }
3336
3337   set_cell_text_bold_if_folder (info, cell, gtk_file_info_get_display_name (info));
3338 }
3339
3340 #if 0
3341 static void
3342 list_size_data_func (GtkTreeViewColumn *tree_column,
3343                      GtkCellRenderer   *cell,
3344                      GtkTreeModel      *tree_model,
3345                      GtkTreeIter       *iter,
3346                      gpointer           data)
3347 {
3348   GtkFileChooserDefault *impl = data;
3349   const GtkFileInfo *info = get_list_file_info (impl, iter);
3350   gint64 size;
3351   gchar *str;
3352
3353   if (!info || gtk_file_info_get_is_folder (info))
3354     return;
3355
3356   size = gtk_file_info_get_size (info);
3357
3358   if (size < (gint64)1024)
3359     str = g_strdup_printf (ngettext ("%d byte", "%d bytes", (gint)size), (gint)size);
3360   else if (size < (gint64)1024*1024)
3361     str = g_strdup_printf (_("%.1f K"), size / (1024.));
3362   else if (size < (gint64)1024*1024*1024)
3363     str = g_strdup_printf (_("%.1f M"), size / (1024.*1024.));
3364   else
3365     str = g_strdup_printf (_("%.1f G"), size / (1024.*1024.*1024.));
3366
3367   g_object_set (cell,
3368                 "text", str,
3369                 NULL);
3370
3371   g_free (str);
3372 }
3373 #endif
3374
3375 /* Tree column data callback for the file list; fetches the mtime of a file */
3376 static void
3377 list_mtime_data_func (GtkTreeViewColumn *tree_column,
3378                       GtkCellRenderer   *cell,
3379                       GtkTreeModel      *tree_model,
3380                       GtkTreeIter       *iter,
3381                       gpointer           data)
3382 {
3383   GtkFileChooserDefault *impl;
3384   const GtkFileInfo *info;
3385   GtkFileTime time_mtime, time_now;
3386   GDate mtime, now;
3387   int days_diff;
3388   char buf[256];
3389
3390   impl = data;
3391
3392   info = get_list_file_info (impl, iter);
3393   if (!info)
3394     {
3395       g_object_set (cell,
3396                     "text", "",
3397                     NULL);
3398       return;
3399     }
3400
3401   time_mtime = gtk_file_info_get_modification_time (info);
3402   g_date_set_time (&mtime, (GTime) time_mtime);
3403
3404   time_now = (GTime ) time (NULL);
3405   g_date_set_time (&now, (GTime) time_now);
3406
3407   days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
3408
3409   if (days_diff == 0)
3410     strcpy (buf, _("Today"));
3411   else if (days_diff == 1)
3412     strcpy (buf, _("Yesterday"));
3413   else
3414     {
3415       char *format;
3416
3417       if (days_diff > 1 && days_diff < 7)
3418         format = "%A"; /* Days from last week */
3419       else
3420         format = _("%d/%b/%Y"); /* Any other date */
3421
3422       if (g_date_strftime (buf, sizeof (buf), format, &mtime) == 0)
3423         strcpy (buf, _("Unknown"));
3424     }
3425
3426   set_cell_text_bold_if_folder (info, cell, buf);
3427 }
3428
3429 GtkWidget *
3430 _gtk_file_chooser_default_new (const char *file_system)
3431 {
3432   return  g_object_new (GTK_TYPE_FILE_CHOOSER_DEFAULT,
3433                         "file-system-backend", file_system,
3434                         NULL);
3435 }
3436
3437 static GtkWidget *
3438 location_entry_create (GtkFileChooserDefault *impl)
3439 {
3440   GtkWidget *entry;
3441
3442   entry = _gtk_file_chooser_entry_new ();
3443   gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
3444   _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (entry), impl->file_system);
3445   _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (entry), impl->current_folder);
3446
3447   return GTK_WIDGET (entry);
3448 }
3449
3450 static void
3451 update_from_entry (GtkFileChooserDefault *impl,
3452                    GtkWindow             *parent,
3453                    GtkFileChooserEntry   *chooser_entry)
3454 {
3455   const GtkFilePath *folder_path;
3456   const char *file_part;
3457
3458   folder_path = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
3459   file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
3460
3461   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN && !folder_path)
3462     {
3463       error_message_with_parent (parent,
3464                                  _("Cannot change to the folder you specified as it is an invalid path."));
3465       return;
3466     }
3467
3468   if (file_part[0] == '\0')
3469     {
3470       _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), folder_path);
3471       return;
3472     }
3473   else
3474     {
3475       GtkFileFolder *folder = NULL;
3476       GtkFilePath *subfolder_path = NULL;
3477       GtkFileInfo *info = NULL;
3478       GError *error;
3479
3480       /* If the file part is non-empty, we need to figure out if it refers to a
3481        * folder within folder. We could optimize the case here where the folder
3482        * is already loaded for one of our tree models.
3483        */
3484
3485       error = NULL;
3486       folder = gtk_file_system_get_folder (impl->file_system, folder_path, GTK_FILE_INFO_IS_FOLDER, &error);
3487
3488       if (!folder)
3489         {
3490           error_getting_info_dialog (impl, folder_path, error);
3491           return;
3492         }
3493
3494       error = NULL;
3495       subfolder_path = gtk_file_system_make_path (impl->file_system, folder_path, file_part, &error);
3496
3497       if (!subfolder_path)
3498         {
3499           char *msg;
3500
3501           msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
3502                                  gtk_file_path_get_string (folder_path),
3503                                  file_part,
3504                                  error->message);
3505           error_message (impl, msg);
3506           g_free (msg);
3507           g_object_unref (folder);
3508           return;
3509         }
3510
3511       error = NULL;
3512       info = gtk_file_folder_get_info (folder, subfolder_path, &error);
3513
3514       if (!info)
3515         {
3516 #if 0
3517           if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
3518             {
3519               g_object_unref (folder);
3520               gtk_file_path_free (subfolder_path);
3521               return;
3522             }
3523 #endif
3524           error_getting_info_dialog (impl, subfolder_path, error);
3525           g_object_unref (folder);
3526           gtk_file_path_free (subfolder_path);
3527           return;
3528         }
3529
3530       if (gtk_file_info_get_is_folder (info))
3531         _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), subfolder_path);
3532       else
3533         _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (impl), subfolder_path);
3534
3535       g_object_unref (folder);
3536       gtk_file_path_free (subfolder_path);
3537       gtk_file_info_free (info);
3538     }
3539 }
3540
3541 static void
3542 location_popup_handler (GtkFileChooserDefault *impl)
3543 {
3544   GtkWidget *dialog;
3545   GtkWidget *toplevel;
3546   GtkWidget *hbox;
3547   GtkWidget *label;
3548   GtkWidget *entry;
3549
3550   /* Create dialog */
3551
3552   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
3553   if (!GTK_WIDGET_TOPLEVEL (toplevel))
3554     toplevel = NULL;
3555
3556   dialog = gtk_dialog_new_with_buttons (_("Open Location"),
3557                                         GTK_WINDOW (toplevel),
3558                                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
3559                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3560                                         GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
3561                                         NULL);
3562   gtk_window_set_default_size (GTK_WINDOW (dialog), 300, -1);
3563   gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
3564   gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2);
3565   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
3566
3567   hbox = gtk_hbox_new (FALSE, 12);
3568   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0);
3569   gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
3570
3571   label = gtk_label_new_with_mnemonic (_("_Location:"));
3572   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
3573
3574   entry = location_entry_create (impl);
3575   gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
3576   gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
3577
3578   /* Run */
3579
3580   gtk_widget_show_all (dialog);
3581   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
3582     update_from_entry (impl, GTK_WINDOW (dialog), GTK_FILE_CHOOSER_ENTRY (entry));
3583
3584   gtk_widget_destroy (dialog);
3585 }
3586
3587 /* Handler for the "up-folder" keybinding signal */
3588 static void
3589 up_folder_handler (GtkFileChooserDefault *impl)
3590 {
3591   GtkFilePath *parent_path;
3592   GError *error;
3593
3594   error = NULL;
3595   if (gtk_file_system_get_parent (impl->file_system, impl->current_folder, &parent_path, &error))
3596     {
3597       if (parent_path) /* If we were on a root, parent_path will be NULL */
3598         {
3599           _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), parent_path);
3600           gtk_file_path_free (parent_path);
3601         }
3602     }
3603   else
3604     error_dialog (impl,
3605                   _("Could not go to the parent folder of %s:\n%s"),
3606                   impl->current_folder,
3607                   error);
3608 }
3609
3610 /* Handler for the "home-folder" keybinding signal */
3611 static void
3612 home_folder_handler (GtkFileChooserDefault *impl)
3613 {
3614   const char *home;
3615
3616   /* Should we pull this information from impl->has_home and the shortcuts data
3617    * instead?  Sounds like a bit of overkill...
3618    */
3619
3620   home = g_get_home_dir ();
3621   gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (impl), home);
3622 }