1 /* GTK - The GIMP Toolkit
2 * gtkfilechooserdefault.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 "gdk/gdkkeysyms.h"
22 #include "gtkalignment.h"
23 #include "gtkbindings.h"
24 #include "gtkbutton.h"
25 #include "gtkcellrendererpixbuf.h"
26 #include "gtkcellrendererseptext.h"
27 #include "gtkcellrenderertext.h"
28 #include "gtkcombobox.h"
30 #include "gtkexpander.h"
31 #include "gtkfilechooserdefault.h"
32 #include "gtkfilechooserembed.h"
33 #include "gtkfilechooserentry.h"
34 #include "gtkfilechooserutils.h"
35 #include "gtkfilechooser.h"
36 #include "gtkfilesystemmodel.h"
39 #include "gtkhpaned.h"
40 #include "gtkicontheme.h"
44 #include "gtkmarshalers.h"
45 #include "gtkmenuitem.h"
46 #include "gtkmessagedialog.h"
47 #include "gtkpathbar.h"
48 #include "gtkprivate.h"
49 #include "gtkscrolledwindow.h"
50 #include "gtksizegroup.h"
53 #include "gtktreeview.h"
54 #include "gtktreemodelsort.h"
55 #include "gtktreeselection.h"
56 #include "gtktreestore.h"
57 #include "gtktypebuiltins.h"
60 #if defined (G_OS_UNIX)
61 #include "gtkfilesystemunix.h"
62 #elif defined (G_OS_WIN32)
63 #include "gtkfilesystemwin32.h"
69 typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
71 #define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
72 #define GTK_IS_FILE_CHOOSER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT))
73 #define GTK_FILE_CHOOSER_DEFAULT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
76 struct _GtkFileChooserDefaultClass
78 GtkVBoxClass parent_class;
81 struct _GtkFileChooserDefault
83 GtkVBox parent_instance;
85 GtkFileChooserAction action;
87 GtkFileSystem *file_system;
89 /* Save mode widgets */
90 GtkWidget *save_widgets;
92 GtkWidget *save_file_name_entry;
93 GtkWidget *save_folder_label;
94 GtkWidget *save_folder_combo;
95 GtkWidget *save_extra_align;
96 GtkWidget *save_expander;
98 /* The file browsing widgets */
99 GtkWidget *browse_widgets;
100 GtkWidget *browse_shortcuts_tree_view;
101 GtkWidget *browse_shortcuts_swin;
102 GtkWidget *browse_shortcuts_add_button;
103 GtkWidget *browse_shortcuts_remove_button;
104 GtkWidget *browse_files_swin;
105 GtkWidget *browse_files_tree_view;
106 GtkWidget *browse_directories_swin;
107 GtkWidget *browse_directories_tree_view;
108 GtkWidget *browse_new_folder_button;
109 GtkWidget *browse_path_bar;
110 GtkWidget *browse_extra_align;
111 GtkTreeModel *browse_shortcuts_model;
112 GtkFileSystemModel *browse_files_model;
113 GtkFileSystemModel *browse_directories_model;
115 GtkWidget *filter_combo;
116 GtkWidget *preview_box;
117 GtkWidget *preview_label;
118 GtkWidget *preview_widget;
119 GtkWidget *extra_widget;
121 GtkListStore *shortcuts_model;
122 GtkTreeModelSort *sort_model;
124 GtkFileFilter *current_filter;
128 gboolean has_desktop;
134 guint volumes_changed_id;
135 guint bookmarks_changed_id;
137 GtkFilePath *current_volume_path;
138 GtkFilePath *current_folder;
139 GtkFilePath *preview_path;
140 char *preview_display_name;
142 GtkTreeViewColumn *list_name_column;
143 GtkCellRenderer *list_name_renderer;
147 guint local_only : 1;
148 guint preview_widget_active : 1;
149 guint use_preview_label : 1;
150 guint select_multiple : 1;
151 guint show_hidden : 1;
152 guint list_sort_ascending : 1;
153 guint changing_folder : 1;
164 static guint signals[LAST_SIGNAL] = { 0 };
166 /* Column numbers for the shortcuts tree. Keep these in sync with shortcuts_model_create() */
168 SHORTCUTS_COL_PIXBUF,
171 SHORTCUTS_COL_REMOVABLE,
172 SHORTCUTS_COL_PIXBUF_VISIBLE,
173 SHORTCUTS_COL_NUM_COLUMNS
176 /* Column numbers for the file list */
181 FILE_LIST_COL_NUM_COLUMNS
184 /* Identifiers for target types */
189 /* Target types for DnD in the shortcuts list */
190 static GtkTargetEntry shortcuts_targets[] = {
191 { "text/uri-list", 0, TEXT_URI_LIST }
194 static const int num_shortcuts_targets = sizeof (shortcuts_targets) / sizeof (shortcuts_targets[0]);
196 /* Interesting places in the shortcuts bar */
206 /* Standard icon size */
207 /* FIXME: maybe this should correspond to the font size in the tree views... */
209 #define PREVIEW_HBOX_SPACING 12
213 static void gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class);
214 static void gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface);
215 static void gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface);
216 static void gtk_file_chooser_default_init (GtkFileChooserDefault *impl);
218 static GObject* gtk_file_chooser_default_constructor (GType type,
219 guint n_construct_properties,
220 GObjectConstructParam *construct_params);
221 static void gtk_file_chooser_default_finalize (GObject *object);
222 static void gtk_file_chooser_default_set_property (GObject *object,
226 static void gtk_file_chooser_default_get_property (GObject *object,
230 static void gtk_file_chooser_default_dispose (GObject *object);
231 static void gtk_file_chooser_default_show_all (GtkWidget *widget);
232 static void gtk_file_chooser_default_style_set (GtkWidget *widget,
233 GtkStyle *previous_style);
234 static void gtk_file_chooser_default_screen_changed (GtkWidget *widget,
235 GdkScreen *previous_screen);
237 static void gtk_file_chooser_default_set_current_folder (GtkFileChooser *chooser,
238 const GtkFilePath *path);
239 static GtkFilePath * gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser);
240 static void gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
242 static void gtk_file_chooser_default_select_path (GtkFileChooser *chooser,
243 const GtkFilePath *path);
244 static void gtk_file_chooser_default_unselect_path (GtkFileChooser *chooser,
245 const GtkFilePath *path);
246 static void gtk_file_chooser_default_select_all (GtkFileChooser *chooser);
247 static void gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser);
248 static GSList * gtk_file_chooser_default_get_paths (GtkFileChooser *chooser);
249 static GtkFilePath * gtk_file_chooser_default_get_preview_path (GtkFileChooser *chooser);
250 static GtkFileSystem *gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser);
251 static void gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
252 GtkFileFilter *filter);
253 static void gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
254 GtkFileFilter *filter);
255 static GSList * gtk_file_chooser_default_list_filters (GtkFileChooser *chooser);
256 static gboolean gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser *chooser,
257 const GtkFilePath *path,
259 static gboolean gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser *chooser,
260 const GtkFilePath *path,
262 static GSList * gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser);
263 static void gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
265 gint *default_height);
266 static void gtk_file_chooser_default_get_resizable_hints (GtkFileChooserEmbed *chooser_embed,
267 gboolean *resize_horizontally,
268 gboolean *resize_vertically);
270 static void location_popup_handler (GtkFileChooserDefault *impl);
271 static void up_folder_handler (GtkFileChooserDefault *impl);
272 static void home_folder_handler (GtkFileChooserDefault *impl);
273 static void update_appearance (GtkFileChooserDefault *impl);
275 static void set_current_filter (GtkFileChooserDefault *impl,
276 GtkFileFilter *filter);
277 static void check_preview_change (GtkFileChooserDefault *impl);
279 static void filter_combo_changed (GtkComboBox *combo_box,
280 GtkFileChooserDefault *impl);
281 static void tree_selection_changed (GtkTreeSelection *tree_selection,
282 GtkFileChooserDefault *impl);
284 static void shortcuts_row_activated_cb (GtkTreeView *tree_view,
286 GtkTreeViewColumn *column,
287 GtkFileChooserDefault *impl);
288 static gboolean shortcuts_select_func (GtkTreeSelection *selection,
291 gboolean path_currently_selected,
294 static void list_selection_changed (GtkTreeSelection *tree_selection,
295 GtkFileChooserDefault *impl);
296 static void list_row_activated (GtkTreeView *tree_view,
298 GtkTreeViewColumn *column,
299 GtkFileChooserDefault *impl);
301 static void path_bar_clicked (GtkPathBar *path_bar,
302 GtkFilePath *file_path,
303 GtkFileChooserDefault *impl);
305 static void add_bookmark_button_clicked_cb (GtkButton *button,
306 GtkFileChooserDefault *impl);
307 static void remove_bookmark_button_clicked_cb (GtkButton *button,
308 GtkFileChooserDefault *impl);
310 static void tree_name_data_func (GtkTreeViewColumn *tree_column,
311 GtkCellRenderer *cell,
312 GtkTreeModel *tree_model,
315 static void list_icon_data_func (GtkTreeViewColumn *tree_column,
316 GtkCellRenderer *cell,
317 GtkTreeModel *tree_model,
320 static void list_name_data_func (GtkTreeViewColumn *tree_column,
321 GtkCellRenderer *cell,
322 GtkTreeModel *tree_model,
326 static void list_size_data_func (GtkTreeViewColumn *tree_column,
327 GtkCellRenderer *cell,
328 GtkTreeModel *tree_model,
332 static void list_mtime_data_func (GtkTreeViewColumn *tree_column,
333 GtkCellRenderer *cell,
334 GtkTreeModel *tree_model,
338 static GObjectClass *parent_class;
341 _gtk_file_chooser_default_get_type (void)
343 static GType file_chooser_default_type = 0;
345 if (!file_chooser_default_type)
347 static const GTypeInfo file_chooser_default_info =
349 sizeof (GtkFileChooserDefaultClass),
350 NULL, /* base_init */
351 NULL, /* base_finalize */
352 (GClassInitFunc) gtk_file_chooser_default_class_init,
353 NULL, /* class_finalize */
354 NULL, /* class_data */
355 sizeof (GtkFileChooserDefault),
357 (GInstanceInitFunc) gtk_file_chooser_default_init,
360 static const GInterfaceInfo file_chooser_info =
362 (GInterfaceInitFunc) gtk_file_chooser_default_iface_init, /* interface_init */
363 NULL, /* interface_finalize */
364 NULL /* interface_data */
367 static const GInterfaceInfo file_chooser_embed_info =
369 (GInterfaceInitFunc) gtk_file_chooser_embed_default_iface_init, /* interface_init */
370 NULL, /* interface_finalize */
371 NULL /* interface_data */
374 file_chooser_default_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserDefault",
375 &file_chooser_default_info, 0);
377 g_type_add_interface_static (file_chooser_default_type,
378 GTK_TYPE_FILE_CHOOSER,
380 g_type_add_interface_static (file_chooser_default_type,
381 GTK_TYPE_FILE_CHOOSER_EMBED,
382 &file_chooser_embed_info);
385 return file_chooser_default_type;
389 gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
391 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
392 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
393 GtkBindingSet *binding_set;
395 parent_class = g_type_class_peek_parent (class);
397 gobject_class->finalize = gtk_file_chooser_default_finalize;
398 gobject_class->constructor = gtk_file_chooser_default_constructor;
399 gobject_class->set_property = gtk_file_chooser_default_set_property;
400 gobject_class->get_property = gtk_file_chooser_default_get_property;
401 gobject_class->dispose = gtk_file_chooser_default_dispose;
403 widget_class->show_all = gtk_file_chooser_default_show_all;
404 widget_class->style_set = gtk_file_chooser_default_style_set;
405 widget_class->screen_changed = gtk_file_chooser_default_screen_changed;
407 signals[LOCATION_POPUP] =
408 _gtk_binding_signal_new ("location-popup",
409 G_OBJECT_CLASS_TYPE (class),
410 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
411 G_CALLBACK (location_popup_handler),
413 _gtk_marshal_VOID__VOID,
416 _gtk_binding_signal_new ("up-folder",
417 G_OBJECT_CLASS_TYPE (class),
418 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
419 G_CALLBACK (up_folder_handler),
421 _gtk_marshal_VOID__VOID,
423 signals[HOME_FOLDER] =
424 _gtk_binding_signal_new ("home-folder",
425 G_OBJECT_CLASS_TYPE (class),
426 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
427 G_CALLBACK (home_folder_handler),
429 _gtk_marshal_VOID__VOID,
432 binding_set = gtk_binding_set_by_class (class);
434 gtk_binding_entry_add_signal (binding_set,
435 GDK_l, GDK_CONTROL_MASK,
439 gtk_binding_entry_add_signal (binding_set,
440 GDK_Up, GDK_MOD1_MASK,
443 gtk_binding_entry_add_signal (binding_set,
444 GDK_KP_Up, GDK_MOD1_MASK,
448 gtk_binding_entry_add_signal (binding_set,
449 GDK_Home, GDK_MOD1_MASK,
452 gtk_binding_entry_add_signal (binding_set,
453 GDK_KP_Home, GDK_MOD1_MASK,
457 _gtk_file_chooser_install_properties (gobject_class);
459 gtk_settings_install_property (g_param_spec_string ("gtk-file-chooser-backend",
460 P_("Default file chooser backend"),
461 P_("Name of the GtkFileChooser backend to use by default"),
467 gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
469 iface->select_path = gtk_file_chooser_default_select_path;
470 iface->unselect_path = gtk_file_chooser_default_unselect_path;
471 iface->select_all = gtk_file_chooser_default_select_all;
472 iface->unselect_all = gtk_file_chooser_default_unselect_all;
473 iface->get_paths = gtk_file_chooser_default_get_paths;
474 iface->get_preview_path = gtk_file_chooser_default_get_preview_path;
475 iface->get_file_system = gtk_file_chooser_default_get_file_system;
476 iface->set_current_folder = gtk_file_chooser_default_set_current_folder;
477 iface->get_current_folder = gtk_file_chooser_default_get_current_folder;
478 iface->set_current_name = gtk_file_chooser_default_set_current_name;
479 iface->add_filter = gtk_file_chooser_default_add_filter;
480 iface->remove_filter = gtk_file_chooser_default_remove_filter;
481 iface->list_filters = gtk_file_chooser_default_list_filters;
482 iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder;
483 iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder;
484 iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders;
488 gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
490 iface->get_default_size = gtk_file_chooser_default_get_default_size;
491 iface->get_resizable_hints = gtk_file_chooser_default_get_resizable_hints;
494 gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
496 impl->local_only = TRUE;
497 impl->preview_widget_active = TRUE;
498 impl->use_preview_label = TRUE;
499 impl->select_multiple = FALSE;
500 impl->show_hidden = FALSE;
502 gtk_box_set_spacing (GTK_BOX (impl), 12);
506 gtk_file_chooser_default_finalize (GObject *object)
508 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
511 g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
512 impl->volumes_changed_id = 0;
513 g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
514 impl->bookmarks_changed_id = 0;
515 g_object_unref (impl->file_system);
517 for (l = impl->filters; l; l = l->next)
519 GtkFileFilter *filter;
521 filter = GTK_FILE_FILTER (l->data);
522 g_object_unref (filter);
524 g_slist_free (impl->filters);
526 if (impl->current_filter)
527 g_object_unref (impl->current_filter);
529 if (impl->current_volume_path)
530 gtk_file_path_free (impl->current_volume_path);
532 if (impl->current_folder)
533 gtk_file_path_free (impl->current_folder);
535 if (impl->preview_path)
536 gtk_file_path_free (impl->preview_path);
538 g_free (impl->preview_display_name);
540 G_OBJECT_CLASS (parent_class)->finalize (object);
543 /* Shows an error dialog set as transient for the specified window */
545 error_message_with_parent (GtkWindow *parent,
550 dialog = gtk_message_dialog_new (parent,
551 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
556 gtk_dialog_run (GTK_DIALOG (dialog));
557 gtk_widget_destroy (dialog);
560 /* Shows an error dialog for the file chooser */
562 error_message (GtkFileChooserDefault *impl,
567 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
568 if (!GTK_WIDGET_TOPLEVEL (toplevel))
571 error_message_with_parent (toplevel ? GTK_WINDOW (toplevel) : NULL,
575 /* Shows a simple error dialog relative to a path. Frees the GError as well. */
577 error_dialog (GtkFileChooserDefault *impl,
579 const GtkFilePath *path,
584 text = g_strdup_printf (msg,
585 gtk_file_path_get_string (path),
587 error_message (impl, text);
589 g_error_free (error);
592 /* Displays an error message about not being able to get information for a file.
593 * Frees the GError as well.
596 error_getting_info_dialog (GtkFileChooserDefault *impl,
597 const GtkFilePath *path,
601 _("Could not retrieve information about %s:\n%s"),
605 /* Shows an error dialog about not being able to add a bookmark */
607 error_could_not_add_bookmark_dialog (GtkFileChooserDefault *impl,
608 const GtkFilePath *path,
612 _("Could not add a bookmark for %s:\n%s"),
616 /* Shows an error dialog about not being able to compose a filename */
618 error_building_filename_dialog (GtkFileChooserDefault *impl,
619 const GtkFilePath *base_path,
620 const char *file_part,
625 msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
626 gtk_file_path_get_string (base_path),
629 error_message (impl, msg);
631 g_error_free (error);
635 update_preview_widget_visibility (GtkFileChooserDefault *impl)
637 if (impl->use_preview_label)
639 if (!impl->preview_label)
641 impl->preview_label = gtk_label_new (impl->preview_display_name);
642 gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_label, FALSE, FALSE, 0);
643 gtk_box_reorder_child (GTK_BOX (impl->preview_box), impl->preview_label, 0);
644 gtk_widget_show (impl->preview_label);
649 if (impl->preview_label)
651 gtk_widget_destroy (impl->preview_label);
652 impl->preview_label = NULL;
656 if (impl->preview_widget_active && impl->preview_widget)
657 gtk_widget_show (impl->preview_box);
659 gtk_widget_hide (impl->preview_box);
661 g_signal_emit_by_name (impl, "default-size-changed");
665 set_preview_widget (GtkFileChooserDefault *impl,
666 GtkWidget *preview_widget)
668 if (preview_widget == impl->preview_widget)
671 if (impl->preview_widget)
672 gtk_container_remove (GTK_CONTAINER (impl->preview_box),
673 impl->preview_widget);
675 impl->preview_widget = preview_widget;
676 if (impl->preview_widget)
678 gtk_widget_show_all (impl->preview_widget);
679 gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_widget, TRUE, TRUE, 0);
680 gtk_box_reorder_child (GTK_BOX (impl->preview_box),
681 impl->preview_widget,
682 (impl->use_preview_label && impl->preview_label) ? 1 : 0);
685 update_preview_widget_visibility (impl);
688 /* Clears the selection in the shortcuts tree */
690 shortcuts_unselect_all (GtkFileChooserDefault *impl)
692 GtkTreeSelection *selection;
694 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
695 gtk_tree_selection_unselect_all (selection);
698 /* Convenience function to get the display name and icon info for a path */
700 get_file_info (GtkFileSystem *file_system, const GtkFilePath *path, GError **error)
702 GtkFilePath *parent_path;
703 GtkFileFolder *parent_folder;
706 if (!gtk_file_system_get_parent (file_system, path, &parent_path, error))
709 parent_folder = gtk_file_system_get_folder (file_system, parent_path,
710 GTK_FILE_INFO_DISPLAY_NAME
714 | GTK_FILE_INFO_IS_FOLDER,
716 gtk_file_path_free (parent_path);
721 info = gtk_file_folder_get_info (parent_folder, path, error);
722 g_object_unref (parent_folder);
727 /* Inserts a path in the shortcuts tree, making a copy of it; alternatively,
728 * inserts a volume. A position of -1 indicates the end of the tree.
731 shortcuts_insert_path (GtkFileChooserDefault *impl,
734 GtkFileSystemVolume *volume,
735 const GtkFilePath *path,
748 label_copy = gtk_file_system_volume_get_display_name (impl->file_system, volume);
749 pixbuf = gtk_file_system_volume_render_icon (impl->file_system,
759 info = get_file_info (impl->file_system, path, error);
763 data = gtk_file_path_copy (path);
766 label_copy = g_strdup (label);
768 label_copy = g_strdup (gtk_file_info_get_display_name (info));
770 pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl), ICON_SIZE, NULL);
772 gtk_file_info_free (info);
776 gtk_list_store_append (impl->shortcuts_model, &iter);
778 gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
780 gtk_list_store_set (impl->shortcuts_model, &iter,
781 SHORTCUTS_COL_PIXBUF, pixbuf,
782 SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
783 SHORTCUTS_COL_NAME, label_copy,
784 SHORTCUTS_COL_PATH, data,
785 SHORTCUTS_COL_REMOVABLE, removable,
791 g_object_unref (pixbuf);
796 /* Appends an item for the user's home directory to the shortcuts model */
798 shortcuts_append_home (GtkFileChooserDefault *impl)
801 GtkFilePath *home_path;
804 home = g_get_home_dir ();
805 home_path = gtk_file_system_filename_to_path (impl->file_system, home);
808 impl->has_home = shortcuts_insert_path (impl, -1, FALSE, NULL, home_path, _("Home"), FALSE, &error);
810 error_getting_info_dialog (impl, home_path, error);
812 gtk_file_path_free (home_path);
815 /* Appends the ~/Desktop directory to the shortcuts model */
817 shortcuts_append_desktop (GtkFileChooserDefault *impl)
822 name = g_build_filename (g_get_home_dir (), "Desktop", NULL);
823 path = gtk_file_system_filename_to_path (impl->file_system, name);
826 impl->has_desktop = shortcuts_insert_path (impl, -1, FALSE, NULL, path, _("Desktop"), FALSE, NULL);
827 /* We do not actually pop up an error dialog if there is no desktop directory
828 * because some people may really not want to have one.
831 gtk_file_path_free (path);
834 /* Appends a list of GtkFilePath to the shortcuts model; returns how many were inserted */
836 shortcuts_append_paths (GtkFileChooserDefault *impl,
843 for (; paths; paths = paths->next)
851 /* NULL GError, but we don't really want to show error boxes here */
853 if (shortcuts_insert_path (impl, -1, FALSE, NULL, path, NULL, TRUE, NULL))
860 /* Returns the index for the corresponding item in the shortcuts bar */
862 shortcuts_get_index (GtkFileChooserDefault *impl,
863 ShortcutsIndex where)
869 if (where == SHORTCUTS_HOME)
872 n += impl->has_home ? 1 : 0;
874 if (where == SHORTCUTS_DESKTOP)
877 n += impl->has_desktop ? 1 : 0;
879 if (where == SHORTCUTS_VOLUMES)
882 n += impl->num_volumes;
884 if (where == SHORTCUTS_SHORTCUTS)
887 n += impl->num_shortcuts;
889 if (where == SHORTCUTS_SEPARATOR)
892 /* If there are no bookmarks there won't be a separator */
893 n += (impl->num_bookmarks > 0) ? 1 : 0;
895 if (where == SHORTCUTS_BOOKMARKS)
898 g_assert_not_reached ();
905 typedef void (* RemoveFunc) (GtkFileChooserDefault *impl, gpointer data);
907 /* Removes the specified number of rows from the shortcuts list */
909 shortcuts_remove_rows (GtkFileChooserDefault *impl,
912 RemoveFunc remove_fn)
916 path = gtk_tree_path_new_from_indices (start_row, -1);
918 for (; n_rows; n_rows--)
923 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
924 g_assert_not_reached ();
928 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &data, -1);
929 (* remove_fn) (impl, data);
932 gtk_list_store_remove (impl->shortcuts_model, &iter);
935 gtk_tree_path_free (path);
938 /* Used from shortcuts_remove_rows() */
940 volume_remove_cb (GtkFileChooserDefault *impl, gpointer data)
942 GtkFileSystemVolume *volume;
945 gtk_file_system_volume_free (impl->file_system, volume);
948 /* Adds all the file system volumes to the shortcuts model */
950 shortcuts_add_volumes (GtkFileChooserDefault *impl)
956 start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
957 shortcuts_remove_rows (impl, start_row, impl->num_volumes, volume_remove_cb);
958 impl->num_volumes = 0;
960 list = gtk_file_system_list_volumes (impl->file_system);
964 for (l = list; l; l = l->next)
966 GtkFileSystemVolume *volume;
970 shortcuts_insert_path (impl, start_row + n, TRUE, volume, NULL, NULL, FALSE, NULL);
974 impl->num_volumes = n;
979 /* Used from shortcuts_remove_rows() */
981 remove_bookmark_cb (GtkFileChooserDefault *impl, gpointer data)
986 gtk_file_path_free (path);
989 /* Inserts the bookmarks separator node */
991 shortcuts_insert_separator (GtkFileChooserDefault *impl)
995 gtk_list_store_insert (impl->shortcuts_model, &iter,
996 shortcuts_get_index (impl, SHORTCUTS_SEPARATOR));
997 gtk_list_store_set (impl->shortcuts_model, &iter,
998 SHORTCUTS_COL_PIXBUF, NULL,
999 SHORTCUTS_COL_PIXBUF_VISIBLE, FALSE,
1000 SHORTCUTS_COL_NAME, NULL,
1001 SHORTCUTS_COL_PATH, NULL,
1005 /* Creates the GtkTreeStore used as the shortcuts model */
1006 /* Updates the list of bookmarks */
1008 shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
1012 if (impl->num_bookmarks > 0)
1014 shortcuts_remove_rows (impl,
1015 shortcuts_get_index (impl, SHORTCUTS_SEPARATOR),
1016 impl->num_bookmarks + 1,
1017 remove_bookmark_cb);
1021 bookmarks = gtk_file_system_list_bookmarks (impl->file_system);
1022 impl->num_bookmarks = shortcuts_append_paths (impl, bookmarks);
1023 gtk_file_paths_free (bookmarks);
1025 if (impl->num_bookmarks > 0)
1027 shortcuts_insert_separator (impl);
1032 shortcuts_model_create (GtkFileChooserDefault *impl)
1034 if (impl->shortcuts_model)
1035 g_object_unref (impl->shortcuts_model);
1037 /* Keep this order in sync with the SHORCUTS_COL_* enum values */
1038 impl->shortcuts_model = gtk_list_store_new (SHORTCUTS_COL_NUM_COLUMNS,
1039 GDK_TYPE_PIXBUF, /* pixbuf */
1040 G_TYPE_STRING, /* name */
1041 G_TYPE_POINTER, /* path or volume */
1042 G_TYPE_BOOLEAN, /* removable */
1043 G_TYPE_BOOLEAN); /* pixbuf cell visibility */
1045 if (impl->file_system)
1047 shortcuts_append_home (impl);
1048 shortcuts_append_desktop (impl);
1049 shortcuts_add_volumes (impl);
1050 shortcuts_add_bookmarks (impl);
1053 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), GTK_TREE_MODEL (impl->shortcuts_model));
1056 /* Callback used when the "New Folder" toolbar button is clicked */
1058 new_folder_button_clicked (GtkButton *button,
1059 GtkFileChooserDefault *impl)
1064 /* FIXME: this doesn't work for folder mode, just for file mode */
1066 _gtk_file_system_model_add_editable (impl->browse_files_model, &iter);
1067 g_object_set (impl->list_name_renderer, "editable", TRUE, NULL);
1069 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->browse_files_model), &iter);
1070 gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view),
1072 impl->list_name_column,
1076 /* Callback used from the text cell renderer when the new folder is named */
1078 renderer_edited_cb (GtkCellRendererText *cell_renderer_text,
1080 const gchar *new_text,
1081 GtkFileChooserDefault *impl)
1084 GtkFilePath *file_path;
1086 _gtk_file_system_model_remove_editable (impl->browse_files_model);
1087 g_object_set (impl->list_name_renderer, "editable", FALSE, NULL);
1090 file_path = gtk_file_system_make_path (impl->file_system, impl->current_folder, new_text, &error);
1093 error_building_filename_dialog (impl, impl->current_folder, new_text, error);
1098 if (!gtk_file_system_create_folder (impl->file_system, file_path, &error))
1100 _("Could not create folder %s:\n%s"),
1103 gtk_file_path_free (file_path);
1105 /* FIXME: scroll to the new folder and select it */
1108 /* Callback used from the text cell renderer when the new folder edition gets
1112 renderer_editing_canceled_cb (GtkCellRendererText *cell_renderer_text,
1113 GtkFileChooserDefault *impl)
1115 _gtk_file_system_model_remove_editable (impl->browse_files_model);
1116 g_object_set (impl->list_name_renderer, "editable", FALSE, NULL);
1119 /* Creates the widgets for the filter combo box */
1121 filter_create (GtkFileChooserDefault *impl)
1123 impl->filter_combo = gtk_combo_box_new_text ();
1124 g_signal_connect (impl->filter_combo, "changed",
1125 G_CALLBACK (filter_combo_changed), impl);
1127 return impl->filter_combo;
1131 button_new (GtkFileChooserDefault *impl,
1133 const char *stock_id,
1143 button = gtk_button_new ();
1144 hbox = gtk_hbox_new (FALSE, 2);
1145 align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1147 gtk_container_add (GTK_CONTAINER (button), align);
1148 gtk_container_add (GTK_CONTAINER (align), hbox);
1149 widget = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
1151 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1153 widget = gtk_label_new_with_mnemonic (text);
1154 gtk_label_set_mnemonic_widget (GTK_LABEL (widget), GTK_WIDGET (button));
1155 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1157 gtk_widget_set_sensitive (button, sensitive);
1158 g_signal_connect (button, "clicked", callback, impl);
1160 gtk_widget_show_all (align);
1163 gtk_widget_show (button);
1168 /* Creates the widgets for the folder tree */
1170 create_folder_tree (GtkFileChooserDefault *impl)
1172 GtkTreeSelection *selection;
1174 /* Scrolled window */
1176 impl->browse_directories_swin = gtk_scrolled_window_new (NULL, NULL);
1177 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->browse_directories_swin),
1178 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1179 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (impl->browse_directories_swin),
1183 impl->browse_directories_tree_view = gtk_tree_view_new ();
1184 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->browse_directories_tree_view), FALSE);
1186 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_directories_tree_view));
1187 gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_directories_tree_view),
1190 num_shortcuts_targets,
1193 g_signal_connect (selection, "changed",
1194 G_CALLBACK (tree_selection_changed), impl);
1196 gtk_container_add (GTK_CONTAINER (impl->browse_directories_swin), impl->browse_directories_tree_view);
1197 gtk_widget_show (impl->browse_directories_tree_view);
1201 gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (impl->browse_directories_tree_view), 0,
1203 gtk_cell_renderer_text_new (),
1204 tree_name_data_func, impl, NULL);
1205 gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_directories_tree_view),
1206 GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
1208 return impl->browse_directories_swin;
1211 /* Returns whether a path is already present in the shortcuts list */
1213 shortcut_exists (GtkFileChooserDefault *impl,
1214 const GtkFilePath *path)
1223 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
1227 separator_idx = shortcuts_get_index (impl, SHORTCUTS_SEPARATOR);
1228 volumes_idx = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
1236 if (i == separator_idx)
1239 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &data, -1);
1241 if (i >= volumes_idx && i < volumes_idx + impl->num_volumes)
1243 GtkFileSystemVolume *volume;
1244 GtkFilePath *base_path;
1247 base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
1249 exists = strcmp (gtk_file_path_get_string (path),
1250 gtk_file_path_get_string (base_path)) == 0;
1258 GtkFilePath *model_path;
1262 if (model_path && gtk_file_path_compare (model_path, path) == 0)
1269 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter));
1275 /* Tries to add a bookmark from a path name */
1277 shortcuts_add_bookmark_from_path (GtkFileChooserDefault *impl,
1278 const GtkFilePath *path)
1283 if (shortcut_exists (impl, path))
1287 info = get_file_info (impl->file_system, path, &error);
1290 error_getting_info_dialog (impl, path, error);
1291 else if (!gtk_file_info_get_is_folder (info))
1295 msg = g_strdup_printf (_("Could not add bookmark for %s because it is not a folder."),
1296 gtk_file_path_get_string (path));
1297 error_message (impl, msg);
1303 if (!gtk_file_system_insert_bookmark (impl->file_system, path, -1, &error))
1304 error_could_not_add_bookmark_dialog (impl, path, error);
1309 add_bookmark_foreach_cb (GtkTreeModel *model,
1314 GtkFileChooserDefault *impl;
1315 GtkFileSystemModel *fs_model;
1316 GtkTreeIter child_iter;
1317 const GtkFilePath *file_path;
1319 impl = GTK_FILE_CHOOSER_DEFAULT (data);
1321 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
1322 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
1324 fs_model = impl->browse_directories_model;
1329 fs_model = impl->browse_files_model;
1330 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter);
1333 file_path = _gtk_file_system_model_get_path (GTK_FILE_SYSTEM_MODEL (fs_model), &child_iter);
1334 shortcuts_add_bookmark_from_path (impl, file_path);
1337 /* Callback used when the "Add bookmark" button is clicked */
1339 add_bookmark_button_clicked_cb (GtkButton *button,
1340 GtkFileChooserDefault *impl)
1342 GtkWidget *tree_view;
1343 GtkTreeSelection *selection;
1345 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
1346 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
1347 tree_view = impl->browse_directories_tree_view;
1349 tree_view = impl->browse_files_tree_view;
1351 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
1352 if (gtk_tree_selection_count_selected_rows (selection) == 0)
1353 shortcuts_add_bookmark_from_path (impl, impl->current_folder);
1355 gtk_tree_selection_selected_foreach (selection,
1356 add_bookmark_foreach_cb,
1360 /* Callback used when the "Remove bookmark" button is clicked */
1362 remove_bookmark_button_clicked_cb (GtkButton *button,
1363 GtkFileChooserDefault *impl)
1365 GtkTreeSelection *selection;
1371 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1374 if (gtk_tree_selection_get_selected (selection, NULL, &iter))
1376 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
1377 SHORTCUTS_COL_PATH, &path,
1378 SHORTCUTS_COL_REMOVABLE, &removable, -1);
1381 g_assert_not_reached ();
1386 if (!gtk_file_system_remove_bookmark (impl->file_system, path, &error))
1388 _("Could not remove bookmark for %s:\n%s"),
1394 struct is_folders_foreach_closure {
1395 GtkFileChooserDefault *impl;
1396 gboolean all_folders;
1399 /* Used from gtk_tree_selection_selected_foreach() */
1401 is_folders_foreach_cb (GtkTreeModel *model,
1406 struct is_folders_foreach_closure *closure;
1407 GtkTreeIter child_iter;
1408 const GtkFileInfo *info;
1412 gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
1414 info = _gtk_file_system_model_get_info (closure->impl->browse_files_model, &child_iter);
1415 closure->all_folders &= gtk_file_info_get_is_folder (info);
1418 /* Returns whether the selected items in the file list are all folders */
1420 selection_is_folders (GtkFileChooserDefault *impl)
1422 struct is_folders_foreach_closure closure;
1423 GtkTreeSelection *selection;
1425 closure.impl = impl;
1426 closure.all_folders = TRUE;
1428 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
1429 gtk_tree_selection_selected_foreach (selection,
1430 is_folders_foreach_cb,
1433 return closure.all_folders;
1436 /* Sensitize the "add bookmark" button if all the selected items are folders, or
1437 * if there are no selected items *and* the current folder is not in the
1438 * bookmarks list. De-sensitize the button otherwise.
1441 bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl)
1443 GtkWidget *tree_view;
1444 GtkTreeSelection *selection;
1447 /* Check selection */
1449 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
1450 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
1451 tree_view = impl->browse_directories_tree_view;
1453 tree_view = impl->browse_files_tree_view;
1455 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
1457 if (gtk_tree_selection_count_selected_rows (selection) == 0)
1458 active = !shortcut_exists (impl, impl->current_folder);
1460 active = (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
1461 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER ||
1462 selection_is_folders (impl));
1464 gtk_widget_set_sensitive (impl->browse_shortcuts_add_button, active);
1467 /* Sets the sensitivity of the "remove bookmark" button depending on whether a
1468 * bookmark row is selected in the shortcuts tree.
1471 bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl)
1473 GtkTreeSelection *selection;
1475 gboolean removable = FALSE;
1477 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1479 if (gtk_tree_selection_get_selected (selection, NULL, &iter))
1480 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
1481 SHORTCUTS_COL_REMOVABLE, &removable,
1484 gtk_widget_set_sensitive (impl->browse_shortcuts_remove_button, removable);
1487 /* Converts raw selection data from text/uri-list to a list of strings */
1489 split_uris (const char *data)
1492 const char *p, *start;
1498 for (p = start; *p != 0; p++)
1499 if (*p == '\r' && *(p + 1) == '\n')
1503 name = g_strndup (start, p - start);
1504 uris = g_slist_prepend (uris, name);
1510 uris = g_slist_reverse (uris);
1514 /* Callback used when we get the drag data for the bookmarks list. We add the
1515 * received URIs as bookmarks if they are folders.
1518 shortcuts_drag_data_received_cb (GtkWidget *widget,
1519 GdkDragContext *context,
1522 GtkSelectionData *selection_data,
1527 GtkFileChooserDefault *impl;
1530 impl = GTK_FILE_CHOOSER_DEFAULT (data);
1532 uris = split_uris (selection_data->data);
1534 for (l = uris; l; l = l->next)
1540 path = gtk_file_system_uri_to_path (impl->file_system, uri);
1544 shortcuts_add_bookmark_from_path (impl, path);
1545 gtk_file_path_free (path);
1551 msg = g_strdup_printf (_("Could not add a bookmark for %s because it is an invalid path name."),
1553 error_message (impl, msg);
1560 g_slist_free (uris);
1563 /* Callback used when the selection in the shortcuts tree changes */
1565 shortcuts_selection_changed_cb (GtkTreeSelection *selection,
1566 GtkFileChooserDefault *impl)
1568 bookmarks_check_remove_sensitivity (impl);
1571 /* Creates the widgets for the shortcuts and bookmarks tree */
1573 shortcuts_list_create (GtkFileChooserDefault *impl)
1575 GtkTreeSelection *selection;
1576 GtkTreeViewColumn *column;
1577 GtkCellRenderer *renderer;
1579 /* Scrolled window */
1581 impl->browse_shortcuts_swin = gtk_scrolled_window_new (NULL, NULL);
1582 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->browse_shortcuts_swin),
1583 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1584 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (impl->browse_shortcuts_swin),
1586 gtk_widget_show (impl->browse_shortcuts_swin);
1590 impl->browse_shortcuts_tree_view = gtk_tree_view_new ();
1591 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), FALSE);
1593 gtk_drag_dest_set (impl->browse_shortcuts_tree_view,
1594 GTK_DEST_DEFAULT_ALL,
1596 num_shortcuts_targets,
1599 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1600 gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
1601 gtk_tree_selection_set_select_function (selection,
1602 shortcuts_select_func,
1605 g_signal_connect (selection, "changed",
1606 G_CALLBACK (shortcuts_selection_changed_cb), impl);
1608 g_signal_connect (impl->browse_shortcuts_tree_view, "row-activated",
1609 G_CALLBACK (shortcuts_row_activated_cb), impl);
1611 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-received",
1612 G_CALLBACK (shortcuts_drag_data_received_cb), impl);
1614 gtk_container_add (GTK_CONTAINER (impl->browse_shortcuts_swin), impl->browse_shortcuts_tree_view);
1615 gtk_widget_show (impl->browse_shortcuts_tree_view);
1619 shortcuts_model_create (impl);
1623 column = gtk_tree_view_column_new ();
1624 gtk_tree_view_column_set_title (column, _("Folder"));
1626 renderer = gtk_cell_renderer_pixbuf_new ();
1627 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1628 gtk_tree_view_column_set_attributes (column, renderer,
1629 "pixbuf", SHORTCUTS_COL_PIXBUF,
1630 "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
1633 renderer = _gtk_cell_renderer_sep_text_new ();
1634 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1635 gtk_tree_view_column_set_attributes (column, renderer,
1636 "text", SHORTCUTS_COL_NAME,
1639 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), column);
1641 return impl->browse_shortcuts_swin;
1644 /* Creates the widgets for the shortcuts/bookmarks pane */
1646 shortcuts_pane_create (GtkFileChooserDefault *impl,
1647 GtkSizeGroup *size_group)
1653 vbox = gtk_vbox_new (FALSE, 6);
1654 gtk_widget_show (vbox);
1656 /* Shortcuts tree */
1658 widget = shortcuts_list_create (impl);
1659 gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
1661 /* Box for buttons */
1663 hbox = gtk_hbox_new (TRUE, 6);
1664 gtk_size_group_add_widget (size_group, hbox);
1665 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1666 gtk_widget_show (hbox);
1668 /* Add bookmark button */
1670 impl->browse_shortcuts_add_button = button_new (impl,
1675 G_CALLBACK (add_bookmark_button_clicked_cb));
1676 gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_add_button, TRUE, TRUE, 0);
1678 /* Remove bookmark button */
1680 impl->browse_shortcuts_remove_button = button_new (impl,
1685 G_CALLBACK (remove_bookmark_button_clicked_cb));
1686 gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_remove_button, TRUE, TRUE, 0);
1691 /* Creates the widgets for the file list */
1693 create_file_list (GtkFileChooserDefault *impl)
1695 GtkTreeSelection *selection;
1696 GtkTreeViewColumn *column;
1697 GtkCellRenderer *renderer;
1699 /* Scrolled window */
1701 impl->browse_files_swin = gtk_scrolled_window_new (NULL, NULL);
1702 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->browse_files_swin),
1703 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1704 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (impl->browse_files_swin),
1707 /* Tree/list view */
1709 impl->browse_files_tree_view = gtk_tree_view_new ();
1710 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->browse_files_tree_view), TRUE);
1711 gtk_container_add (GTK_CONTAINER (impl->browse_files_swin), impl->browse_files_tree_view);
1712 g_signal_connect (impl->browse_files_tree_view, "row_activated",
1713 G_CALLBACK (list_row_activated), impl);
1714 gtk_widget_show (impl->browse_files_tree_view);
1716 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
1717 gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_files_tree_view),
1720 num_shortcuts_targets,
1723 g_signal_connect (selection, "changed",
1724 G_CALLBACK (list_selection_changed), impl);
1726 /* Filename column */
1728 impl->list_name_column = gtk_tree_view_column_new ();
1729 gtk_tree_view_column_set_expand (impl->list_name_column, TRUE);
1730 gtk_tree_view_column_set_title (impl->list_name_column, _("Name"));
1731 gtk_tree_view_column_set_sort_column_id (impl->list_name_column, FILE_LIST_COL_NAME);
1733 renderer = gtk_cell_renderer_pixbuf_new ();
1734 gtk_tree_view_column_pack_start (impl->list_name_column, renderer, FALSE);
1735 gtk_tree_view_column_set_cell_data_func (impl->list_name_column, renderer,
1736 list_icon_data_func, impl, NULL);
1738 impl->list_name_renderer = gtk_cell_renderer_text_new ();
1739 g_signal_connect (impl->list_name_renderer, "edited",
1740 G_CALLBACK (renderer_edited_cb), impl);
1741 g_signal_connect (impl->list_name_renderer, "editing-canceled",
1742 G_CALLBACK (renderer_editing_canceled_cb), impl);
1743 gtk_tree_view_column_pack_start (impl->list_name_column, impl->list_name_renderer, TRUE);
1744 gtk_tree_view_column_set_cell_data_func (impl->list_name_column, impl->list_name_renderer,
1745 list_name_data_func, impl, NULL);
1747 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), impl->list_name_column);
1751 column = gtk_tree_view_column_new ();
1752 gtk_tree_view_column_set_title (column, _("Size"));
1754 renderer = gtk_cell_renderer_text_new ();
1755 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1756 gtk_tree_view_column_set_cell_data_func (column, renderer,
1757 list_size_data_func, impl, NULL);
1758 gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_SIZE);
1759 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
1761 /* Modification time column */
1763 column = gtk_tree_view_column_new ();
1764 gtk_tree_view_column_set_title (column, _("Modified"));
1766 renderer = gtk_cell_renderer_text_new ();
1767 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1768 gtk_tree_view_column_set_cell_data_func (column, renderer,
1769 list_mtime_data_func, impl, NULL);
1770 gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_MTIME);
1771 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
1773 return impl->browse_files_swin;
1777 create_filename_entry_and_filter_combo (GtkFileChooserDefault *impl)
1782 hbox = gtk_hbox_new (FALSE, 12);
1783 gtk_widget_show (hbox);
1787 widget = filter_create (impl);
1788 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1793 /* Creates the widgets for the files/folders pane */
1795 file_pane_create (GtkFileChooserDefault *impl,
1796 GtkSizeGroup *size_group)
1802 vbox = gtk_vbox_new (FALSE, 6);
1803 gtk_widget_show (vbox);
1805 /* The path bar and 'Create Folder' button */
1806 hbox = gtk_hbox_new (FALSE, 12);
1807 gtk_widget_show (hbox);
1808 impl->browse_path_bar = g_object_new (GTK_TYPE_PATH_BAR, NULL);
1809 g_signal_connect (impl->browse_path_bar, "path_clicked", G_CALLBACK (path_bar_clicked), impl);
1810 gtk_widget_show_all (impl->browse_path_bar);
1811 gtk_box_pack_start (GTK_BOX (hbox), impl->browse_path_bar, TRUE, TRUE, 0);
1814 impl->browse_new_folder_button = gtk_button_new_with_mnemonic (_("Create _Folder"));
1815 g_signal_connect (impl->browse_new_folder_button, "clicked",
1816 G_CALLBACK (new_folder_button_clicked), impl);
1817 gtk_box_pack_end (GTK_BOX (hbox), impl->browse_new_folder_button, FALSE, FALSE, 0);
1818 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1820 /* Box for lists and preview */
1822 hbox = gtk_hbox_new (FALSE, PREVIEW_HBOX_SPACING);
1823 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
1824 gtk_widget_show (hbox);
1828 widget = create_folder_tree (impl);
1829 gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
1833 widget = create_file_list (impl);
1834 gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
1838 impl->preview_box = gtk_vbox_new (FALSE, 12);
1839 gtk_box_pack_start (GTK_BOX (hbox), impl->preview_box, FALSE, FALSE, 0);
1840 /* Don't show preview box initially */
1842 /* Filename entry and filter combo */
1843 hbox = gtk_hbox_new (FALSE, 0);
1844 gtk_size_group_add_widget (size_group, hbox);
1845 widget = create_filename_entry_and_filter_combo (impl);
1846 gtk_box_pack_end (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1847 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1848 gtk_widget_show (hbox);
1852 /* Callback used when the "Browse for more folders" expander is toggled */
1854 expander_changed_cb (GtkExpander *expander,
1856 GtkFileChooserDefault *impl)
1858 update_appearance (impl);
1861 /* Creates the widgets specific to Save mode */
1863 save_widgets_create (GtkFileChooserDefault *impl)
1868 GtkWidget *alignment;
1870 vbox = gtk_vbox_new (FALSE, 12);
1872 table = gtk_table_new (2, 2, FALSE);
1873 gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
1874 gtk_widget_show (table);
1875 gtk_table_set_row_spacings (GTK_TABLE (table), 12);
1876 gtk_table_set_col_spacings (GTK_TABLE (table), 12);
1880 widget = gtk_label_new_with_mnemonic (_("_Name:"));
1881 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
1882 gtk_table_attach (GTK_TABLE (table), widget,
1886 gtk_widget_show (widget);
1888 impl->save_file_name_entry = gtk_entry_new ();
1889 gtk_entry_set_width_chars (GTK_ENTRY (impl->save_file_name_entry), 45);
1890 gtk_entry_set_activates_default (GTK_ENTRY (impl->save_file_name_entry), TRUE);
1891 gtk_table_attach (GTK_TABLE (table), impl->save_file_name_entry,
1893 GTK_EXPAND | GTK_FILL, 0,
1895 gtk_widget_show (impl->save_file_name_entry);
1896 gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->save_file_name_entry);
1899 impl->save_folder_label = gtk_label_new_with_mnemonic (_("Save in _Folder:"));
1900 gtk_misc_set_alignment (GTK_MISC (impl->save_folder_label), 0.0, 0.5);
1901 gtk_table_attach (GTK_TABLE (table), impl->save_folder_label,
1905 gtk_widget_show (impl->save_folder_label);
1907 /* FIXME: create the combo */
1910 impl->save_extra_align = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
1911 gtk_box_pack_start (GTK_BOX (vbox), impl->save_extra_align, FALSE, FALSE, 0);
1914 alignment = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
1915 gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
1917 impl->save_expander = gtk_expander_new_with_mnemonic (_("_Browse for other folders"));
1918 gtk_container_add (GTK_CONTAINER (alignment), impl->save_expander);
1919 g_signal_connect (impl->save_expander, "notify::expanded",
1920 G_CALLBACK (expander_changed_cb),
1922 gtk_widget_show_all (alignment);
1927 /* Creates the main hpaned with the widgets shared by Open and Save mode */
1929 browse_widgets_create (GtkFileChooserDefault *impl)
1934 GtkSizeGroup *size_group;
1936 /* size group is used by the [+][-] buttons and the filter combo */
1937 size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
1938 vbox = gtk_vbox_new (FALSE, 12);
1941 hpaned = gtk_hpaned_new ();
1942 gtk_widget_show (hpaned);
1943 gtk_paned_set_position (GTK_PANED (hpaned), 200); /* FIXME: this sucks */
1944 gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
1946 widget = shortcuts_pane_create (impl, size_group);
1947 gtk_paned_pack1 (GTK_PANED (hpaned), widget, FALSE, FALSE);
1948 widget = file_pane_create (impl, size_group);
1949 gtk_paned_pack2 (GTK_PANED (hpaned), widget, TRUE, FALSE);
1951 /* Alignment to hold custom widget */
1952 impl->browse_extra_align = gtk_alignment_new (0.0, .5, 1.0, 1.0);
1953 gtk_box_pack_start (GTK_BOX (vbox), impl->browse_extra_align, FALSE, FALSE, 0);
1959 gtk_file_chooser_default_constructor (GType type,
1960 guint n_construct_properties,
1961 GObjectConstructParam *construct_params)
1963 GtkFileChooserDefault *impl;
1966 object = parent_class->constructor (type,
1967 n_construct_properties,
1969 impl = GTK_FILE_CHOOSER_DEFAULT (object);
1971 g_assert (impl->file_system);
1973 gtk_widget_push_composite_child ();
1975 /* Widgets for Save mode */
1976 impl->save_widgets = save_widgets_create (impl);
1977 gtk_box_pack_start (GTK_BOX (impl), impl->save_widgets, FALSE, FALSE, 0);
1979 /* The browse widgets */
1980 impl->browse_widgets = browse_widgets_create (impl);
1981 gtk_box_pack_start (GTK_BOX (impl), impl->browse_widgets, TRUE, TRUE, 0);
1983 gtk_widget_pop_composite_child ();
1984 update_appearance (impl);
1989 /* Sets the extra_widget by packing it in the appropriate place */
1991 set_extra_widget (GtkFileChooserDefault *impl,
1992 GtkWidget *extra_widget)
1996 g_object_ref (extra_widget);
1997 /* FIXME: is this right ? */
1998 gtk_widget_show (extra_widget);
2001 if (impl->extra_widget)
2002 g_object_unref (impl->extra_widget);
2004 impl->extra_widget = extra_widget;
2008 volumes_changed_cb (GtkFileSystem *file_system,
2009 GtkFileChooserDefault *impl)
2011 shortcuts_add_volumes (impl);
2014 /* Callback used when the set of bookmarks changes in the file system */
2016 bookmarks_changed_cb (GtkFileSystem *file_system,
2017 GtkFileChooserDefault *impl)
2019 shortcuts_add_bookmarks (impl);
2021 bookmarks_check_add_sensitivity (impl);
2022 bookmarks_check_remove_sensitivity (impl);
2025 /* Sets the file chooser to multiple selection mode */
2027 set_select_multiple (GtkFileChooserDefault *impl,
2028 gboolean select_multiple,
2029 gboolean property_notify)
2031 GtkTreeSelection *selection;
2032 GtkSelectionMode mode;
2034 if (select_multiple == impl->select_multiple)
2037 mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE;
2039 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_directories_tree_view));
2040 gtk_tree_selection_set_mode (selection, mode);
2042 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2043 gtk_tree_selection_set_mode (selection, mode);
2045 impl->select_multiple = select_multiple;
2046 g_object_notify (G_OBJECT (impl), "select-multiple");
2048 /* FIXME #132255: See note in check_preview_change() */
2049 check_preview_change (impl);
2053 set_file_system_backend (GtkFileChooserDefault *impl,
2054 const char *backend)
2056 if (impl->file_system)
2058 g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
2059 impl->volumes_changed_id = 0;
2060 g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
2061 impl->bookmarks_changed_id = 0;
2062 g_object_unref (impl->file_system);
2065 impl->file_system = NULL;
2067 impl->file_system = _gtk_file_system_create (backend);
2070 GtkSettings *settings = gtk_settings_get_default ();
2071 gchar *default_backend = NULL;
2073 g_object_get (settings, "gtk-file-chooser-backend", &default_backend, NULL);
2074 if (default_backend)
2076 impl->file_system = _gtk_file_system_create (default_backend);
2077 g_free (default_backend);
2081 if (!impl->file_system)
2083 #if defined (G_OS_UNIX)
2084 impl->file_system = gtk_file_system_unix_new ();
2085 #elif defined (G_OS_WIN32)
2086 impl->file_system = gtk_file_system_win32_new ();
2088 #error "No default filesystem implementation on the platform"
2092 if (impl->file_system)
2094 impl->volumes_changed_id = g_signal_connect (impl->file_system, "volumes-changed",
2095 G_CALLBACK (volumes_changed_cb),
2097 impl->bookmarks_changed_id = g_signal_connect (impl->file_system, "bookmarks-changed",
2098 G_CALLBACK (bookmarks_changed_cb),
2103 /* This function is basically a do_all function.
2105 * It sets the visibility on all the widgets based on the current state, and
2106 * moves the custom_widget if needed.
2109 update_appearance (GtkFileChooserDefault *impl)
2113 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
2114 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
2116 gtk_widget_show (impl->save_widgets);
2118 if (gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
2120 gtk_widget_set_sensitive (impl->save_folder_label, FALSE);
2121 /*gtk_widget_set_sensitive (impl->save_folder_combo, FALSE);*/
2122 gtk_widget_show (impl->browse_widgets);
2126 gtk_widget_set_sensitive (impl->save_folder_label, TRUE);
2127 /*gtk_widget_set_sensitive (impl->save_folder_combo, TRUE);*/
2128 gtk_widget_hide (impl->browse_widgets);
2131 gtk_widget_show (impl->browse_new_folder_button);
2133 if (impl->select_multiple)
2135 g_warning ("Save mode cannot be set in conjunction with multiple selection mode. "
2136 "Re-setting to single selection mode.");
2137 set_select_multiple (impl, FALSE, TRUE);
2140 else if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
2141 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
2143 gtk_widget_hide (impl->save_widgets);
2144 gtk_widget_show (impl->browse_widgets);
2147 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
2148 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
2150 gtk_widget_show (impl->browse_new_folder_button);
2151 gtk_widget_hide (impl->browse_files_swin);
2152 gtk_widget_show (impl->browse_directories_swin);
2156 gtk_widget_hide (impl->browse_new_folder_button);
2157 gtk_widget_hide (impl->browse_directories_swin);
2158 gtk_widget_show (impl->browse_files_swin);
2161 if (impl->extra_widget)
2164 GtkWidget *unused_align;
2166 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
2168 align = impl->save_extra_align;
2169 unused_align = impl->browse_extra_align;
2173 align = impl->browse_extra_align;
2174 unused_align = impl->save_extra_align;
2177 /* We own a ref on extra_widget, so it's safe to do this */
2178 child = GTK_BIN (unused_align)->child;
2180 gtk_container_remove (GTK_CONTAINER (unused_align), child);
2182 child = GTK_BIN (align)->child;
2183 if (child && child != impl->extra_widget)
2185 gtk_container_remove (GTK_CONTAINER (align), child);
2186 gtk_container_add (GTK_CONTAINER (align), impl->extra_widget);
2188 else if (child == NULL)
2190 gtk_container_add (GTK_CONTAINER (align), impl->extra_widget);
2193 gtk_widget_show (align);
2194 gtk_widget_hide (unused_align);
2198 child = GTK_BIN (impl->browse_extra_align)->child;
2200 gtk_container_remove (GTK_CONTAINER (impl->browse_extra_align), child);
2202 child = GTK_BIN (impl->save_extra_align)->child;
2204 gtk_container_remove (GTK_CONTAINER (impl->save_extra_align), child);
2206 gtk_widget_hide (impl->save_extra_align);
2207 gtk_widget_hide (impl->browse_extra_align);
2210 g_signal_emit_by_name (impl, "default-size-changed");
2211 g_signal_emit_by_name (impl, "resizable-hints-changed");
2215 gtk_file_chooser_default_set_property (GObject *object,
2217 const GValue *value,
2221 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
2225 case GTK_FILE_CHOOSER_PROP_ACTION:
2227 GtkFileChooserAction action = g_value_get_enum (value);
2229 if (action != impl->action)
2231 if (action == GTK_FILE_CHOOSER_ACTION_SAVE && impl->select_multiple)
2233 g_warning ("Multiple selection mode is not allowed in Save mode");
2234 set_select_multiple (impl, FALSE, TRUE);
2236 impl->action = action;
2237 update_appearance (impl);
2241 case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
2242 set_file_system_backend (impl, g_value_get_string (value));
2244 case GTK_FILE_CHOOSER_PROP_FILTER:
2245 set_current_filter (impl, g_value_get_object (value));
2247 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
2248 impl->local_only = g_value_get_boolean (value);
2250 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
2251 set_preview_widget (impl, g_value_get_object (value));
2253 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
2254 impl->preview_widget_active = g_value_get_boolean (value);
2255 update_preview_widget_visibility (impl);
2257 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
2258 impl->use_preview_label = g_value_get_boolean (value);
2259 update_preview_widget_visibility (impl);
2261 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
2262 set_extra_widget (impl, g_value_get_object (value));
2263 update_appearance (impl);
2265 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
2267 gboolean select_multiple = g_value_get_boolean (value);
2268 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE && select_multiple)
2270 g_warning ("Multiple selection mode is not allowed in Save mode");
2274 set_select_multiple (impl, select_multiple, FALSE);
2277 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
2279 gboolean show_hidden = g_value_get_boolean (value);
2280 if (show_hidden != impl->show_hidden)
2282 impl->show_hidden = show_hidden;
2283 _gtk_file_system_model_set_show_hidden (GTK_FILE_SYSTEM_MODEL (impl->browse_directories_model),
2285 _gtk_file_system_model_set_show_hidden (GTK_FILE_SYSTEM_MODEL (impl->browse_files_model),
2291 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2297 gtk_file_chooser_default_get_property (GObject *object,
2302 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
2306 case GTK_FILE_CHOOSER_PROP_ACTION:
2307 g_value_set_enum (value, impl->action);
2309 case GTK_FILE_CHOOSER_PROP_FILTER:
2310 g_value_set_object (value, impl->current_filter);
2312 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
2313 g_value_set_boolean (value, impl->local_only);
2315 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
2316 g_value_set_object (value, impl->preview_widget);
2318 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
2319 g_value_set_boolean (value, impl->preview_widget_active);
2321 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
2322 g_value_set_boolean (value, impl->use_preview_label);
2324 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
2325 g_value_set_object (value, impl->extra_widget);
2327 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
2328 g_value_set_boolean (value, impl->select_multiple);
2330 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
2331 g_value_set_boolean (value, impl->show_hidden);
2334 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2341 gtk_file_chooser_default_dispose (GObject *object)
2343 GtkFileChooserDefault *impl = (GtkFileChooserDefault *) object;
2345 if (impl->extra_widget)
2347 g_object_unref (impl->extra_widget);
2348 impl->extra_widget = NULL;
2350 G_OBJECT_CLASS (parent_class)->dispose (object);
2353 /* We override show-all since we have internal widgets that
2354 * shouldn't be shown when you call show_all(), like the filter
2358 gtk_file_chooser_default_show_all (GtkWidget *widget)
2360 gtk_widget_show (widget);
2364 gtk_file_chooser_default_style_set (GtkWidget *widget,
2365 GtkStyle *previous_style)
2367 if (GTK_WIDGET_CLASS (parent_class)->style_set)
2368 GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
2370 g_signal_emit_by_name (widget, "default-size-changed");
2374 gtk_file_chooser_default_screen_changed (GtkWidget *widget,
2375 GdkScreen *previous_screen)
2377 if (GTK_WIDGET_CLASS (parent_class)->screen_changed)
2378 GTK_WIDGET_CLASS (parent_class)->screen_changed (widget, previous_screen);
2380 g_signal_emit_by_name (widget, "default-size-changed");
2384 expand_and_select_func (GtkFileSystemModel *model,
2389 GtkFileChooserDefault *impl = user_data;
2390 GtkTreeView *tree_view;
2392 if (model == impl->browse_directories_model)
2393 tree_view = GTK_TREE_VIEW (impl->browse_directories_tree_view);
2395 tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
2397 gtk_tree_view_expand_to_path (tree_view, path);
2398 gtk_tree_view_expand_row (tree_view, path, FALSE);
2399 gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
2400 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_directories_tree_view), path, NULL, TRUE, 0.3, 0.5);
2404 list_model_filter_func (GtkFileSystemModel *model,
2406 const GtkFileInfo *file_info,
2409 GtkFileChooserDefault *impl = user_data;
2410 GtkFileFilterInfo filter_info;
2411 GtkFileFilterFlags needed;
2414 if (!impl->current_filter)
2417 if (gtk_file_info_get_is_folder (file_info))
2420 filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
2422 needed = gtk_file_filter_get_needed (impl->current_filter);
2424 filter_info.display_name = gtk_file_info_get_display_name (file_info);
2425 filter_info.mime_type = gtk_file_info_get_mime_type (file_info);
2427 if (needed & GTK_FILE_FILTER_FILENAME)
2429 filter_info.filename = gtk_file_system_path_to_filename (impl->file_system, path);
2430 if (filter_info.filename)
2431 filter_info.contains |= GTK_FILE_FILTER_FILENAME;
2434 filter_info.filename = NULL;
2436 if (needed & GTK_FILE_FILTER_URI)
2438 filter_info.uri = gtk_file_system_path_to_uri (impl->file_system, path);
2439 if (filter_info.uri)
2440 filter_info.contains |= GTK_FILE_FILTER_URI;
2443 filter_info.uri = NULL;
2445 result = gtk_file_filter_filter (impl->current_filter, &filter_info);
2447 if (filter_info.filename)
2448 g_free ((gchar *)filter_info.filename);
2449 if (filter_info.uri)
2450 g_free ((gchar *)filter_info.uri);
2456 install_list_model_filter (GtkFileChooserDefault *impl)
2458 if (impl->current_filter)
2459 _gtk_file_system_model_set_filter (impl->browse_files_model,
2460 list_model_filter_func,
2464 #define COMPARE_DIRECTORIES \
2465 GtkFileChooserDefault *impl = user_data; \
2466 const GtkFileInfo *info_a = _gtk_file_system_model_get_info (impl->browse_files_model, a); \
2467 const GtkFileInfo *info_b = _gtk_file_system_model_get_info (impl->browse_files_model, b); \
2468 gboolean dir_a, dir_b; \
2471 dir_a = gtk_file_info_get_is_folder (info_a); \
2473 return impl->list_sort_ascending ? -1 : 1; \
2476 dir_b = gtk_file_info_get_is_folder (info_b); \
2478 return impl->list_sort_ascending ? 1 : -1; \
2480 if (dir_a != dir_b) \
2481 return impl->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */
2483 /* Sort callback for the filename column */
2485 name_sort_func (GtkTreeModel *model,
2490 COMPARE_DIRECTORIES;
2492 return strcmp (gtk_file_info_get_display_key (info_a), gtk_file_info_get_display_key (info_b));
2495 /* Sort callback for the size column */
2497 size_sort_func (GtkTreeModel *model,
2502 COMPARE_DIRECTORIES;
2505 gint64 size_a = gtk_file_info_get_size (info_a);
2506 gint64 size_b = gtk_file_info_get_size (info_b);
2508 return size_a > size_b ? -1 : (size_a == size_b ? 0 : 1);
2512 /* Sort callback for the mtime column */
2514 mtime_sort_func (GtkTreeModel *model,
2519 COMPARE_DIRECTORIES;
2522 GtkFileTime ta = gtk_file_info_get_modification_time (info_a);
2523 GtkFileTime tb = gtk_file_info_get_modification_time (info_b);
2525 return ta > tb ? -1 : (ta == tb ? 0 : 1);
2529 /* Callback used when the sort column changes. We cache the sort order for use
2530 * in name_sort_func().
2533 list_sort_column_changed_cb (GtkTreeSortable *sortable,
2534 GtkFileChooserDefault *impl)
2536 GtkSortType sort_type;
2538 if (gtk_tree_sortable_get_sort_column_id (sortable, NULL, &sort_type))
2539 impl->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
2542 /* Gets rid of the old list model and creates a new one for the current folder */
2544 set_list_model (GtkFileChooserDefault *impl)
2546 if (impl->browse_files_model)
2548 g_object_unref (impl->browse_files_model);
2549 g_object_unref (impl->sort_model);
2552 impl->browse_files_model = _gtk_file_system_model_new (impl->file_system,
2553 impl->current_folder, 0,
2555 _gtk_file_system_model_set_show_hidden (impl->browse_files_model, impl->show_hidden);
2556 install_list_model_filter (impl);
2558 impl->sort_model = (GtkTreeModelSort *)gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (impl->browse_files_model));
2559 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, name_sort_func, impl, NULL);
2560 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_SIZE, size_sort_func, impl, NULL);
2561 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_MTIME, mtime_sort_func, impl, NULL);
2562 gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->sort_model), NULL, NULL, NULL);
2563 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, GTK_SORT_ASCENDING);
2564 impl->list_sort_ascending = TRUE;
2566 g_signal_connect (impl->sort_model, "sort_column_changed",
2567 G_CALLBACK (list_sort_column_changed_cb), impl);
2569 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
2570 GTK_TREE_MODEL (impl->sort_model));
2571 gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->browse_files_tree_view));
2572 gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_files_tree_view),
2573 GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
2576 /* Gets rid of the old folder tree model and creates a new one for the volume
2577 * corresponding to the specified path.
2580 set_tree_model (GtkFileChooserDefault *impl, const GtkFilePath *path)
2582 GtkFileSystemVolume *volume;
2583 GtkFilePath *base_path, *parent_path;
2587 volume = gtk_file_system_get_volume_for_path (impl->file_system, path);
2590 base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
2592 if (base_path == NULL)
2594 base_path = gtk_file_path_copy (path);
2595 while (gtk_file_system_get_parent (impl->file_system,
2599 parent_path != NULL)
2601 gtk_file_path_free (base_path);
2602 base_path = parent_path;
2606 if (impl->current_volume_path && gtk_file_path_compare (base_path, impl->current_volume_path) == 0)
2609 if (impl->browse_directories_model)
2610 g_object_unref (impl->browse_directories_model);
2612 impl->current_volume_path = gtk_file_path_copy (base_path);
2614 impl->browse_directories_model = _gtk_file_system_model_new (impl->file_system, impl->current_volume_path, -1,
2615 GTK_FILE_INFO_DISPLAY_NAME);
2616 _gtk_file_system_model_set_show_files (GTK_FILE_SYSTEM_MODEL (impl->browse_directories_model),
2618 _gtk_file_system_model_set_show_hidden (GTK_FILE_SYSTEM_MODEL (impl->browse_directories_model),
2621 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_directories_tree_view),
2622 GTK_TREE_MODEL (impl->browse_directories_model));
2626 gtk_file_path_free (base_path);
2628 gtk_file_system_volume_free (impl->file_system, volume);
2632 update_chooser_entry (GtkFileChooserDefault *impl)
2634 GtkTreeSelection *selection;
2635 const GtkFileInfo *info;
2637 GtkTreeIter child_iter;
2639 if (impl->action != GTK_FILE_CHOOSER_ACTION_SAVE)
2642 g_assert (!impl->select_multiple);
2643 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2645 if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
2648 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
2652 info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
2654 if (!gtk_file_info_get_is_folder (info))
2655 gtk_entry_set_text (GTK_ENTRY (impl->save_file_name_entry),
2656 gtk_file_info_get_display_name (info));
2660 gtk_file_chooser_default_set_current_folder (GtkFileChooser *chooser,
2661 const GtkFilePath *path)
2663 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2665 if (impl->current_folder)
2666 gtk_file_path_free (impl->current_folder);
2668 impl->current_folder = gtk_file_path_copy (path);
2670 /* Change the current folder label */
2671 gtk_path_bar_set_path (GTK_PATH_BAR (impl->browse_path_bar), path, impl->file_system, NULL);
2673 /* Update the folder tree */
2675 if (!impl->changing_folder)
2677 impl->changing_folder = TRUE;
2678 set_tree_model (impl, impl->current_folder);
2679 _gtk_file_system_model_path_do (GTK_FILE_SYSTEM_MODEL (impl->browse_directories_model),
2680 path, expand_and_select_func, impl);
2681 impl->changing_folder = FALSE;
2684 /* Create a new list model */
2685 set_list_model (impl);
2687 /* Refresh controls */
2689 shortcuts_unselect_all (impl);
2691 g_signal_emit_by_name (impl, "current-folder-changed", 0);
2693 check_preview_change (impl);
2694 bookmarks_check_add_sensitivity (impl);
2696 g_signal_emit_by_name (impl, "selection-changed", 0);
2699 static GtkFilePath *
2700 gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser)
2702 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2704 return gtk_file_path_copy (impl->current_folder);
2708 gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
2711 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2713 g_return_if_fail (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE);
2715 gtk_entry_set_text (GTK_ENTRY (impl->save_file_name_entry), name);
2719 select_func (GtkFileSystemModel *model,
2724 GtkFileChooserDefault *impl = user_data;
2725 GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
2726 GtkTreePath *sorted_path;
2728 sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model, path);
2729 gtk_tree_view_set_cursor (tree_view, sorted_path, NULL, FALSE);
2730 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_directories_tree_view), sorted_path, NULL, TRUE, 0.3, 0.0);
2731 gtk_tree_path_free (sorted_path);
2735 gtk_file_chooser_default_select_path (GtkFileChooser *chooser,
2736 const GtkFilePath *path)
2738 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2739 GtkFilePath *parent_path;
2743 if (!gtk_file_system_get_parent (impl->file_system, path, &parent_path, &error))
2745 error_getting_info_dialog (impl, path, error);
2751 _gtk_file_chooser_set_current_folder_path (chooser, path);
2755 _gtk_file_chooser_set_current_folder_path (chooser, parent_path);
2756 gtk_file_path_free (parent_path);
2757 _gtk_file_system_model_path_do (impl->browse_files_model, path,
2763 unselect_func (GtkFileSystemModel *model,
2768 GtkFileChooserDefault *impl = user_data;
2769 GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
2770 GtkTreePath *sorted_path;
2772 sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model,
2774 gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (tree_view),
2776 gtk_tree_path_free (sorted_path);
2780 gtk_file_chooser_default_unselect_path (GtkFileChooser *chooser,
2781 const GtkFilePath *path)
2783 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2785 _gtk_file_system_model_path_do (impl->browse_files_model, path,
2786 unselect_func, impl);
2790 gtk_file_chooser_default_select_all (GtkFileChooser *chooser)
2792 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2793 if (impl->select_multiple)
2795 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2796 gtk_tree_selection_select_all (selection);
2801 gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser)
2803 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2804 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2806 gtk_tree_selection_unselect_all (selection);
2809 struct get_paths_closure {
2810 GtkFileChooserDefault *impl;
2812 GtkFilePath *path_from_entry;
2816 get_paths_foreach (GtkTreeModel *model,
2821 struct get_paths_closure *info;
2822 const GtkFilePath *file_path;
2823 GtkFileSystemModel *fs_model;
2824 GtkTreeIter sel_iter;
2828 if (info->impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
2829 info->impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
2831 fs_model = info->impl->browse_directories_model;
2836 fs_model = info->impl->browse_files_model;
2837 gtk_tree_model_sort_convert_iter_to_child_iter (info->impl->sort_model, &sel_iter, iter);
2840 file_path = _gtk_file_system_model_get_path (GTK_FILE_SYSTEM_MODEL (fs_model), &sel_iter);
2842 if (!info->path_from_entry
2843 || gtk_file_path_compare (info->path_from_entry, file_path) != 0)
2844 info->result = g_slist_prepend (info->result, gtk_file_path_copy (file_path));
2848 gtk_file_chooser_default_get_paths (GtkFileChooser *chooser)
2850 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2851 struct get_paths_closure info;
2855 info.path_from_entry = NULL;
2857 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
2859 const char *filename;
2861 filename = gtk_entry_get_text (GTK_ENTRY (impl->save_file_name_entry));
2863 if (filename != NULL && filename[0] != '\0')
2865 GtkFilePath *selected;
2866 GError *error = NULL;
2868 selected = gtk_file_system_make_path (impl->file_system, impl->current_folder, filename, &error);
2872 error_building_filename_dialog (impl, impl->current_folder, filename, error);
2876 info.path_from_entry = selected;
2880 if (!info.path_from_entry || impl->select_multiple)
2882 GtkTreeSelection *selection;
2886 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
2887 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
2889 if (impl->browse_directories_model)
2890 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_directories_tree_view));
2894 if (impl->sort_model)
2895 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2899 gtk_tree_selection_selected_foreach (selection, get_paths_foreach, &info);
2902 if (info.path_from_entry)
2903 info.result = g_slist_prepend (info.result, info.path_from_entry);
2905 return g_slist_reverse (info.result);
2908 static GtkFilePath *
2909 gtk_file_chooser_default_get_preview_path (GtkFileChooser *chooser)
2911 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2913 if (impl->preview_path)
2914 return gtk_file_path_copy (impl->preview_path);
2919 static GtkFileSystem *
2920 gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser)
2922 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2924 return impl->file_system;
2927 /* Shows or hides the filter widgets */
2929 toolbar_show_filters (GtkFileChooserDefault *impl,
2933 gtk_widget_show (impl->filter_combo);
2935 gtk_widget_hide (impl->filter_combo);
2939 gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
2940 GtkFileFilter *filter)
2942 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2945 if (g_slist_find (impl->filters, filter))
2947 g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
2951 g_object_ref (filter);
2952 gtk_object_sink (GTK_OBJECT (filter));
2953 impl->filters = g_slist_append (impl->filters, filter);
2955 name = gtk_file_filter_get_name (filter);
2957 name = "Untitled filter"; /* Place-holder, doesn't need to be marked for translation */
2959 gtk_combo_box_append_text (GTK_COMBO_BOX (impl->filter_combo), name);
2961 if (!g_slist_find (impl->filters, impl->current_filter))
2962 set_current_filter (impl, filter);
2964 toolbar_show_filters (impl, TRUE);
2968 gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
2969 GtkFileFilter *filter)
2971 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2972 GtkTreeModel *model;
2976 filter_index = g_slist_index (impl->filters, filter);
2978 if (filter_index < 0)
2980 g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
2984 impl->filters = g_slist_remove (impl->filters, filter);
2986 if (filter == impl->current_filter)
2989 set_current_filter (impl, impl->filters->data);
2991 set_current_filter (impl, NULL);
2994 /* Remove row from the combo box */
2995 model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->filter_combo));
2996 gtk_tree_model_iter_nth_child (model, &iter, NULL, filter_index);
2997 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
2999 g_object_unref (filter);
3002 toolbar_show_filters (impl, FALSE);
3006 gtk_file_chooser_default_list_filters (GtkFileChooser *chooser)
3008 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3010 return g_slist_copy (impl->filters);
3013 /* Returns the position in the shortcuts tree where the nth specified shortcut would appear */
3015 shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl,
3018 return pos + shortcuts_get_index (impl, SHORTCUTS_SHORTCUTS);
3022 gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser *chooser,
3023 const GtkFilePath *path,
3026 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3030 pos = shortcuts_get_pos_for_shortcut_folder (impl, impl->num_shortcuts);
3032 result = shortcuts_insert_path (impl, pos, FALSE, NULL, path, NULL, FALSE, error);
3035 impl->num_shortcuts++;
3041 gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser *chooser,
3042 const GtkFilePath *path,
3045 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3050 if (impl->num_shortcuts == 0)
3053 pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
3054 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
3055 g_assert_not_reached ();
3057 for (i = 0; i < impl->num_shortcuts; i++)
3059 GtkFilePath *shortcut;
3061 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &shortcut, -1);
3062 g_assert (shortcut != NULL);
3064 if (gtk_file_path_compare (shortcut, path) == 0)
3066 /* The other columns are freed by the GtkTreeStore */
3067 gtk_file_path_free (shortcut);
3068 gtk_list_store_remove (impl->shortcuts_model, &iter);
3069 impl->num_shortcuts--;
3073 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
3074 g_assert_not_reached ();
3080 GTK_FILE_CHOOSER_ERROR,
3081 GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
3082 _("shortcut %s does not exist"),
3083 gtk_file_path_get_string (path));
3089 gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser)
3091 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3097 if (impl->num_shortcuts == 0)
3100 pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
3101 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
3102 g_assert_not_reached ();
3106 for (i = 0; i < impl->num_shortcuts; i++)
3108 GtkFilePath *shortcut;
3110 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &shortcut, -1);
3111 g_assert (shortcut != NULL);
3113 list = g_slist_prepend (list, gtk_file_path_copy (shortcut));
3115 if (i != impl->num_shortcuts - 1)
3117 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
3118 g_assert_not_reached ();
3122 return g_slist_reverse (list);
3125 /* Guesses a size based upon font sizes */
3127 find_good_size_from_style (GtkWidget *widget,
3131 GtkFileChooserDefault *impl;
3132 gint default_width, default_height;
3135 GtkRequisition preview_req;
3137 g_assert (widget->style != NULL);
3138 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
3140 font_size = pango_font_description_get_size (widget->style->font_desc);
3141 font_size = PANGO_PIXELS (font_size);
3143 default_width = font_size * NUM_CHARS;
3144 default_height = font_size * NUM_LINES;
3146 /* Use at least the requisition size not including the preview widget */
3147 gtk_widget_size_request (widget, &req);
3149 if (impl->preview_widget_active && impl->preview_widget)
3150 gtk_widget_size_request (impl->preview_box, &preview_req);
3152 preview_req.width = 0;
3154 default_width = MAX (default_width, (req.width - (preview_req.width + PREVIEW_HBOX_SPACING)));
3155 default_height = MAX (default_height, req.height);
3157 *width = default_width;
3158 *height = default_height;
3162 gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
3163 gint *default_width,
3164 gint *default_height)
3166 GtkFileChooserDefault *impl;
3168 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
3170 find_good_size_from_style (GTK_WIDGET (chooser_embed), default_width, default_height);
3172 if (impl->preview_widget_active && impl->preview_widget)
3173 *default_width += impl->preview_box->requisition.width + PREVIEW_HBOX_SPACING;
3177 gtk_file_chooser_default_get_resizable_hints (GtkFileChooserEmbed *chooser_embed,
3178 gboolean *resize_horizontally,
3179 gboolean *resize_vertically)
3181 GtkFileChooserDefault *impl;
3183 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
3185 if (*resize_horizontally)
3186 *resize_horizontally = TRUE;
3188 if (resize_vertically)
3190 *resize_vertically = TRUE;
3192 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
3193 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
3195 if (! gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
3197 *resize_vertically = FALSE;
3198 *resize_horizontally = FALSE;
3205 set_current_filter (GtkFileChooserDefault *impl,
3206 GtkFileFilter *filter)
3208 if (impl->current_filter != filter)
3212 /* If we have filters, new filter must be one of them
3214 filter_index = g_slist_index (impl->filters, filter);
3215 if (impl->filters && filter_index < 0)
3218 if (impl->current_filter)
3219 g_object_unref (impl->current_filter);
3220 impl->current_filter = filter;
3221 if (impl->current_filter)
3223 g_object_ref (impl->current_filter);
3224 gtk_object_sink (GTK_OBJECT (filter));
3228 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
3231 install_list_model_filter (impl);
3233 g_object_notify (G_OBJECT (impl), "filter");
3238 open_and_close (GtkTreeView *tree_view,
3239 GtkTreePath *target_path)
3241 GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
3245 path = gtk_tree_path_new ();
3246 gtk_tree_path_append_index (path, 0);
3248 gtk_tree_model_get_iter (model, &iter, path);
3252 if (gtk_tree_path_is_ancestor (path, target_path) ||
3253 gtk_tree_path_compare (path, target_path) == 0)
3255 GtkTreeIter child_iter;
3256 gtk_tree_view_expand_row (tree_view, path, FALSE);
3257 if (gtk_tree_model_iter_children (model, &child_iter, &iter))
3260 gtk_tree_path_down (path);
3265 gtk_tree_view_collapse_row (tree_view, path);
3269 GtkTreeIter parent_iter;
3270 GtkTreeIter next_iter;
3273 if (gtk_tree_model_iter_next (model, &next_iter))
3276 gtk_tree_path_next (path);
3280 if (!gtk_tree_model_iter_parent (model, &parent_iter, &iter))
3284 gtk_tree_path_up (path);
3291 gtk_tree_path_free (path);
3295 filter_combo_changed (GtkComboBox *combo_box,
3296 GtkFileChooserDefault *impl)
3298 gint new_index = gtk_combo_box_get_active (combo_box);
3299 GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
3301 set_current_filter (impl, new_filter);
3305 check_preview_change (GtkFileChooserDefault *impl)
3307 const GtkFilePath *new_path = NULL;
3308 const GtkFileInfo *new_info = NULL;
3310 /* FIXME #132255: Fixing preview for multiple selection involves getting the
3311 * full selection and diffing to find out what the most recently selected file
3312 * is; there is logic in GtkFileSelection that probably can be
3315 if (impl->sort_model && !impl->select_multiple)
3317 GtkTreeSelection *selection;
3320 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3321 if (gtk_tree_selection_get_selected (selection, NULL, &iter))
3323 GtkTreeIter child_iter;
3325 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
3326 &child_iter, &iter);
3328 new_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
3329 new_info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
3333 if (new_path != impl->preview_path &&
3334 !(new_path && impl->preview_path &&
3335 gtk_file_path_compare (new_path, impl->preview_path) == 0))
3337 if (impl->preview_path)
3339 gtk_file_path_free (impl->preview_path);
3340 g_free (impl->preview_display_name);
3345 impl->preview_path = gtk_file_path_copy (new_path);
3346 impl->preview_display_name = g_strdup (gtk_file_info_get_display_name (new_info));
3350 impl->preview_path = NULL;
3351 impl->preview_display_name = NULL;
3354 if (impl->use_preview_label && impl->preview_label)
3355 gtk_label_set_text (GTK_LABEL (impl->preview_label), impl->preview_display_name);
3357 g_signal_emit_by_name (impl, "update-preview");
3362 tree_selection_changed (GtkTreeSelection *selection,
3363 GtkFileChooserDefault *impl)
3366 const GtkFilePath *file_path;
3369 /* FIXME #132255: Fixing this for multiple selection involves getting the full
3370 * selection and diffing to find out what the most recently selected file is;
3371 * there is logic in GtkFileSelection that probably can be copied;
3372 * check_preview_change() is similar.
3374 if (impl->select_multiple
3375 || !gtk_tree_selection_get_selected (selection, NULL, &iter))
3378 file_path = _gtk_file_system_model_get_path (GTK_FILE_SYSTEM_MODEL (impl->browse_directories_model),
3380 if (impl->current_folder && gtk_file_path_compare (file_path, impl->current_folder) == 0)
3383 /* Close the tree up to only the parents of the newly selected
3384 * node and it's immediate children are visible.
3386 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->browse_directories_model), &iter);
3387 open_and_close (GTK_TREE_VIEW (impl->browse_directories_tree_view), path);
3388 gtk_tree_path_free (path);
3390 if (!impl->changing_folder)
3391 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), file_path);
3394 /* Activates a volume by mounting it if necessary and then switching to its
3398 shortcuts_activate_volume (GtkFileChooserDefault *impl,
3399 GtkFileSystemVolume *volume)
3403 if (!gtk_file_system_volume_get_is_mounted (impl->file_system, volume))
3408 if (!gtk_file_system_volume_mount (impl->file_system, volume, &error))
3412 msg = g_strdup_printf ("Could not mount %s:\n%s",
3413 gtk_file_system_volume_get_display_name (impl->file_system, volume),
3415 error_message (impl, msg);
3417 g_error_free (error);
3423 path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
3424 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), path);
3425 gtk_file_path_free (path);
3428 /* Callback used when a row in the shortcuts list is activated */
3430 shortcuts_row_activated_cb (GtkTreeView *tree_view,
3432 GtkTreeViewColumn *column,
3433 GtkFileChooserDefault *impl)
3436 int selected, start_row;
3439 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
3442 selected = *gtk_tree_path_get_indices (path);
3444 if (selected == shortcuts_get_index (impl, SHORTCUTS_SEPARATOR))
3447 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &data, -1);
3449 start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
3450 if (selected >= start_row && selected < start_row + impl->num_volumes)
3452 GtkFileSystemVolume *volume;
3455 shortcuts_activate_volume (impl, volume);
3459 GtkFilePath *file_path;
3462 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), file_path);
3467 shortcuts_select_func (GtkTreeSelection *selection,
3468 GtkTreeModel *model,
3470 gboolean path_currently_selected,
3473 GtkFileChooserDefault *impl = data;
3475 return (*gtk_tree_path_get_indices (path) != shortcuts_get_index (impl, SHORTCUTS_SEPARATOR));
3479 list_selection_changed (GtkTreeSelection *selection,
3480 GtkFileChooserDefault *impl)
3482 /* See if we are in the new folder editable row for Save mode */
3483 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
3485 GtkTreeSelection *selection;
3486 GtkTreeIter iter, child_iter;
3487 const GtkFileInfo *info;
3489 g_assert (!impl->select_multiple);
3490 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3491 if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
3494 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
3498 info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
3500 return; /* We are on the editable row for New Folder */
3503 update_chooser_entry (impl);
3504 check_preview_change (impl);
3505 bookmarks_check_add_sensitivity (impl);
3507 g_signal_emit_by_name (impl, "selection-changed", 0);
3510 /* Callback used when a row in the file list is activated */
3512 list_row_activated (GtkTreeView *tree_view,
3514 GtkTreeViewColumn *column,
3515 GtkFileChooserDefault *impl)
3517 GtkTreeIter iter, child_iter;
3518 const GtkFileInfo *info;
3520 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
3523 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
3525 info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
3527 if (gtk_file_info_get_is_folder (info))
3529 const GtkFilePath *file_path;
3531 file_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
3532 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), file_path);
3537 g_signal_emit_by_name (impl, "file-activated");
3541 path_bar_clicked (GtkPathBar *path_bar,
3542 GtkFilePath *file_path,
3543 GtkFileChooserDefault *impl)
3545 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), file_path);
3548 static const GtkFileInfo *
3549 get_list_file_info (GtkFileChooserDefault *impl,
3552 GtkTreeIter child_iter;
3554 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
3558 return _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
3562 tree_name_data_func (GtkTreeViewColumn *tree_column,
3563 GtkCellRenderer *cell,
3564 GtkTreeModel *tree_model,
3568 GtkFileChooserDefault *impl = data;
3569 const GtkFileInfo *info;
3571 info = _gtk_file_system_model_get_info (GTK_FILE_SYSTEM_MODEL (impl->browse_directories_model),
3577 "text", gtk_file_info_get_display_name (info),
3583 list_icon_data_func (GtkTreeViewColumn *tree_column,
3584 GtkCellRenderer *cell,
3585 GtkTreeModel *tree_model,
3589 GtkFileChooserDefault *impl = data;
3590 GtkTreeIter child_iter;
3591 const GtkFilePath *path;
3594 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
3597 path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
3601 /* FIXME: NULL GError */
3602 pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl), ICON_SIZE, NULL);
3608 g_object_unref (pixbuf);
3612 list_name_data_func (GtkTreeViewColumn *tree_column,
3613 GtkCellRenderer *cell,
3614 GtkTreeModel *tree_model,
3618 GtkFileChooserDefault *impl = data;
3619 const GtkFileInfo *info = get_list_file_info (impl, iter);
3624 "text", _("Type name of new folder"),
3630 "text", gtk_file_info_get_display_name (info),
3636 list_size_data_func (GtkTreeViewColumn *tree_column,
3637 GtkCellRenderer *cell,
3638 GtkTreeModel *tree_model,
3642 GtkFileChooserDefault *impl = data;
3643 const GtkFileInfo *info = get_list_file_info (impl, iter);
3647 if (!info || gtk_file_info_get_is_folder (info))
3650 size = gtk_file_info_get_size (info);
3652 if (size < (gint64)1024)
3653 str = g_strdup_printf (ngettext ("%d byte", "%d bytes", (gint)size), (gint)size);
3654 else if (size < (gint64)1024*1024)
3655 str = g_strdup_printf (_("%.1f K"), size / (1024.));
3656 else if (size < (gint64)1024*1024*1024)
3657 str = g_strdup_printf (_("%.1f M"), size / (1024.*1024.));
3659 str = g_strdup_printf (_("%.1f G"), size / (1024.*1024.*1024.));
3669 /* Tree column data callback for the file list; fetches the mtime of a file */
3671 list_mtime_data_func (GtkTreeViewColumn *tree_column,
3672 GtkCellRenderer *cell,
3673 GtkTreeModel *tree_model,
3677 GtkFileChooserDefault *impl;
3678 const GtkFileInfo *info;
3679 GtkFileTime time_mtime, time_now;
3686 info = get_list_file_info (impl, iter);
3695 time_mtime = gtk_file_info_get_modification_time (info);
3696 g_date_set_time (&mtime, (GTime) time_mtime);
3698 time_now = (GTime ) time (NULL);
3699 g_date_set_time (&now, (GTime) time_now);
3701 days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
3704 strcpy (buf, _("Today"));
3705 else if (days_diff == 1)
3706 strcpy (buf, _("Yesterday"));
3711 if (days_diff > 1 && days_diff < 7)
3712 format = "%A"; /* Days from last week */
3714 /* FIXME: Get the right format for the locale */
3715 format = _("%d/%b/%Y"); /* Any other date */
3717 if (g_date_strftime (buf, sizeof (buf), format, &mtime) == 0)
3718 strcpy (buf, _("Unknown"));
3727 _gtk_file_chooser_default_new (const char *file_system)
3729 return g_object_new (GTK_TYPE_FILE_CHOOSER_DEFAULT,
3730 "file-system-backend", file_system,
3735 location_entry_create (GtkFileChooserDefault *impl)
3739 entry = _gtk_file_chooser_entry_new ();
3740 /* Pick a good width for the entry */
3741 gtk_entry_set_width_chars (GTK_ENTRY (entry), 25);
3742 gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
3743 _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (entry), impl->file_system);
3744 _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (entry), impl->current_folder);
3746 return GTK_WIDGET (entry);
3750 update_from_entry (GtkFileChooserDefault *impl,
3752 GtkFileChooserEntry *chooser_entry)
3754 const GtkFilePath *folder_path;
3755 const char *file_part;
3757 folder_path = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
3758 file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
3760 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN && !folder_path)
3762 error_message_with_parent (parent,
3763 _("Cannot change to the folder you specified as it is an invalid path."));
3767 if (file_part[0] == '\0')
3769 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), folder_path);
3774 GtkFileFolder *folder = NULL;
3775 GtkFilePath *subfolder_path = NULL;
3776 GtkFileInfo *info = NULL;
3779 /* If the file part is non-empty, we need to figure out if it refers to a
3780 * folder within folder. We could optimize the case here where the folder
3781 * is already loaded for one of our tree models.
3785 folder = gtk_file_system_get_folder (impl->file_system, folder_path, GTK_FILE_INFO_IS_FOLDER, &error);
3789 error_getting_info_dialog (impl, folder_path, error);
3794 subfolder_path = gtk_file_system_make_path (impl->file_system, folder_path, file_part, &error);
3796 if (!subfolder_path)
3800 msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
3801 gtk_file_path_get_string (folder_path),
3804 error_message (impl, msg);
3806 g_object_unref (folder);
3811 info = gtk_file_folder_get_info (folder, subfolder_path, &error);
3816 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
3818 g_object_unref (folder);
3819 gtk_file_path_free (subfolder_path);
3823 error_getting_info_dialog (impl, subfolder_path, error);
3824 g_object_unref (folder);
3825 gtk_file_path_free (subfolder_path);
3829 if (gtk_file_info_get_is_folder (info))
3830 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), subfolder_path);
3832 _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (impl), subfolder_path);
3834 g_object_unref (folder);
3835 gtk_file_path_free (subfolder_path);
3836 gtk_file_info_free (info);
3841 location_popup_handler (GtkFileChooserDefault *impl)
3844 GtkWidget *toplevel;
3851 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
3852 if (!GTK_WIDGET_TOPLEVEL (toplevel))
3855 dialog = gtk_dialog_new_with_buttons (_("Open Location"),
3856 GTK_WINDOW (toplevel),
3857 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
3858 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3859 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
3861 gtk_window_set_default_size (GTK_WINDOW (dialog), 300, -1);
3862 gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
3863 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2);
3864 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
3866 hbox = gtk_hbox_new (FALSE, 12);
3867 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0);
3868 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
3870 label = gtk_label_new_with_mnemonic (_("_Location:"));
3871 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
3873 entry = location_entry_create (impl);
3874 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
3875 gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
3879 gtk_widget_show_all (dialog);
3880 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
3881 update_from_entry (impl, GTK_WINDOW (dialog), GTK_FILE_CHOOSER_ENTRY (entry));
3883 gtk_widget_destroy (dialog);
3886 /* Handler for the "up-folder" keybinding signal */
3888 up_folder_handler (GtkFileChooserDefault *impl)
3890 GtkFilePath *parent_path;
3894 if (gtk_file_system_get_parent (impl->file_system, impl->current_folder, &parent_path, &error))
3896 if (parent_path) /* If we were on a root, parent_path will be NULL */
3898 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), parent_path);
3899 gtk_file_path_free (parent_path);
3904 _("Could not go to the parent folder of %s:\n%s"),
3905 impl->current_folder,
3909 /* Handler for the "home-folder" keybinding signal */
3911 home_folder_handler (GtkFileChooserDefault *impl)
3915 /* Should we pull this information from impl->has_home and the shortcuts data
3916 * instead? Sounds like a bit of overkill...
3919 home = g_get_home_dir ();
3920 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (impl), home);