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