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"
24 #include "gtkalignment.h"
25 #include "gtkbindings.h"
26 #include "gtkbutton.h"
27 #include "gtkcelllayout.h"
28 #include "gtkcellrendererpixbuf.h"
29 #include "gtkcellrenderertext.h"
30 #include "gtkcellrenderertext.h"
31 #include "gtkcheckmenuitem.h"
32 #include "gtkcombobox.h"
34 #include "gtkeventbox.h"
35 #include "gtkexpander.h"
36 #include "gtkfilechooserdefault.h"
37 #include "gtkfilechooserembed.h"
38 #include "gtkfilechooserentry.h"
39 #include "gtkfilechooserutils.h"
40 #include "gtkfilechooser.h"
41 #include "gtkfilesystemmodel.h"
44 #include "gtkhpaned.h"
45 #include "gtkiconfactory.h"
46 #include "gtkicontheme.h"
48 #include "gtkimagemenuitem.h"
51 #include "gtkmarshalers.h"
52 #include "gtkmenuitem.h"
53 #include "gtkmessagedialog.h"
54 #include "gtkpathbar.h"
55 #include "gtkprivate.h"
56 #include "gtkscrolledwindow.h"
57 #include "gtkseparatormenuitem.h"
58 #include "gtksizegroup.h"
61 #include "gtktreednd.h"
62 #include "gtktreeprivate.h"
63 #include "gtktreeview.h"
64 #include "gtktreemodelsort.h"
65 #include "gtktreeselection.h"
66 #include "gtktreestore.h"
67 #include "gtktooltips.h"
68 #include "gtktypebuiltins.h"
71 #if defined (G_OS_UNIX)
72 #include "gtkfilesystemunix.h"
73 #elif defined (G_OS_WIN32)
74 #include "gtkfilesystemwin32.h"
81 typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
83 #define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
84 #define GTK_IS_FILE_CHOOSER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT))
85 #define GTK_FILE_CHOOSER_DEFAULT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
92 #define MAX_LOADING_TIME 500
94 struct _GtkFileChooserDefaultClass
96 GtkVBoxClass parent_class;
99 struct _GtkFileChooserDefault
101 GtkVBox parent_instance;
103 GtkFileChooserAction action;
105 GtkFileSystem *file_system;
107 /* Save mode widgets */
108 GtkWidget *save_widgets;
110 GtkWidget *save_file_name_entry;
111 GtkWidget *save_folder_label;
112 GtkWidget *save_folder_combo;
113 GtkWidget *save_expander;
115 /* The file browsing widgets */
116 GtkWidget *browse_widgets;
117 GtkWidget *browse_shortcuts_tree_view;
118 GtkWidget *browse_shortcuts_add_button;
119 GtkWidget *browse_shortcuts_remove_button;
120 GtkWidget *browse_files_tree_view;
121 GtkWidget *browse_files_popup_menu;
122 GtkWidget *browse_files_popup_menu_add_shortcut_item;
123 GtkWidget *browse_files_popup_menu_hidden_files_item;
124 GtkWidget *browse_new_folder_button;
125 GtkWidget *browse_path_bar;
127 GtkFileSystemModel *browse_files_model;
129 GtkWidget *filter_combo_hbox;
130 GtkWidget *filter_combo;
131 GtkWidget *preview_box;
132 GtkWidget *preview_label;
133 GtkWidget *preview_widget;
134 GtkWidget *extra_align;
135 GtkWidget *extra_widget;
137 GtkListStore *shortcuts_model;
138 GtkTreeModel *shortcuts_filter_model;
140 GtkTreeModelSort *sort_model;
142 LoadState load_state;
143 guint load_timeout_id;
145 GtkFileFilter *current_filter;
148 GtkTooltips *tooltips;
151 gboolean has_desktop;
157 gulong volumes_changed_id;
158 gulong bookmarks_changed_id;
160 GtkFilePath *current_volume_path;
161 GtkFilePath *current_folder;
162 GtkFilePath *preview_path;
163 char *preview_display_name;
165 GtkTreeViewColumn *list_name_column;
166 GtkCellRenderer *list_name_renderer;
168 GSource *edited_idle;
169 char *edited_new_text;
171 gulong settings_signal_id;
174 gulong toplevel_set_focus_id;
175 GtkWidget *toplevel_last_focus_widget;
178 GdkDragContext *shortcuts_drag_context;
179 GSource *shortcuts_drag_outside_idle;
184 guint local_only : 1;
185 guint preview_widget_active : 1;
186 guint use_preview_label : 1;
187 guint select_multiple : 1;
188 guint show_hidden : 1;
189 guint list_sort_ascending : 1;
190 guint changing_folder : 1;
191 guint shortcuts_current_folder_active : 1;
194 guint shortcuts_drag_outside : 1;
207 static guint signals[LAST_SIGNAL] = { 0 };
209 /* Column numbers for the shortcuts tree. Keep these in sync with shortcuts_model_create() */
211 SHORTCUTS_COL_PIXBUF,
214 SHORTCUTS_COL_IS_VOLUME,
215 SHORTCUTS_COL_REMOVABLE,
216 SHORTCUTS_COL_PIXBUF_VISIBLE,
217 SHORTCUTS_COL_NUM_COLUMNS
220 /* Column numbers for the file list */
225 FILE_LIST_COL_NUM_COLUMNS
228 /* Identifiers for target types */
234 /* Target types for dragging from the shortcuts list */
235 static GtkTargetEntry shortcuts_source_targets[] = {
236 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }
239 static const int num_shortcuts_source_targets = (sizeof (shortcuts_source_targets)
240 / sizeof (shortcuts_source_targets[0]));
242 /* Target types for dropping into the shortcuts list */
243 static GtkTargetEntry shortcuts_dest_targets[] = {
244 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW },
245 { "text/uri-list", 0, TEXT_URI_LIST }
248 static const int num_shortcuts_dest_targets = (sizeof (shortcuts_dest_targets)
249 / sizeof (shortcuts_dest_targets[0]));
251 /* Target types for DnD from the file list */
252 static GtkTargetEntry file_list_source_targets[] = {
253 { "text/uri-list", 0, TEXT_URI_LIST }
256 static const int num_file_list_source_targets = (sizeof (file_list_source_targets)
257 / sizeof (file_list_source_targets[0]));
259 /* Interesting places in the shortcuts bar */
265 SHORTCUTS_BOOKMARKS_SEPARATOR,
267 SHORTCUTS_CURRENT_FOLDER_SEPARATOR,
268 SHORTCUTS_CURRENT_FOLDER
271 /* Icon size for if we can't get it from the theme */
272 #define FALLBACK_ICON_SIZE 20
274 #define PREVIEW_HBOX_SPACING 12
278 static void gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class);
279 static void gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface);
280 static void gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface);
281 static void gtk_file_chooser_default_init (GtkFileChooserDefault *impl);
283 static GObject* gtk_file_chooser_default_constructor (GType type,
284 guint n_construct_properties,
285 GObjectConstructParam *construct_params);
286 static void gtk_file_chooser_default_finalize (GObject *object);
287 static void gtk_file_chooser_default_set_property (GObject *object,
291 static void gtk_file_chooser_default_get_property (GObject *object,
295 static void gtk_file_chooser_default_dispose (GObject *object);
296 static void gtk_file_chooser_default_show_all (GtkWidget *widget);
297 static void gtk_file_chooser_default_hierarchy_changed (GtkWidget *widget,
298 GtkWidget *previous_toplevel);
299 static void gtk_file_chooser_default_style_set (GtkWidget *widget,
300 GtkStyle *previous_style);
301 static void gtk_file_chooser_default_screen_changed (GtkWidget *widget,
302 GdkScreen *previous_screen);
304 static gboolean gtk_file_chooser_default_set_current_folder (GtkFileChooser *chooser,
305 const GtkFilePath *path,
307 static GtkFilePath * gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser);
308 static void gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
310 static gboolean gtk_file_chooser_default_select_path (GtkFileChooser *chooser,
311 const GtkFilePath *path,
313 static void gtk_file_chooser_default_unselect_path (GtkFileChooser *chooser,
314 const GtkFilePath *path);
315 static void gtk_file_chooser_default_select_all (GtkFileChooser *chooser);
316 static void gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser);
317 static GSList * gtk_file_chooser_default_get_paths (GtkFileChooser *chooser);
318 static GtkFilePath * gtk_file_chooser_default_get_preview_path (GtkFileChooser *chooser);
319 static GtkFileSystem *gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser);
320 static void gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
321 GtkFileFilter *filter);
322 static void gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
323 GtkFileFilter *filter);
324 static GSList * gtk_file_chooser_default_list_filters (GtkFileChooser *chooser);
325 static gboolean gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser *chooser,
326 const GtkFilePath *path,
328 static gboolean gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser *chooser,
329 const GtkFilePath *path,
331 static GSList * gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser);
333 static void gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
335 gint *default_height);
336 static void gtk_file_chooser_default_get_resizable_hints (GtkFileChooserEmbed *chooser_embed,
337 gboolean *resize_horizontally,
338 gboolean *resize_vertically);
339 static gboolean gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed);
340 static void gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed);
342 static void location_popup_handler (GtkFileChooserDefault *impl,
344 static void up_folder_handler (GtkFileChooserDefault *impl);
345 static void down_folder_handler (GtkFileChooserDefault *impl);
346 static void home_folder_handler (GtkFileChooserDefault *impl);
347 static void update_appearance (GtkFileChooserDefault *impl);
349 static void set_current_filter (GtkFileChooserDefault *impl,
350 GtkFileFilter *filter);
351 static void check_preview_change (GtkFileChooserDefault *impl);
353 static void filter_combo_changed (GtkComboBox *combo_box,
354 GtkFileChooserDefault *impl);
355 static void shortcuts_row_activated_cb (GtkTreeView *tree_view,
357 GtkTreeViewColumn *column,
358 GtkFileChooserDefault *impl);
360 static gboolean shortcuts_key_press_event_cb (GtkWidget *widget,
362 GtkFileChooserDefault *impl);
364 static gboolean shortcuts_select_func (GtkTreeSelection *selection,
367 gboolean path_currently_selected,
369 static void shortcuts_activate_iter (GtkFileChooserDefault *impl,
371 static int shortcuts_get_index (GtkFileChooserDefault *impl,
372 ShortcutsIndex where);
373 static int shortcut_find_position (GtkFileChooserDefault *impl,
374 const GtkFilePath *path);
376 static void bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl);
378 static gboolean list_select_func (GtkTreeSelection *selection,
381 gboolean path_currently_selected,
384 static void list_selection_changed (GtkTreeSelection *tree_selection,
385 GtkFileChooserDefault *impl);
386 static void list_row_activated (GtkTreeView *tree_view,
388 GtkTreeViewColumn *column,
389 GtkFileChooserDefault *impl);
391 static void path_bar_clicked (GtkPathBar *path_bar,
392 GtkFilePath *file_path,
393 gboolean child_is_hidden,
394 GtkFileChooserDefault *impl);
396 static void add_bookmark_button_clicked_cb (GtkButton *button,
397 GtkFileChooserDefault *impl);
398 static void remove_bookmark_button_clicked_cb (GtkButton *button,
399 GtkFileChooserDefault *impl);
401 static void list_icon_data_func (GtkTreeViewColumn *tree_column,
402 GtkCellRenderer *cell,
403 GtkTreeModel *tree_model,
406 static void list_name_data_func (GtkTreeViewColumn *tree_column,
407 GtkCellRenderer *cell,
408 GtkTreeModel *tree_model,
412 static void list_size_data_func (GtkTreeViewColumn *tree_column,
413 GtkCellRenderer *cell,
414 GtkTreeModel *tree_model,
418 static void list_mtime_data_func (GtkTreeViewColumn *tree_column,
419 GtkCellRenderer *cell,
420 GtkTreeModel *tree_model,
424 static const GtkFileInfo *get_list_file_info (GtkFileChooserDefault *impl,
427 static void load_remove_timer (GtkFileChooserDefault *impl);
429 static GObjectClass *parent_class;
433 /* Drag and drop interface declarations */
436 GtkTreeModelFilter parent;
438 GtkFileChooserDefault *impl;
439 } ShortcutsModelFilter;
442 GtkTreeModelFilterClass parent_class;
443 } ShortcutsModelFilterClass;
445 #define SHORTCUTS_MODEL_FILTER_TYPE (_shortcuts_model_filter_get_type ())
446 #define SHORTCUTS_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_MODEL_FILTER_TYPE, ShortcutsModelFilter))
448 static void shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface);
450 G_DEFINE_TYPE_WITH_CODE (ShortcutsModelFilter,
451 _shortcuts_model_filter,
452 GTK_TYPE_TREE_MODEL_FILTER,
453 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
454 shortcuts_model_filter_drag_source_iface_init));
456 static GtkTreeModel *shortcuts_model_filter_new (GtkFileChooserDefault *impl,
457 GtkTreeModel *child_model,
463 _gtk_file_chooser_default_get_type (void)
465 static GType file_chooser_default_type = 0;
467 if (!file_chooser_default_type)
469 static const GTypeInfo file_chooser_default_info =
471 sizeof (GtkFileChooserDefaultClass),
472 NULL, /* base_init */
473 NULL, /* base_finalize */
474 (GClassInitFunc) gtk_file_chooser_default_class_init,
475 NULL, /* class_finalize */
476 NULL, /* class_data */
477 sizeof (GtkFileChooserDefault),
479 (GInstanceInitFunc) gtk_file_chooser_default_init,
482 static const GInterfaceInfo file_chooser_info =
484 (GInterfaceInitFunc) gtk_file_chooser_default_iface_init, /* interface_init */
485 NULL, /* interface_finalize */
486 NULL /* interface_data */
489 static const GInterfaceInfo file_chooser_embed_info =
491 (GInterfaceInitFunc) gtk_file_chooser_embed_default_iface_init, /* interface_init */
492 NULL, /* interface_finalize */
493 NULL /* interface_data */
496 file_chooser_default_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserDefault",
497 &file_chooser_default_info, 0);
499 g_type_add_interface_static (file_chooser_default_type,
500 GTK_TYPE_FILE_CHOOSER,
502 g_type_add_interface_static (file_chooser_default_type,
503 GTK_TYPE_FILE_CHOOSER_EMBED,
504 &file_chooser_embed_info);
507 return file_chooser_default_type;
511 gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
513 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
514 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
515 GtkBindingSet *binding_set;
517 parent_class = g_type_class_peek_parent (class);
519 gobject_class->finalize = gtk_file_chooser_default_finalize;
520 gobject_class->constructor = gtk_file_chooser_default_constructor;
521 gobject_class->set_property = gtk_file_chooser_default_set_property;
522 gobject_class->get_property = gtk_file_chooser_default_get_property;
523 gobject_class->dispose = gtk_file_chooser_default_dispose;
525 widget_class->show_all = gtk_file_chooser_default_show_all;
526 widget_class->hierarchy_changed = gtk_file_chooser_default_hierarchy_changed;
527 widget_class->style_set = gtk_file_chooser_default_style_set;
528 widget_class->screen_changed = gtk_file_chooser_default_screen_changed;
530 signals[LOCATION_POPUP] =
531 _gtk_binding_signal_new ("location-popup",
532 G_OBJECT_CLASS_TYPE (class),
533 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
534 G_CALLBACK (location_popup_handler),
536 _gtk_marshal_VOID__STRING,
537 G_TYPE_NONE, 1, G_TYPE_STRING);
539 _gtk_binding_signal_new ("up-folder",
540 G_OBJECT_CLASS_TYPE (class),
541 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
542 G_CALLBACK (up_folder_handler),
544 _gtk_marshal_VOID__VOID,
546 signals[DOWN_FOLDER] =
547 _gtk_binding_signal_new ("down-folder",
548 G_OBJECT_CLASS_TYPE (class),
549 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
550 G_CALLBACK (down_folder_handler),
552 _gtk_marshal_VOID__VOID,
554 signals[HOME_FOLDER] =
555 _gtk_binding_signal_new ("home-folder",
556 G_OBJECT_CLASS_TYPE (class),
557 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
558 G_CALLBACK (home_folder_handler),
560 _gtk_marshal_VOID__VOID,
563 binding_set = gtk_binding_set_by_class (class);
565 gtk_binding_entry_add_signal (binding_set,
566 GDK_l, GDK_CONTROL_MASK,
568 1, G_TYPE_STRING, "");
570 gtk_binding_entry_add_signal (binding_set,
573 1, G_TYPE_STRING, "/");
575 gtk_binding_entry_add_signal (binding_set,
576 GDK_Up, GDK_MOD1_MASK,
579 gtk_binding_entry_add_signal (binding_set,
583 gtk_binding_entry_add_signal (binding_set,
584 GDK_KP_Up, GDK_MOD1_MASK,
588 gtk_binding_entry_add_signal (binding_set,
589 GDK_Down, GDK_MOD1_MASK,
592 gtk_binding_entry_add_signal (binding_set,
593 GDK_KP_Down, GDK_MOD1_MASK,
597 gtk_binding_entry_add_signal (binding_set,
598 GDK_Home, GDK_MOD1_MASK,
601 gtk_binding_entry_add_signal (binding_set,
602 GDK_KP_Home, GDK_MOD1_MASK,
606 _gtk_file_chooser_install_properties (gobject_class);
608 gtk_settings_install_property (g_param_spec_string ("gtk-file-chooser-backend",
609 P_("Default file chooser backend"),
610 P_("Name of the GtkFileChooser backend to use by default"),
616 gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
618 iface->select_path = gtk_file_chooser_default_select_path;
619 iface->unselect_path = gtk_file_chooser_default_unselect_path;
620 iface->select_all = gtk_file_chooser_default_select_all;
621 iface->unselect_all = gtk_file_chooser_default_unselect_all;
622 iface->get_paths = gtk_file_chooser_default_get_paths;
623 iface->get_preview_path = gtk_file_chooser_default_get_preview_path;
624 iface->get_file_system = gtk_file_chooser_default_get_file_system;
625 iface->set_current_folder = gtk_file_chooser_default_set_current_folder;
626 iface->get_current_folder = gtk_file_chooser_default_get_current_folder;
627 iface->set_current_name = gtk_file_chooser_default_set_current_name;
628 iface->add_filter = gtk_file_chooser_default_add_filter;
629 iface->remove_filter = gtk_file_chooser_default_remove_filter;
630 iface->list_filters = gtk_file_chooser_default_list_filters;
631 iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder;
632 iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder;
633 iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders;
637 gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
639 iface->get_default_size = gtk_file_chooser_default_get_default_size;
640 iface->get_resizable_hints = gtk_file_chooser_default_get_resizable_hints;
641 iface->should_respond = gtk_file_chooser_default_should_respond;
642 iface->initial_focus = gtk_file_chooser_default_initial_focus;
645 gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
647 impl->local_only = TRUE;
648 impl->preview_widget_active = TRUE;
649 impl->use_preview_label = TRUE;
650 impl->select_multiple = FALSE;
651 impl->show_hidden = FALSE;
652 impl->icon_size = FALLBACK_ICON_SIZE;
653 impl->load_state = LOAD_FINISHED;
655 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (impl), TRUE);
656 gtk_box_set_spacing (GTK_BOX (impl), 12);
658 impl->tooltips = gtk_tooltips_new ();
659 g_object_ref (impl->tooltips);
660 gtk_object_sink (GTK_OBJECT (impl->tooltips));
663 /* Frees the data columns for the specified iter in the shortcuts model*/
665 shortcuts_free_row_data (GtkFileChooserDefault *impl,
671 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
672 SHORTCUTS_COL_DATA, &col_data,
673 SHORTCUTS_COL_IS_VOLUME, &is_volume,
680 GtkFileSystemVolume *volume;
683 gtk_file_system_volume_free (impl->file_system, volume);
690 gtk_file_path_free (path);
694 /* Frees all the data columns in the shortcuts model */
696 shortcuts_free (GtkFileChooserDefault *impl)
700 if (!impl->shortcuts_model)
703 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
706 shortcuts_free_row_data (impl, &iter);
708 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter));
710 g_object_unref (impl->shortcuts_model);
711 impl->shortcuts_model = NULL;
715 gtk_file_chooser_default_finalize (GObject *object)
717 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
720 if (impl->shortcuts_filter_model)
721 g_object_unref (impl->shortcuts_filter_model);
723 shortcuts_free (impl);
725 g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
726 impl->volumes_changed_id = 0;
727 g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
728 impl->bookmarks_changed_id = 0;
729 g_object_unref (impl->file_system);
731 for (l = impl->filters; l; l = l->next)
733 GtkFileFilter *filter;
735 filter = GTK_FILE_FILTER (l->data);
736 g_object_unref (filter);
738 g_slist_free (impl->filters);
740 if (impl->current_filter)
741 g_object_unref (impl->current_filter);
743 if (impl->current_volume_path)
744 gtk_file_path_free (impl->current_volume_path);
746 if (impl->current_folder)
747 gtk_file_path_free (impl->current_folder);
749 if (impl->preview_path)
750 gtk_file_path_free (impl->preview_path);
752 load_remove_timer (impl);
754 /* Free all the Models we have */
755 if (impl->browse_files_model)
756 g_object_unref (impl->browse_files_model);
758 if (impl->sort_model)
759 g_object_unref (impl->sort_model);
761 g_free (impl->preview_display_name);
763 g_free (impl->edited_new_text);
765 g_object_unref (impl->tooltips);
767 G_OBJECT_CLASS (parent_class)->finalize (object);
770 /* Shows an error dialog set as transient for the specified window */
772 error_message_with_parent (GtkWindow *parent,
777 dialog = gtk_message_dialog_new (parent,
778 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
783 gtk_dialog_run (GTK_DIALOG (dialog));
784 gtk_widget_destroy (dialog);
787 /* Returns a toplevel GtkWindow, or NULL if none */
789 get_toplevel (GtkWidget *widget)
793 toplevel = gtk_widget_get_toplevel (widget);
794 if (!GTK_WIDGET_TOPLEVEL (toplevel))
797 return GTK_WINDOW (toplevel);
800 /* Shows an error dialog for the file chooser */
802 error_message (GtkFileChooserDefault *impl,
805 error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg);
808 /* Shows a simple error dialog relative to a path. Frees the GError as well. */
810 error_dialog (GtkFileChooserDefault *impl,
812 const GtkFilePath *path,
815 g_return_if_fail (path != NULL);
819 char *uri = gtk_file_system_path_to_uri (impl->file_system, path);
820 char *text = g_strdup_printf (msg,
823 error_message (impl, text);
826 g_error_free (error);
830 /* Displays an error message about not being able to get information for a file.
831 * Frees the GError as well.
834 error_getting_info_dialog (GtkFileChooserDefault *impl,
835 const GtkFilePath *path,
839 _("Could not retrieve information about %s:\n%s"),
843 /* Shows an error dialog about not being able to add a bookmark */
845 error_could_not_add_bookmark_dialog (GtkFileChooserDefault *impl,
846 const GtkFilePath *path,
850 _("Could not add a bookmark for %s:\n%s"),
854 /* Shows an error dialog about not being able to compose a filename */
856 error_building_filename_dialog (GtkFileChooserDefault *impl,
857 const GtkFilePath *base_path,
858 const char *file_part,
864 uri = gtk_file_system_path_to_uri (impl->file_system, base_path);
865 msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
868 error_message (impl, msg);
871 g_error_free (error);
874 /* Shows an error dialog when we cannot switch to a folder */
876 error_changing_folder_dialog (GtkFileChooserDefault *impl,
877 const GtkFilePath *path,
881 _("Could not change the current folder to %s:\n%s"),
886 /* Changes folders, displaying an error dialog if this fails */
888 change_folder_and_display_error (GtkFileChooserDefault *impl,
889 const GtkFilePath *path)
893 GtkFilePath *path_copy;
895 /* We copy the path because of this case:
897 * list_row_activated()
898 * fetches path from model; path belongs to the model (*)
899 * calls change_folder_and_display_error()
900 * calls _gtk_file_chooser_set_current_folder_path()
901 * changing folders fails, sets model to NULL, thus freeing the path in (*)
904 path_copy = gtk_file_path_copy (path);
907 result = _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), path_copy, &error);
910 error_changing_folder_dialog (impl, path_copy, error);
912 gtk_file_path_free (path_copy);
918 update_preview_widget_visibility (GtkFileChooserDefault *impl)
920 if (impl->use_preview_label)
922 if (!impl->preview_label)
924 impl->preview_label = gtk_label_new (impl->preview_display_name);
925 gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_label, FALSE, FALSE, 0);
926 gtk_box_reorder_child (GTK_BOX (impl->preview_box), impl->preview_label, 0);
927 gtk_widget_show (impl->preview_label);
932 if (impl->preview_label)
934 gtk_widget_destroy (impl->preview_label);
935 impl->preview_label = NULL;
939 if (impl->preview_widget_active && impl->preview_widget)
940 gtk_widget_show (impl->preview_box);
942 gtk_widget_hide (impl->preview_box);
944 g_signal_emit_by_name (impl, "default-size-changed");
948 set_preview_widget (GtkFileChooserDefault *impl,
949 GtkWidget *preview_widget)
951 if (preview_widget == impl->preview_widget)
954 if (impl->preview_widget)
955 gtk_container_remove (GTK_CONTAINER (impl->preview_box),
956 impl->preview_widget);
958 impl->preview_widget = preview_widget;
959 if (impl->preview_widget)
961 gtk_widget_show (impl->preview_widget);
962 gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_widget, TRUE, TRUE, 0);
963 gtk_box_reorder_child (GTK_BOX (impl->preview_box),
964 impl->preview_widget,
965 (impl->use_preview_label && impl->preview_label) ? 1 : 0);
968 update_preview_widget_visibility (impl);
971 /* Re-reads all the icons for the shortcuts, used when the theme changes */
973 shortcuts_reload_icons (GtkFileChooserDefault *impl)
977 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
983 gboolean pixbuf_visible;
986 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
987 SHORTCUTS_COL_DATA, &data,
988 SHORTCUTS_COL_IS_VOLUME, &is_volume,
989 SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible,
992 if (pixbuf_visible && data)
996 GtkFileSystemVolume *volume;
999 pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl),
1000 impl->icon_size, NULL);
1004 const GtkFilePath *path;
1007 pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
1008 impl->icon_size, NULL);
1011 gtk_list_store_set (impl->shortcuts_model, &iter,
1012 SHORTCUTS_COL_PIXBUF, pixbuf,
1015 g_object_unref (pixbuf);
1017 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
1020 /* If a shortcut corresponds to the current folder, selects it */
1022 shortcuts_find_current_folder (GtkFileChooserDefault *impl)
1024 GtkTreeSelection *selection;
1028 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1030 pos = shortcut_find_position (impl, impl->current_folder);
1033 gtk_tree_selection_unselect_all (selection);
1037 path = gtk_tree_path_new_from_indices (pos, -1);
1038 gtk_tree_selection_select_path (selection, path);
1039 gtk_tree_path_free (path);
1042 /* Convenience function to get the display name and icon info for a path */
1043 static GtkFileInfo *
1044 get_file_info (GtkFileSystem *file_system, const GtkFilePath *path, gboolean name_only, GError **error)
1046 GtkFilePath *parent_path;
1047 GtkFileFolder *parent_folder;
1052 if (!gtk_file_system_get_parent (file_system, path, &parent_path, error))
1055 parent_folder = gtk_file_system_get_folder (file_system, parent_path ? parent_path : path,
1056 GTK_FILE_INFO_DISPLAY_NAME
1057 | (name_only ? 0 : GTK_FILE_INFO_IS_FOLDER),
1062 info = gtk_file_folder_get_info (parent_folder, parent_path ? path : NULL, error);
1063 g_object_unref (parent_folder);
1067 gtk_file_path_free (parent_path);
1071 /* Returns whether a path is a folder */
1073 check_is_folder (GtkFileSystem *file_system, const GtkFilePath *path, GError **error)
1078 /* Use get_file_info() rather than trying get_folder() and checking
1079 * for an error directly because older versions of the gnome-vfs
1080 * backend don't return an error immediately. This way is also
1081 * more efficient if we already have the parent folder.
1083 info = get_file_info (file_system, path, FALSE, error);
1088 is_folder = gtk_file_info_get_is_folder (info);
1092 GTK_FILE_SYSTEM_ERROR,
1093 GTK_FILE_SYSTEM_ERROR_NOT_FOLDER,
1095 gtk_file_info_get_display_name (info),
1096 g_strerror (ENOTDIR));
1098 gtk_file_info_free (info);
1103 /* Inserts a path in the shortcuts tree, making a copy of it; alternatively,
1104 * inserts a volume. A position of -1 indicates the end of the tree.
1107 shortcuts_insert_path (GtkFileChooserDefault *impl,
1110 GtkFileSystemVolume *volume,
1111 const GtkFilePath *path,
1124 label_copy = gtk_file_system_volume_get_display_name (impl->file_system, volume);
1125 pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl),
1126 impl->icon_size, NULL);
1130 if (!check_is_folder (impl->file_system, path, error))
1134 label_copy = g_strdup (label);
1137 GtkFileInfo *info = get_file_info (impl->file_system, path, TRUE, error);
1142 label_copy = g_strdup (gtk_file_info_get_display_name (info));
1143 gtk_file_info_free (info);
1146 data = gtk_file_path_copy (path);
1147 pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
1148 impl->icon_size, NULL);
1152 gtk_list_store_append (impl->shortcuts_model, &iter);
1154 gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
1156 gtk_list_store_set (impl->shortcuts_model, &iter,
1157 SHORTCUTS_COL_PIXBUF, pixbuf,
1158 SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1159 SHORTCUTS_COL_NAME, label_copy,
1160 SHORTCUTS_COL_DATA, data,
1161 SHORTCUTS_COL_IS_VOLUME, is_volume,
1162 SHORTCUTS_COL_REMOVABLE, removable,
1165 g_free (label_copy);
1168 g_object_unref (pixbuf);
1173 /* Appends an item for the user's home directory to the shortcuts model */
1175 shortcuts_append_home (GtkFileChooserDefault *impl)
1178 GtkFilePath *home_path;
1181 home = g_get_home_dir ();
1185 home_path = gtk_file_system_filename_to_path (impl->file_system, home);
1188 impl->has_home = shortcuts_insert_path (impl, -1, FALSE, NULL, home_path, _("Home"), FALSE, &error);
1189 if (!impl->has_home)
1190 error_getting_info_dialog (impl, home_path, error);
1192 gtk_file_path_free (home_path);
1195 /* Appends the ~/Desktop directory to the shortcuts model */
1197 shortcuts_append_desktop (GtkFileChooserDefault *impl)
1203 home = g_get_home_dir ();
1207 name = g_build_filename (home, "Desktop", NULL);
1208 path = gtk_file_system_filename_to_path (impl->file_system, name);
1211 impl->has_desktop = shortcuts_insert_path (impl, -1, FALSE, NULL, path, _("Desktop"), FALSE, NULL);
1212 /* We do not actually pop up an error dialog if there is no desktop directory
1213 * because some people may really not want to have one.
1216 gtk_file_path_free (path);
1219 /* Appends a list of GtkFilePath to the shortcuts model; returns how many were inserted */
1221 shortcuts_append_paths (GtkFileChooserDefault *impl,
1227 /* As there is no separator now, we want to start there.
1229 start_row = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
1232 for (; paths; paths = paths->next)
1240 if (impl->local_only &&
1241 !gtk_file_system_path_is_local (impl->file_system, path))
1244 /* NULL GError, but we don't really want to show error boxes here */
1245 if (shortcuts_insert_path (impl, start_row + num_inserted, FALSE, NULL, path, NULL, TRUE, NULL))
1249 return num_inserted;
1252 /* Returns the index for the corresponding item in the shortcuts bar */
1254 shortcuts_get_index (GtkFileChooserDefault *impl,
1255 ShortcutsIndex where)
1261 if (where == SHORTCUTS_HOME)
1264 n += impl->has_home ? 1 : 0;
1266 if (where == SHORTCUTS_DESKTOP)
1269 n += impl->has_desktop ? 1 : 0;
1271 if (where == SHORTCUTS_VOLUMES)
1274 n += impl->num_volumes;
1276 if (where == SHORTCUTS_SHORTCUTS)
1279 n += impl->num_shortcuts;
1281 if (where == SHORTCUTS_BOOKMARKS_SEPARATOR)
1284 /* If there are no bookmarks there won't be a separator */
1285 n += (impl->num_bookmarks > 0) ? 1 : 0;
1287 if (where == SHORTCUTS_BOOKMARKS)
1290 n += impl->num_bookmarks;
1292 if (where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR)
1297 if (where == SHORTCUTS_CURRENT_FOLDER)
1300 g_assert_not_reached ();
1307 /* Removes the specified number of rows from the shortcuts list */
1309 shortcuts_remove_rows (GtkFileChooserDefault *impl,
1315 path = gtk_tree_path_new_from_indices (start_row, -1);
1317 for (; n_rows; n_rows--)
1321 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
1322 g_assert_not_reached ();
1324 shortcuts_free_row_data (impl, &iter);
1325 gtk_list_store_remove (impl->shortcuts_model, &iter);
1328 gtk_tree_path_free (path);
1331 /* Adds all the file system volumes to the shortcuts model */
1333 shortcuts_add_volumes (GtkFileChooserDefault *impl)
1338 gboolean old_changing_folders;
1340 old_changing_folders = impl->changing_folder;
1341 impl->changing_folder = TRUE;
1343 start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
1344 shortcuts_remove_rows (impl, start_row, impl->num_volumes);
1345 impl->num_volumes = 0;
1347 list = gtk_file_system_list_volumes (impl->file_system);
1351 for (l = list; l; l = l->next)
1353 GtkFileSystemVolume *volume;
1357 if (impl->local_only)
1359 GtkFilePath *base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
1360 gboolean is_local = gtk_file_system_path_is_local (impl->file_system, base_path);
1361 gtk_file_path_free (base_path);
1365 gtk_file_system_volume_free (impl->file_system, volume);
1370 if (shortcuts_insert_path (impl, start_row + n, TRUE, volume, NULL, NULL, FALSE, NULL))
1373 gtk_file_system_volume_free (impl->file_system, volume);
1376 impl->num_volumes = n;
1377 g_slist_free (list);
1379 if (impl->shortcuts_filter_model)
1380 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
1382 impl->changing_folder = old_changing_folders;
1385 /* Inserts a separator node in the shortcuts list */
1387 shortcuts_insert_separator (GtkFileChooserDefault *impl,
1388 ShortcutsIndex where)
1392 g_assert (where == SHORTCUTS_BOOKMARKS_SEPARATOR || where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1394 gtk_list_store_insert (impl->shortcuts_model, &iter,
1395 shortcuts_get_index (impl, where));
1396 gtk_list_store_set (impl->shortcuts_model, &iter,
1397 SHORTCUTS_COL_PIXBUF, NULL,
1398 SHORTCUTS_COL_PIXBUF_VISIBLE, FALSE,
1399 SHORTCUTS_COL_NAME, NULL,
1400 SHORTCUTS_COL_DATA, NULL,
1404 /* Updates the list of bookmarks */
1406 shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
1409 gboolean old_changing_folders;
1411 old_changing_folders = impl->changing_folder;
1412 impl->changing_folder = TRUE;
1414 if (impl->num_bookmarks > 0)
1415 shortcuts_remove_rows (impl,
1416 shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR),
1417 impl->num_bookmarks + 1);
1419 bookmarks = gtk_file_system_list_bookmarks (impl->file_system);
1420 impl->num_bookmarks = shortcuts_append_paths (impl, bookmarks);
1421 gtk_file_paths_free (bookmarks);
1423 if (impl->num_bookmarks > 0)
1425 shortcuts_insert_separator (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
1427 if (impl->shortcuts_filter_model)
1428 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
1430 impl->changing_folder = old_changing_folders;
1433 /* Appends a separator and a row to the shortcuts list for the current folder */
1435 shortcuts_add_current_folder (GtkFileChooserDefault *impl)
1440 g_assert (!impl->shortcuts_current_folder_active);
1444 pos = shortcut_find_position (impl, impl->current_folder);
1447 GtkFileSystemVolume *volume;
1448 GtkFilePath *base_path;
1452 shortcuts_insert_separator (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1456 pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER);
1458 volume = gtk_file_system_get_volume_for_path (impl->file_system, impl->current_folder);
1460 base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
1465 strcmp (gtk_file_path_get_string (base_path), gtk_file_path_get_string (impl->current_folder)) == 0)
1467 success = shortcuts_insert_path (impl, pos, TRUE, volume, NULL, NULL, FALSE, NULL);
1472 success = shortcuts_insert_path (impl, pos, FALSE, NULL, impl->current_folder, NULL, FALSE, NULL);
1475 gtk_file_system_volume_free (impl->file_system, volume);
1478 gtk_file_path_free (base_path);
1481 shortcuts_remove_rows (impl, pos - 1, 1); /* remove the separator */
1483 impl->shortcuts_current_folder_active = success;
1487 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), pos);
1490 /* Updates the current folder row in the shortcuts model */
1492 shortcuts_update_current_folder (GtkFileChooserDefault *impl)
1496 pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1498 if (impl->shortcuts_current_folder_active)
1500 shortcuts_remove_rows (impl, pos, 2);
1501 impl->shortcuts_current_folder_active = FALSE;
1504 shortcuts_add_current_folder (impl);
1507 /* Filter function used for the shortcuts filter model */
1509 shortcuts_filter_cb (GtkTreeModel *model,
1513 GtkFileChooserDefault *impl;
1517 impl = GTK_FILE_CHOOSER_DEFAULT (data);
1519 path = gtk_tree_model_get_path (model, iter);
1523 pos = *gtk_tree_path_get_indices (path);
1524 gtk_tree_path_free (path);
1526 return (pos < shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR));
1529 /* Creates the list model for shortcuts */
1531 shortcuts_model_create (GtkFileChooserDefault *impl)
1533 /* Keep this order in sync with the SHORCUTS_COL_* enum values */
1534 impl->shortcuts_model = gtk_list_store_new (SHORTCUTS_COL_NUM_COLUMNS,
1535 GDK_TYPE_PIXBUF, /* pixbuf */
1536 G_TYPE_STRING, /* name */
1537 G_TYPE_POINTER, /* path or volume */
1538 G_TYPE_BOOLEAN, /* is the previous column a volume? */
1539 G_TYPE_BOOLEAN, /* removable */
1540 G_TYPE_BOOLEAN); /* pixbuf cell visibility */
1542 if (impl->file_system)
1544 shortcuts_append_home (impl);
1545 shortcuts_append_desktop (impl);
1546 shortcuts_add_volumes (impl);
1547 shortcuts_add_bookmarks (impl);
1550 impl->shortcuts_filter_model = shortcuts_model_filter_new (impl,
1551 GTK_TREE_MODEL (impl->shortcuts_model),
1554 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
1555 shortcuts_filter_cb,
1560 /* Callback used when the "New Folder" button is clicked */
1562 new_folder_button_clicked (GtkButton *button,
1563 GtkFileChooserDefault *impl)
1568 if (!impl->browse_files_model)
1569 return; /* FIXME: this sucks. Disable the New Folder button or something. */
1571 /* Prevent button from being clicked twice */
1572 gtk_widget_set_sensitive (impl->browse_new_folder_button, FALSE);
1574 _gtk_file_system_model_add_editable (impl->browse_files_model, &iter);
1576 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->browse_files_model), &iter);
1577 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_files_tree_view),
1578 path, impl->list_name_column,
1581 g_object_set (impl->list_name_renderer, "editable", TRUE, NULL);
1582 gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view),
1584 impl->list_name_column,
1587 gtk_tree_path_free (path);
1590 /* Idle handler for creating a new folder after editing its name cell, or for
1591 * canceling the editing.
1594 edited_idle_cb (GtkFileChooserDefault *impl)
1596 g_source_destroy (impl->edited_idle);
1597 impl->edited_idle = NULL;
1599 _gtk_file_system_model_remove_editable (impl->browse_files_model);
1600 g_object_set (impl->list_name_renderer, "editable", FALSE, NULL);
1602 gtk_widget_set_sensitive (impl->browse_new_folder_button, TRUE);
1604 if (impl->edited_new_text) /* not cancelled? */
1607 GtkFilePath *file_path;
1610 file_path = gtk_file_system_make_path (impl->file_system, impl->current_folder, impl->edited_new_text,
1615 if (gtk_file_system_create_folder (impl->file_system, file_path, &error))
1616 change_folder_and_display_error (impl, file_path);
1619 _("Could not create folder %s:\n%s"),
1622 gtk_file_path_free (file_path);
1625 error_building_filename_dialog (impl, impl->current_folder, impl->edited_new_text, error);
1627 g_free (impl->edited_new_text);
1628 impl->edited_new_text = NULL;
1635 queue_edited_idle (GtkFileChooserDefault *impl,
1636 const gchar *new_text)
1638 /* We create the folder in an idle handler so that we don't modify the tree
1642 g_assert (!impl->edited_idle);
1643 g_assert (!impl->edited_new_text);
1645 impl->edited_idle = g_idle_source_new ();
1646 g_source_set_closure (impl->edited_idle,
1647 g_cclosure_new_object (G_CALLBACK (edited_idle_cb),
1649 g_source_attach (impl->edited_idle, NULL);
1652 impl->edited_new_text = g_strdup (new_text);
1655 /* Callback used from the text cell renderer when the new folder is named */
1657 renderer_edited_cb (GtkCellRendererText *cell_renderer_text,
1659 const gchar *new_text,
1660 GtkFileChooserDefault *impl)
1662 /* work around bug #154921 */
1663 g_object_set (cell_renderer_text,
1664 "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
1665 queue_edited_idle (impl, new_text);
1668 /* Callback used from the text cell renderer when the new folder edition gets
1672 renderer_editing_canceled_cb (GtkCellRendererText *cell_renderer_text,
1673 GtkFileChooserDefault *impl)
1675 /* work around bug #154921 */
1676 g_object_set (cell_renderer_text,
1677 "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
1678 queue_edited_idle (impl, NULL);
1681 /* Creates the widgets for the filter combo box */
1683 filter_create (GtkFileChooserDefault *impl)
1685 impl->filter_combo = gtk_combo_box_new_text ();
1686 g_signal_connect (impl->filter_combo, "changed",
1687 G_CALLBACK (filter_combo_changed), impl);
1689 return impl->filter_combo;
1693 button_new (GtkFileChooserDefault *impl,
1695 const char *stock_id,
1705 button = gtk_button_new ();
1706 hbox = gtk_hbox_new (FALSE, 2);
1707 align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1709 gtk_container_add (GTK_CONTAINER (button), align);
1710 gtk_container_add (GTK_CONTAINER (align), hbox);
1711 widget = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
1713 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1715 widget = gtk_label_new_with_mnemonic (text);
1716 gtk_label_set_mnemonic_widget (GTK_LABEL (widget), GTK_WIDGET (button));
1717 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1719 gtk_widget_set_sensitive (button, sensitive);
1720 g_signal_connect (button, "clicked", callback, impl);
1722 gtk_widget_show_all (align);
1725 gtk_widget_show (button);
1730 /* Looks for a path among the shortcuts; returns its index or -1 if it doesn't exist */
1732 shortcut_find_position (GtkFileChooserDefault *impl,
1733 const GtkFilePath *path)
1737 int current_folder_separator_idx;
1739 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
1742 current_folder_separator_idx = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1744 for (i = 0; i < current_folder_separator_idx; i++)
1749 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
1750 SHORTCUTS_COL_DATA, &col_data,
1751 SHORTCUTS_COL_IS_VOLUME, &is_volume,
1758 GtkFileSystemVolume *volume;
1759 GtkFilePath *base_path;
1763 base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
1765 exists = strcmp (gtk_file_path_get_string (path),
1766 gtk_file_path_get_string (base_path)) == 0;
1774 GtkFilePath *model_path;
1776 model_path = col_data;
1778 if (model_path && gtk_file_path_compare (model_path, path) == 0)
1783 gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
1789 /* Tries to add a bookmark from a path name */
1791 shortcuts_add_bookmark_from_path (GtkFileChooserDefault *impl,
1792 const GtkFilePath *path,
1797 if (shortcut_find_position (impl, path) != -1)
1800 /* FIXME: this check really belongs in gtk_file_system_insert_bookmark. */
1802 if (!check_is_folder (impl->file_system, path, &error))
1805 _("Could not add bookmark for %s because it is not a folder."),
1812 if (!gtk_file_system_insert_bookmark (impl->file_system, path, pos, &error))
1814 error_could_not_add_bookmark_dialog (impl, path, error);
1822 add_bookmark_foreach_cb (GtkTreeModel *model,
1827 GtkFileChooserDefault *impl;
1828 GtkFileSystemModel *fs_model;
1829 GtkTreeIter child_iter;
1830 const GtkFilePath *file_path;
1832 impl = (GtkFileChooserDefault *) data;
1834 fs_model = impl->browse_files_model;
1835 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter);
1837 file_path = _gtk_file_system_model_get_path (fs_model, &child_iter);
1838 shortcuts_add_bookmark_from_path (impl, file_path, -1);
1841 /* Adds a bookmark from the currently selected item in the file list */
1843 bookmarks_add_selected_folder (GtkFileChooserDefault *impl)
1845 GtkTreeSelection *selection;
1847 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
1849 if (gtk_tree_selection_count_selected_rows (selection) == 0)
1850 shortcuts_add_bookmark_from_path (impl, impl->current_folder, -1);
1852 gtk_tree_selection_selected_foreach (selection,
1853 add_bookmark_foreach_cb,
1857 /* Callback used when the "Add bookmark" button is clicked */
1859 add_bookmark_button_clicked_cb (GtkButton *button,
1860 GtkFileChooserDefault *impl)
1862 bookmarks_add_selected_folder (impl);
1865 /* Returns TRUE plus an iter in the shortcuts_model if a row is selected;
1866 * returns FALSE if no shortcut is selected.
1869 shortcuts_get_selected (GtkFileChooserDefault *impl,
1872 GtkTreeSelection *selection;
1873 GtkTreeIter parent_iter;
1875 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1877 if (!gtk_tree_selection_get_selected (selection, NULL, &parent_iter))
1880 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
1886 /* Removes the selected bookmarks */
1888 remove_selected_bookmarks (GtkFileChooserDefault *impl)
1897 if (!shortcuts_get_selected (impl, &iter))
1900 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
1901 SHORTCUTS_COL_DATA, &col_data,
1902 SHORTCUTS_COL_IS_VOLUME, &is_volume,
1903 SHORTCUTS_COL_REMOVABLE, &removable,
1905 g_assert (col_data != NULL);
1906 g_assert (!is_volume);
1914 if (!gtk_file_system_remove_bookmark (impl->file_system, path, &error))
1917 _("Could not remove bookmark for %s:\n%s"),
1923 /* Callback used when the "Remove bookmark" button is clicked */
1925 remove_bookmark_button_clicked_cb (GtkButton *button,
1926 GtkFileChooserDefault *impl)
1928 remove_selected_bookmarks (impl);
1931 struct selection_check_closure {
1932 GtkFileChooserDefault *impl;
1935 gboolean all_folders;
1938 /* Used from gtk_tree_selection_selected_foreach() */
1940 selection_check_foreach_cb (GtkTreeModel *model,
1945 struct selection_check_closure *closure;
1946 GtkTreeIter child_iter;
1947 const GtkFileInfo *info;
1951 closure->num_selected++;
1953 gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
1955 info = _gtk_file_system_model_get_info (closure->impl->browse_files_model, &child_iter);
1956 is_folder = gtk_file_info_get_is_folder (info);
1958 closure->all_folders = closure->all_folders && is_folder;
1959 closure->all_files = closure->all_files && !is_folder;
1962 /* Checks whether the selected items in the file list are all files or all folders */
1964 selection_check (GtkFileChooserDefault *impl,
1966 gboolean *all_files,
1967 gboolean *all_folders)
1969 struct selection_check_closure closure;
1970 GtkTreeSelection *selection;
1972 closure.impl = impl;
1973 closure.num_selected = 0;
1974 closure.all_files = TRUE;
1975 closure.all_folders = TRUE;
1977 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
1978 gtk_tree_selection_selected_foreach (selection,
1979 selection_check_foreach_cb,
1982 g_assert (closure.num_selected == 0 || !(closure.all_files && closure.all_folders));
1985 *num_selected = closure.num_selected;
1988 *all_files = closure.all_files;
1991 *all_folders = closure.all_folders;
1994 struct get_selected_path_closure {
1995 GtkFileChooserDefault *impl;
1996 const GtkFilePath *path;
2000 get_selected_path_foreach_cb (GtkTreeModel *model,
2005 struct get_selected_path_closure *closure;
2006 GtkTreeIter child_iter;
2010 gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
2011 closure->path = _gtk_file_system_model_get_path (closure->impl->browse_files_model, &child_iter);
2014 /* Returns a selected path from the file list */
2015 static const GtkFilePath *
2016 get_selected_path (GtkFileChooserDefault *impl)
2018 struct get_selected_path_closure closure;
2019 GtkTreeSelection *selection;
2021 closure.impl = impl;
2022 closure.path = NULL;
2024 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2025 gtk_tree_selection_selected_foreach (selection,
2026 get_selected_path_foreach_cb,
2029 return closure.path;
2033 GtkFileChooserDefault *impl;
2035 } UpdateTooltipData;
2038 update_tooltip (GtkTreeModel *model,
2043 UpdateTooltipData *udata = data;
2044 GtkTreeIter child_iter;
2045 const GtkFileInfo *info;
2047 if (udata->tip == NULL)
2049 gtk_tree_model_sort_convert_iter_to_child_iter (udata->impl->sort_model,
2053 info = _gtk_file_system_model_get_info (udata->impl->browse_files_model, &child_iter);
2054 udata->tip = g_strdup_printf (_("Add the folder '%s' to the bookmarks"),
2055 gtk_file_info_get_display_name (info));
2060 /* Sensitize the "add bookmark" button if all the selected items are folders, or
2061 * if there are no selected items *and* the current folder is not in the
2062 * bookmarks list. De-sensitize the button otherwise.
2065 bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl)
2068 gboolean all_folders;
2072 selection_check (impl, &num_selected, NULL, &all_folders);
2074 if (num_selected == 0)
2075 active = (shortcut_find_position (impl, impl->current_folder) == -1);
2076 else if (num_selected == 1)
2078 const GtkFilePath *path;
2080 path = get_selected_path (impl);
2081 active = all_folders && (shortcut_find_position (impl, path) == -1);
2084 active = all_folders;
2086 gtk_widget_set_sensitive (impl->browse_shortcuts_add_button, active);
2088 if (impl->browse_files_popup_menu_add_shortcut_item)
2089 gtk_widget_set_sensitive (impl->browse_files_popup_menu_add_shortcut_item,
2090 (num_selected == 0) ? FALSE : active);
2094 if (num_selected == 0)
2095 tip = g_strdup_printf (_("Add the current folder to the bookmarks"));
2096 else if (num_selected > 1)
2097 tip = g_strdup_printf (_("Add the selected folders to the bookmarks"));
2100 GtkTreeSelection *selection;
2101 UpdateTooltipData data;
2103 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2106 gtk_tree_selection_selected_foreach (selection, update_tooltip, &data);
2110 gtk_tooltips_set_tip (impl->tooltips, impl->browse_shortcuts_add_button, tip, NULL);
2115 /* Sets the sensitivity of the "remove bookmark" button depending on whether a
2116 * bookmark row is selected in the shortcuts tree.
2119 bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl)
2122 gboolean removable = FALSE;
2125 if (shortcuts_get_selected (impl, &iter))
2126 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2127 SHORTCUTS_COL_REMOVABLE, &removable,
2128 SHORTCUTS_COL_NAME, &name,
2131 gtk_widget_set_sensitive (impl->browse_shortcuts_remove_button, removable);
2137 tip = g_strdup_printf (_("Remove the bookmark '%s'"), name);
2138 gtk_tooltips_set_tip (impl->tooltips, impl->browse_shortcuts_remove_button,
2146 /* GtkWidget::drag-begin handler for the shortcuts list. */
2148 shortcuts_drag_begin_cb (GtkWidget *widget,
2149 GdkDragContext *context,
2150 GtkFileChooserDefault *impl)
2153 impl->shortcuts_drag_context = g_object_ref (context);
2158 /* Removes the idle handler for outside drags */
2160 shortcuts_cancel_drag_outside_idle (GtkFileChooserDefault *impl)
2162 if (!impl->shortcuts_drag_outside_idle)
2165 g_source_destroy (impl->shortcuts_drag_outside_idle);
2166 impl->shortcuts_drag_outside_idle = NULL;
2170 /* GtkWidget::drag-end handler for the shortcuts list. */
2172 shortcuts_drag_end_cb (GtkWidget *widget,
2173 GdkDragContext *context,
2174 GtkFileChooserDefault *impl)
2177 g_object_unref (impl->shortcuts_drag_context);
2179 shortcuts_cancel_drag_outside_idle (impl);
2181 if (!impl->shortcuts_drag_outside)
2184 gtk_button_clicked (GTK_BUTTON (impl->browse_shortcuts_remove_button));
2186 impl->shortcuts_drag_outside = FALSE;
2190 /* GtkWidget::drag-data-delete handler for the shortcuts list. */
2192 shortcuts_drag_data_delete_cb (GtkWidget *widget,
2193 GdkDragContext *context,
2194 GtkFileChooserDefault *impl)
2196 g_signal_stop_emission_by_name (widget, "drag-data-delete");
2200 /* Creates a suitable drag cursor to indicate that the selected bookmark will be
2204 shortcuts_drag_set_delete_cursor (GtkFileChooserDefault *impl,
2207 GtkTreeView *tree_view;
2210 GdkPixmap *row_pixmap;
2215 tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
2217 /* Find the selected path and get its drag pixmap */
2219 if (!shortcuts_get_selected (impl, &iter))
2220 g_assert_not_reached ();
2222 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
2224 row_pixmap = gtk_tree_view_create_row_drag_icon (tree_view, path);
2225 gtk_tree_path_free (path);
2234 pixbuf = gtk_widget_render_icon (impl->browse_shortcuts_tree_view,
2240 GdkPixmap *composite;
2241 int row_pixmap_width, row_pixmap_height;
2242 int pixbuf_width, pixbuf_height;
2243 int composite_width, composite_height;
2244 int pixbuf_x, pixbuf_y;
2245 GdkGC *gc, *mask_gc;
2247 GdkBitmap *pixbuf_mask;
2249 /* Create pixmap and mask for composite image */
2251 gdk_drawable_get_size (row_pixmap, &row_pixmap_width, &row_pixmap_height);
2252 pixbuf_width = gdk_pixbuf_get_width (pixbuf);
2253 pixbuf_height = gdk_pixbuf_get_height (pixbuf);
2255 composite_width = MAX (row_pixmap_width, pixbuf_width);
2256 composite_height = MAX (row_pixmap_height, pixbuf_height);
2258 row_pixmap_y = (composite_height - row_pixmap_height) / 2;
2260 if (gtk_widget_get_direction (impl->browse_shortcuts_tree_view) == GTK_TEXT_DIR_RTL)
2263 pixbuf_x = composite_width - pixbuf_width;
2265 pixbuf_y = (composite_height - pixbuf_height) / 2;
2267 composite = gdk_pixmap_new (row_pixmap, composite_width, composite_height, -1);
2268 gc = gdk_gc_new (composite);
2270 mask = gdk_pixmap_new (row_pixmap, composite_width, composite_height, 1);
2271 mask_gc = gdk_gc_new (mask);
2273 gdk_gc_set_foreground (mask_gc, &color);
2274 gdk_draw_rectangle (mask, mask_gc, TRUE, 0, 0, composite_width, composite_height);
2277 color.green = 0xffff;
2278 color.blue = 0xffff;
2279 gdk_gc_set_rgb_fg_color (gc, &color);
2280 gdk_draw_rectangle (composite, gc, TRUE, 0, 0, composite_width, composite_height);
2282 /* Composite the row pixmap and the pixbuf */
2284 gdk_pixbuf_render_pixmap_and_mask_for_colormap
2286 gtk_widget_get_colormap (impl->browse_shortcuts_tree_view),
2287 NULL, &pixbuf_mask, 128);
2288 gdk_draw_drawable (mask, mask_gc, pixbuf_mask,
2291 pixbuf_width, pixbuf_height);
2292 g_object_unref (pixbuf_mask);
2294 gdk_draw_drawable (composite, gc, row_pixmap,
2297 row_pixmap_width, row_pixmap_height);
2299 gdk_gc_set_foreground (mask_gc, &color);
2300 gdk_draw_rectangle (mask, mask_gc, TRUE, 0, row_pixmap_y, row_pixmap_width, row_pixmap_height);
2302 gdk_draw_pixbuf (composite, gc, pixbuf,
2305 pixbuf_width, pixbuf_height,
2309 g_object_unref (pixbuf);
2310 g_object_unref (row_pixmap);
2312 row_pixmap = composite;
2316 /* The hotspot offsets here are copied from gtk_tree_view_drag_begin(), ugh */
2318 gtk_tree_view_get_path_at_pos (tree_view,
2319 tree_view->priv->press_start_x,
2320 tree_view->priv->press_start_y,
2326 gtk_drag_set_icon_pixmap (impl->shortcuts_drag_context,
2327 gdk_drawable_get_colormap (row_pixmap),
2330 tree_view->priv->press_start_x + 1,
2331 row_pixmap_y + cell_y + 1);
2333 g_object_unref (row_pixmap);
2335 g_object_unref (mask);
2338 /* We set the delete cursor and the shortcuts_drag_outside flag in an idle
2339 * handler so that we can tell apart the drag_leave event that comes right
2340 * before a drag_drop, from a normal drag_leave. We don't want to set the
2341 * cursor nor the flag in the latter case.
2344 shortcuts_drag_outside_idle_cb (GtkFileChooserDefault *impl)
2346 shortcuts_drag_set_delete_cursor (impl, TRUE);
2347 impl->shortcuts_drag_outside = TRUE;
2349 shortcuts_cancel_drag_outside_idle (impl);
2354 /* GtkWidget::drag-leave handler for the shortcuts list. We unhighlight the
2358 shortcuts_drag_leave_cb (GtkWidget *widget,
2359 GdkDragContext *context,
2361 GtkFileChooserDefault *impl)
2364 if (gtk_drag_get_source_widget (context) == widget && !impl->shortcuts_drag_outside_idle)
2366 impl->shortcuts_drag_outside_idle = g_idle_source_new ();
2367 g_source_set_closure (impl->shortcuts_drag_outside_idle,
2368 g_cclosure_new_object (G_CALLBACK (shortcuts_drag_outside_idle_cb),
2370 g_source_attach (impl->shortcuts_drag_outside_idle, NULL);
2374 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
2376 GTK_TREE_VIEW_DROP_BEFORE);
2378 g_signal_stop_emission_by_name (widget, "drag-leave");
2381 /* Computes the appropriate row and position for dropping */
2383 shortcuts_compute_drop_position (GtkFileChooserDefault *impl,
2387 GtkTreeViewDropPosition *pos)
2389 GtkTreeView *tree_view;
2390 GtkTreeViewColumn *column;
2394 int bookmarks_index;
2396 tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
2398 bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2400 if (!gtk_tree_view_get_path_at_pos (tree_view,
2402 y - TREE_VIEW_HEADER_HEIGHT (tree_view),
2408 row = bookmarks_index + impl->num_bookmarks - 1;
2409 *path = gtk_tree_path_new_from_indices (row, -1);
2410 *pos = GTK_TREE_VIEW_DROP_AFTER;
2414 row = *gtk_tree_path_get_indices (*path);
2415 gtk_tree_view_get_background_area (tree_view, *path, column, &cell);
2416 gtk_tree_path_free (*path);
2418 if (row < bookmarks_index)
2420 row = bookmarks_index;
2421 *pos = GTK_TREE_VIEW_DROP_BEFORE;
2423 else if (row > bookmarks_index + impl->num_bookmarks - 1)
2425 row = bookmarks_index + impl->num_bookmarks - 1;
2426 *pos = GTK_TREE_VIEW_DROP_AFTER;
2430 if (cell_y < cell.height / 2)
2431 *pos = GTK_TREE_VIEW_DROP_BEFORE;
2433 *pos = GTK_TREE_VIEW_DROP_AFTER;
2436 *path = gtk_tree_path_new_from_indices (row, -1);
2439 /* GtkWidget::drag-motion handler for the shortcuts list. We basically
2440 * implement the destination side of DnD by hand, due to limitations in
2441 * GtkTreeView's DnD API.
2444 shortcuts_drag_motion_cb (GtkWidget *widget,
2445 GdkDragContext *context,
2449 GtkFileChooserDefault *impl)
2452 GtkTreeViewDropPosition pos;
2453 GdkDragAction action;
2456 if (gtk_drag_get_source_widget (context) == widget)
2458 shortcuts_cancel_drag_outside_idle (impl);
2460 if (impl->shortcuts_drag_outside)
2462 shortcuts_drag_set_delete_cursor (impl, FALSE);
2463 impl->shortcuts_drag_outside = FALSE;
2468 if (context->suggested_action == GDK_ACTION_COPY || (context->actions & GDK_ACTION_COPY) != 0)
2469 action = GDK_ACTION_COPY;
2470 else if (context->suggested_action == GDK_ACTION_MOVE || (context->actions & GDK_ACTION_MOVE) != 0)
2471 action = GDK_ACTION_MOVE;
2478 shortcuts_compute_drop_position (impl, x, y, &path, &pos);
2479 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), path, pos);
2480 gtk_tree_path_free (path);
2484 g_signal_stop_emission_by_name (widget, "drag-motion");
2488 gdk_drag_status (context, action, time_);
2495 /* GtkWidget::drag-drop handler for the shortcuts list. */
2497 shortcuts_drag_drop_cb (GtkWidget *widget,
2498 GdkDragContext *context,
2502 GtkFileChooserDefault *impl)
2505 shortcuts_cancel_drag_outside_idle (impl);
2508 g_signal_stop_emission_by_name (widget, "drag-drop");
2512 /* Parses a "text/uri-list" string and inserts its URIs as bookmarks */
2514 shortcuts_drop_uris (GtkFileChooserDefault *impl,
2521 uris = g_uri_list_extract_uris (data);
2523 for (i = 0; uris[i]; i++)
2529 path = gtk_file_system_uri_to_path (impl->file_system, uri);
2533 if (shortcuts_add_bookmark_from_path (impl, path, position))
2536 gtk_file_path_free (path);
2542 msg = g_strdup_printf (_("Could not add a bookmark for %s because it is an invalid path name."),
2544 error_message (impl, msg);
2552 /* Reorders the selected bookmark to the specified position */
2554 shortcuts_reorder (GtkFileChooserDefault *impl,
2562 int bookmarks_index;
2563 const GtkFilePath *file_path;
2564 GtkFilePath *file_path_copy;
2567 /* Get the selected path */
2569 if (!shortcuts_get_selected (impl, &iter))
2570 g_assert_not_reached ();
2572 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
2573 old_position = *gtk_tree_path_get_indices (path);
2574 gtk_tree_path_free (path);
2576 bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2577 old_position -= bookmarks_index;
2578 g_assert (old_position >= 0 && old_position < impl->num_bookmarks);
2580 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2581 SHORTCUTS_COL_DATA, &col_data,
2582 SHORTCUTS_COL_IS_VOLUME, &is_volume,
2584 g_assert (col_data != NULL);
2585 g_assert (!is_volume);
2587 file_path = col_data;
2588 file_path_copy = gtk_file_path_copy (file_path); /* removal below will free file_path, so we need a copy */
2590 /* Remove the path from the old position and insert it in the new one */
2592 if (new_position > old_position)
2595 if (old_position == new_position)
2599 if (gtk_file_system_remove_bookmark (impl->file_system, file_path_copy, &error))
2600 shortcuts_add_bookmark_from_path (impl, file_path_copy, new_position);
2602 error_could_not_add_bookmark_dialog (impl, file_path_copy, error);
2606 gtk_file_path_free (file_path_copy);
2609 /* Callback used when we get the drag data for the bookmarks list. We add the
2610 * received URIs as bookmarks if they are folders.
2613 shortcuts_drag_data_received_cb (GtkWidget *widget,
2614 GdkDragContext *context,
2617 GtkSelectionData *selection_data,
2622 GtkFileChooserDefault *impl;
2623 GtkTreePath *tree_path;
2624 GtkTreeViewDropPosition tree_pos;
2626 int bookmarks_index;
2628 impl = GTK_FILE_CHOOSER_DEFAULT (data);
2630 /* Compute position */
2632 bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2634 shortcuts_compute_drop_position (impl, x, y, &tree_path, &tree_pos);
2635 position = *gtk_tree_path_get_indices (tree_path);
2636 gtk_tree_path_free (tree_path);
2638 if (tree_pos == GTK_TREE_VIEW_DROP_AFTER)
2641 g_assert (position >= bookmarks_index);
2642 position -= bookmarks_index;
2644 if (selection_data->target == gdk_atom_intern ("text/uri-list", FALSE))
2645 shortcuts_drop_uris (impl, selection_data->data, position);
2646 else if (selection_data->target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE))
2647 shortcuts_reorder (impl, position);
2649 g_signal_stop_emission_by_name (widget, "drag-data-received");
2652 /* Callback used when the selection in the shortcuts tree changes */
2654 shortcuts_selection_changed_cb (GtkTreeSelection *selection,
2655 GtkFileChooserDefault *impl)
2657 bookmarks_check_remove_sensitivity (impl);
2661 shortcuts_row_separator_func (GtkTreeModel *model,
2665 gint column = GPOINTER_TO_INT (data);
2668 gtk_tree_model_get (model, iter, column, &text, -1);
2678 /* Since GtkTreeView has a keybinding attached to '/', we need to catch
2679 * keypresses before the TreeView gets them.
2682 tree_view_keybinding_cb (GtkWidget *tree_view,
2684 GtkFileChooserDefault *impl)
2686 if (event->keyval == GDK_slash &&
2687 ! (event->state & (~GDK_SHIFT_MASK & gtk_accelerator_get_default_mod_mask ())))
2689 location_popup_handler (impl, "/");
2697 /* Creates the widgets for the shortcuts and bookmarks tree */
2699 shortcuts_list_create (GtkFileChooserDefault *impl)
2702 GtkTreeSelection *selection;
2703 GtkTreeViewColumn *column;
2704 GtkCellRenderer *renderer;
2706 /* Scrolled window */
2708 swin = gtk_scrolled_window_new (NULL, NULL);
2709 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
2710 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2711 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
2713 gtk_widget_show (swin);
2717 impl->browse_shortcuts_tree_view = gtk_tree_view_new ();
2718 g_signal_connect (impl->browse_shortcuts_tree_view, "key-press-event",
2719 G_CALLBACK (tree_view_keybinding_cb), impl);
2720 atk_object_set_name (gtk_widget_get_accessible (impl->browse_shortcuts_tree_view), _("Shortcuts"));
2721 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), FALSE);
2723 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), impl->shortcuts_filter_model);
2725 gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
2727 shortcuts_source_targets,
2728 num_shortcuts_source_targets,
2731 gtk_drag_dest_set (impl->browse_shortcuts_tree_view,
2732 GTK_DEST_DEFAULT_ALL,
2733 shortcuts_dest_targets,
2734 num_shortcuts_dest_targets,
2735 GDK_ACTION_COPY | GDK_ACTION_MOVE);
2737 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
2738 gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
2739 gtk_tree_selection_set_select_function (selection,
2740 shortcuts_select_func,
2743 g_signal_connect (selection, "changed",
2744 G_CALLBACK (shortcuts_selection_changed_cb), impl);
2746 g_signal_connect (impl->browse_shortcuts_tree_view, "row-activated",
2747 G_CALLBACK (shortcuts_row_activated_cb), impl);
2749 g_signal_connect (impl->browse_shortcuts_tree_view, "key-press-event",
2750 G_CALLBACK (shortcuts_key_press_event_cb), impl);
2752 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-begin",
2753 G_CALLBACK (shortcuts_drag_begin_cb), impl);
2754 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-end",
2755 G_CALLBACK (shortcuts_drag_end_cb), impl);
2756 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-delete",
2757 G_CALLBACK (shortcuts_drag_data_delete_cb), impl);
2759 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-leave",
2760 G_CALLBACK (shortcuts_drag_leave_cb), impl);
2761 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-motion",
2762 G_CALLBACK (shortcuts_drag_motion_cb), impl);
2763 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-drop",
2764 G_CALLBACK (shortcuts_drag_drop_cb), impl);
2765 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-received",
2766 G_CALLBACK (shortcuts_drag_data_received_cb), impl);
2768 gtk_container_add (GTK_CONTAINER (swin), impl->browse_shortcuts_tree_view);
2769 gtk_widget_show (impl->browse_shortcuts_tree_view);
2773 column = gtk_tree_view_column_new ();
2774 gtk_tree_view_column_set_title (column, _("Folder"));
2776 renderer = gtk_cell_renderer_pixbuf_new ();
2777 gtk_tree_view_column_pack_start (column, renderer, FALSE);
2778 gtk_tree_view_column_set_attributes (column, renderer,
2779 "pixbuf", SHORTCUTS_COL_PIXBUF,
2780 "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
2783 renderer = gtk_cell_renderer_text_new ();
2784 gtk_tree_view_column_pack_start (column, renderer, TRUE);
2785 gtk_tree_view_column_set_attributes (column, renderer,
2786 "text", SHORTCUTS_COL_NAME,
2789 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
2790 shortcuts_row_separator_func,
2791 GINT_TO_POINTER (SHORTCUTS_COL_NAME),
2794 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), column);
2799 /* Creates the widgets for the shortcuts/bookmarks pane */
2801 shortcuts_pane_create (GtkFileChooserDefault *impl,
2802 GtkSizeGroup *size_group)
2808 vbox = gtk_vbox_new (FALSE, 6);
2809 gtk_widget_show (vbox);
2811 /* Shortcuts tree */
2813 widget = shortcuts_list_create (impl);
2814 gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
2816 /* Box for buttons */
2818 hbox = gtk_hbox_new (TRUE, 6);
2819 gtk_size_group_add_widget (size_group, hbox);
2820 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
2821 gtk_widget_show (hbox);
2823 /* Add bookmark button */
2825 impl->browse_shortcuts_add_button = button_new (impl,
2830 G_CALLBACK (add_bookmark_button_clicked_cb));
2831 gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_add_button, TRUE, TRUE, 0);
2832 gtk_tooltips_set_tip (impl->tooltips, impl->browse_shortcuts_add_button,
2833 _("Add the selected folder to the bookmarks"), NULL);
2835 /* Remove bookmark button */
2837 impl->browse_shortcuts_remove_button = button_new (impl,
2842 G_CALLBACK (remove_bookmark_button_clicked_cb));
2843 gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_remove_button, TRUE, TRUE, 0);
2844 gtk_tooltips_set_tip (impl->tooltips, impl->browse_shortcuts_remove_button,
2845 _("Remove the selected bookmark"), NULL);
2850 /* Handles key press events on the file list, so that we can trap Enter to
2851 * activate the default button on our own. Also, checks to see if '/' has been
2852 * pressed. See comment by tree_view_keybinding_cb() for more details.
2855 trap_activate_cb (GtkWidget *widget,
2859 GtkFileChooserDefault *impl;
2861 impl = (GtkFileChooserDefault *) data;
2863 if (event->keyval == GDK_slash &&
2864 ! (event->state & (~GDK_SHIFT_MASK & gtk_accelerator_get_default_mod_mask ())))
2866 location_popup_handler (impl, "/");
2870 if (event->keyval == GDK_Return
2871 || event->keyval == GDK_ISO_Enter
2872 || event->keyval == GDK_KP_Enter
2873 || event->keyval == GDK_space)
2877 window = get_toplevel (widget);
2879 && widget != window->default_widget
2880 && !(widget == window->focus_widget &&
2881 (!window->default_widget || !GTK_WIDGET_SENSITIVE (window->default_widget))))
2883 gtk_window_activate_default (window);
2891 /* Callback used when the file list's popup menu is detached */
2893 popup_menu_detach_cb (GtkWidget *attach_widget,
2896 GtkFileChooserDefault *impl;
2898 impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
2899 g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
2901 impl->browse_files_popup_menu = NULL;
2902 impl->browse_files_popup_menu_add_shortcut_item = NULL;
2903 impl->browse_files_popup_menu_hidden_files_item = NULL;
2906 /* Callback used when the "Add to Shortcuts" menu item is activated */
2908 add_to_shortcuts_cb (GtkMenuItem *item,
2909 GtkFileChooserDefault *impl)
2911 bookmarks_add_selected_folder (impl);
2914 /* Callback used when the "Show Hidden Files" menu item is toggled */
2916 show_hidden_toggled_cb (GtkCheckMenuItem *item,
2917 GtkFileChooserDefault *impl)
2920 "show-hidden", gtk_check_menu_item_get_active (item),
2924 /* Constructs the popup menu for the file list if needed */
2926 file_list_build_popup_menu (GtkFileChooserDefault *impl)
2930 if (impl->browse_files_popup_menu)
2933 impl->browse_files_popup_menu = gtk_menu_new ();
2934 gtk_menu_attach_to_widget (GTK_MENU (impl->browse_files_popup_menu),
2935 impl->browse_files_tree_view,
2936 popup_menu_detach_cb);
2938 item = gtk_image_menu_item_new_with_mnemonic (_("_Add to Shortcuts"));
2939 impl->browse_files_popup_menu_add_shortcut_item = item;
2940 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
2941 gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU));
2942 gtk_widget_set_sensitive (item, FALSE);
2943 g_signal_connect (item, "activate",
2944 G_CALLBACK (add_to_shortcuts_cb), impl);
2945 gtk_widget_show (item);
2946 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
2948 item = gtk_separator_menu_item_new ();
2949 gtk_widget_show (item);
2950 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
2952 item = gtk_check_menu_item_new_with_mnemonic (_("Show _Hidden Files"));
2953 impl->browse_files_popup_menu_hidden_files_item = item;
2954 g_signal_connect (item, "toggled",
2955 G_CALLBACK (show_hidden_toggled_cb), impl);
2956 gtk_widget_show (item);
2957 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
2960 /* Updates the popup menu for the file list, creating it if necessary */
2962 file_list_update_popup_menu (GtkFileChooserDefault *impl)
2964 file_list_build_popup_menu (impl);
2966 /* The sensitivity of the Add to Shortcuts item is set in
2967 * bookmarks_check_add_sensitivity()
2970 g_signal_handlers_block_by_func (impl->browse_files_popup_menu_hidden_files_item,
2971 G_CALLBACK (show_hidden_toggled_cb), impl);
2972 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (impl->browse_files_popup_menu_hidden_files_item),
2974 g_signal_handlers_unblock_by_func (impl->browse_files_popup_menu_hidden_files_item,
2975 G_CALLBACK (show_hidden_toggled_cb), impl);
2979 popup_position_func (GtkMenu *menu,
2985 GtkWidget *widget = GTK_WIDGET (user_data);
2986 GdkScreen *screen = gtk_widget_get_screen (widget);
2989 GdkRectangle monitor;
2991 g_return_if_fail (GTK_WIDGET_REALIZED (widget));
2993 gdk_window_get_origin (widget->window, x, y);
2995 gtk_widget_size_request (GTK_WIDGET (menu), &req);
2997 *x += (widget->allocation.width - req.width) / 2;
2998 *y += (widget->allocation.height - req.height) / 2;
3000 monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
3001 gtk_menu_set_monitor (menu, monitor_num);
3002 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
3004 *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
3005 *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
3011 file_list_popup_menu (GtkFileChooserDefault *impl,
3012 GdkEventButton *event)
3014 file_list_update_popup_menu (impl);
3016 gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
3017 NULL, NULL, NULL, NULL,
3018 event->button, event->time);
3021 gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
3023 popup_position_func, impl->browse_files_tree_view,
3024 0, GDK_CURRENT_TIME);
3025 gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->browse_files_popup_menu),
3031 /* Callback used for the GtkWidget::popup-menu signal of the file list */
3033 list_popup_menu_cb (GtkWidget *widget,
3034 GtkFileChooserDefault *impl)
3036 file_list_popup_menu (impl, NULL);
3040 /* Callback used when a button is pressed on the file list. We trap button 3 to
3041 * bring up a popup menu.
3044 list_button_press_event_cb (GtkWidget *widget,
3045 GdkEventButton *event,
3046 GtkFileChooserDefault *impl)
3048 if (event->button != 3)
3051 file_list_popup_menu (impl, event);
3055 /* Creates the widgets for the file list */
3057 create_file_list (GtkFileChooserDefault *impl)
3060 GtkTreeSelection *selection;
3061 GtkTreeViewColumn *column;
3062 GtkCellRenderer *renderer;
3064 /* Scrolled window */
3066 swin = gtk_scrolled_window_new (NULL, NULL);
3067 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
3068 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3069 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
3072 /* Tree/list view */
3074 impl->browse_files_tree_view = gtk_tree_view_new ();
3075 g_object_set_data (G_OBJECT (impl->browse_files_tree_view), "GtkFileChooserDefault", impl);
3076 atk_object_set_name (gtk_widget_get_accessible (impl->browse_files_tree_view), _("Files"));
3078 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->browse_files_tree_view), TRUE);
3079 gtk_container_add (GTK_CONTAINER (swin), impl->browse_files_tree_view);
3080 g_signal_connect (impl->browse_files_tree_view, "row-activated",
3081 G_CALLBACK (list_row_activated), impl);
3082 g_signal_connect (impl->browse_files_tree_view, "key-press-event",
3083 G_CALLBACK (trap_activate_cb), impl);
3084 g_signal_connect (impl->browse_files_tree_view, "popup-menu",
3085 G_CALLBACK (list_popup_menu_cb), impl);
3086 g_signal_connect (impl->browse_files_tree_view, "button-press-event",
3087 G_CALLBACK (list_button_press_event_cb), impl);
3089 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3090 gtk_tree_selection_set_select_function (selection,
3093 gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_files_tree_view),
3095 file_list_source_targets,
3096 num_file_list_source_targets,
3099 g_signal_connect (selection, "changed",
3100 G_CALLBACK (list_selection_changed), impl);
3102 /* Filename column */
3104 impl->list_name_column = gtk_tree_view_column_new ();
3105 gtk_tree_view_column_set_expand (impl->list_name_column, TRUE);
3106 gtk_tree_view_column_set_resizable (impl->list_name_column, TRUE);
3107 gtk_tree_view_column_set_title (impl->list_name_column, _("Name"));
3108 gtk_tree_view_column_set_sort_column_id (impl->list_name_column, FILE_LIST_COL_NAME);
3110 renderer = gtk_cell_renderer_pixbuf_new ();
3111 gtk_tree_view_column_pack_start (impl->list_name_column, renderer, FALSE);
3112 gtk_tree_view_column_set_cell_data_func (impl->list_name_column, renderer,
3113 list_icon_data_func, impl, NULL);
3115 impl->list_name_renderer = gtk_cell_renderer_text_new ();
3116 g_object_set (impl->list_name_renderer,
3117 "ellipsize", PANGO_ELLIPSIZE_END,
3119 g_signal_connect (impl->list_name_renderer, "edited",
3120 G_CALLBACK (renderer_edited_cb), impl);
3121 g_signal_connect (impl->list_name_renderer, "editing-canceled",
3122 G_CALLBACK (renderer_editing_canceled_cb), impl);
3123 gtk_tree_view_column_pack_start (impl->list_name_column, impl->list_name_renderer, TRUE);
3124 gtk_tree_view_column_set_cell_data_func (impl->list_name_column, impl->list_name_renderer,
3125 list_name_data_func, impl, NULL);
3127 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), impl->list_name_column);
3131 column = gtk_tree_view_column_new ();
3132 gtk_tree_view_column_set_title (column, _("Size"));
3134 renderer = gtk_cell_renderer_text_new ();
3135 gtk_tree_view_column_pack_start (column, renderer, TRUE);
3136 gtk_tree_view_column_set_cell_data_func (column, renderer,
3137 list_size_data_func, impl, NULL);
3138 gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_SIZE);
3139 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
3141 /* Modification time column */
3143 column = gtk_tree_view_column_new ();
3144 gtk_tree_view_column_set_resizable (column, TRUE);
3145 gtk_tree_view_column_set_title (column, _("Modified"));
3147 renderer = gtk_cell_renderer_text_new ();
3148 gtk_tree_view_column_pack_start (column, renderer, TRUE);
3149 gtk_tree_view_column_set_cell_data_func (column, renderer,
3150 list_mtime_data_func, impl, NULL);
3151 gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_MTIME);
3152 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
3153 gtk_widget_show_all (swin);
3159 create_path_bar (GtkFileChooserDefault *impl)
3161 GtkWidget *path_bar;
3163 path_bar = g_object_new (GTK_TYPE_PATH_BAR, NULL);
3164 _gtk_path_bar_set_file_system (GTK_PATH_BAR (path_bar), impl->file_system);
3170 set_filter_tooltip (GtkWidget *widget,
3173 GtkTooltips *tooltips = (GtkTooltips *)data;
3175 if (GTK_IS_BUTTON (widget))
3176 gtk_tooltips_set_tip (tooltips, widget,
3177 _("Select which types of files are shown"),
3182 realize_filter_combo (GtkWidget *combo,
3185 GtkFileChooserDefault *impl = (GtkFileChooserDefault *)data;
3187 gtk_container_forall (GTK_CONTAINER (combo),
3192 /* Creates the widgets for the files/folders pane */
3194 file_pane_create (GtkFileChooserDefault *impl,
3195 GtkSizeGroup *size_group)
3201 vbox = gtk_vbox_new (FALSE, 6);
3202 gtk_widget_show (vbox);
3204 /* The path bar and 'Create Folder' button */
3205 hbox = gtk_hbox_new (FALSE, 12);
3206 gtk_widget_show (hbox);
3207 impl->browse_path_bar = create_path_bar (impl);
3208 g_signal_connect (impl->browse_path_bar, "path-clicked", G_CALLBACK (path_bar_clicked), impl);
3209 gtk_widget_show_all (impl->browse_path_bar);
3210 gtk_box_pack_start (GTK_BOX (hbox), impl->browse_path_bar, TRUE, TRUE, 0);
3213 impl->browse_new_folder_button = gtk_button_new_with_mnemonic (_("Create Fo_lder"));
3214 g_signal_connect (impl->browse_new_folder_button, "clicked",
3215 G_CALLBACK (new_folder_button_clicked), impl);
3216 gtk_box_pack_end (GTK_BOX (hbox), impl->browse_new_folder_button, FALSE, FALSE, 0);
3217 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
3219 /* Box for lists and preview */
3221 hbox = gtk_hbox_new (FALSE, PREVIEW_HBOX_SPACING);
3222 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
3223 gtk_widget_show (hbox);
3227 widget = create_file_list (impl);
3228 gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
3232 impl->preview_box = gtk_vbox_new (FALSE, 12);
3233 gtk_box_pack_start (GTK_BOX (hbox), impl->preview_box, FALSE, FALSE, 0);
3234 /* Don't show preview box initially */
3238 impl->filter_combo_hbox = gtk_hbox_new (FALSE, 12);
3240 widget = filter_create (impl);
3242 g_signal_connect (widget, "realize",
3243 G_CALLBACK (realize_filter_combo), impl);
3245 gtk_widget_show (widget);
3246 gtk_box_pack_end (GTK_BOX (impl->filter_combo_hbox), widget, FALSE, FALSE, 0);
3248 gtk_size_group_add_widget (size_group, impl->filter_combo_hbox);
3249 gtk_box_pack_end (GTK_BOX (vbox), impl->filter_combo_hbox, FALSE, FALSE, 0);
3253 /* Callback used when the "Browse for more folders" expander is toggled */
3255 expander_changed_cb (GtkExpander *expander,
3257 GtkFileChooserDefault *impl)
3259 update_appearance (impl);
3262 /* Callback used when the selection changes in the save folder combo box */
3264 save_folder_combo_changed_cb (GtkComboBox *combo,
3265 GtkFileChooserDefault *impl)
3269 if (impl->changing_folder)
3272 if (gtk_combo_box_get_active_iter (combo, &iter))
3273 shortcuts_activate_iter (impl, &iter);
3276 /* Creates the combo box with the save folders */
3278 save_folder_combo_create (GtkFileChooserDefault *impl)
3281 GtkCellRenderer *cell;
3283 combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (impl->shortcuts_model));
3284 gtk_widget_show (combo);
3286 cell = gtk_cell_renderer_pixbuf_new ();
3287 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE);
3288 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
3289 "pixbuf", SHORTCUTS_COL_PIXBUF,
3290 "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
3291 "sensitive", SHORTCUTS_COL_PIXBUF_VISIBLE,
3294 cell = gtk_cell_renderer_text_new ();
3295 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
3296 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
3297 "text", SHORTCUTS_COL_NAME,
3298 "sensitive", SHORTCUTS_COL_PIXBUF_VISIBLE,
3301 gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo),
3302 shortcuts_row_separator_func,
3303 GINT_TO_POINTER (SHORTCUTS_COL_NAME),
3306 g_signal_connect (combo, "changed",
3307 G_CALLBACK (save_folder_combo_changed_cb), impl);
3312 /* Creates the widgets specific to Save mode */
3314 save_widgets_create (GtkFileChooserDefault *impl)
3319 GtkWidget *alignment;
3321 vbox = gtk_vbox_new (FALSE, 12);
3323 table = gtk_table_new (2, 2, FALSE);
3324 gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
3325 gtk_widget_show (table);
3326 gtk_table_set_row_spacings (GTK_TABLE (table), 12);
3327 gtk_table_set_col_spacings (GTK_TABLE (table), 12);
3331 widget = gtk_label_new_with_mnemonic (_("_Name:"));
3332 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
3333 gtk_table_attach (GTK_TABLE (table), widget,
3337 gtk_widget_show (widget);
3339 impl->save_file_name_entry = _gtk_file_chooser_entry_new (TRUE);
3340 _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry),
3342 gtk_entry_set_width_chars (GTK_ENTRY (impl->save_file_name_entry), 45);
3343 gtk_entry_set_activates_default (GTK_ENTRY (impl->save_file_name_entry), TRUE);
3344 gtk_table_attach (GTK_TABLE (table), impl->save_file_name_entry,
3346 GTK_EXPAND | GTK_FILL, 0,
3348 gtk_widget_show (impl->save_file_name_entry);
3349 gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->save_file_name_entry);
3352 impl->save_folder_label = gtk_label_new (NULL);
3353 gtk_misc_set_alignment (GTK_MISC (impl->save_folder_label), 0.0, 0.5);
3354 gtk_table_attach (GTK_TABLE (table), impl->save_folder_label,
3358 gtk_widget_show (impl->save_folder_label);
3360 impl->save_folder_combo = save_folder_combo_create (impl);
3361 gtk_table_attach (GTK_TABLE (table), impl->save_folder_combo,
3363 GTK_EXPAND | GTK_FILL, GTK_FILL,
3365 gtk_label_set_mnemonic_widget (GTK_LABEL (impl->save_folder_label), impl->save_folder_combo);
3368 alignment = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
3369 gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
3371 impl->save_expander = gtk_expander_new_with_mnemonic (_("_Browse for other folders"));
3372 gtk_container_add (GTK_CONTAINER (alignment), impl->save_expander);
3373 g_signal_connect (impl->save_expander, "notify::expanded",
3374 G_CALLBACK (expander_changed_cb),
3376 gtk_widget_show_all (alignment);
3381 /* Creates the main hpaned with the widgets shared by Open and Save mode */
3383 browse_widgets_create (GtkFileChooserDefault *impl)
3388 GtkSizeGroup *size_group;
3390 /* size group is used by the [+][-] buttons and the filter combo */
3391 size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
3392 vbox = gtk_vbox_new (FALSE, 12);
3395 hpaned = gtk_hpaned_new ();
3396 gtk_widget_show (hpaned);
3397 gtk_paned_set_position (GTK_PANED (hpaned), 200); /* FIXME: this sucks */
3398 gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
3400 widget = shortcuts_pane_create (impl, size_group);
3401 gtk_paned_pack1 (GTK_PANED (hpaned), widget, FALSE, FALSE);
3402 widget = file_pane_create (impl, size_group);
3403 gtk_paned_pack2 (GTK_PANED (hpaned), widget, TRUE, FALSE);
3405 g_object_unref (size_group);
3411 gtk_file_chooser_default_constructor (GType type,
3412 guint n_construct_properties,
3413 GObjectConstructParam *construct_params)
3415 GtkFileChooserDefault *impl;
3418 object = parent_class->constructor (type,
3419 n_construct_properties,
3421 impl = GTK_FILE_CHOOSER_DEFAULT (object);
3423 g_assert (impl->file_system);
3425 gtk_widget_push_composite_child ();
3427 /* Shortcuts model */
3429 shortcuts_model_create (impl);
3431 /* Widgets for Save mode */
3432 impl->save_widgets = save_widgets_create (impl);
3433 gtk_box_pack_start (GTK_BOX (impl), impl->save_widgets, FALSE, FALSE, 0);
3435 /* The browse widgets */
3436 impl->browse_widgets = browse_widgets_create (impl);
3437 gtk_box_pack_start (GTK_BOX (impl), impl->browse_widgets, TRUE, TRUE, 0);
3439 /* Alignment to hold extra widget */
3440 impl->extra_align = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
3441 gtk_box_pack_start (GTK_BOX (impl), impl->extra_align, FALSE, FALSE, 0);
3443 gtk_widget_pop_composite_child ();
3444 update_appearance (impl);
3449 /* Sets the extra_widget by packing it in the appropriate place */
3451 set_extra_widget (GtkFileChooserDefault *impl,
3452 GtkWidget *extra_widget)
3456 g_object_ref (extra_widget);
3457 /* FIXME: is this right ? */
3458 gtk_widget_show (extra_widget);
3461 if (impl->extra_widget)
3463 gtk_container_remove (GTK_CONTAINER (impl->extra_align), impl->extra_widget);
3464 g_object_unref (impl->extra_widget);
3467 impl->extra_widget = extra_widget;
3468 if (impl->extra_widget)
3470 gtk_container_add (GTK_CONTAINER (impl->extra_align), impl->extra_widget);
3471 gtk_widget_show (impl->extra_align);
3474 gtk_widget_hide (impl->extra_align);
3478 set_local_only (GtkFileChooserDefault *impl,
3479 gboolean local_only)
3481 if (local_only != impl->local_only)
3483 impl->local_only = local_only;
3485 if (impl->shortcuts_model && impl->file_system)
3487 shortcuts_add_volumes (impl);
3488 shortcuts_add_bookmarks (impl);
3492 !gtk_file_system_path_is_local (impl->file_system, impl->current_folder))
3494 /* If we are pointing to a non-local folder, make an effort to change
3495 * back to a local folder, but it's really up to the app to not cause
3496 * such a situation, so we ignore errors.
3498 const gchar *home = g_get_home_dir ();
3499 GtkFilePath *home_path;
3504 home_path = gtk_file_system_filename_to_path (impl->file_system, home);
3506 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), home_path, NULL);
3508 gtk_file_path_free (home_path);
3514 volumes_changed_cb (GtkFileSystem *file_system,
3515 GtkFileChooserDefault *impl)
3517 shortcuts_add_volumes (impl);
3520 /* Callback used when the set of bookmarks changes in the file system */
3522 bookmarks_changed_cb (GtkFileSystem *file_system,
3523 GtkFileChooserDefault *impl)
3525 shortcuts_add_bookmarks (impl);
3527 bookmarks_check_add_sensitivity (impl);
3528 bookmarks_check_remove_sensitivity (impl);
3531 /* Sets the file chooser to multiple selection mode */
3533 set_select_multiple (GtkFileChooserDefault *impl,
3534 gboolean select_multiple,
3535 gboolean property_notify)
3537 GtkTreeSelection *selection;
3538 GtkSelectionMode mode;
3540 if (select_multiple == impl->select_multiple)
3543 mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE;
3545 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
3546 gtk_tree_selection_set_mode (selection, mode);
3548 impl->select_multiple = select_multiple;
3549 g_object_notify (G_OBJECT (impl), "select-multiple");
3551 check_preview_change (impl);
3555 set_file_system_backend (GtkFileChooserDefault *impl,
3556 const char *backend)
3558 if (impl->file_system)
3560 g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
3561 impl->volumes_changed_id = 0;
3562 g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
3563 impl->bookmarks_changed_id = 0;
3564 g_object_unref (impl->file_system);
3567 impl->file_system = NULL;
3569 impl->file_system = _gtk_file_system_create (backend);
3572 GtkSettings *settings = gtk_settings_get_default ();
3573 gchar *default_backend = NULL;
3575 g_object_get (settings, "gtk-file-chooser-backend", &default_backend, NULL);
3576 if (default_backend)
3578 impl->file_system = _gtk_file_system_create (default_backend);
3579 g_free (default_backend);
3583 if (!impl->file_system)
3585 #if defined (G_OS_UNIX)
3586 impl->file_system = gtk_file_system_unix_new ();
3587 #elif defined (G_OS_WIN32)
3588 impl->file_system = gtk_file_system_win32_new ();
3590 #error "No default filesystem implementation on the platform"
3594 if (impl->file_system)
3596 impl->volumes_changed_id = g_signal_connect (impl->file_system, "volumes-changed",
3597 G_CALLBACK (volumes_changed_cb),
3599 impl->bookmarks_changed_id = g_signal_connect (impl->file_system, "bookmarks-changed",
3600 G_CALLBACK (bookmarks_changed_cb),
3605 /* This function is basically a do_all function.
3607 * It sets the visibility on all the widgets based on the current state, and
3608 * moves the custom_widget if needed.
3611 update_appearance (GtkFileChooserDefault *impl)
3613 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
3614 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
3618 gtk_widget_show (impl->save_widgets);
3620 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
3621 text = _("Save in _folder:");
3623 text = _("Create in _folder:");
3625 gtk_label_set_text_with_mnemonic (GTK_LABEL (impl->save_folder_label), text);
3627 if (gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
3629 gtk_widget_set_sensitive (impl->save_folder_label, FALSE);
3630 gtk_widget_set_sensitive (impl->save_folder_combo, FALSE);
3631 gtk_widget_show (impl->browse_widgets);
3635 gtk_widget_set_sensitive (impl->save_folder_label, TRUE);
3636 gtk_widget_set_sensitive (impl->save_folder_combo, TRUE);
3637 gtk_widget_hide (impl->browse_widgets);
3640 gtk_widget_show (impl->browse_new_folder_button);
3642 if (impl->select_multiple)
3644 g_warning ("Save mode cannot be set in conjunction with multiple selection mode. "
3645 "Re-setting to single selection mode.");
3646 set_select_multiple (impl, FALSE, TRUE);
3649 else if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
3650 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
3652 gtk_widget_hide (impl->save_widgets);
3653 gtk_widget_show (impl->browse_widgets);
3656 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
3657 gtk_widget_hide (impl->browse_new_folder_button);
3659 gtk_widget_show (impl->browse_new_folder_button);
3661 gtk_widget_queue_draw (impl->browse_files_tree_view);
3663 g_signal_emit_by_name (impl, "default-size-changed");
3667 gtk_file_chooser_default_set_property (GObject *object,
3669 const GValue *value,
3673 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
3677 case GTK_FILE_CHOOSER_PROP_ACTION:
3679 GtkFileChooserAction action = g_value_get_enum (value);
3681 if (action != impl->action)
3683 gtk_file_chooser_default_unselect_all (GTK_FILE_CHOOSER (impl));
3685 if (action == GTK_FILE_CHOOSER_ACTION_SAVE && impl->select_multiple)
3687 g_warning ("Multiple selection mode is not allowed in Save mode");
3688 set_select_multiple (impl, FALSE, TRUE);
3690 impl->action = action;
3691 update_appearance (impl);
3694 if (impl->save_file_name_entry)
3695 _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry),
3699 case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
3700 set_file_system_backend (impl, g_value_get_string (value));
3702 case GTK_FILE_CHOOSER_PROP_FILTER:
3703 set_current_filter (impl, g_value_get_object (value));
3705 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
3706 set_local_only (impl, g_value_get_boolean (value));
3708 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
3709 set_preview_widget (impl, g_value_get_object (value));
3711 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
3712 impl->preview_widget_active = g_value_get_boolean (value);
3713 update_preview_widget_visibility (impl);
3715 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
3716 impl->use_preview_label = g_value_get_boolean (value);
3717 update_preview_widget_visibility (impl);
3719 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
3720 set_extra_widget (impl, g_value_get_object (value));
3722 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
3724 gboolean select_multiple = g_value_get_boolean (value);
3725 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE && select_multiple)
3727 g_warning ("Multiple selection mode is not allowed in Save mode");
3731 set_select_multiple (impl, select_multiple, FALSE);
3734 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
3736 gboolean show_hidden = g_value_get_boolean (value);
3737 if (show_hidden != impl->show_hidden)
3739 impl->show_hidden = show_hidden;
3741 if (impl->browse_files_model)
3742 _gtk_file_system_model_set_show_hidden (impl->browse_files_model, show_hidden);
3747 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3753 gtk_file_chooser_default_get_property (GObject *object,
3758 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
3762 case GTK_FILE_CHOOSER_PROP_ACTION:
3763 g_value_set_enum (value, impl->action);
3765 case GTK_FILE_CHOOSER_PROP_FILTER:
3766 g_value_set_object (value, impl->current_filter);
3768 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
3769 g_value_set_boolean (value, impl->local_only);
3771 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
3772 g_value_set_object (value, impl->preview_widget);
3774 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
3775 g_value_set_boolean (value, impl->preview_widget_active);
3777 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
3778 g_value_set_boolean (value, impl->use_preview_label);
3780 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
3781 g_value_set_object (value, impl->extra_widget);
3783 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
3784 g_value_set_boolean (value, impl->select_multiple);
3786 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
3787 g_value_set_boolean (value, impl->show_hidden);
3790 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3795 /* Removes the settings signal handler. It's safe to call multiple times */
3797 remove_settings_signal (GtkFileChooserDefault *impl,
3800 if (impl->settings_signal_id)
3802 GtkSettings *settings;
3804 settings = gtk_settings_get_for_screen (screen);
3805 g_signal_handler_disconnect (settings,
3806 impl->settings_signal_id);
3807 impl->settings_signal_id = 0;
3812 gtk_file_chooser_default_dispose (GObject *object)
3814 GtkFileChooserDefault *impl = (GtkFileChooserDefault *) object;
3816 if (impl->extra_widget)
3818 g_object_unref (impl->extra_widget);
3819 impl->extra_widget = NULL;
3822 remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl)));
3824 G_OBJECT_CLASS (parent_class)->dispose (object);
3827 /* We override show-all since we have internal widgets that
3828 * shouldn't be shown when you call show_all(), like the filter
3832 gtk_file_chooser_default_show_all (GtkWidget *widget)
3834 GtkFileChooserDefault *impl = (GtkFileChooserDefault *) widget;
3836 gtk_widget_show (widget);
3838 if (impl->extra_widget)
3839 gtk_widget_show_all (impl->extra_widget);
3842 /* Handler for GtkWindow::set-focus; this is where we save the last-focused
3843 * widget on our toplevel. See gtk_file_chooser_default_hierarchy_changed()
3846 toplevel_set_focus_cb (GtkWindow *window,
3848 GtkFileChooserDefault *impl)
3850 impl->toplevel_last_focus_widget = gtk_window_get_focus (window);
3853 /* We monitor the focus widget on our toplevel to be able to know which widget
3854 * was last focused at the time our "should_respond" method gets called.
3857 gtk_file_chooser_default_hierarchy_changed (GtkWidget *widget,
3858 GtkWidget *previous_toplevel)
3860 GtkFileChooserDefault *impl;
3861 GtkWidget *toplevel;
3863 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
3865 if (previous_toplevel)
3867 g_assert (impl->toplevel_set_focus_id != 0);
3868 g_signal_handler_disconnect (previous_toplevel, impl->toplevel_set_focus_id);
3869 impl->toplevel_set_focus_id = 0;
3870 impl->toplevel_last_focus_widget = NULL;
3873 g_assert (impl->toplevel_set_focus_id == 0);
3875 toplevel = gtk_widget_get_toplevel (widget);
3876 if (GTK_IS_WINDOW (toplevel))
3878 impl->toplevel_set_focus_id = g_signal_connect (toplevel, "set-focus",
3879 G_CALLBACK (toplevel_set_focus_cb), impl);
3880 impl->toplevel_last_focus_widget = gtk_window_get_focus (GTK_WINDOW (toplevel));
3884 /* Changes the icons wherever it is needed */
3886 change_icon_theme (GtkFileChooserDefault *impl)
3888 GtkSettings *settings;
3891 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
3893 if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_SMALL_TOOLBAR, &width, &height))
3894 impl->icon_size = MAX (width, height);
3896 impl->icon_size = FALLBACK_ICON_SIZE;
3898 shortcuts_reload_icons (impl);
3899 gtk_widget_queue_resize (impl->browse_files_tree_view);
3902 /* Callback used when a GtkSettings value changes */
3904 settings_notify_cb (GObject *object,
3906 GtkFileChooserDefault *impl)
3910 name = g_param_spec_get_name (pspec);
3912 if (strcmp (name, "gtk-icon-theme-name") == 0
3913 || strcmp (name, "gtk-icon-sizes") == 0)
3914 change_icon_theme (impl);
3917 /* Installs a signal handler for GtkSettings so that we can monitor changes in
3921 check_icon_theme (GtkFileChooserDefault *impl)
3923 GtkSettings *settings;
3925 if (impl->settings_signal_id)
3928 if (gtk_widget_has_screen (GTK_WIDGET (impl)))
3930 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
3931 impl->settings_signal_id = g_signal_connect (settings, "notify",
3932 G_CALLBACK (settings_notify_cb), impl);
3934 change_icon_theme (impl);
3939 gtk_file_chooser_default_style_set (GtkWidget *widget,
3940 GtkStyle *previous_style)
3942 GtkFileChooserDefault *impl;
3944 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
3946 if (GTK_WIDGET_CLASS (parent_class)->style_set)
3947 GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
3949 if (gtk_widget_has_screen (GTK_WIDGET (impl)))
3950 change_icon_theme (impl);
3952 g_signal_emit_by_name (widget, "default-size-changed");
3956 gtk_file_chooser_default_screen_changed (GtkWidget *widget,
3957 GdkScreen *previous_screen)
3959 GtkFileChooserDefault *impl;
3961 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
3963 if (GTK_WIDGET_CLASS (parent_class)->screen_changed)
3964 GTK_WIDGET_CLASS (parent_class)->screen_changed (widget, previous_screen);
3966 remove_settings_signal (impl, previous_screen);
3967 check_icon_theme (impl);
3969 g_signal_emit_by_name (widget, "default-size-changed");
3973 list_model_filter_func (GtkFileSystemModel *model,
3975 const GtkFileInfo *file_info,
3978 GtkFileChooserDefault *impl = user_data;
3979 GtkFileFilterInfo filter_info;
3980 GtkFileFilterFlags needed;
3983 if (!impl->current_filter)
3986 if (gtk_file_info_get_is_folder (file_info))
3989 filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
3991 needed = gtk_file_filter_get_needed (impl->current_filter);
3993 filter_info.display_name = gtk_file_info_get_display_name (file_info);
3994 filter_info.mime_type = gtk_file_info_get_mime_type (file_info);
3996 if (needed & GTK_FILE_FILTER_FILENAME)
3998 filter_info.filename = gtk_file_system_path_to_filename (impl->file_system, path);
3999 if (filter_info.filename)
4000 filter_info.contains |= GTK_FILE_FILTER_FILENAME;
4003 filter_info.filename = NULL;
4005 if (needed & GTK_FILE_FILTER_URI)
4007 filter_info.uri = gtk_file_system_path_to_uri (impl->file_system, path);
4008 if (filter_info.uri)
4009 filter_info.contains |= GTK_FILE_FILTER_URI;
4012 filter_info.uri = NULL;
4014 result = gtk_file_filter_filter (impl->current_filter, &filter_info);
4016 if (filter_info.filename)
4017 g_free ((gchar *)filter_info.filename);
4018 if (filter_info.uri)
4019 g_free ((gchar *)filter_info.uri);
4025 install_list_model_filter (GtkFileChooserDefault *impl)
4027 g_assert (impl->browse_files_model != NULL);
4029 if (impl->current_filter)
4030 _gtk_file_system_model_set_filter (impl->browse_files_model,
4031 list_model_filter_func,
4035 #define COMPARE_DIRECTORIES \
4036 GtkFileChooserDefault *impl = user_data; \
4037 const GtkFileInfo *info_a = _gtk_file_system_model_get_info (impl->browse_files_model, a); \
4038 const GtkFileInfo *info_b = _gtk_file_system_model_get_info (impl->browse_files_model, b); \
4039 gboolean dir_a, dir_b; \
4042 dir_a = gtk_file_info_get_is_folder (info_a); \
4044 return impl->list_sort_ascending ? -1 : 1; \
4047 dir_b = gtk_file_info_get_is_folder (info_b); \
4049 return impl->list_sort_ascending ? 1 : -1; \
4051 if (dir_a != dir_b) \
4052 return impl->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */
4054 /* Sort callback for the filename column */
4056 name_sort_func (GtkTreeModel *model,
4061 COMPARE_DIRECTORIES;
4063 return strcmp (gtk_file_info_get_display_key (info_a), gtk_file_info_get_display_key (info_b));
4066 /* Sort callback for the size column */
4068 size_sort_func (GtkTreeModel *model,
4073 COMPARE_DIRECTORIES;
4076 gint64 size_a = gtk_file_info_get_size (info_a);
4077 gint64 size_b = gtk_file_info_get_size (info_b);
4079 return size_a > size_b ? -1 : (size_a == size_b ? 0 : 1);
4083 /* Sort callback for the mtime column */
4085 mtime_sort_func (GtkTreeModel *model,
4090 COMPARE_DIRECTORIES;
4093 GtkFileTime ta = gtk_file_info_get_modification_time (info_a);
4094 GtkFileTime tb = gtk_file_info_get_modification_time (info_b);
4096 return ta > tb ? -1 : (ta == tb ? 0 : 1);
4100 /* Callback used when the sort column changes. We cache the sort order for use
4101 * in name_sort_func().
4104 list_sort_column_changed_cb (GtkTreeSortable *sortable,
4105 GtkFileChooserDefault *impl)
4107 GtkSortType sort_type;
4109 if (gtk_tree_sortable_get_sort_column_id (sortable, NULL, &sort_type))
4110 impl->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
4114 set_busy_cursor (GtkFileChooserDefault *impl,
4117 GtkWindow *toplevel;
4118 GdkDisplay *display;
4121 toplevel = get_toplevel (GTK_WIDGET (impl));
4122 if (!toplevel || !GTK_WIDGET_REALIZED (toplevel))
4125 display = gtk_widget_get_display (GTK_WIDGET (toplevel));
4128 cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
4132 gdk_window_set_cursor (GTK_WIDGET (toplevel)->window, cursor);
4133 gdk_display_flush (display);
4136 gdk_cursor_unref (cursor);
4139 /* Creates a sort model to wrap the file system model and sets it on the tree view */
4141 load_set_model (GtkFileChooserDefault *impl)
4143 g_assert (impl->browse_files_model != NULL);
4144 g_assert (impl->sort_model == NULL);
4146 impl->sort_model = (GtkTreeModelSort *)gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (impl->browse_files_model));
4147 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, name_sort_func, impl, NULL);
4148 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_SIZE, size_sort_func, impl, NULL);
4149 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_MTIME, mtime_sort_func, impl, NULL);
4150 gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->sort_model), NULL, NULL, NULL);
4151 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, GTK_SORT_ASCENDING);
4152 impl->list_sort_ascending = TRUE;
4154 g_signal_connect (impl->sort_model, "sort-column-changed",
4155 G_CALLBACK (list_sort_column_changed_cb), impl);
4157 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
4158 GTK_TREE_MODEL (impl->sort_model));
4159 gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->browse_files_tree_view));
4160 gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_files_tree_view),
4161 GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
4164 /* Timeout callback used when the loading timer expires */
4166 load_timeout_cb (gpointer data)
4168 GtkFileChooserDefault *impl;
4170 impl = GTK_FILE_CHOOSER_DEFAULT (data);
4171 g_assert (impl->load_state == LOAD_LOADING);
4172 g_assert (impl->load_timeout_id != 0);
4173 g_assert (impl->browse_files_model != NULL);
4175 impl->load_timeout_id = 0;
4176 impl->load_state = LOAD_FINISHED;
4178 load_set_model (impl);
4183 /* Sets up a new load timer for the model and switches to the LOAD_LOADING state */
4185 load_setup_timer (GtkFileChooserDefault *impl)
4187 g_assert (impl->load_timeout_id == 0);
4188 g_assert (impl->load_state == LOAD_FINISHED);
4190 impl->load_timeout_id = g_timeout_add (MAX_LOADING_TIME, load_timeout_cb, impl);
4191 impl->load_state = LOAD_LOADING;
4194 /* Removes the load timeout and switches to the LOAD_FINISHED state */
4196 load_remove_timer (GtkFileChooserDefault *impl)
4198 if (impl->load_timeout_id != 0)
4200 g_assert (impl->load_state == LOAD_LOADING);
4202 g_source_remove (impl->load_timeout_id);
4203 impl->load_timeout_id = 0;
4204 impl->load_state = LOAD_FINISHED;
4207 g_assert (impl->load_state == LOAD_FINISHED);
4210 /* Callback used when the file system model finishes loading */
4212 browse_files_model_finished_loading_cb (GtkFileSystemModel *model,
4213 GtkFileChooserDefault *impl)
4215 if (impl->load_state == LOAD_LOADING)
4217 load_remove_timer (impl);
4218 load_set_model (impl);
4221 g_assert (impl->load_state == LOAD_FINISHED);
4223 set_busy_cursor (impl, FALSE);
4226 /* Gets rid of the old list model and creates a new one for the current folder */
4228 set_list_model (GtkFileChooserDefault *impl,
4231 load_remove_timer (impl);
4233 if (impl->browse_files_model)
4235 g_object_unref (impl->browse_files_model);
4236 impl->browse_files_model = NULL;
4239 if (impl->sort_model)
4241 g_object_unref (impl->sort_model);
4242 impl->sort_model = NULL;
4245 set_busy_cursor (impl, TRUE);
4246 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
4248 impl->browse_files_model = _gtk_file_system_model_new (impl->file_system,
4249 impl->current_folder, 0,
4252 if (!impl->browse_files_model)
4254 set_busy_cursor (impl, FALSE);
4258 load_setup_timer (impl);
4260 g_signal_connect (impl->browse_files_model, "finished-loading",
4261 G_CALLBACK (browse_files_model_finished_loading_cb), impl);
4263 _gtk_file_system_model_set_show_hidden (impl->browse_files_model, impl->show_hidden);
4265 install_list_model_filter (impl);
4271 update_chooser_entry (GtkFileChooserDefault *impl)
4273 GtkTreeSelection *selection;
4274 const GtkFileInfo *info;
4276 GtkTreeIter child_iter;
4278 if (impl->action != GTK_FILE_CHOOSER_ACTION_SAVE)
4281 g_assert (!impl->select_multiple);
4282 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4284 if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
4287 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
4291 info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
4293 if (!gtk_file_info_get_is_folder (info))
4294 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry),
4295 gtk_file_info_get_display_name (info));
4299 gtk_file_chooser_default_set_current_folder (GtkFileChooser *chooser,
4300 const GtkFilePath *path,
4303 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4306 if (impl->local_only &&
4307 !gtk_file_system_path_is_local (impl->file_system, path))
4310 GTK_FILE_SYSTEM_ERROR,
4311 GTK_FILE_SYSTEM_ERROR_FAILED,
4312 _("Cannot change to folder because it is not local"));
4317 /* Test validity of path here. */
4318 if (!check_is_folder (impl->file_system, path, error))
4321 if (!_gtk_path_bar_set_path (GTK_PATH_BAR (impl->browse_path_bar), path, error))
4324 if (impl->current_folder != path)
4326 if (impl->current_folder)
4327 gtk_file_path_free (impl->current_folder);
4329 impl->current_folder = gtk_file_path_copy (path);
4332 /* Update the widgets that may trigger a folder change themselves. */
4334 if (!impl->changing_folder)
4336 impl->changing_folder = TRUE;
4338 shortcuts_update_current_folder (impl);
4340 impl->changing_folder = FALSE;
4343 /* Set the folder on the save entry */
4345 _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry),
4346 impl->current_folder);
4348 /* Create a new list model. This is slightly evil; we store the result value
4349 * but perform more actions rather than returning immediately even if it
4350 * generates an error.
4352 result = set_list_model (impl, error);
4354 /* Refresh controls */
4356 shortcuts_find_current_folder (impl);
4358 g_signal_emit_by_name (impl, "current-folder-changed", 0);
4360 check_preview_change (impl);
4361 bookmarks_check_add_sensitivity (impl);
4363 g_signal_emit_by_name (impl, "selection-changed", 0);
4368 static GtkFilePath *
4369 gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser)
4371 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4373 return gtk_file_path_copy (impl->current_folder);
4377 gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
4380 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4382 g_return_if_fail (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
4383 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
4385 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry), name);
4389 select_func (GtkFileSystemModel *model,
4394 GtkFileChooserDefault *impl = user_data;
4395 GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
4396 GtkTreePath *sorted_path;
4398 sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model, path);
4399 gtk_tree_view_set_cursor (tree_view, sorted_path, NULL, FALSE);
4400 gtk_tree_view_scroll_to_cell (tree_view, sorted_path, NULL, FALSE, 0.0, 0.0);
4401 gtk_tree_path_free (sorted_path);
4405 gtk_file_chooser_default_select_path (GtkFileChooser *chooser,
4406 const GtkFilePath *path,
4409 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4410 GtkFilePath *parent_path;
4412 if (!gtk_file_system_get_parent (impl->file_system, path, &parent_path, error))
4416 return _gtk_file_chooser_set_current_folder_path (chooser, path, error);
4420 GtkFileFolder *folder;
4424 result = _gtk_file_chooser_set_current_folder_path (chooser, parent_path, error);
4428 gtk_file_path_free (parent_path);
4432 folder = gtk_file_system_get_folder (impl->file_system, parent_path, GTK_FILE_INFO_IS_HIDDEN, error);
4433 gtk_file_path_free (parent_path);
4438 info = gtk_file_folder_get_info (folder, path, error);
4439 g_object_unref (folder);
4444 is_hidden = gtk_file_info_get_is_hidden (info);
4445 gtk_file_info_free (info);
4448 g_object_set (impl, "show-hidden", TRUE, NULL);
4450 result = _gtk_file_system_model_path_do (impl->browse_files_model, path,
4454 GTK_FILE_CHOOSER_ERROR,
4455 GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
4456 _("Could not find the path"));
4461 g_assert_not_reached ();
4465 unselect_func (GtkFileSystemModel *model,
4470 GtkFileChooserDefault *impl = user_data;
4471 GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
4472 GtkTreePath *sorted_path;
4474 sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model,
4476 gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (tree_view),
4478 gtk_tree_path_free (sorted_path);
4482 gtk_file_chooser_default_unselect_path (GtkFileChooser *chooser,
4483 const GtkFilePath *path)
4485 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4487 if (!impl->browse_files_model)
4490 _gtk_file_system_model_path_do (impl->browse_files_model, path,
4491 unselect_func, impl);
4495 maybe_select (GtkTreeModel *model,
4500 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (data);
4501 GtkTreeSelection *selection;
4502 const GtkFileInfo *info;
4505 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4507 info = get_list_file_info (impl, iter);
4508 is_folder = gtk_file_info_get_is_folder (info);
4510 if ((is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) ||
4511 (!is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN))
4512 gtk_tree_selection_select_iter (selection, iter);
4514 gtk_tree_selection_unselect_iter (selection, iter);
4520 gtk_file_chooser_default_select_all (GtkFileChooser *chooser)
4522 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4523 if (impl->select_multiple)
4524 gtk_tree_model_foreach (GTK_TREE_MODEL (impl->sort_model),
4525 maybe_select, impl);
4529 gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser)
4531 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4532 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4534 gtk_tree_selection_unselect_all (selection);
4537 /* Checks whether the filename entry for the Save modes contains a valid filename */
4538 static GtkFilePath *
4539 check_save_entry (GtkFileChooserDefault *impl,
4543 GtkFileChooserEntry *chooser_entry;
4544 const GtkFilePath *current_folder;
4545 const char *file_part;
4549 g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
4550 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
4552 chooser_entry = GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry);
4554 current_folder = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
4555 file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
4557 if (!file_part || file_part[0] == '\0')
4567 path = gtk_file_system_make_path (impl->file_system, current_folder, file_part, &error);
4571 error_building_filename_dialog (impl, current_folder, file_part, error);
4580 struct get_paths_closure {
4581 GtkFileChooserDefault *impl;
4583 GtkFilePath *path_from_entry;
4587 get_paths_foreach (GtkTreeModel *model,
4592 struct get_paths_closure *info;
4593 const GtkFilePath *file_path;
4594 GtkFileSystemModel *fs_model;
4595 GtkTreeIter sel_iter;
4598 fs_model = info->impl->browse_files_model;
4599 gtk_tree_model_sort_convert_iter_to_child_iter (info->impl->sort_model, &sel_iter, iter);
4601 file_path = _gtk_file_system_model_get_path (fs_model, &sel_iter);
4603 return; /* We are on the editable row */
4605 if (!info->path_from_entry
4606 || gtk_file_path_compare (info->path_from_entry, file_path) != 0)
4607 info->result = g_slist_prepend (info->result, gtk_file_path_copy (file_path));
4611 gtk_file_chooser_default_get_paths (GtkFileChooser *chooser)
4613 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4614 struct get_paths_closure info;
4618 info.path_from_entry = NULL;
4620 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
4621 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4623 gboolean is_valid, is_empty;
4625 info.path_from_entry = check_save_entry (impl, &is_valid, &is_empty);
4626 if (!is_valid && !is_empty)
4630 if (!info.path_from_entry || impl->select_multiple)
4632 GtkTreeSelection *selection;
4634 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4635 gtk_tree_selection_selected_foreach (selection, get_paths_foreach, &info);
4638 if (info.path_from_entry)
4639 info.result = g_slist_prepend (info.result, info.path_from_entry);
4641 /* If there's no folder selected, and we're in SELECT_FOLDER mode, then we
4642 * fall back to the current directory */
4643 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
4644 info.result == NULL)
4646 info.result = g_slist_prepend (info.result, gtk_file_path_copy (impl->current_folder));
4649 return g_slist_reverse (info.result);
4652 static GtkFilePath *
4653 gtk_file_chooser_default_get_preview_path (GtkFileChooser *chooser)
4655 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4657 if (impl->preview_path)
4658 return gtk_file_path_copy (impl->preview_path);
4663 static GtkFileSystem *
4664 gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser)
4666 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4668 return impl->file_system;
4671 /* Shows or hides the filter widgets */
4673 show_filters (GtkFileChooserDefault *impl,
4677 gtk_widget_show (impl->filter_combo_hbox);
4679 gtk_widget_hide (impl->filter_combo_hbox);
4683 gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
4684 GtkFileFilter *filter)
4686 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4689 if (g_slist_find (impl->filters, filter))
4691 g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
4695 g_object_ref (filter);
4696 gtk_object_sink (GTK_OBJECT (filter));
4697 impl->filters = g_slist_append (impl->filters, filter);
4699 name = gtk_file_filter_get_name (filter);
4701 name = "Untitled filter"; /* Place-holder, doesn't need to be marked for translation */
4703 gtk_combo_box_append_text (GTK_COMBO_BOX (impl->filter_combo), name);
4705 if (!g_slist_find (impl->filters, impl->current_filter))
4706 set_current_filter (impl, filter);
4708 show_filters (impl, TRUE);
4712 gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
4713 GtkFileFilter *filter)
4715 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4716 GtkTreeModel *model;
4720 filter_index = g_slist_index (impl->filters, filter);
4722 if (filter_index < 0)
4724 g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
4728 impl->filters = g_slist_remove (impl->filters, filter);
4730 if (filter == impl->current_filter)
4733 set_current_filter (impl, impl->filters->data);
4735 set_current_filter (impl, NULL);
4738 /* Remove row from the combo box */
4739 model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->filter_combo));
4740 gtk_tree_model_iter_nth_child (model, &iter, NULL, filter_index);
4741 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
4743 g_object_unref (filter);
4746 show_filters (impl, FALSE);
4750 gtk_file_chooser_default_list_filters (GtkFileChooser *chooser)
4752 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4754 return g_slist_copy (impl->filters);
4757 /* Returns the position in the shortcuts tree where the nth specified shortcut would appear */
4759 shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl,
4762 return pos + shortcuts_get_index (impl, SHORTCUTS_SHORTCUTS);
4766 gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser *chooser,
4767 const GtkFilePath *path,
4770 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4774 /* Test validity of path here. */
4775 if (!check_is_folder (impl->file_system, path, error))
4778 pos = shortcuts_get_pos_for_shortcut_folder (impl, impl->num_shortcuts);
4780 result = shortcuts_insert_path (impl, pos, FALSE, NULL, path, NULL, FALSE, error);
4783 impl->num_shortcuts++;
4785 if (impl->shortcuts_filter_model)
4786 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
4792 gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser *chooser,
4793 const GtkFilePath *path,
4796 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4802 if (impl->num_shortcuts == 0)
4805 pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
4806 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
4807 g_assert_not_reached ();
4809 for (i = 0; i < impl->num_shortcuts; i++)
4813 GtkFilePath *shortcut;
4815 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
4816 SHORTCUTS_COL_DATA, &col_data,
4817 SHORTCUTS_COL_IS_VOLUME, &is_volume,
4819 g_assert (col_data != NULL);
4820 g_assert (!is_volume);
4822 shortcut = col_data;
4823 if (gtk_file_path_compare (shortcut, path) == 0)
4825 shortcuts_remove_rows (impl, pos + i, 1);
4826 impl->num_shortcuts--;
4830 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
4831 g_assert_not_reached ();
4836 uri = gtk_file_system_path_to_uri (impl->file_system, path);
4838 GTK_FILE_CHOOSER_ERROR,
4839 GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
4840 _("shortcut %s does not exist"),
4848 gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser)
4850 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
4856 if (impl->num_shortcuts == 0)
4859 pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
4860 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
4861 g_assert_not_reached ();
4865 for (i = 0; i < impl->num_shortcuts; i++)
4869 GtkFilePath *shortcut;
4871 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
4872 SHORTCUTS_COL_DATA, &col_data,
4873 SHORTCUTS_COL_IS_VOLUME, &is_volume,
4875 g_assert (col_data != NULL);
4876 g_assert (!is_volume);
4878 shortcut = col_data;
4879 list = g_slist_prepend (list, gtk_file_path_copy (shortcut));
4881 if (i != impl->num_shortcuts - 1)
4883 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
4884 g_assert_not_reached ();
4888 return g_slist_reverse (list);
4891 /* Guesses a size based upon font sizes */
4893 find_good_size_from_style (GtkWidget *widget,
4897 GtkFileChooserDefault *impl;
4898 gint default_width, default_height;
4901 GtkRequisition preview_req;
4903 g_assert (widget->style != NULL);
4904 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
4906 font_size = pango_font_description_get_size (widget->style->font_desc);
4907 font_size = PANGO_PIXELS (font_size);
4909 default_width = font_size * NUM_CHARS;
4910 default_height = font_size * NUM_LINES;
4912 /* Use at least the requisition size not including the preview widget */
4913 gtk_widget_size_request (widget, &req);
4915 if (impl->preview_widget_active && impl->preview_widget)
4916 gtk_widget_size_request (impl->preview_box, &preview_req);
4918 preview_req.width = 0;
4920 default_width = MAX (default_width, (req.width - (preview_req.width + PREVIEW_HBOX_SPACING)));
4921 default_height = MAX (default_height, req.height);
4923 *width = default_width;
4924 *height = default_height;
4928 gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
4929 gint *default_width,
4930 gint *default_height)
4932 GtkFileChooserDefault *impl;
4934 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
4936 find_good_size_from_style (GTK_WIDGET (chooser_embed), default_width, default_height);
4938 if (impl->preview_widget_active && impl->preview_widget)
4939 *default_width += impl->preview_box->requisition.width + PREVIEW_HBOX_SPACING;
4943 gtk_file_chooser_default_get_resizable_hints (GtkFileChooserEmbed *chooser_embed,
4944 gboolean *resize_horizontally,
4945 gboolean *resize_vertically)
4947 GtkFileChooserDefault *impl;
4949 g_return_if_fail (resize_horizontally != NULL);
4950 g_return_if_fail (resize_vertically != NULL);
4952 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
4954 *resize_horizontally = TRUE;
4955 *resize_vertically = TRUE;
4957 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
4958 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
4960 if (! gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
4962 *resize_horizontally = FALSE;
4963 *resize_vertically = FALSE;
4968 struct switch_folder_closure {
4969 GtkFileChooserDefault *impl;
4970 const GtkFilePath *path;
4974 /* Used from gtk_tree_selection_selected_foreach() in switch_to_selected_folder() */
4976 switch_folder_foreach_cb (GtkTreeModel *model,
4981 struct switch_folder_closure *closure;
4982 GtkTreeIter child_iter;
4986 gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
4988 closure->path = _gtk_file_system_model_get_path (closure->impl->browse_files_model, &child_iter);
4989 closure->num_selected++;
4992 /* Changes to the selected folder in the list view */
4994 switch_to_selected_folder (GtkFileChooserDefault *impl)
4996 GtkTreeSelection *selection;
4997 struct switch_folder_closure closure;
4999 /* We do this with foreach() rather than get_selected() as we may be in
5000 * multiple selection mode
5003 closure.impl = impl;
5004 closure.path = NULL;
5005 closure.num_selected = 0;
5007 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5008 gtk_tree_selection_selected_foreach (selection, switch_folder_foreach_cb, &closure);
5010 g_assert (closure.path && closure.num_selected == 1);
5012 change_folder_and_display_error (impl, closure.path);
5015 /* Implementation for GtkFileChooserEmbed::should_respond() */
5017 gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
5019 GtkFileChooserDefault *impl;
5020 GtkWidget *toplevel;
5021 GtkWidget *current_focus;
5023 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
5025 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
5026 g_assert (GTK_IS_WINDOW (toplevel));
5028 current_focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
5030 if (current_focus == impl->browse_files_tree_view)
5033 gboolean all_files, all_folders;
5037 selection_check (impl, &num_selected, &all_files, &all_folders);
5039 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5041 if (num_selected != 1)
5042 return TRUE; /* zero means current folder; more than one means use the whole selection */
5043 else if (current_focus != impl->browse_files_tree_view)
5045 /* a single folder is selected and a button was clicked */
5046 switch_to_selected_folder (impl);
5050 if (num_selected == 0)
5052 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5053 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5054 goto save_entry; /* it makes sense to use the typed name */
5059 if (num_selected == 1 && all_folders)
5061 switch_to_selected_folder (impl);
5067 else if (current_focus == impl->save_file_name_entry)
5070 gboolean is_valid, is_empty;
5073 GtkFileChooserEntry *entry;
5077 g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5078 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
5080 entry = GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry);
5081 path = check_save_entry (impl, &is_valid, &is_empty);
5083 if (!is_empty && !is_valid)
5087 path = gtk_file_path_copy (_gtk_file_chooser_entry_get_current_folder (entry));
5089 is_folder = check_is_folder (impl->file_system, path, NULL);
5092 _gtk_file_chooser_entry_set_file_part (entry, "");
5093 change_folder_and_display_error (impl, path);
5099 gtk_file_path_free (path);
5102 else if (impl->toplevel_last_focus_widget == impl->browse_shortcuts_tree_view)
5104 /* The focus is on a dialog's action area button, *and* the widget that
5105 * was focused immediately before it is the shortcuts list. Switch to the
5106 * selected shortcut and tell the caller not to respond.
5110 if (shortcuts_get_selected (impl, &iter))
5112 shortcuts_activate_iter (impl, &iter);
5114 gtk_widget_grab_focus (impl->browse_files_tree_view);
5121 else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
5123 /* The focus is on a dialog's action area button, *and* the widget that
5124 * was focused immediately before it is the file list.
5129 /* The focus is on a dialog's action area button or something else */
5130 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5131 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5136 g_assert_not_reached ();
5140 /* Implementation for GtkFileChooserEmbed::initial_focus() */
5142 gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed)
5144 GtkFileChooserDefault *impl;
5147 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
5149 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
5150 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5152 if (impl->load_state == LOAD_FINISHED)
5156 path = gtk_tree_path_new_from_indices (0, -1);
5157 gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), path, NULL, FALSE);
5158 gtk_tree_path_free (path);
5161 widget = impl->browse_files_tree_view;
5163 else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5164 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5165 widget = impl->save_file_name_entry;
5168 g_assert_not_reached ();
5172 gtk_widget_grab_focus (widget);
5176 set_current_filter (GtkFileChooserDefault *impl,
5177 GtkFileFilter *filter)
5179 if (impl->current_filter != filter)
5183 /* If we have filters, new filter must be one of them
5185 filter_index = g_slist_index (impl->filters, filter);
5186 if (impl->filters && filter_index < 0)
5189 if (impl->current_filter)
5190 g_object_unref (impl->current_filter);
5191 impl->current_filter = filter;
5192 if (impl->current_filter)
5194 g_object_ref (impl->current_filter);
5195 gtk_object_sink (GTK_OBJECT (filter));
5199 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
5202 if (impl->browse_files_model)
5203 install_list_model_filter (impl);
5205 g_object_notify (G_OBJECT (impl), "filter");
5210 filter_combo_changed (GtkComboBox *combo_box,
5211 GtkFileChooserDefault *impl)
5213 gint new_index = gtk_combo_box_get_active (combo_box);
5214 GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
5216 set_current_filter (impl, new_filter);
5220 check_preview_change (GtkFileChooserDefault *impl)
5222 GtkTreePath *cursor_path;
5223 const GtkFilePath *new_path;
5224 const GtkFileInfo *new_info;
5226 gtk_tree_view_get_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), &cursor_path, NULL);
5227 if (cursor_path && impl->sort_model)
5230 GtkTreeIter child_iter;
5232 gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, cursor_path);
5233 gtk_tree_path_free (cursor_path);
5235 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
5237 new_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
5238 new_info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
5246 if (new_path != impl->preview_path &&
5247 !(new_path && impl->preview_path &&
5248 gtk_file_path_compare (new_path, impl->preview_path) == 0))
5250 if (impl->preview_path)
5252 gtk_file_path_free (impl->preview_path);
5253 g_free (impl->preview_display_name);
5258 impl->preview_path = gtk_file_path_copy (new_path);
5259 impl->preview_display_name = g_strdup (gtk_file_info_get_display_name (new_info));
5263 impl->preview_path = NULL;
5264 impl->preview_display_name = NULL;
5267 if (impl->use_preview_label && impl->preview_label)
5268 gtk_label_set_text (GTK_LABEL (impl->preview_label), impl->preview_display_name);
5270 g_signal_emit_by_name (impl, "update-preview");
5274 /* Activates a volume by mounting it if necessary and then switching to its
5278 shortcuts_activate_volume (GtkFileChooserDefault *impl,
5279 GtkFileSystemVolume *volume)
5283 /* We ref the file chooser since volume_mount() may run a main loop, and the
5284 * user could close the file chooser window in the meantime.
5286 g_object_ref (impl);
5288 if (!gtk_file_system_volume_get_is_mounted (impl->file_system, volume))
5293 set_busy_cursor (impl, TRUE);
5296 result = gtk_file_system_volume_mount (impl->file_system, volume, &error);
5302 msg = g_strdup_printf ("Could not mount %s:\n%s",
5303 gtk_file_system_volume_get_display_name (impl->file_system, volume),
5305 error_message (impl, msg);
5307 g_error_free (error);
5310 set_busy_cursor (impl, FALSE);
5316 path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
5317 change_folder_and_display_error (impl, path);
5318 gtk_file_path_free (path);
5322 g_object_unref (impl);
5325 /* Opens the folder or volume at the specified iter in the shortcuts model */
5327 shortcuts_activate_iter (GtkFileChooserDefault *impl,
5333 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
5334 SHORTCUTS_COL_DATA, &col_data,
5335 SHORTCUTS_COL_IS_VOLUME, &is_volume,
5339 return; /* We are on a separator */
5343 GtkFileSystemVolume *volume;
5347 shortcuts_activate_volume (impl, volume);
5351 const GtkFilePath *file_path;
5353 file_path = col_data;
5354 change_folder_and_display_error (impl, file_path);
5358 /* Callback used when a row in the shortcuts list is activated */
5360 shortcuts_row_activated_cb (GtkTreeView *tree_view,
5362 GtkTreeViewColumn *column,
5363 GtkFileChooserDefault *impl)
5366 GtkTreeIter child_iter;
5368 if (!gtk_tree_model_get_iter (impl->shortcuts_filter_model, &iter, path))
5371 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
5374 shortcuts_activate_iter (impl, &child_iter);
5376 gtk_widget_grab_focus (impl->browse_files_tree_view);
5379 /* Handler for GtkWidget::key-press-event on the shortcuts list */
5381 shortcuts_key_press_event_cb (GtkWidget *widget,
5383 GtkFileChooserDefault *impl)
5387 modifiers = gtk_accelerator_get_default_mod_mask ();
5389 if ((event->keyval == GDK_BackSpace
5390 || event->keyval == GDK_Delete
5391 || event->keyval == GDK_KP_Delete)
5392 && (event->state & modifiers) == 0)
5394 remove_selected_bookmarks (impl);
5402 shortcuts_select_func (GtkTreeSelection *selection,
5403 GtkTreeModel *model,
5405 gboolean path_currently_selected,
5408 GtkFileChooserDefault *impl = data;
5410 return (*gtk_tree_path_get_indices (path) != shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR));
5414 list_select_func (GtkTreeSelection *selection,
5415 GtkTreeModel *model,
5417 gboolean path_currently_selected,
5420 GtkFileChooserDefault *impl = data;
5422 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
5423 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5425 GtkTreeIter iter, child_iter;
5426 const GtkFileInfo *info;
5428 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
5431 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
5433 info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
5435 if (!gtk_file_info_get_is_folder (info))
5443 list_selection_changed (GtkTreeSelection *selection,
5444 GtkFileChooserDefault *impl)
5446 /* See if we are in the new folder editable row for Save mode */
5447 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
5449 GtkTreeSelection *selection;
5450 GtkTreeIter iter, child_iter;
5451 const GtkFileInfo *info;
5453 g_assert (!impl->select_multiple);
5454 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5455 if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
5458 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
5462 info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
5464 return; /* We are on the editable row for New Folder */
5467 update_chooser_entry (impl);
5468 check_preview_change (impl);
5469 bookmarks_check_add_sensitivity (impl);
5471 g_signal_emit_by_name (impl, "selection-changed", 0);
5474 /* Callback used when a row in the file list is activated */
5476 list_row_activated (GtkTreeView *tree_view,
5478 GtkTreeViewColumn *column,
5479 GtkFileChooserDefault *impl)
5481 GtkTreeIter iter, child_iter;
5482 const GtkFileInfo *info;
5484 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
5487 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
5489 info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
5491 if (gtk_file_info_get_is_folder (info))
5493 const GtkFilePath *file_path;
5495 file_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
5496 change_folder_and_display_error (impl, file_path);
5501 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
5502 impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
5503 g_signal_emit_by_name (impl, "file-activated");
5507 path_bar_clicked (GtkPathBar *path_bar,
5508 GtkFilePath *file_path,
5509 gboolean child_is_hidden,
5510 GtkFileChooserDefault *impl)
5512 if (!change_folder_and_display_error (impl, file_path))
5515 /* Say we have "/foo/bar/[.baz]" and the user clicks on "bar". We should then
5516 * show hidden files so that ".baz" appears in the file list, as it will still
5517 * be shown in the path bar: "/foo/[bar]/.baz"
5519 if (child_is_hidden)
5520 g_object_set (impl, "show-hidden", TRUE, NULL);
5523 static const GtkFileInfo *
5524 get_list_file_info (GtkFileChooserDefault *impl,
5527 GtkTreeIter child_iter;
5529 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
5533 return _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
5537 list_icon_data_func (GtkTreeViewColumn *tree_column,
5538 GtkCellRenderer *cell,
5539 GtkTreeModel *tree_model,
5543 GtkFileChooserDefault *impl = data;
5544 GtkTreeIter child_iter;
5545 const GtkFilePath *path;
5547 const GtkFileInfo *info;
5548 gboolean sensitive = TRUE;
5550 info = get_list_file_info (impl, iter);
5552 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
5555 path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter);
5559 /* FIXME: NULL GError */
5560 pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
5561 impl->icon_size, NULL);
5563 if (info && (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
5564 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
5565 sensitive = gtk_file_info_get_is_folder (info);
5569 "sensitive", sensitive,
5573 g_object_unref (pixbuf);
5577 list_name_data_func (GtkTreeViewColumn *tree_column,
5578 GtkCellRenderer *cell,
5579 GtkTreeModel *tree_model,
5583 GtkFileChooserDefault *impl = data;
5584 const GtkFileInfo *info = get_list_file_info (impl, iter);
5585 gboolean sensitive = TRUE;
5590 "text", _("Type name of new folder"),
5597 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
5598 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5600 sensitive = gtk_file_info_get_is_folder (info);
5604 "text", gtk_file_info_get_display_name (info),
5605 "sensitive", sensitive,
5611 list_size_data_func (GtkTreeViewColumn *tree_column,
5612 GtkCellRenderer *cell,
5613 GtkTreeModel *tree_model,
5617 GtkFileChooserDefault *impl = data;
5618 const GtkFileInfo *info = get_list_file_info (impl, iter);
5621 gboolean sensitive = TRUE;
5623 if (!info || gtk_file_info_get_is_folder (info))
5625 g_object_set (cell,"sensitive", sensitive, NULL);
5629 size = gtk_file_info_get_size (info);
5631 if (size < (gint64)1024)
5632 str = g_strdup_printf (ngettext ("%d byte", "%d bytes", (gint)size), (gint)size);
5633 else if (size < (gint64)1024*1024)
5634 str = g_strdup_printf (_("%.1f K"), size / (1024.));
5635 else if (size < (gint64)1024*1024*1024)
5636 str = g_strdup_printf (_("%.1f M"), size / (1024.*1024.));
5638 str = g_strdup_printf (_("%.1f G"), size / (1024.*1024.*1024.));
5640 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
5641 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5646 "sensitive", sensitive,
5653 /* Tree column data callback for the file list; fetches the mtime of a file */
5655 list_mtime_data_func (GtkTreeViewColumn *tree_column,
5656 GtkCellRenderer *cell,
5657 GtkTreeModel *tree_model,
5661 GtkFileChooserDefault *impl;
5662 const GtkFileInfo *info;
5663 GtkFileTime time_mtime, time_now;
5667 gboolean sensitive = TRUE;
5671 info = get_list_file_info (impl, iter);
5681 time_mtime = gtk_file_info_get_modification_time (info);
5682 g_date_set_time (&mtime, (GTime) time_mtime);
5684 time_now = (GTime ) time (NULL);
5685 g_date_set_time (&now, (GTime) time_now);
5687 days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
5690 strcpy (buf, _("Today"));
5691 else if (days_diff == 1)
5692 strcpy (buf, _("Yesterday"));
5697 if (days_diff > 1 && days_diff < 7)
5698 format = "%A"; /* Days from last week */
5700 format = "%x"; /* Any other date */
5702 if (g_date_strftime (buf, sizeof (buf), format, &mtime) == 0)
5703 strcpy (buf, _("Unknown"));
5706 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
5707 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5708 sensitive = gtk_file_info_get_is_folder (info);
5712 "sensitive", sensitive,
5717 _gtk_file_chooser_default_new (const char *file_system)
5719 return g_object_new (GTK_TYPE_FILE_CHOOSER_DEFAULT,
5720 "file-system-backend", file_system,
5725 location_entry_create (GtkFileChooserDefault *impl,
5730 entry = _gtk_file_chooser_entry_new (TRUE);
5731 /* Pick a good width for the entry */
5732 gtk_entry_set_width_chars (GTK_ENTRY (entry), 30);
5733 gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
5734 _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (entry), impl->file_system);
5735 _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (entry), impl->action);
5738 _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (entry),
5739 gtk_file_path_new_steal ((gchar *)path));
5740 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (entry), path);
5744 _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (entry), impl->current_folder);
5745 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
5746 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5747 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (entry), "");
5748 else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5749 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5750 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (entry),
5751 gtk_entry_get_text (GTK_ENTRY (impl->save_file_name_entry)));
5753 g_assert_not_reached ();
5756 return GTK_WIDGET (entry);
5760 update_from_entry (GtkFileChooserDefault *impl,
5762 GtkFileChooserEntry *chooser_entry)
5764 const GtkFilePath *folder_path;
5765 const char *file_part;
5767 folder_path = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
5768 file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
5770 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN && !folder_path)
5772 error_message_with_parent (parent,
5773 _("Cannot change to the folder you specified as it is an invalid path."));
5777 if (file_part[0] == '\0')
5778 return change_folder_and_display_error (impl, folder_path);
5781 GtkFileFolder *folder = NULL;
5782 GtkFilePath *subfolder_path = NULL;
5783 GtkFileInfo *info = NULL;
5789 /* If the file part is non-empty, we need to figure out if it refers to a
5790 * folder within folder. We could optimize the case here where the folder
5791 * is already loaded for one of our tree models.
5795 folder = gtk_file_system_get_folder (impl->file_system, folder_path, GTK_FILE_INFO_IS_FOLDER, &error);
5799 error_getting_info_dialog (impl, folder_path, error);
5804 subfolder_path = gtk_file_system_make_path (impl->file_system, folder_path, file_part, &error);
5806 if (!subfolder_path)
5811 uri = gtk_file_system_path_to_uri (impl->file_system, folder_path);
5812 msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
5815 error_message (impl, msg);
5822 info = gtk_file_folder_get_info (folder, subfolder_path, &error);
5826 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5827 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5829 if (!change_folder_and_display_error (impl, folder_path))
5832 gtk_file_chooser_default_set_current_name (GTK_FILE_CHOOSER (impl), file_part);
5835 error_getting_info_dialog (impl, subfolder_path, error);
5840 if (gtk_file_info_get_is_folder (info))
5841 result = change_folder_and_display_error (impl, subfolder_path);
5847 result = _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (impl), subfolder_path, &error);
5850 _("Could not select %s:\n%s"),
5851 subfolder_path, error);
5857 g_object_unref (folder);
5859 gtk_file_path_free (subfolder_path);
5862 gtk_file_info_free (info);
5867 g_assert_not_reached ();
5871 location_popup_handler (GtkFileChooserDefault *impl,
5875 GtkWindow *toplevel;
5881 const char *accept_stock;
5885 toplevel = get_toplevel (GTK_WIDGET (impl));
5887 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
5888 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5890 title = _("Open Location");
5891 accept_stock = GTK_STOCK_OPEN;
5895 g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5896 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
5897 title = _("Save in Location");
5898 accept_stock = GTK_STOCK_SAVE;
5901 dialog = gtk_dialog_new_with_buttons (title,
5903 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
5904 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
5905 accept_stock, GTK_RESPONSE_ACCEPT,
5907 gtk_window_set_default_size (GTK_WINDOW (dialog), 300, -1);
5908 gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
5909 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2);
5910 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
5912 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
5913 GTK_RESPONSE_ACCEPT,
5914 GTK_RESPONSE_CANCEL,
5917 hbox = gtk_hbox_new (FALSE, 12);
5918 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0);
5919 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
5921 label = gtk_label_new_with_mnemonic (_("_Location:"));
5922 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
5924 entry = location_entry_create (impl, path);
5926 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
5927 gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
5931 gtk_widget_show_all (dialog);
5932 /* If the dialog is brought up by typing the first characters
5933 * of a path, unselect the text in the entry, so that you can
5934 * just type on without erasing the initial part.
5937 gtk_editable_select_region (GTK_EDITABLE (entry), -1, -1);
5941 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
5943 if (update_from_entry (impl, GTK_WINDOW (dialog), GTK_FILE_CHOOSER_ENTRY (entry)))
5945 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
5946 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5948 gtk_widget_grab_focus (impl->browse_files_tree_view);
5952 g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
5953 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
5954 gtk_widget_grab_focus (impl->save_file_name_entry);
5962 GtkWindow *toplevel;
5964 toplevel = get_toplevel (GTK_WIDGET (impl));
5965 if (toplevel && toplevel->focus_widget)
5966 gtk_widget_grab_focus (toplevel->focus_widget);
5969 gtk_widget_destroy (dialog);
5972 /* Handler for the "up-folder" keybinding signal */
5974 up_folder_handler (GtkFileChooserDefault *impl)
5976 _gtk_path_bar_up (GTK_PATH_BAR (impl->browse_path_bar));
5979 /* Handler for the "down-folder" keybinding signal */
5981 down_folder_handler (GtkFileChooserDefault *impl)
5983 _gtk_path_bar_down (GTK_PATH_BAR (impl->browse_path_bar));
5986 /* Handler for the "home-folder" keybinding signal */
5988 home_folder_handler (GtkFileChooserDefault *impl)
5993 if (!impl->has_home)
5994 return; /* Should we put up an error dialog? */
5996 pos = shortcuts_get_index (impl, SHORTCUTS_HOME);
5997 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
5998 g_assert_not_reached ();
6000 shortcuts_activate_iter (impl, &iter);
6005 /* Drag and drop interfaces */
6008 _shortcuts_model_filter_class_init (ShortcutsModelFilterClass *class)
6013 _shortcuts_model_filter_init (ShortcutsModelFilter *model)
6018 /* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */
6020 shortcuts_model_filter_row_draggable (GtkTreeDragSource *drag_source,
6023 ShortcutsModelFilter *model;
6027 model = SHORTCUTS_MODEL_FILTER (drag_source);
6029 pos = *gtk_tree_path_get_indices (path);
6030 bookmarks_pos = shortcuts_get_index (model->impl, SHORTCUTS_BOOKMARKS);
6032 return (pos >= bookmarks_pos && pos < bookmarks_pos + model->impl->num_bookmarks);
6035 /* GtkTreeDragSource::drag_data_get implementation for the shortcuts filter model */
6037 shortcuts_model_filter_drag_data_get (GtkTreeDragSource *drag_source,
6039 GtkSelectionData *selection_data)
6041 ShortcutsModelFilter *model;
6043 model = SHORTCUTS_MODEL_FILTER (drag_source);
6050 /* Fill the GtkTreeDragSourceIface vtable */
6052 shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface)
6054 iface->row_draggable = shortcuts_model_filter_row_draggable;
6055 iface->drag_data_get = shortcuts_model_filter_drag_data_get;
6059 /* Fill the GtkTreeDragDestIface vtable */
6061 shortcuts_model_filter_drag_dest_iface_init (GtkTreeDragDestIface *iface)
6063 iface->drag_data_received = shortcuts_model_filter_drag_data_received;
6064 iface->row_drop_possible = shortcuts_model_filter_row_drop_possible;
6068 static GtkTreeModel *
6069 shortcuts_model_filter_new (GtkFileChooserDefault *impl,
6070 GtkTreeModel *child_model,
6073 ShortcutsModelFilter *model;
6075 model = g_object_new (SHORTCUTS_MODEL_FILTER_TYPE,
6076 "child_model", child_model,
6077 "virtual_root", root,
6082 return GTK_TREE_MODEL (model);