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.
22 #include "gdk/gdkkeysyms.h"
23 #include "gtkalignment.h"
24 #include "gtkbindings.h"
25 #include "gtkbutton.h"
26 #include "gtkcelllayout.h"
27 #include "gtkcellrendererpixbuf.h"
28 #include "gtkcellrendererseptext.h"
29 #include "gtkcellrenderertext.h"
30 #include "gtkcombobox.h"
32 #include "gtkexpander.h"
33 #include "gtkfilechooserdefault.h"
34 #include "gtkfilechooserembed.h"
35 #include "gtkfilechooserentry.h"
36 #include "gtkfilechooserutils.h"
37 #include "gtkfilechooser.h"
38 #include "gtkfilesystemmodel.h"
41 #include "gtkhpaned.h"
42 #include "gtkiconfactory.h"
43 #include "gtkicontheme.h"
47 #include "gtkmarshalers.h"
48 #include "gtkmenuitem.h"
49 #include "gtkmessagedialog.h"
50 #include "gtkpathbar.h"
51 #include "gtkprivate.h"
52 #include "gtkscrolledwindow.h"
53 #include "gtksizegroup.h"
56 #include "gtktreednd.h"
57 #include "gtktreeprivate.h"
58 #include "gtktreeview.h"
59 #include "gtktreemodelsort.h"
60 #include "gtktreeselection.h"
61 #include "gtktreestore.h"
62 #include "gtktypebuiltins.h"
65 #if defined (G_OS_UNIX)
66 #include "gtkfilesystemunix.h"
67 #elif defined (G_OS_WIN32)
68 #include "gtkfilesystemwin32.h"
74 typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
76 #define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
77 #define GTK_IS_FILE_CHOOSER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT))
78 #define GTK_FILE_CHOOSER_DEFAULT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
81 struct _GtkFileChooserDefaultClass
83 GtkVBoxClass parent_class;
86 struct _GtkFileChooserDefault
88 GtkVBox parent_instance;
90 GtkFileChooserAction action;
92 GtkFileSystem *file_system;
94 /* Save mode widgets */
95 GtkWidget *save_widgets;
97 GtkWidget *save_file_name_entry;
98 GtkWidget *save_folder_label;
99 GtkWidget *save_folder_combo;
100 GtkWidget *save_extra_align;
101 GtkWidget *save_expander;
103 /* The file browsing widgets */
104 GtkWidget *browse_widgets;
105 GtkWidget *browse_shortcuts_tree_view;
106 GtkWidget *browse_shortcuts_add_button;
107 GtkWidget *browse_shortcuts_remove_button;
108 GtkWidget *browse_files_tree_view;
109 GtkWidget *browse_new_folder_button;
110 GtkWidget *browse_path_bar;
111 GtkWidget *browse_extra_align;
113 GtkFileSystemModel *browse_files_model;
115 GtkWidget *filter_combo;
116 GtkWidget *preview_box;
117 GtkWidget *preview_label;
118 GtkWidget *preview_widget;
119 GtkWidget *extra_widget;
121 GtkListStore *shortcuts_model;
122 GtkTreeModel *shortcuts_filter_model;
124 GtkTreeModelSort *sort_model;
126 GtkFileFilter *current_filter;
130 gboolean has_desktop;
136 guint volumes_changed_id;
137 guint bookmarks_changed_id;
139 GtkFilePath *current_volume_path;
140 GtkFilePath *current_folder;
141 GtkFilePath *preview_path;
142 char *preview_display_name;
144 GtkTreeViewColumn *list_name_column;
145 GtkCellRenderer *list_name_renderer;
147 guint settings_signal_id;
151 GdkDragContext *shortcuts_drag_context;
152 GSource *shortcuts_drag_outside_idle;
157 guint local_only : 1;
158 guint preview_widget_active : 1;
159 guint use_preview_label : 1;
160 guint select_multiple : 1;
161 guint show_hidden : 1;
162 guint list_sort_ascending : 1;
163 guint changing_folder : 1;
164 guint shortcuts_current_folder_active : 1;
165 guint shortcuts_current_folder_is_volume : 1;
168 guint shortcuts_drag_outside : 1;
181 static guint signals[LAST_SIGNAL] = { 0 };
183 /* Column numbers for the shortcuts tree. Keep these in sync with shortcuts_model_create() */
185 SHORTCUTS_COL_PIXBUF,
188 SHORTCUTS_COL_REMOVABLE,
189 SHORTCUTS_COL_PIXBUF_VISIBLE,
190 SHORTCUTS_COL_NUM_COLUMNS
193 /* Column numbers for the file list */
198 FILE_LIST_COL_NUM_COLUMNS
201 /* Identifiers for target types */
207 /* Target types for dragging from the shortcuts list */
208 static GtkTargetEntry shortcuts_source_targets[] = {
209 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }
212 static const int num_shortcuts_source_targets = (sizeof (shortcuts_source_targets)
213 / sizeof (shortcuts_source_targets[0]));
215 /* Target types for dropping into the shortcuts list */
216 static GtkTargetEntry shortcuts_dest_targets[] = {
217 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW },
218 { "text/uri-list", 0, TEXT_URI_LIST }
221 static const int num_shortcuts_dest_targets = (sizeof (shortcuts_dest_targets)
222 / sizeof (shortcuts_dest_targets[0]));
224 /* Target types for DnD from the file list */
225 static GtkTargetEntry file_list_source_targets[] = {
226 { "text/uri-list", 0, TEXT_URI_LIST }
229 static const int num_file_list_source_targets = (sizeof (file_list_source_targets)
230 / sizeof (file_list_source_targets[0]));
232 /* Interesting places in the shortcuts bar */
238 SHORTCUTS_BOOKMARKS_SEPARATOR,
240 SHORTCUTS_CURRENT_FOLDER_SEPARATOR,
241 SHORTCUTS_CURRENT_FOLDER
244 /* Icon size for if we can't get it from the theme */
245 #define FALLBACK_ICON_SIZE 20
247 #define PREVIEW_HBOX_SPACING 12
251 static void gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class);
252 static void gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface);
253 static void gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface);
254 static void gtk_file_chooser_default_init (GtkFileChooserDefault *impl);
256 static GObject* gtk_file_chooser_default_constructor (GType type,
257 guint n_construct_properties,
258 GObjectConstructParam *construct_params);
259 static void gtk_file_chooser_default_finalize (GObject *object);
260 static void gtk_file_chooser_default_set_property (GObject *object,
264 static void gtk_file_chooser_default_get_property (GObject *object,
268 static void gtk_file_chooser_default_dispose (GObject *object);
269 static void gtk_file_chooser_default_show_all (GtkWidget *widget);
270 static void gtk_file_chooser_default_style_set (GtkWidget *widget,
271 GtkStyle *previous_style);
272 static void gtk_file_chooser_default_screen_changed (GtkWidget *widget,
273 GdkScreen *previous_screen);
275 static gboolean gtk_file_chooser_default_set_current_folder (GtkFileChooser *chooser,
276 const GtkFilePath *path,
278 static GtkFilePath * gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser);
279 static void gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
281 static gboolean gtk_file_chooser_default_select_path (GtkFileChooser *chooser,
282 const GtkFilePath *path,
284 static void gtk_file_chooser_default_unselect_path (GtkFileChooser *chooser,
285 const GtkFilePath *path);
286 static void gtk_file_chooser_default_select_all (GtkFileChooser *chooser);
287 static void gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser);
288 static GSList * gtk_file_chooser_default_get_paths (GtkFileChooser *chooser);
289 static GtkFilePath * gtk_file_chooser_default_get_preview_path (GtkFileChooser *chooser);
290 static GtkFileSystem *gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser);
291 static void gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
292 GtkFileFilter *filter);
293 static void gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
294 GtkFileFilter *filter);
295 static GSList * gtk_file_chooser_default_list_filters (GtkFileChooser *chooser);
296 static gboolean gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser *chooser,
297 const GtkFilePath *path,
299 static gboolean gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser *chooser,
300 const GtkFilePath *path,
302 static GSList * gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser);
304 static void gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
306 gint *default_height);
307 static void gtk_file_chooser_default_get_resizable_hints (GtkFileChooserEmbed *chooser_embed,
308 gboolean *resize_horizontally,
309 gboolean *resize_vertically);
310 static gboolean gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed);
311 static void gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed);
313 static void location_popup_handler (GtkFileChooserDefault *impl);
314 static void up_folder_handler (GtkFileChooserDefault *impl);
315 static void down_folder_handler (GtkFileChooserDefault *impl);
316 static void home_folder_handler (GtkFileChooserDefault *impl);
317 static void update_appearance (GtkFileChooserDefault *impl);
319 static void set_current_filter (GtkFileChooserDefault *impl,
320 GtkFileFilter *filter);
321 static void check_preview_change (GtkFileChooserDefault *impl);
323 static void filter_combo_changed (GtkComboBox *combo_box,
324 GtkFileChooserDefault *impl);
325 static void shortcuts_row_activated_cb (GtkTreeView *tree_view,
327 GtkTreeViewColumn *column,
328 GtkFileChooserDefault *impl);
329 static gboolean shortcuts_select_func (GtkTreeSelection *selection,
332 gboolean path_currently_selected,
334 static void shortcuts_activate_item (GtkFileChooserDefault *impl,
336 static int shortcuts_get_index (GtkFileChooserDefault *impl,
337 ShortcutsIndex where);
338 static int shortcut_find_position (GtkFileChooserDefault *impl,
339 const GtkFilePath *path);
341 static void list_selection_changed (GtkTreeSelection *tree_selection,
342 GtkFileChooserDefault *impl);
343 static void list_row_activated (GtkTreeView *tree_view,
345 GtkTreeViewColumn *column,
346 GtkFileChooserDefault *impl);
348 static void path_bar_clicked (GtkPathBar *path_bar,
349 GtkFilePath *file_path,
350 GtkFileChooserDefault *impl);
352 static void add_bookmark_button_clicked_cb (GtkButton *button,
353 GtkFileChooserDefault *impl);
354 static void remove_bookmark_button_clicked_cb (GtkButton *button,
355 GtkFileChooserDefault *impl);
357 static void list_icon_data_func (GtkTreeViewColumn *tree_column,
358 GtkCellRenderer *cell,
359 GtkTreeModel *tree_model,
362 static void list_name_data_func (GtkTreeViewColumn *tree_column,
363 GtkCellRenderer *cell,
364 GtkTreeModel *tree_model,
368 static void list_size_data_func (GtkTreeViewColumn *tree_column,
369 GtkCellRenderer *cell,
370 GtkTreeModel *tree_model,
374 static void list_mtime_data_func (GtkTreeViewColumn *tree_column,
375 GtkCellRenderer *cell,
376 GtkTreeModel *tree_model,
380 static GObjectClass *parent_class;
384 /* Drag and drop interface declarations */
387 GtkTreeModelFilter parent;
389 GtkFileChooserDefault *impl;
390 } ShortcutsModelFilter;
393 GtkTreeModelFilterClass parent_class;
394 } ShortcutsModelFilterClass;
396 #define SHORTCUTS_MODEL_FILTER_TYPE (shortcuts_model_filter_get_type ())
397 #define SHORTCUTS_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_MODEL_FILTER_TYPE, ShortcutsModelFilter))
399 static void shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface);
401 G_DEFINE_TYPE_WITH_CODE (ShortcutsModelFilter,
402 shortcuts_model_filter,
403 GTK_TYPE_TREE_MODEL_FILTER,
404 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
405 shortcuts_model_filter_drag_source_iface_init));
407 static GtkTreeModel *shortcuts_model_filter_new (GtkFileChooserDefault *impl,
408 GtkTreeModel *child_model,
414 _gtk_file_chooser_default_get_type (void)
416 static GType file_chooser_default_type = 0;
418 if (!file_chooser_default_type)
420 static const GTypeInfo file_chooser_default_info =
422 sizeof (GtkFileChooserDefaultClass),
423 NULL, /* base_init */
424 NULL, /* base_finalize */
425 (GClassInitFunc) gtk_file_chooser_default_class_init,
426 NULL, /* class_finalize */
427 NULL, /* class_data */
428 sizeof (GtkFileChooserDefault),
430 (GInstanceInitFunc) gtk_file_chooser_default_init,
433 static const GInterfaceInfo file_chooser_info =
435 (GInterfaceInitFunc) gtk_file_chooser_default_iface_init, /* interface_init */
436 NULL, /* interface_finalize */
437 NULL /* interface_data */
440 static const GInterfaceInfo file_chooser_embed_info =
442 (GInterfaceInitFunc) gtk_file_chooser_embed_default_iface_init, /* interface_init */
443 NULL, /* interface_finalize */
444 NULL /* interface_data */
447 file_chooser_default_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserDefault",
448 &file_chooser_default_info, 0);
450 g_type_add_interface_static (file_chooser_default_type,
451 GTK_TYPE_FILE_CHOOSER,
453 g_type_add_interface_static (file_chooser_default_type,
454 GTK_TYPE_FILE_CHOOSER_EMBED,
455 &file_chooser_embed_info);
458 return file_chooser_default_type;
462 gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
464 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
465 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
466 GtkBindingSet *binding_set;
468 parent_class = g_type_class_peek_parent (class);
470 gobject_class->finalize = gtk_file_chooser_default_finalize;
471 gobject_class->constructor = gtk_file_chooser_default_constructor;
472 gobject_class->set_property = gtk_file_chooser_default_set_property;
473 gobject_class->get_property = gtk_file_chooser_default_get_property;
474 gobject_class->dispose = gtk_file_chooser_default_dispose;
476 widget_class->show_all = gtk_file_chooser_default_show_all;
477 widget_class->style_set = gtk_file_chooser_default_style_set;
478 widget_class->screen_changed = gtk_file_chooser_default_screen_changed;
480 signals[LOCATION_POPUP] =
481 _gtk_binding_signal_new ("location-popup",
482 G_OBJECT_CLASS_TYPE (class),
483 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
484 G_CALLBACK (location_popup_handler),
486 _gtk_marshal_VOID__VOID,
489 _gtk_binding_signal_new ("up-folder",
490 G_OBJECT_CLASS_TYPE (class),
491 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
492 G_CALLBACK (up_folder_handler),
494 _gtk_marshal_VOID__VOID,
496 signals[DOWN_FOLDER] =
497 _gtk_binding_signal_new ("down-folder",
498 G_OBJECT_CLASS_TYPE (class),
499 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
500 G_CALLBACK (down_folder_handler),
502 _gtk_marshal_VOID__VOID,
504 signals[HOME_FOLDER] =
505 _gtk_binding_signal_new ("home-folder",
506 G_OBJECT_CLASS_TYPE (class),
507 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
508 G_CALLBACK (home_folder_handler),
510 _gtk_marshal_VOID__VOID,
513 binding_set = gtk_binding_set_by_class (class);
515 gtk_binding_entry_add_signal (binding_set,
516 GDK_l, GDK_CONTROL_MASK,
520 gtk_binding_entry_add_signal (binding_set,
521 GDK_Up, GDK_MOD1_MASK,
524 gtk_binding_entry_add_signal (binding_set,
525 GDK_KP_Up, GDK_MOD1_MASK,
529 gtk_binding_entry_add_signal (binding_set,
530 GDK_Down, GDK_MOD1_MASK,
533 gtk_binding_entry_add_signal (binding_set,
534 GDK_KP_Down, GDK_MOD1_MASK,
538 gtk_binding_entry_add_signal (binding_set,
539 GDK_Home, GDK_MOD1_MASK,
542 gtk_binding_entry_add_signal (binding_set,
543 GDK_KP_Home, GDK_MOD1_MASK,
547 _gtk_file_chooser_install_properties (gobject_class);
549 gtk_settings_install_property (g_param_spec_string ("gtk-file-chooser-backend",
550 P_("Default file chooser backend"),
551 P_("Name of the GtkFileChooser backend to use by default"),
557 gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
559 iface->select_path = gtk_file_chooser_default_select_path;
560 iface->unselect_path = gtk_file_chooser_default_unselect_path;
561 iface->select_all = gtk_file_chooser_default_select_all;
562 iface->unselect_all = gtk_file_chooser_default_unselect_all;
563 iface->get_paths = gtk_file_chooser_default_get_paths;
564 iface->get_preview_path = gtk_file_chooser_default_get_preview_path;
565 iface->get_file_system = gtk_file_chooser_default_get_file_system;
566 iface->set_current_folder = gtk_file_chooser_default_set_current_folder;
567 iface->get_current_folder = gtk_file_chooser_default_get_current_folder;
568 iface->set_current_name = gtk_file_chooser_default_set_current_name;
569 iface->add_filter = gtk_file_chooser_default_add_filter;
570 iface->remove_filter = gtk_file_chooser_default_remove_filter;
571 iface->list_filters = gtk_file_chooser_default_list_filters;
572 iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder;
573 iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder;
574 iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders;
578 gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
580 iface->get_default_size = gtk_file_chooser_default_get_default_size;
581 iface->get_resizable_hints = gtk_file_chooser_default_get_resizable_hints;
582 iface->should_respond = gtk_file_chooser_default_should_respond;
583 iface->initial_focus = gtk_file_chooser_default_initial_focus;
586 gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
588 impl->local_only = TRUE;
589 impl->preview_widget_active = TRUE;
590 impl->use_preview_label = TRUE;
591 impl->select_multiple = FALSE;
592 impl->show_hidden = FALSE;
593 impl->icon_size = FALLBACK_ICON_SIZE;
595 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (impl), TRUE);
596 gtk_box_set_spacing (GTK_BOX (impl), 12);
600 gtk_file_chooser_default_finalize (GObject *object)
602 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
605 g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
606 impl->volumes_changed_id = 0;
607 g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
608 impl->bookmarks_changed_id = 0;
609 g_object_unref (impl->file_system);
611 for (l = impl->filters; l; l = l->next)
613 GtkFileFilter *filter;
615 filter = GTK_FILE_FILTER (l->data);
616 g_object_unref (filter);
618 g_slist_free (impl->filters);
620 if (impl->current_filter)
621 g_object_unref (impl->current_filter);
623 if (impl->current_volume_path)
624 gtk_file_path_free (impl->current_volume_path);
626 if (impl->current_folder)
627 gtk_file_path_free (impl->current_folder);
629 if (impl->preview_path)
630 gtk_file_path_free (impl->preview_path);
632 /* Free all the Models we have */
633 if (impl->browse_files_model)
634 g_object_unref (impl->browse_files_model);
636 if (impl->shortcuts_model)
637 g_object_unref (impl->shortcuts_model);
639 if (impl->shortcuts_filter_model)
640 g_object_unref (impl->shortcuts_filter_model);
642 if (impl->sort_model)
643 g_object_unref (impl->sort_model);
645 g_free (impl->preview_display_name);
647 G_OBJECT_CLASS (parent_class)->finalize (object);
650 /* Shows an error dialog set as transient for the specified window */
652 error_message_with_parent (GtkWindow *parent,
657 dialog = gtk_message_dialog_new (parent,
658 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
663 gtk_dialog_run (GTK_DIALOG (dialog));
664 gtk_widget_destroy (dialog);
667 /* Returns a toplevel GtkWindow, or NULL if none */
669 get_toplevel (GtkWidget *widget)
673 toplevel = gtk_widget_get_toplevel (widget);
674 if (!GTK_WIDGET_TOPLEVEL (toplevel))
677 return GTK_WINDOW (toplevel);
680 /* Shows an error dialog for the file chooser */
682 error_message (GtkFileChooserDefault *impl,
685 error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg);
688 /* Shows a simple error dialog relative to a path. Frees the GError as well. */
690 error_dialog (GtkFileChooserDefault *impl,
692 const GtkFilePath *path,
695 g_return_if_fail (path != NULL);
699 char *uri = gtk_file_system_path_to_uri (impl->file_system, path);
700 char *text = g_strdup_printf (msg,
703 error_message (impl, text);
706 g_error_free (error);
710 /* Displays an error message about not being able to get information for a file.
711 * Frees the GError as well.
714 error_getting_info_dialog (GtkFileChooserDefault *impl,
715 const GtkFilePath *path,
719 _("Could not retrieve information about %s:\n%s"),
723 /* Shows an error dialog about not being able to add a bookmark */
725 error_could_not_add_bookmark_dialog (GtkFileChooserDefault *impl,
726 const GtkFilePath *path,
730 _("Could not add a bookmark for %s:\n%s"),
734 /* Shows an error dialog about not being able to compose a filename */
736 error_building_filename_dialog (GtkFileChooserDefault *impl,
737 const GtkFilePath *base_path,
738 const char *file_part,
744 uri = gtk_file_system_path_to_uri (impl->file_system, base_path);
745 msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
748 error_message (impl, msg);
751 g_error_free (error);
754 /* Shows an error dialog when we cannot switch to a folder */
756 error_changing_folder_dialog (GtkFileChooserDefault *impl,
757 const GtkFilePath *path,
761 _("Could not change the current folder to %s:\n%s"),
766 /* Changes folders, displaying an error dialog if this fails */
768 change_folder_and_display_error (GtkFileChooserDefault *impl,
769 const GtkFilePath *path)
775 result = _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), path, &error);
778 error_changing_folder_dialog (impl, path, error);
784 update_preview_widget_visibility (GtkFileChooserDefault *impl)
786 if (impl->use_preview_label)
788 if (!impl->preview_label)
790 impl->preview_label = gtk_label_new (impl->preview_display_name);
791 gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_label, FALSE, FALSE, 0);
792 gtk_box_reorder_child (GTK_BOX (impl->preview_box), impl->preview_label, 0);
793 gtk_widget_show (impl->preview_label);
798 if (impl->preview_label)
800 gtk_widget_destroy (impl->preview_label);
801 impl->preview_label = NULL;
805 if (impl->preview_widget_active && impl->preview_widget)
806 gtk_widget_show (impl->preview_box);
808 gtk_widget_hide (impl->preview_box);
810 g_signal_emit_by_name (impl, "default-size-changed");
814 set_preview_widget (GtkFileChooserDefault *impl,
815 GtkWidget *preview_widget)
817 if (preview_widget == impl->preview_widget)
820 if (impl->preview_widget)
821 gtk_container_remove (GTK_CONTAINER (impl->preview_box),
822 impl->preview_widget);
824 impl->preview_widget = preview_widget;
825 if (impl->preview_widget)
827 gtk_widget_show (impl->preview_widget);
828 gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_widget, TRUE, TRUE, 0);
829 gtk_box_reorder_child (GTK_BOX (impl->preview_box),
830 impl->preview_widget,
831 (impl->use_preview_label && impl->preview_label) ? 1 : 0);
834 update_preview_widget_visibility (impl);
837 /* Re-reads all the icons for the shortcuts, used when the theme changes */
839 shortcuts_reload_icons (GtkFileChooserDefault *impl)
843 int bookmarks_separator_idx;
844 int current_folder_separator_idx;
847 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
850 bookmarks_separator_idx = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
851 current_folder_separator_idx = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
852 volumes_idx = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
858 gboolean pixbuf_visible;
861 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
862 SHORTCUTS_COL_PATH, &data,
863 SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible,
866 if (!pixbuf_visible || !data)
869 if (i >= volumes_idx && i < volumes_idx + impl->num_volumes)
871 GtkFileSystemVolume *volume;
874 pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl),
875 impl->icon_size, NULL);
879 const GtkFilePath *path;
882 pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
883 impl->icon_size, NULL);
886 gtk_list_store_set (impl->shortcuts_model, &iter,
887 SHORTCUTS_COL_PIXBUF, pixbuf,
892 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
895 /* If a shortcut corresponds to the current folder, selects it */
897 shortcuts_find_current_folder (GtkFileChooserDefault *impl)
899 GtkTreeSelection *selection;
903 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
905 pos = shortcut_find_position (impl, impl->current_folder);
908 gtk_tree_selection_unselect_all (selection);
912 path = gtk_tree_path_new_from_indices (pos, -1);
913 gtk_tree_selection_select_path (selection, path);
914 gtk_tree_path_free (path);
917 /* Returns whether a path is a folder */
919 check_is_folder (GtkFileSystem *file_system, const GtkFilePath *path, GError **error)
921 GtkFileFolder *folder;
923 folder = gtk_file_system_get_folder (file_system, path,
924 GTK_FILE_INFO_DISPLAY_NAME,
929 g_object_unref (folder);
933 /* Convenience function to get the display name and icon info for a path */
935 get_file_info (GtkFileSystem *file_system, const GtkFilePath *path, gboolean name_only, GError **error)
937 GtkFilePath *parent_path;
938 GtkFileFolder *parent_folder;
943 if (!gtk_file_system_get_parent (file_system, path, &parent_path, error))
946 parent_folder = gtk_file_system_get_folder (file_system, parent_path ? parent_path : path,
947 GTK_FILE_INFO_DISPLAY_NAME
948 | (name_only ? 0 : GTK_FILE_INFO_IS_FOLDER),
953 info = gtk_file_folder_get_info (parent_folder, parent_path ? path : NULL, error);
954 g_object_unref (parent_folder);
958 gtk_file_path_free (parent_path);
962 /* Inserts a path in the shortcuts tree, making a copy of it; alternatively,
963 * inserts a volume. A position of -1 indicates the end of the tree.
966 shortcuts_insert_path (GtkFileChooserDefault *impl,
969 GtkFileSystemVolume *volume,
970 const GtkFilePath *path,
983 label_copy = gtk_file_system_volume_get_display_name (impl->file_system, volume);
984 pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl),
985 impl->icon_size, NULL);
989 if (!check_is_folder (impl->file_system, path, error))
993 label_copy = g_strdup (label);
996 GtkFileInfo *info = get_file_info (impl->file_system, path, TRUE, error);
1001 label_copy = g_strdup (gtk_file_info_get_display_name (info));
1002 gtk_file_info_free (info);
1005 data = gtk_file_path_copy (path);
1006 pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
1007 impl->icon_size, NULL);
1011 gtk_list_store_append (impl->shortcuts_model, &iter);
1013 gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
1015 gtk_list_store_set (impl->shortcuts_model, &iter,
1016 SHORTCUTS_COL_PIXBUF, pixbuf,
1017 SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1018 SHORTCUTS_COL_NAME, label_copy,
1019 SHORTCUTS_COL_PATH, data,
1020 SHORTCUTS_COL_REMOVABLE, removable,
1023 g_free (label_copy);
1026 g_object_unref (pixbuf);
1031 /* Appends an item for the user's home directory to the shortcuts model */
1033 shortcuts_append_home (GtkFileChooserDefault *impl)
1036 GtkFilePath *home_path;
1039 home = g_get_home_dir ();
1040 home_path = gtk_file_system_filename_to_path (impl->file_system, home);
1043 impl->has_home = shortcuts_insert_path (impl, -1, FALSE, NULL, home_path, _("Home"), FALSE, &error);
1044 if (!impl->has_home)
1045 error_getting_info_dialog (impl, home_path, error);
1047 gtk_file_path_free (home_path);
1050 /* Appends the ~/Desktop directory to the shortcuts model */
1052 shortcuts_append_desktop (GtkFileChooserDefault *impl)
1057 name = g_build_filename (g_get_home_dir (), "Desktop", NULL);
1058 path = gtk_file_system_filename_to_path (impl->file_system, name);
1061 impl->has_desktop = shortcuts_insert_path (impl, -1, FALSE, NULL, path, _("Desktop"), FALSE, NULL);
1062 /* We do not actually pop up an error dialog if there is no desktop directory
1063 * because some people may really not want to have one.
1066 gtk_file_path_free (path);
1069 /* Appends a list of GtkFilePath to the shortcuts model; returns how many were inserted */
1071 shortcuts_append_paths (GtkFileChooserDefault *impl,
1077 /* As there is no separator now, we want to start there.
1079 start_row = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
1082 for (; paths; paths = paths->next)
1090 if (impl->local_only &&
1091 !gtk_file_system_path_is_local (impl->file_system, path))
1094 /* NULL GError, but we don't really want to show error boxes here */
1095 if (shortcuts_insert_path (impl, start_row + num_inserted, FALSE, NULL, path, NULL, TRUE, NULL))
1099 return num_inserted;
1102 /* Returns the index for the corresponding item in the shortcuts bar */
1104 shortcuts_get_index (GtkFileChooserDefault *impl,
1105 ShortcutsIndex where)
1111 if (where == SHORTCUTS_HOME)
1114 n += impl->has_home ? 1 : 0;
1116 if (where == SHORTCUTS_DESKTOP)
1119 n += impl->has_desktop ? 1 : 0;
1121 if (where == SHORTCUTS_VOLUMES)
1124 n += impl->num_volumes;
1126 if (where == SHORTCUTS_SHORTCUTS)
1129 n += impl->num_shortcuts;
1131 if (where == SHORTCUTS_BOOKMARKS_SEPARATOR)
1134 /* If there are no bookmarks there won't be a separator */
1135 n += (impl->num_bookmarks > 0) ? 1 : 0;
1137 if (where == SHORTCUTS_BOOKMARKS)
1140 n += impl->num_bookmarks;
1142 if (where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR)
1147 if (where == SHORTCUTS_CURRENT_FOLDER)
1150 g_assert_not_reached ();
1157 typedef void (* RemoveFunc) (GtkFileChooserDefault *impl, gpointer data);
1159 /* Removes the specified number of rows from the shortcuts list */
1161 shortcuts_remove_rows (GtkFileChooserDefault *impl,
1164 RemoveFunc remove_fn)
1168 path = gtk_tree_path_new_from_indices (start_row, -1);
1170 for (; n_rows; n_rows--)
1175 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
1176 g_assert_not_reached ();
1180 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &data, -1);
1181 (* remove_fn) (impl, data);
1184 gtk_list_store_remove (impl->shortcuts_model, &iter);
1187 gtk_tree_path_free (path);
1190 /* Used from shortcuts_remove_rows() in shortcuts_add_volumes() */
1192 volume_remove_cb (GtkFileChooserDefault *impl, gpointer data)
1194 GtkFileSystemVolume *volume;
1197 gtk_file_system_volume_free (impl->file_system, volume);
1200 /* Adds all the file system volumes to the shortcuts model */
1202 shortcuts_add_volumes (GtkFileChooserDefault *impl)
1207 gboolean old_changing_folders;
1209 old_changing_folders = impl->changing_folder;
1210 impl->changing_folder = TRUE;
1212 start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
1213 shortcuts_remove_rows (impl, start_row, impl->num_volumes, volume_remove_cb);
1214 impl->num_volumes = 0;
1216 list = gtk_file_system_list_volumes (impl->file_system);
1220 for (l = list; l; l = l->next)
1222 GtkFileSystemVolume *volume;
1226 if (impl->local_only)
1228 GtkFilePath *base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
1229 gboolean is_local = gtk_file_system_path_is_local (impl->file_system, base_path);
1230 gtk_file_path_free (base_path);
1234 gtk_file_system_volume_free (impl->file_system, volume);
1239 if (shortcuts_insert_path (impl, start_row + n, TRUE, volume, NULL, NULL, FALSE, NULL))
1242 gtk_file_system_volume_free (impl->file_system, volume);
1245 impl->num_volumes = n;
1246 g_slist_free (list);
1248 if (impl->shortcuts_filter_model)
1249 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
1251 impl->changing_folder = old_changing_folders;
1254 /* Used from shortcuts_remove_rows() */
1256 remove_bookmark_cb (GtkFileChooserDefault *impl, gpointer data)
1261 gtk_file_path_free (path);
1264 /* Inserts a separator node in the shortcuts list */
1266 shortcuts_insert_separator (GtkFileChooserDefault *impl,
1267 ShortcutsIndex where)
1271 g_assert (where == SHORTCUTS_BOOKMARKS_SEPARATOR || where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1273 gtk_list_store_insert (impl->shortcuts_model, &iter,
1274 shortcuts_get_index (impl, where));
1275 gtk_list_store_set (impl->shortcuts_model, &iter,
1276 SHORTCUTS_COL_PIXBUF, NULL,
1277 SHORTCUTS_COL_PIXBUF_VISIBLE, FALSE,
1278 SHORTCUTS_COL_NAME, NULL,
1279 SHORTCUTS_COL_PATH, NULL,
1283 /* Updates the list of bookmarks */
1285 shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
1288 gboolean old_changing_folders;
1290 old_changing_folders = impl->changing_folder;
1291 impl->changing_folder = TRUE;
1293 if (impl->num_bookmarks > 0)
1295 shortcuts_remove_rows (impl,
1296 shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR),
1297 impl->num_bookmarks + 1,
1298 remove_bookmark_cb);
1302 bookmarks = gtk_file_system_list_bookmarks (impl->file_system);
1303 impl->num_bookmarks = shortcuts_append_paths (impl, bookmarks);
1304 gtk_file_paths_free (bookmarks);
1306 if (impl->num_bookmarks > 0)
1308 shortcuts_insert_separator (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
1310 if (impl->shortcuts_filter_model)
1311 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
1313 impl->changing_folder = old_changing_folders;
1316 /* Appends a separator and a row to the shortcuts list for the current folder */
1318 shortcuts_add_current_folder (GtkFileChooserDefault *impl)
1323 g_assert (!impl->shortcuts_current_folder_active);
1327 pos = shortcut_find_position (impl, impl->current_folder);
1330 GtkFileSystemVolume *volume;
1331 GtkFilePath *base_path;
1335 shortcuts_insert_separator (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1339 pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER);
1341 volume = gtk_file_system_get_volume_for_path (impl->file_system, impl->current_folder);
1343 base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
1348 strcmp (gtk_file_path_get_string (base_path), gtk_file_path_get_string (impl->current_folder)) == 0)
1350 success = shortcuts_insert_path (impl, pos, TRUE, volume, NULL, NULL, FALSE, NULL);
1351 impl->shortcuts_current_folder_is_volume = TRUE;
1355 success = shortcuts_insert_path (impl, pos, FALSE, NULL, impl->current_folder, NULL, FALSE, NULL);
1356 impl->shortcuts_current_folder_is_volume = FALSE;
1360 gtk_file_system_volume_free (impl->file_system, volume);
1361 gtk_file_path_free (base_path);
1364 shortcuts_remove_rows (impl, pos - 1, 1, NULL); /* remove the separator */
1366 impl->shortcuts_current_folder_active = success;
1370 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), pos);
1373 /* Used from shortcuts_remove_rows() in shortcuts_update_current_folder() */
1375 remove_current_folder_cb (GtkFileChooserDefault *impl,
1378 if (impl->shortcuts_current_folder_is_volume)
1379 gtk_file_system_volume_free (impl->file_system, data);
1381 gtk_file_path_free (data);
1384 /* Updates the current folder row in the shortcuts model */
1386 shortcuts_update_current_folder (GtkFileChooserDefault *impl)
1390 pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1392 if (impl->shortcuts_current_folder_active)
1394 shortcuts_remove_rows (impl, pos, 2, remove_current_folder_cb);
1395 impl->shortcuts_current_folder_active = FALSE;
1398 shortcuts_add_current_folder (impl);
1401 /* Filter function used for the shortcuts filter model */
1403 shortcuts_filter_cb (GtkTreeModel *model,
1407 GtkFileChooserDefault *impl;
1411 impl = GTK_FILE_CHOOSER_DEFAULT (data);
1413 path = gtk_tree_model_get_path (model, iter);
1417 pos = *gtk_tree_path_get_indices (path);
1418 gtk_tree_path_free (path);
1420 return (pos < shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR));
1423 /* Creates the list model for shortcuts */
1425 shortcuts_model_create (GtkFileChooserDefault *impl)
1427 /* Keep this order in sync with the SHORCUTS_COL_* enum values */
1428 impl->shortcuts_model = gtk_list_store_new (SHORTCUTS_COL_NUM_COLUMNS,
1429 GDK_TYPE_PIXBUF, /* pixbuf */
1430 G_TYPE_STRING, /* name */
1431 G_TYPE_POINTER, /* path or volume */
1432 G_TYPE_BOOLEAN, /* removable */
1433 G_TYPE_BOOLEAN); /* pixbuf cell visibility */
1435 if (impl->file_system)
1437 shortcuts_append_home (impl);
1438 shortcuts_append_desktop (impl);
1439 shortcuts_add_volumes (impl);
1440 shortcuts_add_bookmarks (impl);
1443 impl->shortcuts_filter_model = shortcuts_model_filter_new (impl,
1444 GTK_TREE_MODEL (impl->shortcuts_model),
1447 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
1448 shortcuts_filter_cb,
1453 /* Callback used when the "New Folder" toolbar button is clicked */
1455 new_folder_button_clicked (GtkButton *button,
1456 GtkFileChooserDefault *impl)
1461 /* FIXME: this doesn't work for folder mode, just for file mode */
1463 _gtk_file_system_model_add_editable (impl->browse_files_model, &iter);
1465 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->browse_files_model), &iter);
1466 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_files_tree_view),
1467 path, impl->list_name_column,
1470 g_object_set (impl->list_name_renderer, "editable", TRUE, NULL);
1471 gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view),
1473 impl->list_name_column,
1477 /* Callback used from the text cell renderer when the new folder is named */
1479 renderer_edited_cb (GtkCellRendererText *cell_renderer_text,
1481 const gchar *new_text,
1482 GtkFileChooserDefault *impl)
1485 GtkFilePath *file_path;
1487 _gtk_file_system_model_remove_editable (impl->browse_files_model);
1488 g_object_set (impl->list_name_renderer, "editable", FALSE, NULL);
1491 file_path = gtk_file_system_make_path (impl->file_system, impl->current_folder, new_text, &error);
1494 error_building_filename_dialog (impl, impl->current_folder, new_text, error);
1499 if (!gtk_file_system_create_folder (impl->file_system, file_path, &error))
1502 _("Could not create folder %s:\n%s"),
1506 gtk_file_path_free (file_path);
1508 /* FIXME: scroll to the new folder and select it */
1511 /* Callback used from the text cell renderer when the new folder edition gets
1515 renderer_editing_canceled_cb (GtkCellRendererText *cell_renderer_text,
1516 GtkFileChooserDefault *impl)
1518 _gtk_file_system_model_remove_editable (impl->browse_files_model);
1519 g_object_set (impl->list_name_renderer, "editable", FALSE, NULL);
1522 /* Creates the widgets for the filter combo box */
1524 filter_create (GtkFileChooserDefault *impl)
1526 impl->filter_combo = gtk_combo_box_new_text ();
1527 g_signal_connect (impl->filter_combo, "changed",
1528 G_CALLBACK (filter_combo_changed), impl);
1530 return impl->filter_combo;
1534 button_new (GtkFileChooserDefault *impl,
1536 const char *stock_id,
1546 button = gtk_button_new ();
1547 hbox = gtk_hbox_new (FALSE, 2);
1548 align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1550 gtk_container_add (GTK_CONTAINER (button), align);
1551 gtk_container_add (GTK_CONTAINER (align), hbox);
1552 widget = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
1554 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1556 widget = gtk_label_new_with_mnemonic (text);
1557 gtk_label_set_mnemonic_widget (GTK_LABEL (widget), GTK_WIDGET (button));
1558 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1560 gtk_widget_set_sensitive (button, sensitive);
1561 g_signal_connect (button, "clicked", callback, impl);
1563 gtk_widget_show_all (align);
1566 gtk_widget_show (button);
1571 /* Looks for a path among the shortcuts; returns its index or -1 if it doesn't exist */
1573 shortcut_find_position (GtkFileChooserDefault *impl,
1574 const GtkFilePath *path)
1578 int bookmarks_separator_idx;
1579 int current_folder_separator_idx;
1582 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
1585 bookmarks_separator_idx = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
1586 current_folder_separator_idx = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1587 volumes_idx = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
1591 for (i = 0; i < current_folder_separator_idx; i++)
1595 if (i == bookmarks_separator_idx)
1598 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &data, -1);
1600 if (i >= volumes_idx && i < volumes_idx + impl->num_volumes)
1602 GtkFileSystemVolume *volume;
1603 GtkFilePath *base_path;
1607 base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
1609 exists = strcmp (gtk_file_path_get_string (path),
1610 gtk_file_path_get_string (base_path)) == 0;
1618 GtkFilePath *model_path;
1622 if (model_path && gtk_file_path_compare (model_path, path) == 0)
1627 gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
1633 /* Tries to add a bookmark from a path name */
1635 shortcuts_add_bookmark_from_path (GtkFileChooserDefault *impl,
1636 const GtkFilePath *path,
1641 if (shortcut_find_position (impl, path) != -1)
1644 /* FIXME: this check really belongs in gtk_file_system_insert_bookmark. */
1646 if (!check_is_folder (impl->file_system, path, &error))
1649 _("Could not add bookmark for %s because it is not a folder."),
1656 if (!gtk_file_system_insert_bookmark (impl->file_system, path, pos, &error))
1658 error_could_not_add_bookmark_dialog (impl, path, error);
1666 add_bookmark_foreach_cb (GtkTreeModel *model,
1671 GtkFileChooserDefault *impl;
1672 GtkFileSystemModel *fs_model;
1673 GtkTreeIter child_iter;
1674 const GtkFilePath *file_path;
1676 impl = (GtkFileChooserDefault *) data;
1678 fs_model = impl->browse_files_model;
1679 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter);
1681 file_path = _gtk_file_system_model_get_path (GTK_FILE_SYSTEM_MODEL (fs_model), &child_iter);
1682 shortcuts_add_bookmark_from_path (impl, file_path, -1);
1685 /* Callback used when the "Add bookmark" button is clicked */
1687 add_bookmark_button_clicked_cb (GtkButton *button,
1688 GtkFileChooserDefault *impl)
1690 GtkTreeSelection *selection;
1692 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
1694 if (gtk_tree_selection_count_selected_rows (selection) == 0)
1695 shortcuts_add_bookmark_from_path (impl, impl->current_folder, -1);
1697 gtk_tree_selection_selected_foreach (selection,
1698 add_bookmark_foreach_cb,
1702 /* Callback used when the "Remove bookmark" button is clicked */
1704 remove_bookmark_button_clicked_cb (GtkButton *button,
1705 GtkFileChooserDefault *impl)
1707 GtkTreeSelection *selection;
1713 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1715 if (gtk_tree_selection_get_selected (selection, NULL, &iter))
1717 gtk_tree_model_get (impl->shortcuts_filter_model, &iter,
1718 SHORTCUTS_COL_PATH, &path,
1719 SHORTCUTS_COL_REMOVABLE, &removable, -1);
1722 g_assert_not_reached ();
1727 if (!gtk_file_system_remove_bookmark (impl->file_system, path, &error))
1730 _("Could not remove bookmark for %s:\n%s"),
1737 struct selection_check_closure {
1738 GtkFileChooserDefault *impl;
1741 gboolean all_folders;
1744 /* Used from gtk_tree_selection_selected_foreach() */
1746 selection_check_foreach_cb (GtkTreeModel *model,
1751 struct selection_check_closure *closure;
1752 GtkTreeIter child_iter;
1753 const GtkFileInfo *info;
1757 closure->empty = FALSE;
1759 gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
1761 info = _gtk_file_system_model_get_info (closure->impl->browse_files_model, &child_iter);
1762 is_folder = gtk_file_info_get_is_folder (info);
1764 closure->all_folders &= is_folder;
1765 closure->all_files &= !is_folder;
1768 /* Checks whether the selected items in the file list are all files or all folders */
1770 selection_check (GtkFileChooserDefault *impl,
1772 gboolean *all_files,
1773 gboolean *all_folders)
1775 struct selection_check_closure closure;
1776 GtkTreeSelection *selection;
1778 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
1780 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
1781 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
1783 if (gtk_tree_selection_count_selected_rows (selection) == 0)
1784 closure.empty = TRUE;
1787 closure.empty = FALSE;
1788 closure.all_files = FALSE;
1789 closure.all_folders = TRUE;
1794 g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
1795 || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE);
1797 closure.impl = impl;
1798 closure.empty = TRUE;
1799 closure.all_files = TRUE;
1800 closure.all_folders = TRUE;
1802 gtk_tree_selection_selected_foreach (selection,
1803 selection_check_foreach_cb,
1807 g_assert (closure.empty || !(closure.all_files && closure.all_folders));
1810 *empty = closure.empty;
1813 *all_files = closure.all_files;
1816 *all_folders = closure.all_folders;
1819 /* Sensitize the "add bookmark" button if all the selected items are folders, or
1820 * if there are no selected items *and* the current folder is not in the
1821 * bookmarks list. De-sensitize the button otherwise.
1824 bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl)
1826 GtkTreeSelection *selection;
1829 /* Check selection */
1831 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
1833 if (gtk_tree_selection_count_selected_rows (selection) == 0)
1834 active = (shortcut_find_position (impl, impl->current_folder) == -1);
1837 gboolean all_folders;
1839 selection_check (impl, NULL, NULL, &all_folders);
1840 active = (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
1841 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER ||
1845 gtk_widget_set_sensitive (impl->browse_shortcuts_add_button, active);
1848 /* Sets the sensitivity of the "remove bookmark" button depending on whether a
1849 * bookmark row is selected in the shortcuts tree.
1852 bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl)
1854 GtkTreeSelection *selection;
1856 gboolean removable = FALSE;
1858 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1860 if (gtk_tree_selection_get_selected (selection, NULL, &iter))
1861 gtk_tree_model_get (impl->shortcuts_filter_model, &iter,
1862 SHORTCUTS_COL_REMOVABLE, &removable,
1865 gtk_widget_set_sensitive (impl->browse_shortcuts_remove_button, removable);
1868 /* GtkWidget::drag-begin handler for the shortcuts list. */
1870 shortcuts_drag_begin_cb (GtkWidget *widget,
1871 GdkDragContext *context,
1872 GtkFileChooserDefault *impl)
1875 impl->shortcuts_drag_context = g_object_ref (context);
1880 /* Removes the idle handler for outside drags */
1882 shortcuts_cancel_drag_outside_idle (GtkFileChooserDefault *impl)
1884 if (!impl->shortcuts_drag_outside_idle)
1887 g_source_destroy (impl->shortcuts_drag_outside_idle);
1888 impl->shortcuts_drag_outside_idle = NULL;
1892 /* GtkWidget::drag-end handler for the shortcuts list. */
1894 shortcuts_drag_end_cb (GtkWidget *widget,
1895 GdkDragContext *context,
1896 GtkFileChooserDefault *impl)
1899 g_object_unref (impl->shortcuts_drag_context);
1901 shortcuts_cancel_drag_outside_idle (impl);
1903 if (!impl->shortcuts_drag_outside)
1906 gtk_button_clicked (GTK_BUTTON (impl->browse_shortcuts_remove_button));
1908 impl->shortcuts_drag_outside = FALSE;
1912 /* GtkWidget::drag-data-delete handler for the shortcuts list. */
1914 shortcuts_drag_data_delete_cb (GtkWidget *widget,
1915 GdkDragContext *context,
1916 GtkFileChooserDefault *impl)
1918 g_signal_stop_emission_by_name (widget, "drag-data-delete");
1922 /* Creates a suitable drag cursor to indicate that the selected bookmark will be
1926 shortcuts_drag_set_delete_cursor (GtkFileChooserDefault *impl,
1929 GtkTreeView *tree_view;
1930 GtkTreeSelection *selection;
1931 GtkTreeIter iter, child_iter;
1933 GdkPixmap *row_pixmap;
1938 tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
1940 /* Find the selected path and get its drag pixmap */
1942 selection = gtk_tree_view_get_selection (tree_view);
1943 if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
1944 g_assert_not_reached ();
1946 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
1950 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &child_iter);
1952 row_pixmap = gtk_tree_view_create_row_drag_icon (tree_view, path);
1953 gtk_tree_path_free (path);
1962 pixbuf = gtk_widget_render_icon (impl->browse_shortcuts_tree_view,
1968 GdkPixmap *composite;
1969 int row_pixmap_width, row_pixmap_height;
1970 int pixbuf_width, pixbuf_height;
1971 int composite_width, composite_height;
1972 int pixbuf_x, pixbuf_y;
1973 GdkGC *gc, *mask_gc;
1975 GdkBitmap *pixbuf_mask;
1977 /* Create pixmap and mask for composite image */
1979 gdk_drawable_get_size (row_pixmap, &row_pixmap_width, &row_pixmap_height);
1980 pixbuf_width = gdk_pixbuf_get_width (pixbuf);
1981 pixbuf_height = gdk_pixbuf_get_height (pixbuf);
1983 composite_width = MAX (row_pixmap_width, pixbuf_width);
1984 composite_height = MAX (row_pixmap_height, pixbuf_height);
1986 row_pixmap_y = (composite_height - row_pixmap_height) / 2;
1988 if (gtk_widget_get_direction (impl->browse_shortcuts_tree_view) == GTK_TEXT_DIR_RTL)
1991 pixbuf_x = composite_width - pixbuf_width;
1993 pixbuf_y = (composite_height - pixbuf_height) / 2;
1995 composite = gdk_pixmap_new (row_pixmap, composite_width, composite_height, -1);
1996 gc = gdk_gc_new (composite);
1998 mask = gdk_pixmap_new (row_pixmap, composite_width, composite_height, 1);
1999 mask_gc = gdk_gc_new (mask);
2001 gdk_gc_set_foreground (mask_gc, &color);
2002 gdk_draw_rectangle (mask, mask_gc, TRUE, 0, 0, composite_width, composite_height);
2005 color.green = 0xffff;
2006 color.blue = 0xffff;
2007 gdk_gc_set_rgb_fg_color (gc, &color);
2008 gdk_draw_rectangle (composite, gc, TRUE, 0, 0, composite_width, composite_height);
2010 /* Composite the row pixmap and the pixbuf */
2012 gdk_pixbuf_render_pixmap_and_mask_for_colormap
2014 gtk_widget_get_colormap (impl->browse_shortcuts_tree_view),
2015 NULL, &pixbuf_mask, 128);
2016 gdk_draw_drawable (mask, mask_gc, pixbuf_mask,
2019 pixbuf_width, pixbuf_height);
2020 g_object_unref (pixbuf_mask);
2022 gdk_draw_drawable (composite, gc, row_pixmap,
2025 row_pixmap_width, row_pixmap_height);
2027 gdk_gc_set_foreground (mask_gc, &color);
2028 gdk_draw_rectangle (mask, mask_gc, TRUE, 0, row_pixmap_y, row_pixmap_width, row_pixmap_height);
2030 gdk_draw_pixbuf (composite, gc, pixbuf,
2033 pixbuf_width, pixbuf_height,
2037 g_object_unref (pixbuf);
2038 g_object_unref (row_pixmap);
2040 row_pixmap = composite;
2044 /* The hotspot offsets here are copied from gtk_tree_view_drag_begin(), ugh */
2046 gtk_tree_view_get_path_at_pos (tree_view,
2047 tree_view->priv->press_start_x,
2048 tree_view->priv->press_start_y,
2054 gtk_drag_set_icon_pixmap (impl->shortcuts_drag_context,
2055 gdk_drawable_get_colormap (row_pixmap),
2058 tree_view->priv->press_start_x + 1,
2059 row_pixmap_y + cell_y + 1);
2061 g_object_unref (row_pixmap);
2063 g_object_unref (mask);
2066 /* We set the delete cursor and the shortcuts_drag_outside flag in an idle
2067 * handler so that we can tell apart the drag_leave event that comes right
2068 * before a drag_drop, from a normal drag_leave. We don't want to set the
2069 * cursor nor the flag in the latter case.
2072 shortcuts_drag_outside_idle_cb (GtkFileChooserDefault *impl)
2074 shortcuts_drag_set_delete_cursor (impl, TRUE);
2075 impl->shortcuts_drag_outside = TRUE;
2077 shortcuts_cancel_drag_outside_idle (impl);
2082 /* GtkWidget::drag-leave handler for the shortcuts list. We unhighlight the
2086 shortcuts_drag_leave_cb (GtkWidget *widget,
2087 GdkDragContext *context,
2089 GtkFileChooserDefault *impl)
2092 if (gtk_drag_get_source_widget (context) == widget && !impl->shortcuts_drag_outside_idle)
2094 impl->shortcuts_drag_outside_idle = g_idle_source_new ();
2095 g_source_set_closure (impl->shortcuts_drag_outside_idle,
2096 g_cclosure_new_object (G_CALLBACK (shortcuts_drag_outside_idle_cb),
2098 g_source_attach (impl->shortcuts_drag_outside_idle, NULL);
2102 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
2104 GTK_TREE_VIEW_DROP_BEFORE);
2106 g_signal_stop_emission_by_name (widget, "drag-leave");
2109 /* Computes the appropriate row and position for dropping */
2111 shortcuts_compute_drop_position (GtkFileChooserDefault *impl,
2115 GtkTreeViewDropPosition *pos)
2117 GtkTreeView *tree_view;
2118 GtkTreeViewColumn *column;
2122 int bookmarks_index;
2124 tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
2126 bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2128 if (!gtk_tree_view_get_path_at_pos (tree_view,
2130 y - TREE_VIEW_HEADER_HEIGHT (tree_view),
2136 row = bookmarks_index + impl->num_bookmarks - 1;
2137 *path = gtk_tree_path_new_from_indices (row, -1);
2138 *pos = GTK_TREE_VIEW_DROP_AFTER;
2142 row = *gtk_tree_path_get_indices (*path);
2143 gtk_tree_view_get_background_area (tree_view, *path, column, &cell);
2144 gtk_tree_path_free (*path);
2146 if (row < bookmarks_index)
2148 row = bookmarks_index;
2149 *pos = GTK_TREE_VIEW_DROP_BEFORE;
2151 else if (row > bookmarks_index + impl->num_bookmarks - 1)
2153 row = bookmarks_index + impl->num_bookmarks - 1;
2154 *pos = GTK_TREE_VIEW_DROP_AFTER;
2158 if (cell_y < cell.height / 2)
2159 *pos = GTK_TREE_VIEW_DROP_BEFORE;
2161 *pos = GTK_TREE_VIEW_DROP_AFTER;
2164 *path = gtk_tree_path_new_from_indices (row, -1);
2167 /* GtkWidget::drag-motion handler for the shortcuts list. We basically
2168 * implement the destination side of DnD by hand, due to limitations in
2169 * GtkTreeView's DnD API.
2172 shortcuts_drag_motion_cb (GtkWidget *widget,
2173 GdkDragContext *context,
2177 GtkFileChooserDefault *impl)
2180 GtkTreeViewDropPosition pos;
2181 GdkDragAction action;
2184 if (gtk_drag_get_source_widget (context) == widget)
2186 shortcuts_cancel_drag_outside_idle (impl);
2188 if (impl->shortcuts_drag_outside)
2190 shortcuts_drag_set_delete_cursor (impl, FALSE);
2191 impl->shortcuts_drag_outside = FALSE;
2196 if (context->suggested_action == GDK_ACTION_COPY || (context->actions & GDK_ACTION_COPY) != 0)
2197 action = GDK_ACTION_COPY;
2198 else if (context->suggested_action == GDK_ACTION_MOVE || (context->actions & GDK_ACTION_MOVE) != 0)
2199 action = GDK_ACTION_MOVE;
2206 shortcuts_compute_drop_position (impl, x, y, &path, &pos);
2207 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), path, pos);
2208 gtk_tree_path_free (path);
2212 g_signal_stop_emission_by_name (widget, "drag-motion");
2216 gdk_drag_status (context, action, time_);
2223 /* GtkWidget::drag-drop handler for the shortcuts list. */
2225 shortcuts_drag_drop_cb (GtkWidget *widget,
2226 GdkDragContext *context,
2230 GtkFileChooserDefault *impl)
2233 shortcuts_cancel_drag_outside_idle (impl);
2236 g_signal_stop_emission_by_name (widget, "drag-drop");
2240 /* Converts raw selection data from text/uri-list to a list of strings */
2242 split_uris (const char *data)
2245 const char *p, *start;
2251 for (p = start; *p != 0; p++)
2252 if (*p == '\r' && *(p + 1) == '\n')
2256 name = g_strndup (start, p - start);
2257 uris = g_slist_prepend (uris, name);
2263 uris = g_slist_reverse (uris);
2267 /* Parses a "text/uri-list" string and inserts its URIs as bookmarks */
2269 shortcuts_drop_uris (GtkFileChooserDefault *impl,
2275 uris = split_uris (data);
2277 for (l = uris; l; l = l->next)
2283 path = gtk_file_system_uri_to_path (impl->file_system, uri);
2287 if (shortcuts_add_bookmark_from_path (impl, path, position))
2290 gtk_file_path_free (path);
2296 msg = g_strdup_printf (_("Could not add a bookmark for %s because it is an invalid path name."),
2298 error_message (impl, msg);
2305 g_slist_free (uris);
2308 /* Reorders the selected bookmark to the specified position */
2310 shortcuts_reorder (GtkFileChooserDefault *impl,
2313 GtkTreeSelection *selection;
2314 GtkTreeIter iter, child_iter;
2317 int bookmarks_index;
2318 const GtkFilePath *file_path;
2319 GtkFilePath *file_path_copy;
2322 /* Get the selected path */
2324 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
2325 if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
2326 g_assert_not_reached ();
2328 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
2332 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &child_iter);
2333 old_position = *gtk_tree_path_get_indices (path);
2334 gtk_tree_path_free (path);
2336 bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2337 old_position -= bookmarks_index;
2338 g_assert (old_position >= 0 && old_position < impl->num_bookmarks);
2340 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &child_iter,
2341 SHORTCUTS_COL_PATH, &file_path,
2343 file_path_copy = gtk_file_path_copy (file_path); /* removal below will free file_path, so we need a copy */
2345 /* Remove the path from the old position and insert it in the new one */
2347 if (new_position > old_position)
2350 if (old_position == new_position)
2354 if (gtk_file_system_remove_bookmark (impl->file_system, file_path_copy, &error))
2355 shortcuts_add_bookmark_from_path (impl, file_path_copy, new_position);
2357 error_could_not_add_bookmark_dialog (impl, file_path_copy, error);
2359 gtk_file_path_free (file_path_copy);
2362 /* Callback used when we get the drag data for the bookmarks list. We add the
2363 * received URIs as bookmarks if they are folders.
2366 shortcuts_drag_data_received_cb (GtkWidget *widget,
2367 GdkDragContext *context,
2370 GtkSelectionData *selection_data,
2375 GtkFileChooserDefault *impl;
2376 GtkTreePath *tree_path;
2377 GtkTreeViewDropPosition tree_pos;
2379 int bookmarks_index;
2381 impl = GTK_FILE_CHOOSER_DEFAULT (data);
2383 /* Compute position */
2385 bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2387 shortcuts_compute_drop_position (impl, x, y, &tree_path, &tree_pos);
2388 position = *gtk_tree_path_get_indices (tree_path);
2389 gtk_tree_path_free (tree_path);
2391 if (tree_pos == GTK_TREE_VIEW_DROP_AFTER)
2394 g_assert (position >= bookmarks_index);
2395 position -= bookmarks_index;
2397 if (selection_data->target == gdk_atom_intern ("text/uri-list", FALSE))
2398 shortcuts_drop_uris (impl, selection_data->data, position);
2399 else if (selection_data->target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE))
2400 shortcuts_reorder (impl, position);
2402 g_signal_stop_emission_by_name (widget, "drag-data-received");
2405 /* Callback used when the selection in the shortcuts tree changes */
2407 shortcuts_selection_changed_cb (GtkTreeSelection *selection,
2408 GtkFileChooserDefault *impl)
2410 bookmarks_check_remove_sensitivity (impl);
2413 /* Creates the widgets for the shortcuts and bookmarks tree */
2415 shortcuts_list_create (GtkFileChooserDefault *impl)
2418 GtkTreeSelection *selection;
2419 GtkTreeViewColumn *column;
2420 GtkCellRenderer *renderer;
2422 /* Scrolled window */
2424 swin = gtk_scrolled_window_new (NULL, NULL);
2425 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
2426 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2427 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
2429 gtk_widget_show (swin);
2433 impl->browse_shortcuts_tree_view = gtk_tree_view_new ();
2434 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), FALSE);
2436 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), impl->shortcuts_filter_model);
2438 gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
2440 shortcuts_source_targets,
2441 num_shortcuts_source_targets,
2444 gtk_drag_dest_set (impl->browse_shortcuts_tree_view,
2445 GTK_DEST_DEFAULT_ALL,
2446 shortcuts_dest_targets,
2447 num_shortcuts_dest_targets,
2448 GDK_ACTION_COPY | GDK_ACTION_MOVE);
2450 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
2451 gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
2452 gtk_tree_selection_set_select_function (selection,
2453 shortcuts_select_func,
2456 g_signal_connect (selection, "changed",
2457 G_CALLBACK (shortcuts_selection_changed_cb), impl);
2459 g_signal_connect (impl->browse_shortcuts_tree_view, "row-activated",
2460 G_CALLBACK (shortcuts_row_activated_cb), impl);
2462 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-begin",
2463 G_CALLBACK (shortcuts_drag_begin_cb), impl);
2464 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-end",
2465 G_CALLBACK (shortcuts_drag_end_cb), impl);
2466 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-delete",
2467 G_CALLBACK (shortcuts_drag_data_delete_cb), impl);
2469 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-leave",
2470 G_CALLBACK (shortcuts_drag_leave_cb), impl);
2471 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-motion",
2472 G_CALLBACK (shortcuts_drag_motion_cb), impl);
2473 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-drop",
2474 G_CALLBACK (shortcuts_drag_drop_cb), impl);
2475 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-received",
2476 G_CALLBACK (shortcuts_drag_data_received_cb), impl);
2478 gtk_container_add (GTK_CONTAINER (swin), impl->browse_shortcuts_tree_view);
2479 gtk_widget_show (impl->browse_shortcuts_tree_view);
2483 column = gtk_tree_view_column_new ();
2484 gtk_tree_view_column_set_title (column, _("Folder"));
2486 renderer = gtk_cell_renderer_pixbuf_new ();
2487 gtk_tree_view_column_pack_start (column, renderer, FALSE);
2488 gtk_tree_view_column_set_attributes (column, renderer,
2489 "pixbuf", SHORTCUTS_COL_PIXBUF,
2490 "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
2493 renderer = _gtk_cell_renderer_sep_text_new ();
2494 gtk_tree_view_column_pack_start (column, renderer, TRUE);
2495 gtk_tree_view_column_set_attributes (column, renderer,
2496 "text", SHORTCUTS_COL_NAME,
2499 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), column);
2504 /* Creates the widgets for the shortcuts/bookmarks pane */
2506 shortcuts_pane_create (GtkFileChooserDefault *impl,
2507 GtkSizeGroup *size_group)
2513 vbox = gtk_vbox_new (FALSE, 6);
2514 gtk_widget_show (vbox);
2516 /* Shortcuts tree */
2518 widget = shortcuts_list_create (impl);
2519 gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
2521 /* Box for buttons */
2523 hbox = gtk_hbox_new (TRUE, 6);
2524 gtk_size_group_add_widget (size_group, hbox);
2525 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
2526 gtk_widget_show (hbox);
2528 /* Add bookmark button */
2530 impl->browse_shortcuts_add_button = button_new (impl,
2535 G_CALLBACK (add_bookmark_button_clicked_cb));
2536 gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_add_button, TRUE, TRUE, 0);
2538 /* Remove bookmark button */
2540 impl->browse_shortcuts_remove_button = button_new (impl,
2545 G_CALLBACK (remove_bookmark_button_clicked_cb));
2546 gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_remove_button, TRUE, TRUE, 0);
2552 trap_activate_cb (GtkWidget *widget,
2556 GtkFileChooserDefault *impl;
2558 impl = (GtkFileChooserDefault *) data;
2560 if (event->keyval == GDK_Return
2561 || event->keyval == GDK_ISO_Enter
2562 || event->keyval == GDK_KP_Enter
2563 || event->keyval == GDK_space)
2567 window = get_toplevel (widget);
2569 && widget != window->default_widget
2570 && !(widget == window->focus_widget &&
2571 (!window->default_widget || !GTK_WIDGET_SENSITIVE (window->default_widget))))
2572 gtk_window_activate_default (window);
2580 /* Creates the widgets for the file list */
2582 create_file_list (GtkFileChooserDefault *impl)
2585 GtkTreeSelection *selection;
2586 GtkTreeViewColumn *column;
2587 GtkCellRenderer *renderer;
2589 /* Scrolled window */
2591 swin = gtk_scrolled_window_new (NULL, NULL);
2592 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
2593 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2594 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
2597 /* Tree/list view */
2599 impl->browse_files_tree_view = gtk_tree_view_new ();
2600 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->browse_files_tree_view), TRUE);
2601 gtk_container_add (GTK_CONTAINER (swin), impl->browse_files_tree_view);
2602 g_signal_connect (impl->browse_files_tree_view, "row-activated",
2603 G_CALLBACK (list_row_activated), impl);
2604 g_signal_connect (impl->browse_files_tree_view, "key-press-event",
2605 G_CALLBACK (trap_activate_cb), impl);
2607 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2608 gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_files_tree_view),
2610 file_list_source_targets,
2611 num_file_list_source_targets,
2614 g_signal_connect (selection, "changed",
2615 G_CALLBACK (list_selection_changed), impl);
2617 /* Filename column */
2619 impl->list_name_column = gtk_tree_view_column_new ();
2620 gtk_tree_view_column_set_expand (impl->list_name_column, TRUE);
2621 gtk_tree_view_column_set_title (impl->list_name_column, _("Name"));
2622 gtk_tree_view_column_set_sort_column_id (impl->list_name_column, FILE_LIST_COL_NAME);
2624 renderer = gtk_cell_renderer_pixbuf_new ();
2625 gtk_tree_view_column_pack_start (impl->list_name_column, renderer, FALSE);
2626 gtk_tree_view_column_set_cell_data_func (impl->list_name_column, renderer,
2627 list_icon_data_func, impl, NULL);
2629 impl->list_name_renderer = gtk_cell_renderer_text_new ();
2630 g_signal_connect (impl->list_name_renderer, "edited",
2631 G_CALLBACK (renderer_edited_cb), impl);
2632 g_signal_connect (impl->list_name_renderer, "editing-canceled",
2633 G_CALLBACK (renderer_editing_canceled_cb), impl);
2634 gtk_tree_view_column_pack_start (impl->list_name_column, impl->list_name_renderer, TRUE);
2635 gtk_tree_view_column_set_cell_data_func (impl->list_name_column, impl->list_name_renderer,
2636 list_name_data_func, impl, NULL);
2638 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), impl->list_name_column);
2642 column = gtk_tree_view_column_new ();
2643 gtk_tree_view_column_set_title (column, _("Size"));
2645 renderer = gtk_cell_renderer_text_new ();
2646 gtk_tree_view_column_pack_start (column, renderer, TRUE);
2647 gtk_tree_view_column_set_cell_data_func (column, renderer,
2648 list_size_data_func, impl, NULL);
2649 gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_SIZE);
2650 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
2652 /* Modification time column */
2654 column = gtk_tree_view_column_new ();
2655 gtk_tree_view_column_set_title (column, _("Modified"));
2657 renderer = gtk_cell_renderer_text_new ();
2658 gtk_tree_view_column_pack_start (column, renderer, TRUE);
2659 gtk_tree_view_column_set_cell_data_func (column, renderer,
2660 list_mtime_data_func, impl, NULL);
2661 gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_MTIME);
2662 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
2663 gtk_widget_show_all (swin);
2669 create_filename_entry_and_filter_combo (GtkFileChooserDefault *impl)
2674 hbox = gtk_hbox_new (FALSE, 12);
2675 gtk_widget_show (hbox);
2679 widget = filter_create (impl);
2680 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
2686 create_path_bar (GtkFileChooserDefault *impl)
2688 GtkWidget *path_bar;
2690 path_bar = g_object_new (GTK_TYPE_PATH_BAR, NULL);
2691 _gtk_path_bar_set_file_system (GTK_PATH_BAR (path_bar), impl->file_system);
2696 /* Creates the widgets for the files/folders pane */
2698 file_pane_create (GtkFileChooserDefault *impl,
2699 GtkSizeGroup *size_group)
2705 vbox = gtk_vbox_new (FALSE, 6);
2706 gtk_widget_show (vbox);
2708 /* The path bar and 'Create Folder' button */
2709 hbox = gtk_hbox_new (FALSE, 12);
2710 gtk_widget_show (hbox);
2711 impl->browse_path_bar = create_path_bar (impl);
2712 g_signal_connect (impl->browse_path_bar, "path-clicked", G_CALLBACK (path_bar_clicked), impl);
2713 gtk_widget_show_all (impl->browse_path_bar);
2714 gtk_box_pack_start (GTK_BOX (hbox), impl->browse_path_bar, TRUE, TRUE, 0);
2717 impl->browse_new_folder_button = gtk_button_new_with_mnemonic (_("Create _Folder"));
2718 g_signal_connect (impl->browse_new_folder_button, "clicked",
2719 G_CALLBACK (new_folder_button_clicked), impl);
2720 gtk_box_pack_end (GTK_BOX (hbox), impl->browse_new_folder_button, FALSE, FALSE, 0);
2721 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
2723 /* Box for lists and preview */
2725 hbox = gtk_hbox_new (FALSE, PREVIEW_HBOX_SPACING);
2726 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
2727 gtk_widget_show (hbox);
2731 widget = create_file_list (impl);
2732 gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
2736 impl->preview_box = gtk_vbox_new (FALSE, 12);
2737 gtk_box_pack_start (GTK_BOX (hbox), impl->preview_box, FALSE, FALSE, 0);
2738 /* Don't show preview box initially */
2740 /* Filename entry and filter combo */
2741 hbox = gtk_hbox_new (FALSE, 0);
2742 gtk_size_group_add_widget (size_group, hbox);
2743 widget = create_filename_entry_and_filter_combo (impl);
2744 gtk_box_pack_end (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
2745 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
2746 gtk_widget_show (hbox);
2750 /* Callback used when the "Browse for more folders" expander is toggled */
2752 expander_changed_cb (GtkExpander *expander,
2754 GtkFileChooserDefault *impl)
2756 update_appearance (impl);
2759 /* Callback used when the selection changes in the save folder combo box */
2761 save_folder_combo_changed_cb (GtkComboBox *combo,
2762 GtkFileChooserDefault *impl)
2766 if (impl->changing_folder)
2769 active = gtk_combo_box_get_active (combo);
2773 shortcuts_activate_item (impl, active);
2776 /* Creates the combo box with the save folders */
2778 save_folder_combo_create (GtkFileChooserDefault *impl)
2781 GtkCellRenderer *cell;
2783 combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (impl->shortcuts_model));
2784 gtk_widget_show (combo);
2786 cell = gtk_cell_renderer_pixbuf_new ();
2787 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE);
2788 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
2789 "pixbuf", SHORTCUTS_COL_PIXBUF,
2790 "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
2793 cell = _gtk_cell_renderer_sep_text_new ();
2794 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
2795 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
2796 "text", SHORTCUTS_COL_NAME,
2799 g_signal_connect (combo, "changed",
2800 G_CALLBACK (save_folder_combo_changed_cb), impl);
2805 /* Creates the widgets specific to Save mode */
2807 save_widgets_create (GtkFileChooserDefault *impl)
2812 GtkWidget *alignment;
2814 vbox = gtk_vbox_new (FALSE, 12);
2816 table = gtk_table_new (2, 2, FALSE);
2817 gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
2818 gtk_widget_show (table);
2819 gtk_table_set_row_spacings (GTK_TABLE (table), 12);
2820 gtk_table_set_col_spacings (GTK_TABLE (table), 12);
2824 widget = gtk_label_new_with_mnemonic (_("_Name:"));
2825 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
2826 gtk_table_attach (GTK_TABLE (table), widget,
2830 gtk_widget_show (widget);
2832 impl->save_file_name_entry = gtk_entry_new ();
2833 gtk_entry_set_width_chars (GTK_ENTRY (impl->save_file_name_entry), 45);
2834 gtk_entry_set_activates_default (GTK_ENTRY (impl->save_file_name_entry), TRUE);
2835 gtk_table_attach (GTK_TABLE (table), impl->save_file_name_entry,
2837 GTK_EXPAND | GTK_FILL, 0,
2839 gtk_widget_show (impl->save_file_name_entry);
2840 gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->save_file_name_entry);
2843 impl->save_folder_label = gtk_label_new (NULL);
2844 gtk_misc_set_alignment (GTK_MISC (impl->save_folder_label), 0.0, 0.5);
2845 gtk_table_attach (GTK_TABLE (table), impl->save_folder_label,
2849 gtk_widget_show (impl->save_folder_label);
2851 impl->save_folder_combo = save_folder_combo_create (impl);
2852 gtk_table_attach (GTK_TABLE (table), impl->save_folder_combo,
2854 GTK_EXPAND | GTK_FILL, GTK_FILL,
2856 gtk_label_set_mnemonic_widget (GTK_LABEL (impl->save_folder_label), impl->save_folder_combo);
2859 impl->save_extra_align = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
2860 gtk_box_pack_start (GTK_BOX (vbox), impl->save_extra_align, FALSE, FALSE, 0);
2863 alignment = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
2864 gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
2866 impl->save_expander = gtk_expander_new_with_mnemonic (_("_Browse for other folders"));
2867 gtk_container_add (GTK_CONTAINER (alignment), impl->save_expander);
2868 g_signal_connect (impl->save_expander, "notify::expanded",
2869 G_CALLBACK (expander_changed_cb),
2871 gtk_widget_show_all (alignment);
2876 /* Creates the main hpaned with the widgets shared by Open and Save mode */
2878 browse_widgets_create (GtkFileChooserDefault *impl)
2883 GtkSizeGroup *size_group;
2885 /* size group is used by the [+][-] buttons and the filter combo */
2886 size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
2887 vbox = gtk_vbox_new (FALSE, 12);
2890 hpaned = gtk_hpaned_new ();
2891 gtk_widget_show (hpaned);
2892 gtk_paned_set_position (GTK_PANED (hpaned), 200); /* FIXME: this sucks */
2893 gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
2895 widget = shortcuts_pane_create (impl, size_group);
2896 gtk_paned_pack1 (GTK_PANED (hpaned), widget, FALSE, FALSE);
2897 widget = file_pane_create (impl, size_group);
2898 gtk_paned_pack2 (GTK_PANED (hpaned), widget, TRUE, FALSE);
2900 g_object_unref (size_group);
2902 /* Alignment to hold custom widget */
2903 impl->browse_extra_align = gtk_alignment_new (0.0, .5, 1.0, 1.0);
2904 gtk_box_pack_start (GTK_BOX (vbox), impl->browse_extra_align, FALSE, FALSE, 0);
2910 gtk_file_chooser_default_constructor (GType type,
2911 guint n_construct_properties,
2912 GObjectConstructParam *construct_params)
2914 GtkFileChooserDefault *impl;
2917 object = parent_class->constructor (type,
2918 n_construct_properties,
2920 impl = GTK_FILE_CHOOSER_DEFAULT (object);
2922 g_assert (impl->file_system);
2924 gtk_widget_push_composite_child ();
2926 /* Shortcuts model */
2928 shortcuts_model_create (impl);
2930 /* Widgets for Save mode */
2931 impl->save_widgets = save_widgets_create (impl);
2932 gtk_box_pack_start (GTK_BOX (impl), impl->save_widgets, FALSE, FALSE, 0);
2934 /* The browse widgets */
2935 impl->browse_widgets = browse_widgets_create (impl);
2936 gtk_box_pack_start (GTK_BOX (impl), impl->browse_widgets, TRUE, TRUE, 0);
2938 gtk_widget_pop_composite_child ();
2939 update_appearance (impl);
2944 /* Sets the extra_widget by packing it in the appropriate place */
2946 set_extra_widget (GtkFileChooserDefault *impl,
2947 GtkWidget *extra_widget)
2951 g_object_ref (extra_widget);
2952 /* FIXME: is this right ? */
2953 gtk_widget_show (extra_widget);
2956 if (impl->extra_widget)
2957 g_object_unref (impl->extra_widget);
2959 impl->extra_widget = extra_widget;
2963 set_local_only (GtkFileChooserDefault *impl,
2964 gboolean local_only)
2966 if (local_only != impl->local_only)
2968 impl->local_only = local_only;
2970 if (impl->shortcuts_model && impl->file_system)
2972 shortcuts_add_volumes (impl);
2973 shortcuts_add_bookmarks (impl);
2977 !gtk_file_system_path_is_local (impl->file_system, impl->current_folder))
2979 /* If we are pointing to a non-local folder, make an effort to change
2980 * back to a local folder, but it's really up to the app to not cause
2981 * such a situation, so we ignore errors.
2983 const gchar *home = g_get_home_dir ();
2984 GtkFilePath *home_path = gtk_file_system_filename_to_path (impl->file_system, home);
2986 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), home_path, NULL);
2988 gtk_file_path_free (home_path);
2994 volumes_changed_cb (GtkFileSystem *file_system,
2995 GtkFileChooserDefault *impl)
2997 shortcuts_add_volumes (impl);
3000 /* Callback used when the set of bookmarks changes in the file system */
3002 bookmarks_changed_cb (GtkFileSystem *file_system,
3003 GtkFileChooserDefault *impl)
3005 shortcuts_add_bookmarks (impl);
3007 bookmarks_check_add_sensitivity (impl);
3008 bookmarks_check_remove_sensitivity (impl);
3011 /* Sets the file chooser to multiple selection mode */
3013 set_select_multiple (GtkFileChooserDefault *impl,
3014 gboolean select_multiple,
3015 gboolean property_notify)
3017 GtkTreeSelection *selection;
3018 GtkSelectionMode mode;
3020 if (select_multiple == impl->select_multiple)
3023 mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE;
3025 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3026 gtk_tree_selection_set_mode (selection, mode);
3028 impl->select_multiple = select_multiple;
3029 g_object_notify (G_OBJECT (impl), "select-multiple");
3031 /* FIXME #132255: See note in check_preview_change() */
3032 check_preview_change (impl);
3036 set_file_system_backend (GtkFileChooserDefault *impl,
3037 const char *backend)
3039 if (impl->file_system)
3041 g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
3042 impl->volumes_changed_id = 0;
3043 g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
3044 impl->bookmarks_changed_id = 0;
3045 g_object_unref (impl->file_system);
3048 impl->file_system = NULL;
3050 impl->file_system = _gtk_file_system_create (backend);
3053 GtkSettings *settings = gtk_settings_get_default ();
3054 gchar *default_backend = NULL;
3056 g_object_get (settings, "gtk-file-chooser-backend", &default_backend, NULL);
3057 if (default_backend)
3059 impl->file_system = _gtk_file_system_create (default_backend);
3060 g_free (default_backend);
3064 if (!impl->file_system)
3066 #if defined (G_OS_UNIX)
3067 impl->file_system = gtk_file_system_unix_new ();
3068 #elif defined (G_OS_WIN32)
3069 impl->file_system = gtk_file_system_win32_new ();
3071 #error "No default filesystem implementation on the platform"
3075 if (impl->file_system)
3077 impl->volumes_changed_id = g_signal_connect (impl->file_system, "volumes-changed",
3078 G_CALLBACK (volumes_changed_cb),
3080 impl->bookmarks_changed_id = g_signal_connect (impl->file_system, "bookmarks-changed",
3081 G_CALLBACK (bookmarks_changed_cb),
3086 /* This function is basically a do_all function.
3088 * It sets the visibility on all the widgets based on the current state, and
3089 * moves the custom_widget if needed.
3092 update_appearance (GtkFileChooserDefault *impl)
3096 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
3097 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
3101 gtk_widget_show (impl->save_widgets);
3103 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
3104 text = _("Save in _folder:");
3106 text = _("Create in _folder:");
3108 gtk_label_set_text_with_mnemonic (GTK_LABEL (impl->save_folder_label), text);
3110 if (gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
3112 gtk_widget_set_sensitive (impl->save_folder_label, FALSE);
3113 gtk_widget_set_sensitive (impl->save_folder_combo, FALSE);
3114 gtk_widget_show (impl->browse_widgets);
3118 gtk_widget_set_sensitive (impl->save_folder_label, TRUE);
3119 gtk_widget_set_sensitive (impl->save_folder_combo, TRUE);
3120 gtk_widget_hide (impl->browse_widgets);
3123 gtk_widget_show (impl->browse_new_folder_button);
3125 if (impl->select_multiple)
3127 g_warning ("Save mode cannot be set in conjunction with multiple selection mode. "
3128 "Re-setting to single selection mode.");
3129 set_select_multiple (impl, FALSE, TRUE);
3132 else if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
3133 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
3135 gtk_widget_hide (impl->save_widgets);
3136 gtk_widget_show (impl->browse_widgets);
3139 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
3140 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
3142 if (impl->browse_files_model)
3143 _gtk_file_system_model_set_show_files (impl->browse_files_model, FALSE);
3147 if (impl->browse_files_model)
3148 _gtk_file_system_model_set_show_files (impl->browse_files_model, TRUE);
3151 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
3152 gtk_widget_hide (impl->browse_new_folder_button);
3154 gtk_widget_show (impl->browse_new_folder_button);
3156 if (impl->extra_widget)
3159 GtkWidget *unused_align;
3161 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
3162 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
3164 align = impl->save_extra_align;
3165 unused_align = impl->browse_extra_align;
3169 align = impl->browse_extra_align;
3170 unused_align = impl->save_extra_align;
3173 /* We own a ref on extra_widget, so it's safe to do this */
3174 child = GTK_BIN (unused_align)->child;
3176 gtk_container_remove (GTK_CONTAINER (unused_align), child);
3178 child = GTK_BIN (align)->child;
3179 if (child && child != impl->extra_widget)
3181 gtk_container_remove (GTK_CONTAINER (align), child);
3182 gtk_container_add (GTK_CONTAINER (align), impl->extra_widget);
3184 else if (child == NULL)
3186 gtk_container_add (GTK_CONTAINER (align), impl->extra_widget);
3189 gtk_widget_show (align);
3190 gtk_widget_hide (unused_align);
3194 child = GTK_BIN (impl->browse_extra_align)->child;
3196 gtk_container_remove (GTK_CONTAINER (impl->browse_extra_align), child);
3198 child = GTK_BIN (impl->save_extra_align)->child;
3200 gtk_container_remove (GTK_CONTAINER (impl->save_extra_align), child);
3202 gtk_widget_hide (impl->save_extra_align);
3203 gtk_widget_hide (impl->browse_extra_align);
3206 g_signal_emit_by_name (impl, "default-size-changed");
3210 gtk_file_chooser_default_set_property (GObject *object,
3212 const GValue *value,
3216 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
3220 case GTK_FILE_CHOOSER_PROP_ACTION:
3222 GtkFileChooserAction action = g_value_get_enum (value);
3224 if (action != impl->action)
3226 if (action == GTK_FILE_CHOOSER_ACTION_SAVE && impl->select_multiple)
3228 g_warning ("Multiple selection mode is not allowed in Save mode");
3229 set_select_multiple (impl, FALSE, TRUE);
3231 impl->action = action;
3232 update_appearance (impl);
3236 case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
3237 set_file_system_backend (impl, g_value_get_string (value));
3239 case GTK_FILE_CHOOSER_PROP_FILTER:
3240 set_current_filter (impl, g_value_get_object (value));
3242 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
3243 set_local_only (impl, g_value_get_boolean (value));
3245 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
3246 set_preview_widget (impl, g_value_get_object (value));
3248 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
3249 impl->preview_widget_active = g_value_get_boolean (value);
3250 update_preview_widget_visibility (impl);
3252 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
3253 impl->use_preview_label = g_value_get_boolean (value);
3254 update_preview_widget_visibility (impl);
3256 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
3257 set_extra_widget (impl, g_value_get_object (value));
3258 update_appearance (impl);
3260 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
3262 gboolean select_multiple = g_value_get_boolean (value);
3263 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE && select_multiple)
3265 g_warning ("Multiple selection mode is not allowed in Save mode");
3269 set_select_multiple (impl, select_multiple, FALSE);
3272 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
3274 gboolean show_hidden = g_value_get_boolean (value);
3275 if (show_hidden != impl->show_hidden)
3277 impl->show_hidden = show_hidden;
3278 _gtk_file_system_model_set_show_hidden (GTK_FILE_SYSTEM_MODEL (impl->browse_files_model),
3284 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3290 gtk_file_chooser_default_get_property (GObject *object,
3295 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
3299 case GTK_FILE_CHOOSER_PROP_ACTION:
3300 g_value_set_enum (value, impl->action);
3302 case GTK_FILE_CHOOSER_PROP_FILTER:
3303 g_value_set_object (value, impl->current_filter);
3305 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
3306 g_value_set_boolean (value, impl->local_only);
3308 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
3309 g_value_set_object (value, impl->preview_widget);
3311 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
3312 g_value_set_boolean (value, impl->preview_widget_active);
3314 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
3315 g_value_set_boolean (value, impl->use_preview_label);
3317 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
3318 g_value_set_object (value, impl->extra_widget);
3320 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
3321 g_value_set_boolean (value, impl->select_multiple);
3323 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
3324 g_value_set_boolean (value, impl->show_hidden);
3327 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3332 /* Removes the settings signal handler. It's safe to call multiple times */
3334 remove_settings_signal (GtkFileChooserDefault *impl,
3337 if (impl->settings_signal_id)
3339 GtkSettings *settings;
3341 settings = gtk_settings_get_for_screen (screen);
3342 g_signal_handler_disconnect (settings,
3343 impl->settings_signal_id);
3344 impl->settings_signal_id = 0;
3349 gtk_file_chooser_default_dispose (GObject *object)
3351 GtkFileChooserDefault *impl = (GtkFileChooserDefault *) object;
3353 if (impl->extra_widget)
3355 g_object_unref (impl->extra_widget);
3356 impl->extra_widget = NULL;
3359 remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl)));
3361 G_OBJECT_CLASS (parent_class)->dispose (object);
3364 /* We override show-all since we have internal widgets that
3365 * shouldn't be shown when you call show_all(), like the filter
3369 gtk_file_chooser_default_show_all (GtkWidget *widget)
3371 GtkFileChooserDefault *impl = (GtkFileChooserDefault *) widget;
3373 gtk_widget_show (widget);
3375 if (impl->extra_widget)
3376 gtk_widget_show_all (impl->extra_widget);
3379 /* Changes the icons wherever it is needed */
3381 change_icon_theme (GtkFileChooserDefault *impl)
3383 GtkSettings *settings;
3386 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
3388 if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_SMALL_TOOLBAR, &width, &height))
3389 impl->icon_size = MAX (width, height);
3391 impl->icon_size = FALLBACK_ICON_SIZE;
3393 shortcuts_reload_icons (impl);
3394 gtk_widget_queue_resize (impl->browse_files_tree_view);
3397 /* Callback used when a GtkSettings value changes */
3399 settings_notify_cb (GObject *object,
3401 GtkFileChooserDefault *impl)
3405 name = g_param_spec_get_name (pspec);
3407 if (strcmp (name, "gtk-icon-theme-name") == 0
3408 || strcmp (name, "gtk-icon-sizes") == 0)
3409 change_icon_theme (impl);
3412 /* Installs a signal handler for GtkSettings so that we can monitor changes in
3416 check_icon_theme (GtkFileChooserDefault *impl)
3418 GtkSettings *settings;
3420 if (impl->settings_signal_id)
3423 if (gtk_widget_has_screen (GTK_WIDGET (impl)))
3425 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
3426 impl->settings_signal_id = g_signal_connect (settings, "notify",
3427 G_CALLBACK (settings_notify_cb), impl);
3429 change_icon_theme (impl);
3434 gtk_file_chooser_default_style_set (GtkWidget *widget,
3435 GtkStyle *previous_style)
3437 GtkFileChooserDefault *impl;
3439 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
3441 if (GTK_WIDGET_CLASS (parent_class)->style_set)
3442 GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
3444 check_icon_theme (impl);
3446 g_signal_emit_by_name (widget, "default-size-changed");
3450 gtk_file_chooser_default_screen_changed (GtkWidget *widget,
3451 GdkScreen *previous_screen)
3453 GtkFileChooserDefault *impl;
3455 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
3457 if (GTK_WIDGET_CLASS (parent_class)->screen_changed)
3458 GTK_WIDGET_CLASS (parent_class)->screen_changed (widget, previous_screen);
3460 remove_settings_signal (impl, previous_screen);
3461 check_icon_theme (impl);
3463 g_signal_emit_by_name (widget, "default-size-changed");
3467 list_model_filter_func (GtkFileSystemModel *model,
3469 const GtkFileInfo *file_info,
3472 GtkFileChooserDefault *impl = user_data;
3473 GtkFileFilterInfo filter_info;
3474 GtkFileFilterFlags needed;
3477 if (!impl->current_filter)
3480 if (gtk_file_info_get_is_folder (file_info))
3483 filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
3485 needed = gtk_file_filter_get_needed (impl->current_filter);
3487 filter_info.display_name = gtk_file_info_get_display_name (file_info);
3488 filter_info.mime_type = gtk_file_info_get_mime_type (file_info);
3490 if (needed & GTK_FILE_FILTER_FILENAME)
3492 filter_info.filename = gtk_file_system_path_to_filename (impl->file_system, path);
3493 if (filter_info.filename)
3494 filter_info.contains |= GTK_FILE_FILTER_FILENAME;
3497 filter_info.filename = NULL;
3499 if (needed & GTK_FILE_FILTER_URI)
3501 filter_info.uri = gtk_file_system_path_to_uri (impl->file_system, path);
3502 if (filter_info.uri)
3503 filter_info.contains |= GTK_FILE_FILTER_URI;
3506 filter_info.uri = NULL;
3508 result = gtk_file_filter_filter (impl->current_filter, &filter_info);
3510 if (filter_info.filename)
3511 g_free ((gchar *)filter_info.filename);
3512 if (filter_info.uri)
3513 g_free ((gchar *)filter_info.uri);
3519 install_list_model_filter (GtkFileChooserDefault *impl)
3521 if (impl->current_filter)
3522 _gtk_file_system_model_set_filter (impl->browse_files_model,
3523 list_model_filter_func,
3527 #define COMPARE_DIRECTORIES \
3528 GtkFileChooserDefault *impl = user_data; \
3529 const GtkFileInfo *info_a = _gtk_file_system_model_get_info (impl->browse_files_model, a); \
3530 const GtkFileInfo *info_b = _gtk_file_system_model_get_info (impl->browse_files_model, b); \
3531 gboolean dir_a, dir_b; \
3534 dir_a = gtk_file_info_get_is_folder (info_a); \
3536 return impl->list_sort_ascending ? -1 : 1; \
3539 dir_b = gtk_file_info_get_is_folder (info_b); \
3541 return impl->list_sort_ascending ? 1 : -1; \
3543 if (dir_a != dir_b) \
3544 return impl->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */
3546 /* Sort callback for the filename column */
3548 name_sort_func (GtkTreeModel *model,
3553 COMPARE_DIRECTORIES;
3555 return strcmp (gtk_file_info_get_display_key (info_a), gtk_file_info_get_display_key (info_b));
3558 /* Sort callback for the size column */
3560 size_sort_func (GtkTreeModel *model,
3565 COMPARE_DIRECTORIES;
3568 gint64 size_a = gtk_file_info_get_size (info_a);
3569 gint64 size_b = gtk_file_info_get_size (info_b);
3571 return size_a > size_b ? -1 : (size_a == size_b ? 0 : 1);
3575 /* Sort callback for the mtime column */
3577 mtime_sort_func (GtkTreeModel *model,
3582 COMPARE_DIRECTORIES;
3585 GtkFileTime ta = gtk_file_info_get_modification_time (info_a);
3586 GtkFileTime tb = gtk_file_info_get_modification_time (info_b);
3588 return ta > tb ? -1 : (ta == tb ? 0 : 1);
3592 /* Callback used when the sort column changes. We cache the sort order for use
3593 * in name_sort_func().
3596 list_sort_column_changed_cb (GtkTreeSortable *sortable,
3597 GtkFileChooserDefault *impl)
3599 GtkSortType sort_type;
3601 if (gtk_tree_sortable_get_sort_column_id (sortable, NULL, &sort_type))
3602 impl->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
3606 set_busy_cursor (GtkFileChooserDefault *impl,
3609 GtkWindow *toplevel;
3610 GdkDisplay *display;
3613 toplevel = get_toplevel (GTK_WIDGET (impl));
3614 if (!toplevel || !GTK_WIDGET_REALIZED (toplevel))
3617 display = gtk_widget_get_display (GTK_WIDGET (toplevel));
3620 cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
3624 gdk_window_set_cursor (GTK_WIDGET (toplevel)->window, cursor);
3625 gdk_display_flush (display);
3628 gdk_cursor_unref (cursor);
3631 /* Callback used when the file system model finishes loading */
3633 browse_files_model_finished_loading_cb (GtkFileSystemModel *model,
3634 GtkFileChooserDefault *impl)
3636 set_busy_cursor (impl, FALSE);
3639 /* Gets rid of the old list model and creates a new one for the current folder */
3641 set_list_model (GtkFileChooserDefault *impl)
3643 if (impl->browse_files_model)
3645 g_object_unref (impl->browse_files_model);
3646 g_object_unref (impl->sort_model);
3649 set_busy_cursor (impl, TRUE);
3651 impl->browse_files_model = _gtk_file_system_model_new (impl->file_system,
3652 impl->current_folder, 0,
3654 g_signal_connect (impl->browse_files_model, "finished-loading",
3655 G_CALLBACK (browse_files_model_finished_loading_cb), impl);
3657 _gtk_file_system_model_set_show_hidden (impl->browse_files_model, impl->show_hidden);
3658 switch (impl->action)
3660 case GTK_FILE_CHOOSER_ACTION_OPEN:
3661 case GTK_FILE_CHOOSER_ACTION_SAVE:
3662 _gtk_file_system_model_set_show_files (impl->browse_files_model, TRUE);
3664 case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
3665 case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
3666 _gtk_file_system_model_set_show_files (impl->browse_files_model, FALSE);
3669 g_assert_not_reached ();
3671 install_list_model_filter (impl);
3673 impl->sort_model = (GtkTreeModelSort *)gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (impl->browse_files_model));
3674 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, name_sort_func, impl, NULL);
3675 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_SIZE, size_sort_func, impl, NULL);
3676 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_MTIME, mtime_sort_func, impl, NULL);
3677 gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->sort_model), NULL, NULL, NULL);
3678 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, GTK_SORT_ASCENDING);
3679 impl->list_sort_ascending = TRUE;
3681 g_signal_connect (impl->sort_model, "sort-column-changed",
3682 G_CALLBACK (list_sort_column_changed_cb), impl);
3684 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
3685 GTK_TREE_MODEL (impl->sort_model));
3686 gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->browse_files_tree_view));
3687 gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_files_tree_view),
3688 GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
3692 update_chooser_entry (GtkFileChooserDefault *impl)
3694 GtkTreeSelection *selection;
3695 const GtkFileInfo *info;
3697 GtkTreeIter child_iter;
3699 if (impl->action != GTK_FILE_CHOOSER_ACTION_SAVE)
3702 g_assert (!impl->select_multiple);
3703 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3705 if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
3708 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
3712 info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
3714 if (!gtk_file_info_get_is_folder (info))
3715 gtk_entry_set_text (GTK_ENTRY (impl->save_file_name_entry),
3716 gtk_file_info_get_display_name (info));
3720 gtk_file_chooser_default_set_current_folder (GtkFileChooser *chooser,
3721 const GtkFilePath *path,
3724 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3726 /* Test validity of path here. */
3727 if (!check_is_folder (impl->file_system, path, error))
3730 if (impl->local_only &&
3731 !gtk_file_system_path_is_local (impl->file_system, path))
3734 GTK_FILE_SYSTEM_ERROR,
3735 GTK_FILE_SYSTEM_ERROR_FAILED,
3736 _("Can't change to folder because it isn't local"));
3741 if (!_gtk_path_bar_set_path (GTK_PATH_BAR (impl->browse_path_bar), path, error))
3744 if (impl->current_folder != path)
3746 if (impl->current_folder)
3747 gtk_file_path_free (impl->current_folder);
3749 impl->current_folder = gtk_file_path_copy (path);
3752 /* Update the widgets that may trigger a folder change themselves. */
3754 if (!impl->changing_folder)
3756 impl->changing_folder = TRUE;
3758 shortcuts_update_current_folder (impl);
3760 impl->changing_folder = FALSE;
3763 /* Create a new list model */
3764 set_list_model (impl);
3766 /* Refresh controls */
3768 shortcuts_find_current_folder (impl);
3770 g_signal_emit_by_name (impl, "current-folder-changed", 0);
3772 check_preview_change (impl);
3773 bookmarks_check_add_sensitivity (impl);
3775 g_signal_emit_by_name (impl, "selection-changed", 0);
3780 static GtkFilePath *
3781 gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser)
3783 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3785 return gtk_file_path_copy (impl->current_folder);
3789 gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
3792 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3794 g_return_if_fail (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
3795 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
3797 gtk_entry_set_text (GTK_ENTRY (impl->save_file_name_entry), name);
3801 select_func (GtkFileSystemModel *model,
3806 GtkFileChooserDefault *impl = user_data;
3807 GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
3808 GtkTreePath *sorted_path;
3810 sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model, path);
3811 gtk_tree_view_set_cursor (tree_view, sorted_path, NULL, FALSE);
3812 gtk_tree_path_free (sorted_path);
3816 gtk_file_chooser_default_select_path (GtkFileChooser *chooser,
3817 const GtkFilePath *path,
3820 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3821 GtkFilePath *parent_path;
3823 if (!gtk_file_system_get_parent (impl->file_system, path, &parent_path, error))
3827 return _gtk_file_chooser_set_current_folder_path (chooser, path, error);
3832 result = _gtk_file_chooser_set_current_folder_path (chooser, parent_path, error);
3833 gtk_file_path_free (parent_path);
3834 _gtk_file_system_model_path_do (impl->browse_files_model, path,
3840 g_assert_not_reached ();
3844 unselect_func (GtkFileSystemModel *model,
3849 GtkFileChooserDefault *impl = user_data;
3850 GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
3851 GtkTreePath *sorted_path;
3853 sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model,
3855 gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (tree_view),
3857 gtk_tree_path_free (sorted_path);
3861 gtk_file_chooser_default_unselect_path (GtkFileChooser *chooser,
3862 const GtkFilePath *path)
3864 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3866 _gtk_file_system_model_path_do (impl->browse_files_model, path,
3867 unselect_func, impl);
3871 gtk_file_chooser_default_select_all (GtkFileChooser *chooser)
3873 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3874 if (impl->select_multiple)
3876 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3877 gtk_tree_selection_select_all (selection);
3882 gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser)
3884 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3885 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3887 gtk_tree_selection_unselect_all (selection);
3890 /* Checks whether the filename entry for the Save modes contains a valid filename */
3891 static GtkFilePath *
3892 check_save_entry (GtkFileChooserDefault *impl,
3896 const char *filename;
3900 g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
3901 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
3903 filename = gtk_entry_get_text (GTK_ENTRY (impl->save_file_name_entry));
3905 if (!filename || filename[0] == '\0')
3915 path = gtk_file_system_make_path (impl->file_system, impl->current_folder, filename, &error);
3919 error_building_filename_dialog (impl, impl->current_folder, filename, error);
3928 struct get_paths_closure {
3929 GtkFileChooserDefault *impl;
3931 GtkFilePath *path_from_entry;
3935 get_paths_foreach (GtkTreeModel *model,
3940 struct get_paths_closure *info;
3941 const GtkFilePath *file_path;
3942 GtkFileSystemModel *fs_model;
3943 GtkTreeIter sel_iter;
3946 fs_model = info->impl->browse_files_model;
3947 gtk_tree_model_sort_convert_iter_to_child_iter (info->impl->sort_model, &sel_iter, iter);
3949 file_path = _gtk_file_system_model_get_path (GTK_FILE_SYSTEM_MODEL (fs_model), &sel_iter);
3951 return; /* We are on the editable row */
3953 if (!info->path_from_entry
3954 || gtk_file_path_compare (info->path_from_entry, file_path) != 0)
3955 info->result = g_slist_prepend (info->result, gtk_file_path_copy (file_path));
3959 gtk_file_chooser_default_get_paths (GtkFileChooser *chooser)
3961 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
3962 struct get_paths_closure info;
3966 info.path_from_entry = NULL;
3968 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
3969 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
3971 gboolean is_valid, is_empty;
3973 info.path_from_entry = check_save_entry (impl, &is_valid, &is_empty);
3974 if (!is_valid && !is_empty)
3978 if (!info.path_from_entry || impl->select_multiple)
3980 GtkTreeSelection *selection;
3982 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3983 gtk_tree_selection_selected_foreach (selection, get_paths_foreach, &info);
3986 if (info.path_from_entry)
3987 info.result = g_slist_prepend (info.result, info.path_from_entry);
3989 /* If there's no folder selected, and we're in SELECT_FOLDER mode, then we
3990 * fall back to the current directory */
3991 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
3992 info.result == NULL)
3994 info.result = g_slist_prepend (info.result, gtk_file_path_copy (impl->current_folder));
3997 return g_slist_reverse (info.result);
4000 static GtkFilePath *
4001 gtk_file_chooser_default_get_preview_path (GtkFileChooser *chooser)
4003 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4005 if (impl->preview_path)
4006 return gtk_file_path_copy (impl->preview_path);
4011 static GtkFileSystem *
4012 gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser)
4014 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4016 return impl->file_system;
4019 /* Shows or hides the filter widgets */
4021 toolbar_show_filters (GtkFileChooserDefault *impl,
4025 gtk_widget_show (impl->filter_combo);
4027 gtk_widget_hide (impl->filter_combo);
4031 gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
4032 GtkFileFilter *filter)
4034 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4037 if (g_slist_find (impl->filters, filter))
4039 g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
4043 g_object_ref (filter);
4044 gtk_object_sink (GTK_OBJECT (filter));
4045 impl->filters = g_slist_append (impl->filters, filter);
4047 name = gtk_file_filter_get_name (filter);
4049 name = "Untitled filter"; /* Place-holder, doesn't need to be marked for translation */
4051 gtk_combo_box_append_text (GTK_COMBO_BOX (impl->filter_combo), name);
4053 if (!g_slist_find (impl->filters, impl->current_filter))
4054 set_current_filter (impl, filter);
4056 toolbar_show_filters (impl, TRUE);
4060 gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
4061 GtkFileFilter *filter)
4063 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4064 GtkTreeModel *model;
4068 filter_index = g_slist_index (impl->filters, filter);
4070 if (filter_index < 0)
4072 g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
4076 impl->filters = g_slist_remove (impl->filters, filter);
4078 if (filter == impl->current_filter)
4081 set_current_filter (impl, impl->filters->data);
4083 set_current_filter (impl, NULL);
4086 /* Remove row from the combo box */
4087 model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->filter_combo));
4088 gtk_tree_model_iter_nth_child (model, &iter, NULL, filter_index);
4089 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
4091 g_object_unref (filter);
4094 toolbar_show_filters (impl, FALSE);
4098 gtk_file_chooser_default_list_filters (GtkFileChooser *chooser)
4100 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4102 return g_slist_copy (impl->filters);
4105 /* Returns the position in the shortcuts tree where the nth specified shortcut would appear */
4107 shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl,
4110 return pos + shortcuts_get_index (impl, SHORTCUTS_SHORTCUTS);
4114 gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser *chooser,
4115 const GtkFilePath *path,
4118 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4122 /* Test validity of path here. */
4123 if (!check_is_folder (impl->file_system, path, error))
4126 pos = shortcuts_get_pos_for_shortcut_folder (impl, impl->num_shortcuts);
4128 result = shortcuts_insert_path (impl, pos, FALSE, NULL, path, NULL, FALSE, error);
4131 impl->num_shortcuts++;
4133 if (impl->shortcuts_filter_model)
4134 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
4140 gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser *chooser,
4141 const GtkFilePath *path,
4144 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4150 if (impl->num_shortcuts == 0)
4153 pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
4154 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
4155 g_assert_not_reached ();
4157 for (i = 0; i < impl->num_shortcuts; i++)
4159 GtkFilePath *shortcut;
4161 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &shortcut, -1);
4162 g_assert (shortcut != NULL);
4164 if (gtk_file_path_compare (shortcut, path) == 0)
4166 /* The other columns are freed by the GtkTreeStore */
4167 gtk_file_path_free (shortcut);
4168 gtk_list_store_remove (impl->shortcuts_model, &iter);
4169 impl->num_shortcuts--;
4173 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
4174 g_assert_not_reached ();
4179 uri = gtk_file_system_path_to_uri (impl->file_system, path);
4181 GTK_FILE_CHOOSER_ERROR,
4182 GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
4183 _("shortcut %s does not exist"),
4191 gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser)
4193 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4199 if (impl->num_shortcuts == 0)
4202 pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
4203 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
4204 g_assert_not_reached ();
4208 for (i = 0; i < impl->num_shortcuts; i++)
4210 GtkFilePath *shortcut;
4212 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &shortcut, -1);
4213 g_assert (shortcut != NULL);
4215 list = g_slist_prepend (list, gtk_file_path_copy (shortcut));
4217 if (i != impl->num_shortcuts - 1)
4219 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
4220 g_assert_not_reached ();
4224 return g_slist_reverse (list);
4227 /* Guesses a size based upon font sizes */
4229 find_good_size_from_style (GtkWidget *widget,
4233 GtkFileChooserDefault *impl;
4234 gint default_width, default_height;
4237 GtkRequisition preview_req;
4239 g_assert (widget->style != NULL);
4240 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
4242 font_size = pango_font_description_get_size (widget->style->font_desc);
4243 font_size = PANGO_PIXELS (font_size);
4245 default_width = font_size * NUM_CHARS;
4246 default_height = font_size * NUM_LINES;
4248 /* Use at least the requisition size not including the preview widget */
4249 gtk_widget_size_request (widget, &req);
4251 if (impl->preview_widget_active && impl->preview_widget)
4252 gtk_widget_size_request (impl->preview_box, &preview_req);
4254 preview_req.width = 0;
4256 default_width = MAX (default_width, (req.width - (preview_req.width + PREVIEW_HBOX_SPACING)));
4257 default_height = MAX (default_height, req.height);
4259 *width = default_width;
4260 *height = default_height;
4264 gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
4265 gint *default_width,
4266 gint *default_height)
4268 GtkFileChooserDefault *impl;
4270 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
4272 find_good_size_from_style (GTK_WIDGET (chooser_embed), default_width, default_height);
4274 if (impl->preview_widget_active && impl->preview_widget)
4275 *default_width += impl->preview_box->requisition.width + PREVIEW_HBOX_SPACING;
4279 gtk_file_chooser_default_get_resizable_hints (GtkFileChooserEmbed *chooser_embed,
4280 gboolean *resize_horizontally,
4281 gboolean *resize_vertically)
4283 GtkFileChooserDefault *impl;
4285 g_return_if_fail (resize_horizontally != NULL);
4286 g_return_if_fail (resize_vertically != NULL);
4288 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
4290 *resize_horizontally = TRUE;
4291 *resize_vertically = TRUE;
4293 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
4294 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4296 if (! gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
4298 *resize_horizontally = FALSE;
4299 *resize_vertically = FALSE;
4304 struct switch_folder_closure {
4305 GtkFileChooserDefault *impl;
4306 const GtkFilePath *path;
4310 /* Used from gtk_tree_selection_selected_foreach() in switch_to_selected_folder() */
4312 switch_folder_foreach_cb (GtkTreeModel *model,
4317 struct switch_folder_closure *closure;
4318 GtkTreeIter child_iter;
4322 gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
4324 closure->path = _gtk_file_system_model_get_path (closure->impl->browse_files_model, &child_iter);
4325 closure->num_selected++;
4328 /* Changes to the selected folder in the list view */
4330 switch_to_selected_folder (GtkFileChooserDefault *impl)
4332 GtkTreeSelection *selection;
4333 struct switch_folder_closure closure;
4335 g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
4336 || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE);
4338 /* We do this with foreach() rather than get_selected() as we may be in
4339 * multiple selection mode
4342 closure.impl = impl;
4343 closure.path = NULL;
4344 closure.num_selected = 0;
4346 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4347 gtk_tree_selection_selected_foreach (selection, switch_folder_foreach_cb, &closure);
4349 g_assert (closure.path && closure.num_selected == 1);
4351 change_folder_and_display_error (impl, closure.path);
4354 /* Implementation for GtkFileChooserEmbed::should_respond() */
4356 gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
4358 GtkFileChooserDefault *impl;
4359 GtkTreeSelection *selection;
4362 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
4364 /* First, check the save entry. If it has a valid name, we are done */
4366 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
4367 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4370 gboolean is_valid, is_empty;
4372 path = check_save_entry (impl, &is_valid, &is_empty);
4376 gtk_file_path_free (path);
4383 /* Second, do we have an empty selection */
4384 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
4385 || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
4387 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4388 num_selected = gtk_tree_selection_count_selected_rows (selection);
4389 if (num_selected == 0)
4393 /* Third, should we return file names or folder names? */
4395 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
4396 || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
4398 gboolean all_files, all_folders;
4400 selection_check (impl, NULL, &all_files, &all_folders);
4402 if (num_selected == 1)
4406 switch_to_selected_folder (impl);
4415 else if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
4416 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4417 /* There can be no files selected in folder mode since we don't show them,
4422 g_assert_not_reached ();
4426 /* Implementation for GtkFileChooserEmbed::initial_focus() */
4428 gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed)
4430 GtkFileChooserDefault *impl;
4433 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
4435 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
4436 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
4440 path = gtk_tree_path_new_from_indices (0, -1);
4441 gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), path, NULL, FALSE);
4442 gtk_tree_path_free (path);
4444 widget = impl->browse_files_tree_view;
4446 else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
4447 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4448 widget = impl->save_file_name_entry;
4451 g_assert_not_reached ();
4455 gtk_widget_grab_focus (widget);
4459 set_current_filter (GtkFileChooserDefault *impl,
4460 GtkFileFilter *filter)
4462 if (impl->current_filter != filter)
4466 /* If we have filters, new filter must be one of them
4468 filter_index = g_slist_index (impl->filters, filter);
4469 if (impl->filters && filter_index < 0)
4472 if (impl->current_filter)
4473 g_object_unref (impl->current_filter);
4474 impl->current_filter = filter;
4475 if (impl->current_filter)
4477 g_object_ref (impl->current_filter);
4478 gtk_object_sink (GTK_OBJECT (filter));
4482 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
4485 install_list_model_filter (impl);
4487 g_object_notify (G_OBJECT (impl), "filter");
4492 filter_combo_changed (GtkComboBox *combo_box,
4493 GtkFileChooserDefault *impl)
4495 gint new_index = gtk_combo_box_get_active (combo_box);
4496 GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
4498 set_current_filter (impl, new_filter);
4502 check_preview_change (GtkFileChooserDefault *impl)
4504 const GtkFilePath *new_path = NULL;
4505 const GtkFileInfo *new_info = NULL;
4507 /* FIXME #132255: Fixing preview for multiple selection involves getting the
4508 * full selection and diffing to find out what the most recently selected file
4509 * is; there is logic in GtkFileSelection that probably can be
4512 if (impl->sort_model && !impl->select_multiple)
4514 GtkTreeSelection *selection;
4517 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4518 if (gtk_tree_selection_get_selected (selection, NULL, &iter))
4520 GtkTreeIter child_iter;
4522 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
4523 &child_iter, &iter);
4525 new_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
4526 new_info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
4530 if (new_path != impl->preview_path &&
4531 !(new_path && impl->preview_path &&
4532 gtk_file_path_compare (new_path, impl->preview_path) == 0))
4534 if (impl->preview_path)
4536 gtk_file_path_free (impl->preview_path);
4537 g_free (impl->preview_display_name);
4542 impl->preview_path = gtk_file_path_copy (new_path);
4543 impl->preview_display_name = g_strdup (gtk_file_info_get_display_name (new_info));
4547 impl->preview_path = NULL;
4548 impl->preview_display_name = NULL;
4551 if (impl->use_preview_label && impl->preview_label)
4552 gtk_label_set_text (GTK_LABEL (impl->preview_label), impl->preview_display_name);
4554 g_signal_emit_by_name (impl, "update-preview");
4558 /* Activates a volume by mounting it if necessary and then switching to its
4562 shortcuts_activate_volume (GtkFileChooserDefault *impl,
4563 GtkFileSystemVolume *volume)
4567 if (!gtk_file_system_volume_get_is_mounted (impl->file_system, volume))
4572 if (!gtk_file_system_volume_mount (impl->file_system, volume, &error))
4576 msg = g_strdup_printf ("Could not mount %s:\n%s",
4577 gtk_file_system_volume_get_display_name (impl->file_system, volume),
4579 error_message (impl, msg);
4581 g_error_free (error);
4587 path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
4588 change_folder_and_display_error (impl, path);
4589 gtk_file_path_free (path);
4592 /* Opens the folder or volume at the specified index in the shortcuts list */
4594 shortcuts_activate_item (GtkFileChooserDefault *impl,
4603 if (item_num == shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR)
4604 || item_num == shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR))
4607 path = gtk_tree_path_new_from_indices (item_num, -1);
4608 result = gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path);
4609 gtk_tree_path_free (path);
4614 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &data, -1);
4616 start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
4617 if ((item_num >= start_row && item_num < start_row + impl->num_volumes)
4618 || (item_num == shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER) && impl->shortcuts_current_folder_is_volume))
4620 GtkFileSystemVolume *volume;
4623 shortcuts_activate_volume (impl, volume);
4627 const GtkFilePath *file_path;
4630 change_folder_and_display_error (impl, file_path);
4634 /* Callback used when a row in the shortcuts list is activated */
4636 shortcuts_row_activated_cb (GtkTreeView *tree_view,
4638 GtkTreeViewColumn *column,
4639 GtkFileChooserDefault *impl)
4643 GtkTreeIter child_iter;
4644 GtkTreePath *child_path;
4646 if (!gtk_tree_model_get_iter (impl->shortcuts_filter_model, &iter, path))
4649 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
4652 child_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &child_iter);
4656 selected = *gtk_tree_path_get_indices (child_path);
4657 gtk_tree_path_free (child_path);
4659 shortcuts_activate_item (impl, selected);
4663 shortcuts_select_func (GtkTreeSelection *selection,
4664 GtkTreeModel *model,
4666 gboolean path_currently_selected,
4669 GtkFileChooserDefault *impl = data;
4671 return (*gtk_tree_path_get_indices (path) != shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR));
4675 list_selection_changed (GtkTreeSelection *selection,
4676 GtkFileChooserDefault *impl)
4678 /* See if we are in the new folder editable row for Save mode */
4679 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
4681 GtkTreeSelection *selection;
4682 GtkTreeIter iter, child_iter;
4683 const GtkFileInfo *info;
4685 g_assert (!impl->select_multiple);
4686 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4687 if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
4690 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
4694 info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
4696 return; /* We are on the editable row for New Folder */
4699 update_chooser_entry (impl);
4700 check_preview_change (impl);
4701 bookmarks_check_add_sensitivity (impl);
4703 g_signal_emit_by_name (impl, "selection-changed", 0);
4706 /* Callback used when a row in the file list is activated */
4708 list_row_activated (GtkTreeView *tree_view,
4710 GtkTreeViewColumn *column,
4711 GtkFileChooserDefault *impl)
4713 GtkTreeIter iter, child_iter;
4714 const GtkFileInfo *info;
4716 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
4719 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
4721 info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
4723 if (gtk_file_info_get_is_folder (info))
4725 const GtkFilePath *file_path;
4727 file_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
4728 change_folder_and_display_error (impl, file_path);
4733 g_signal_emit_by_name (impl, "file-activated");
4737 path_bar_clicked (GtkPathBar *path_bar,
4738 GtkFilePath *file_path,
4739 GtkFileChooserDefault *impl)
4741 change_folder_and_display_error (impl, file_path);
4744 static const GtkFileInfo *
4745 get_list_file_info (GtkFileChooserDefault *impl,
4748 GtkTreeIter child_iter;
4750 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
4754 return _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
4758 list_icon_data_func (GtkTreeViewColumn *tree_column,
4759 GtkCellRenderer *cell,
4760 GtkTreeModel *tree_model,
4764 GtkFileChooserDefault *impl = data;
4765 GtkTreeIter child_iter;
4766 const GtkFilePath *path;
4769 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
4772 path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
4776 /* FIXME: NULL GError */
4777 pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
4778 impl->icon_size, NULL);
4784 g_object_unref (pixbuf);
4788 list_name_data_func (GtkTreeViewColumn *tree_column,
4789 GtkCellRenderer *cell,
4790 GtkTreeModel *tree_model,
4794 GtkFileChooserDefault *impl = data;
4795 const GtkFileInfo *info = get_list_file_info (impl, iter);
4800 "text", _("Type name of new folder"),
4806 "text", gtk_file_info_get_display_name (info),
4812 list_size_data_func (GtkTreeViewColumn *tree_column,
4813 GtkCellRenderer *cell,
4814 GtkTreeModel *tree_model,
4818 GtkFileChooserDefault *impl = data;
4819 const GtkFileInfo *info = get_list_file_info (impl, iter);
4823 if (!info || gtk_file_info_get_is_folder (info))
4826 size = gtk_file_info_get_size (info);
4828 if (size < (gint64)1024)
4829 str = g_strdup_printf (ngettext ("%d byte", "%d bytes", (gint)size), (gint)size);
4830 else if (size < (gint64)1024*1024)
4831 str = g_strdup_printf (_("%.1f K"), size / (1024.));
4832 else if (size < (gint64)1024*1024*1024)
4833 str = g_strdup_printf (_("%.1f M"), size / (1024.*1024.));
4835 str = g_strdup_printf (_("%.1f G"), size / (1024.*1024.*1024.));
4845 /* Tree column data callback for the file list; fetches the mtime of a file */
4847 list_mtime_data_func (GtkTreeViewColumn *tree_column,
4848 GtkCellRenderer *cell,
4849 GtkTreeModel *tree_model,
4853 GtkFileChooserDefault *impl;
4854 const GtkFileInfo *info;
4855 GtkFileTime time_mtime, time_now;
4862 info = get_list_file_info (impl, iter);
4871 time_mtime = gtk_file_info_get_modification_time (info);
4872 g_date_set_time (&mtime, (GTime) time_mtime);
4874 time_now = (GTime ) time (NULL);
4875 g_date_set_time (&now, (GTime) time_now);
4877 days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
4880 strcpy (buf, _("Today"));
4881 else if (days_diff == 1)
4882 strcpy (buf, _("Yesterday"));
4887 if (days_diff > 1 && days_diff < 7)
4888 format = "%A"; /* Days from last week */
4890 format = "%x"; /* Any other date */
4892 if (g_date_strftime (buf, sizeof (buf), format, &mtime) == 0)
4893 strcpy (buf, _("Unknown"));
4902 _gtk_file_chooser_default_new (const char *file_system)
4904 return g_object_new (GTK_TYPE_FILE_CHOOSER_DEFAULT,
4905 "file-system-backend", file_system,
4910 location_entry_create (GtkFileChooserDefault *impl)
4914 entry = _gtk_file_chooser_entry_new ();
4915 /* Pick a good width for the entry */
4916 gtk_entry_set_width_chars (GTK_ENTRY (entry), 30);
4917 gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
4918 _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (entry), impl->file_system);
4919 _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (entry), impl->current_folder);
4921 return GTK_WIDGET (entry);
4925 update_from_entry (GtkFileChooserDefault *impl,
4927 GtkFileChooserEntry *chooser_entry)
4929 const GtkFilePath *folder_path;
4930 const char *file_part;
4932 folder_path = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
4933 file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
4935 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN && !folder_path)
4937 error_message_with_parent (parent,
4938 _("Cannot change to the folder you specified as it is an invalid path."));
4942 if (file_part[0] == '\0')
4943 return change_folder_and_display_error (impl, folder_path);
4946 GtkFileFolder *folder = NULL;
4947 GtkFilePath *subfolder_path = NULL;
4948 GtkFileInfo *info = NULL;
4954 /* If the file part is non-empty, we need to figure out if it refers to a
4955 * folder within folder. We could optimize the case here where the folder
4956 * is already loaded for one of our tree models.
4960 folder = gtk_file_system_get_folder (impl->file_system, folder_path, GTK_FILE_INFO_IS_FOLDER, &error);
4964 error_getting_info_dialog (impl, folder_path, error);
4969 subfolder_path = gtk_file_system_make_path (impl->file_system, folder_path, file_part, &error);
4971 if (!subfolder_path)
4976 uri = gtk_file_system_path_to_uri (impl->file_system, folder_path);
4977 msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
4980 error_message (impl, msg);
4987 info = gtk_file_folder_get_info (folder, subfolder_path, &error);
4991 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
4992 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4994 if (!change_folder_and_display_error (impl, folder_path))
4997 gtk_file_chooser_default_set_current_name (GTK_FILE_CHOOSER (impl), file_part);
5000 error_getting_info_dialog (impl, subfolder_path, error);
5005 if (gtk_file_info_get_is_folder (info))
5006 result = change_folder_and_display_error (impl, subfolder_path);
5012 result = _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (impl), subfolder_path, &error);
5015 _("Could not select %s:\n%s"),
5016 subfolder_path, error);
5022 g_object_unref (folder);
5024 gtk_file_path_free (subfolder_path);
5027 gtk_file_info_free (info);
5032 g_assert_not_reached ();
5036 location_popup_handler (GtkFileChooserDefault *impl)
5039 GtkWindow *toplevel;
5048 toplevel = get_toplevel (GTK_WIDGET (impl));
5050 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
5051 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5053 title = _("Open Location");
5057 g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5058 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
5059 title = ""; /* FIXME: #137272, fix for 2.4.1 */
5062 dialog = gtk_dialog_new_with_buttons (title,
5064 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
5065 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
5066 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
5068 gtk_window_set_default_size (GTK_WINDOW (dialog), 300, -1);
5069 gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
5070 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2);
5071 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
5073 hbox = gtk_hbox_new (FALSE, 12);
5074 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0);
5075 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
5077 label = gtk_label_new_with_mnemonic (_("_Location:"));
5078 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
5080 entry = location_entry_create (impl);
5081 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
5082 gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
5086 gtk_widget_show_all (dialog);
5090 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
5092 if (update_from_entry (impl, GTK_WINDOW (dialog), GTK_FILE_CHOOSER_ENTRY (entry)))
5094 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
5095 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5097 gtk_widget_grab_focus (impl->browse_files_tree_view);
5101 g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5102 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
5103 gtk_widget_grab_focus (impl->save_file_name_entry);
5111 GtkWindow *toplevel;
5113 toplevel = get_toplevel (GTK_WIDGET (impl));
5114 if (toplevel && toplevel->focus_widget)
5115 gtk_widget_grab_focus (toplevel->focus_widget);
5118 gtk_widget_destroy (dialog);
5121 /* Handler for the "up-folder" keybinding signal */
5123 up_folder_handler (GtkFileChooserDefault *impl)
5125 _gtk_path_bar_up (GTK_PATH_BAR (impl->browse_path_bar));
5128 /* Handler for the "down-folder" keybinding signal */
5130 down_folder_handler (GtkFileChooserDefault *impl)
5132 _gtk_path_bar_down (GTK_PATH_BAR (impl->browse_path_bar));
5135 /* Handler for the "home-folder" keybinding signal */
5137 home_folder_handler (GtkFileChooserDefault *impl)
5143 if (!impl->has_home)
5144 return; /* Should we put up an error dialog? */
5146 pos = shortcuts_get_index (impl, SHORTCUTS_HOME);
5147 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
5148 g_assert_not_reached ();
5150 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &path, -1);
5151 g_assert (path != NULL);
5153 change_folder_and_display_error (impl, path);
5158 /* Drag and drop interfaces */
5161 shortcuts_model_filter_class_init (ShortcutsModelFilterClass *class)
5166 shortcuts_model_filter_init (ShortcutsModelFilter *model)
5171 /* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */
5173 shortcuts_model_filter_row_draggable (GtkTreeDragSource *drag_source,
5176 ShortcutsModelFilter *model;
5180 model = SHORTCUTS_MODEL_FILTER (drag_source);
5182 pos = *gtk_tree_path_get_indices (path);
5183 bookmarks_pos = shortcuts_get_index (model->impl, SHORTCUTS_BOOKMARKS);
5185 return (pos >= bookmarks_pos && pos < bookmarks_pos + model->impl->num_bookmarks);
5188 /* GtkTreeDragSource::drag_data_get implementation for the shortcuts filter model */
5190 shortcuts_model_filter_drag_data_get (GtkTreeDragSource *drag_source,
5192 GtkSelectionData *selection_data)
5194 ShortcutsModelFilter *model;
5196 model = SHORTCUTS_MODEL_FILTER (drag_source);
5203 /* Fill the GtkTreeDragSourceIface vtable */
5205 shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface)
5207 iface->row_draggable = shortcuts_model_filter_row_draggable;
5208 iface->drag_data_get = shortcuts_model_filter_drag_data_get;
5212 /* Fill the GtkTreeDragDestIface vtable */
5214 shortcuts_model_filter_drag_dest_iface_init (GtkTreeDragDestIface *iface)
5216 iface->drag_data_received = shortcuts_model_filter_drag_data_received;
5217 iface->row_drop_possible = shortcuts_model_filter_row_drop_possible;
5221 static GtkTreeModel *
5222 shortcuts_model_filter_new (GtkFileChooserDefault *impl,
5223 GtkTreeModel *child_model,
5226 ShortcutsModelFilter *model;
5228 model = g_object_new (SHORTCUTS_MODEL_FILTER_TYPE,
5229 "child_model", child_model,
5230 "virtual_root", root,
5235 return GTK_TREE_MODEL (model);