]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilechooserdefault.c
Add over-big test case for preview (much of size from a modified copy of
[~andy/gtk] / gtk / gtkfilechooserdefault.c
1 /* GTK - The GIMP Toolkit
2  * gtkfilechooserimpldefault.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 "gtkfilechooserimpldefault.h"
22 #include "gtkfilechooserentry.h"
23 #include "gtkfilechooserenums.h"
24 #include "gtkfilechooserutils.h"
25 #include "gtkfilechooser.h"
26 #include "gtkfilesystemmodel.h"
27
28 #include <gtk/gtkalignment.h>
29 #include <gtk/gtkcellrendererpixbuf.h>
30 #include <gtk/gtkcellrenderertext.h>
31 #include <gtk/gtkentry.h>
32 #include <gtk/gtkframe.h>
33 #include <gtk/gtkhbox.h>
34 #include <gtk/gtkhpaned.h>
35 #include <gtk/gtkicontheme.h>
36 #include <gtk/gtklabel.h>
37 #include <gtk/gtkmenuitem.h>
38 #include <gtk/gtkoptionmenu.h>
39 #include <gtk/gtkscrolledwindow.h>
40 #include <gtk/gtktable.h>
41 #include <gtk/gtktreeview.h>
42 #include <gtk/gtktreemodelsort.h>
43 #include <gtk/gtktreeselection.h>
44 #include <gtk/gtkvbox.h>
45
46 #include <string.h>
47
48 typedef struct _GtkFileChooserImplDefaultClass GtkFileChooserImplDefaultClass;
49
50 #define GTK_FILE_CHOOSER_IMPL_DEFAULT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_IMPL_DEFAULT, GtkFileChooserImplDefaultClass))
51 #define GTK_IS_FILE_CHOOSER_IMPL_DEFAULT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_IMPL_DEFAULT))
52 #define GTK_FILE_CHOOSER_IMPL_DEFAULT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_IMPL_DEFAULT, GtkFileChooserImplDefaultClass))
53
54 struct _GtkFileChooserImplDefaultClass
55 {
56   GtkVBoxClass parent_class;
57 };
58
59 struct _GtkFileChooserImplDefault
60 {
61   GtkVBox parent_instance;
62
63   GtkFileSystem *file_system;
64   GtkFileSystemModel *tree_model;
65   GtkFileSystemModel *list_model;
66   GtkTreeModelSort *sort_model;
67
68   GtkFileChooserAction action;
69
70   GtkFileFilter *current_filter;
71   GSList *filters;
72   
73   GtkFilePath *current_folder;
74   GtkFilePath *preview_path;
75   
76   GtkWidget *preview_frame;
77
78   guint folder_mode : 1;
79   guint local_only : 1;
80   guint preview_widget_active : 1;
81   guint select_multiple : 1;
82   guint show_hidden : 1;
83
84   GtkWidget *filter_alignment;
85   GtkWidget *filter_option_menu;
86   GtkWidget *tree_scrollwin;
87   GtkWidget *tree;
88   GtkWidget *list_scrollwin;
89   GtkWidget *list;
90   GtkWidget *entry;
91   GtkWidget *preview_widget;
92 };
93
94 static void gtk_file_chooser_impl_default_class_init   (GtkFileChooserImplDefaultClass *class);
95 static void gtk_file_chooser_impl_default_iface_init   (GtkFileChooserIface            *iface);
96 static void gtk_file_chooser_impl_default_init         (GtkFileChooserImplDefault      *impl);
97
98 static GObject* gtk_file_chooser_impl_default_constructor  (GType                  type,
99                                                             guint                  n_construct_properties,
100                                                             GObjectConstructParam *construct_params);
101 static void     gtk_file_chooser_impl_default_finalize     (GObject               *object);
102 static void     gtk_file_chooser_impl_default_set_property (GObject               *object,
103                                                             guint                  prop_id,
104                                                             const GValue          *value,
105                                                             GParamSpec            *pspec);
106 static void     gtk_file_chooser_impl_default_get_property (GObject               *object,
107                                                             guint                  prop_id,
108                                                             GValue                *value,
109                                                             GParamSpec            *pspec);
110 static void     gtk_file_chooser_impl_default_show_all     (GtkWidget             *widget);
111
112 static void           gtk_file_chooser_impl_default_set_current_folder (GtkFileChooser    *chooser,
113                                                                         const GtkFilePath *path);
114 static GtkFilePath *  gtk_file_chooser_impl_default_get_current_folder (GtkFileChooser    *chooser);
115 static void           gtk_file_chooser_impl_default_set_current_name   (GtkFileChooser    *chooser,
116                                                                         const gchar       *name);
117 static void           gtk_file_chooser_impl_default_select_path        (GtkFileChooser    *chooser,
118                                                                         const GtkFilePath *path);
119 static void           gtk_file_chooser_impl_default_unselect_path      (GtkFileChooser    *chooser,
120                                                                         const GtkFilePath *path);
121 static void           gtk_file_chooser_impl_default_select_all         (GtkFileChooser    *chooser);
122 static void           gtk_file_chooser_impl_default_unselect_all       (GtkFileChooser    *chooser);
123 static GSList *       gtk_file_chooser_impl_default_get_paths          (GtkFileChooser    *chooser);
124 static GtkFilePath *  gtk_file_chooser_impl_default_get_preview_path   (GtkFileChooser    *chooser);
125 static GtkFileSystem *gtk_file_chooser_impl_default_get_file_system    (GtkFileChooser    *chooser);
126 static void           gtk_file_chooser_impl_default_add_filter         (GtkFileChooser    *chooser,
127                                                                         GtkFileFilter     *filter);
128 static void           gtk_file_chooser_impl_default_remove_filter      (GtkFileChooser    *chooser,
129                                                                         GtkFileFilter     *filter);
130 static GSList *       gtk_file_chooser_impl_default_list_filters       (GtkFileChooser    *chooser);
131
132 static void set_current_filter   (GtkFileChooserImplDefault *impl,
133                                   GtkFileFilter             *filter);
134 static void check_preview_change (GtkFileChooserImplDefault *impl);
135
136 static void filter_option_menu_changed (GtkOptionMenu             *option_menu,
137                                         GtkFileChooserImplDefault *impl);
138 static void tree_selection_changed     (GtkTreeSelection          *tree_selection,
139                                         GtkFileChooserImplDefault *impl);
140 static void list_selection_changed     (GtkTreeSelection          *tree_selection,
141                                         GtkFileChooserImplDefault *impl);
142 static void entry_activate             (GtkEntry                  *entry,
143                                         GtkFileChooserImplDefault *impl);
144
145 static void tree_name_data_func (GtkTreeViewColumn *tree_column,
146                                  GtkCellRenderer   *cell,
147                                  GtkTreeModel      *tree_model,
148                                  GtkTreeIter       *iter,
149                                  gpointer           data);
150 static void list_icon_data_func (GtkTreeViewColumn *tree_column,
151                                  GtkCellRenderer   *cell,
152                                  GtkTreeModel      *tree_model,
153                                  GtkTreeIter       *iter,
154                                  gpointer           data);
155 static void list_name_data_func (GtkTreeViewColumn *tree_column,
156                                  GtkCellRenderer   *cell,
157                                  GtkTreeModel      *tree_model,
158                                  GtkTreeIter       *iter,
159                                  gpointer           data);
160 static void list_size_data_func (GtkTreeViewColumn *tree_column,
161                                  GtkCellRenderer   *cell,
162                                  GtkTreeModel      *tree_model,
163                                  GtkTreeIter       *iter,
164                                  gpointer           data);
165
166 static GObjectClass *parent_class;
167
168 GType
169 _gtk_file_chooser_impl_default_get_type (void)
170 {
171   static GType file_chooser_impl_default_type = 0;
172
173   if (!file_chooser_impl_default_type)
174     {
175       static const GTypeInfo file_chooser_impl_default_info =
176       {
177         sizeof (GtkFileChooserImplDefaultClass),
178         NULL,           /* base_init */
179         NULL,           /* base_finalize */
180         (GClassInitFunc) gtk_file_chooser_impl_default_class_init,
181         NULL,           /* class_finalize */
182         NULL,           /* class_data */
183         sizeof (GtkFileChooserImplDefault),
184         0,              /* n_preallocs */
185         (GInstanceInitFunc) gtk_file_chooser_impl_default_init,
186       };
187       
188       static const GInterfaceInfo file_chooser_info =
189       {
190         (GInterfaceInitFunc) gtk_file_chooser_impl_default_iface_init, /* interface_init */
191         NULL,                                                          /* interface_finalize */
192         NULL                                                           /* interface_data */
193       };
194
195       file_chooser_impl_default_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserImplDefault",
196                                                          &file_chooser_impl_default_info, 0);
197       g_type_add_interface_static (file_chooser_impl_default_type,
198                                    GTK_TYPE_FILE_CHOOSER,
199                                    &file_chooser_info);
200     }
201
202   return file_chooser_impl_default_type;
203 }
204
205 static void
206 gtk_file_chooser_impl_default_class_init (GtkFileChooserImplDefaultClass *class)
207 {
208   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
209   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
210
211   parent_class = g_type_class_peek_parent (class);
212
213   gobject_class->finalize = gtk_file_chooser_impl_default_finalize;
214   gobject_class->constructor = gtk_file_chooser_impl_default_constructor;
215   gobject_class->set_property = gtk_file_chooser_impl_default_set_property;
216   gobject_class->get_property = gtk_file_chooser_impl_default_get_property;
217
218   widget_class->show_all = gtk_file_chooser_impl_default_show_all;
219
220   _gtk_file_chooser_install_properties (gobject_class);
221 }
222
223 static void
224 gtk_file_chooser_impl_default_iface_init (GtkFileChooserIface *iface)
225 {
226   iface->select_path = gtk_file_chooser_impl_default_select_path;
227   iface->unselect_path = gtk_file_chooser_impl_default_unselect_path;
228   iface->select_all = gtk_file_chooser_impl_default_select_all;
229   iface->unselect_all = gtk_file_chooser_impl_default_unselect_all;
230   iface->get_paths = gtk_file_chooser_impl_default_get_paths;
231   iface->get_preview_path = gtk_file_chooser_impl_default_get_preview_path;
232   iface->get_file_system = gtk_file_chooser_impl_default_get_file_system;
233   iface->set_current_folder = gtk_file_chooser_impl_default_set_current_folder;
234   iface->get_current_folder = gtk_file_chooser_impl_default_get_current_folder;
235   iface->set_current_name = gtk_file_chooser_impl_default_set_current_name;
236   iface->add_filter = gtk_file_chooser_impl_default_add_filter;
237   iface->remove_filter = gtk_file_chooser_impl_default_remove_filter;
238   iface->list_filters = gtk_file_chooser_impl_default_list_filters;
239 }
240
241 static void
242 gtk_file_chooser_impl_default_init (GtkFileChooserImplDefault *impl)
243 {
244   impl->folder_mode = FALSE;
245   impl->local_only = TRUE;
246   impl->preview_widget_active = TRUE;
247   impl->select_multiple = FALSE;
248   impl->show_hidden = FALSE;
249
250   gtk_container_set_border_width (GTK_CONTAINER (impl), 5);
251 }
252
253 static void
254 gtk_file_chooser_impl_default_finalize (GObject *object)
255 {
256   GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (object);
257
258   g_object_unref (impl->file_system);
259
260   G_OBJECT_CLASS (parent_class)->finalize (object);
261 }
262
263 static void
264 update_preview_widget_visibility (GtkFileChooserImplDefault *impl)
265 {
266   if (impl->preview_widget_active && impl->preview_widget)
267     gtk_widget_show (impl->preview_frame);
268   else
269     gtk_widget_hide (impl->preview_frame);
270 }
271
272 static void
273 set_preview_widget (GtkFileChooserImplDefault *impl,
274                     GtkWidget                 *preview_widget)
275 {
276   if (preview_widget == impl->preview_widget)
277     return;
278
279   if (impl->preview_widget)
280     {
281       g_object_unref (impl->preview_widget);
282       impl->preview_widget = NULL;
283
284       gtk_container_remove (GTK_CONTAINER (impl->preview_frame),
285                             impl->preview_widget);
286     }
287
288   impl->preview_widget = preview_widget;
289   if (impl->preview_widget)
290     {
291       g_object_ref (impl->preview_widget);
292       gtk_object_sink (GTK_OBJECT (impl->preview_widget));
293       
294       gtk_widget_show (impl->preview_widget);
295       gtk_container_add (GTK_CONTAINER (impl->preview_frame),
296                          impl->preview_widget);
297     }
298
299   update_preview_widget_visibility (impl);
300 }
301
302 static GObject*
303 gtk_file_chooser_impl_default_constructor (GType                  type,
304                                            guint                  n_construct_properties,
305                                            GObjectConstructParam *construct_params)
306 {
307   GtkFileChooserImplDefault *impl;
308   GtkTreeViewColumn *column;
309   GtkCellRenderer *renderer;
310   GObject *object;
311   GtkWidget *table;
312   GtkWidget *hpaned;
313   GtkWidget *hbox;
314   GtkWidget *label;
315   GtkTreeSelection *selection;
316 #if 0  
317   GList *focus_chain;
318 #endif  
319
320   object = parent_class->constructor (type,
321                                       n_construct_properties,
322                                       construct_params);
323   impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (object);
324
325   g_assert (impl->file_system);
326
327   gtk_widget_push_composite_child ();
328
329   table = gtk_table_new (3, 2, FALSE);
330   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
331   gtk_box_pack_start (GTK_BOX (impl), table, TRUE, TRUE, 0);
332   gtk_widget_show (table);
333
334   impl->filter_alignment = gtk_alignment_new (0.0, 0.5, 0.0, 1.0);
335   gtk_alignment_set_padding (GTK_ALIGNMENT (impl->filter_alignment), 0, 6, 0, 0);
336   gtk_table_attach (GTK_TABLE (table), impl->filter_alignment,
337                     0, 1,                   0, 1,
338                     GTK_EXPAND | GTK_FILL,  0,
339                     0,                      0);
340   /* Don't show filter initially */
341
342   hbox = gtk_hbox_new (FALSE, 6);
343   gtk_container_add (GTK_CONTAINER (impl->filter_alignment), hbox);
344   gtk_widget_show (hbox);
345
346   label = gtk_label_new_with_mnemonic ("Files of _type:");
347   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
348   gtk_widget_show (label);
349
350   impl->filter_option_menu = gtk_option_menu_new ();
351   gtk_option_menu_set_menu (GTK_OPTION_MENU (impl->filter_option_menu),
352                             gtk_menu_new ());
353   gtk_box_pack_start (GTK_BOX (hbox), impl->filter_option_menu, FALSE, FALSE, 0);
354   gtk_widget_show (impl->filter_option_menu);
355
356   gtk_label_set_mnemonic_widget (GTK_LABEL (label), impl->filter_option_menu);
357
358   g_signal_connect (impl->filter_option_menu, "changed",
359                     G_CALLBACK (filter_option_menu_changed), impl);
360
361   hpaned = gtk_hpaned_new ();
362   gtk_table_attach (GTK_TABLE (table), hpaned,
363                     0, 1,                   1, 2,
364                     GTK_EXPAND | GTK_FILL,  GTK_EXPAND | GTK_FILL,
365                     0,                      0);
366   gtk_widget_show (hpaned);
367
368   impl->tree_scrollwin = gtk_scrolled_window_new (NULL, NULL);
369   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->tree_scrollwin),
370                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
371   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (impl->tree_scrollwin),
372                                        GTK_SHADOW_IN);
373   gtk_paned_add1 (GTK_PANED (hpaned), impl->tree_scrollwin);
374   gtk_widget_show (impl->tree_scrollwin);
375   
376   impl->tree = gtk_tree_view_new ();
377   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->tree), FALSE);
378   
379   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->tree));
380   g_signal_connect (selection, "changed",
381                     G_CALLBACK (tree_selection_changed), impl);
382
383   gtk_paned_set_position (GTK_PANED (hpaned), 200);
384
385   gtk_container_add (GTK_CONTAINER (impl->tree_scrollwin), impl->tree);
386   gtk_widget_show (impl->tree);
387   
388   impl->list_scrollwin = gtk_scrolled_window_new (NULL, NULL);
389   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->list_scrollwin),
390                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
391   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (impl->list_scrollwin),
392                                        GTK_SHADOW_IN);
393   gtk_paned_add2 (GTK_PANED (hpaned), impl->list_scrollwin);
394   gtk_widget_show (impl->list_scrollwin);
395   
396   impl->list = gtk_tree_view_new ();
397   gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->list), TRUE);
398   gtk_container_add (GTK_CONTAINER (impl->list_scrollwin), impl->list);
399   gtk_widget_show (impl->list);
400   
401   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
402   g_signal_connect (selection, "changed",
403                     G_CALLBACK (list_selection_changed), impl);
404
405   hbox = gtk_hbox_new (FALSE, 6);
406   gtk_table_attach (GTK_TABLE (table), hbox,
407                     0, 2,                   2, 3,
408                     GTK_EXPAND | GTK_FILL,  0,
409                     0,                      6);
410   gtk_widget_show (hbox);
411
412   label = gtk_label_new_with_mnemonic ("_Location:");
413   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
414   gtk_widget_show (label);
415   
416   impl->entry = _gtk_file_chooser_entry_new ();
417   gtk_entry_set_activates_default (GTK_ENTRY (impl->entry), TRUE);
418   g_signal_connect (impl->entry, "activate",
419                     G_CALLBACK (entry_activate), impl);
420   
421   gtk_box_pack_start (GTK_BOX (hbox), impl->entry, TRUE, TRUE, 0);
422   gtk_widget_show (impl->entry);
423
424   gtk_label_set_mnemonic_widget (GTK_LABEL (label), impl->entry);
425
426   impl->preview_frame = gtk_frame_new ("Preview");
427   gtk_table_attach (GTK_TABLE (table), impl->preview_frame,
428                     1, 2,                   0, 2,
429                     0,                      GTK_EXPAND | GTK_FILL,
430                     0,                      0);
431   /* Don't show preview frame initially */
432   
433 #if 0  
434   focus_chain = g_list_append (NULL, impl->entry);
435   focus_chain = g_list_append (focus_chain, impl->tree);
436   focus_chain = g_list_append (focus_chain, impl->list);
437   gtk_container_set_focus_chain (GTK_CONTAINER (impl), focus_chain);
438   g_list_free (focus_chain);
439 #endif  
440   
441   gtk_widget_pop_composite_child ();
442
443   impl->tree_model = _gtk_file_system_model_new (impl->file_system, NULL, -1,
444                                                  GTK_FILE_INFO_DISPLAY_NAME);
445   _gtk_file_system_model_set_show_files (impl->tree_model, FALSE);
446
447   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->tree),
448                            GTK_TREE_MODEL (impl->tree_model));
449
450   gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (impl->tree), 0,
451                                               "File name",
452                                               gtk_cell_renderer_text_new (),
453                                               tree_name_data_func, impl, NULL);
454   gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->tree),
455                                    GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
456
457   column = gtk_tree_view_column_new ();
458   gtk_tree_view_column_set_title  (column, "File name");
459
460   renderer = gtk_cell_renderer_pixbuf_new ();
461   gtk_tree_view_column_pack_start (column, renderer, TRUE);
462   gtk_tree_view_column_set_cell_data_func (column, renderer,
463                                            list_icon_data_func, impl, NULL);
464   gtk_tree_view_column_set_sort_column_id (column, 0);
465   
466   renderer = gtk_cell_renderer_text_new ();
467   gtk_tree_view_column_pack_start (column, renderer, TRUE);
468   gtk_tree_view_column_set_cell_data_func (column, renderer,
469                                            list_name_data_func, impl, NULL);
470   gtk_tree_view_column_set_sort_column_id (column, 0);
471   
472   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->list), column);
473   
474   column = gtk_tree_view_column_new ();
475   gtk_tree_view_column_set_title  (column, "Size");
476   renderer = gtk_cell_renderer_text_new ();
477   gtk_tree_view_column_pack_start (column, renderer, TRUE);
478   gtk_tree_view_column_set_cell_data_func (column, renderer,
479                                            list_size_data_func, impl, NULL);
480   gtk_tree_view_column_set_sort_column_id (column, 1);
481   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->list), column);
482
483   _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->entry),
484                                            impl->file_system);
485   return object;
486 }
487
488 static void
489 gtk_file_chooser_impl_default_set_property (GObject         *object,
490                                             guint            prop_id,
491                                             const GValue    *value,
492                                             GParamSpec      *pspec)
493      
494 {
495   GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (object);
496   
497   switch (prop_id)
498     {
499     case GTK_FILE_CHOOSER_PROP_ACTION:
500       impl->action = g_value_get_enum (value);
501       break;
502     case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM:
503       {
504         GtkFileSystem *file_system = g_value_get_object (value);
505         if (impl->file_system != file_system)
506           {
507             if (impl->file_system)
508               g_object_unref (impl->file_system);
509             impl->file_system = file_system;
510             if (impl->file_system)
511               g_object_ref (impl->file_system);
512           }
513       }
514       break;
515     case GTK_FILE_CHOOSER_PROP_FILTER:
516       set_current_filter (impl, g_value_get_object (value));
517       break;
518     case GTK_FILE_CHOOSER_PROP_FOLDER_MODE:
519       { 
520         gboolean folder_mode = g_value_get_boolean (value);
521         if (folder_mode != impl->folder_mode)
522           {
523             impl->folder_mode = folder_mode;
524             if (impl->folder_mode)
525               gtk_widget_hide (impl->list_scrollwin);
526             else
527               gtk_widget_show (impl->list_scrollwin);
528           }
529       }
530       break;
531     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
532       impl->local_only = g_value_get_boolean (value);
533       break;
534     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
535       set_preview_widget (impl, g_value_get_object (value));
536       break;
537     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
538       impl->preview_widget_active = g_value_get_boolean (value);
539       update_preview_widget_visibility (impl);
540       break;
541     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
542       {
543         gboolean select_multiple = g_value_get_boolean (value);
544         if (select_multiple != impl->select_multiple)
545           {
546             GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
547             
548             impl->select_multiple = select_multiple;
549             gtk_tree_selection_set_mode (selection,
550                                          (select_multiple ?
551                                           GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE));
552             /* FIXME: See note in check_preview_change() */
553             check_preview_change (impl);
554           }
555       }
556       break;
557     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
558       {
559         gboolean show_hidden = g_value_get_boolean (value);
560         if (show_hidden != impl->show_hidden)
561           {
562             impl->show_hidden = show_hidden;
563             _gtk_file_system_model_set_show_hidden (impl->tree_model, show_hidden);
564             _gtk_file_system_model_set_show_hidden (impl->list_model, show_hidden);
565           }
566       }
567       break;
568     default:
569       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
570       break;    
571     }
572 }
573
574 static void
575 gtk_file_chooser_impl_default_get_property (GObject         *object,
576                                             guint            prop_id,
577                                             GValue          *value,
578                                             GParamSpec      *pspec)
579 {
580   GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (object);
581   
582   switch (prop_id)
583     {
584     case GTK_FILE_CHOOSER_PROP_ACTION:
585       g_value_set_enum (value, impl->action);
586       break;
587     case GTK_FILE_CHOOSER_PROP_FILTER:
588       g_value_set_object (value, impl->current_filter);
589       break;
590     case GTK_FILE_CHOOSER_PROP_FOLDER_MODE:
591       g_value_set_boolean (value, impl->folder_mode);
592       break;
593     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
594       g_value_set_boolean (value, impl->local_only);
595       break;
596     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
597       g_value_set_object (value, impl->preview_widget);
598       break;
599     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
600       g_value_set_boolean (value, impl->preview_widget_active);
601       break;
602     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
603       g_value_set_boolean (value, impl->select_multiple);
604       break;
605     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
606       g_value_set_boolean (value, impl->show_hidden);
607       break;
608     default:
609       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
610       break;    
611     }
612 }
613
614 /* We override show-all since we have internal widgets that
615  * shouldn't be shown when you call show_all(), like the filter
616  * option menu.
617  */
618 static void
619 gtk_file_chooser_impl_default_show_all (GtkWidget *widget)
620 {
621   gtk_widget_show (widget);
622 }
623
624 static void
625 expand_and_select_func (GtkFileSystemModel *model,
626                         GtkTreePath        *path,
627                         GtkTreeIter        *iter,
628                         gpointer            user_data)
629 {
630   GtkFileChooserImplDefault *impl = user_data;
631   GtkTreeView *tree_view;
632
633   if (model == impl->tree_model)
634     tree_view = GTK_TREE_VIEW (impl->tree);
635   else
636     tree_view = GTK_TREE_VIEW (impl->list);
637
638   gtk_tree_view_expand_to_path (tree_view, path);
639   gtk_tree_view_expand_row (tree_view, path, FALSE);
640   gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
641   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->tree), path, NULL, TRUE, 0.3, 0.5);
642 }
643
644 static void
645 gtk_file_chooser_impl_default_set_current_folder (GtkFileChooser    *chooser,
646                                                   const GtkFilePath *path)
647 {
648   GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
649
650   _gtk_file_system_model_path_do (impl->tree_model, path,
651                                   expand_and_select_func, impl);
652 }
653
654 static GtkFilePath *
655 gtk_file_chooser_impl_default_get_current_folder (GtkFileChooser *chooser)
656 {
657   GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
658
659   return gtk_file_path_copy (impl->current_folder);
660 }
661
662 static void
663 gtk_file_chooser_impl_default_set_current_name (GtkFileChooser *chooser,
664                                                 const gchar    *name)
665 {
666   GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
667
668   _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->entry), name);
669 }
670
671 static void
672 select_func (GtkFileSystemModel *model,
673              GtkTreePath        *path,
674              GtkTreeIter        *iter,
675              gpointer            user_data)
676 {
677   GtkFileChooserImplDefault *impl = user_data;
678   GtkTreeView *tree_view = GTK_TREE_VIEW (impl->list);
679   GtkTreePath *sorted_path;
680
681   sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model, path);
682   gtk_tree_view_set_cursor (tree_view, sorted_path, NULL, FALSE);
683   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->tree), sorted_path, NULL, TRUE, 0.3, 0.0);
684   gtk_tree_path_free (sorted_path);
685 }
686
687 static void
688 gtk_file_chooser_impl_default_select_path (GtkFileChooser    *chooser,
689                                            const GtkFilePath *path)
690 {
691   GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
692   GtkFilePath *parent_path;
693   
694   if (!gtk_file_system_get_parent (impl->file_system, path, &parent_path, NULL))        /* NULL-GError */
695     return;
696
697   if (!parent_path)
698     {
699       _gtk_file_chooser_set_current_folder_path (chooser, path);
700     }
701   else
702     {
703       _gtk_file_chooser_set_current_folder_path (chooser, parent_path);
704       gtk_file_path_free (parent_path);
705       _gtk_file_system_model_path_do (impl->list_model, path,
706                                       select_func, impl);
707     }
708 }
709
710 static void
711 unselect_func (GtkFileSystemModel *model,
712                GtkTreePath        *path,
713                GtkTreeIter        *iter,
714                gpointer            user_data)
715 {
716   GtkFileChooserImplDefault *impl = user_data;
717   GtkTreeView *tree_view = GTK_TREE_VIEW (impl->list);
718   GtkTreePath *sorted_path;
719
720   sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model,
721                                                                 path);
722   gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (tree_view),
723                                     sorted_path);
724   gtk_tree_path_free (sorted_path);
725 }
726
727 static void
728 gtk_file_chooser_impl_default_unselect_path (GtkFileChooser    *chooser,
729                                              const GtkFilePath *path)
730 {
731   GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
732
733   _gtk_file_system_model_path_do (impl->list_model, path,
734                                  unselect_func, impl);
735 }
736
737 static void
738 gtk_file_chooser_impl_default_select_all (GtkFileChooser *chooser)
739 {
740   GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
741   if (impl->select_multiple)
742     {
743       GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
744       gtk_tree_selection_select_all (selection);
745     }
746 }
747
748 static void
749 gtk_file_chooser_impl_default_unselect_all (GtkFileChooser *chooser)
750 {
751   GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
752   GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
753   
754   gtk_tree_selection_unselect_all (selection);
755 }
756
757 static void
758 get_paths_foreach (GtkTreeModel *model,
759                   GtkTreePath   *path,
760                   GtkTreeIter   *iter,
761                   gpointer       data)
762 {
763   GtkTreePath *child_path;
764   GtkTreeIter child_iter;
765   const GtkFilePath *file_path;
766   
767   struct {
768     GSList *result;
769     GtkFileChooserImplDefault *impl;
770   } *info = data;
771   
772   child_path = gtk_tree_model_sort_convert_path_to_child_path (info->impl->sort_model, path);
773   gtk_tree_model_get_iter (GTK_TREE_MODEL (info->impl->list_model), &child_iter, child_path);
774   gtk_tree_path_free (child_path);
775   
776   file_path = _gtk_file_system_model_get_path (info->impl->list_model, &child_iter);
777   info->result = g_slist_prepend (info->result, gtk_file_path_copy (file_path));
778 }
779
780 static GSList *
781 gtk_file_chooser_impl_default_get_paths (GtkFileChooser *chooser)
782 {
783   GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
784   GtkTreeSelection *selection;
785   
786   struct {
787     GSList *result;
788     GtkFileChooserImplDefault *impl;
789   } info = { NULL, };
790
791   if (!impl->sort_model)
792     return NULL;
793
794   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
795   
796   info.impl = impl;
797   gtk_tree_selection_selected_foreach (selection,
798                                        get_paths_foreach, &info);
799   return g_slist_reverse (info.result);
800 }
801
802 static GtkFilePath *
803 gtk_file_chooser_impl_default_get_preview_path (GtkFileChooser *chooser)
804 {
805   GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
806     
807   if (impl->preview_path)
808     return gtk_file_path_copy (impl->preview_path);
809   else
810     return NULL;
811 }
812
813 static GtkFileSystem *
814 gtk_file_chooser_impl_default_get_file_system (GtkFileChooser *chooser)
815 {
816   GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
817
818   return impl->file_system;
819 }
820
821 static GtkWidget *
822 find_filter_menu_item (GtkFileChooserImplDefault *impl,
823                        GtkFileFilter             *filter,
824                        gint                      *index_return)
825 {
826   GtkWidget *menu = gtk_option_menu_get_menu (GTK_OPTION_MENU (impl->filter_option_menu));
827   GList *children = gtk_container_get_children (GTK_CONTAINER (menu));
828   GList *tmp_list;
829   int index = 0;
830
831   if (index_return)
832     *index_return = -1;
833
834   for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
835     {
836       if (g_object_get_data (tmp_list->data, "gtk-file-filter") == filter)
837         {
838           if (index_return)
839             *index_return = index;
840           return tmp_list->data;
841         }
842       index++;
843     }
844
845   g_list_free (children);
846   
847   return NULL;
848 }
849
850 static void
851 gtk_file_chooser_impl_default_add_filter (GtkFileChooser *chooser,
852                                           GtkFileFilter  *filter)
853 {
854   GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
855   GtkWidget *menu;
856   GtkWidget *menu_item;
857   const gchar *name;
858
859   if (g_slist_find (impl->filters, filter))
860     {
861       g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
862       return;
863     }
864
865   g_object_ref (filter);
866   gtk_object_sink (GTK_OBJECT (filter));
867   impl->filters = g_slist_append (impl->filters, filter);
868
869   name = gtk_file_filter_get_name (filter);
870   if (!name)
871     name = "Untitled filter";   /* Place-holder, doesn't need to be marked for translation */
872   
873   menu_item = gtk_menu_item_new_with_label (name);
874   g_object_set_data (G_OBJECT (menu_item), "gtk-file-filter", filter);
875   gtk_widget_show (menu_item);
876   
877   menu = gtk_option_menu_get_menu (GTK_OPTION_MENU (impl->filter_option_menu));
878   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
879   /* Option menus don't react to menu size changes properly */
880   gtk_widget_size_request (menu, NULL);
881
882   if (!g_slist_find (impl->filters, impl->current_filter))
883     set_current_filter (impl, filter);
884
885   gtk_widget_show (impl->filter_alignment);
886 }
887
888 static void
889 gtk_file_chooser_impl_default_remove_filter (GtkFileChooser    *chooser,
890                                              GtkFileFilter     *filter)
891 {
892   GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
893   GtkWidget *menu;
894   GtkWidget *menu_item;
895     
896   if (!g_slist_find (impl->filters, filter))
897     {
898       g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
899       return;
900     }
901
902   impl->filters = g_slist_remove (impl->filters, filter);
903
904   if (filter == impl->current_filter)
905     {
906       if (impl->filters)
907         set_current_filter (impl, impl->filters->data);
908       else
909         set_current_filter (impl, NULL);
910     }
911
912   menu = gtk_option_menu_get_menu (GTK_OPTION_MENU (impl->filter_option_menu));
913   menu_item = find_filter_menu_item (impl, filter, NULL);
914   g_assert (menu_item);
915   gtk_widget_destroy (menu_item);
916   /* Option menus don't react to menu size changes properly */
917   gtk_widget_size_request (menu, NULL);
918   
919   g_object_unref (filter);
920
921   if (!impl->filters)
922     gtk_widget_hide (impl->filter_alignment);
923 }
924
925 static GSList *
926 gtk_file_chooser_impl_default_list_filters (GtkFileChooser *chooser)
927 {
928   GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
929
930   return g_slist_copy (impl->filters);
931 }
932
933 static gboolean
934 list_model_filter_func (GtkFileSystemModel *model,
935                         GtkFilePath        *path,
936                         const GtkFileInfo  *file_info,
937                         gpointer            user_data)
938 {
939   GtkFileChooserImplDefault *impl = user_data;
940   GtkFileFilterInfo filter_info;
941   GtkFileFilterFlags needed;
942   gboolean result;
943   
944   if (!impl->current_filter)
945     return TRUE;
946
947   filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
948     
949   needed = gtk_file_filter_get_needed (impl->current_filter);
950   
951   filter_info.display_name = gtk_file_info_get_display_name (file_info);
952   filter_info.mime_type = gtk_file_info_get_mime_type (file_info);
953
954   if (needed & GTK_FILE_FILTER_FILENAME)
955     {
956       filter_info.filename = gtk_file_system_path_to_filename (impl->file_system, path);
957       if (filter_info.filename)
958         filter_info.contains |= GTK_FILE_FILTER_FILENAME;
959     }
960   else
961     filter_info.filename = NULL;
962
963   if (needed & GTK_FILE_FILTER_URI)
964     {
965       filter_info.uri = gtk_file_system_path_to_uri (impl->file_system, path);
966       if (filter_info.filename)
967         filter_info.contains |= GTK_FILE_FILTER_URI;
968     }
969   else
970     filter_info.uri = NULL;
971
972   result = gtk_file_filter_filter (impl->current_filter, &filter_info);
973
974   if (filter_info.filename)
975     g_free ((gchar *)filter_info.filename);
976   if (filter_info.uri)
977     g_free ((gchar *)filter_info.uri);
978
979   return result;
980 }
981
982 static void
983 install_list_model_filter (GtkFileChooserImplDefault *impl)
984 {
985   if (impl->current_filter)
986     _gtk_file_system_model_set_filter (impl->list_model,
987                                        list_model_filter_func,
988                                        impl);
989 }
990
991 static void
992 set_current_filter (GtkFileChooserImplDefault *impl,
993                     GtkFileFilter             *filter)
994 {
995   if (impl->current_filter != filter)
996     {
997       int menu_item_index;
998
999       /* If we have filters, new filter must be one of them
1000        */
1001       find_filter_menu_item (impl, filter, &menu_item_index);
1002       if (impl->filters && menu_item_index < 0)
1003         return;
1004       
1005       if (impl->current_filter)
1006         g_object_unref (impl->current_filter);
1007       impl->current_filter = filter;
1008       if (impl->current_filter)
1009         {
1010           g_object_ref (impl->current_filter);
1011           gtk_object_sink (GTK_OBJECT (filter));
1012         }
1013       
1014       if (impl->filters)
1015         gtk_option_menu_set_history (GTK_OPTION_MENU (impl->filter_option_menu),
1016                                      menu_item_index);
1017
1018       install_list_model_filter (impl);
1019
1020       g_object_notify (G_OBJECT (impl), "filter");
1021     }
1022 }
1023
1024 static gint
1025 name_sort_func (GtkTreeModel *model,
1026                 GtkTreeIter  *a,
1027                 GtkTreeIter  *b,
1028                 gpointer      user_data)
1029 {
1030   GtkFileChooserImplDefault *impl = user_data;
1031   const GtkFileInfo *info_a = _gtk_file_system_model_get_info (impl->tree_model, a);
1032   const GtkFileInfo *info_b = _gtk_file_system_model_get_info (impl->tree_model, b);
1033
1034   return strcmp (gtk_file_info_get_display_key (info_a), gtk_file_info_get_display_key (info_b));
1035 }
1036
1037 static gint
1038 size_sort_func (GtkTreeModel *model,
1039                 GtkTreeIter  *a,
1040                 GtkTreeIter  *b,
1041                 gpointer      user_data)
1042 {
1043   GtkFileChooserImplDefault *impl = user_data;
1044   const GtkFileInfo *info_a = _gtk_file_system_model_get_info (impl->tree_model, a);
1045   const GtkFileInfo *info_b = _gtk_file_system_model_get_info (impl->tree_model, b);
1046   gint64 size_a = gtk_file_info_get_size (info_a);
1047   gint64 size_b = gtk_file_info_get_size (info_b);
1048
1049   return size_a > size_b ? -1 : (size_a == size_b ? 0 : 1);
1050 }
1051
1052 static void
1053 open_and_close (GtkTreeView *tree_view,
1054                 GtkTreePath *target_path)
1055 {
1056   GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
1057   GtkTreeIter iter;
1058   GtkTreePath *path;
1059
1060   path = gtk_tree_path_new ();
1061   gtk_tree_path_append_index (path, 0);
1062
1063   gtk_tree_model_get_iter (model, &iter, path);
1064
1065   while (TRUE)
1066     {
1067       if (gtk_tree_path_is_ancestor (path, target_path) ||
1068           gtk_tree_path_compare (path, target_path) == 0)
1069         {
1070           GtkTreeIter child_iter;
1071           gtk_tree_view_expand_row (tree_view, path, FALSE);
1072           if (gtk_tree_model_iter_children (model, &child_iter, &iter))
1073             {
1074               iter = child_iter;
1075               gtk_tree_path_down (path);
1076               goto next;
1077             }
1078         }
1079       else
1080         gtk_tree_view_collapse_row (tree_view, path);
1081
1082       while (TRUE)
1083         {
1084           GtkTreeIter parent_iter;
1085           GtkTreeIter next_iter;
1086       
1087           next_iter = iter;
1088           if (gtk_tree_model_iter_next (model, &next_iter))
1089             {
1090               iter = next_iter;
1091               gtk_tree_path_next (path);
1092               goto next;
1093             }
1094
1095           if (!gtk_tree_model_iter_parent (model, &parent_iter, &iter))
1096             goto out;
1097
1098           iter = parent_iter;
1099           gtk_tree_path_up (path);
1100         }
1101     next:
1102       ;
1103     }
1104   
1105  out:
1106   gtk_tree_path_free (path);
1107 }
1108
1109 static void
1110 update_chooser_entry (GtkFileChooserImplDefault *impl)
1111 {
1112   GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
1113   const GtkFileInfo *info;
1114   GtkTreeIter iter;
1115   GtkTreeIter child_iter;
1116
1117   /* Fixing this for multiple selection involves getting the full
1118    * selection and diffing to find out what the most recently selected
1119    * file is; there is logic in GtkFileSelection that probably can
1120    * be copied; check_preview_change() is similar.
1121    */
1122   if (impl->select_multiple ||
1123       !gtk_tree_selection_get_selected (selection, NULL, &iter))
1124     return;
1125
1126   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
1127                                                   &child_iter,
1128                                                   &iter);
1129
1130   info = _gtk_file_system_model_get_info (impl->list_model, &child_iter);
1131   
1132   _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->entry),
1133                                          gtk_file_info_get_display_name (info));
1134 }
1135      
1136 static void
1137 filter_option_menu_changed (GtkOptionMenu             *option_menu,
1138                             GtkFileChooserImplDefault *impl)
1139 {
1140   gint new_index = gtk_option_menu_get_history (GTK_OPTION_MENU (option_menu));
1141   GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
1142
1143   set_current_filter (impl, new_filter);
1144 }
1145
1146 static void
1147 check_preview_change (GtkFileChooserImplDefault *impl)
1148 {
1149   const GtkFilePath *new_path = NULL;
1150
1151   /* Fixing preview for multiple selection involves getting the full
1152    * selection and diffing to find out what the most recently selected
1153    * file is; there is logic in GtkFileSelection that probably can
1154    * be copied. update_chooser_entry() is similar.
1155    */
1156   if (impl->sort_model && !impl->select_multiple)
1157     {
1158       GtkTreeSelection *selection;
1159       GtkTreeIter iter;
1160       
1161       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
1162       if (gtk_tree_selection_get_selected  (selection, NULL, &iter))
1163         {
1164           GtkTreeIter child_iter;
1165           
1166           gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
1167                                                           &child_iter, &iter);
1168           
1169           new_path = _gtk_file_system_model_get_path (impl->list_model, &child_iter);
1170         }
1171     }
1172       
1173   if (new_path != impl->preview_path &&
1174       !(new_path && impl->preview_path &&
1175         gtk_file_path_compare (new_path, impl->preview_path) == 0))
1176     {
1177       if (impl->preview_path)
1178         gtk_file_path_free (impl->preview_path);
1179       
1180       if (new_path)
1181         impl->preview_path = gtk_file_path_copy (new_path);
1182       else
1183         impl->preview_path = NULL;
1184           
1185       g_signal_emit_by_name (impl, "update-preview");
1186     }
1187 }
1188
1189 static void
1190 tree_selection_changed (GtkTreeSelection          *selection,
1191                         GtkFileChooserImplDefault *impl)
1192 {
1193   GtkTreeIter iter;
1194   const GtkFilePath *file_path;
1195   GtkTreePath *path;
1196
1197   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
1198     return;
1199
1200   file_path = _gtk_file_system_model_get_path (impl->tree_model, &iter);
1201   if (impl->current_folder && gtk_file_path_compare (file_path, impl->current_folder) == 0)
1202     return;
1203   
1204   if (impl->current_folder)
1205     gtk_file_path_free (impl->current_folder);
1206   impl->current_folder = gtk_file_path_copy (file_path);
1207   _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->entry), file_path);
1208   
1209   if (impl->list_model)
1210     {
1211       g_object_unref (impl->list_model);
1212       impl->list_model = NULL;
1213       
1214       g_object_unref (impl->sort_model);
1215       impl->sort_model = NULL;
1216     }
1217   
1218   /* Close the tree up to only the parents of the newly selected
1219    * node and it's immediate children are visible.
1220    */
1221   path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->tree_model), &iter);
1222   open_and_close (GTK_TREE_VIEW (impl->tree), path);
1223   gtk_tree_path_free (path);
1224   
1225   /* Now update the list view to show the new row.
1226    */
1227   file_path = _gtk_file_system_model_get_path (impl->tree_model, &iter);
1228   
1229   impl->list_model = _gtk_file_system_model_new (impl->file_system,
1230                                                  file_path, 0,
1231                                                  GTK_FILE_INFO_ICON |
1232                                                  GTK_FILE_INFO_DISPLAY_NAME |
1233                                                  GTK_FILE_INFO_SIZE); 
1234   _gtk_file_system_model_set_show_folders (impl->list_model, FALSE);
1235   install_list_model_filter (impl);
1236   
1237   impl->sort_model = (GtkTreeModelSort *)gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (impl->list_model));
1238   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), 0, name_sort_func, impl, NULL);
1239   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), 1, size_sort_func, impl, NULL);
1240   gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->sort_model),
1241                                            name_sort_func, impl, NULL);
1242   
1243   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->list),
1244                            GTK_TREE_MODEL (impl->sort_model));
1245   gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->list),
1246                                    GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
1247
1248   g_signal_emit_by_name (impl, "current-folder-changed", 0);
1249
1250   update_chooser_entry (impl);
1251   check_preview_change (impl);
1252
1253   g_signal_emit_by_name (impl, "selection-changed", 0);
1254 }
1255
1256 static void
1257 list_selection_changed (GtkTreeSelection          *selection,
1258                         GtkFileChooserImplDefault *impl)
1259 {
1260   update_chooser_entry (impl);
1261   check_preview_change (impl);
1262   
1263   g_signal_emit_by_name (impl, "selection-changed", 0);
1264 }
1265
1266 static void
1267 entry_activate (GtkEntry                  *entry,
1268                 GtkFileChooserImplDefault *impl)
1269 {
1270   GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (entry);
1271   const GtkFilePath *folder_path = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
1272   const gchar *file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
1273   GtkFilePath *new_folder = NULL;
1274
1275   /* If the file part is non-empty, we need to figure out if it
1276    * refers to a folder within folder. We could optimize the case
1277    * here where the folder is already loaded for one of our tree models.
1278    */
1279   if (file_part[0] == '\0' && gtk_file_path_compare (impl->current_folder, folder_path) != 0)
1280     new_folder = gtk_file_path_copy (folder_path);
1281   else
1282     {
1283       GtkFileFolder *folder = NULL;
1284       GtkFilePath *subfolder_path = NULL;
1285       GtkFileInfo *info = NULL;
1286       
1287       folder = gtk_file_system_get_folder (impl->file_system,
1288                                            folder_path,
1289                                            GTK_FILE_INFO_IS_FOLDER,
1290                                            NULL);       /* NULL-GError */
1291       
1292       if (folder)
1293         subfolder_path = gtk_file_system_make_path (impl->file_system,
1294                                                   folder_path,
1295                                                   file_part,
1296                                                   NULL); /* NULL-GError */
1297
1298       if (subfolder_path)
1299         info = gtk_file_folder_get_info (folder,
1300                                          subfolder_path,
1301                                          NULL); /* NULL-GError */
1302
1303       if (info && gtk_file_info_get_is_folder (info))
1304         new_folder = gtk_file_path_copy (subfolder_path);
1305
1306       if (folder)
1307         g_object_unref (folder);
1308
1309       if (subfolder_path)
1310         gtk_file_path_free (subfolder_path);
1311
1312       if (info)
1313         gtk_file_info_free (info);
1314     }
1315
1316   if (new_folder)
1317     {
1318       g_signal_stop_emission_by_name (entry, "activate");
1319
1320       _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), new_folder);
1321       _gtk_file_chooser_entry_set_file_part (chooser_entry, "");
1322
1323       gtk_file_path_free (new_folder);
1324     }
1325 }
1326
1327 static const GtkFileInfo *
1328 get_list_file_info (GtkFileChooserImplDefault *impl,
1329                     GtkTreeIter               *iter)
1330 {
1331   GtkTreeIter child_iter;
1332
1333   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
1334                                                   &child_iter,
1335                                                   iter);
1336
1337   return _gtk_file_system_model_get_info (impl->tree_model, &child_iter);
1338 }
1339
1340 static void
1341 tree_name_data_func (GtkTreeViewColumn *tree_column,
1342                      GtkCellRenderer   *cell,
1343                      GtkTreeModel      *tree_model,
1344                      GtkTreeIter       *iter,
1345                      gpointer           data)
1346 {
1347   GtkFileChooserImplDefault *impl = data;
1348   const GtkFileInfo *info = _gtk_file_system_model_get_info (impl->tree_model, iter);
1349
1350   if (info)
1351     {
1352       g_object_set (cell,
1353                     "text", gtk_file_info_get_display_name (info),
1354                     NULL);
1355     }
1356 }
1357
1358 static void
1359 list_icon_data_func (GtkTreeViewColumn *tree_column,
1360                      GtkCellRenderer   *cell,
1361                      GtkTreeModel      *tree_model,
1362                      GtkTreeIter       *iter,
1363                      gpointer           data)
1364 {
1365   GtkFileChooserImplDefault *impl = data;
1366   const GtkFileInfo *info = get_list_file_info (impl, iter);
1367
1368   if (info)
1369     {
1370       GtkWidget *widget = GTK_TREE_VIEW_COLUMN (tree_column)->tree_view;
1371       GdkPixbuf *pixbuf = gtk_file_info_render_icon (info, widget, 36);
1372       
1373       g_object_set (cell,
1374                     "pixbuf", pixbuf,
1375                     NULL);
1376
1377       if (pixbuf)
1378         g_object_unref (pixbuf);
1379     }
1380 }
1381
1382 static void
1383 list_name_data_func (GtkTreeViewColumn *tree_column,
1384                      GtkCellRenderer   *cell,
1385                      GtkTreeModel      *tree_model,
1386                      GtkTreeIter       *iter,
1387                      gpointer           data)
1388 {
1389   GtkFileChooserImplDefault *impl = data;
1390   const GtkFileInfo *info = get_list_file_info (impl, iter);
1391
1392   if (info)
1393     {
1394       g_object_set (cell,
1395                     "text", gtk_file_info_get_display_name (info),
1396                     NULL);
1397     }
1398 }
1399
1400 static void
1401 list_size_data_func (GtkTreeViewColumn *tree_column,
1402                      GtkCellRenderer   *cell,
1403                      GtkTreeModel      *tree_model,
1404                      GtkTreeIter       *iter,
1405                      gpointer           data)
1406 {
1407   GtkFileChooserImplDefault *impl = data;
1408   const GtkFileInfo *info = get_list_file_info (impl, iter);
1409
1410   if (info)
1411     {
1412       gint64 size = gtk_file_info_get_size (info);
1413       gchar *str;
1414       
1415       if (size < (gint64)1024)
1416         str = g_strdup_printf ("%d bytes", (gint)size);
1417       else if (size < (gint64)1024*1024)
1418         str = g_strdup_printf ("%.1f K", size / (1024.));
1419       else if (size < (gint64)1024*1024*1024)
1420         str = g_strdup_printf ("%.1f M", size / (1024.*1024.));
1421       else
1422         str = g_strdup_printf ("%.1f G", size / (1024.*1024.*1024.));
1423
1424       g_object_set (cell,
1425                     "text", str,
1426                     NULL);
1427       
1428       g_free (str);
1429     }
1430
1431 }
1432
1433 GtkWidget *
1434 _gtk_file_chooser_impl_default_new (GtkFileSystem *file_system)
1435 {
1436   return  g_object_new (GTK_TYPE_FILE_CHOOSER_IMPL_DEFAULT,
1437                         "file-system", file_system,
1438                         NULL);
1439 }