1 /* GTK - The GIMP Toolkit
2 * gtkfilechooserimpldefault.c: Default implementation of GtkFileChooser
3 * Copyright (C) 2003, Red Hat, Inc.
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.
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.
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.
21 #include "gtkfilechooserimpldefault.h"
22 #include "gtkfilechooserentry.h"
23 #include "gtkfilechooserenums.h"
24 #include "gtkfilechooserutils.h"
25 #include "gtkfilechooser.h"
26 #include "gtkfilesystemmodel.h"
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>
48 typedef struct _GtkFileChooserImplDefaultClass GtkFileChooserImplDefaultClass;
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))
54 struct _GtkFileChooserImplDefaultClass
56 GtkVBoxClass parent_class;
59 struct _GtkFileChooserImplDefault
61 GtkVBox parent_instance;
63 GtkFileSystem *file_system;
64 GtkFileSystemModel *tree_model;
65 GtkFileSystemModel *list_model;
66 GtkTreeModelSort *sort_model;
68 GtkFileChooserAction action;
70 GtkFileFilter *current_filter;
73 GtkFilePath *current_folder;
74 GtkFilePath *preview_path;
76 GtkWidget *preview_frame;
78 guint folder_mode : 1;
80 guint preview_widget_active : 1;
81 guint select_multiple : 1;
82 guint show_hidden : 1;
84 GtkWidget *filter_alignment;
85 GtkWidget *filter_option_menu;
86 GtkWidget *tree_scrollwin;
88 GtkWidget *list_scrollwin;
91 GtkWidget *preview_widget;
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);
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,
106 static void gtk_file_chooser_impl_default_get_property (GObject *object,
110 static void gtk_file_chooser_impl_default_show_all (GtkWidget *widget);
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,
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);
132 static void set_current_filter (GtkFileChooserImplDefault *impl,
133 GtkFileFilter *filter);
134 static void check_preview_change (GtkFileChooserImplDefault *impl);
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);
145 static void tree_name_data_func (GtkTreeViewColumn *tree_column,
146 GtkCellRenderer *cell,
147 GtkTreeModel *tree_model,
150 static void list_icon_data_func (GtkTreeViewColumn *tree_column,
151 GtkCellRenderer *cell,
152 GtkTreeModel *tree_model,
155 static void list_name_data_func (GtkTreeViewColumn *tree_column,
156 GtkCellRenderer *cell,
157 GtkTreeModel *tree_model,
160 static void list_size_data_func (GtkTreeViewColumn *tree_column,
161 GtkCellRenderer *cell,
162 GtkTreeModel *tree_model,
166 static GObjectClass *parent_class;
169 _gtk_file_chooser_impl_default_get_type (void)
171 static GType file_chooser_impl_default_type = 0;
173 if (!file_chooser_impl_default_type)
175 static const GTypeInfo file_chooser_impl_default_info =
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),
185 (GInstanceInitFunc) gtk_file_chooser_impl_default_init,
188 static const GInterfaceInfo file_chooser_info =
190 (GInterfaceInitFunc) gtk_file_chooser_impl_default_iface_init, /* interface_init */
191 NULL, /* interface_finalize */
192 NULL /* interface_data */
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,
202 return file_chooser_impl_default_type;
206 gtk_file_chooser_impl_default_class_init (GtkFileChooserImplDefaultClass *class)
208 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
209 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
211 parent_class = g_type_class_peek_parent (class);
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;
218 widget_class->show_all = gtk_file_chooser_impl_default_show_all;
220 _gtk_file_chooser_install_properties (gobject_class);
224 gtk_file_chooser_impl_default_iface_init (GtkFileChooserIface *iface)
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;
242 gtk_file_chooser_impl_default_init (GtkFileChooserImplDefault *impl)
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;
250 gtk_container_set_border_width (GTK_CONTAINER (impl), 5);
254 gtk_file_chooser_impl_default_finalize (GObject *object)
256 GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (object);
258 g_object_unref (impl->file_system);
260 G_OBJECT_CLASS (parent_class)->finalize (object);
264 update_preview_widget_visibility (GtkFileChooserImplDefault *impl)
266 if (impl->preview_widget_active && impl->preview_widget)
267 gtk_widget_show (impl->preview_frame);
269 gtk_widget_hide (impl->preview_frame);
273 set_preview_widget (GtkFileChooserImplDefault *impl,
274 GtkWidget *preview_widget)
276 if (preview_widget == impl->preview_widget)
279 if (impl->preview_widget)
281 g_object_unref (impl->preview_widget);
282 impl->preview_widget = NULL;
284 gtk_container_remove (GTK_CONTAINER (impl->preview_frame),
285 impl->preview_widget);
288 impl->preview_widget = preview_widget;
289 if (impl->preview_widget)
291 g_object_ref (impl->preview_widget);
292 gtk_object_sink (GTK_OBJECT (impl->preview_widget));
294 gtk_widget_show (impl->preview_widget);
295 gtk_container_add (GTK_CONTAINER (impl->preview_frame),
296 impl->preview_widget);
299 update_preview_widget_visibility (impl);
303 gtk_file_chooser_impl_default_constructor (GType type,
304 guint n_construct_properties,
305 GObjectConstructParam *construct_params)
307 GtkFileChooserImplDefault *impl;
308 GtkTreeViewColumn *column;
309 GtkCellRenderer *renderer;
315 GtkTreeSelection *selection;
320 object = parent_class->constructor (type,
321 n_construct_properties,
323 impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (object);
325 g_assert (impl->file_system);
327 gtk_widget_push_composite_child ();
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);
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,
338 GTK_EXPAND | GTK_FILL, 0,
340 /* Don't show filter initially */
342 hbox = gtk_hbox_new (FALSE, 6);
343 gtk_container_add (GTK_CONTAINER (impl->filter_alignment), hbox);
344 gtk_widget_show (hbox);
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);
350 impl->filter_option_menu = gtk_option_menu_new ();
351 gtk_option_menu_set_menu (GTK_OPTION_MENU (impl->filter_option_menu),
353 gtk_box_pack_start (GTK_BOX (hbox), impl->filter_option_menu, FALSE, FALSE, 0);
354 gtk_widget_show (impl->filter_option_menu);
356 gtk_label_set_mnemonic_widget (GTK_LABEL (label), impl->filter_option_menu);
358 g_signal_connect (impl->filter_option_menu, "changed",
359 G_CALLBACK (filter_option_menu_changed), impl);
361 hpaned = gtk_hpaned_new ();
362 gtk_table_attach (GTK_TABLE (table), hpaned,
364 GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
366 gtk_widget_show (hpaned);
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),
373 gtk_paned_add1 (GTK_PANED (hpaned), impl->tree_scrollwin);
374 gtk_widget_show (impl->tree_scrollwin);
376 impl->tree = gtk_tree_view_new ();
377 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->tree), FALSE);
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);
383 gtk_paned_set_position (GTK_PANED (hpaned), 200);
385 gtk_container_add (GTK_CONTAINER (impl->tree_scrollwin), impl->tree);
386 gtk_widget_show (impl->tree);
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),
393 gtk_paned_add2 (GTK_PANED (hpaned), impl->list_scrollwin);
394 gtk_widget_show (impl->list_scrollwin);
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);
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);
405 hbox = gtk_hbox_new (FALSE, 6);
406 gtk_table_attach (GTK_TABLE (table), hbox,
408 GTK_EXPAND | GTK_FILL, 0,
410 gtk_widget_show (hbox);
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);
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);
421 gtk_box_pack_start (GTK_BOX (hbox), impl->entry, TRUE, TRUE, 0);
422 gtk_widget_show (impl->entry);
424 gtk_label_set_mnemonic_widget (GTK_LABEL (label), impl->entry);
426 impl->preview_frame = gtk_frame_new ("Preview");
427 gtk_table_attach (GTK_TABLE (table), impl->preview_frame,
429 0, GTK_EXPAND | GTK_FILL,
431 /* Don't show preview frame initially */
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);
441 gtk_widget_pop_composite_child ();
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);
447 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->tree),
448 GTK_TREE_MODEL (impl->tree_model));
450 gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (impl->tree), 0,
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);
457 column = gtk_tree_view_column_new ();
458 gtk_tree_view_column_set_title (column, "File name");
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);
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);
472 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->list), column);
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);
483 _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->entry),
489 gtk_file_chooser_impl_default_set_property (GObject *object,
495 GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (object);
499 case GTK_FILE_CHOOSER_PROP_ACTION:
500 impl->action = g_value_get_enum (value);
502 case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM:
504 GtkFileSystem *file_system = g_value_get_object (value);
505 if (impl->file_system != file_system)
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);
515 case GTK_FILE_CHOOSER_PROP_FILTER:
516 set_current_filter (impl, g_value_get_object (value));
518 case GTK_FILE_CHOOSER_PROP_FOLDER_MODE:
520 gboolean folder_mode = g_value_get_boolean (value);
521 if (folder_mode != impl->folder_mode)
523 impl->folder_mode = folder_mode;
524 if (impl->folder_mode)
525 gtk_widget_hide (impl->list_scrollwin);
527 gtk_widget_show (impl->list_scrollwin);
531 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
532 impl->local_only = g_value_get_boolean (value);
534 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
535 set_preview_widget (impl, g_value_get_object (value));
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);
541 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
543 gboolean select_multiple = g_value_get_boolean (value);
544 if (select_multiple != impl->select_multiple)
546 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
548 impl->select_multiple = select_multiple;
549 gtk_tree_selection_set_mode (selection,
551 GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE));
552 /* FIXME: See note in check_preview_change() */
553 check_preview_change (impl);
557 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
559 gboolean show_hidden = g_value_get_boolean (value);
560 if (show_hidden != impl->show_hidden)
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);
569 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
575 gtk_file_chooser_impl_default_get_property (GObject *object,
580 GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (object);
584 case GTK_FILE_CHOOSER_PROP_ACTION:
585 g_value_set_enum (value, impl->action);
587 case GTK_FILE_CHOOSER_PROP_FILTER:
588 g_value_set_object (value, impl->current_filter);
590 case GTK_FILE_CHOOSER_PROP_FOLDER_MODE:
591 g_value_set_boolean (value, impl->folder_mode);
593 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
594 g_value_set_boolean (value, impl->local_only);
596 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
597 g_value_set_object (value, impl->preview_widget);
599 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
600 g_value_set_boolean (value, impl->preview_widget_active);
602 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
603 g_value_set_boolean (value, impl->select_multiple);
605 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
606 g_value_set_boolean (value, impl->show_hidden);
609 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
614 /* We override show-all since we have internal widgets that
615 * shouldn't be shown when you call show_all(), like the filter
619 gtk_file_chooser_impl_default_show_all (GtkWidget *widget)
621 gtk_widget_show (widget);
625 expand_and_select_func (GtkFileSystemModel *model,
630 GtkFileChooserImplDefault *impl = user_data;
631 GtkTreeView *tree_view;
633 if (model == impl->tree_model)
634 tree_view = GTK_TREE_VIEW (impl->tree);
636 tree_view = GTK_TREE_VIEW (impl->list);
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);
645 gtk_file_chooser_impl_default_set_current_folder (GtkFileChooser *chooser,
646 const GtkFilePath *path)
648 GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
650 _gtk_file_system_model_path_do (impl->tree_model, path,
651 expand_and_select_func, impl);
655 gtk_file_chooser_impl_default_get_current_folder (GtkFileChooser *chooser)
657 GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
659 return gtk_file_path_copy (impl->current_folder);
663 gtk_file_chooser_impl_default_set_current_name (GtkFileChooser *chooser,
666 GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
668 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->entry), name);
672 select_func (GtkFileSystemModel *model,
677 GtkFileChooserImplDefault *impl = user_data;
678 GtkTreeView *tree_view = GTK_TREE_VIEW (impl->list);
679 GtkTreePath *sorted_path;
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);
688 gtk_file_chooser_impl_default_select_path (GtkFileChooser *chooser,
689 const GtkFilePath *path)
691 GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
692 GtkFilePath *parent_path;
694 if (!gtk_file_system_get_parent (impl->file_system, path, &parent_path, NULL)) /* NULL-GError */
699 _gtk_file_chooser_set_current_folder_path (chooser, path);
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,
711 unselect_func (GtkFileSystemModel *model,
716 GtkFileChooserImplDefault *impl = user_data;
717 GtkTreeView *tree_view = GTK_TREE_VIEW (impl->list);
718 GtkTreePath *sorted_path;
720 sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model,
722 gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (tree_view),
724 gtk_tree_path_free (sorted_path);
728 gtk_file_chooser_impl_default_unselect_path (GtkFileChooser *chooser,
729 const GtkFilePath *path)
731 GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
733 _gtk_file_system_model_path_do (impl->list_model, path,
734 unselect_func, impl);
738 gtk_file_chooser_impl_default_select_all (GtkFileChooser *chooser)
740 GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
741 if (impl->select_multiple)
743 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
744 gtk_tree_selection_select_all (selection);
749 gtk_file_chooser_impl_default_unselect_all (GtkFileChooser *chooser)
751 GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
752 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
754 gtk_tree_selection_unselect_all (selection);
758 get_paths_foreach (GtkTreeModel *model,
763 GtkTreePath *child_path;
764 GtkTreeIter child_iter;
765 const GtkFilePath *file_path;
769 GtkFileChooserImplDefault *impl;
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);
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));
781 gtk_file_chooser_impl_default_get_paths (GtkFileChooser *chooser)
783 GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
784 GtkTreeSelection *selection;
788 GtkFileChooserImplDefault *impl;
791 if (!impl->sort_model)
794 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
797 gtk_tree_selection_selected_foreach (selection,
798 get_paths_foreach, &info);
799 return g_slist_reverse (info.result);
803 gtk_file_chooser_impl_default_get_preview_path (GtkFileChooser *chooser)
805 GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
807 if (impl->preview_path)
808 return gtk_file_path_copy (impl->preview_path);
813 static GtkFileSystem *
814 gtk_file_chooser_impl_default_get_file_system (GtkFileChooser *chooser)
816 GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
818 return impl->file_system;
822 find_filter_menu_item (GtkFileChooserImplDefault *impl,
823 GtkFileFilter *filter,
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));
834 for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
836 if (g_object_get_data (tmp_list->data, "gtk-file-filter") == filter)
839 *index_return = index;
840 return tmp_list->data;
845 g_list_free (children);
851 gtk_file_chooser_impl_default_add_filter (GtkFileChooser *chooser,
852 GtkFileFilter *filter)
854 GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
856 GtkWidget *menu_item;
859 if (g_slist_find (impl->filters, filter))
861 g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
865 g_object_ref (filter);
866 gtk_object_sink (GTK_OBJECT (filter));
867 impl->filters = g_slist_append (impl->filters, filter);
869 name = gtk_file_filter_get_name (filter);
871 name = "Untitled filter"; /* Place-holder, doesn't need to be marked for translation */
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);
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);
882 if (!g_slist_find (impl->filters, impl->current_filter))
883 set_current_filter (impl, filter);
885 gtk_widget_show (impl->filter_alignment);
889 gtk_file_chooser_impl_default_remove_filter (GtkFileChooser *chooser,
890 GtkFileFilter *filter)
892 GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
894 GtkWidget *menu_item;
896 if (!g_slist_find (impl->filters, filter))
898 g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
902 impl->filters = g_slist_remove (impl->filters, filter);
904 if (filter == impl->current_filter)
907 set_current_filter (impl, impl->filters->data);
909 set_current_filter (impl, NULL);
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);
919 g_object_unref (filter);
922 gtk_widget_hide (impl->filter_alignment);
926 gtk_file_chooser_impl_default_list_filters (GtkFileChooser *chooser)
928 GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (chooser);
930 return g_slist_copy (impl->filters);
934 list_model_filter_func (GtkFileSystemModel *model,
936 const GtkFileInfo *file_info,
939 GtkFileChooserImplDefault *impl = user_data;
940 GtkFileFilterInfo filter_info;
941 GtkFileFilterFlags needed;
944 if (!impl->current_filter)
947 filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
949 needed = gtk_file_filter_get_needed (impl->current_filter);
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);
954 if (needed & GTK_FILE_FILTER_FILENAME)
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;
961 filter_info.filename = NULL;
963 if (needed & GTK_FILE_FILTER_URI)
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;
970 filter_info.uri = NULL;
972 result = gtk_file_filter_filter (impl->current_filter, &filter_info);
974 if (filter_info.filename)
975 g_free ((gchar *)filter_info.filename);
977 g_free ((gchar *)filter_info.uri);
983 install_list_model_filter (GtkFileChooserImplDefault *impl)
985 if (impl->current_filter)
986 _gtk_file_system_model_set_filter (impl->list_model,
987 list_model_filter_func,
992 set_current_filter (GtkFileChooserImplDefault *impl,
993 GtkFileFilter *filter)
995 if (impl->current_filter != filter)
999 /* If we have filters, new filter must be one of them
1001 find_filter_menu_item (impl, filter, &menu_item_index);
1002 if (impl->filters && menu_item_index < 0)
1005 if (impl->current_filter)
1006 g_object_unref (impl->current_filter);
1007 impl->current_filter = filter;
1008 if (impl->current_filter)
1010 g_object_ref (impl->current_filter);
1011 gtk_object_sink (GTK_OBJECT (filter));
1015 gtk_option_menu_set_history (GTK_OPTION_MENU (impl->filter_option_menu),
1018 install_list_model_filter (impl);
1020 g_object_notify (G_OBJECT (impl), "filter");
1025 name_sort_func (GtkTreeModel *model,
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);
1034 return strcmp (gtk_file_info_get_display_key (info_a), gtk_file_info_get_display_key (info_b));
1038 size_sort_func (GtkTreeModel *model,
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);
1049 return size_a > size_b ? -1 : (size_a == size_b ? 0 : 1);
1053 open_and_close (GtkTreeView *tree_view,
1054 GtkTreePath *target_path)
1056 GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
1060 path = gtk_tree_path_new ();
1061 gtk_tree_path_append_index (path, 0);
1063 gtk_tree_model_get_iter (model, &iter, path);
1067 if (gtk_tree_path_is_ancestor (path, target_path) ||
1068 gtk_tree_path_compare (path, target_path) == 0)
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))
1075 gtk_tree_path_down (path);
1080 gtk_tree_view_collapse_row (tree_view, path);
1084 GtkTreeIter parent_iter;
1085 GtkTreeIter next_iter;
1088 if (gtk_tree_model_iter_next (model, &next_iter))
1091 gtk_tree_path_next (path);
1095 if (!gtk_tree_model_iter_parent (model, &parent_iter, &iter))
1099 gtk_tree_path_up (path);
1106 gtk_tree_path_free (path);
1110 update_chooser_entry (GtkFileChooserImplDefault *impl)
1112 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
1113 const GtkFileInfo *info;
1115 GtkTreeIter child_iter;
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.
1122 if (impl->select_multiple ||
1123 !gtk_tree_selection_get_selected (selection, NULL, &iter))
1126 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
1130 info = _gtk_file_system_model_get_info (impl->list_model, &child_iter);
1132 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->entry),
1133 gtk_file_info_get_display_name (info));
1137 filter_option_menu_changed (GtkOptionMenu *option_menu,
1138 GtkFileChooserImplDefault *impl)
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);
1143 set_current_filter (impl, new_filter);
1147 check_preview_change (GtkFileChooserImplDefault *impl)
1149 const GtkFilePath *new_path = NULL;
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.
1156 if (impl->sort_model && !impl->select_multiple)
1158 GtkTreeSelection *selection;
1161 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
1162 if (gtk_tree_selection_get_selected (selection, NULL, &iter))
1164 GtkTreeIter child_iter;
1166 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
1167 &child_iter, &iter);
1169 new_path = _gtk_file_system_model_get_path (impl->list_model, &child_iter);
1173 if (new_path != impl->preview_path &&
1174 !(new_path && impl->preview_path &&
1175 gtk_file_path_compare (new_path, impl->preview_path) == 0))
1177 if (impl->preview_path)
1178 gtk_file_path_free (impl->preview_path);
1181 impl->preview_path = gtk_file_path_copy (new_path);
1183 impl->preview_path = NULL;
1185 g_signal_emit_by_name (impl, "update-preview");
1190 tree_selection_changed (GtkTreeSelection *selection,
1191 GtkFileChooserImplDefault *impl)
1194 const GtkFilePath *file_path;
1197 if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
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)
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);
1209 if (impl->list_model)
1211 g_object_unref (impl->list_model);
1212 impl->list_model = NULL;
1214 g_object_unref (impl->sort_model);
1215 impl->sort_model = NULL;
1218 /* Close the tree up to only the parents of the newly selected
1219 * node and it's immediate children are visible.
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);
1225 /* Now update the list view to show the new row.
1227 file_path = _gtk_file_system_model_get_path (impl->tree_model, &iter);
1229 impl->list_model = _gtk_file_system_model_new (impl->file_system,
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);
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);
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);
1248 g_signal_emit_by_name (impl, "current-folder-changed", 0);
1250 update_chooser_entry (impl);
1251 check_preview_change (impl);
1253 g_signal_emit_by_name (impl, "selection-changed", 0);
1257 list_selection_changed (GtkTreeSelection *selection,
1258 GtkFileChooserImplDefault *impl)
1260 update_chooser_entry (impl);
1261 check_preview_change (impl);
1263 g_signal_emit_by_name (impl, "selection-changed", 0);
1267 entry_activate (GtkEntry *entry,
1268 GtkFileChooserImplDefault *impl)
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;
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.
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);
1283 GtkFileFolder *folder = NULL;
1284 GtkFilePath *subfolder_path = NULL;
1285 GtkFileInfo *info = NULL;
1287 folder = gtk_file_system_get_folder (impl->file_system,
1289 GTK_FILE_INFO_IS_FOLDER,
1290 NULL); /* NULL-GError */
1293 subfolder_path = gtk_file_system_make_path (impl->file_system,
1296 NULL); /* NULL-GError */
1299 info = gtk_file_folder_get_info (folder,
1301 NULL); /* NULL-GError */
1303 if (info && gtk_file_info_get_is_folder (info))
1304 new_folder = gtk_file_path_copy (subfolder_path);
1307 g_object_unref (folder);
1310 gtk_file_path_free (subfolder_path);
1313 gtk_file_info_free (info);
1318 g_signal_stop_emission_by_name (entry, "activate");
1320 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), new_folder);
1321 _gtk_file_chooser_entry_set_file_part (chooser_entry, "");
1323 gtk_file_path_free (new_folder);
1327 static const GtkFileInfo *
1328 get_list_file_info (GtkFileChooserImplDefault *impl,
1331 GtkTreeIter child_iter;
1333 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
1337 return _gtk_file_system_model_get_info (impl->tree_model, &child_iter);
1341 tree_name_data_func (GtkTreeViewColumn *tree_column,
1342 GtkCellRenderer *cell,
1343 GtkTreeModel *tree_model,
1347 GtkFileChooserImplDefault *impl = data;
1348 const GtkFileInfo *info = _gtk_file_system_model_get_info (impl->tree_model, iter);
1353 "text", gtk_file_info_get_display_name (info),
1359 list_icon_data_func (GtkTreeViewColumn *tree_column,
1360 GtkCellRenderer *cell,
1361 GtkTreeModel *tree_model,
1365 GtkFileChooserImplDefault *impl = data;
1366 const GtkFileInfo *info = get_list_file_info (impl, iter);
1370 GtkWidget *widget = GTK_TREE_VIEW_COLUMN (tree_column)->tree_view;
1371 GdkPixbuf *pixbuf = gtk_file_info_render_icon (info, widget, 36);
1378 g_object_unref (pixbuf);
1383 list_name_data_func (GtkTreeViewColumn *tree_column,
1384 GtkCellRenderer *cell,
1385 GtkTreeModel *tree_model,
1389 GtkFileChooserImplDefault *impl = data;
1390 const GtkFileInfo *info = get_list_file_info (impl, iter);
1395 "text", gtk_file_info_get_display_name (info),
1401 list_size_data_func (GtkTreeViewColumn *tree_column,
1402 GtkCellRenderer *cell,
1403 GtkTreeModel *tree_model,
1407 GtkFileChooserImplDefault *impl = data;
1408 const GtkFileInfo *info = get_list_file_info (impl, iter);
1412 gint64 size = gtk_file_info_get_size (info);
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.));
1422 str = g_strdup_printf ("%.1f G", size / (1024.*1024.*1024.));
1434 _gtk_file_chooser_impl_default_new (GtkFileSystem *file_system)
1436 return g_object_new (GTK_TYPE_FILE_CHOOSER_IMPL_DEFAULT,
1437 "file-system", file_system,