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