]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserdefault.c
16232bc35d05f87bb03c6aec6e76bab1c443b63b
[~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 "gtkprivate.h"
43 #include "gtkscrolledwindow.h"
44 #include "gtkstock.h"
45 #include "gtktable.h"
46 #include "gtktoolbar.h"
47 #include "gtktoolbutton.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 #include <string.h>
56 #include <time.h>
57
58 typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
59
60 #define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
61 #define GTK_IS_FILE_CHOOSER_DEFAULT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT))
62 #define GTK_FILE_CHOOSER_DEFAULT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
63
64 struct _GtkFileChooserDefaultClass
65 {
66   GtkVBoxClass parent_class;
67 };
68
69 struct _GtkFileChooserDefault
70 {
71   GtkVBox parent_instance;
72
73   GtkFileSystem *file_system;
74   GtkFileSystemModel *tree_model;
75   GtkTreeStore *shortcuts_model;
76   GtkFileSystemModel *list_model;
77   GtkTreeModelSort *sort_model;
78
79   GtkFileChooserAction action;
80
81   GtkFileFilter *current_filter;
82   GSList *filters;
83
84   gboolean has_home;
85   gboolean has_desktop;
86   int num_roots;
87   int num_shortcuts;
88   int num_bookmarks;
89
90   guint bookmarks_changed_id;
91   GtkTreeIter bookmarks_iter;
92
93   GtkFilePath *current_folder;
94   GtkFilePath *preview_path;
95
96   GtkToolItem *up_button;
97
98   GtkWidget *preview_frame;
99
100   GtkWidget *toolbar;
101   GtkWidget *filter_alignment;
102   GtkWidget *filter_combo;
103   GtkWidget *tree_scrollwin;
104   GtkWidget *tree;
105   GtkWidget *shortcuts_scrollwin;
106   GtkWidget *shortcuts_tree;
107   GtkWidget *add_bookmark_button;
108   GtkWidget *remove_bookmark_button;
109   GtkWidget *list_scrollwin;
110   GtkWidget *list;
111   GtkWidget *entry;
112   GtkWidget *preview_widget;
113   GtkWidget *extra_widget;
114
115   guint folder_mode : 1;
116   guint local_only : 1;
117   guint preview_widget_active : 1;
118   guint select_multiple : 1;
119   guint show_hidden : 1;
120   guint changing_folder : 1;
121   guint list_sort_ascending : 1;
122   guint bookmarks_set : 1;
123 };
124
125 /* Column numbers for the shortcuts tree.  Keep these in sync with create_shortcuts_model() */
126 enum {
127   SHORTCUTS_COL_PIXBUF,
128   SHORTCUTS_COL_NAME,
129   SHORTCUTS_COL_PATH,
130   SHORTCUTS_COL_NUM_COLUMNS
131 };
132
133 /* Column numbers for the file list */
134 enum {
135   FILE_LIST_COL_NAME,
136   FILE_LIST_COL_SIZE,
137   FILE_LIST_COL_MTIME,
138   FILE_LIST_COL_NUM_COLUMNS
139 };
140
141 /* Standard icon size */
142 /* FIXME: maybe this should correspond to the font size in the tree views... */
143 #define ICON_SIZE 20
144
145 static void gtk_file_chooser_default_class_init   (GtkFileChooserDefaultClass *class);
146 static void gtk_file_chooser_default_iface_init   (GtkFileChooserIface        *iface);
147 static void gtk_file_chooser_default_init         (GtkFileChooserDefault      *impl);
148
149 static GObject* gtk_file_chooser_default_constructor  (GType                  type,
150                                                        guint                  n_construct_properties,
151                                                        GObjectConstructParam *construct_params);
152 static void     gtk_file_chooser_default_finalize     (GObject               *object);
153 static void     gtk_file_chooser_default_set_property (GObject               *object,
154                                                        guint                  prop_id,
155                                                        const GValue          *value,
156                                                        GParamSpec            *pspec);
157 static void     gtk_file_chooser_default_get_property (GObject               *object,
158                                                        guint                  prop_id,
159                                                        GValue                *value,
160                                                        GParamSpec            *pspec);
161 static void     gtk_file_chooser_default_show_all     (GtkWidget             *widget);
162
163 static void           gtk_file_chooser_default_set_current_folder          (GtkFileChooser    *chooser,
164                                                                             const GtkFilePath *path);
165 static GtkFilePath *  gtk_file_chooser_default_get_current_folder          (GtkFileChooser    *chooser);
166 static void           gtk_file_chooser_default_set_current_name            (GtkFileChooser    *chooser,
167                                                                             const gchar       *name);
168 static void           gtk_file_chooser_default_select_path                 (GtkFileChooser    *chooser,
169                                                                             const GtkFilePath *path);
170 static void           gtk_file_chooser_default_unselect_path               (GtkFileChooser    *chooser,
171                                                                             const GtkFilePath *path);
172 static void           gtk_file_chooser_default_select_all                  (GtkFileChooser    *chooser);
173 static void           gtk_file_chooser_default_unselect_all                (GtkFileChooser    *chooser);
174 static GSList *       gtk_file_chooser_default_get_paths                   (GtkFileChooser    *chooser);
175 static GtkFilePath *  gtk_file_chooser_default_get_preview_path            (GtkFileChooser    *chooser);
176 static GtkFileSystem *gtk_file_chooser_default_get_file_system             (GtkFileChooser    *chooser);
177 static void           gtk_file_chooser_default_add_filter                  (GtkFileChooser    *chooser,
178                                                                             GtkFileFilter     *filter);
179 static void           gtk_file_chooser_default_remove_filter               (GtkFileChooser    *chooser,
180                                                                             GtkFileFilter     *filter);
181 static GSList *       gtk_file_chooser_default_list_filters                (GtkFileChooser    *chooser);
182 static gboolean       gtk_file_chooser_default_add_shortcut_folder    (GtkFileChooser    *chooser,
183                                                                        const GtkFilePath *path,
184                                                                        GError           **error);
185 static gboolean       gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
186                                                                        const GtkFilePath *path,
187                                                                        GError           **error);
188 static GSList *       gtk_file_chooser_default_list_shortcut_folders  (GtkFileChooser    *chooser);
189
190 static void set_current_filter   (GtkFileChooserDefault *impl,
191                                   GtkFileFilter         *filter);
192 static void check_preview_change (GtkFileChooserDefault *impl);
193
194 static void filter_combo_changed       (GtkComboBox           *combo_box,
195                                         GtkFileChooserDefault *impl);
196 static void tree_selection_changed     (GtkTreeSelection      *tree_selection,
197                                         GtkFileChooserDefault *impl);
198
199 static void     shortcuts_row_activated (GtkTreeView           *tree_view,
200                                          GtkTreePath           *path,
201                                          GtkTreeViewColumn     *column,
202                                          GtkFileChooserDefault *impl);
203 static gboolean shortcuts_select_func   (GtkTreeSelection      *selection,
204                                          GtkTreeModel          *model,
205                                          GtkTreePath           *path,
206                                          gboolean               path_currentlny_selected,
207                                          gpointer               data);
208
209 static void list_selection_changed     (GtkTreeSelection      *tree_selection,
210                                         GtkFileChooserDefault *impl);
211 static void list_row_activated         (GtkTreeView           *tree_view,
212                                         GtkTreePath           *path,
213                                         GtkTreeViewColumn     *column,
214                                         GtkFileChooserDefault *impl);
215 static void entry_activate             (GtkEntry              *entry,
216                                         GtkFileChooserDefault *impl);
217
218 static void tree_name_data_func (GtkTreeViewColumn *tree_column,
219                                  GtkCellRenderer   *cell,
220                                  GtkTreeModel      *tree_model,
221                                  GtkTreeIter       *iter,
222                                  gpointer           data);
223 static void list_icon_data_func (GtkTreeViewColumn *tree_column,
224                                  GtkCellRenderer   *cell,
225                                  GtkTreeModel      *tree_model,
226                                  GtkTreeIter       *iter,
227                                  gpointer           data);
228 static void list_name_data_func (GtkTreeViewColumn *tree_column,
229                                  GtkCellRenderer   *cell,
230                                  GtkTreeModel      *tree_model,
231                                  GtkTreeIter       *iter,
232                                  gpointer           data);
233 #if 0
234 static void list_size_data_func (GtkTreeViewColumn *tree_column,
235                                  GtkCellRenderer   *cell,
236                                  GtkTreeModel      *tree_model,
237                                  GtkTreeIter       *iter,
238                                  gpointer           data);
239 #endif
240 static void list_mtime_data_func (GtkTreeViewColumn *tree_column,
241                                   GtkCellRenderer   *cell,
242                                   GtkTreeModel      *tree_model,
243                                   GtkTreeIter       *iter,
244                                   gpointer           data);
245
246 static GObjectClass *parent_class;
247
248 GType
249 _gtk_file_chooser_default_get_type (void)
250 {
251   static GType file_chooser_default_type = 0;
252
253   if (!file_chooser_default_type)
254     {
255       static const GTypeInfo file_chooser_default_info =
256       {
257         sizeof (GtkFileChooserDefaultClass),
258         NULL,           /* base_init */
259         NULL,           /* base_finalize */
260         (GClassInitFunc) gtk_file_chooser_default_class_init,
261         NULL,           /* class_finalize */
262         NULL,           /* class_data */
263         sizeof (GtkFileChooserDefault),
264         0,              /* n_preallocs */
265         (GInstanceInitFunc) gtk_file_chooser_default_init,
266       };
267
268       static const GInterfaceInfo file_chooser_info =
269       {
270         (GInterfaceInitFunc) gtk_file_chooser_default_iface_init, /* interface_init */
271         NULL,                                                          /* interface_finalize */
272         NULL                                                           /* interface_data */
273       };
274
275       file_chooser_default_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserDefault",
276                                                          &file_chooser_default_info, 0);
277       g_type_add_interface_static (file_chooser_default_type,
278                                    GTK_TYPE_FILE_CHOOSER,
279                                    &file_chooser_info);
280     }
281
282   return file_chooser_default_type;
283 }
284
285 static void
286 gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
287 {
288   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
289   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
290
291   parent_class = g_type_class_peek_parent (class);
292
293   gobject_class->finalize = gtk_file_chooser_default_finalize;
294   gobject_class->constructor = gtk_file_chooser_default_constructor;
295   gobject_class->set_property = gtk_file_chooser_default_set_property;
296   gobject_class->get_property = gtk_file_chooser_default_get_property;
297
298   widget_class->show_all = gtk_file_chooser_default_show_all;
299
300   _gtk_file_chooser_install_properties (gobject_class);
301 }
302
303 static void
304 gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
305 {
306   iface->select_path = gtk_file_chooser_default_select_path;
307   iface->unselect_path = gtk_file_chooser_default_unselect_path;
308   iface->select_all = gtk_file_chooser_default_select_all;
309   iface->unselect_all = gtk_file_chooser_default_unselect_all;
310   iface->get_paths = gtk_file_chooser_default_get_paths;
311   iface->get_preview_path = gtk_file_chooser_default_get_preview_path;
312   iface->get_file_system = gtk_file_chooser_default_get_file_system;
313   iface->set_current_folder = gtk_file_chooser_default_set_current_folder;
314   iface->get_current_folder = gtk_file_chooser_default_get_current_folder;
315   iface->set_current_name = gtk_file_chooser_default_set_current_name;
316   iface->add_filter = gtk_file_chooser_default_add_filter;
317   iface->remove_filter = gtk_file_chooser_default_remove_filter;
318   iface->list_filters = gtk_file_chooser_default_list_filters;
319   iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder;
320   iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder;
321   iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders;
322 }
323
324 static void
325 gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
326 {
327   impl->folder_mode = FALSE;
328   impl->local_only = TRUE;
329   impl->preview_widget_active = TRUE;
330   impl->select_multiple = FALSE;
331   impl->show_hidden = FALSE;
332
333   gtk_box_set_spacing (GTK_BOX (impl), 12);
334 }
335
336 static void
337 gtk_file_chooser_default_finalize (GObject *object)
338 {
339   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
340
341   g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
342   impl->bookmarks_changed_id = 0;
343   g_object_unref (impl->file_system);
344
345   G_OBJECT_CLASS (parent_class)->finalize (object);
346 }
347
348 /* Shows an error dialog */
349 static void
350 error_message (GtkFileChooserDefault *impl,
351                const char            *msg)
352 {
353   GtkWidget *toplevel;
354   GtkWidget *dialog;
355
356   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
357
358   dialog = gtk_message_dialog_new (toplevel ? GTK_WINDOW (toplevel) : NULL,
359                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
360                                    GTK_MESSAGE_ERROR,
361                                    GTK_BUTTONS_CLOSE,
362                                    "%s",
363                                    msg);
364   gtk_dialog_run (GTK_DIALOG (dialog));
365   gtk_widget_destroy (dialog);
366 }
367
368 /* Shows a simple error dialog relative to a path.  Frees the GError as well. */
369 static void
370 error_dialog (GtkFileChooserDefault *impl,
371               const char            *msg,
372               const GtkFilePath     *path,
373               GError                *error)
374 {
375   char *text;
376
377   text = g_strdup_printf (msg,
378                           gtk_file_path_get_string (path),
379                           error->message);
380   error_message (impl, text);
381   g_free (text);
382   g_error_free (error);
383 }
384
385 static void
386 update_preview_widget_visibility (GtkFileChooserDefault *impl)
387 {
388   if (impl->preview_widget_active && impl->preview_widget)
389     gtk_widget_show (impl->preview_frame);
390   else
391     gtk_widget_hide (impl->preview_frame);
392 }
393
394 static void
395 set_preview_widget (GtkFileChooserDefault *impl,
396                     GtkWidget             *preview_widget)
397 {
398   if (preview_widget == impl->preview_widget)
399     return;
400
401   if (impl->preview_widget)
402     gtk_container_remove (GTK_CONTAINER (impl->preview_frame),
403                           impl->preview_widget);
404
405   impl->preview_widget = preview_widget;
406   if (impl->preview_widget)
407     {
408       gtk_widget_show (impl->preview_widget);
409       gtk_container_add (GTK_CONTAINER (impl->preview_frame),
410                          impl->preview_widget);
411     }
412
413   update_preview_widget_visibility (impl);
414 }
415
416 /* Clears the selection in the shortcuts tree */
417 static void
418 shortcuts_unselect_all (GtkFileChooserDefault *impl)
419 {
420   GtkTreeSelection *selection;
421
422   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->shortcuts_tree));
423   gtk_tree_selection_unselect_all (selection);
424 }
425
426 /* Convenience function to get the display name and icon info for a path */
427 static GtkFileInfo *
428 get_file_info (GtkFileSystem *file_system, const GtkFilePath *path, GError **error)
429 {
430   GtkFilePath *parent_path;
431   GtkFileFolder *parent_folder;
432   GtkFileInfo *info;
433
434   if (!gtk_file_system_get_parent (file_system, path, &parent_path, error))
435     return NULL;
436
437   parent_folder = gtk_file_system_get_folder (file_system, parent_path,
438 #if 0
439                                               GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_ICON,
440 #else
441                                               GTK_FILE_INFO_DISPLAY_NAME,
442 #endif
443                                               error);
444   gtk_file_path_free (parent_path);
445
446   if (!parent_folder)
447     return NULL;
448
449   info = gtk_file_folder_get_info (parent_folder, path, error);
450   g_object_unref (parent_folder);
451
452   return info;
453 }
454
455 /* Inserts a path in the shortcuts tree, making a copy of it.  A position of -1
456  * indicates the end of the tree.  If the label is NULL, then the display name
457  * of a GtkFileInfo is used.
458  */
459 static gboolean
460 shortcuts_insert_path (GtkFileChooserDefault *impl,
461                        int                    pos,
462                        const GtkFilePath     *path,
463                        gboolean               is_root,
464                        const char            *label,
465                        GError               **error)
466 {
467   GtkFileInfo *info;
468   GtkFilePath *path_copy;
469   GdkPixbuf *pixbuf;
470   GtkTreeIter iter;
471
472   /* FIXME: what if someone adds a shortcut to a root?  get_file_info() will not
473    * work in that case, I think...
474    */
475
476   if (is_root)
477     info = gtk_file_system_get_root_info (impl->file_system,
478                                           path,
479 #if 0
480                                           GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_ICON,
481 #else
482                                           GTK_FILE_INFO_DISPLAY_NAME,
483 #endif
484                                           error);
485   else
486     info = get_file_info (impl->file_system, path, error);
487
488   if (!info)
489     return FALSE;
490 #if 0
491   pixbuf = gtk_file_info_render_icon (info, impl->shortcuts_tree, ICON_SIZE);
492 #endif
493   /* FIXME: NULL GError */
494   pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl), ICON_SIZE, NULL);
495
496   gtk_tree_store_insert (impl->shortcuts_model, &iter, NULL, pos);
497   path_copy = gtk_file_path_copy (path);
498
499   if (!label)
500     label = gtk_file_info_get_display_name (info);
501
502   gtk_tree_store_set (impl->shortcuts_model, &iter,
503                       SHORTCUTS_COL_PIXBUF, pixbuf,
504                       SHORTCUTS_COL_NAME, label,
505                       SHORTCUTS_COL_PATH, path_copy,
506                       -1);
507
508   gtk_file_info_free (info);
509
510   if (pixbuf)
511     g_object_unref (pixbuf);
512
513   return TRUE;
514 }
515
516 /* Displays an error message about not being able to get information for a file.
517  * Frees the GError as well.
518  */
519 static void
520 error_getting_info_dialog (GtkFileChooserDefault *impl,
521                            const GtkFilePath     *path,
522                            GError                *error)
523 {
524   error_dialog (impl,
525                 _("Could not retrieve information about %s:\n%s"),
526                 path, error);
527 }
528
529 /* Appends an item for the user's home directory to the shortcuts model */
530 static void
531 shortcuts_append_home (GtkFileChooserDefault *impl)
532 {
533   const char *name;
534   const char *home;
535   GtkFilePath *home_path;
536   char *label;
537   GError *error;
538
539   name = g_get_user_name ();
540   label = g_strdup_printf (_("%s's Home"), name);
541
542   home = g_get_home_dir ();
543   home_path = gtk_file_system_filename_to_path (impl->file_system, home);
544
545   error = NULL;
546   impl->has_home = shortcuts_insert_path (impl, -1, home_path, FALSE, label, &error);
547   if (!impl->has_home)
548     error_getting_info_dialog (impl, home_path, error);
549
550   g_free (label);
551   gtk_file_path_free (home_path);
552 }
553
554 /* Appends the ~/Desktop directory to the shortcuts model */
555 static void
556 shortcuts_append_desktop (GtkFileChooserDefault *impl)
557 {
558   char *name;
559   GtkFilePath *path;
560   GError *error;
561
562   /* FIXME: What is the Right Way of finding the desktop directory? */
563
564   name = g_build_filename (g_get_home_dir (), _("Desktop"), NULL);
565   path = gtk_file_system_filename_to_path (impl->file_system, name);
566   g_free (name);
567
568   error = NULL;
569   impl->has_desktop = shortcuts_insert_path (impl, -1, path, FALSE, NULL, &error);
570   if (!impl->has_desktop)
571     error_getting_info_dialog (impl, path, error);
572
573   gtk_file_path_free (path);
574 }
575
576 /* Appends a list of GtkFilePath to the shortcuts model; returns how many were inserted */
577 static int
578 shortcuts_append_paths (GtkFileChooserDefault *impl,
579                         GSList                *paths,
580                         gboolean               is_file_system_root)
581 {
582   int num_inserted;
583
584   num_inserted = 0;
585
586   for (; paths; paths = paths->next)
587     {
588       GtkFilePath *path;
589       GError *error;
590
591       path = paths->data;
592       error = NULL;
593
594       if (shortcuts_insert_path (impl, -1, path, is_file_system_root, NULL, &error))
595         num_inserted++;
596       else
597         error_getting_info_dialog (impl, path, error);
598     }
599
600   return num_inserted;
601 }
602
603 /* Appends all the file system roots to the shortcuts model */
604 static void
605 shortcuts_append_file_system_roots (GtkFileChooserDefault *impl)
606 {
607   GSList *roots;
608
609   roots = gtk_file_system_list_roots (impl->file_system);
610   /* FIXME: handle the roots-changed signal on the file system */
611
612   impl->num_roots = shortcuts_append_paths (impl, roots, TRUE);
613   gtk_file_paths_free (roots);
614 }
615
616 /* Removes the bookmarks separator node and all the bookmarks from the tree
617  * model.
618  */
619 static void
620 remove_bookmark_rows (GtkFileChooserDefault *impl)
621 {
622   GtkTreePath *path;
623   GtkTreeIter iter;
624
625   if (!impl->bookmarks_set)
626     return;
627
628   /* Ugh.  Is there a better way to do this? */
629
630   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &impl->bookmarks_iter);
631
632   while (gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
633     gtk_tree_store_remove (impl->shortcuts_model, &impl->bookmarks_iter);
634
635   impl->bookmarks_set = FALSE;
636 }
637
638 /* Appends the bookmarks separator node and the bookmarks from the file system. */
639 static void
640 shortcuts_append_bookmarks (GtkFileChooserDefault *impl)
641 {
642   GSList *bookmarks;
643
644   remove_bookmark_rows (impl);
645
646   gtk_tree_store_append (impl->shortcuts_model, &impl->bookmarks_iter, NULL);
647   gtk_tree_store_set (impl->shortcuts_model, &impl->bookmarks_iter,
648                       SHORTCUTS_COL_PIXBUF, NULL,
649                       SHORTCUTS_COL_NAME, NULL,
650                       SHORTCUTS_COL_PATH, NULL,
651                       -1);
652   impl->bookmarks_set = TRUE;
653
654   bookmarks = gtk_file_system_list_bookmarks (impl->file_system);
655
656   /* FIXME: How do we know if a bookmark is a file system root? */
657   impl->num_bookmarks = shortcuts_append_paths (impl, bookmarks, FALSE);
658   gtk_file_paths_free (bookmarks);
659 }
660
661 /* Creates the GtkTreeStore used as the shortcuts model */
662 static void
663 create_shortcuts_model (GtkFileChooserDefault *impl)
664 {
665   if (impl->shortcuts_model)
666     g_object_unref (impl->shortcuts_model);
667
668   /* Keep this order in sync with the SHORCUTS_COL_* enum values */
669   impl->shortcuts_model = gtk_tree_store_new (SHORTCUTS_COL_NUM_COLUMNS,
670                                               GDK_TYPE_PIXBUF,  /* pixbuf */
671                                               G_TYPE_STRING,    /* name */
672                                               G_TYPE_POINTER);  /* path */
673
674   if (impl->file_system)
675     {
676       shortcuts_append_home (impl);
677       shortcuts_append_desktop (impl);
678       shortcuts_append_file_system_roots (impl);
679       shortcuts_append_bookmarks (impl);
680     }
681
682   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->shortcuts_tree), GTK_TREE_MODEL (impl->shortcuts_model));
683 }
684
685 /* Callback used when the "Up" toolbar button is clicked */
686 static void
687 toolbar_up_cb (GtkToolButton         *button,
688                GtkFileChooserDefault *impl)
689 {
690   GtkFilePath *parent_path;
691   GError *error;
692
693   error = NULL;
694   if (gtk_file_system_get_parent (impl->file_system, impl->current_folder, &parent_path, &error))
695     {
696       if (parent_path) /* If we were on a root, parent_path will be NULL */
697         {
698           _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), parent_path);
699           gtk_file_path_free (parent_path);
700         }
701     }
702   else
703     error_dialog (impl,
704                   _("Could not go to the parent folder of %s:\n%s"),
705                   impl->current_folder,
706                   error);
707 }
708
709 /* Appends an item to the toolbar */
710 static GtkToolItem *
711 toolbar_add_item (GtkFileChooserDefault *impl,
712                   const char            *stock_id,
713                   GCallback              callback)
714 {
715   GtkToolItem *item;
716
717   item = gtk_tool_button_new_from_stock (stock_id);
718   g_signal_connect (item, "clicked", callback, impl);
719   gtk_toolbar_insert (GTK_TOOLBAR (impl->toolbar), item, -1);
720   gtk_widget_show (GTK_WIDGET (item));
721
722   return item;
723 }
724
725 /* Creates the toolbar widget */
726 static GtkWidget *
727 toolbar_create (GtkFileChooserDefault *impl)
728 {
729   impl->toolbar = gtk_toolbar_new ();
730   gtk_toolbar_set_style (GTK_TOOLBAR (impl->toolbar), GTK_TOOLBAR_ICONS);
731
732   impl->up_button = toolbar_add_item (impl, GTK_STOCK_GO_UP, G_CALLBACK (toolbar_up_cb));
733
734   return impl->toolbar;
735 }
736
737 /* Sets the sensitivity of the toolbar buttons */
738 static void
739 toolbar_check_sensitivity (GtkFileChooserDefault *impl)
740 {
741   GtkFilePath *parent_path;
742   gboolean has_parent;
743
744   has_parent = FALSE;
745
746   /* I don't think we need to check GError here, do we? */
747   if (gtk_file_system_get_parent (impl->file_system, impl->current_folder, &parent_path, NULL))
748     {
749       if (parent_path)
750         {
751           gtk_file_path_free (parent_path);
752           has_parent = TRUE;
753         }
754     }
755
756   gtk_widget_set_sensitive (GTK_WIDGET (impl->up_button), has_parent);
757 }
758
759 /* Creates the widgets for the filter combo box */
760 static GtkWidget *
761 create_filter (GtkFileChooserDefault *impl)
762 {
763   GtkWidget *hbox;
764   GtkWidget *label;
765
766   impl->filter_alignment = gtk_alignment_new (0.0, 0.5, 0.0, 1.0);
767   gtk_alignment_set_padding (GTK_ALIGNMENT (impl->filter_alignment), 0, 6, 0, 0);
768   /* Don't show filter initially -- don't gtk_widget_show() the filter_alignment here */
769
770   hbox = gtk_hbox_new (FALSE, 6);
771   gtk_container_add (GTK_CONTAINER (impl->filter_alignment), hbox);
772   gtk_widget_show (hbox);
773
774   label = gtk_label_new_with_mnemonic (_("Files of _type:"));
775   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
776   gtk_widget_show (label);
777
778   impl->filter_combo = gtk_combo_box_new_text ();
779   gtk_box_pack_start (GTK_BOX (hbox), impl->filter_combo, FALSE, FALSE, 0);
780   gtk_widget_show (impl->filter_combo);
781
782   gtk_label_set_mnemonic_widget (GTK_LABEL (label), impl->filter_combo);
783
784   g_signal_connect (impl->filter_combo, "changed",
785                     G_CALLBACK (filter_combo_changed), impl);
786
787   return impl->filter_alignment;
788 }
789
790 /* Creates the widgets for the folder tree */
791 static GtkWidget *
792 create_folder_tree (GtkFileChooserDefault *impl)
793 {
794   GtkTreeSelection *selection;
795
796   /* Scrolled window */
797
798   impl->tree_scrollwin = gtk_scrolled_window_new (NULL, NULL);
799   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->tree_scrollwin),
800                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
801   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (impl->tree_scrollwin),
802                                        GTK_SHADOW_IN);
803   if (impl->folder_mode)
804     gtk_widget_show (impl->tree_scrollwin);
805
806   /* Tree */
807
808   impl->tree = gtk_tree_view_new ();
809   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->tree), FALSE);
810
811   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->tree));
812   g_signal_connect (selection, "changed",
813                     G_CALLBACK (tree_selection_changed), impl);
814
815   gtk_container_add (GTK_CONTAINER (impl->tree_scrollwin), impl->tree);
816   gtk_widget_show (impl->tree);
817
818   /* Model */
819
820   impl->tree_model = _gtk_file_system_model_new (impl->file_system, NULL, -1,
821                                                  GTK_FILE_INFO_DISPLAY_NAME);
822   _gtk_file_system_model_set_show_files (impl->tree_model, FALSE);
823
824   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->tree),
825                            GTK_TREE_MODEL (impl->tree_model));
826
827   /* Column */
828
829   gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (impl->tree), 0,
830                                               _("File name"),
831                                               gtk_cell_renderer_text_new (),
832                                               tree_name_data_func, impl, NULL);
833   gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->tree),
834                                    GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
835
836   return impl->tree_scrollwin;
837 }
838
839 /* Callback used when the "Add bookmark" button is clicked */
840 static void
841 add_bookmark_button_clicked_cb (GtkButton *button,
842                                 GtkFileChooserDefault *impl)
843 {
844   GError *error;
845
846   error = NULL;
847   if (!gtk_file_system_add_bookmark (impl->file_system, impl->current_folder, &error))
848     error_dialog (impl,
849                   _("Could not add bookmark for %s:\n%s"),
850                   impl->current_folder,
851                   error);
852 }
853
854 /* Callback used when the "Remove bookmark" button is clicked */
855 static void
856 remove_bookmark_button_clicked_cb (GtkButton *button,
857                                    GtkFileChooserDefault *impl)
858 {
859   GtkTreeSelection *selection;
860   GtkTreeIter iter;
861   GtkFilePath *path;
862   GError *error;
863
864   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->shortcuts_tree));
865
866   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
867     return;
868
869   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &path, -1);
870
871   error = NULL;
872   if (!gtk_file_system_remove_bookmark (impl->file_system, path, &error))
873     error_dialog (impl,
874                   _("Could not remove bookmark for %s:\n%s"),
875                   path,
876                   error);
877 }
878
879 /* Sensitize the "add bookmark" button if the current folder is not in the
880  * bookmarks list, or de-sensitize it otherwise.
881  */
882 static void
883 bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl)
884 {
885   GtkTreeIter iter;
886   gboolean exists;
887
888   exists = FALSE;
889
890   if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
891     do
892       {
893         GtkFilePath *path;
894
895         gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &path, -1);
896
897         if (path && gtk_file_path_compare (path, impl->current_folder) == 0)
898           {
899             exists = TRUE;
900             break;
901           }
902       }
903     while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter));
904
905   gtk_widget_set_sensitive (impl->add_bookmark_button, !exists);
906 }
907
908 /* Sets the sensitivity of the "remove bookmark" button depending on whether a
909  * bookmark row is selected in the shortcuts tree.
910  */
911 static void
912 bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl)
913 {
914   GtkTreeSelection *selection;
915   GtkTreeIter iter;
916   gboolean is_bookmark;
917
918   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->shortcuts_tree));
919
920   if (gtk_tree_selection_get_selected (selection, NULL, &iter))
921     {
922       GtkTreePath *bookmarks_path;
923       GtkTreePath *sel_path;
924
925       bookmarks_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model),
926                                                 &impl->bookmarks_iter);
927       sel_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
928
929       is_bookmark = (gtk_tree_path_compare (bookmarks_path, sel_path) < 0);
930
931       gtk_tree_path_free (bookmarks_path);
932       gtk_tree_path_free (sel_path);
933     }
934   else
935     is_bookmark = FALSE;
936
937   gtk_widget_set_sensitive (impl->remove_bookmark_button, is_bookmark);
938 }
939
940 /* Creates the widgets for the shortcuts and bookmarks tree */
941 static GtkWidget *
942 create_shortcuts_tree (GtkFileChooserDefault *impl)
943 {
944   GtkWidget *vbox;
945   GtkWidget *hbox;
946   GtkWidget *hbox2;
947   GtkWidget *widget;
948   GtkTreeSelection *selection;
949   GtkTreeViewColumn *column;
950   GtkCellRenderer *renderer;
951   GtkWidget *image;
952
953   vbox = gtk_vbox_new (FALSE, 12);
954   gtk_widget_show (vbox);
955
956   /* Scrolled window */
957
958   impl->shortcuts_scrollwin = gtk_scrolled_window_new (NULL, NULL);
959   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->shortcuts_scrollwin),
960                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
961   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (impl->shortcuts_scrollwin),
962                                        GTK_SHADOW_IN);
963   gtk_box_pack_start (GTK_BOX (vbox), impl->shortcuts_scrollwin, TRUE, TRUE, 0);
964   gtk_widget_show (impl->shortcuts_scrollwin);
965
966   /* Tree */
967
968   impl->shortcuts_tree = gtk_tree_view_new ();
969   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->shortcuts_tree), FALSE);
970
971   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->shortcuts_tree));
972   gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
973   gtk_tree_selection_set_select_function (selection,
974                                           shortcuts_select_func,
975                                           impl, NULL);
976   
977   g_signal_connect (impl->shortcuts_tree, "row_activated",
978                     G_CALLBACK (shortcuts_row_activated), impl);
979
980   gtk_container_add (GTK_CONTAINER (impl->shortcuts_scrollwin), impl->shortcuts_tree);
981   gtk_widget_show (impl->shortcuts_tree);
982
983   /* Model */
984
985   create_shortcuts_model (impl);
986
987   /* Column */
988
989   column = gtk_tree_view_column_new ();
990   gtk_tree_view_column_set_title (column, _("Folder"));
991
992   renderer = gtk_cell_renderer_pixbuf_new ();
993   gtk_tree_view_column_pack_start (column, renderer, FALSE);
994   gtk_tree_view_column_set_attributes (column, renderer,
995                                        "pixbuf", SHORTCUTS_COL_PIXBUF,
996                                        NULL);
997
998   renderer = _gtk_cell_renderer_sep_text_new ();
999   gtk_tree_view_column_pack_start (column, renderer, TRUE);
1000   gtk_tree_view_column_set_attributes (column, renderer,
1001                                        "text", SHORTCUTS_COL_NAME,
1002                                        NULL);
1003
1004   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->shortcuts_tree), column);
1005
1006   /* Bookmark buttons */
1007
1008   hbox = gtk_hbox_new (FALSE, 12);
1009   gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1010   gtk_widget_show (hbox);
1011
1012   /* "Add bookmark" */
1013
1014   impl->add_bookmark_button = gtk_button_new ();
1015
1016   hbox2 = gtk_hbox_new (FALSE, 2);
1017   gtk_container_add (GTK_CONTAINER (impl->add_bookmark_button), hbox2);
1018   widget = gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON);
1019   gtk_box_pack_start (GTK_BOX (hbox2), widget, FALSE, FALSE, 0);
1020   widget = gtk_label_new (_("Add bookmark"));
1021   gtk_box_pack_start (GTK_BOX (hbox2), widget, FALSE, FALSE, 0);
1022   gtk_widget_show_all (impl->add_bookmark_button);
1023
1024   g_signal_connect (impl->add_bookmark_button, "clicked",
1025                     G_CALLBACK (add_bookmark_button_clicked_cb), impl);
1026   gtk_box_pack_start (GTK_BOX (hbox), impl->add_bookmark_button, FALSE, FALSE, 0);
1027   gtk_widget_set_sensitive (impl->add_bookmark_button, FALSE);
1028   gtk_widget_show (impl->add_bookmark_button);
1029
1030   /* "Remove bookmark" */
1031
1032   impl->remove_bookmark_button = gtk_button_new ();
1033   g_signal_connect (impl->remove_bookmark_button, "clicked",
1034                     G_CALLBACK (remove_bookmark_button_clicked_cb), impl);
1035   image = gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_BUTTON);
1036   gtk_container_add (GTK_CONTAINER (impl->remove_bookmark_button), image);
1037   gtk_widget_set_sensitive (impl->remove_bookmark_button, FALSE);
1038   gtk_box_pack_start (GTK_BOX (hbox), impl->remove_bookmark_button, FALSE, FALSE, 0);
1039   gtk_widget_show_all (impl->remove_bookmark_button);
1040
1041   return vbox;
1042 }
1043
1044 /* Creates the widgets for the file list */
1045 static GtkWidget *
1046 create_file_list (GtkFileChooserDefault *impl)
1047 {
1048   GtkTreeSelection *selection;
1049   GtkTreeViewColumn *column;
1050   GtkCellRenderer *renderer;
1051
1052   /* Scrolled window */
1053
1054   impl->list_scrollwin = gtk_scrolled_window_new (NULL, NULL);
1055   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->list_scrollwin),
1056                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1057   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (impl->list_scrollwin),
1058                                        GTK_SHADOW_IN);
1059   if (!impl->folder_mode)
1060     gtk_widget_show (impl->list_scrollwin);
1061
1062   /* Tree/list view */
1063
1064   impl->list = gtk_tree_view_new ();
1065   gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->list), TRUE);
1066   gtk_container_add (GTK_CONTAINER (impl->list_scrollwin), impl->list);
1067   g_signal_connect (impl->list, "row_activated",
1068                     G_CALLBACK (list_row_activated), impl);
1069   gtk_widget_show (impl->list);
1070
1071   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
1072   g_signal_connect (selection, "changed",
1073                     G_CALLBACK (list_selection_changed), impl);
1074
1075   /* Filename column */
1076
1077   column = gtk_tree_view_column_new ();
1078   gtk_tree_view_column_set_title (column, _("File name"));
1079   gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_NAME);
1080
1081   renderer = gtk_cell_renderer_pixbuf_new ();
1082   gtk_tree_view_column_pack_start (column, renderer, TRUE);
1083   gtk_tree_view_column_set_cell_data_func (column, renderer,
1084                                            list_icon_data_func, impl, NULL);
1085
1086   renderer = gtk_cell_renderer_text_new ();
1087   gtk_tree_view_column_pack_start (column, renderer, TRUE);
1088   gtk_tree_view_column_set_cell_data_func (column, renderer,
1089                                            list_name_data_func, impl, NULL);
1090
1091   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->list), column);
1092 #if 0
1093   /* Size column */
1094
1095   column = gtk_tree_view_column_new ();
1096   gtk_tree_view_column_set_title (column, _("Size"));
1097
1098   renderer = gtk_cell_renderer_text_new ();
1099   gtk_tree_view_column_pack_start (column, renderer, TRUE);
1100   gtk_tree_view_column_set_cell_data_func (column, renderer,
1101                                            list_size_data_func, impl, NULL);
1102   gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_SIZE);
1103   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->list), column);
1104 #endif
1105   /* Modification time column */
1106
1107   column = gtk_tree_view_column_new ();
1108   gtk_tree_view_column_set_title (column, _("Modified"));
1109
1110   renderer = gtk_cell_renderer_text_new ();
1111   gtk_tree_view_column_pack_start (column, renderer, TRUE);
1112   gtk_tree_view_column_set_cell_data_func (column, renderer,
1113                                            list_mtime_data_func, impl, NULL);
1114   gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_MTIME);
1115   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->list), column);
1116
1117   return impl->list_scrollwin;
1118 }
1119
1120 static GtkWidget *
1121 create_filename_entry (GtkFileChooserDefault *impl)
1122 {
1123   GtkWidget *hbox;
1124   GtkWidget *label;
1125
1126   hbox = gtk_hbox_new (FALSE, 6);
1127   gtk_widget_show (hbox);
1128
1129   label = gtk_label_new_with_mnemonic (_("_Location:"));
1130   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1131   gtk_widget_show (label);
1132
1133   impl->entry = _gtk_file_chooser_entry_new ();
1134   gtk_entry_set_activates_default (GTK_ENTRY (impl->entry), TRUE);
1135   g_signal_connect (impl->entry, "activate",
1136                     G_CALLBACK (entry_activate), impl);
1137   _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->entry),
1138                                            impl->file_system);
1139
1140   gtk_box_pack_start (GTK_BOX (hbox), impl->entry, TRUE, TRUE, 0);
1141   gtk_widget_show (impl->entry);
1142
1143   gtk_label_set_mnemonic_widget (GTK_LABEL (label), impl->entry);
1144
1145   return hbox;
1146 }
1147
1148 static GObject*
1149 gtk_file_chooser_default_constructor (GType                  type,
1150                                       guint                  n_construct_properties,
1151                                       GObjectConstructParam *construct_params)
1152 {
1153   GtkFileChooserDefault *impl;
1154   GObject *object;
1155   GtkWidget *table;
1156   GtkWidget *hpaned;
1157   GtkWidget *entry_widget;
1158   GtkWidget *filter_widget;
1159   GtkWidget *widget;
1160   GList *focus_chain;
1161   GtkWidget *hbox;
1162
1163   object = parent_class->constructor (type,
1164                                       n_construct_properties,
1165                                       construct_params);
1166   impl = GTK_FILE_CHOOSER_DEFAULT (object);
1167
1168   g_assert (impl->file_system);
1169
1170   gtk_widget_push_composite_child ();
1171
1172   /* Toolbar */
1173   widget = toolbar_create (impl);
1174   gtk_box_pack_start (GTK_BOX (impl), widget, FALSE, FALSE, 0);
1175   gtk_widget_show (widget);
1176
1177   /* Basic table */
1178
1179   table = gtk_table_new (3, 2, FALSE);
1180   gtk_table_set_col_spacings (GTK_TABLE (table), 12);
1181   gtk_table_set_row_spacings (GTK_TABLE (table), 12);
1182   gtk_box_pack_start (GTK_BOX (impl), table, TRUE, TRUE, 0);
1183   gtk_widget_show (table);
1184
1185   /* Filter */
1186
1187   filter_widget = create_filter (impl);
1188   gtk_table_attach (GTK_TABLE (table), filter_widget,
1189                     0, 1,                   0, 1,
1190                     GTK_EXPAND | GTK_FILL,  0,
1191                     0,                      0);
1192
1193   /* Paned widget */
1194
1195   hpaned = gtk_hpaned_new ();
1196   gtk_table_attach (GTK_TABLE (table), hpaned,
1197                     0, 1,                   1, 2,
1198                     GTK_EXPAND | GTK_FILL,  GTK_EXPAND | GTK_FILL,
1199                     0,                      0);
1200   gtk_paned_set_position (GTK_PANED (hpaned), 200); /* FIXME: this sucks */
1201   gtk_widget_show (hpaned);
1202
1203   /* Shortcuts list */
1204
1205   widget = create_shortcuts_tree (impl);
1206   gtk_paned_add1 (GTK_PANED (hpaned), widget);
1207
1208   /* Folder tree */
1209
1210   hbox = gtk_hbox_new (FALSE, 12);
1211   gtk_paned_add2 (GTK_PANED (hpaned), hbox);
1212   gtk_widget_show (hbox);
1213
1214   widget = create_folder_tree (impl);
1215   gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
1216
1217   /* File list */
1218
1219   widget = create_file_list (impl);
1220   gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
1221
1222   /* Location/filename entry */
1223
1224   entry_widget = create_filename_entry (impl);
1225   gtk_table_attach (GTK_TABLE (table), entry_widget,
1226                     0, 2,                   2, 3,
1227                     GTK_EXPAND | GTK_FILL,  0,
1228                     0,                      6);
1229
1230   /* Preview */
1231
1232   impl->preview_frame = gtk_frame_new (_("Preview"));
1233   gtk_table_attach (GTK_TABLE (table), impl->preview_frame,
1234                     1, 2,                   0, 2,
1235                     0,                      GTK_EXPAND | GTK_FILL,
1236                     0,                      0);
1237   /* Don't show preview frame initially */
1238
1239   /* Make the entry the first widget in the focus chain
1240    */
1241   focus_chain = g_list_append (NULL, entry_widget);
1242   focus_chain = g_list_append (focus_chain, filter_widget);
1243   focus_chain = g_list_append (focus_chain, hpaned);
1244   focus_chain = g_list_append (focus_chain, impl->preview_frame);
1245   gtk_container_set_focus_chain (GTK_CONTAINER (table), focus_chain);
1246   g_list_free (focus_chain);
1247     
1248   gtk_widget_pop_composite_child ();
1249
1250   return object;
1251 }
1252
1253 /* Sets the extra_widget by packing it in the appropriate place */
1254 static void
1255 set_extra_widget (GtkFileChooserDefault *impl,
1256                   GtkWidget             *extra_widget)
1257 {
1258   if (extra_widget == impl->extra_widget)
1259     return;
1260
1261   if (impl->extra_widget)
1262     gtk_container_remove (GTK_CONTAINER (impl), impl->extra_widget);
1263
1264   impl->extra_widget = extra_widget;
1265   if (impl->extra_widget)
1266     {
1267       gtk_widget_show (impl->extra_widget);
1268       gtk_box_pack_end (GTK_BOX (impl), impl->extra_widget, FALSE, FALSE, 0);
1269     }
1270 }
1271
1272 /* Callback used when the set of bookmarks changes in the file system */
1273 static void
1274 bookmarks_changed_cb (GtkFileSystem         *file_system,
1275                       GtkFileChooserDefault *impl)
1276 {
1277   shortcuts_append_bookmarks (impl);
1278
1279   bookmarks_check_add_sensitivity (impl);
1280   bookmarks_check_remove_sensitivity (impl);
1281 }
1282
1283 static void
1284 gtk_file_chooser_default_set_property (GObject      *object,
1285                                        guint         prop_id,
1286                                        const GValue *value,
1287                                        GParamSpec   *pspec)
1288
1289 {
1290   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
1291
1292   switch (prop_id)
1293     {
1294     case GTK_FILE_CHOOSER_PROP_ACTION:
1295       impl->action = g_value_get_enum (value);
1296       break;
1297     case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM:
1298       {
1299         GtkFileSystem *file_system = g_value_get_object (value);
1300         if (impl->file_system != file_system)
1301           {
1302             if (impl->file_system)
1303               {
1304                 g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
1305                 impl->bookmarks_changed_id = 0;
1306                 g_object_unref (impl->file_system);
1307               }
1308             impl->file_system = file_system;
1309             if (impl->file_system)
1310               {
1311                 g_object_ref (impl->file_system);
1312                 impl->bookmarks_changed_id = g_signal_connect (impl->file_system, "bookmarks-changed",
1313                                                                G_CALLBACK (bookmarks_changed_cb),
1314                                                                impl);
1315               }
1316           }
1317       }
1318       break;
1319     case GTK_FILE_CHOOSER_PROP_FILTER:
1320       set_current_filter (impl, g_value_get_object (value));
1321       break;
1322     case GTK_FILE_CHOOSER_PROP_FOLDER_MODE:
1323       {
1324         gboolean folder_mode = g_value_get_boolean (value);
1325         if (folder_mode != impl->folder_mode)
1326           {
1327             impl->folder_mode = folder_mode;
1328             if (impl->folder_mode)
1329               {
1330                 gtk_widget_hide (impl->list_scrollwin);
1331                 gtk_widget_show (impl->tree_scrollwin);
1332               }
1333             else
1334               {
1335                 gtk_widget_hide (impl->tree_scrollwin);
1336                 gtk_widget_show (impl->list_scrollwin);
1337               }
1338           }
1339       }
1340       break;
1341     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
1342       impl->local_only = g_value_get_boolean (value);
1343       break;
1344     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
1345       set_preview_widget (impl, g_value_get_object (value));
1346       break;
1347     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
1348       impl->preview_widget_active = g_value_get_boolean (value);
1349       update_preview_widget_visibility (impl);
1350       break;
1351     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
1352       set_extra_widget (impl, g_value_get_object (value));
1353       break;
1354     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
1355       {
1356         gboolean select_multiple = g_value_get_boolean (value);
1357         if (select_multiple != impl->select_multiple)
1358           {
1359             GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
1360
1361             impl->select_multiple = select_multiple;
1362             gtk_tree_selection_set_mode (selection,
1363                                          (select_multiple ?
1364                                           GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE));
1365             /* FIXME: See note in check_preview_change() */
1366             check_preview_change (impl);
1367           }
1368       }
1369       break;
1370     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
1371       {
1372         gboolean show_hidden = g_value_get_boolean (value);
1373         if (show_hidden != impl->show_hidden)
1374           {
1375             impl->show_hidden = show_hidden;
1376             _gtk_file_system_model_set_show_hidden (impl->tree_model, show_hidden);
1377             _gtk_file_system_model_set_show_hidden (impl->list_model, show_hidden);
1378           }
1379       }
1380       break;
1381     default:
1382       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1383       break;
1384     }
1385 }
1386
1387 static void
1388 gtk_file_chooser_default_get_property (GObject    *object,
1389                                        guint       prop_id,
1390                                        GValue     *value,
1391                                        GParamSpec *pspec)
1392 {
1393   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
1394
1395   switch (prop_id)
1396     {
1397     case GTK_FILE_CHOOSER_PROP_ACTION:
1398       g_value_set_enum (value, impl->action);
1399       break;
1400     case GTK_FILE_CHOOSER_PROP_FILTER:
1401       g_value_set_object (value, impl->current_filter);
1402       break;
1403     case GTK_FILE_CHOOSER_PROP_FOLDER_MODE:
1404       g_value_set_boolean (value, impl->folder_mode);
1405       break;
1406     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
1407       g_value_set_boolean (value, impl->local_only);
1408       break;
1409     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
1410       g_value_set_object (value, impl->preview_widget);
1411       break;
1412     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
1413       g_value_set_boolean (value, impl->preview_widget_active);
1414       break;
1415     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
1416       g_value_set_object (value, impl->extra_widget);
1417       break;
1418     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
1419       g_value_set_boolean (value, impl->select_multiple);
1420       break;
1421     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
1422       g_value_set_boolean (value, impl->show_hidden);
1423       break;
1424     default:
1425       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1426       break;
1427     }
1428 }
1429
1430 /* We override show-all since we have internal widgets that
1431  * shouldn't be shown when you call show_all(), like the filter
1432  * combo box.
1433  */
1434 static void
1435 gtk_file_chooser_default_show_all (GtkWidget *widget)
1436 {
1437   gtk_widget_show (widget);
1438 }
1439
1440 static void
1441 expand_and_select_func (GtkFileSystemModel *model,
1442                         GtkTreePath        *path,
1443                         GtkTreeIter        *iter,
1444                         gpointer            user_data)
1445 {
1446   GtkFileChooserDefault *impl = user_data;
1447   GtkTreeView *tree_view;
1448
1449   if (model == impl->tree_model)
1450     tree_view = GTK_TREE_VIEW (impl->tree);
1451   else
1452     tree_view = GTK_TREE_VIEW (impl->list);
1453
1454   gtk_tree_view_expand_to_path (tree_view, path);
1455   gtk_tree_view_expand_row (tree_view, path, FALSE);
1456   gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
1457   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->tree), path, NULL, TRUE, 0.3, 0.5);
1458 }
1459
1460 static gboolean
1461 list_model_filter_func (GtkFileSystemModel *model,
1462                         GtkFilePath        *path,
1463                         const GtkFileInfo  *file_info,
1464                         gpointer            user_data)
1465 {
1466   GtkFileChooserDefault *impl = user_data;
1467   GtkFileFilterInfo filter_info;
1468   GtkFileFilterFlags needed;
1469   gboolean result;
1470
1471   if (!impl->current_filter)
1472     return TRUE;
1473
1474   filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
1475
1476   needed = gtk_file_filter_get_needed (impl->current_filter);
1477
1478   filter_info.display_name = gtk_file_info_get_display_name (file_info);
1479   filter_info.mime_type = gtk_file_info_get_mime_type (file_info);
1480
1481   if (needed & GTK_FILE_FILTER_FILENAME)
1482     {
1483       filter_info.filename = gtk_file_system_path_to_filename (impl->file_system, path);
1484       if (filter_info.filename)
1485         filter_info.contains |= GTK_FILE_FILTER_FILENAME;
1486     }
1487   else
1488     filter_info.filename = NULL;
1489
1490   if (needed & GTK_FILE_FILTER_URI)
1491     {
1492       filter_info.uri = gtk_file_system_path_to_uri (impl->file_system, path);
1493       if (filter_info.filename)
1494         filter_info.contains |= GTK_FILE_FILTER_URI;
1495     }
1496   else
1497     filter_info.uri = NULL;
1498
1499   result = gtk_file_filter_filter (impl->current_filter, &filter_info);
1500
1501   if (filter_info.filename)
1502     g_free ((gchar *)filter_info.filename);
1503   if (filter_info.uri)
1504     g_free ((gchar *)filter_info.uri);
1505
1506   return result;
1507 }
1508
1509 static void
1510 install_list_model_filter (GtkFileChooserDefault *impl)
1511 {
1512   if (impl->current_filter)
1513     _gtk_file_system_model_set_filter (impl->list_model,
1514                                        list_model_filter_func,
1515                                        impl);
1516 }
1517
1518 #define COMPARE_DIRECTORIES                                                                                     \
1519   GtkFileChooserDefault *impl = user_data;                                                                      \
1520   const GtkFileInfo *info_a = _gtk_file_system_model_get_info (impl->tree_model, a);                            \
1521   const GtkFileInfo *info_b = _gtk_file_system_model_get_info (impl->tree_model, b);                            \
1522   gboolean dir_a = gtk_file_info_get_is_folder (info_a);                                                        \
1523   gboolean dir_b = gtk_file_info_get_is_folder (info_b);                                                        \
1524                                                                                                                 \
1525   if (dir_a != dir_b)                                                                                           \
1526     return impl->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */
1527
1528 /* Sort callback for the filename column */
1529 static gint
1530 name_sort_func (GtkTreeModel *model,
1531                 GtkTreeIter  *a,
1532                 GtkTreeIter  *b,
1533                 gpointer      user_data)
1534 {
1535   COMPARE_DIRECTORIES;
1536   else
1537     return strcmp (gtk_file_info_get_display_key (info_a), gtk_file_info_get_display_key (info_b));
1538 }
1539
1540 /* Sort callback for the size column */
1541 static gint
1542 size_sort_func (GtkTreeModel *model,
1543                 GtkTreeIter  *a,
1544                 GtkTreeIter  *b,
1545                 gpointer      user_data)
1546 {
1547   COMPARE_DIRECTORIES;
1548   else
1549     {
1550       gint64 size_a = gtk_file_info_get_size (info_a);
1551       gint64 size_b = gtk_file_info_get_size (info_b);
1552
1553       return size_a > size_b ? -1 : (size_a == size_b ? 0 : 1);
1554     }
1555 }
1556
1557 /* Sort callback for the mtime column */
1558 static gint
1559 mtime_sort_func (GtkTreeModel *model,
1560                  GtkTreeIter  *a,
1561                  GtkTreeIter  *b,
1562                  gpointer      user_data)
1563 {
1564   COMPARE_DIRECTORIES;
1565   else
1566     {
1567       GtkFileTime ta = gtk_file_info_get_modification_time (info_a);
1568       GtkFileTime tb = gtk_file_info_get_modification_time (info_b);
1569
1570       return ta > tb ? -1 : (ta == tb ? 0 : 1);
1571     }
1572 }
1573
1574 /* Callback used when the sort column changes.  We cache the sort order for use
1575  * in name_sort_func().
1576  */
1577 static void
1578 list_sort_column_changed_cb (GtkTreeSortable       *sortable,
1579                              GtkFileChooserDefault *impl)
1580 {
1581   GtkSortType sort_type;
1582
1583   if (gtk_tree_sortable_get_sort_column_id (sortable, NULL, &sort_type))
1584     impl->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
1585 }
1586
1587 /* Gets rid of the old list model and creates a new one for the current folder */
1588 static void
1589 set_list_model (GtkFileChooserDefault *impl)
1590 {
1591   if (impl->list_model)
1592     {
1593       g_object_unref (impl->list_model);
1594       impl->list_model = NULL;
1595
1596       g_object_unref (impl->sort_model);
1597       impl->sort_model = NULL;
1598     }
1599
1600   impl->list_model = _gtk_file_system_model_new (impl->file_system,
1601                                                  impl->current_folder, 0,
1602 #if 0
1603                                                  GTK_FILE_INFO_ICON |
1604 #endif
1605                                                  GTK_FILE_INFO_DISPLAY_NAME |
1606                                                  GTK_FILE_INFO_IS_FOLDER |
1607                                                  GTK_FILE_INFO_SIZE |
1608                                                  GTK_FILE_INFO_MODIFICATION_TIME);
1609   install_list_model_filter (impl);
1610
1611   impl->sort_model = (GtkTreeModelSort *)gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (impl->list_model));
1612   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, name_sort_func, impl, NULL);
1613   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_SIZE, size_sort_func, impl, NULL);
1614   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_MTIME, mtime_sort_func, impl, NULL);
1615   gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->sort_model), NULL, NULL, NULL);
1616   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, GTK_SORT_ASCENDING);
1617   impl->list_sort_ascending = TRUE;
1618
1619   g_signal_connect (impl->sort_model, "sort_column_changed",
1620                     G_CALLBACK (list_sort_column_changed_cb), impl);
1621
1622   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->list),
1623                            GTK_TREE_MODEL (impl->sort_model));
1624   gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->list));
1625   gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->list),
1626                                    GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
1627 }
1628
1629 static void
1630 update_chooser_entry (GtkFileChooserDefault *impl)
1631 {
1632   GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
1633   const GtkFileInfo *info;
1634   GtkTreeIter iter;
1635   GtkTreeIter child_iter;
1636
1637   /* Fixing this for multiple selection involves getting the full
1638    * selection and diffing to find out what the most recently selected
1639    * file is; there is logic in GtkFileSelection that probably can
1640    * be copied; check_preview_change() is similar.
1641    */
1642   if (impl->select_multiple ||
1643       !gtk_tree_selection_get_selected (selection, NULL, &iter))
1644     return;
1645
1646   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
1647                                                   &child_iter,
1648                                                   &iter);
1649
1650   info = _gtk_file_system_model_get_info (impl->list_model, &child_iter);
1651
1652   _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->entry),
1653                                          gtk_file_info_get_display_name (info));
1654 }
1655
1656 static void
1657 gtk_file_chooser_default_set_current_folder (GtkFileChooser    *chooser,
1658                                              const GtkFilePath *path)
1659 {
1660   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
1661
1662   _gtk_file_system_model_path_do (impl->tree_model, path,
1663                                   expand_and_select_func, impl);
1664 }
1665
1666 static GtkFilePath *
1667 gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser)
1668 {
1669   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
1670
1671   return gtk_file_path_copy (impl->current_folder);
1672 }
1673
1674 static void
1675 gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
1676                                            const gchar    *name)
1677 {
1678   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
1679
1680   _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->entry), name);
1681 }
1682
1683 static void
1684 select_func (GtkFileSystemModel *model,
1685              GtkTreePath        *path,
1686              GtkTreeIter        *iter,
1687              gpointer            user_data)
1688 {
1689   GtkFileChooserDefault *impl = user_data;
1690   GtkTreeView *tree_view = GTK_TREE_VIEW (impl->list);
1691   GtkTreePath *sorted_path;
1692
1693   sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model, path);
1694   gtk_tree_view_set_cursor (tree_view, sorted_path, NULL, FALSE);
1695   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->tree), sorted_path, NULL, TRUE, 0.3, 0.0);
1696   gtk_tree_path_free (sorted_path);
1697 }
1698
1699 static void
1700 gtk_file_chooser_default_select_path (GtkFileChooser    *chooser,
1701                                       const GtkFilePath *path)
1702 {
1703   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
1704   GtkFilePath *parent_path;
1705   GError *error;
1706
1707   error = NULL;
1708   if (!gtk_file_system_get_parent (impl->file_system, path, &parent_path, &error))
1709     {
1710       error_getting_info_dialog (impl, path, error);
1711       return;
1712     }
1713
1714   if (!parent_path)
1715     {
1716       _gtk_file_chooser_set_current_folder_path (chooser, path);
1717     }
1718   else
1719     {
1720       _gtk_file_chooser_set_current_folder_path (chooser, parent_path);
1721       gtk_file_path_free (parent_path);
1722       _gtk_file_system_model_path_do (impl->list_model, path,
1723                                       select_func, impl);
1724     }
1725 }
1726
1727 static void
1728 unselect_func (GtkFileSystemModel *model,
1729                GtkTreePath        *path,
1730                GtkTreeIter        *iter,
1731                gpointer            user_data)
1732 {
1733   GtkFileChooserDefault *impl = user_data;
1734   GtkTreeView *tree_view = GTK_TREE_VIEW (impl->list);
1735   GtkTreePath *sorted_path;
1736
1737   sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model,
1738                                                                 path);
1739   gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (tree_view),
1740                                     sorted_path);
1741   gtk_tree_path_free (sorted_path);
1742 }
1743
1744 static void
1745 gtk_file_chooser_default_unselect_path (GtkFileChooser    *chooser,
1746                                         const GtkFilePath *path)
1747 {
1748   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
1749
1750   _gtk_file_system_model_path_do (impl->list_model, path,
1751                                  unselect_func, impl);
1752 }
1753
1754 static void
1755 gtk_file_chooser_default_select_all (GtkFileChooser *chooser)
1756 {
1757   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
1758   if (impl->select_multiple)
1759     {
1760       GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
1761       gtk_tree_selection_select_all (selection);
1762     }
1763 }
1764
1765 static void
1766 gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser)
1767 {
1768   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
1769   GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
1770
1771   gtk_tree_selection_unselect_all (selection);
1772 }
1773
1774 static void
1775 get_paths_foreach (GtkTreeModel *model,
1776                    GtkTreePath   *path,
1777                    GtkTreeIter   *iter,
1778                    gpointer       data)
1779 {
1780   GtkTreePath *child_path;
1781   GtkTreeIter child_iter;
1782   const GtkFilePath *file_path;
1783
1784   struct {
1785     GSList *result;
1786     GtkFileChooserDefault *impl;
1787   } *info = data;
1788
1789   child_path = gtk_tree_model_sort_convert_path_to_child_path (info->impl->sort_model, path);
1790   gtk_tree_model_get_iter (GTK_TREE_MODEL (info->impl->list_model), &child_iter, child_path);
1791   gtk_tree_path_free (child_path);
1792
1793   file_path = _gtk_file_system_model_get_path (info->impl->list_model, &child_iter);
1794   info->result = g_slist_prepend (info->result, gtk_file_path_copy (file_path));
1795 }
1796
1797 static GSList *
1798 gtk_file_chooser_default_get_paths (GtkFileChooser *chooser)
1799 {
1800   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
1801   GtkTreeSelection *selection;
1802
1803   struct {
1804     GSList *result;
1805     GtkFileChooserDefault *impl;
1806   } info = { NULL, };
1807
1808   if (!impl->sort_model)
1809     return NULL;
1810
1811   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
1812
1813   info.impl = impl;
1814   gtk_tree_selection_selected_foreach (selection,
1815                                        get_paths_foreach, &info);
1816   return g_slist_reverse (info.result);
1817 }
1818
1819 static GtkFilePath *
1820 gtk_file_chooser_default_get_preview_path (GtkFileChooser *chooser)
1821 {
1822   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
1823
1824   if (impl->preview_path)
1825     return gtk_file_path_copy (impl->preview_path);
1826   else
1827     return NULL;
1828 }
1829
1830 static GtkFileSystem *
1831 gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser)
1832 {
1833   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
1834
1835   return impl->file_system;
1836 }
1837
1838 static void
1839 gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
1840                                      GtkFileFilter  *filter)
1841 {
1842   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
1843   const gchar *name;
1844
1845   if (g_slist_find (impl->filters, filter))
1846     {
1847       g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
1848       return;
1849     }
1850
1851   g_object_ref (filter);
1852   gtk_object_sink (GTK_OBJECT (filter));
1853   impl->filters = g_slist_append (impl->filters, filter);
1854
1855   name = gtk_file_filter_get_name (filter);
1856   if (!name)
1857     name = "Untitled filter";   /* Place-holder, doesn't need to be marked for translation */
1858
1859   gtk_combo_box_append_text (GTK_COMBO_BOX (impl->filter_combo), name);
1860
1861   if (!g_slist_find (impl->filters, impl->current_filter))
1862     set_current_filter (impl, filter);
1863
1864   gtk_widget_show (impl->filter_alignment);
1865 }
1866
1867 static void
1868 gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
1869                                         GtkFileFilter  *filter)
1870 {
1871   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
1872   GtkTreeModel *model;
1873   GtkTreeIter iter;
1874   gint filter_index;
1875
1876   filter_index = g_slist_index (impl->filters, filter);
1877
1878   if (filter_index < 0)
1879     {
1880       g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
1881       return;
1882     }
1883
1884   impl->filters = g_slist_remove (impl->filters, filter);
1885
1886   if (filter == impl->current_filter)
1887     {
1888       if (impl->filters)
1889         set_current_filter (impl, impl->filters->data);
1890       else
1891         set_current_filter (impl, NULL);
1892     }
1893
1894   /* Remove row from the combo box */
1895   model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->filter_combo));
1896   gtk_tree_model_iter_nth_child  (model, &iter, NULL, filter_index);
1897   gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
1898   
1899   g_object_unref (filter);
1900
1901   if (!impl->filters)
1902     gtk_widget_hide (impl->filter_alignment);
1903 }
1904
1905 static GSList *
1906 gtk_file_chooser_default_list_filters (GtkFileChooser *chooser)
1907 {
1908   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
1909
1910   return g_slist_copy (impl->filters);
1911 }
1912
1913 /* Returns the position in the shortcuts tree where the nth specified shortcut would appear */
1914 static int
1915 shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl,
1916                                        int                    pos)
1917 {
1918   return pos + ((impl->has_home ? 1 : 0)
1919                 + (impl->has_desktop ? 1 : 0)
1920                 + impl->num_roots);
1921 }
1922
1923 static gboolean
1924 gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser    *chooser,
1925                                               const GtkFilePath *path,
1926                                               GError           **error)
1927 {
1928   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
1929   gboolean result;
1930   int pos;
1931
1932   pos = shortcuts_get_pos_for_shortcut_folder (impl, impl->num_shortcuts);
1933
1934   /* FIXME: how do we know if the path is a file system root? */
1935   result = shortcuts_insert_path (impl, pos, path, FALSE, NULL, error);
1936
1937   if (result)
1938     impl->num_shortcuts++;
1939
1940   return result;
1941 }
1942
1943 static gboolean
1944 gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
1945                                                  const GtkFilePath *path,
1946                                                  GError           **error)
1947 {
1948   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
1949   int pos;
1950   GtkTreeIter iter;
1951   int i;
1952
1953   if (impl->num_shortcuts == 0)
1954     goto out;
1955
1956   pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
1957   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
1958     g_assert_not_reached ();
1959
1960   for (i = 0; i < impl->num_shortcuts; i++)
1961     {
1962       GtkFilePath *shortcut;
1963
1964       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &shortcut, -1);
1965       g_assert (shortcut != NULL);
1966
1967       if (gtk_file_path_compare (shortcut, path) == 0)
1968         {
1969           /* The other columns are freed by the GtkTreeStore */
1970           gtk_file_path_free (shortcut);
1971           gtk_tree_store_remove (impl->shortcuts_model, &iter);
1972           impl->num_shortcuts--;
1973           return TRUE;
1974         }
1975
1976       if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
1977         g_assert_not_reached ();
1978     }
1979
1980  out:
1981
1982   g_set_error (error,
1983                GTK_FILE_CHOOSER_ERROR,
1984                GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
1985                _("shortcut %s does not exist"),
1986                gtk_file_path_get_string (path));
1987
1988   return FALSE;
1989 }
1990
1991 static GSList *
1992 gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser)
1993 {
1994   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
1995   int pos;
1996   GtkTreeIter iter;
1997   int i;
1998   GSList *list;
1999
2000   pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
2001   if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
2002     g_assert_not_reached ();
2003
2004   list = NULL;
2005
2006   for (i = 0; i < impl->num_shortcuts; i++)
2007     {
2008       GtkFilePath *shortcut;
2009
2010       gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &shortcut, -1);
2011       g_assert (shortcut != NULL);
2012
2013       list = g_slist_prepend (list, gtk_file_path_copy (shortcut));
2014
2015       if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
2016         g_assert_not_reached ();
2017     }
2018
2019   return g_slist_reverse (list);
2020 }
2021
2022 static void
2023 set_current_filter (GtkFileChooserDefault *impl,
2024                     GtkFileFilter         *filter)
2025 {
2026   if (impl->current_filter != filter)
2027     {
2028       int filter_index;
2029
2030       /* If we have filters, new filter must be one of them
2031        */
2032       filter_index = g_slist_index (impl->filters, filter);
2033       if (impl->filters && filter_index < 0)
2034         return;
2035
2036       if (impl->current_filter)
2037         g_object_unref (impl->current_filter);
2038       impl->current_filter = filter;
2039       if (impl->current_filter)
2040         {
2041           g_object_ref (impl->current_filter);
2042           gtk_object_sink (GTK_OBJECT (filter));
2043         }
2044
2045       if (impl->filters)
2046         gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
2047                                   filter_index);
2048
2049       install_list_model_filter (impl);
2050
2051       g_object_notify (G_OBJECT (impl), "filter");
2052     }
2053 }
2054
2055 static void
2056 open_and_close (GtkTreeView *tree_view,
2057                 GtkTreePath *target_path)
2058 {
2059   GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
2060   GtkTreeIter iter;
2061   GtkTreePath *path;
2062
2063   path = gtk_tree_path_new ();
2064   gtk_tree_path_append_index (path, 0);
2065
2066   gtk_tree_model_get_iter (model, &iter, path);
2067
2068   while (TRUE)
2069     {
2070       if (gtk_tree_path_is_ancestor (path, target_path) ||
2071           gtk_tree_path_compare (path, target_path) == 0)
2072         {
2073           GtkTreeIter child_iter;
2074           gtk_tree_view_expand_row (tree_view, path, FALSE);
2075           if (gtk_tree_model_iter_children (model, &child_iter, &iter))
2076             {
2077               iter = child_iter;
2078               gtk_tree_path_down (path);
2079               goto next;
2080             }
2081         }
2082       else
2083         gtk_tree_view_collapse_row (tree_view, path);
2084
2085       while (TRUE)
2086         {
2087           GtkTreeIter parent_iter;
2088           GtkTreeIter next_iter;
2089
2090           next_iter = iter;
2091           if (gtk_tree_model_iter_next (model, &next_iter))
2092             {
2093               iter = next_iter;
2094               gtk_tree_path_next (path);
2095               goto next;
2096             }
2097
2098           if (!gtk_tree_model_iter_parent (model, &parent_iter, &iter))
2099             goto out;
2100
2101           iter = parent_iter;
2102           gtk_tree_path_up (path);
2103         }
2104     next:
2105       ;
2106     }
2107
2108  out:
2109   gtk_tree_path_free (path);
2110 }
2111
2112 static void
2113 filter_combo_changed (GtkComboBox           *combo_box,
2114                       GtkFileChooserDefault *impl)
2115 {
2116   gint new_index = gtk_combo_box_get_active (combo_box);
2117   GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
2118
2119   set_current_filter (impl, new_filter);
2120 }
2121
2122 static void
2123 check_preview_change (GtkFileChooserDefault *impl)
2124 {
2125   const GtkFilePath *new_path = NULL;
2126
2127   /* Fixing preview for multiple selection involves getting the full
2128    * selection and diffing to find out what the most recently selected
2129    * file is; there is logic in GtkFileSelection that probably can
2130    * be copied. update_chooser_entry() is similar.
2131    */
2132   if (impl->sort_model && !impl->select_multiple)
2133     {
2134       GtkTreeSelection *selection;
2135       GtkTreeIter iter;
2136
2137       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
2138       if (gtk_tree_selection_get_selected  (selection, NULL, &iter))
2139         {
2140           GtkTreeIter child_iter;
2141
2142           gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
2143                                                           &child_iter, &iter);
2144
2145           new_path = _gtk_file_system_model_get_path (impl->list_model, &child_iter);
2146         }
2147     }
2148
2149   if (new_path != impl->preview_path &&
2150       !(new_path && impl->preview_path &&
2151         gtk_file_path_compare (new_path, impl->preview_path) == 0))
2152     {
2153       if (impl->preview_path)
2154         gtk_file_path_free (impl->preview_path);
2155
2156       if (new_path)
2157         impl->preview_path = gtk_file_path_copy (new_path);
2158       else
2159         impl->preview_path = NULL;
2160
2161       g_signal_emit_by_name (impl, "update-preview");
2162     }
2163 }
2164
2165 static void
2166 tree_selection_changed (GtkTreeSelection      *selection,
2167                         GtkFileChooserDefault *impl)
2168 {
2169   GtkTreeIter iter;
2170   const GtkFilePath *file_path;
2171   GtkTreePath *path;
2172
2173   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
2174     return;
2175
2176   file_path = _gtk_file_system_model_get_path (impl->tree_model, &iter);
2177   if (impl->current_folder && gtk_file_path_compare (file_path, impl->current_folder) == 0)
2178     return;
2179
2180   if (impl->current_folder)
2181     gtk_file_path_free (impl->current_folder);
2182   impl->current_folder = gtk_file_path_copy (file_path);
2183   _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->entry), file_path);
2184
2185   /* Close the tree up to only the parents of the newly selected
2186    * node and it's immediate children are visible.
2187    */
2188   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->tree_model), &iter);
2189   open_and_close (GTK_TREE_VIEW (impl->tree), path);
2190   gtk_tree_path_free (path);
2191
2192   /* Create the new list model */
2193   set_list_model (impl);
2194
2195   shortcuts_unselect_all (impl);
2196   toolbar_check_sensitivity (impl);
2197
2198   g_signal_emit_by_name (impl, "current-folder-changed", 0);
2199
2200   update_chooser_entry (impl);
2201   check_preview_change (impl);
2202   bookmarks_check_add_sensitivity (impl);
2203
2204   g_signal_emit_by_name (impl, "selection-changed", 0);
2205 }
2206
2207 /* Callback used when a row in the shortcuts list is activated */
2208 static void
2209 shortcuts_row_activated (GtkTreeView           *tree_view,
2210                          GtkTreePath           *path,
2211                          GtkTreeViewColumn     *column,
2212                          GtkFileChooserDefault *impl)
2213 {
2214   GtkTreeIter iter;
2215   GtkFilePath *model_path;
2216   
2217   if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
2218     return;
2219
2220   bookmarks_check_remove_sensitivity (impl);
2221
2222   /* Set the current folder */
2223
2224   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &model_path, -1);
2225
2226   if (!model_path)
2227     {
2228       /* We are on the bookmarks separator node, so do nothing */
2229       return;
2230     }
2231
2232   impl->changing_folder = TRUE;
2233   _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), model_path);
2234   impl->changing_folder = FALSE;
2235 }
2236
2237 static gboolean
2238 shortcuts_select_func  (GtkTreeSelection  *selection,
2239                         GtkTreeModel      *model,
2240                         GtkTreePath       *path,
2241                         gboolean           path_currently_selected,
2242                         gpointer           data)
2243 {
2244   GtkFileChooserDefault *impl = data;
2245   GtkTreeIter iter;
2246   GtkFilePath *model_path;
2247
2248   if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
2249     return FALSE;
2250
2251   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &model_path, -1);
2252
2253   /* Don't allow the separator node to be selected */
2254   return model_path != NULL;
2255 }
2256
2257 static void
2258 list_selection_changed (GtkTreeSelection      *selection,
2259                         GtkFileChooserDefault *impl)
2260 {
2261   update_chooser_entry (impl);
2262   check_preview_change (impl);
2263
2264   g_signal_emit_by_name (impl, "selection-changed", 0);
2265 }
2266
2267 /* Callback used when a row in the file list is activated */
2268 static void
2269 list_row_activated (GtkTreeView           *tree_view,
2270                     GtkTreePath           *path,
2271                     GtkTreeViewColumn     *column,
2272                     GtkFileChooserDefault *impl)
2273 {
2274   GtkTreeIter iter, child_iter;
2275   const GtkFileInfo *info;
2276
2277   if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
2278     return;
2279
2280   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
2281
2282   info = _gtk_file_system_model_get_info (impl->list_model, &child_iter);
2283
2284   if (gtk_file_info_get_is_folder (info))
2285     {
2286       const GtkFilePath *file_path;
2287
2288       file_path = _gtk_file_system_model_get_path (impl->list_model, &child_iter);
2289       _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), file_path);
2290
2291       return;
2292     }
2293
2294   g_signal_emit_by_name (impl, "file-activated");
2295 }
2296
2297 static void
2298 entry_activate (GtkEntry              *entry,
2299                 GtkFileChooserDefault *impl)
2300 {
2301   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (entry);
2302   const GtkFilePath *folder_path = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
2303   const gchar *file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
2304   GtkFilePath *new_folder = NULL;
2305
2306   /* If the file part is non-empty, we need to figure out if it
2307    * refers to a folder within folder. We could optimize the case
2308    * here where the folder is already loaded for one of our tree models.
2309    */
2310   if (file_part[0] == '\0' && gtk_file_path_compare (impl->current_folder, folder_path) != 0)
2311     new_folder = gtk_file_path_copy (folder_path);
2312   else
2313     {
2314       GtkFileFolder *folder = NULL;
2315       GtkFilePath *subfolder_path = NULL;
2316       GtkFileInfo *info = NULL;
2317       GError *error;
2318
2319       error = NULL;
2320       folder = gtk_file_system_get_folder (impl->file_system, folder_path, GTK_FILE_INFO_IS_FOLDER, &error);
2321
2322       if (!folder)
2323         {
2324           error_getting_info_dialog (impl, folder_path, error);
2325           return;
2326         }
2327
2328       error = NULL;
2329       subfolder_path = gtk_file_system_make_path (impl->file_system, folder_path, file_part, &error);
2330
2331       if (!subfolder_path)
2332         {
2333           char *msg;
2334
2335           msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
2336                                  gtk_file_path_get_string (folder_path),
2337                                  file_part,
2338                                  error->message);
2339           error_message (impl, msg);
2340           g_free (msg);
2341           g_object_unref (folder);
2342           return;
2343         }
2344
2345       error = NULL;
2346       info = gtk_file_folder_get_info (folder, subfolder_path, &error);
2347
2348       if (!info)
2349         {
2350           error_getting_info_dialog (impl, subfolder_path, error);
2351           g_object_unref (folder);
2352           gtk_file_path_free (subfolder_path);
2353           return;
2354         }
2355
2356       if (gtk_file_info_get_is_folder (info))
2357         new_folder = gtk_file_path_copy (subfolder_path);
2358
2359       g_object_unref (folder);
2360       gtk_file_path_free (subfolder_path);
2361       gtk_file_info_free (info);
2362     }
2363
2364   if (new_folder)
2365     {
2366       g_signal_stop_emission_by_name (entry, "activate");
2367
2368       _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), new_folder);
2369       _gtk_file_chooser_entry_set_file_part (chooser_entry, "");
2370
2371       gtk_file_path_free (new_folder);
2372     }
2373 }
2374
2375 static const GtkFileInfo *
2376 get_list_file_info (GtkFileChooserDefault *impl,
2377                     GtkTreeIter           *iter)
2378 {
2379   GtkTreeIter child_iter;
2380
2381   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
2382                                                   &child_iter,
2383                                                   iter);
2384
2385   return _gtk_file_system_model_get_info (impl->tree_model, &child_iter);
2386 }
2387
2388 static void
2389 tree_name_data_func (GtkTreeViewColumn *tree_column,
2390                      GtkCellRenderer   *cell,
2391                      GtkTreeModel      *tree_model,
2392                      GtkTreeIter       *iter,
2393                      gpointer           data)
2394 {
2395   GtkFileChooserDefault *impl = data;
2396   const GtkFileInfo *info = _gtk_file_system_model_get_info (impl->tree_model, iter);
2397
2398   if (info)
2399     {
2400       g_object_set (cell,
2401                     "text", gtk_file_info_get_display_name (info),
2402                     NULL);
2403     }
2404 }
2405
2406 static void
2407 list_icon_data_func (GtkTreeViewColumn *tree_column,
2408                      GtkCellRenderer   *cell,
2409                      GtkTreeModel      *tree_model,
2410                      GtkTreeIter       *iter,
2411                      gpointer           data)
2412 {
2413   GtkFileChooserDefault *impl = data;
2414   GtkTreeIter child_iter;
2415   const GtkFilePath *path;
2416   GdkPixbuf *pixbuf;
2417
2418   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
2419                                                   &child_iter,
2420                                                   iter);
2421   path = _gtk_file_system_model_get_path (impl->list_model, &child_iter);
2422
2423   /* FIXME: NULL GError */
2424   pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl), ICON_SIZE, NULL);
2425   g_object_set (cell,
2426                 "pixbuf", pixbuf,
2427                 NULL);
2428
2429   if (pixbuf)
2430     g_object_unref (pixbuf);
2431                 
2432 #if 0
2433   const GtkFileInfo *info = get_list_file_info (impl, iter);
2434
2435   if (info)
2436     {
2437       GtkWidget *widget = GTK_TREE_VIEW_COLUMN (tree_column)->tree_view;
2438       GdkPixbuf *pixbuf = gtk_file_info_render_icon (info, widget, ICON_SIZE);
2439
2440       g_object_set (cell,
2441                     "pixbuf", pixbuf,
2442                     NULL);
2443
2444       if (pixbuf)
2445         g_object_unref (pixbuf);
2446     }
2447 #endif
2448 }
2449
2450 /* Sets a cellrenderer's text, making it bold if the GtkFileInfo is a folder */
2451 static void
2452 set_cell_text_bold_if_folder (const GtkFileInfo *info, GtkCellRenderer *cell, const char *text)
2453 {
2454   g_object_set (cell,
2455                 "text", text,
2456                 "weight", gtk_file_info_get_is_folder (info) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
2457                 NULL);
2458 }
2459
2460 static void
2461 list_name_data_func (GtkTreeViewColumn *tree_column,
2462                      GtkCellRenderer   *cell,
2463                      GtkTreeModel      *tree_model,
2464                      GtkTreeIter       *iter,
2465                      gpointer           data)
2466 {
2467   GtkFileChooserDefault *impl = data;
2468   const GtkFileInfo *info = get_list_file_info (impl, iter);
2469
2470   if (!info)
2471     return;
2472
2473   set_cell_text_bold_if_folder (info, cell, gtk_file_info_get_display_name (info));
2474 }
2475
2476 #if 0
2477 static void
2478 list_size_data_func (GtkTreeViewColumn *tree_column,
2479                      GtkCellRenderer   *cell,
2480                      GtkTreeModel      *tree_model,
2481                      GtkTreeIter       *iter,
2482                      gpointer           data)
2483 {
2484   GtkFileChooserDefault *impl = data;
2485   const GtkFileInfo *info = get_list_file_info (impl, iter);
2486   gint64 size = gtk_file_info_get_size (info);
2487   gchar *str;
2488
2489   if (!info || gtk_file_info_get_is_folder (info))
2490     return;
2491
2492   if (size < (gint64)1024)
2493     str = g_strdup_printf (_("%d bytes"), (gint)size);
2494   else if (size < (gint64)1024*1024)
2495     str = g_strdup_printf (_("%.1f K"), size / (1024.));
2496   else if (size < (gint64)1024*1024*1024)
2497     str = g_strdup_printf (_("%.1f M"), size / (1024.*1024.));
2498   else
2499     str = g_strdup_printf (_("%.1f G"), size / (1024.*1024.*1024.));
2500
2501   g_object_set (cell,
2502                 "text", str,
2503                 NULL);
2504
2505   g_free (str);
2506 }
2507 #endif
2508
2509 /* Tree column data callback for the file list; fetches the mtime of a file */
2510 static void
2511 list_mtime_data_func (GtkTreeViewColumn *tree_column,
2512                       GtkCellRenderer   *cell,
2513                       GtkTreeModel      *tree_model,
2514                       GtkTreeIter       *iter,
2515                       gpointer           data)
2516 {
2517   GtkFileChooserDefault *impl;
2518   const GtkFileInfo *info;
2519   time_t mtime, now;
2520   struct tm tm, now_tm;
2521   char buf[256];
2522
2523   impl = data;
2524
2525   info = get_list_file_info (impl, iter);
2526   if (!info)
2527     return;
2528
2529   mtime = (time_t) gtk_file_info_get_modification_time (info);
2530   tm = *localtime (&mtime);
2531
2532   now = time (NULL);
2533   now_tm = *localtime (&now);
2534
2535   /* Today */
2536   if (tm.tm_mday == now_tm.tm_mday
2537       && tm.tm_mon == now_tm.tm_mon
2538       && tm.tm_year == now_tm.tm_year)
2539     strcpy (buf, _("Today"));
2540   else
2541     {
2542       int i;
2543
2544       /* Days from last week */
2545
2546       for (i = 1; i < 7; i++)
2547         {
2548           time_t then;
2549           struct tm then_tm;
2550
2551           then = now - i * 60 * 60 * 24;
2552           then_tm = *localtime (&then);
2553
2554           if (tm.tm_mday == then_tm.tm_mday
2555               && tm.tm_mon == then_tm.tm_mon
2556               && tm.tm_year == then_tm.tm_year)
2557             {
2558               if (i == 1)
2559                 strcpy (buf, _("Yesterday"));
2560               else
2561                 if (strftime (buf, sizeof (buf), "%A", &tm) == 0)
2562                   strcpy (buf, _("Unknown"));
2563
2564               break;
2565             }
2566         }
2567
2568       /* Any other date */
2569
2570       if (i == 7)
2571         {
2572           if (strftime (buf, sizeof (buf), _("%d/%b/%Y"), &tm) == 0)
2573             strcpy (buf, _("Unknown"));
2574         }
2575     }
2576
2577   set_cell_text_bold_if_folder (info, cell, buf);
2578 }
2579
2580 GtkWidget *
2581 _gtk_file_chooser_default_new (GtkFileSystem *file_system)
2582 {
2583   return  g_object_new (GTK_TYPE_FILE_CHOOSER_DEFAULT,
2584                         "file-system", file_system,
2585                         NULL);
2586 }