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 "gtkfilechooserentry.h"
33 #include "gtkfilechooserutils.h"
34 #include "gtkfilechooser.h"
35 #include "gtkfilesystemmodel.h"
38 #include "gtkhpaned.h"
39 #include "gtkicontheme.h"
43 #include "gtkmarshalers.h"
44 #include "gtkmenuitem.h"
45 #include "gtkmessagedialog.h"
46 #include "gtkpathbar.h"
47 #include "gtkprivate.h"
48 #include "gtkscrolledwindow.h"
49 #include "gtksizegroup.h"
52 #include "gtktreeview.h"
53 #include "gtktreemodelsort.h"
54 #include "gtktreeselection.h"
55 #include "gtktreestore.h"
56 #include "gtktypebuiltins.h"
59 #if defined (G_OS_UNIX)
60 #include "gtkfilesystemunix.h"
61 #elif defined (G_OS_WIN32)
62 #include "gtkfilesystemwin32.h"
68 typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
70 #define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
71 #define GTK_IS_FILE_CHOOSER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT))
72 #define GTK_FILE_CHOOSER_DEFAULT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
74 struct _GtkFileChooserDefaultClass
76 GtkVBoxClass parent_class;
79 struct _GtkFileChooserDefault
81 GtkVBox parent_instance;
83 GtkFileChooserAction action;
85 GtkFileSystem *file_system;
87 /* Save mode widgets */
88 GtkWidget *save_widgets;
90 GtkWidget *save_file_name_entry;
91 GtkWidget *save_folder_label;
92 GtkWidget *save_folder_combo;
93 GtkWidget *save_extra_align;
94 GtkWidget *save_expander;
96 /* The file browsing widgets */
97 GtkWidget *browse_widgets;
98 GtkWidget *browse_shortcuts_tree_view;
99 GtkWidget *browse_shortcuts_swin;
100 GtkWidget *browse_shortcuts_add_button;
101 GtkWidget *browse_shortcuts_remove_button;
102 GtkWidget *browse_files_swin;
103 GtkWidget *browse_files_tree_view;
104 GtkWidget *browse_directories_swin;
105 GtkWidget *browse_directories_tree_view;
106 GtkWidget *browse_new_folder_button;
107 GtkWidget *browse_path_bar;
108 GtkWidget *browse_extra_align;
109 GtkTreeModel *browse_shortcuts_model;
110 GtkFileSystemModel *browse_files_model;
111 GtkFileSystemModel *browse_directories_model;
113 GtkWidget *filter_combo;
114 GtkWidget *preview_widget;
115 GtkWidget *extra_widget;
117 GtkListStore *shortcuts_model;
118 GtkTreeModelSort *sort_model;
120 GtkFileFilter *current_filter;
124 gboolean has_desktop;
130 guint volumes_changed_id;
131 guint bookmarks_changed_id;
133 GtkFilePath *current_volume_path;
134 GtkFilePath *current_folder;
135 GtkFilePath *preview_path;
137 GtkWidget *preview_frame;
139 GtkTreeViewColumn *list_name_column;
140 GtkCellRenderer *list_name_renderer;
145 guint local_only : 1;
146 guint preview_widget_active : 1;
147 guint select_multiple : 1;
148 guint show_hidden : 1;
149 guint list_sort_ascending : 1;
150 guint changing_folder : 1;
161 static guint signals[LAST_SIGNAL] = { 0 };
163 /* Column numbers for the shortcuts tree. Keep these in sync with shortcuts_model_create() */
165 SHORTCUTS_COL_PIXBUF,
168 SHORTCUTS_COL_REMOVABLE,
169 SHORTCUTS_COL_PIXBUF_VISIBLE,
170 SHORTCUTS_COL_NUM_COLUMNS
173 /* Column numbers for the file list */
178 FILE_LIST_COL_NUM_COLUMNS
181 /* Identifiers for target types */
186 /* Target types for DnD in the shortcuts list */
187 static GtkTargetEntry shortcuts_targets[] = {
188 { "text/uri-list", 0, TEXT_URI_LIST }
191 static const int num_shortcuts_targets = sizeof (shortcuts_targets) / sizeof (shortcuts_targets[0]);
193 /* Interesting places in the shortcuts bar */
203 /* Standard icon size */
204 /* FIXME: maybe this should correspond to the font size in the tree views... */
207 static void gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class);
208 static void gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface);
209 static void gtk_file_chooser_default_init (GtkFileChooserDefault *impl);
211 static GObject* gtk_file_chooser_default_constructor (GType type,
212 guint n_construct_properties,
213 GObjectConstructParam *construct_params);
214 static void gtk_file_chooser_default_finalize (GObject *object);
215 static void gtk_file_chooser_default_set_property (GObject *object,
219 static void gtk_file_chooser_default_get_property (GObject *object,
223 static void gtk_file_chooser_default_dispose (GObject *object);
224 static void gtk_file_chooser_default_show_all (GtkWidget *widget);
226 static void gtk_file_chooser_default_set_current_folder (GtkFileChooser *chooser,
227 const GtkFilePath *path);
228 static GtkFilePath * gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser);
229 static void gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
231 static void gtk_file_chooser_default_select_path (GtkFileChooser *chooser,
232 const GtkFilePath *path);
233 static void gtk_file_chooser_default_unselect_path (GtkFileChooser *chooser,
234 const GtkFilePath *path);
235 static void gtk_file_chooser_default_select_all (GtkFileChooser *chooser);
236 static void gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser);
237 static GSList * gtk_file_chooser_default_get_paths (GtkFileChooser *chooser);
238 static GtkFilePath * gtk_file_chooser_default_get_preview_path (GtkFileChooser *chooser);
239 static GtkFileSystem *gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser);
240 static void gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
241 GtkFileFilter *filter);
242 static void gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
243 GtkFileFilter *filter);
244 static GSList * gtk_file_chooser_default_list_filters (GtkFileChooser *chooser);
245 static gboolean gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser *chooser,
246 const GtkFilePath *path,
248 static gboolean gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser *chooser,
249 const GtkFilePath *path,
251 static GSList * gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser);
253 static void location_popup_handler (GtkFileChooserDefault *impl);
254 static void up_folder_handler (GtkFileChooserDefault *impl);
255 static void home_folder_handler (GtkFileChooserDefault *impl);
256 static void update_appearance (GtkFileChooserDefault *impl);
258 static void set_current_filter (GtkFileChooserDefault *impl,
259 GtkFileFilter *filter);
260 static void check_preview_change (GtkFileChooserDefault *impl);
262 static void filter_combo_changed (GtkComboBox *combo_box,
263 GtkFileChooserDefault *impl);
264 static void tree_selection_changed (GtkTreeSelection *tree_selection,
265 GtkFileChooserDefault *impl);
267 static void shortcuts_row_activated_cb (GtkTreeView *tree_view,
269 GtkTreeViewColumn *column,
270 GtkFileChooserDefault *impl);
271 static gboolean shortcuts_select_func (GtkTreeSelection *selection,
274 gboolean path_currently_selected,
277 static void list_selection_changed (GtkTreeSelection *tree_selection,
278 GtkFileChooserDefault *impl);
279 static void list_row_activated (GtkTreeView *tree_view,
281 GtkTreeViewColumn *column,
282 GtkFileChooserDefault *impl);
284 static void path_bar_clicked (GtkPathBar *path_bar,
285 GtkFilePath *file_path,
286 GtkFileChooserDefault *impl);
288 static void add_bookmark_button_clicked_cb (GtkButton *button,
289 GtkFileChooserDefault *impl);
290 static void remove_bookmark_button_clicked_cb (GtkButton *button,
291 GtkFileChooserDefault *impl);
293 static void tree_name_data_func (GtkTreeViewColumn *tree_column,
294 GtkCellRenderer *cell,
295 GtkTreeModel *tree_model,
298 static void list_icon_data_func (GtkTreeViewColumn *tree_column,
299 GtkCellRenderer *cell,
300 GtkTreeModel *tree_model,
303 static void list_name_data_func (GtkTreeViewColumn *tree_column,
304 GtkCellRenderer *cell,
305 GtkTreeModel *tree_model,
309 static void list_size_data_func (GtkTreeViewColumn *tree_column,
310 GtkCellRenderer *cell,
311 GtkTreeModel *tree_model,
315 static void list_mtime_data_func (GtkTreeViewColumn *tree_column,
316 GtkCellRenderer *cell,
317 GtkTreeModel *tree_model,
321 static GObjectClass *parent_class;
324 _gtk_file_chooser_default_get_type (void)
326 static GType file_chooser_default_type = 0;
328 if (!file_chooser_default_type)
330 static const GTypeInfo file_chooser_default_info =
332 sizeof (GtkFileChooserDefaultClass),
333 NULL, /* base_init */
334 NULL, /* base_finalize */
335 (GClassInitFunc) gtk_file_chooser_default_class_init,
336 NULL, /* class_finalize */
337 NULL, /* class_data */
338 sizeof (GtkFileChooserDefault),
340 (GInstanceInitFunc) gtk_file_chooser_default_init,
343 static const GInterfaceInfo file_chooser_info =
345 (GInterfaceInitFunc) gtk_file_chooser_default_iface_init, /* interface_init */
346 NULL, /* interface_finalize */
347 NULL /* interface_data */
350 file_chooser_default_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserDefault",
351 &file_chooser_default_info, 0);
352 g_type_add_interface_static (file_chooser_default_type,
353 GTK_TYPE_FILE_CHOOSER,
357 return file_chooser_default_type;
361 gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
363 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
364 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
365 GtkBindingSet *binding_set;
367 parent_class = g_type_class_peek_parent (class);
369 gobject_class->finalize = gtk_file_chooser_default_finalize;
370 gobject_class->constructor = gtk_file_chooser_default_constructor;
371 gobject_class->set_property = gtk_file_chooser_default_set_property;
372 gobject_class->get_property = gtk_file_chooser_default_get_property;
373 gobject_class->dispose = gtk_file_chooser_default_dispose;
375 widget_class->show_all = gtk_file_chooser_default_show_all;
377 signals[LOCATION_POPUP] =
378 _gtk_binding_signal_new ("location-popup",
379 G_OBJECT_CLASS_TYPE (class),
380 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
381 G_CALLBACK (location_popup_handler),
383 _gtk_marshal_VOID__VOID,
386 _gtk_binding_signal_new ("up-folder",
387 G_OBJECT_CLASS_TYPE (class),
388 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
389 G_CALLBACK (up_folder_handler),
391 _gtk_marshal_VOID__VOID,
393 signals[HOME_FOLDER] =
394 _gtk_binding_signal_new ("home-folder",
395 G_OBJECT_CLASS_TYPE (class),
396 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
397 G_CALLBACK (home_folder_handler),
399 _gtk_marshal_VOID__VOID,
402 binding_set = gtk_binding_set_by_class (class);
404 gtk_binding_entry_add_signal (binding_set,
405 GDK_l, GDK_CONTROL_MASK,
409 gtk_binding_entry_add_signal (binding_set,
410 GDK_Up, GDK_MOD1_MASK,
413 gtk_binding_entry_add_signal (binding_set,
414 GDK_KP_Up, GDK_MOD1_MASK,
418 gtk_binding_entry_add_signal (binding_set,
419 GDK_Home, GDK_MOD1_MASK,
422 gtk_binding_entry_add_signal (binding_set,
423 GDK_KP_Home, GDK_MOD1_MASK,
427 _gtk_file_chooser_install_properties (gobject_class);
431 gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
433 iface->select_path = gtk_file_chooser_default_select_path;
434 iface->unselect_path = gtk_file_chooser_default_unselect_path;
435 iface->select_all = gtk_file_chooser_default_select_all;
436 iface->unselect_all = gtk_file_chooser_default_unselect_all;
437 iface->get_paths = gtk_file_chooser_default_get_paths;
438 iface->get_preview_path = gtk_file_chooser_default_get_preview_path;
439 iface->get_file_system = gtk_file_chooser_default_get_file_system;
440 iface->set_current_folder = gtk_file_chooser_default_set_current_folder;
441 iface->get_current_folder = gtk_file_chooser_default_get_current_folder;
442 iface->set_current_name = gtk_file_chooser_default_set_current_name;
443 iface->add_filter = gtk_file_chooser_default_add_filter;
444 iface->remove_filter = gtk_file_chooser_default_remove_filter;
445 iface->list_filters = gtk_file_chooser_default_list_filters;
446 iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder;
447 iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder;
448 iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders;
452 gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
454 impl->local_only = TRUE;
455 impl->preview_widget_active = TRUE;
456 impl->select_multiple = FALSE;
457 impl->show_hidden = FALSE;
459 gtk_box_set_spacing (GTK_BOX (impl), 12);
463 gtk_file_chooser_default_finalize (GObject *object)
465 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
468 g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
469 impl->volumes_changed_id = 0;
470 g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
471 impl->bookmarks_changed_id = 0;
472 g_object_unref (impl->file_system);
474 for (l = impl->filters; l; l = l->next)
476 GtkFileFilter *filter;
478 filter = GTK_FILE_FILTER (l->data);
479 g_object_unref (filter);
481 g_slist_free (impl->filters);
483 if (impl->current_filter)
484 g_object_unref (impl->current_filter);
486 if (impl->current_volume_path)
487 gtk_file_path_free (impl->current_volume_path);
489 if (impl->current_folder)
490 gtk_file_path_free (impl->current_folder);
492 if (impl->preview_path)
493 gtk_file_path_free (impl->preview_path);
495 G_OBJECT_CLASS (parent_class)->finalize (object);
498 /* Shows an error dialog set as transient for the specified window */
500 error_message_with_parent (GtkWindow *parent,
505 dialog = gtk_message_dialog_new (parent,
506 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
511 gtk_dialog_run (GTK_DIALOG (dialog));
512 gtk_widget_destroy (dialog);
515 /* Shows an error dialog for the file chooser */
517 error_message (GtkFileChooserDefault *impl,
522 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
523 if (!GTK_WIDGET_TOPLEVEL (toplevel))
526 error_message_with_parent (toplevel ? GTK_WINDOW (toplevel) : NULL,
530 /* Shows a simple error dialog relative to a path. Frees the GError as well. */
532 error_dialog (GtkFileChooserDefault *impl,
534 const GtkFilePath *path,
539 text = g_strdup_printf (msg,
540 gtk_file_path_get_string (path),
542 error_message (impl, text);
544 g_error_free (error);
547 /* Displays an error message about not being able to get information for a file.
548 * Frees the GError as well.
551 error_getting_info_dialog (GtkFileChooserDefault *impl,
552 const GtkFilePath *path,
556 _("Could not retrieve information about %s:\n%s"),
560 /* Shows an error dialog about not being able to add a bookmark */
562 error_could_not_add_bookmark_dialog (GtkFileChooserDefault *impl,
563 const GtkFilePath *path,
567 _("Could not add a bookmark for %s:\n%s"),
571 /* Shows an error dialog about not being able to compose a filename */
573 error_building_filename_dialog (GtkFileChooserDefault *impl,
574 const GtkFilePath *base_path,
575 const char *file_part,
580 msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
581 gtk_file_path_get_string (base_path),
584 error_message (impl, msg);
586 g_error_free (error);
590 update_preview_widget_visibility (GtkFileChooserDefault *impl)
592 if (impl->preview_widget_active && impl->preview_widget)
593 gtk_widget_show (impl->preview_frame);
595 gtk_widget_hide (impl->preview_frame);
599 set_preview_widget (GtkFileChooserDefault *impl,
600 GtkWidget *preview_widget)
602 if (preview_widget == impl->preview_widget)
605 if (impl->preview_widget)
606 gtk_container_remove (GTK_CONTAINER (impl->preview_frame),
607 impl->preview_widget);
609 impl->preview_widget = preview_widget;
610 if (impl->preview_widget)
612 gtk_widget_show_all (impl->preview_widget);
613 gtk_container_add (GTK_CONTAINER (impl->preview_frame),
614 impl->preview_widget);
617 update_preview_widget_visibility (impl);
620 /* Clears the selection in the shortcuts tree */
622 shortcuts_unselect_all (GtkFileChooserDefault *impl)
624 GtkTreeSelection *selection;
626 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
627 gtk_tree_selection_unselect_all (selection);
630 /* Convenience function to get the display name and icon info for a path */
632 get_file_info (GtkFileSystem *file_system, const GtkFilePath *path, GError **error)
634 GtkFilePath *parent_path;
635 GtkFileFolder *parent_folder;
638 if (!gtk_file_system_get_parent (file_system, path, &parent_path, error))
641 parent_folder = gtk_file_system_get_folder (file_system, parent_path,
642 GTK_FILE_INFO_DISPLAY_NAME
646 | GTK_FILE_INFO_IS_FOLDER,
648 gtk_file_path_free (parent_path);
653 info = gtk_file_folder_get_info (parent_folder, path, error);
654 g_object_unref (parent_folder);
659 /* Inserts a path in the shortcuts tree, making a copy of it; alternatively,
660 * inserts a volume. A position of -1 indicates the end of the tree.
663 shortcuts_insert_path (GtkFileChooserDefault *impl,
666 GtkFileSystemVolume *volume,
667 const GtkFilePath *path,
680 label_copy = gtk_file_system_volume_get_display_name (impl->file_system, volume);
681 pixbuf = gtk_file_system_volume_render_icon (impl->file_system,
691 info = get_file_info (impl->file_system, path, error);
695 data = gtk_file_path_copy (path);
698 label_copy = g_strdup (label);
700 label_copy = g_strdup (gtk_file_info_get_display_name (info));
702 pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl), ICON_SIZE, NULL);
704 gtk_file_info_free (info);
708 gtk_list_store_append (impl->shortcuts_model, &iter);
710 gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
712 gtk_list_store_set (impl->shortcuts_model, &iter,
713 SHORTCUTS_COL_PIXBUF, pixbuf,
714 SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
715 SHORTCUTS_COL_NAME, label_copy,
716 SHORTCUTS_COL_PATH, data,
717 SHORTCUTS_COL_REMOVABLE, removable,
723 g_object_unref (pixbuf);
728 /* Appends an item for the user's home directory to the shortcuts model */
730 shortcuts_append_home (GtkFileChooserDefault *impl)
733 GtkFilePath *home_path;
736 home = g_get_home_dir ();
737 home_path = gtk_file_system_filename_to_path (impl->file_system, home);
740 impl->has_home = shortcuts_insert_path (impl, -1, FALSE, NULL, home_path, _("Home"), FALSE, &error);
742 error_getting_info_dialog (impl, home_path, error);
744 gtk_file_path_free (home_path);
747 /* Appends the ~/Desktop directory to the shortcuts model */
749 shortcuts_append_desktop (GtkFileChooserDefault *impl)
754 name = g_build_filename (g_get_home_dir (), "Desktop", NULL);
755 path = gtk_file_system_filename_to_path (impl->file_system, name);
758 impl->has_desktop = shortcuts_insert_path (impl, -1, FALSE, NULL, path, _("Desktop"), FALSE, NULL);
759 /* We do not actually pop up an error dialog if there is no desktop directory
760 * because some people may really not want to have one.
763 gtk_file_path_free (path);
766 /* Appends a list of GtkFilePath to the shortcuts model; returns how many were inserted */
768 shortcuts_append_paths (GtkFileChooserDefault *impl,
775 for (; paths; paths = paths->next)
783 /* NULL GError, but we don't really want to show error boxes here */
785 if (shortcuts_insert_path (impl, -1, FALSE, NULL, path, NULL, TRUE, NULL))
792 /* Returns the index for the corresponding item in the shortcuts bar */
794 shortcuts_get_index (GtkFileChooserDefault *impl,
795 ShortcutsIndex where)
801 if (where == SHORTCUTS_HOME)
804 n += impl->has_home ? 1 : 0;
806 if (where == SHORTCUTS_DESKTOP)
809 n += impl->has_desktop ? 1 : 0;
811 if (where == SHORTCUTS_VOLUMES)
814 n += impl->num_volumes;
816 if (where == SHORTCUTS_SHORTCUTS)
819 n += impl->num_shortcuts;
821 if (where == SHORTCUTS_SEPARATOR)
824 /* If there are no bookmarks there won't be a separator */
825 n += impl->num_shortcuts > 0 ? 1 : 0;
827 if (where == SHORTCUTS_BOOKMARKS)
830 g_assert_not_reached ();
837 typedef void (* RemoveFunc) (GtkFileChooserDefault *impl, gpointer data);
839 /* Removes the specified number of rows from the shortcuts list */
841 shortcuts_remove_rows (GtkFileChooserDefault *impl,
844 RemoveFunc remove_fn)
848 path = gtk_tree_path_new_from_indices (start_row, -1);
850 for (; n_rows; n_rows--)
855 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
856 g_assert_not_reached ();
860 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &data, -1);
861 (* remove_fn) (impl, data);
864 gtk_list_store_remove (impl->shortcuts_model, &iter);
867 gtk_tree_path_free (path);
870 /* Used from shortcuts_remove_rows() */
872 volume_remove_cb (GtkFileChooserDefault *impl, gpointer data)
874 GtkFileSystemVolume *volume;
877 gtk_file_system_volume_free (impl->file_system, volume);
880 /* Adds all the file system volumes to the shortcuts model */
882 shortcuts_add_volumes (GtkFileChooserDefault *impl)
888 start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
889 shortcuts_remove_rows (impl, start_row, impl->num_volumes, volume_remove_cb);
890 impl->num_volumes = 0;
892 list = gtk_file_system_list_volumes (impl->file_system);
896 for (l = list; l; l = l->next)
898 GtkFileSystemVolume *volume;
902 shortcuts_insert_path (impl, start_row + n, TRUE, volume, NULL, NULL, FALSE, NULL);
906 impl->num_volumes = n;
911 /* Used from shortcuts_remove_rows() */
913 remove_bookmark_cb (GtkFileChooserDefault *impl, gpointer data)
918 gtk_file_path_free (path);
921 /* Inserts the bookmarks separator node */
923 shortcuts_insert_separator (GtkFileChooserDefault *impl)
927 gtk_list_store_insert (impl->shortcuts_model, &iter,
928 shortcuts_get_index (impl, SHORTCUTS_SEPARATOR));
929 gtk_list_store_set (impl->shortcuts_model, &iter,
930 SHORTCUTS_COL_PIXBUF, NULL,
931 SHORTCUTS_COL_PIXBUF_VISIBLE, FALSE,
932 SHORTCUTS_COL_NAME, NULL,
933 SHORTCUTS_COL_PATH, NULL,
937 /* Creates the GtkTreeStore used as the shortcuts model */
938 /* Updates the list of bookmarks */
940 shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
944 if (impl->num_bookmarks > 0)
946 shortcuts_remove_rows (impl,
947 shortcuts_get_index (impl, SHORTCUTS_SEPARATOR),
948 impl->num_bookmarks + 1,
953 bookmarks = gtk_file_system_list_bookmarks (impl->file_system);
954 impl->num_bookmarks = shortcuts_append_paths (impl, bookmarks);
955 gtk_file_paths_free (bookmarks);
957 if (impl->num_bookmarks > 0)
959 shortcuts_insert_separator (impl);
964 shortcuts_model_create (GtkFileChooserDefault *impl)
966 if (impl->shortcuts_model)
967 g_object_unref (impl->shortcuts_model);
969 /* Keep this order in sync with the SHORCUTS_COL_* enum values */
970 impl->shortcuts_model = gtk_list_store_new (SHORTCUTS_COL_NUM_COLUMNS,
971 GDK_TYPE_PIXBUF, /* pixbuf */
972 G_TYPE_STRING, /* name */
973 G_TYPE_POINTER, /* path or volume */
974 G_TYPE_BOOLEAN, /* removable */
975 G_TYPE_BOOLEAN); /* pixbuf cell visibility */
977 if (impl->file_system)
979 shortcuts_append_home (impl);
980 shortcuts_append_desktop (impl);
981 shortcuts_add_volumes (impl);
982 shortcuts_add_bookmarks (impl);
985 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), GTK_TREE_MODEL (impl->shortcuts_model));
988 /* Callback used when the "New Folder" toolbar button is clicked */
990 new_folder_button_clicked (GtkButton *button,
991 GtkFileChooserDefault *impl)
996 /* FIXME: this doesn't work for folder mode, just for file mode */
998 _gtk_file_system_model_add_editable (impl->browse_files_model, &iter);
999 g_object_set (impl->list_name_renderer, "editable", TRUE, NULL);
1001 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->browse_files_model), &iter);
1002 gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view),
1004 impl->list_name_column,
1008 /* Callback used from the text cell renderer when the new folder is named */
1010 renderer_edited_cb (GtkCellRendererText *cell_renderer_text,
1012 const gchar *new_text,
1013 GtkFileChooserDefault *impl)
1016 GtkFilePath *file_path;
1018 _gtk_file_system_model_remove_editable (impl->browse_files_model);
1019 g_object_set (impl->list_name_renderer, "editable", FALSE, NULL);
1022 file_path = gtk_file_system_make_path (impl->file_system, impl->current_folder, new_text, &error);
1025 error_building_filename_dialog (impl, impl->current_folder, new_text, error);
1030 if (!gtk_file_system_create_folder (impl->file_system, file_path, &error))
1032 _("Could not create folder %s:\n%s"),
1035 gtk_file_path_free (file_path);
1037 /* FIXME: scroll to the new folder and select it */
1040 /* Callback used from the text cell renderer when the new folder edition gets
1044 renderer_editing_canceled_cb (GtkCellRendererText *cell_renderer_text,
1045 GtkFileChooserDefault *impl)
1047 _gtk_file_system_model_remove_editable (impl->browse_files_model);
1048 g_object_set (impl->list_name_renderer, "editable", FALSE, NULL);
1051 /* Creates the widgets for the filter combo box */
1053 filter_create (GtkFileChooserDefault *impl)
1055 impl->filter_combo = gtk_combo_box_new_text ();
1056 g_signal_connect (impl->filter_combo, "changed",
1057 G_CALLBACK (filter_combo_changed), impl);
1059 return impl->filter_combo;
1063 button_new (GtkFileChooserDefault *impl,
1065 const char *stock_id,
1075 button = gtk_button_new ();
1076 hbox = gtk_hbox_new (FALSE, 2);
1077 align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1079 gtk_container_add (GTK_CONTAINER (button), align);
1080 gtk_container_add (GTK_CONTAINER (align), hbox);
1081 widget = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
1083 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1085 widget = gtk_label_new_with_mnemonic (text);
1086 gtk_label_set_mnemonic_widget (GTK_LABEL (widget), GTK_WIDGET (button));
1087 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1089 gtk_widget_set_sensitive (button, sensitive);
1090 g_signal_connect (button, "clicked", callback, impl);
1092 gtk_widget_show_all (align);
1095 gtk_widget_show (button);
1100 /* Creates the widgets for the folder tree */
1102 create_folder_tree (GtkFileChooserDefault *impl)
1104 GtkTreeSelection *selection;
1106 /* Scrolled window */
1108 impl->browse_directories_swin = gtk_scrolled_window_new (NULL, NULL);
1109 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->browse_directories_swin),
1110 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1111 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (impl->browse_directories_swin),
1115 impl->browse_directories_tree_view = gtk_tree_view_new ();
1116 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->browse_directories_tree_view), FALSE);
1118 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_directories_tree_view));
1119 gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_directories_tree_view),
1122 num_shortcuts_targets,
1125 g_signal_connect (selection, "changed",
1126 G_CALLBACK (tree_selection_changed), impl);
1128 gtk_container_add (GTK_CONTAINER (impl->browse_directories_swin), impl->browse_directories_tree_view);
1129 gtk_widget_show (impl->browse_directories_tree_view);
1133 gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (impl->browse_directories_tree_view), 0,
1135 gtk_cell_renderer_text_new (),
1136 tree_name_data_func, impl, NULL);
1137 gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_directories_tree_view),
1138 GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
1140 return impl->browse_directories_swin;
1143 /* Returns whether a path is already present in the shortcuts list */
1145 shortcut_exists (GtkFileChooserDefault *impl,
1146 const GtkFilePath *path)
1155 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
1159 separator_idx = shortcuts_get_index (impl, SHORTCUTS_SEPARATOR);
1160 volumes_idx = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
1168 if (i == separator_idx)
1171 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &data, -1);
1173 if (i >= volumes_idx && i < volumes_idx + impl->num_volumes)
1175 GtkFileSystemVolume *volume;
1176 GtkFilePath *base_path;
1179 base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
1181 exists = strcmp (gtk_file_path_get_string (path),
1182 gtk_file_path_get_string (base_path)) == 0;
1190 GtkFilePath *model_path;
1194 if (model_path && gtk_file_path_compare (model_path, path) == 0)
1201 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter));
1207 /* Tries to add a bookmark from a path name */
1209 shortcuts_add_bookmark_from_path (GtkFileChooserDefault *impl,
1210 const GtkFilePath *path)
1215 if (shortcut_exists (impl, path))
1219 info = get_file_info (impl->file_system, path, &error);
1222 error_getting_info_dialog (impl, path, error);
1223 else if (!gtk_file_info_get_is_folder (info))
1227 msg = g_strdup_printf (_("Could not add bookmark for %s because it is not a folder."),
1228 gtk_file_path_get_string (path));
1229 error_message (impl, msg);
1235 if (!gtk_file_system_add_bookmark (impl->file_system, path, &error))
1236 error_could_not_add_bookmark_dialog (impl, path, error);
1241 add_bookmark_foreach_cb (GtkTreeModel *model,
1246 GtkFileChooserDefault *impl;
1247 GtkFileSystemModel *fs_model;
1248 GtkTreeIter child_iter;
1249 const GtkFilePath *file_path;
1251 impl = GTK_FILE_CHOOSER_DEFAULT (data);
1253 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
1254 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
1256 fs_model = impl->browse_directories_model;
1261 fs_model = impl->browse_files_model;
1262 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter);
1265 file_path = _gtk_file_system_model_get_path (GTK_FILE_SYSTEM_MODEL (fs_model), &child_iter);
1266 shortcuts_add_bookmark_from_path (impl, file_path);
1269 /* Callback used when the "Add bookmark" button is clicked */
1271 add_bookmark_button_clicked_cb (GtkButton *button,
1272 GtkFileChooserDefault *impl)
1274 GtkWidget *tree_view;
1275 GtkTreeSelection *selection;
1277 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
1278 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
1279 tree_view = impl->browse_directories_tree_view;
1281 tree_view = impl->browse_files_tree_view;
1283 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
1284 if (gtk_tree_selection_count_selected_rows (selection) == 0)
1285 shortcuts_add_bookmark_from_path (impl, impl->current_folder);
1287 gtk_tree_selection_selected_foreach (selection,
1288 add_bookmark_foreach_cb,
1292 /* Callback used when the "Remove bookmark" button is clicked */
1294 remove_bookmark_button_clicked_cb (GtkButton *button,
1295 GtkFileChooserDefault *impl)
1297 GtkTreeSelection *selection;
1303 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1306 if (gtk_tree_selection_get_selected (selection, NULL, &iter))
1308 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
1309 SHORTCUTS_COL_PATH, &path,
1310 SHORTCUTS_COL_REMOVABLE, &removable, -1);
1313 g_assert_not_reached ();
1318 if (!gtk_file_system_remove_bookmark (impl->file_system, path, &error))
1320 _("Could not remove bookmark for %s:\n%s"),
1326 struct is_folders_foreach_closure {
1327 GtkFileChooserDefault *impl;
1328 gboolean all_folders;
1331 /* Used from gtk_tree_selection_selected_foreach() */
1333 is_folders_foreach_cb (GtkTreeModel *model,
1338 struct is_folders_foreach_closure *closure;
1339 GtkTreeIter child_iter;
1340 const GtkFileInfo *info;
1344 gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
1346 info = _gtk_file_system_model_get_info (closure->impl->browse_files_model, &child_iter);
1347 closure->all_folders &= gtk_file_info_get_is_folder (info);
1350 /* Returns whether the selected items in the file list are all folders */
1352 selection_is_folders (GtkFileChooserDefault *impl)
1354 struct is_folders_foreach_closure closure;
1355 GtkTreeSelection *selection;
1357 closure.impl = impl;
1358 closure.all_folders = TRUE;
1360 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
1361 gtk_tree_selection_selected_foreach (selection,
1362 is_folders_foreach_cb,
1365 return closure.all_folders;
1368 /* Sensitize the "add bookmark" button if all the selected items are folders, or
1369 * if there are no selected items *and* the current folder is not in the
1370 * bookmarks list. De-sensitize the button otherwise.
1373 bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl)
1375 GtkWidget *tree_view;
1376 GtkTreeSelection *selection;
1379 /* Check selection */
1381 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
1382 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
1383 tree_view = impl->browse_directories_tree_view;
1385 tree_view = impl->browse_files_tree_view;
1387 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
1389 if (gtk_tree_selection_count_selected_rows (selection) == 0)
1390 active = !shortcut_exists (impl, impl->current_folder);
1392 active = (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
1393 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER ||
1394 selection_is_folders (impl));
1396 gtk_widget_set_sensitive (impl->browse_shortcuts_add_button, active);
1399 /* Sets the sensitivity of the "remove bookmark" button depending on whether a
1400 * bookmark row is selected in the shortcuts tree.
1403 bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl)
1405 GtkTreeSelection *selection;
1407 gboolean removable = FALSE;
1409 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1411 if (gtk_tree_selection_get_selected (selection, NULL, &iter))
1412 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
1413 SHORTCUTS_COL_REMOVABLE, &removable,
1416 gtk_widget_set_sensitive (impl->browse_shortcuts_remove_button, removable);
1419 /* Converts raw selection data from text/uri-list to a list of strings */
1421 split_uris (const char *data)
1424 const char *p, *start;
1430 for (p = start; *p != 0; p++)
1431 if (*p == '\r' && *(p + 1) == '\n')
1435 name = g_strndup (start, p - start);
1436 uris = g_slist_prepend (uris, name);
1442 uris = g_slist_reverse (uris);
1446 /* Callback used when we get the drag data for the bookmarks list. We add the
1447 * received URIs as bookmarks if they are folders.
1450 shortcuts_drag_data_received_cb (GtkWidget *widget,
1451 GdkDragContext *context,
1454 GtkSelectionData *selection_data,
1459 GtkFileChooserDefault *impl;
1462 impl = GTK_FILE_CHOOSER_DEFAULT (data);
1464 uris = split_uris (selection_data->data);
1466 for (l = uris; l; l = l->next)
1472 path = gtk_file_system_uri_to_path (impl->file_system, uri);
1476 shortcuts_add_bookmark_from_path (impl, path);
1477 gtk_file_path_free (path);
1483 msg = g_strdup_printf (_("Could not add a bookmark for %s because it is an invalid path name."),
1485 error_message (impl, msg);
1492 g_slist_free (uris);
1495 /* Callback used when the selection in the shortcuts tree changes */
1497 shortcuts_selection_changed_cb (GtkTreeSelection *selection,
1498 GtkFileChooserDefault *impl)
1500 bookmarks_check_remove_sensitivity (impl);
1503 /* Creates the widgets for the shortcuts and bookmarks tree */
1505 shortcuts_list_create (GtkFileChooserDefault *impl)
1507 GtkTreeSelection *selection;
1508 GtkTreeViewColumn *column;
1509 GtkCellRenderer *renderer;
1511 /* Scrolled window */
1513 impl->browse_shortcuts_swin = gtk_scrolled_window_new (NULL, NULL);
1514 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->browse_shortcuts_swin),
1515 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1516 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (impl->browse_shortcuts_swin),
1518 gtk_widget_show (impl->browse_shortcuts_swin);
1522 impl->browse_shortcuts_tree_view = gtk_tree_view_new ();
1523 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), FALSE);
1525 gtk_drag_dest_set (impl->browse_shortcuts_tree_view,
1526 GTK_DEST_DEFAULT_ALL,
1528 num_shortcuts_targets,
1531 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1532 gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
1533 gtk_tree_selection_set_select_function (selection,
1534 shortcuts_select_func,
1537 g_signal_connect (selection, "changed",
1538 G_CALLBACK (shortcuts_selection_changed_cb), impl);
1540 g_signal_connect (impl->browse_shortcuts_tree_view, "row-activated",
1541 G_CALLBACK (shortcuts_row_activated_cb), impl);
1543 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-received",
1544 G_CALLBACK (shortcuts_drag_data_received_cb), impl);
1546 gtk_container_add (GTK_CONTAINER (impl->browse_shortcuts_swin), impl->browse_shortcuts_tree_view);
1547 gtk_widget_show (impl->browse_shortcuts_tree_view);
1551 shortcuts_model_create (impl);
1555 column = gtk_tree_view_column_new ();
1556 gtk_tree_view_column_set_title (column, _("Folder"));
1558 renderer = gtk_cell_renderer_pixbuf_new ();
1559 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1560 gtk_tree_view_column_set_attributes (column, renderer,
1561 "pixbuf", SHORTCUTS_COL_PIXBUF,
1562 "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
1565 renderer = _gtk_cell_renderer_sep_text_new ();
1566 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1567 gtk_tree_view_column_set_attributes (column, renderer,
1568 "text", SHORTCUTS_COL_NAME,
1571 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), column);
1573 return impl->browse_shortcuts_swin;
1576 /* Creates the widgets for the shortcuts/bookmarks pane */
1578 shortcuts_pane_create (GtkFileChooserDefault *impl,
1579 GtkSizeGroup *size_group)
1585 vbox = gtk_vbox_new (FALSE, 6);
1586 gtk_widget_show (vbox);
1588 /* Shortcuts tree */
1590 widget = shortcuts_list_create (impl);
1591 gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
1593 /* Box for buttons */
1595 hbox = gtk_hbox_new (TRUE, 6);
1596 gtk_size_group_add_widget (size_group, hbox);
1597 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1598 gtk_widget_show (hbox);
1600 /* Add bookmark button */
1602 impl->browse_shortcuts_add_button = button_new (impl,
1607 G_CALLBACK (add_bookmark_button_clicked_cb));
1608 gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_add_button, TRUE, TRUE, 0);
1610 /* Remove bookmark button */
1612 impl->browse_shortcuts_remove_button = button_new (impl,
1617 G_CALLBACK (remove_bookmark_button_clicked_cb));
1618 gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_remove_button, TRUE, TRUE, 0);
1623 /* Creates the widgets for the file list */
1625 create_file_list (GtkFileChooserDefault *impl)
1627 GtkTreeSelection *selection;
1628 GtkTreeViewColumn *column;
1629 GtkCellRenderer *renderer;
1631 /* Scrolled window */
1633 impl->browse_files_swin = gtk_scrolled_window_new (NULL, NULL);
1634 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->browse_files_swin),
1635 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1636 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (impl->browse_files_swin),
1639 /* Tree/list view */
1641 impl->browse_files_tree_view = gtk_tree_view_new ();
1642 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->browse_files_tree_view), TRUE);
1643 gtk_container_add (GTK_CONTAINER (impl->browse_files_swin), impl->browse_files_tree_view);
1644 g_signal_connect (impl->browse_files_tree_view, "row_activated",
1645 G_CALLBACK (list_row_activated), impl);
1646 gtk_widget_show (impl->browse_files_tree_view);
1648 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
1649 gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_files_tree_view),
1652 num_shortcuts_targets,
1655 g_signal_connect (selection, "changed",
1656 G_CALLBACK (list_selection_changed), impl);
1658 /* Filename column */
1660 impl->list_name_column = gtk_tree_view_column_new ();
1661 gtk_tree_view_column_set_expand (impl->list_name_column, TRUE);
1662 gtk_tree_view_column_set_title (impl->list_name_column, _("File name"));
1663 gtk_tree_view_column_set_sort_column_id (impl->list_name_column, FILE_LIST_COL_NAME);
1665 renderer = gtk_cell_renderer_pixbuf_new ();
1666 gtk_tree_view_column_pack_start (impl->list_name_column, renderer, FALSE);
1667 gtk_tree_view_column_set_cell_data_func (impl->list_name_column, renderer,
1668 list_icon_data_func, impl, NULL);
1670 impl->list_name_renderer = gtk_cell_renderer_text_new ();
1671 g_signal_connect (impl->list_name_renderer, "edited",
1672 G_CALLBACK (renderer_edited_cb), impl);
1673 g_signal_connect (impl->list_name_renderer, "editing-canceled",
1674 G_CALLBACK (renderer_editing_canceled_cb), impl);
1675 gtk_tree_view_column_pack_start (impl->list_name_column, impl->list_name_renderer, TRUE);
1676 gtk_tree_view_column_set_cell_data_func (impl->list_name_column, impl->list_name_renderer,
1677 list_name_data_func, impl, NULL);
1679 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), impl->list_name_column);
1683 column = gtk_tree_view_column_new ();
1684 gtk_tree_view_column_set_title (column, _("Size"));
1686 renderer = gtk_cell_renderer_text_new ();
1687 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1688 gtk_tree_view_column_set_cell_data_func (column, renderer,
1689 list_size_data_func, impl, NULL);
1690 gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_SIZE);
1691 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
1693 /* Modification time column */
1695 column = gtk_tree_view_column_new ();
1696 gtk_tree_view_column_set_title (column, _("Modified"));
1698 renderer = gtk_cell_renderer_text_new ();
1699 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1700 gtk_tree_view_column_set_cell_data_func (column, renderer,
1701 list_mtime_data_func, impl, NULL);
1702 gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_MTIME);
1703 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
1705 return impl->browse_files_swin;
1709 create_filename_entry_and_filter_combo (GtkFileChooserDefault *impl)
1714 hbox = gtk_hbox_new (FALSE, 12);
1715 gtk_widget_show (hbox);
1719 widget = filter_create (impl);
1720 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1725 /* Creates the widgets for the files/folders pane */
1727 file_pane_create (GtkFileChooserDefault *impl,
1728 GtkSizeGroup *size_group)
1734 vbox = gtk_vbox_new (FALSE, 6);
1735 gtk_widget_show (vbox);
1737 /* The path bar and 'Create Folder' button */
1738 hbox = gtk_hbox_new (FALSE, 12);
1739 gtk_widget_show (hbox);
1740 impl->browse_path_bar = g_object_new (GTK_TYPE_PATH_BAR, NULL);
1741 g_signal_connect (impl->browse_path_bar, "path_clicked", G_CALLBACK (path_bar_clicked), impl);
1742 gtk_widget_show_all (impl->browse_path_bar);
1743 gtk_box_pack_start (GTK_BOX (hbox), impl->browse_path_bar, TRUE, TRUE, 0);
1746 impl->browse_new_folder_button = gtk_button_new_with_mnemonic (_("Create _Folder"));
1747 g_signal_connect (impl->browse_new_folder_button, "clicked",
1748 G_CALLBACK (new_folder_button_clicked), impl);
1749 gtk_box_pack_end (GTK_BOX (hbox), impl->browse_new_folder_button, FALSE, FALSE, 0);
1750 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1752 /* Box for lists and preview */
1754 hbox = gtk_hbox_new (FALSE, 12);
1755 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
1756 gtk_widget_show (hbox);
1760 widget = create_folder_tree (impl);
1761 gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
1765 widget = create_file_list (impl);
1766 gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
1770 impl->preview_frame = gtk_frame_new (_("Preview"));
1771 gtk_box_pack_start (GTK_BOX (hbox), impl->preview_frame, FALSE, FALSE, 0);
1772 /* Don't show preview frame initially */
1774 /* Filename entry and filter combo */
1775 hbox = gtk_hbox_new (FALSE, 0);
1776 gtk_size_group_add_widget (size_group, hbox);
1777 widget = create_filename_entry_and_filter_combo (impl);
1778 gtk_box_pack_end (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1779 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1780 gtk_widget_show (hbox);
1784 /* Callback used when the "Browse for more folders" expander is toggled */
1786 expander_changed_cb (GtkExpander *expander,
1788 GtkFileChooserDefault *impl)
1792 active = gtk_expander_get_expanded (expander);
1793 update_appearance (impl);
1796 /* Creates the widgets specific to Save mode */
1798 save_widgets_create (GtkFileChooserDefault *impl)
1803 GtkWidget *alignment;
1805 vbox = gtk_vbox_new (FALSE, 12);
1807 table = gtk_table_new (2, 2, FALSE);
1808 gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
1809 gtk_widget_show (table);
1810 gtk_table_set_row_spacings (GTK_TABLE (table), 12);
1811 gtk_table_set_col_spacings (GTK_TABLE (table), 12);
1815 widget = gtk_label_new_with_mnemonic (_("_Name:"));
1816 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
1817 gtk_table_attach (GTK_TABLE (table), widget,
1821 gtk_widget_show (widget);
1823 impl->save_file_name_entry = gtk_entry_new ();
1824 gtk_entry_set_width_chars (GTK_ENTRY (impl->save_file_name_entry), 25);
1825 gtk_entry_set_activates_default (GTK_ENTRY (impl->save_file_name_entry), TRUE);
1826 gtk_table_attach (GTK_TABLE (table), impl->save_file_name_entry,
1828 GTK_EXPAND | GTK_FILL, 0,
1830 gtk_widget_show (impl->save_file_name_entry);
1831 gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->save_file_name_entry);
1834 impl->save_folder_label = gtk_label_new_with_mnemonic (_("Save in _Folder:"));
1835 gtk_misc_set_alignment (GTK_MISC (impl->save_folder_label), 0.0, 0.5);
1836 gtk_table_attach (GTK_TABLE (table), impl->save_folder_label,
1840 gtk_widget_show (impl->save_folder_label);
1842 /* FIXME: create the combo */
1845 impl->save_extra_align = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
1846 gtk_box_pack_start (GTK_BOX (vbox), impl->save_extra_align, FALSE, FALSE, 0);
1849 alignment = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
1850 gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
1852 impl->save_expander = gtk_expander_new_with_mnemonic (_("_Browse for other folders"));
1853 gtk_container_add (GTK_CONTAINER (alignment), impl->save_expander);
1854 g_signal_connect (impl->save_expander, "notify::expanded",
1855 G_CALLBACK (expander_changed_cb),
1857 gtk_widget_show_all (alignment);
1862 /* Creates the main hpaned with the widgets shared by Open and Save mode */
1864 browse_widgets_create (GtkFileChooserDefault *impl)
1869 GtkSizeGroup *size_group;
1871 /* size group is used by the [+][-] buttons and the filter combo */
1872 size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
1873 vbox = gtk_vbox_new (FALSE, 12);
1876 hpaned = gtk_hpaned_new ();
1877 gtk_widget_show (hpaned);
1878 gtk_paned_set_position (GTK_PANED (hpaned), 200); /* FIXME: this sucks */
1879 gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
1881 widget = shortcuts_pane_create (impl, size_group);
1882 gtk_paned_pack1 (GTK_PANED (hpaned), widget, FALSE, FALSE);
1883 widget = file_pane_create (impl, size_group);
1884 gtk_paned_pack2 (GTK_PANED (hpaned), widget, TRUE, FALSE);
1886 /* Alignment to hold custom widget */
1887 impl->browse_extra_align = gtk_alignment_new (0.0, .5, 1.0, 1.0);
1888 gtk_box_pack_start (GTK_BOX (vbox), impl->browse_extra_align, FALSE, FALSE, 0);
1894 gtk_file_chooser_default_constructor (GType type,
1895 guint n_construct_properties,
1896 GObjectConstructParam *construct_params)
1898 GtkFileChooserDefault *impl;
1901 object = parent_class->constructor (type,
1902 n_construct_properties,
1904 impl = GTK_FILE_CHOOSER_DEFAULT (object);
1906 g_assert (impl->file_system);
1908 gtk_widget_push_composite_child ();
1910 /* Widgets for Save mode */
1911 impl->save_widgets = save_widgets_create (impl);
1912 gtk_box_pack_start (GTK_BOX (impl), impl->save_widgets, FALSE, FALSE, 0);
1914 /* The browse widgets */
1915 impl->browse_widgets = browse_widgets_create (impl);
1916 gtk_box_pack_start (GTK_BOX (impl), impl->browse_widgets, TRUE, TRUE, 0);
1918 gtk_widget_pop_composite_child ();
1919 update_appearance (impl);
1924 /* Sets the extra_widget by packing it in the appropriate place */
1926 set_extra_widget (GtkFileChooserDefault *impl,
1927 GtkWidget *extra_widget)
1931 g_object_ref (extra_widget);
1932 /* FIXME: is this right ? */
1933 gtk_widget_show (extra_widget);
1936 if (impl->extra_widget)
1937 g_object_unref (impl->extra_widget);
1939 impl->extra_widget = extra_widget;
1943 volumes_changed_cb (GtkFileSystem *file_system,
1944 GtkFileChooserDefault *impl)
1946 shortcuts_add_volumes (impl);
1949 /* Callback used when the set of bookmarks changes in the file system */
1951 bookmarks_changed_cb (GtkFileSystem *file_system,
1952 GtkFileChooserDefault *impl)
1954 shortcuts_add_bookmarks (impl);
1956 bookmarks_check_add_sensitivity (impl);
1957 bookmarks_check_remove_sensitivity (impl);
1960 /* Sets the file chooser to multiple selection mode */
1962 set_select_multiple (GtkFileChooserDefault *impl,
1963 gboolean select_multiple,
1964 gboolean property_notify)
1966 GtkTreeSelection *selection;
1967 GtkSelectionMode mode;
1969 if (select_multiple == impl->select_multiple)
1972 mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE;
1974 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_directories_tree_view));
1975 gtk_tree_selection_set_mode (selection, mode);
1977 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
1978 gtk_tree_selection_set_mode (selection, mode);
1980 impl->select_multiple = select_multiple;
1981 g_object_notify (G_OBJECT (impl), "select-multiple");
1983 /* FIXME #132255: See note in check_preview_change() */
1984 check_preview_change (impl);
1988 set_file_system_backend (GtkFileChooserDefault *impl,
1989 const char *backend)
1991 if (impl->file_system)
1993 g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
1994 impl->volumes_changed_id = 0;
1995 g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
1996 impl->bookmarks_changed_id = 0;
1997 g_object_unref (impl->file_system);
2000 impl->file_system = NULL;
2002 impl->file_system = _gtk_file_system_create (backend);
2004 if (!impl->file_system)
2006 #if defined (G_OS_UNIX)
2007 impl->file_system = gtk_file_system_unix_new ();
2008 #elif defined (G_OS_WIN32)
2009 impl->file_system = gtk_file_system_win32_new ();
2011 #error "No default filesystem implementation on the platform"
2015 if (impl->file_system)
2017 impl->volumes_changed_id = g_signal_connect (impl->file_system, "volumes-changed",
2018 G_CALLBACK (volumes_changed_cb),
2020 impl->bookmarks_changed_id = g_signal_connect (impl->file_system, "bookmarks-changed",
2021 G_CALLBACK (bookmarks_changed_cb),
2026 /* This function is basically a do_all function.
2028 * It sets the visibility on all the widgets based on the current state, and
2029 * moves the custom_widget if needed.
2032 update_appearance (GtkFileChooserDefault *impl)
2036 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
2037 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
2039 gtk_widget_show (impl->save_widgets);
2041 if (gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
2043 gtk_widget_set_sensitive (impl->save_folder_label, FALSE);
2044 /*gtk_widget_set_sensitive (impl->save_folder_combo, FALSE);*/
2045 gtk_widget_show (impl->browse_widgets);
2049 gtk_widget_set_sensitive (impl->save_folder_label, TRUE);
2050 /*gtk_widget_set_sensitive (impl->save_folder_combo, TRUE);*/
2051 gtk_widget_hide (impl->browse_widgets);
2054 gtk_widget_show (impl->browse_new_folder_button);
2056 if (impl->select_multiple)
2058 g_warning ("Save mode cannot be set in conjunction with multiple selection mode. "
2059 "Re-setting to single selection mode.");
2060 set_select_multiple (impl, FALSE, TRUE);
2063 else if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
2064 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
2066 gtk_widget_hide (impl->save_widgets);
2067 gtk_widget_show (impl->browse_widgets);
2070 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
2071 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
2073 gtk_widget_show (impl->browse_new_folder_button);
2074 gtk_widget_hide (impl->browse_files_swin);
2075 gtk_widget_show (impl->browse_directories_swin);
2079 gtk_widget_hide (impl->browse_new_folder_button);
2080 gtk_widget_hide (impl->browse_directories_swin);
2081 gtk_widget_show (impl->browse_files_swin);
2084 if (impl->extra_widget)
2087 GtkWidget *unused_align;
2089 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
2091 align = impl->save_extra_align;
2092 unused_align = impl->browse_extra_align;
2096 align = impl->browse_extra_align;
2097 unused_align = impl->save_extra_align;
2100 /* We own a ref on extra_widget, so it's safe to do this */
2101 child = GTK_BIN (unused_align)->child;
2103 gtk_container_remove (GTK_CONTAINER (unused_align), child);
2105 child = GTK_BIN (align)->child;
2106 if (child && child != impl->extra_widget)
2108 gtk_container_remove (GTK_CONTAINER (align), child);
2109 gtk_container_add (GTK_CONTAINER (align), impl->extra_widget);
2111 else if (child == NULL)
2113 gtk_container_add (GTK_CONTAINER (align), impl->extra_widget);
2116 gtk_widget_show (align);
2117 gtk_widget_hide (unused_align);
2121 child = GTK_BIN (impl->browse_extra_align)->child;
2123 gtk_container_remove (GTK_CONTAINER (impl->browse_extra_align), child);
2125 child = GTK_BIN (impl->save_extra_align)->child;
2127 gtk_container_remove (GTK_CONTAINER (impl->save_extra_align), child);
2129 gtk_widget_hide (impl->save_extra_align);
2130 gtk_widget_hide (impl->browse_extra_align);
2135 gtk_file_chooser_default_set_property (GObject *object,
2137 const GValue *value,
2141 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
2145 case GTK_FILE_CHOOSER_PROP_ACTION:
2147 GtkFileChooserAction action = g_value_get_enum (value);
2149 if (action != impl->action)
2151 if (action == GTK_FILE_CHOOSER_ACTION_SAVE && impl->select_multiple)
2153 g_warning ("Multiple selection mode is not allowed in Save mode");
2154 set_select_multiple (impl, FALSE, TRUE);
2156 impl->action = action;
2157 update_appearance (impl);
2161 case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
2162 set_file_system_backend (impl, g_value_get_string (value));
2164 case GTK_FILE_CHOOSER_PROP_FILTER:
2165 set_current_filter (impl, g_value_get_object (value));
2167 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
2168 impl->local_only = g_value_get_boolean (value);
2170 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
2171 set_preview_widget (impl, g_value_get_object (value));
2173 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
2174 impl->preview_widget_active = g_value_get_boolean (value);
2175 update_preview_widget_visibility (impl);
2177 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
2178 set_extra_widget (impl, g_value_get_object (value));
2179 update_appearance (impl);
2181 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
2183 gboolean select_multiple = g_value_get_boolean (value);
2184 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE && select_multiple)
2186 g_warning ("Multiple selection mode is not allowed in Save mode");
2190 set_select_multiple (impl, select_multiple, FALSE);
2193 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
2195 gboolean show_hidden = g_value_get_boolean (value);
2196 if (show_hidden != impl->show_hidden)
2198 impl->show_hidden = show_hidden;
2199 _gtk_file_system_model_set_show_hidden (GTK_FILE_SYSTEM_MODEL (impl->browse_directories_model),
2201 _gtk_file_system_model_set_show_hidden (GTK_FILE_SYSTEM_MODEL (impl->browse_files_model),
2207 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2213 gtk_file_chooser_default_get_property (GObject *object,
2218 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
2222 case GTK_FILE_CHOOSER_PROP_ACTION:
2223 g_value_set_enum (value, impl->action);
2225 case GTK_FILE_CHOOSER_PROP_FILTER:
2226 g_value_set_object (value, impl->current_filter);
2228 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
2229 g_value_set_boolean (value, impl->local_only);
2231 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
2232 g_value_set_object (value, impl->preview_widget);
2234 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
2235 g_value_set_boolean (value, impl->preview_widget_active);
2237 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
2238 g_value_set_object (value, impl->extra_widget);
2240 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
2241 g_value_set_boolean (value, impl->select_multiple);
2243 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
2244 g_value_set_boolean (value, impl->show_hidden);
2247 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2254 gtk_file_chooser_default_dispose (GObject *object)
2256 GtkFileChooserDefault *impl = (GtkFileChooserDefault *) object;
2258 if (impl->extra_widget)
2260 g_object_unref (impl->extra_widget);
2261 impl->extra_widget = NULL;
2263 G_OBJECT_CLASS (parent_class)->dispose (object);
2266 /* We override show-all since we have internal widgets that
2267 * shouldn't be shown when you call show_all(), like the filter
2271 gtk_file_chooser_default_show_all (GtkWidget *widget)
2273 gtk_widget_show (widget);
2277 expand_and_select_func (GtkFileSystemModel *model,
2282 GtkFileChooserDefault *impl = user_data;
2283 GtkTreeView *tree_view;
2285 if (model == impl->browse_directories_model)
2286 tree_view = GTK_TREE_VIEW (impl->browse_directories_tree_view);
2288 tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
2290 gtk_tree_view_expand_to_path (tree_view, path);
2291 gtk_tree_view_expand_row (tree_view, path, FALSE);
2292 gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
2293 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_directories_tree_view), path, NULL, TRUE, 0.3, 0.5);
2297 list_model_filter_func (GtkFileSystemModel *model,
2299 const GtkFileInfo *file_info,
2302 GtkFileChooserDefault *impl = user_data;
2303 GtkFileFilterInfo filter_info;
2304 GtkFileFilterFlags needed;
2307 if (!impl->current_filter)
2310 if (gtk_file_info_get_is_folder (file_info))
2313 filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
2315 needed = gtk_file_filter_get_needed (impl->current_filter);
2317 filter_info.display_name = gtk_file_info_get_display_name (file_info);
2318 filter_info.mime_type = gtk_file_info_get_mime_type (file_info);
2320 if (needed & GTK_FILE_FILTER_FILENAME)
2322 filter_info.filename = gtk_file_system_path_to_filename (impl->file_system, path);
2323 if (filter_info.filename)
2324 filter_info.contains |= GTK_FILE_FILTER_FILENAME;
2327 filter_info.filename = NULL;
2329 if (needed & GTK_FILE_FILTER_URI)
2331 filter_info.uri = gtk_file_system_path_to_uri (impl->file_system, path);
2332 if (filter_info.filename)
2333 filter_info.contains |= GTK_FILE_FILTER_URI;
2336 filter_info.uri = NULL;
2338 result = gtk_file_filter_filter (impl->current_filter, &filter_info);
2340 if (filter_info.filename)
2341 g_free ((gchar *)filter_info.filename);
2342 if (filter_info.uri)
2343 g_free ((gchar *)filter_info.uri);
2349 install_list_model_filter (GtkFileChooserDefault *impl)
2351 if (impl->current_filter)
2352 _gtk_file_system_model_set_filter (impl->browse_files_model,
2353 list_model_filter_func,
2357 #define COMPARE_DIRECTORIES \
2358 GtkFileChooserDefault *impl = user_data; \
2359 const GtkFileInfo *info_a = _gtk_file_system_model_get_info (impl->browse_files_model, a); \
2360 const GtkFileInfo *info_b = _gtk_file_system_model_get_info (impl->browse_files_model, b); \
2361 gboolean dir_a, dir_b; \
2364 dir_a = gtk_file_info_get_is_folder (info_a); \
2366 return impl->list_sort_ascending ? -1 : 1; \
2369 dir_b = gtk_file_info_get_is_folder (info_b); \
2371 return impl->list_sort_ascending ? 1 : -1; \
2373 if (dir_a != dir_b) \
2374 return impl->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */
2376 /* Sort callback for the filename column */
2378 name_sort_func (GtkTreeModel *model,
2383 COMPARE_DIRECTORIES;
2385 return strcmp (gtk_file_info_get_display_key (info_a), gtk_file_info_get_display_key (info_b));
2388 /* Sort callback for the size column */
2390 size_sort_func (GtkTreeModel *model,
2395 COMPARE_DIRECTORIES;
2398 gint64 size_a = gtk_file_info_get_size (info_a);
2399 gint64 size_b = gtk_file_info_get_size (info_b);
2401 return size_a > size_b ? -1 : (size_a == size_b ? 0 : 1);
2405 /* Sort callback for the mtime column */
2407 mtime_sort_func (GtkTreeModel *model,
2412 COMPARE_DIRECTORIES;
2415 GtkFileTime ta = gtk_file_info_get_modification_time (info_a);
2416 GtkFileTime tb = gtk_file_info_get_modification_time (info_b);
2418 return ta > tb ? -1 : (ta == tb ? 0 : 1);
2422 /* Callback used when the sort column changes. We cache the sort order for use
2423 * in name_sort_func().
2426 list_sort_column_changed_cb (GtkTreeSortable *sortable,
2427 GtkFileChooserDefault *impl)
2429 GtkSortType sort_type;
2431 if (gtk_tree_sortable_get_sort_column_id (sortable, NULL, &sort_type))
2432 impl->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
2435 /* Gets rid of the old list model and creates a new one for the current folder */
2437 set_list_model (GtkFileChooserDefault *impl)
2439 if (impl->browse_files_model)
2441 g_object_unref (impl->browse_files_model);
2442 g_object_unref (impl->sort_model);
2445 impl->browse_files_model = _gtk_file_system_model_new (impl->file_system,
2446 impl->current_folder, 0,
2448 _gtk_file_system_model_set_show_hidden (impl->browse_files_model, impl->show_hidden);
2449 install_list_model_filter (impl);
2451 impl->sort_model = (GtkTreeModelSort *)gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (impl->browse_files_model));
2452 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, name_sort_func, impl, NULL);
2453 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_SIZE, size_sort_func, impl, NULL);
2454 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_MTIME, mtime_sort_func, impl, NULL);
2455 gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->sort_model), NULL, NULL, NULL);
2456 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, GTK_SORT_ASCENDING);
2457 impl->list_sort_ascending = TRUE;
2459 g_signal_connect (impl->sort_model, "sort_column_changed",
2460 G_CALLBACK (list_sort_column_changed_cb), impl);
2462 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
2463 GTK_TREE_MODEL (impl->sort_model));
2464 gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->browse_files_tree_view));
2465 gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_files_tree_view),
2466 GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
2469 /* Gets rid of the old folder tree model and creates a new one for the volume
2470 * corresponding to the specified path.
2473 set_tree_model (GtkFileChooserDefault *impl, const GtkFilePath *path)
2475 GtkFileSystemVolume *volume;
2476 GtkFilePath *base_path, *parent_path;
2480 volume = gtk_file_system_get_volume_for_path (impl->file_system, path);
2483 base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
2485 if (base_path == NULL)
2487 base_path = gtk_file_path_copy (path);
2488 while (gtk_file_system_get_parent (impl->file_system,
2492 parent_path != NULL)
2494 gtk_file_path_free (base_path);
2495 base_path = parent_path;
2499 if (impl->current_volume_path && gtk_file_path_compare (base_path, impl->current_volume_path) == 0)
2502 if (impl->browse_directories_model)
2503 g_object_unref (impl->browse_directories_model);
2505 impl->current_volume_path = gtk_file_path_copy (base_path);
2507 impl->browse_directories_model = _gtk_file_system_model_new (impl->file_system, impl->current_volume_path, -1,
2508 GTK_FILE_INFO_DISPLAY_NAME);
2509 _gtk_file_system_model_set_show_files (GTK_FILE_SYSTEM_MODEL (impl->browse_directories_model),
2511 _gtk_file_system_model_set_show_hidden (GTK_FILE_SYSTEM_MODEL (impl->browse_directories_model),
2514 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_directories_tree_view),
2515 GTK_TREE_MODEL (impl->browse_directories_model));
2519 gtk_file_path_free (base_path);
2521 gtk_file_system_volume_free (impl->file_system, volume);
2525 update_chooser_entry (GtkFileChooserDefault *impl)
2527 GtkTreeSelection *selection;
2528 const GtkFileInfo *info;
2530 GtkTreeIter child_iter;
2532 if (impl->action != GTK_FILE_CHOOSER_ACTION_SAVE)
2535 g_assert (!impl->select_multiple);
2536 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2538 if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
2541 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
2545 info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
2547 if (!gtk_file_info_get_is_folder (info))
2548 gtk_entry_set_text (GTK_ENTRY (impl->save_file_name_entry),
2549 gtk_file_info_get_display_name (info));
2553 gtk_file_chooser_default_set_current_folder (GtkFileChooser *chooser,
2554 const GtkFilePath *path)
2556 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2558 if (impl->current_folder)
2559 gtk_file_path_free (impl->current_folder);
2561 impl->current_folder = gtk_file_path_copy (path);
2563 /* Change the current folder label */
2564 gtk_path_bar_set_path (GTK_PATH_BAR (impl->browse_path_bar), path, impl->file_system, NULL);
2566 /* Update the folder tree */
2568 if (!impl->changing_folder)
2570 impl->changing_folder = TRUE;
2571 set_tree_model (impl, impl->current_folder);
2572 _gtk_file_system_model_path_do (GTK_FILE_SYSTEM_MODEL (impl->browse_directories_model),
2573 path, expand_and_select_func, impl);
2574 impl->changing_folder = FALSE;
2577 /* Create a new list model */
2578 set_list_model (impl);
2580 /* Refresh controls */
2582 shortcuts_unselect_all (impl);
2584 g_signal_emit_by_name (impl, "current-folder-changed", 0);
2586 check_preview_change (impl);
2587 bookmarks_check_add_sensitivity (impl);
2589 g_signal_emit_by_name (impl, "selection-changed", 0);
2592 static GtkFilePath *
2593 gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser)
2595 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2597 return gtk_file_path_copy (impl->current_folder);
2601 gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
2604 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2606 g_return_if_fail (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE);
2608 gtk_entry_set_text (GTK_ENTRY (impl->save_file_name_entry), name);
2612 select_func (GtkFileSystemModel *model,
2617 GtkFileChooserDefault *impl = user_data;
2618 GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
2619 GtkTreePath *sorted_path;
2621 sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model, path);
2622 gtk_tree_view_set_cursor (tree_view, sorted_path, NULL, FALSE);
2623 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_directories_tree_view), sorted_path, NULL, TRUE, 0.3, 0.0);
2624 gtk_tree_path_free (sorted_path);
2628 gtk_file_chooser_default_select_path (GtkFileChooser *chooser,
2629 const GtkFilePath *path)
2631 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2632 GtkFilePath *parent_path;
2636 if (!gtk_file_system_get_parent (impl->file_system, path, &parent_path, &error))
2638 error_getting_info_dialog (impl, path, error);
2644 _gtk_file_chooser_set_current_folder_path (chooser, path);
2648 _gtk_file_chooser_set_current_folder_path (chooser, parent_path);
2649 gtk_file_path_free (parent_path);
2650 _gtk_file_system_model_path_do (impl->browse_files_model, path,
2656 unselect_func (GtkFileSystemModel *model,
2661 GtkFileChooserDefault *impl = user_data;
2662 GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
2663 GtkTreePath *sorted_path;
2665 sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model,
2667 gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (tree_view),
2669 gtk_tree_path_free (sorted_path);
2673 gtk_file_chooser_default_unselect_path (GtkFileChooser *chooser,
2674 const GtkFilePath *path)
2676 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2678 _gtk_file_system_model_path_do (impl->browse_files_model, path,
2679 unselect_func, impl);
2683 gtk_file_chooser_default_select_all (GtkFileChooser *chooser)
2685 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2686 if (impl->select_multiple)
2688 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2689 gtk_tree_selection_select_all (selection);
2694 gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser)
2696 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2697 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2699 gtk_tree_selection_unselect_all (selection);
2702 struct get_paths_closure {
2703 GtkFileChooserDefault *impl;
2705 GtkFilePath *path_from_entry;
2709 get_paths_foreach (GtkTreeModel *model,
2714 struct get_paths_closure *info;
2715 const GtkFilePath *file_path;
2716 GtkFileSystemModel *fs_model;
2717 GtkTreeIter sel_iter;
2721 if (info->impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
2722 info->impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
2724 fs_model = info->impl->browse_directories_model;
2729 fs_model = info->impl->browse_files_model;
2730 gtk_tree_model_sort_convert_iter_to_child_iter (info->impl->sort_model, &sel_iter, iter);
2733 file_path = _gtk_file_system_model_get_path (GTK_FILE_SYSTEM_MODEL (fs_model), &sel_iter);
2735 if (!info->path_from_entry
2736 || gtk_file_path_compare (info->path_from_entry, file_path) != 0)
2737 info->result = g_slist_prepend (info->result, gtk_file_path_copy (file_path));
2741 gtk_file_chooser_default_get_paths (GtkFileChooser *chooser)
2743 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2744 struct get_paths_closure info;
2748 info.path_from_entry = NULL;
2750 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
2752 const char *filename;
2754 filename = gtk_entry_get_text (GTK_ENTRY (impl->save_file_name_entry));
2756 if (filename != NULL && filename[0] != '\0')
2758 GtkFilePath *selected;
2759 GError *error = NULL;
2761 selected = gtk_file_system_make_path (impl->file_system, impl->current_folder, filename, &error);
2765 error_building_filename_dialog (impl, impl->current_folder, filename, error);
2769 info.path_from_entry = selected;
2773 if (!info.path_from_entry || impl->select_multiple)
2775 GtkTreeSelection *selection;
2779 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
2780 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
2782 if (impl->browse_directories_model)
2783 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_directories_tree_view));
2787 if (impl->sort_model)
2788 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2792 gtk_tree_selection_selected_foreach (selection, get_paths_foreach, &info);
2795 if (info.path_from_entry)
2796 info.result = g_slist_prepend (info.result, info.path_from_entry);
2798 return g_slist_reverse (info.result);
2801 static GtkFilePath *
2802 gtk_file_chooser_default_get_preview_path (GtkFileChooser *chooser)
2804 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2806 if (impl->preview_path)
2807 return gtk_file_path_copy (impl->preview_path);
2812 static GtkFileSystem *
2813 gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser)
2815 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2817 return impl->file_system;
2820 /* Shows or hides the filter widgets */
2822 toolbar_show_filters (GtkFileChooserDefault *impl,
2826 gtk_widget_show (impl->filter_combo);
2828 gtk_widget_hide (impl->filter_combo);
2832 gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
2833 GtkFileFilter *filter)
2835 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2838 if (g_slist_find (impl->filters, filter))
2840 g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
2844 g_object_ref (filter);
2845 gtk_object_sink (GTK_OBJECT (filter));
2846 impl->filters = g_slist_append (impl->filters, filter);
2848 name = gtk_file_filter_get_name (filter);
2850 name = "Untitled filter"; /* Place-holder, doesn't need to be marked for translation */
2852 gtk_combo_box_append_text (GTK_COMBO_BOX (impl->filter_combo), name);
2854 if (!g_slist_find (impl->filters, impl->current_filter))
2855 set_current_filter (impl, filter);
2857 toolbar_show_filters (impl, TRUE);
2861 gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
2862 GtkFileFilter *filter)
2864 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2865 GtkTreeModel *model;
2869 filter_index = g_slist_index (impl->filters, filter);
2871 if (filter_index < 0)
2873 g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
2877 impl->filters = g_slist_remove (impl->filters, filter);
2879 if (filter == impl->current_filter)
2882 set_current_filter (impl, impl->filters->data);
2884 set_current_filter (impl, NULL);
2887 /* Remove row from the combo box */
2888 model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->filter_combo));
2889 gtk_tree_model_iter_nth_child (model, &iter, NULL, filter_index);
2890 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
2892 g_object_unref (filter);
2895 toolbar_show_filters (impl, FALSE);
2899 gtk_file_chooser_default_list_filters (GtkFileChooser *chooser)
2901 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2903 return g_slist_copy (impl->filters);
2906 /* Returns the position in the shortcuts tree where the nth specified shortcut would appear */
2908 shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl,
2911 return pos + shortcuts_get_index (impl, SHORTCUTS_SHORTCUTS);
2915 gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser *chooser,
2916 const GtkFilePath *path,
2919 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2923 pos = shortcuts_get_pos_for_shortcut_folder (impl, impl->num_shortcuts);
2925 result = shortcuts_insert_path (impl, pos, FALSE, NULL, path, NULL, FALSE, error);
2928 impl->num_shortcuts++;
2934 gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser *chooser,
2935 const GtkFilePath *path,
2938 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2943 if (impl->num_shortcuts == 0)
2946 pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
2947 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
2948 g_assert_not_reached ();
2950 for (i = 0; i < impl->num_shortcuts; i++)
2952 GtkFilePath *shortcut;
2954 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &shortcut, -1);
2955 g_assert (shortcut != NULL);
2957 if (gtk_file_path_compare (shortcut, path) == 0)
2959 /* The other columns are freed by the GtkTreeStore */
2960 gtk_file_path_free (shortcut);
2961 gtk_list_store_remove (impl->shortcuts_model, &iter);
2962 impl->num_shortcuts--;
2966 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
2967 g_assert_not_reached ();
2973 GTK_FILE_CHOOSER_ERROR,
2974 GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
2975 _("shortcut %s does not exist"),
2976 gtk_file_path_get_string (path));
2982 gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser)
2984 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
2990 pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
2991 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
2992 g_assert_not_reached ();
2996 for (i = 0; i < impl->num_shortcuts; i++)
2998 GtkFilePath *shortcut;
3000 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &shortcut, -1);
3001 g_assert (shortcut != NULL);
3003 list = g_slist_prepend (list, gtk_file_path_copy (shortcut));
3005 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
3006 g_assert_not_reached ();
3009 return g_slist_reverse (list);
3013 set_current_filter (GtkFileChooserDefault *impl,
3014 GtkFileFilter *filter)
3016 if (impl->current_filter != filter)
3020 /* If we have filters, new filter must be one of them
3022 filter_index = g_slist_index (impl->filters, filter);
3023 if (impl->filters && filter_index < 0)
3026 if (impl->current_filter)
3027 g_object_unref (impl->current_filter);
3028 impl->current_filter = filter;
3029 if (impl->current_filter)
3031 g_object_ref (impl->current_filter);
3032 gtk_object_sink (GTK_OBJECT (filter));
3036 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
3039 install_list_model_filter (impl);
3041 g_object_notify (G_OBJECT (impl), "filter");
3046 open_and_close (GtkTreeView *tree_view,
3047 GtkTreePath *target_path)
3049 GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
3053 path = gtk_tree_path_new ();
3054 gtk_tree_path_append_index (path, 0);
3056 gtk_tree_model_get_iter (model, &iter, path);
3060 if (gtk_tree_path_is_ancestor (path, target_path) ||
3061 gtk_tree_path_compare (path, target_path) == 0)
3063 GtkTreeIter child_iter;
3064 gtk_tree_view_expand_row (tree_view, path, FALSE);
3065 if (gtk_tree_model_iter_children (model, &child_iter, &iter))
3068 gtk_tree_path_down (path);
3073 gtk_tree_view_collapse_row (tree_view, path);
3077 GtkTreeIter parent_iter;
3078 GtkTreeIter next_iter;
3081 if (gtk_tree_model_iter_next (model, &next_iter))
3084 gtk_tree_path_next (path);
3088 if (!gtk_tree_model_iter_parent (model, &parent_iter, &iter))
3092 gtk_tree_path_up (path);
3099 gtk_tree_path_free (path);
3103 filter_combo_changed (GtkComboBox *combo_box,
3104 GtkFileChooserDefault *impl)
3106 gint new_index = gtk_combo_box_get_active (combo_box);
3107 GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
3109 set_current_filter (impl, new_filter);
3113 check_preview_change (GtkFileChooserDefault *impl)
3115 const GtkFilePath *new_path = NULL;
3117 /* FIXME #132255: Fixing preview for multiple selection involves getting the
3118 * full selection and diffing to find out what the most recently selected file
3119 * is; there is logic in GtkFileSelection that probably can be
3122 if (impl->sort_model && !impl->select_multiple)
3124 GtkTreeSelection *selection;
3127 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3128 if (gtk_tree_selection_get_selected (selection, NULL, &iter))
3130 GtkTreeIter child_iter;
3132 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
3133 &child_iter, &iter);
3135 new_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
3139 if (new_path != impl->preview_path &&
3140 !(new_path && impl->preview_path &&
3141 gtk_file_path_compare (new_path, impl->preview_path) == 0))
3143 if (impl->preview_path)
3144 gtk_file_path_free (impl->preview_path);
3147 impl->preview_path = gtk_file_path_copy (new_path);
3149 impl->preview_path = NULL;
3151 g_signal_emit_by_name (impl, "update-preview");
3156 tree_selection_changed (GtkTreeSelection *selection,
3157 GtkFileChooserDefault *impl)
3160 const GtkFilePath *file_path;
3163 /* FIXME #132255: Fixing this for multiple selection involves getting the full
3164 * selection and diffing to find out what the most recently selected file is;
3165 * there is logic in GtkFileSelection that probably can be copied;
3166 * check_preview_change() is similar.
3168 if (impl->select_multiple
3169 || !gtk_tree_selection_get_selected (selection, NULL, &iter))
3172 file_path = _gtk_file_system_model_get_path (GTK_FILE_SYSTEM_MODEL (impl->browse_directories_model),
3174 if (impl->current_folder && gtk_file_path_compare (file_path, impl->current_folder) == 0)
3177 /* Close the tree up to only the parents of the newly selected
3178 * node and it's immediate children are visible.
3180 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->browse_directories_model), &iter);
3181 open_and_close (GTK_TREE_VIEW (impl->browse_directories_tree_view), path);
3182 gtk_tree_path_free (path);
3184 if (!impl->changing_folder)
3185 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), file_path);
3188 /* Activates a volume by mounting it if necessary and then switching to its
3192 shortcuts_activate_volume (GtkFileChooserDefault *impl,
3193 GtkFileSystemVolume *volume)
3197 if (!gtk_file_system_volume_get_is_mounted (impl->file_system, volume))
3202 if (!gtk_file_system_volume_mount (impl->file_system, volume, &error))
3206 msg = g_strdup_printf ("Could not mount %s:\n%s",
3207 gtk_file_system_volume_get_display_name (impl->file_system, volume),
3209 error_message (impl, msg);
3211 g_error_free (error);
3217 path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
3218 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), path);
3219 gtk_file_path_free (path);
3222 /* Callback used when a row in the shortcuts list is activated */
3224 shortcuts_row_activated_cb (GtkTreeView *tree_view,
3226 GtkTreeViewColumn *column,
3227 GtkFileChooserDefault *impl)
3230 int selected, start_row;
3233 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
3236 selected = *gtk_tree_path_get_indices (path);
3238 if (selected == shortcuts_get_index (impl, SHORTCUTS_SEPARATOR))
3241 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &data, -1);
3243 start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
3244 if (selected >= start_row && selected < start_row + impl->num_volumes)
3246 GtkFileSystemVolume *volume;
3249 shortcuts_activate_volume (impl, volume);
3253 GtkFilePath *file_path;
3256 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), file_path);
3261 shortcuts_select_func (GtkTreeSelection *selection,
3262 GtkTreeModel *model,
3264 gboolean path_currently_selected,
3267 GtkFileChooserDefault *impl = data;
3269 return (*gtk_tree_path_get_indices (path) != shortcuts_get_index (impl, SHORTCUTS_SEPARATOR));
3273 list_selection_changed (GtkTreeSelection *selection,
3274 GtkFileChooserDefault *impl)
3276 /* See if we are in the new folder editable row for Save mode */
3277 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
3279 GtkTreeSelection *selection;
3280 GtkTreeIter iter, child_iter;
3281 const GtkFileInfo *info;
3283 g_assert (!impl->select_multiple);
3284 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3285 if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
3288 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
3292 info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
3294 return; /* We are on the editable row for New Folder */
3297 update_chooser_entry (impl);
3298 check_preview_change (impl);
3299 bookmarks_check_add_sensitivity (impl);
3301 g_signal_emit_by_name (impl, "selection-changed", 0);
3304 /* Callback used when a row in the file list is activated */
3306 list_row_activated (GtkTreeView *tree_view,
3308 GtkTreeViewColumn *column,
3309 GtkFileChooserDefault *impl)
3311 GtkTreeIter iter, child_iter;
3312 const GtkFileInfo *info;
3314 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
3317 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
3319 info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
3321 if (gtk_file_info_get_is_folder (info))
3323 const GtkFilePath *file_path;
3325 file_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
3326 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), file_path);
3331 g_signal_emit_by_name (impl, "file-activated");
3335 path_bar_clicked (GtkPathBar *path_bar,
3336 GtkFilePath *file_path,
3337 GtkFileChooserDefault *impl)
3339 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), file_path);
3342 static const GtkFileInfo *
3343 get_list_file_info (GtkFileChooserDefault *impl,
3346 GtkTreeIter child_iter;
3348 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
3352 return _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
3356 tree_name_data_func (GtkTreeViewColumn *tree_column,
3357 GtkCellRenderer *cell,
3358 GtkTreeModel *tree_model,
3362 GtkFileChooserDefault *impl = data;
3363 const GtkFileInfo *info;
3365 info = _gtk_file_system_model_get_info (GTK_FILE_SYSTEM_MODEL (impl->browse_directories_model),
3371 "text", gtk_file_info_get_display_name (info),
3377 list_icon_data_func (GtkTreeViewColumn *tree_column,
3378 GtkCellRenderer *cell,
3379 GtkTreeModel *tree_model,
3383 GtkFileChooserDefault *impl = data;
3384 GtkTreeIter child_iter;
3385 const GtkFilePath *path;
3388 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
3391 path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
3395 /* FIXME: NULL GError */
3396 pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl), ICON_SIZE, NULL);
3402 g_object_unref (pixbuf);
3405 /* Sets a cellrenderer's text, making it bold if the GtkFileInfo is a folder */
3407 set_cell_text_bold_if_folder (const GtkFileInfo *info, GtkCellRenderer *cell, const char *text)
3411 "weight", gtk_file_info_get_is_folder (info) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
3416 list_name_data_func (GtkTreeViewColumn *tree_column,
3417 GtkCellRenderer *cell,
3418 GtkTreeModel *tree_model,
3422 GtkFileChooserDefault *impl = data;
3423 const GtkFileInfo *info = get_list_file_info (impl, iter);
3428 "text", _("Type name of new folder"),
3433 set_cell_text_bold_if_folder (info, cell, gtk_file_info_get_display_name (info));
3438 list_size_data_func (GtkTreeViewColumn *tree_column,
3439 GtkCellRenderer *cell,
3440 GtkTreeModel *tree_model,
3444 GtkFileChooserDefault *impl = data;
3445 const GtkFileInfo *info = get_list_file_info (impl, iter);
3449 if (!info || gtk_file_info_get_is_folder (info))
3452 size = gtk_file_info_get_size (info);
3454 if (size < (gint64)1024)
3455 str = g_strdup_printf (ngettext ("%d byte", "%d bytes", (gint)size), (gint)size);
3456 else if (size < (gint64)1024*1024)
3457 str = g_strdup_printf (_("%.1f K"), size / (1024.));
3458 else if (size < (gint64)1024*1024*1024)
3459 str = g_strdup_printf (_("%.1f M"), size / (1024.*1024.));
3461 str = g_strdup_printf (_("%.1f G"), size / (1024.*1024.*1024.));
3471 /* Tree column data callback for the file list; fetches the mtime of a file */
3473 list_mtime_data_func (GtkTreeViewColumn *tree_column,
3474 GtkCellRenderer *cell,
3475 GtkTreeModel *tree_model,
3479 GtkFileChooserDefault *impl;
3480 const GtkFileInfo *info;
3481 GtkFileTime time_mtime, time_now;
3488 info = get_list_file_info (impl, iter);
3497 time_mtime = gtk_file_info_get_modification_time (info);
3498 g_date_set_time (&mtime, (GTime) time_mtime);
3500 time_now = (GTime ) time (NULL);
3501 g_date_set_time (&now, (GTime) time_now);
3503 days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
3506 strcpy (buf, _("Today"));
3507 else if (days_diff == 1)
3508 strcpy (buf, _("Yesterday"));
3513 if (days_diff > 1 && days_diff < 7)
3514 format = "%A"; /* Days from last week */
3516 /* FIXME: Get the right format for the locale */
3517 format = _("%d/%b/%Y"); /* Any other date */
3519 if (g_date_strftime (buf, sizeof (buf), format, &mtime) == 0)
3520 strcpy (buf, _("Unknown"));
3523 set_cell_text_bold_if_folder (info, cell, buf);
3527 _gtk_file_chooser_default_new (const char *file_system)
3529 return g_object_new (GTK_TYPE_FILE_CHOOSER_DEFAULT,
3530 "file-system-backend", file_system,
3535 location_entry_create (GtkFileChooserDefault *impl)
3539 entry = _gtk_file_chooser_entry_new ();
3540 /* Pick a good width for the entry */
3541 gtk_entry_set_width_chars (GTK_ENTRY (entry), 25);
3542 gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
3543 _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (entry), impl->file_system);
3544 _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (entry), impl->current_folder);
3546 return GTK_WIDGET (entry);
3550 update_from_entry (GtkFileChooserDefault *impl,
3552 GtkFileChooserEntry *chooser_entry)
3554 const GtkFilePath *folder_path;
3555 const char *file_part;
3557 folder_path = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
3558 file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
3560 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN && !folder_path)
3562 error_message_with_parent (parent,
3563 _("Cannot change to the folder you specified as it is an invalid path."));
3567 if (file_part[0] == '\0')
3569 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), folder_path);
3574 GtkFileFolder *folder = NULL;
3575 GtkFilePath *subfolder_path = NULL;
3576 GtkFileInfo *info = NULL;
3579 /* If the file part is non-empty, we need to figure out if it refers to a
3580 * folder within folder. We could optimize the case here where the folder
3581 * is already loaded for one of our tree models.
3585 folder = gtk_file_system_get_folder (impl->file_system, folder_path, GTK_FILE_INFO_IS_FOLDER, &error);
3589 error_getting_info_dialog (impl, folder_path, error);
3594 subfolder_path = gtk_file_system_make_path (impl->file_system, folder_path, file_part, &error);
3596 if (!subfolder_path)
3600 msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
3601 gtk_file_path_get_string (folder_path),
3604 error_message (impl, msg);
3606 g_object_unref (folder);
3611 info = gtk_file_folder_get_info (folder, subfolder_path, &error);
3616 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
3618 g_object_unref (folder);
3619 gtk_file_path_free (subfolder_path);
3623 error_getting_info_dialog (impl, subfolder_path, error);
3624 g_object_unref (folder);
3625 gtk_file_path_free (subfolder_path);
3629 if (gtk_file_info_get_is_folder (info))
3630 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), subfolder_path);
3632 _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (impl), subfolder_path);
3634 g_object_unref (folder);
3635 gtk_file_path_free (subfolder_path);
3636 gtk_file_info_free (info);
3641 location_popup_handler (GtkFileChooserDefault *impl)
3644 GtkWidget *toplevel;
3651 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
3652 if (!GTK_WIDGET_TOPLEVEL (toplevel))
3655 dialog = gtk_dialog_new_with_buttons (_("Open Location"),
3656 GTK_WINDOW (toplevel),
3657 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
3658 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3659 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
3661 gtk_window_set_default_size (GTK_WINDOW (dialog), 300, -1);
3662 gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
3663 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2);
3664 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
3666 hbox = gtk_hbox_new (FALSE, 12);
3667 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0);
3668 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
3670 label = gtk_label_new_with_mnemonic (_("_Location:"));
3671 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
3673 entry = location_entry_create (impl);
3674 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
3675 gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
3679 gtk_widget_show_all (dialog);
3680 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
3681 update_from_entry (impl, GTK_WINDOW (dialog), GTK_FILE_CHOOSER_ENTRY (entry));
3683 gtk_widget_destroy (dialog);
3686 /* Handler for the "up-folder" keybinding signal */
3688 up_folder_handler (GtkFileChooserDefault *impl)
3690 GtkFilePath *parent_path;
3694 if (gtk_file_system_get_parent (impl->file_system, impl->current_folder, &parent_path, &error))
3696 if (parent_path) /* If we were on a root, parent_path will be NULL */
3698 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), parent_path);
3699 gtk_file_path_free (parent_path);
3704 _("Could not go to the parent folder of %s:\n%s"),
3705 impl->current_folder,
3709 /* Handler for the "home-folder" keybinding signal */
3711 home_folder_handler (GtkFileChooserDefault *impl)
3715 /* Should we pull this information from impl->has_home and the shortcuts data
3716 * instead? Sounds like a bit of overkill...
3719 home = g_get_home_dir ();
3720 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (impl), home);