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