1 /* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
2 /* GTK - The GIMP Toolkit
3 * gtkfilechooserdefault.c: Default implementation of GtkFileChooser
4 * Copyright (C) 2003, Red Hat, Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
24 #include "gdk/gdkkeysyms.h"
25 #include "gtkalignment.h"
26 #include "gtkbindings.h"
27 #include "gtkcelllayout.h"
28 #include "gtkcellrendererpixbuf.h"
29 #include "gtkcellrenderertext.h"
30 #include "gtkcheckmenuitem.h"
31 #include "gtkclipboard.h"
32 #include "gtkcombobox.h"
34 #include "gtkexpander.h"
35 #include "gtkfilechooserprivate.h"
36 #include "gtkfilechooserdefault.h"
37 #include "gtkfilechooserembed.h"
38 #include "gtkfilechooserentry.h"
39 #include "gtkfilechoosersettings.h"
40 #include "gtkfilechooserutils.h"
41 #include "gtkfilechooser.h"
42 #include "gtkfilesystem.h"
43 #include "gtkfilesystemmodel.h"
46 #include "gtkhpaned.h"
47 #include "gtkiconfactory.h"
48 #include "gtkicontheme.h"
50 #include "gtkimagemenuitem.h"
52 #include "gtkmarshalers.h"
53 #include "gtkmessagedialog.h"
54 #include "gtkmountoperation.h"
55 #include "gtkpathbar.h"
56 #include "gtkprivate.h"
57 #include "gtkradiobutton.h"
58 #include "gtkrecentfilter.h"
59 #include "gtkrecentmanager.h"
60 #include "gtkscrolledwindow.h"
61 #include "gtkseparatormenuitem.h"
62 #include "gtksizegroup.h"
65 #include "gtktooltip.h"
66 #include "gtktreednd.h"
67 #include "gtktreeprivate.h"
68 #include "gtktreeselection.h"
78 #include <sys/types.h>
89 #undef PROFILE_FILE_CHOOSER
90 #ifdef PROFILE_FILE_CHOOSER
97 #define PROFILE_INDENT 4
98 static int profile_indent;
101 profile_add_indent (int indent)
103 profile_indent += indent;
104 if (profile_indent < 0)
105 g_error ("You screwed up your indentation");
109 _gtk_file_chooser_profile_log (const char *func, int indent, const char *msg1, const char *msg2)
114 profile_add_indent (indent);
116 if (profile_indent == 0)
117 str = g_strdup_printf ("MARK: %s %s %s", func ? func : "", msg1 ? msg1 : "", msg2 ? msg2 : "");
119 str = g_strdup_printf ("MARK: %*c %s %s %s", profile_indent - 1, ' ', func ? func : "", msg1 ? msg1 : "", msg2 ? msg2 : "");
125 profile_add_indent (indent);
128 #define profile_start(x, y) _gtk_file_chooser_profile_log (G_STRFUNC, PROFILE_INDENT, x, y)
129 #define profile_end(x, y) _gtk_file_chooser_profile_log (G_STRFUNC, -PROFILE_INDENT, x, y)
130 #define profile_msg(x, y) _gtk_file_chooser_profile_log (NULL, 0, x, y)
132 #define profile_start(x, y)
133 #define profile_end(x, y)
134 #define profile_msg(x, y)
139 typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
141 #define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
142 #define GTK_IS_FILE_CHOOSER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT))
143 #define GTK_FILE_CHOOSER_DEFAULT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
145 #define MAX_LOADING_TIME 500
147 struct _GtkFileChooserDefaultClass
149 GtkVBoxClass parent_class;
155 LOCATION_POPUP_ON_PASTE,
161 LOCATION_TOGGLE_POPUP,
169 static guint signals[LAST_SIGNAL] = { 0 };
171 /* Column numbers for the shortcuts tree. Keep these in sync with shortcuts_model_create() */
173 SHORTCUTS_COL_PIXBUF,
177 SHORTCUTS_COL_REMOVABLE,
178 SHORTCUTS_COL_PIXBUF_VISIBLE,
179 SHORTCUTS_COL_CANCELLABLE,
180 SHORTCUTS_COL_NUM_COLUMNS
185 SHORTCUT_TYPE_VOLUME,
186 SHORTCUT_TYPE_SEPARATOR,
187 SHORTCUT_TYPE_SEARCH,
191 /* Column numbers for the file list */
196 FILE_LIST_COL_NUM_COLUMNS
199 /* Column numbers for the search model.
200 * Keep this in sync with search_setup_model()
203 SEARCH_MODEL_COL_FILE,
204 SEARCH_MODEL_COL_DISPLAY_NAME,
205 SEARCH_MODEL_COL_COLLATION_KEY,
206 SEARCH_MODEL_COL_STAT,
207 SEARCH_MODEL_COL_CANCELLABLE,
208 SEARCH_MODEL_COL_PIXBUF,
209 SEARCH_MODEL_COL_MIME_TYPE,
210 SEARCH_MODEL_COL_IS_FOLDER,
211 SEARCH_MODEL_COL_NUM_COLUMNS
215 RECENT_MODEL_COL_FILE,
216 RECENT_MODEL_COL_DISPLAY_NAME,
217 RECENT_MODEL_COL_INFO,
218 RECENT_MODEL_COL_IS_FOLDER,
219 RECENT_MODEL_COL_CANCELLABLE,
220 RECENT_MODEL_COL_NUM_COLUMNS
223 /* Identifiers for target types */
229 search_is_possible (GtkFileChooserDefault *impl)
231 if (impl->search_engine == NULL)
232 impl->search_engine = _gtk_search_engine_new ();
234 return impl->search_engine != NULL;
237 /* Interesting places in the shortcuts bar */
241 SHORTCUTS_RECENT_SEPARATOR,
246 SHORTCUTS_BOOKMARKS_SEPARATOR,
248 SHORTCUTS_CURRENT_FOLDER_SEPARATOR,
249 SHORTCUTS_CURRENT_FOLDER
252 /* Icon size for if we can't get it from the theme */
253 #define FALLBACK_ICON_SIZE 16
255 #define PREVIEW_HBOX_SPACING 12
259 static void gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface);
260 static void gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface);
262 static GObject* gtk_file_chooser_default_constructor (GType type,
263 guint n_construct_properties,
264 GObjectConstructParam *construct_params);
265 static void gtk_file_chooser_default_finalize (GObject *object);
266 static void gtk_file_chooser_default_set_property (GObject *object,
270 static void gtk_file_chooser_default_get_property (GObject *object,
274 static void gtk_file_chooser_default_dispose (GObject *object);
275 static void gtk_file_chooser_default_show_all (GtkWidget *widget);
276 static void gtk_file_chooser_default_map (GtkWidget *widget);
277 static void gtk_file_chooser_default_unmap (GtkWidget *widget);
278 static void gtk_file_chooser_default_hierarchy_changed (GtkWidget *widget,
279 GtkWidget *previous_toplevel);
280 static void gtk_file_chooser_default_style_set (GtkWidget *widget,
281 GtkStyle *previous_style);
282 static void gtk_file_chooser_default_screen_changed (GtkWidget *widget,
283 GdkScreen *previous_screen);
284 static void gtk_file_chooser_default_size_allocate (GtkWidget *widget,
285 GtkAllocation *allocation);
287 static gboolean gtk_file_chooser_default_set_current_folder (GtkFileChooser *chooser,
290 static gboolean gtk_file_chooser_default_update_current_folder (GtkFileChooser *chooser,
293 gboolean clear_entry,
295 static GFile * gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser);
296 static void gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
298 static gboolean gtk_file_chooser_default_select_file (GtkFileChooser *chooser,
301 static void gtk_file_chooser_default_unselect_file (GtkFileChooser *chooser,
303 static void gtk_file_chooser_default_select_all (GtkFileChooser *chooser);
304 static void gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser);
305 static GSList * gtk_file_chooser_default_get_files (GtkFileChooser *chooser);
306 static GFile * gtk_file_chooser_default_get_preview_file (GtkFileChooser *chooser);
307 static GtkFileSystem *gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser);
308 static void gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
309 GtkFileFilter *filter);
310 static void gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
311 GtkFileFilter *filter);
312 static GSList * gtk_file_chooser_default_list_filters (GtkFileChooser *chooser);
313 static gboolean gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser *chooser,
316 static gboolean gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser *chooser,
319 static GSList * gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser);
321 static void gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
323 gint *default_height);
324 static gboolean gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed);
325 static void gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed);
327 static void location_popup_handler (GtkFileChooserDefault *impl,
329 static void location_popup_on_paste_handler (GtkFileChooserDefault *impl);
330 static void location_toggle_popup_handler (GtkFileChooserDefault *impl);
331 static void up_folder_handler (GtkFileChooserDefault *impl);
332 static void down_folder_handler (GtkFileChooserDefault *impl);
333 static void home_folder_handler (GtkFileChooserDefault *impl);
334 static void desktop_folder_handler (GtkFileChooserDefault *impl);
335 static void quick_bookmark_handler (GtkFileChooserDefault *impl,
336 gint bookmark_index);
337 static void show_hidden_handler (GtkFileChooserDefault *impl);
338 static void search_shortcut_handler (GtkFileChooserDefault *impl);
339 static void recent_shortcut_handler (GtkFileChooserDefault *impl);
340 static void update_appearance (GtkFileChooserDefault *impl);
342 static void set_current_filter (GtkFileChooserDefault *impl,
343 GtkFileFilter *filter);
344 static void check_preview_change (GtkFileChooserDefault *impl);
346 static void filter_combo_changed (GtkComboBox *combo_box,
347 GtkFileChooserDefault *impl);
349 static gboolean shortcuts_key_press_event_cb (GtkWidget *widget,
351 GtkFileChooserDefault *impl);
353 static gboolean shortcuts_select_func (GtkTreeSelection *selection,
356 gboolean path_currently_selected,
358 static gboolean shortcuts_get_selected (GtkFileChooserDefault *impl,
360 static void shortcuts_activate_iter (GtkFileChooserDefault *impl,
362 static int shortcuts_get_index (GtkFileChooserDefault *impl,
363 ShortcutsIndex where);
364 static int shortcut_find_position (GtkFileChooserDefault *impl,
367 static void bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl);
369 static gboolean list_select_func (GtkTreeSelection *selection,
372 gboolean path_currently_selected,
375 static void list_selection_changed (GtkTreeSelection *tree_selection,
376 GtkFileChooserDefault *impl);
377 static void list_row_activated (GtkTreeView *tree_view,
379 GtkTreeViewColumn *column,
380 GtkFileChooserDefault *impl);
382 static void select_func (GtkFileSystemModel *model,
387 static void path_bar_clicked (GtkPathBar *path_bar,
390 gboolean child_is_hidden,
391 GtkFileChooserDefault *impl);
393 static void add_bookmark_button_clicked_cb (GtkButton *button,
394 GtkFileChooserDefault *impl);
395 static void remove_bookmark_button_clicked_cb (GtkButton *button,
396 GtkFileChooserDefault *impl);
397 static void save_folder_combo_changed_cb (GtkComboBox *combo,
398 GtkFileChooserDefault *impl);
400 static void list_icon_data_func (GtkTreeViewColumn *tree_column,
401 GtkCellRenderer *cell,
402 GtkTreeModel *tree_model,
405 static void list_name_data_func (GtkTreeViewColumn *tree_column,
406 GtkCellRenderer *cell,
407 GtkTreeModel *tree_model,
411 static void list_size_data_func (GtkTreeViewColumn *tree_column,
412 GtkCellRenderer *cell,
413 GtkTreeModel *tree_model,
417 static void list_mtime_data_func (GtkTreeViewColumn *tree_column,
418 GtkCellRenderer *cell,
419 GtkTreeModel *tree_model,
423 static GFileInfo *get_list_file_info (GtkFileChooserDefault *impl,
426 static void load_remove_timer (GtkFileChooserDefault *impl);
427 static void browse_files_center_selected_row (GtkFileChooserDefault *impl);
429 static void location_button_toggled_cb (GtkToggleButton *toggle,
430 GtkFileChooserDefault *impl);
431 static void location_switch_to_path_bar (GtkFileChooserDefault *impl);
433 static void search_stop_searching (GtkFileChooserDefault *impl,
434 gboolean remove_query);
435 static void search_clear_model (GtkFileChooserDefault *impl,
436 gboolean remove_from_treeview);
437 static gboolean search_should_respond (GtkFileChooserDefault *impl);
438 static void search_switch_to_browse_mode (GtkFileChooserDefault *impl);
439 static GSList *search_get_selected_files (GtkFileChooserDefault *impl);
440 static void search_entry_activate_cb (GtkEntry *entry,
442 static void settings_load (GtkFileChooserDefault *impl);
443 static void search_get_valid_child_iter (GtkFileChooserDefault *impl,
444 GtkTreeIter *child_iter,
447 static void recent_stop_loading (GtkFileChooserDefault *impl);
448 static void recent_clear_model (GtkFileChooserDefault *impl,
449 gboolean remove_from_treeview);
450 static gboolean recent_should_respond (GtkFileChooserDefault *impl);
451 static void recent_switch_to_browse_mode (GtkFileChooserDefault *impl);
452 static GSList * recent_get_selected_files (GtkFileChooserDefault *impl);
453 static void recent_get_valid_child_iter (GtkFileChooserDefault *impl,
454 GtkTreeIter *child_iter,
456 static void set_file_system_backend (GtkFileChooserDefault *impl);
462 /* Drag and drop interface declarations */
465 GtkTreeModelFilter parent;
467 GtkFileChooserDefault *impl;
468 } ShortcutsPaneModelFilter;
471 GtkTreeModelFilterClass parent_class;
472 } ShortcutsPaneModelFilterClass;
474 #define SHORTCUTS_PANE_MODEL_FILTER_TYPE (_shortcuts_pane_model_filter_get_type ())
475 #define SHORTCUTS_PANE_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_PANE_MODEL_FILTER_TYPE, ShortcutsPaneModelFilter))
477 static void shortcuts_pane_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface);
479 G_DEFINE_TYPE_WITH_CODE (ShortcutsPaneModelFilter,
480 _shortcuts_pane_model_filter,
481 GTK_TYPE_TREE_MODEL_FILTER,
482 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
483 shortcuts_pane_model_filter_drag_source_iface_init))
485 static GtkTreeModel *shortcuts_pane_model_filter_new (GtkFileChooserDefault *impl,
486 GtkTreeModel *child_model,
491 GtkTreeModelSort parent;
493 GtkFileChooserDefault *impl;
497 GtkTreeModelSortClass parent_class;
498 } RecentModelSortClass;
500 #define RECENT_MODEL_SORT_TYPE (_recent_model_sort_get_type ())
501 #define RECENT_MODEL_SORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), RECENT_MODEL_SORT_TYPE, RecentModelSort))
503 static void recent_model_sort_drag_source_iface_init (GtkTreeDragSourceIface *iface);
505 G_DEFINE_TYPE_WITH_CODE (RecentModelSort,
507 GTK_TYPE_TREE_MODEL_SORT,
508 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
509 recent_model_sort_drag_source_iface_init));
511 static GtkTreeModel *recent_model_sort_new (GtkFileChooserDefault *impl,
512 GtkTreeModel *child_model);
516 GtkTreeModelSort parent;
518 GtkFileChooserDefault *impl;
522 GtkTreeModelSortClass parent_class;
523 } SearchModelSortClass;
525 #define SEARCH_MODEL_SORT_TYPE (_search_model_sort_get_type ())
526 #define SEARCH_MODEL_SORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEARCH_MODEL_SORT_TYPE, SearchModelSort))
528 static void search_model_sort_drag_source_iface_init (GtkTreeDragSourceIface *iface);
530 G_DEFINE_TYPE_WITH_CODE (SearchModelSort,
532 GTK_TYPE_TREE_MODEL_SORT,
533 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
534 search_model_sort_drag_source_iface_init));
536 static GtkTreeModel *search_model_sort_new (GtkFileChooserDefault *impl,
537 GtkTreeModel *child_model);
541 G_DEFINE_TYPE_WITH_CODE (GtkFileChooserDefault, _gtk_file_chooser_default, GTK_TYPE_VBOX,
542 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER,
543 gtk_file_chooser_default_iface_init)
544 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER_EMBED,
545 gtk_file_chooser_embed_default_iface_init));
548 _gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
550 static const guint quick_bookmark_keyvals[10] = {
551 GDK_1, GDK_2, GDK_3, GDK_4, GDK_5, GDK_6, GDK_7, GDK_8, GDK_9, GDK_0
553 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
554 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
555 GtkBindingSet *binding_set;
558 gobject_class->finalize = gtk_file_chooser_default_finalize;
559 gobject_class->constructor = gtk_file_chooser_default_constructor;
560 gobject_class->set_property = gtk_file_chooser_default_set_property;
561 gobject_class->get_property = gtk_file_chooser_default_get_property;
562 gobject_class->dispose = gtk_file_chooser_default_dispose;
564 widget_class->show_all = gtk_file_chooser_default_show_all;
565 widget_class->map = gtk_file_chooser_default_map;
566 widget_class->unmap = gtk_file_chooser_default_unmap;
567 widget_class->hierarchy_changed = gtk_file_chooser_default_hierarchy_changed;
568 widget_class->style_set = gtk_file_chooser_default_style_set;
569 widget_class->screen_changed = gtk_file_chooser_default_screen_changed;
570 widget_class->size_allocate = gtk_file_chooser_default_size_allocate;
572 signals[LOCATION_POPUP] =
573 _gtk_binding_signal_new (I_("location-popup"),
574 G_OBJECT_CLASS_TYPE (class),
575 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
576 G_CALLBACK (location_popup_handler),
578 _gtk_marshal_VOID__STRING,
579 G_TYPE_NONE, 1, G_TYPE_STRING);
580 signals[LOCATION_POPUP_ON_PASTE] =
581 _gtk_binding_signal_new ("location-popup-on-paste",
582 G_OBJECT_CLASS_TYPE (class),
583 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
584 G_CALLBACK (location_popup_on_paste_handler),
586 _gtk_marshal_VOID__VOID,
588 signals[LOCATION_TOGGLE_POPUP] =
589 _gtk_binding_signal_new (I_("location-toggle-popup"),
590 G_OBJECT_CLASS_TYPE (class),
591 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
592 G_CALLBACK (location_toggle_popup_handler),
594 _gtk_marshal_VOID__VOID,
597 _gtk_binding_signal_new (I_("up-folder"),
598 G_OBJECT_CLASS_TYPE (class),
599 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
600 G_CALLBACK (up_folder_handler),
602 _gtk_marshal_VOID__VOID,
604 signals[DOWN_FOLDER] =
605 _gtk_binding_signal_new (I_("down-folder"),
606 G_OBJECT_CLASS_TYPE (class),
607 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
608 G_CALLBACK (down_folder_handler),
610 _gtk_marshal_VOID__VOID,
612 signals[HOME_FOLDER] =
613 _gtk_binding_signal_new (I_("home-folder"),
614 G_OBJECT_CLASS_TYPE (class),
615 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
616 G_CALLBACK (home_folder_handler),
618 _gtk_marshal_VOID__VOID,
620 signals[DESKTOP_FOLDER] =
621 _gtk_binding_signal_new (I_("desktop-folder"),
622 G_OBJECT_CLASS_TYPE (class),
623 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
624 G_CALLBACK (desktop_folder_handler),
626 _gtk_marshal_VOID__VOID,
628 signals[QUICK_BOOKMARK] =
629 _gtk_binding_signal_new (I_("quick-bookmark"),
630 G_OBJECT_CLASS_TYPE (class),
631 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
632 G_CALLBACK (quick_bookmark_handler),
634 _gtk_marshal_VOID__INT,
635 G_TYPE_NONE, 1, G_TYPE_INT);
636 signals[SHOW_HIDDEN] =
637 _gtk_binding_signal_new ("show-hidden",
638 G_OBJECT_CLASS_TYPE (class),
639 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
640 G_CALLBACK (show_hidden_handler),
642 _gtk_marshal_VOID__VOID,
644 signals[SEARCH_SHORTCUT] =
645 _gtk_binding_signal_new ("search-shortcut",
646 G_OBJECT_CLASS_TYPE (class),
647 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
648 G_CALLBACK (search_shortcut_handler),
650 _gtk_marshal_VOID__VOID,
652 signals[RECENT_SHORTCUT] =
653 _gtk_binding_signal_new ("recent-shortcut",
654 G_OBJECT_CLASS_TYPE (class),
655 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
656 G_CALLBACK (recent_shortcut_handler),
658 _gtk_marshal_VOID__VOID,
661 binding_set = gtk_binding_set_by_class (class);
663 gtk_binding_entry_add_signal (binding_set,
664 GDK_l, GDK_CONTROL_MASK,
665 "location-toggle-popup",
668 gtk_binding_entry_add_signal (binding_set,
671 1, G_TYPE_STRING, "/");
672 gtk_binding_entry_add_signal (binding_set,
675 1, G_TYPE_STRING, "/");
678 gtk_binding_entry_add_signal (binding_set,
681 1, G_TYPE_STRING, "~");
684 gtk_binding_entry_add_signal (binding_set,
685 GDK_v, GDK_CONTROL_MASK,
686 "location-popup-on-paste",
688 gtk_binding_entry_add_signal (binding_set,
689 GDK_Up, GDK_MOD1_MASK,
692 gtk_binding_entry_add_signal (binding_set,
696 gtk_binding_entry_add_signal (binding_set,
697 GDK_KP_Up, GDK_MOD1_MASK,
701 gtk_binding_entry_add_signal (binding_set,
702 GDK_Down, GDK_MOD1_MASK,
705 gtk_binding_entry_add_signal (binding_set,
706 GDK_KP_Down, GDK_MOD1_MASK,
710 gtk_binding_entry_add_signal (binding_set,
711 GDK_Home, GDK_MOD1_MASK,
714 gtk_binding_entry_add_signal (binding_set,
715 GDK_KP_Home, GDK_MOD1_MASK,
718 gtk_binding_entry_add_signal (binding_set,
719 GDK_d, GDK_MOD1_MASK,
722 gtk_binding_entry_add_signal (binding_set,
723 GDK_h, GDK_CONTROL_MASK,
726 gtk_binding_entry_add_signal (binding_set,
727 GDK_s, GDK_MOD1_MASK,
730 gtk_binding_entry_add_signal (binding_set,
731 GDK_r, GDK_MOD1_MASK,
735 for (i = 0; i < 10; i++)
736 gtk_binding_entry_add_signal (binding_set,
737 quick_bookmark_keyvals[i], GDK_MOD1_MASK,
741 _gtk_file_chooser_install_properties (gobject_class);
745 gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
747 iface->select_file = gtk_file_chooser_default_select_file;
748 iface->unselect_file = gtk_file_chooser_default_unselect_file;
749 iface->select_all = gtk_file_chooser_default_select_all;
750 iface->unselect_all = gtk_file_chooser_default_unselect_all;
751 iface->get_files = gtk_file_chooser_default_get_files;
752 iface->get_preview_file = gtk_file_chooser_default_get_preview_file;
753 iface->get_file_system = gtk_file_chooser_default_get_file_system;
754 iface->set_current_folder = gtk_file_chooser_default_set_current_folder;
755 iface->get_current_folder = gtk_file_chooser_default_get_current_folder;
756 iface->set_current_name = gtk_file_chooser_default_set_current_name;
757 iface->add_filter = gtk_file_chooser_default_add_filter;
758 iface->remove_filter = gtk_file_chooser_default_remove_filter;
759 iface->list_filters = gtk_file_chooser_default_list_filters;
760 iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder;
761 iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder;
762 iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders;
766 gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
768 iface->get_default_size = gtk_file_chooser_default_get_default_size;
769 iface->should_respond = gtk_file_chooser_default_should_respond;
770 iface->initial_focus = gtk_file_chooser_default_initial_focus;
774 _gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
776 profile_start ("start", NULL);
777 #ifdef PROFILE_FILE_CHOOSER
778 access ("MARK: *** CREATE FILE CHOOSER", F_OK);
780 impl->local_only = TRUE;
781 impl->preview_widget_active = TRUE;
782 impl->use_preview_label = TRUE;
783 impl->select_multiple = FALSE;
784 impl->show_hidden = FALSE;
785 impl->icon_size = FALLBACK_ICON_SIZE;
786 impl->load_state = LOAD_EMPTY;
787 impl->reload_state = RELOAD_EMPTY;
788 impl->pending_select_files = NULL;
789 impl->location_mode = LOCATION_MODE_PATH_BAR;
790 impl->operation_mode = OPERATION_MODE_BROWSE;
791 impl->recent_manager = gtk_recent_manager_get_default ();
793 gtk_box_set_spacing (GTK_BOX (impl), 12);
795 set_file_system_backend (impl);
797 profile_end ("end", NULL);
800 /* Frees the data columns for the specified iter in the shortcuts model*/
802 shortcuts_free_row_data (GtkFileChooserDefault *impl,
806 ShortcutType shortcut_type;
807 GCancellable *cancellable;
809 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
810 SHORTCUTS_COL_DATA, &col_data,
811 SHORTCUTS_COL_TYPE, &shortcut_type,
812 SHORTCUTS_COL_CANCELLABLE, &cancellable,
816 g_cancellable_cancel (cancellable);
818 if (!(shortcut_type == SHORTCUT_TYPE_FILE ||
819 shortcut_type == SHORTCUT_TYPE_VOLUME) ||
823 if (shortcut_type == SHORTCUT_TYPE_VOLUME)
825 GtkFileSystemVolume *volume;
828 _gtk_file_system_volume_free (volume);
834 g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
837 g_object_unref (file);
841 /* Frees all the data columns in the shortcuts model */
843 shortcuts_free (GtkFileChooserDefault *impl)
847 if (!impl->shortcuts_model)
850 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
853 shortcuts_free_row_data (impl, &iter);
855 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter));
857 g_object_unref (impl->shortcuts_model);
858 impl->shortcuts_model = NULL;
862 pending_select_files_free (GtkFileChooserDefault *impl)
864 g_slist_foreach (impl->pending_select_files, (GFunc) g_object_unref, NULL);
865 g_slist_free (impl->pending_select_files);
866 impl->pending_select_files = NULL;
870 pending_select_files_add (GtkFileChooserDefault *impl,
873 impl->pending_select_files =
874 g_slist_prepend (impl->pending_select_files, g_object_ref (file));
877 /* Used from gtk_tree_selection_selected_foreach() */
879 store_selection_foreach (GtkTreeModel *model,
884 GtkFileChooserDefault *impl;
885 GtkTreeIter child_iter;
888 impl = GTK_FILE_CHOOSER_DEFAULT (data);
890 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter);
892 file = _gtk_file_system_model_get_file (impl->browse_files_model, &child_iter);
893 pending_select_files_add (impl, file);
896 /* Stores the current selection in the list of paths to select; this is used to
897 * preserve the selection when reloading the current folder.
900 pending_select_files_store_selection (GtkFileChooserDefault *impl)
902 GtkTreeSelection *selection;
904 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
905 gtk_tree_selection_selected_foreach (selection, store_selection_foreach, impl);
909 gtk_file_chooser_default_finalize (GObject *object)
911 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
914 if (impl->shortcuts_pane_filter_model)
915 g_object_unref (impl->shortcuts_pane_filter_model);
917 if (impl->shortcuts_combo_filter_model)
918 g_object_unref (impl->shortcuts_combo_filter_model);
920 shortcuts_free (impl);
922 g_object_unref (impl->file_system);
924 g_free (impl->browse_files_last_selected_name);
926 for (l = impl->filters; l; l = l->next)
928 GtkFileFilter *filter;
930 filter = GTK_FILE_FILTER (l->data);
931 g_object_unref (filter);
933 g_slist_free (impl->filters);
935 if (impl->current_filter)
936 g_object_unref (impl->current_filter);
938 if (impl->current_volume_file)
939 g_object_unref (impl->current_volume_file);
941 if (impl->current_folder)
942 g_object_unref (impl->current_folder);
944 if (impl->preview_file)
945 g_object_unref (impl->preview_file);
947 load_remove_timer (impl);
949 /* Free all the Models we have */
950 if (impl->browse_files_model)
951 g_object_unref (impl->browse_files_model);
953 if (impl->sort_model)
954 g_object_unref (impl->sort_model);
956 search_clear_model (impl, FALSE);
957 recent_clear_model (impl, FALSE);
959 g_free (impl->preview_display_name);
961 g_free (impl->edited_new_text);
963 G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->finalize (object);
966 /* Shows an error dialog set as transient for the specified window */
968 error_message_with_parent (GtkWindow *parent,
974 dialog = gtk_message_dialog_new (parent,
975 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
980 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
984 gtk_window_group_add_window (parent->group, GTK_WINDOW (dialog));
986 gtk_dialog_run (GTK_DIALOG (dialog));
987 gtk_widget_destroy (dialog);
990 /* Returns a toplevel GtkWindow, or NULL if none */
992 get_toplevel (GtkWidget *widget)
996 toplevel = gtk_widget_get_toplevel (widget);
997 if (!GTK_WIDGET_TOPLEVEL (toplevel))
1000 return GTK_WINDOW (toplevel);
1003 /* Shows an error dialog for the file chooser */
1005 error_message (GtkFileChooserDefault *impl,
1009 error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail);
1012 /* Shows a simple error dialog relative to a path. Frees the GError as well. */
1014 error_dialog (GtkFileChooserDefault *impl,
1025 uri = g_file_get_uri (file);
1026 text = g_strdup_printf (msg, uri);
1027 error_message (impl, text, error->message);
1030 g_error_free (error);
1034 /* Displays an error message about not being able to get information for a file.
1035 * Frees the GError as well.
1038 error_getting_info_dialog (GtkFileChooserDefault *impl,
1043 _("Could not retrieve information about the file"),
1047 /* Shows an error dialog about not being able to add a bookmark */
1049 error_adding_bookmark_dialog (GtkFileChooserDefault *impl,
1054 _("Could not add a bookmark"),
1058 /* Shows an error dialog about not being able to remove a bookmark */
1060 error_removing_bookmark_dialog (GtkFileChooserDefault *impl,
1065 _("Could not remove bookmark"),
1069 /* Shows an error dialog about not being able to create a folder */
1071 error_creating_folder_dialog (GtkFileChooserDefault *impl,
1076 _("The folder could not be created"),
1080 /* Shows an error about not being able to create a folder because a file with
1081 * the same name is already there.
1084 error_creating_folder_over_existing_file_dialog (GtkFileChooserDefault *impl,
1089 _("The folder could not be created, as a file with the same "
1090 "name already exists. Try using a different name for the "
1091 "folder, or rename the file first."),
1095 /* Shows an error dialog about not being able to create a filename */
1097 error_building_filename_dialog (GtkFileChooserDefault *impl,
1100 error_dialog (impl, _("Invalid file name"),
1104 /* Shows an error dialog when we cannot switch to a folder */
1106 error_changing_folder_dialog (GtkFileChooserDefault *impl,
1110 error_dialog (impl, _("The folder contents could not be displayed"),
1114 /* Changes folders, displaying an error dialog if this fails */
1116 change_folder_and_display_error (GtkFileChooserDefault *impl,
1118 gboolean clear_entry)
1123 g_return_val_if_fail (G_IS_FILE (file), FALSE);
1125 /* We copy the path because of this case:
1127 * list_row_activated()
1128 * fetches path from model; path belongs to the model (*)
1129 * calls change_folder_and_display_error()
1130 * calls _gtk_file_chooser_set_current_folder_file()
1131 * changing folders fails, sets model to NULL, thus freeing the path in (*)
1135 result = gtk_file_chooser_default_update_current_folder (GTK_FILE_CHOOSER (impl), file, TRUE, clear_entry, &error);
1138 error_changing_folder_dialog (impl, file, error);
1144 update_preview_widget_visibility (GtkFileChooserDefault *impl)
1146 if (impl->use_preview_label)
1148 if (!impl->preview_label)
1150 impl->preview_label = gtk_label_new (impl->preview_display_name);
1151 gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_label, FALSE, FALSE, 0);
1152 gtk_box_reorder_child (GTK_BOX (impl->preview_box), impl->preview_label, 0);
1153 gtk_label_set_ellipsize (GTK_LABEL (impl->preview_label), PANGO_ELLIPSIZE_MIDDLE);
1154 gtk_widget_show (impl->preview_label);
1159 if (impl->preview_label)
1161 gtk_widget_destroy (impl->preview_label);
1162 impl->preview_label = NULL;
1166 if (impl->preview_widget_active && impl->preview_widget)
1167 gtk_widget_show (impl->preview_box);
1169 gtk_widget_hide (impl->preview_box);
1171 g_signal_emit_by_name (impl, "default-size-changed");
1175 set_preview_widget (GtkFileChooserDefault *impl,
1176 GtkWidget *preview_widget)
1178 if (preview_widget == impl->preview_widget)
1181 if (impl->preview_widget)
1182 gtk_container_remove (GTK_CONTAINER (impl->preview_box),
1183 impl->preview_widget);
1185 impl->preview_widget = preview_widget;
1186 if (impl->preview_widget)
1188 gtk_widget_show (impl->preview_widget);
1189 gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_widget, TRUE, TRUE, 0);
1190 gtk_box_reorder_child (GTK_BOX (impl->preview_box),
1191 impl->preview_widget,
1192 (impl->use_preview_label && impl->preview_label) ? 1 : 0);
1195 update_preview_widget_visibility (impl);
1198 /* Renders a "Search" icon at an appropriate size for a tree view */
1200 render_search_icon (GtkFileChooserDefault *impl)
1202 return gtk_widget_render_icon (GTK_WIDGET (impl), GTK_STOCK_FIND, GTK_ICON_SIZE_MENU, NULL);
1206 render_recent_icon (GtkFileChooserDefault *impl)
1208 GtkIconTheme *theme;
1211 if (gtk_widget_has_screen (GTK_WIDGET (impl)))
1212 theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
1214 theme = gtk_icon_theme_get_default ();
1216 retval = gtk_icon_theme_load_icon (theme, "document-open-recent",
1222 retval = gtk_widget_render_icon (GTK_WIDGET (impl), GTK_STOCK_FILE, GTK_ICON_SIZE_MENU, NULL);
1228 /* Re-reads all the icons for the shortcuts, used when the theme changes */
1229 struct ReloadIconsData
1231 GtkFileChooserDefault *impl;
1232 GtkTreeRowReference *row_ref;
1236 shortcuts_reload_icons_get_info_cb (GCancellable *cancellable,
1238 const GError *error,
1244 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1245 struct ReloadIconsData *data = user_data;
1247 if (!g_slist_find (data->impl->reload_icon_cancellables, cancellable))
1250 data->impl->reload_icon_cancellables = g_slist_remove (data->impl->reload_icon_cancellables, cancellable);
1252 if (cancelled || error)
1255 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->impl), data->impl->icon_size);
1257 path = gtk_tree_row_reference_get_path (data->row_ref);
1258 gtk_tree_model_get_iter (GTK_TREE_MODEL (data->impl->shortcuts_model), &iter, path);
1259 gtk_list_store_set (data->impl->shortcuts_model, &iter,
1260 SHORTCUTS_COL_PIXBUF, pixbuf,
1262 gtk_tree_path_free (path);
1265 g_object_unref (pixbuf);
1268 gtk_tree_row_reference_free (data->row_ref);
1269 g_object_unref (data->impl);
1272 g_object_unref (cancellable);
1276 shortcuts_reload_icons (GtkFileChooserDefault *impl)
1281 profile_start ("start", NULL);
1283 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
1286 for (l = impl->reload_icon_cancellables; l; l = l->next)
1288 GCancellable *cancellable = G_CANCELLABLE (l->data);
1289 g_cancellable_cancel (cancellable);
1291 g_slist_free (impl->reload_icon_cancellables);
1292 impl->reload_icon_cancellables = NULL;
1297 ShortcutType shortcut_type;
1298 gboolean pixbuf_visible;
1301 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
1302 SHORTCUTS_COL_DATA, &data,
1303 SHORTCUTS_COL_TYPE, &shortcut_type,
1304 SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible,
1310 if (shortcut_type == SHORTCUT_TYPE_VOLUME)
1312 GtkFileSystemVolume *volume;
1315 pixbuf = _gtk_file_system_volume_render_icon (volume, GTK_WIDGET (impl),
1316 impl->icon_size, NULL);
1318 else if (shortcut_type == SHORTCUT_TYPE_FILE)
1320 if (g_file_is_native (G_FILE (data)))
1323 struct ReloadIconsData *info;
1324 GtkTreePath *tree_path;
1325 GCancellable *cancellable;
1329 info = g_new0 (struct ReloadIconsData, 1);
1330 info->impl = g_object_ref (impl);
1331 tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
1332 info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->shortcuts_model), tree_path);
1333 gtk_tree_path_free (tree_path);
1335 cancellable = _gtk_file_system_get_info (impl->file_system, file,
1337 shortcuts_reload_icons_get_info_cb,
1339 impl->reload_icon_cancellables = g_slist_append (impl->reload_icon_cancellables, cancellable);
1343 GtkIconTheme *icon_theme;
1345 /* Don't call get_info for remote paths to avoid latency and
1347 * If we switch to a better bookmarks file format (XBEL), we
1348 * should use mime info to get a better icon.
1350 icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
1351 pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote",
1352 impl->icon_size, 0, NULL);
1355 else if (shortcut_type == SHORTCUT_TYPE_SEARCH)
1357 pixbuf = render_search_icon (impl);
1359 else if (shortcut_type == SHORTCUT_TYPE_RECENT)
1361 pixbuf = render_recent_icon (impl);
1364 gtk_list_store_set (impl->shortcuts_model, &iter,
1365 SHORTCUTS_COL_PIXBUF, pixbuf,
1369 g_object_unref (pixbuf);
1373 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
1377 profile_end ("end", NULL);
1381 shortcuts_find_folder (GtkFileChooserDefault *impl,
1384 GtkTreeSelection *selection;
1388 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1390 g_assert (folder != NULL);
1391 pos = shortcut_find_position (impl, folder);
1394 gtk_tree_selection_unselect_all (selection);
1398 path = gtk_tree_path_new_from_indices (pos, -1);
1399 gtk_tree_selection_select_path (selection, path);
1400 gtk_tree_path_free (path);
1403 /* If a shortcut corresponds to the current folder, selects it */
1405 shortcuts_find_current_folder (GtkFileChooserDefault *impl)
1407 shortcuts_find_folder (impl, impl->current_folder);
1410 /* Removes the specified number of rows from the shortcuts list */
1412 shortcuts_remove_rows (GtkFileChooserDefault *impl,
1418 path = gtk_tree_path_new_from_indices (start_row, -1);
1420 for (; n_rows; n_rows--)
1424 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
1425 g_assert_not_reached ();
1427 shortcuts_free_row_data (impl, &iter);
1428 gtk_list_store_remove (impl->shortcuts_model, &iter);
1431 gtk_tree_path_free (path);
1435 shortcuts_update_count (GtkFileChooserDefault *impl,
1436 ShortcutsIndex type,
1441 case SHORTCUTS_HOME:
1443 impl->has_home = FALSE;
1445 impl->has_home = TRUE;
1448 case SHORTCUTS_DESKTOP:
1450 impl->has_desktop = FALSE;
1452 impl->has_desktop = TRUE;
1455 case SHORTCUTS_VOLUMES:
1456 impl->num_volumes += value;
1459 case SHORTCUTS_SHORTCUTS:
1460 impl->num_shortcuts += value;
1463 case SHORTCUTS_BOOKMARKS:
1464 impl->num_bookmarks += value;
1467 case SHORTCUTS_CURRENT_FOLDER:
1469 impl->shortcuts_current_folder_active = FALSE;
1471 impl->shortcuts_current_folder_active = TRUE;
1480 struct ShortcutsInsertRequest
1482 GtkFileChooserDefault *impl;
1486 GtkTreeRowReference *row_ref;
1487 ShortcutsIndex type;
1493 get_file_info_finished (GCancellable *cancellable,
1495 const GError *error,
1499 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1503 GCancellable *model_cancellable;
1504 struct ShortcutsInsertRequest *request = data;
1506 path = gtk_tree_row_reference_get_path (request->row_ref);
1508 /* Handle doesn't exist anymore in the model */
1511 pos = gtk_tree_path_get_indices (path)[0];
1512 gtk_tree_model_get_iter (GTK_TREE_MODEL (request->impl->shortcuts_model),
1514 gtk_tree_path_free (path);
1516 /* validate cancellable, else goto out */
1517 gtk_tree_model_get (GTK_TREE_MODEL (request->impl->shortcuts_model), &iter,
1518 SHORTCUTS_COL_CANCELLABLE, &model_cancellable,
1520 if (cancellable != model_cancellable)
1523 /* set the cancellable to NULL in the model (we unref later on) */
1524 gtk_list_store_set (request->impl->shortcuts_model, &iter,
1525 SHORTCUTS_COL_CANCELLABLE, NULL,
1533 gtk_list_store_remove (request->impl->shortcuts_model, &iter);
1534 shortcuts_update_count (request->impl, request->type, -1);
1536 if (request->type == SHORTCUTS_HOME)
1540 home = g_file_new_for_path (g_get_home_dir ());
1541 error_getting_info_dialog (request->impl, home, g_error_copy (error));
1542 g_object_unref (home);
1544 else if (request->type == SHORTCUTS_CURRENT_FOLDER)
1546 /* Remove the current folder separator */
1547 gint separator_pos = shortcuts_get_index (request->impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1548 shortcuts_remove_rows (request->impl, separator_pos, 1);
1554 if (!request->label_copy)
1555 request->label_copy = g_strdup (g_file_info_get_display_name (info));
1556 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (request->impl),
1557 request->impl->icon_size);
1559 gtk_list_store_set (request->impl->shortcuts_model, &iter,
1560 SHORTCUTS_COL_PIXBUF, pixbuf,
1561 SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1562 SHORTCUTS_COL_NAME, request->label_copy,
1563 SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_FILE,
1564 SHORTCUTS_COL_REMOVABLE, request->removable,
1567 if (request->impl->shortcuts_pane_filter_model)
1568 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (request->impl->shortcuts_pane_filter_model));
1570 if (request->impl->shortcuts_combo_filter_model)
1571 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (request->impl->shortcuts_combo_filter_model));
1573 if (request->type == SHORTCUTS_CURRENT_FOLDER &&
1574 request->impl->save_folder_combo != NULL)
1576 /* The current folder is updated via _activate_iter(), don't
1577 * have save_folder_combo_changed_cb() call _activate_iter()
1580 g_signal_handlers_block_by_func (request->impl->save_folder_combo,
1581 G_CALLBACK (save_folder_combo_changed_cb),
1584 if (request->impl->has_search)
1587 if (request->impl->has_recent)
1590 gtk_combo_box_set_active (GTK_COMBO_BOX (request->impl->save_folder_combo), pos);
1591 g_signal_handlers_unblock_by_func (request->impl->save_folder_combo,
1592 G_CALLBACK (save_folder_combo_changed_cb),
1597 g_object_unref (pixbuf);
1600 g_object_unref (request->impl);
1601 g_object_unref (request->file);
1602 gtk_tree_row_reference_free (request->row_ref);
1603 g_free (request->label_copy);
1606 g_object_unref (cancellable);
1609 /* FIXME: GtkFileSystem needs a function to split a remote path
1610 * into hostname and path components, or maybe just have a
1611 * gtk_file_system_path_get_display_name().
1613 * This function is also used in gtkfilechooserbutton.c
1616 _gtk_file_chooser_label_for_file (GFile *file)
1618 const gchar *path, *start, *end, *p;
1619 gchar *uri, *host, *label;
1621 uri = g_file_get_uri (file);
1623 start = strstr (uri, "://");
1627 path = strchr (start, '/');
1632 end = uri + strlen (uri);
1636 /* strip username */
1637 p = strchr (start, '@');
1641 p = strchr (start, ':');
1645 host = g_strndup (start, end - start);
1647 /* Translators: the first string is a path and the second string
1648 * is a hostname. Nautilus and the panel contain the same string
1651 label = g_strdup_printf (_("%1$s on %2$s"), path, host);
1657 label = g_strdup (uri);
1665 /* Inserts a path in the shortcuts tree, making a copy of it; alternatively,
1666 * inserts a volume. A position of -1 indicates the end of the tree.
1669 shortcuts_insert_file (GtkFileChooserDefault *impl,
1671 ShortcutType shortcut_type,
1672 GtkFileSystemVolume *volume,
1676 ShortcutsIndex type)
1679 GdkPixbuf *pixbuf = NULL;
1680 gpointer data = NULL;
1682 GtkIconTheme *icon_theme;
1684 profile_start ("start shortcut", NULL);
1686 if (shortcut_type == SHORTCUT_TYPE_VOLUME)
1689 label_copy = _gtk_file_system_volume_get_display_name (volume);
1690 pixbuf = _gtk_file_system_volume_render_icon (volume, GTK_WIDGET (impl),
1691 impl->icon_size, NULL);
1693 else if (shortcut_type == SHORTCUT_TYPE_FILE)
1695 if (g_file_is_native (file))
1697 struct ShortcutsInsertRequest *request;
1698 GCancellable *cancellable;
1701 request = g_new0 (struct ShortcutsInsertRequest, 1);
1702 request->impl = g_object_ref (impl);
1703 request->file = g_object_ref (file);
1704 request->name_only = TRUE;
1705 request->removable = removable;
1707 request->type = type;
1709 request->label_copy = g_strdup (label);
1712 gtk_list_store_append (impl->shortcuts_model, &iter);
1714 gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
1716 p = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
1717 request->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->shortcuts_model), p);
1718 gtk_tree_path_free (p);
1720 cancellable = _gtk_file_system_get_info (request->impl->file_system, request->file,
1721 "standard::is-hidden,standard::display-name,standard::icon",
1722 get_file_info_finished, request);
1724 gtk_list_store_set (impl->shortcuts_model, &iter,
1725 SHORTCUTS_COL_DATA, g_object_ref (file),
1726 SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_FILE,
1727 SHORTCUTS_COL_CANCELLABLE, cancellable,
1730 shortcuts_update_count (impl, type, 1);
1736 /* Don't call get_info for remote paths to avoid latency and
1739 data = g_object_ref (file);
1741 label_copy = g_strdup (label);
1743 label_copy = _gtk_file_chooser_label_for_file (file);
1745 /* If we switch to a better bookmarks file format (XBEL), we
1746 * should use mime info to get a better icon.
1748 icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
1749 pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote",
1750 impl->icon_size, 0, NULL);
1755 g_assert_not_reached ();
1761 gtk_list_store_append (impl->shortcuts_model, &iter);
1763 gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
1765 shortcuts_update_count (impl, type, 1);
1767 gtk_list_store_set (impl->shortcuts_model, &iter,
1768 SHORTCUTS_COL_PIXBUF, pixbuf,
1769 SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1770 SHORTCUTS_COL_NAME, label_copy,
1771 SHORTCUTS_COL_DATA, data,
1772 SHORTCUTS_COL_TYPE, shortcut_type,
1773 SHORTCUTS_COL_REMOVABLE, removable,
1774 SHORTCUTS_COL_CANCELLABLE, NULL,
1777 if (impl->shortcuts_pane_filter_model)
1778 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model));
1780 if (impl->shortcuts_combo_filter_model)
1781 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model));
1783 if (type == SHORTCUTS_CURRENT_FOLDER && impl->save_folder_combo != NULL)
1785 /* The current folder is updated via _activate_iter(), don't
1786 * have save_folder_combo_changed_cb() call _activate_iter()
1789 gint combo_pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER);
1791 if (impl->has_search)
1794 if (impl->has_recent)
1797 g_signal_handlers_block_by_func (impl->save_folder_combo,
1798 G_CALLBACK (save_folder_combo_changed_cb),
1801 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), combo_pos);
1802 g_signal_handlers_unblock_by_func (impl->save_folder_combo,
1803 G_CALLBACK (save_folder_combo_changed_cb),
1807 g_free (label_copy);
1810 g_object_unref (pixbuf);
1812 profile_end ("end", NULL);
1816 shortcuts_append_search (GtkFileChooserDefault *impl)
1821 pixbuf = render_search_icon (impl);
1823 gtk_list_store_append (impl->shortcuts_model, &iter);
1824 gtk_list_store_set (impl->shortcuts_model, &iter,
1825 SHORTCUTS_COL_PIXBUF, pixbuf,
1826 SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1827 SHORTCUTS_COL_NAME, _("Search"),
1828 SHORTCUTS_COL_DATA, NULL,
1829 SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_SEARCH,
1830 SHORTCUTS_COL_REMOVABLE, FALSE,
1834 g_object_unref (pixbuf);
1836 impl->has_search = TRUE;
1840 shortcuts_append_recent (GtkFileChooserDefault *impl)
1845 pixbuf = render_recent_icon (impl);
1847 gtk_list_store_append (impl->shortcuts_model, &iter);
1848 gtk_list_store_set (impl->shortcuts_model, &iter,
1849 SHORTCUTS_COL_PIXBUF, pixbuf,
1850 SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1851 SHORTCUTS_COL_NAME, _("Recently Used"),
1852 SHORTCUTS_COL_DATA, NULL,
1853 SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_RECENT,
1854 SHORTCUTS_COL_REMOVABLE, FALSE,
1858 g_object_unref (pixbuf);
1860 impl->has_recent = TRUE;
1863 /* Appends an item for the user's home directory to the shortcuts model */
1865 shortcuts_append_home (GtkFileChooserDefault *impl)
1867 const char *home_path;
1870 profile_start ("start", NULL);
1872 home_path = g_get_home_dir ();
1873 if (home_path == NULL)
1875 profile_end ("end - no home directory!?", NULL);
1879 home = g_file_new_for_path (home_path);
1880 shortcuts_insert_file (impl, -1, SHORTCUT_TYPE_FILE, NULL, home, NULL, FALSE, SHORTCUTS_HOME);
1881 impl->has_home = TRUE;
1883 g_object_unref (home);
1885 profile_end ("end", NULL);
1888 /* Appends the ~/Desktop directory to the shortcuts model */
1890 shortcuts_append_desktop (GtkFileChooserDefault *impl)
1895 profile_start ("start", NULL);
1897 name = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
1898 file = g_file_new_for_path (name);
1899 shortcuts_insert_file (impl, -1, SHORTCUT_TYPE_FILE, NULL, file, _("Desktop"), FALSE, SHORTCUTS_DESKTOP);
1900 impl->has_desktop = TRUE;
1902 /* We do not actually pop up an error dialog if there is no desktop directory
1903 * because some people may really not want to have one.
1906 g_object_unref (file);
1908 profile_end ("end", NULL);
1911 /* Appends a list of GFile to the shortcuts model; returns how many were inserted */
1913 shortcuts_append_bookmarks (GtkFileChooserDefault *impl,
1920 profile_start ("start", NULL);
1922 start_row = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR) + 1;
1925 for (; bookmarks; bookmarks = bookmarks->next)
1929 file = bookmarks->data;
1931 if (impl->local_only && !g_file_is_native (file))
1934 if (shortcut_find_position (impl, file) != -1)
1937 label = _gtk_file_system_get_bookmark_label (impl->file_system, file);
1939 shortcuts_insert_file (impl, start_row + num_inserted, SHORTCUT_TYPE_FILE, NULL, file, label, TRUE, SHORTCUTS_BOOKMARKS);
1943 profile_end ("end", NULL);
1945 return num_inserted;
1948 /* Returns the index for the corresponding item in the shortcuts bar */
1950 shortcuts_get_index (GtkFileChooserDefault *impl,
1951 ShortcutsIndex where)
1957 if (where == SHORTCUTS_SEARCH)
1960 n += impl->has_search ? 1 : 0;
1962 if (where == SHORTCUTS_RECENT)
1965 n += impl->has_recent ? 1 : 0;
1967 if (where == SHORTCUTS_RECENT_SEPARATOR)
1970 n += impl->has_recent ? 1 : 0;
1972 if (where == SHORTCUTS_HOME)
1975 n += impl->has_home ? 1 : 0;
1977 if (where == SHORTCUTS_DESKTOP)
1980 n += impl->has_desktop ? 1 : 0;
1982 if (where == SHORTCUTS_VOLUMES)
1985 n += impl->num_volumes;
1987 if (where == SHORTCUTS_SHORTCUTS)
1990 n += impl->num_shortcuts;
1992 if (where == SHORTCUTS_BOOKMARKS_SEPARATOR)
1995 /* If there are no bookmarks there won't be a separator */
1996 n += (impl->num_bookmarks > 0) ? 1 : 0;
1998 if (where == SHORTCUTS_BOOKMARKS)
2001 n += impl->num_bookmarks;
2003 if (where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR)
2008 if (where == SHORTCUTS_CURRENT_FOLDER)
2011 g_assert_not_reached ();
2018 /* Adds all the file system volumes to the shortcuts model */
2020 shortcuts_add_volumes (GtkFileChooserDefault *impl)
2025 gboolean old_changing_folders;
2027 profile_start ("start", NULL);
2029 old_changing_folders = impl->changing_folder;
2030 impl->changing_folder = TRUE;
2032 start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
2033 shortcuts_remove_rows (impl, start_row, impl->num_volumes);
2034 impl->num_volumes = 0;
2036 list = _gtk_file_system_list_volumes (impl->file_system);
2040 for (l = list; l; l = l->next)
2042 GtkFileSystemVolume *volume;
2046 if (impl->local_only)
2048 if (_gtk_file_system_volume_is_mounted (volume))
2052 base_file = _gtk_file_system_volume_get_root (volume);
2053 if (base_file != NULL && !g_file_is_native (base_file))
2058 shortcuts_insert_file (impl, start_row + n, SHORTCUT_TYPE_VOLUME, volume, NULL, NULL, FALSE, SHORTCUTS_VOLUMES);
2062 impl->num_volumes = n;
2063 g_slist_free (list);
2065 if (impl->shortcuts_pane_filter_model)
2066 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model));
2068 if (impl->shortcuts_combo_filter_model)
2069 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model));
2071 impl->changing_folder = old_changing_folders;
2073 profile_end ("end", NULL);
2076 /* Inserts a separator node in the shortcuts list */
2078 shortcuts_insert_separator (GtkFileChooserDefault *impl,
2079 ShortcutsIndex where)
2083 g_assert (where == SHORTCUTS_RECENT_SEPARATOR ||
2084 where == SHORTCUTS_BOOKMARKS_SEPARATOR ||
2085 where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2087 gtk_list_store_insert (impl->shortcuts_model, &iter,
2088 shortcuts_get_index (impl, where));
2089 gtk_list_store_set (impl->shortcuts_model, &iter,
2090 SHORTCUTS_COL_PIXBUF, NULL,
2091 SHORTCUTS_COL_PIXBUF_VISIBLE, FALSE,
2092 SHORTCUTS_COL_NAME, NULL,
2093 SHORTCUTS_COL_DATA, NULL,
2094 SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_SEPARATOR,
2098 /* Updates the list of bookmarks */
2100 shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
2103 gboolean old_changing_folders;
2105 GFile *list_selected = NULL;
2106 GFile *combo_selected = NULL;
2107 ShortcutType shortcut_type;
2110 profile_start ("start", NULL);
2112 old_changing_folders = impl->changing_folder;
2113 impl->changing_folder = TRUE;
2115 if (shortcuts_get_selected (impl, &iter))
2117 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model),
2119 SHORTCUTS_COL_DATA, &col_data,
2120 SHORTCUTS_COL_TYPE, &shortcut_type,
2123 if (col_data && shortcut_type == SHORTCUT_TYPE_FILE)
2124 list_selected = g_object_ref (col_data);
2127 if (impl->save_folder_combo &&
2128 gtk_combo_box_get_active_iter (GTK_COMBO_BOX (impl->save_folder_combo),
2131 GtkTreeIter child_iter;
2133 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model),
2136 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model),
2138 SHORTCUTS_COL_DATA, &col_data,
2139 SHORTCUTS_COL_TYPE, &shortcut_type,
2142 if (col_data && shortcut_type == SHORTCUT_TYPE_FILE)
2143 combo_selected = g_object_ref (col_data);
2146 if (impl->num_bookmarks > 0)
2147 shortcuts_remove_rows (impl,
2148 shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR),
2149 impl->num_bookmarks + 1);
2151 impl->num_bookmarks = 0;
2152 shortcuts_insert_separator (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
2154 bookmarks = _gtk_file_system_list_bookmarks (impl->file_system);
2155 shortcuts_append_bookmarks (impl, bookmarks);
2156 g_slist_free (bookmarks);
2158 if (impl->num_bookmarks == 0)
2159 shortcuts_remove_rows (impl, shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR), 1);
2161 if (impl->shortcuts_pane_filter_model)
2162 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model));
2164 if (impl->shortcuts_combo_filter_model)
2165 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model));
2169 shortcuts_find_folder (impl, list_selected);
2170 g_object_unref (list_selected);
2177 pos = shortcut_find_position (impl, combo_selected);
2180 if (impl->has_search)
2183 if (impl->has_recent)
2186 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), pos);
2189 g_object_unref (combo_selected);
2192 impl->changing_folder = old_changing_folders;
2194 profile_end ("end", NULL);
2197 /* Appends a separator and a row to the shortcuts list for the current folder */
2199 shortcuts_add_current_folder (GtkFileChooserDefault *impl)
2204 g_assert (!impl->shortcuts_current_folder_active);
2208 g_assert (impl->current_folder != NULL);
2210 pos = shortcut_find_position (impl, impl->current_folder);
2213 GtkFileSystemVolume *volume;
2218 shortcuts_insert_separator (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2222 pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER);
2224 volume = _gtk_file_system_get_volume_for_file (impl->file_system, impl->current_folder);
2226 base_file = _gtk_file_system_volume_get_root (volume);
2230 if (base_file && g_file_equal (base_file, impl->current_folder))
2231 shortcuts_insert_file (impl, pos, SHORTCUT_TYPE_VOLUME, volume, NULL, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER);
2233 shortcuts_insert_file (impl, pos, SHORTCUT_TYPE_FILE, NULL, impl->current_folder, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER);
2236 g_object_unref (base_file);
2238 else if (impl->save_folder_combo != NULL)
2240 if (impl->has_search)
2243 if (impl->has_recent)
2244 pos -= 2; /* + separator */
2246 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), pos);
2250 /* Updates the current folder row in the shortcuts model */
2252 shortcuts_update_current_folder (GtkFileChooserDefault *impl)
2256 pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2258 if (impl->shortcuts_current_folder_active)
2260 shortcuts_remove_rows (impl, pos, 2);
2261 impl->shortcuts_current_folder_active = FALSE;
2264 shortcuts_add_current_folder (impl);
2267 /* Filter function used for the shortcuts filter model */
2269 shortcuts_pane_filter_cb (GtkTreeModel *model,
2273 GtkFileChooserDefault *impl;
2277 impl = GTK_FILE_CHOOSER_DEFAULT (data);
2279 path = gtk_tree_model_get_path (model, iter);
2283 pos = *gtk_tree_path_get_indices (path);
2284 gtk_tree_path_free (path);
2286 return (pos < shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR));
2289 /* Creates the list model for shortcuts */
2291 shortcuts_model_create (GtkFileChooserDefault *impl)
2293 /* Keep this order in sync with the SHORCUTS_COL_* enum values */
2294 impl->shortcuts_model = gtk_list_store_new (SHORTCUTS_COL_NUM_COLUMNS,
2295 GDK_TYPE_PIXBUF, /* pixbuf */
2296 G_TYPE_STRING, /* name */
2297 G_TYPE_POINTER, /* path or volume */
2298 G_TYPE_INT, /* ShortcutType */
2299 G_TYPE_BOOLEAN, /* removable */
2300 G_TYPE_BOOLEAN, /* pixbuf cell visibility */
2301 G_TYPE_POINTER); /* GCancellable */
2303 if (search_is_possible (impl))
2305 shortcuts_append_search (impl);
2308 if (impl->recent_manager)
2310 shortcuts_append_recent (impl);
2311 shortcuts_insert_separator (impl, SHORTCUTS_RECENT_SEPARATOR);
2314 if (impl->file_system)
2316 shortcuts_append_home (impl);
2317 shortcuts_append_desktop (impl);
2318 shortcuts_add_volumes (impl);
2321 impl->shortcuts_pane_filter_model = shortcuts_pane_model_filter_new (impl,
2322 GTK_TREE_MODEL (impl->shortcuts_model),
2325 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
2326 shortcuts_pane_filter_cb,
2331 /* Callback used when the "New Folder" button is clicked */
2333 new_folder_button_clicked (GtkButton *button,
2334 GtkFileChooserDefault *impl)
2339 if (!impl->browse_files_model)
2340 return; /* FIXME: this sucks. Disable the New Folder button or something. */
2342 /* Prevent button from being clicked twice */
2343 gtk_widget_set_sensitive (impl->browse_new_folder_button, FALSE);
2345 _gtk_file_system_model_add_editable (impl->browse_files_model, &iter);
2347 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->browse_files_model), &iter);
2348 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_files_tree_view),
2349 path, impl->list_name_column,
2352 g_object_set (impl->list_name_renderer, "editable", TRUE, NULL);
2353 gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view),
2355 impl->list_name_column,
2358 gtk_tree_path_free (path);
2361 /* Idle handler for creating a new folder after editing its name cell, or for
2362 * canceling the editing.
2365 edited_idle_cb (GtkFileChooserDefault *impl)
2367 GDK_THREADS_ENTER ();
2369 g_source_destroy (impl->edited_idle);
2370 impl->edited_idle = NULL;
2372 _gtk_file_system_model_remove_editable (impl->browse_files_model);
2373 g_object_set (impl->list_name_renderer, "editable", FALSE, NULL);
2375 gtk_widget_set_sensitive (impl->browse_new_folder_button, TRUE);
2377 if (impl->edited_new_text) /* not cancelled? */
2379 GError *error = NULL;
2382 file = g_file_get_child_for_display_name (impl->current_folder,
2383 impl->edited_new_text,
2387 GError *error = NULL;
2389 if (!g_file_make_directory (file, NULL, &error))
2390 change_folder_and_display_error (impl, file, FALSE);
2392 error_creating_folder_dialog (impl, file, error);
2394 g_object_unref (file);
2397 error_creating_folder_dialog (impl, file, error);
2399 g_free (impl->edited_new_text);
2400 impl->edited_new_text = NULL;
2403 GDK_THREADS_LEAVE ();
2409 queue_edited_idle (GtkFileChooserDefault *impl,
2410 const gchar *new_text)
2412 /* We create the folder in an idle handler so that we don't modify the tree
2416 if (!impl->edited_idle)
2418 impl->edited_idle = g_idle_source_new ();
2419 g_source_set_closure (impl->edited_idle,
2420 g_cclosure_new_object (G_CALLBACK (edited_idle_cb),
2422 g_source_attach (impl->edited_idle, NULL);
2425 g_free (impl->edited_new_text);
2426 impl->edited_new_text = g_strdup (new_text);
2429 /* Callback used from the text cell renderer when the new folder is named */
2431 renderer_edited_cb (GtkCellRendererText *cell_renderer_text,
2433 const gchar *new_text,
2434 GtkFileChooserDefault *impl)
2436 /* work around bug #154921 */
2437 g_object_set (cell_renderer_text,
2438 "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
2439 queue_edited_idle (impl, new_text);
2442 /* Callback used from the text cell renderer when the new folder edition gets
2446 renderer_editing_canceled_cb (GtkCellRendererText *cell_renderer_text,
2447 GtkFileChooserDefault *impl)
2449 /* work around bug #154921 */
2450 g_object_set (cell_renderer_text,
2451 "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
2452 queue_edited_idle (impl, NULL);
2455 /* Creates the widgets for the filter combo box */
2457 filter_create (GtkFileChooserDefault *impl)
2459 impl->filter_combo = gtk_combo_box_new_text ();
2460 gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (impl->filter_combo), FALSE);
2462 g_signal_connect (impl->filter_combo, "changed",
2463 G_CALLBACK (filter_combo_changed), impl);
2465 gtk_widget_set_tooltip_text (impl->filter_combo,
2466 _("Select which types of files are shown"));
2468 return impl->filter_combo;
2472 button_new (GtkFileChooserDefault *impl,
2474 const char *stock_id,
2482 button = gtk_button_new_with_mnemonic (text);
2483 image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
2484 gtk_button_set_image (GTK_BUTTON (button), image);
2486 gtk_widget_set_sensitive (button, sensitive);
2487 g_signal_connect (button, "clicked", callback, impl);
2490 gtk_widget_show (button);
2495 /* Looks for a path among the shortcuts; returns its index or -1 if it doesn't exist */
2497 shortcut_find_position (GtkFileChooserDefault *impl,
2502 int current_folder_separator_idx;
2504 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
2507 current_folder_separator_idx = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2510 /* FIXME: is this still needed? */
2511 if (current_folder_separator_idx >= impl->shortcuts_model->length)
2515 for (i = 0; i < current_folder_separator_idx; i++)
2518 ShortcutType shortcut_type;
2520 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2521 SHORTCUTS_COL_DATA, &col_data,
2522 SHORTCUTS_COL_TYPE, &shortcut_type,
2527 if (shortcut_type == SHORTCUT_TYPE_VOLUME)
2529 GtkFileSystemVolume *volume;
2534 base_file = _gtk_file_system_volume_get_root (volume);
2536 exists = base_file && g_file_equal (file, base_file);
2539 g_object_unref (base_file);
2544 else if (shortcut_type == SHORTCUT_TYPE_FILE)
2548 model_file = col_data;
2550 if (model_file && g_file_equal (model_file, file))
2555 if (i < current_folder_separator_idx - 1)
2557 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
2558 g_assert_not_reached ();
2565 /* Tries to add a bookmark from a path name */
2567 shortcuts_add_bookmark_from_file (GtkFileChooserDefault *impl,
2573 g_return_val_if_fail (G_IS_FILE (file), FALSE);
2575 if (shortcut_find_position (impl, file) != -1)
2579 if (!_gtk_file_system_insert_bookmark (impl->file_system, file, pos, &error))
2581 error_adding_bookmark_dialog (impl, file, error);
2589 add_bookmark_foreach_cb (GtkTreeModel *model,
2594 GtkFileChooserDefault *impl;
2595 GtkFileSystemModel *fs_model;
2596 GtkTreeIter child_iter;
2599 impl = (GtkFileChooserDefault *) data;
2601 switch (impl->operation_mode)
2603 case OPERATION_MODE_BROWSE:
2604 fs_model = impl->browse_files_model;
2605 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter);
2606 file = _gtk_file_system_model_get_file (fs_model, &child_iter);
2609 case OPERATION_MODE_SEARCH:
2610 search_get_valid_child_iter (impl, &child_iter, iter);
2611 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter,
2612 SEARCH_MODEL_COL_FILE, &file,
2616 case OPERATION_MODE_RECENT:
2617 recent_get_valid_child_iter (impl, &child_iter, iter);
2618 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter,
2619 RECENT_MODEL_COL_FILE, &file,
2624 shortcuts_add_bookmark_from_file (impl, file, -1);
2627 /* Adds a bookmark from the currently selected item in the file list */
2629 bookmarks_add_selected_folder (GtkFileChooserDefault *impl)
2631 GtkTreeSelection *selection;
2633 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2635 if (gtk_tree_selection_count_selected_rows (selection) == 0)
2636 shortcuts_add_bookmark_from_file (impl, impl->current_folder, -1);
2638 gtk_tree_selection_selected_foreach (selection,
2639 add_bookmark_foreach_cb,
2643 /* Callback used when the "Add bookmark" button is clicked */
2645 add_bookmark_button_clicked_cb (GtkButton *button,
2646 GtkFileChooserDefault *impl)
2648 bookmarks_add_selected_folder (impl);
2651 /* Returns TRUE plus an iter in the shortcuts_model if a row is selected;
2652 * returns FALSE if no shortcut is selected.
2655 shortcuts_get_selected (GtkFileChooserDefault *impl,
2658 GtkTreeSelection *selection;
2659 GtkTreeIter parent_iter;
2661 if (!impl->browse_shortcuts_tree_view)
2664 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
2666 if (!gtk_tree_selection_get_selected (selection, NULL, &parent_iter))
2669 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
2675 /* Removes the selected bookmarks */
2677 remove_selected_bookmarks (GtkFileChooserDefault *impl)
2685 if (!shortcuts_get_selected (impl, &iter))
2688 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2689 SHORTCUTS_COL_DATA, &col_data,
2690 SHORTCUTS_COL_REMOVABLE, &removable,
2696 g_assert (col_data != NULL);
2701 if (!_gtk_file_system_remove_bookmark (impl->file_system, file, &error))
2702 error_removing_bookmark_dialog (impl, file, error);
2705 /* Callback used when the "Remove bookmark" button is clicked */
2707 remove_bookmark_button_clicked_cb (GtkButton *button,
2708 GtkFileChooserDefault *impl)
2710 remove_selected_bookmarks (impl);
2713 struct selection_check_closure {
2714 GtkFileChooserDefault *impl;
2717 gboolean all_folders;
2720 /* Used from gtk_tree_selection_selected_foreach() */
2722 selection_check_foreach_cb (GtkTreeModel *model,
2727 struct selection_check_closure *closure;
2728 GtkTreeIter child_iter;
2733 closure->num_selected++;
2735 switch (closure->impl->operation_mode)
2737 case OPERATION_MODE_BROWSE:
2738 gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
2739 info = _gtk_file_system_model_get_info (closure->impl->browse_files_model, &child_iter);
2740 is_folder = info ? (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) : FALSE;
2743 case OPERATION_MODE_SEARCH:
2744 search_get_valid_child_iter (closure->impl, &child_iter, iter);
2745 gtk_tree_model_get (GTK_TREE_MODEL (closure->impl->search_model), &child_iter,
2746 SEARCH_MODEL_COL_IS_FOLDER, &is_folder,
2750 case OPERATION_MODE_RECENT:
2751 recent_get_valid_child_iter (closure->impl, &child_iter, iter);
2752 gtk_tree_model_get (GTK_TREE_MODEL (closure->impl->recent_model), &child_iter,
2753 RECENT_MODEL_COL_IS_FOLDER, &is_folder,
2758 closure->all_folders = closure->all_folders && is_folder;
2759 closure->all_files = closure->all_files && !is_folder;
2762 /* Checks whether the selected items in the file list are all files or all folders */
2764 selection_check (GtkFileChooserDefault *impl,
2766 gboolean *all_files,
2767 gboolean *all_folders)
2769 struct selection_check_closure closure;
2770 GtkTreeSelection *selection;
2772 closure.impl = impl;
2773 closure.num_selected = 0;
2774 closure.all_files = TRUE;
2775 closure.all_folders = TRUE;
2777 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2778 gtk_tree_selection_selected_foreach (selection,
2779 selection_check_foreach_cb,
2782 g_assert (closure.num_selected == 0 || !(closure.all_files && closure.all_folders));
2785 *num_selected = closure.num_selected;
2788 *all_files = closure.all_files;
2791 *all_folders = closure.all_folders;
2794 struct get_selected_file_closure {
2795 GtkFileChooserDefault *impl;
2800 get_selected_file_foreach_cb (GtkTreeModel *model,
2805 struct get_selected_file_closure *closure;
2806 GtkTreeIter child_iter;
2810 switch (closure->impl->operation_mode)
2812 case OPERATION_MODE_BROWSE:
2813 gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
2814 closure->file = _gtk_file_system_model_get_file (closure->impl->browse_files_model, &child_iter);
2817 case OPERATION_MODE_SEARCH:
2818 search_get_valid_child_iter (closure->impl, &child_iter, iter);
2819 gtk_tree_model_get (GTK_TREE_MODEL (closure->impl->search_model), &child_iter,
2820 SEARCH_MODEL_COL_FILE, &closure->file,
2824 case OPERATION_MODE_RECENT:
2825 recent_get_valid_child_iter (closure->impl, &child_iter, iter);
2826 gtk_tree_model_get (GTK_TREE_MODEL (closure->impl->recent_model), &child_iter,
2827 RECENT_MODEL_COL_FILE, &closure->file,
2833 /* Returns a selected path from the file list */
2835 get_selected_file (GtkFileChooserDefault *impl)
2837 struct get_selected_file_closure closure;
2838 GtkTreeSelection *selection;
2840 closure.impl = impl;
2841 closure.file = NULL;
2843 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2844 gtk_tree_selection_selected_foreach (selection,
2845 get_selected_file_foreach_cb,
2848 return closure.file;
2852 GtkFileChooserDefault *impl;
2854 } UpdateTooltipData;
2857 update_tooltip (GtkTreeModel *model,
2862 UpdateTooltipData *udata = data;
2863 GtkTreeIter child_iter;
2866 if (udata->tip == NULL)
2868 const gchar *display_name;
2870 switch (udata->impl->operation_mode)
2872 case OPERATION_MODE_BROWSE:
2873 gtk_tree_model_sort_convert_iter_to_child_iter (udata->impl->sort_model,
2876 info = _gtk_file_system_model_get_info (udata->impl->browse_files_model, &child_iter);
2877 display_name = g_file_info_get_display_name (info);
2880 case OPERATION_MODE_SEARCH:
2881 search_get_valid_child_iter (udata->impl, &child_iter, iter);
2882 gtk_tree_model_get (GTK_TREE_MODEL (udata->impl->search_model), &child_iter,
2883 SEARCH_MODEL_COL_DISPLAY_NAME, &display_name,
2887 case OPERATION_MODE_RECENT:
2888 recent_get_valid_child_iter (udata->impl, &child_iter, iter);
2889 gtk_tree_model_get (GTK_TREE_MODEL (udata->impl->recent_model), &child_iter,
2890 RECENT_MODEL_COL_DISPLAY_NAME, &display_name,
2895 udata->tip = g_strdup_printf (_("Add the folder '%s' to the bookmarks"),
2901 /* Sensitize the "add bookmark" button if all the selected items are folders, or
2902 * if there are no selected items *and* the current folder is not in the
2903 * bookmarks list. De-sensitize the button otherwise.
2906 bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl)
2909 gboolean all_folders;
2913 selection_check (impl, &num_selected, NULL, &all_folders);
2915 if (num_selected == 0)
2916 active = (impl->current_folder != NULL) && (shortcut_find_position (impl, impl->current_folder) == -1);
2917 else if (num_selected == 1)
2921 file = get_selected_file (impl);
2922 active = all_folders && (shortcut_find_position (impl, file) == -1);
2925 active = all_folders;
2927 gtk_widget_set_sensitive (impl->browse_shortcuts_add_button, active);
2929 if (impl->browse_files_popup_menu_add_shortcut_item)
2930 gtk_widget_set_sensitive (impl->browse_files_popup_menu_add_shortcut_item,
2931 (num_selected == 0) ? FALSE : active);
2935 if (num_selected == 0)
2936 tip = g_strdup_printf (_("Add the current folder to the bookmarks"));
2937 else if (num_selected > 1)
2938 tip = g_strdup_printf (_("Add the selected folders to the bookmarks"));
2941 GtkTreeSelection *selection;
2942 UpdateTooltipData data;
2944 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2947 gtk_tree_selection_selected_foreach (selection, update_tooltip, &data);
2951 gtk_widget_set_tooltip_text (impl->browse_shortcuts_add_button, tip);
2956 /* Sets the sensitivity of the "remove bookmark" button depending on whether a
2957 * bookmark row is selected in the shortcuts tree.
2960 bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl)
2963 gboolean removable = FALSE;
2966 if (shortcuts_get_selected (impl, &iter))
2967 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2968 SHORTCUTS_COL_REMOVABLE, &removable,
2969 SHORTCUTS_COL_NAME, &name,
2972 gtk_widget_set_sensitive (impl->browse_shortcuts_remove_button, removable);
2978 tip = g_strdup_printf (_("Remove the bookmark '%s'"), name);
2979 gtk_widget_set_tooltip_text (impl->browse_shortcuts_remove_button, tip);
2987 shortcuts_check_popup_sensitivity (GtkFileChooserDefault *impl)
2990 gboolean removable = FALSE;
2992 if (impl->browse_shortcuts_popup_menu == NULL)
2995 if (shortcuts_get_selected (impl, &iter))
2996 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2997 SHORTCUTS_COL_REMOVABLE, &removable,
3000 gtk_widget_set_sensitive (impl->browse_shortcuts_popup_menu_remove_item, removable);
3001 gtk_widget_set_sensitive (impl->browse_shortcuts_popup_menu_rename_item, removable);
3004 /* GtkWidget::drag-begin handler for the shortcuts list. */
3006 shortcuts_drag_begin_cb (GtkWidget *widget,
3007 GdkDragContext *context,
3008 GtkFileChooserDefault *impl)
3011 impl->shortcuts_drag_context = g_object_ref (context);
3016 /* Removes the idle handler for outside drags */
3018 shortcuts_cancel_drag_outside_idle (GtkFileChooserDefault *impl)
3020 if (!impl->shortcuts_drag_outside_idle)
3023 g_source_destroy (impl->shortcuts_drag_outside_idle);
3024 impl->shortcuts_drag_outside_idle = NULL;
3028 /* GtkWidget::drag-end handler for the shortcuts list. */
3030 shortcuts_drag_end_cb (GtkWidget *widget,
3031 GdkDragContext *context,
3032 GtkFileChooserDefault *impl)
3035 g_object_unref (impl->shortcuts_drag_context);
3037 shortcuts_cancel_drag_outside_idle (impl);
3039 if (!impl->shortcuts_drag_outside)
3042 gtk_button_clicked (GTK_BUTTON (impl->browse_shortcuts_remove_button));
3044 impl->shortcuts_drag_outside = FALSE;
3048 /* GtkWidget::drag-data-delete handler for the shortcuts list. */
3050 shortcuts_drag_data_delete_cb (GtkWidget *widget,
3051 GdkDragContext *context,
3052 GtkFileChooserDefault *impl)
3054 g_signal_stop_emission_by_name (widget, "drag_data_delete");
3058 /* Creates a suitable drag cursor to indicate that the selected bookmark will be
3062 shortcuts_drag_set_delete_cursor (GtkFileChooserDefault *impl,
3065 GtkTreeView *tree_view;
3068 GdkPixmap *row_pixmap;
3073 tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
3075 /* Find the selected path and get its drag pixmap */
3077 if (!shortcuts_get_selected (impl, &iter))
3078 g_assert_not_reached ();
3080 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
3082 row_pixmap = gtk_tree_view_create_row_drag_icon (tree_view, path);
3083 gtk_tree_path_free (path);
3092 pixbuf = gtk_widget_render_icon (impl->browse_shortcuts_tree_view,
3098 GdkPixmap *composite;
3099 int row_pixmap_width, row_pixmap_height;
3100 int pixbuf_width, pixbuf_height;
3101 int composite_width, composite_height;
3102 int pixbuf_x, pixbuf_y;
3103 GdkGC *gc, *mask_gc;
3105 GdkBitmap *pixbuf_mask;
3107 /* Create pixmap and mask for composite image */
3109 gdk_drawable_get_size (row_pixmap, &row_pixmap_width, &row_pixmap_height);
3110 pixbuf_width = gdk_pixbuf_get_width (pixbuf);
3111 pixbuf_height = gdk_pixbuf_get_height (pixbuf);
3113 composite_width = MAX (row_pixmap_width, pixbuf_width);
3114 composite_height = MAX (row_pixmap_height, pixbuf_height);
3116 row_pixmap_y = (composite_height - row_pixmap_height) / 2;
3118 if (gtk_widget_get_direction (impl->browse_shortcuts_tree_view) == GTK_TEXT_DIR_RTL)
3121 pixbuf_x = composite_width - pixbuf_width;
3123 pixbuf_y = (composite_height - pixbuf_height) / 2;
3125 composite = gdk_pixmap_new (row_pixmap, composite_width, composite_height, -1);
3126 gc = gdk_gc_new (composite);
3128 mask = gdk_pixmap_new (row_pixmap, composite_width, composite_height, 1);
3129 mask_gc = gdk_gc_new (mask);
3131 gdk_gc_set_foreground (mask_gc, &color);
3132 gdk_draw_rectangle (mask, mask_gc, TRUE, 0, 0, composite_width, composite_height);
3135 color.green = 0xffff;
3136 color.blue = 0xffff;
3137 gdk_gc_set_rgb_fg_color (gc, &color);
3138 gdk_draw_rectangle (composite, gc, TRUE, 0, 0, composite_width, composite_height);
3140 /* Composite the row pixmap and the pixbuf */
3142 gdk_pixbuf_render_pixmap_and_mask_for_colormap
3144 gtk_widget_get_colormap (impl->browse_shortcuts_tree_view),
3145 NULL, &pixbuf_mask, 128);
3146 gdk_draw_drawable (mask, mask_gc, pixbuf_mask,
3149 pixbuf_width, pixbuf_height);
3150 g_object_unref (pixbuf_mask);
3152 gdk_draw_drawable (composite, gc, row_pixmap,
3155 row_pixmap_width, row_pixmap_height);
3157 gdk_gc_set_foreground (mask_gc, &color);
3158 gdk_draw_rectangle (mask, mask_gc, TRUE, 0, row_pixmap_y, row_pixmap_width, row_pixmap_height);
3160 gdk_draw_pixbuf (composite, gc, pixbuf,
3163 pixbuf_width, pixbuf_height,
3167 g_object_unref (pixbuf);
3168 g_object_unref (row_pixmap);
3170 row_pixmap = composite;
3174 /* The hotspot offsets here are copied from gtk_tree_view_drag_begin(), ugh */
3176 gtk_tree_view_get_path_at_pos (tree_view,
3177 tree_view->priv->press_start_x,
3178 tree_view->priv->press_start_y,
3184 gtk_drag_set_icon_pixmap (impl->shortcuts_drag_context,
3185 gdk_drawable_get_colormap (row_pixmap),
3188 tree_view->priv->press_start_x + 1,
3189 row_pixmap_y + cell_y + 1);
3191 g_object_unref (row_pixmap);
3193 g_object_unref (mask);
3196 /* We set the delete cursor and the shortcuts_drag_outside flag in an idle
3197 * handler so that we can tell apart the drag_leave event that comes right
3198 * before a drag_drop, from a normal drag_leave. We don't want to set the
3199 * cursor nor the flag in the latter case.
3202 shortcuts_drag_outside_idle_cb (GtkFileChooserDefault *impl)
3204 GDK_THREADS_ENTER ();
3206 shortcuts_drag_set_delete_cursor (impl, TRUE);
3207 impl->shortcuts_drag_outside = TRUE;
3209 shortcuts_cancel_drag_outside_idle (impl);
3211 GDK_THREADS_LEAVE ();
3217 /* GtkWidget::drag-leave handler for the shortcuts list. We unhighlight the
3221 shortcuts_drag_leave_cb (GtkWidget *widget,
3222 GdkDragContext *context,
3224 GtkFileChooserDefault *impl)
3227 if (gtk_drag_get_source_widget (context) == widget && !impl->shortcuts_drag_outside_idle)
3229 impl->shortcuts_drag_outside_idle = g_idle_source_new ();
3230 g_source_set_closure (impl->shortcuts_drag_outside_idle,
3231 g_cclosure_new_object (G_CALLBACK (shortcuts_drag_outside_idle_cb),
3233 g_source_attach (impl->shortcuts_drag_outside_idle, NULL);
3237 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3239 GTK_TREE_VIEW_DROP_BEFORE);
3241 g_signal_stop_emission_by_name (widget, "drag_leave");
3244 /* Computes the appropriate row and position for dropping */
3246 shortcuts_compute_drop_position (GtkFileChooserDefault *impl,
3250 GtkTreeViewDropPosition *pos)
3252 GtkTreeView *tree_view;
3253 GtkTreeViewColumn *column;
3257 int bookmarks_index;
3259 tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
3261 bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
3263 if (!gtk_tree_view_get_path_at_pos (tree_view,
3265 y - TREE_VIEW_HEADER_HEIGHT (tree_view),
3271 row = bookmarks_index + impl->num_bookmarks - 1;
3272 *path = gtk_tree_path_new_from_indices (row, -1);
3273 *pos = GTK_TREE_VIEW_DROP_AFTER;
3277 row = *gtk_tree_path_get_indices (*path);
3278 gtk_tree_view_get_background_area (tree_view, *path, column, &cell);
3279 gtk_tree_path_free (*path);
3281 if (row < bookmarks_index)
3283 row = bookmarks_index;
3284 *pos = GTK_TREE_VIEW_DROP_BEFORE;
3286 else if (row > bookmarks_index + impl->num_bookmarks - 1)
3288 row = bookmarks_index + impl->num_bookmarks - 1;
3289 *pos = GTK_TREE_VIEW_DROP_AFTER;
3293 if (cell_y < cell.height / 2)
3294 *pos = GTK_TREE_VIEW_DROP_BEFORE;
3296 *pos = GTK_TREE_VIEW_DROP_AFTER;
3299 *path = gtk_tree_path_new_from_indices (row, -1);
3302 /* GtkWidget::drag-motion handler for the shortcuts list. We basically
3303 * implement the destination side of DnD by hand, due to limitations in
3304 * GtkTreeView's DnD API.
3307 shortcuts_drag_motion_cb (GtkWidget *widget,
3308 GdkDragContext *context,
3312 GtkFileChooserDefault *impl)
3315 GtkTreeViewDropPosition pos;
3316 GdkDragAction action;
3319 if (gtk_drag_get_source_widget (context) == widget)
3321 shortcuts_cancel_drag_outside_idle (impl);
3323 if (impl->shortcuts_drag_outside)
3325 shortcuts_drag_set_delete_cursor (impl, FALSE);
3326 impl->shortcuts_drag_outside = FALSE;
3331 if (context->suggested_action == GDK_ACTION_COPY ||
3332 (context->actions & GDK_ACTION_COPY) != 0)
3333 action = GDK_ACTION_COPY;
3334 else if (context->suggested_action == GDK_ACTION_MOVE ||
3335 (context->actions & GDK_ACTION_MOVE) != 0)
3336 action = GDK_ACTION_MOVE;
3343 shortcuts_compute_drop_position (impl, x, y, &path, &pos);
3344 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), path, pos);
3345 gtk_tree_path_free (path);
3349 g_signal_stop_emission_by_name (widget, "drag_motion");
3353 gdk_drag_status (context, action, time_);
3360 /* GtkWidget::drag-drop handler for the shortcuts list. */
3362 shortcuts_drag_drop_cb (GtkWidget *widget,
3363 GdkDragContext *context,
3367 GtkFileChooserDefault *impl)
3370 shortcuts_cancel_drag_outside_idle (impl);
3373 g_signal_stop_emission_by_name (widget, "drag_drop");
3377 /* Parses a "text/uri-list" string and inserts its URIs as bookmarks */
3379 shortcuts_drop_uris (GtkFileChooserDefault *impl,
3380 GtkSelectionData *selection_data,
3386 uris = gtk_selection_data_get_uris (selection_data);
3390 for (i = 0; uris[i]; i++)
3396 file = g_file_new_for_uri (uri);
3398 if (shortcuts_add_bookmark_from_file (impl, file, position))
3401 g_object_unref (file);
3407 /* Reorders the selected bookmark to the specified position */
3409 shortcuts_reorder (GtkFileChooserDefault *impl,
3414 ShortcutType shortcut_type;
3417 int bookmarks_index;
3422 /* Get the selected path */
3424 if (!shortcuts_get_selected (impl, &iter))
3425 g_assert_not_reached ();
3427 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
3428 old_position = *gtk_tree_path_get_indices (path);
3429 gtk_tree_path_free (path);
3431 bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
3432 old_position -= bookmarks_index;
3433 g_assert (old_position >= 0 && old_position < impl->num_bookmarks);
3435 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
3436 SHORTCUTS_COL_NAME, &name,
3437 SHORTCUTS_COL_DATA, &col_data,
3438 SHORTCUTS_COL_TYPE, &shortcut_type,
3440 g_assert (col_data != NULL);
3441 g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
3444 g_object_ref (file); /* removal below will free file, so we need a new ref */
3446 /* Remove the path from the old position and insert it in the new one */
3448 if (new_position > old_position)
3451 if (old_position == new_position)
3455 if (_gtk_file_system_remove_bookmark (impl->file_system, file, &error))
3457 shortcuts_add_bookmark_from_file (impl, file, new_position);
3458 _gtk_file_system_set_bookmark_label (impl->file_system, file, name);
3461 error_adding_bookmark_dialog (impl, file, error);
3465 g_object_unref (file);
3468 /* Callback used when we get the drag data for the bookmarks list. We add the
3469 * received URIs as bookmarks if they are folders.
3472 shortcuts_drag_data_received_cb (GtkWidget *widget,
3473 GdkDragContext *context,
3476 GtkSelectionData *selection_data,
3481 GtkFileChooserDefault *impl;
3482 GtkTreePath *tree_path;
3483 GtkTreeViewDropPosition tree_pos;
3485 int bookmarks_index;
3487 impl = GTK_FILE_CHOOSER_DEFAULT (data);
3489 /* Compute position */
3491 bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
3493 shortcuts_compute_drop_position (impl, x, y, &tree_path, &tree_pos);
3494 position = *gtk_tree_path_get_indices (tree_path);
3495 gtk_tree_path_free (tree_path);
3497 if (tree_pos == GTK_TREE_VIEW_DROP_AFTER)
3500 g_assert (position >= bookmarks_index);
3501 position -= bookmarks_index;
3503 if (gtk_targets_include_uri (&selection_data->target, 1))
3504 shortcuts_drop_uris (impl, selection_data, position);
3505 else if (selection_data->target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
3506 shortcuts_reorder (impl, position);
3508 g_signal_stop_emission_by_name (widget, "drag_data_received");
3511 /* Callback used to display a tooltip in the shortcuts tree */
3513 shortcuts_query_tooltip_cb (GtkWidget *widget,
3516 gboolean keyboard_mode,
3517 GtkTooltip *tooltip,
3518 GtkFileChooserDefault *impl)
3520 GtkTreeModel *model;
3523 if (gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget),
3531 ShortcutType shortcut_type;
3533 gtk_tree_model_get (model, &iter,
3534 SHORTCUTS_COL_DATA, &col_data,
3535 SHORTCUTS_COL_TYPE, &shortcut_type,
3538 if (shortcut_type == SHORTCUT_TYPE_SEPARATOR)
3540 else if (shortcut_type == SHORTCUT_TYPE_VOLUME)
3544 else if (shortcut_type == SHORTCUT_TYPE_FILE)
3549 file = G_FILE (col_data);
3550 parse_name = g_file_get_parse_name (file);
3552 gtk_tooltip_set_text (tooltip, parse_name);
3554 g_free (parse_name);
3558 else if (shortcut_type == SHORTCUT_TYPE_SEARCH)
3562 else if (shortcut_type == SHORTCUT_TYPE_RECENT)
3572 /* Callback used when the selection in the shortcuts tree changes */
3574 shortcuts_selection_changed_cb (GtkTreeSelection *selection,
3575 GtkFileChooserDefault *impl)
3578 GtkTreeIter child_iter;
3580 bookmarks_check_remove_sensitivity (impl);
3581 shortcuts_check_popup_sensitivity (impl);
3583 if (impl->changing_folder)
3586 if (gtk_tree_selection_get_selected(selection, NULL, &iter))
3588 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
3591 shortcuts_activate_iter (impl, &child_iter);
3596 shortcuts_row_separator_func (GtkTreeModel *model,
3600 ShortcutType shortcut_type;
3602 gtk_tree_model_get (model, iter, SHORTCUTS_COL_TYPE, &shortcut_type, -1);
3604 return shortcut_type == SHORTCUT_TYPE_SEPARATOR;
3607 /* Since GtkTreeView has a keybinding attached to '/', we need to catch
3608 * keypresses before the TreeView gets them.
3611 tree_view_keybinding_cb (GtkWidget *tree_view,
3613 GtkFileChooserDefault *impl)
3615 if ((event->keyval == GDK_slash
3616 || event->keyval == GDK_KP_Divide
3618 || event->keyval == GDK_asciitilde
3620 ) && ! (event->state & (~GDK_SHIFT_MASK & gtk_accelerator_get_default_mod_mask ())))
3622 location_popup_handler (impl, event->string);
3629 /* Callback used when the file list's popup menu is detached */
3631 shortcuts_popup_menu_detach_cb (GtkWidget *attach_widget,
3634 GtkFileChooserDefault *impl;
3636 impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
3637 g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
3639 impl->browse_shortcuts_popup_menu = NULL;
3640 impl->browse_shortcuts_popup_menu_remove_item = NULL;
3641 impl->browse_shortcuts_popup_menu_rename_item = NULL;
3645 remove_shortcut_cb (GtkMenuItem *item,
3646 GtkFileChooserDefault *impl)
3648 remove_selected_bookmarks (impl);
3651 /* Rename the selected bookmark */
3653 rename_selected_bookmark (GtkFileChooserDefault *impl)
3657 GtkTreeViewColumn *column;
3658 GtkCellRenderer *cell;
3661 if (shortcuts_get_selected (impl, &iter))
3663 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
3664 column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), 0);
3665 renderers = gtk_tree_view_column_get_cell_renderers (column);
3666 cell = g_list_nth_data (renderers, 1);
3667 g_list_free (renderers);
3668 g_object_set (cell, "editable", TRUE, NULL);
3669 gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3670 path, column, cell, TRUE);
3671 gtk_tree_path_free (path);
3676 rename_shortcut_cb (GtkMenuItem *item,
3677 GtkFileChooserDefault *impl)
3679 rename_selected_bookmark (impl);
3682 /* Constructs the popup menu for the file list if needed */
3684 shortcuts_build_popup_menu (GtkFileChooserDefault *impl)
3688 if (impl->browse_shortcuts_popup_menu)
3691 impl->browse_shortcuts_popup_menu = gtk_menu_new ();
3692 gtk_menu_attach_to_widget (GTK_MENU (impl->browse_shortcuts_popup_menu),
3693 impl->browse_shortcuts_tree_view,
3694 shortcuts_popup_menu_detach_cb);
3696 item = gtk_image_menu_item_new_with_label (_("Remove"));
3697 impl->browse_shortcuts_popup_menu_remove_item = item;
3698 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
3699 gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU));
3700 g_signal_connect (item, "activate",
3701 G_CALLBACK (remove_shortcut_cb), impl);
3702 gtk_widget_show (item);
3703 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu), item);
3705 item = gtk_menu_item_new_with_label (_("Rename..."));
3706 impl->browse_shortcuts_popup_menu_rename_item = item;
3707 g_signal_connect (item, "activate",
3708 G_CALLBACK (rename_shortcut_cb), impl);
3709 gtk_widget_show (item);
3710 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu), item);
3714 shortcuts_update_popup_menu (GtkFileChooserDefault *impl)
3716 shortcuts_build_popup_menu (impl);
3717 shortcuts_check_popup_sensitivity (impl);
3721 popup_position_func (GtkMenu *menu,
3725 gpointer user_data);
3728 shortcuts_popup_menu (GtkFileChooserDefault *impl,
3729 GdkEventButton *event)
3731 shortcuts_update_popup_menu (impl);
3733 gtk_menu_popup (GTK_MENU (impl->browse_shortcuts_popup_menu),
3734 NULL, NULL, NULL, NULL,
3735 event->button, event->time);
3738 gtk_menu_popup (GTK_MENU (impl->browse_shortcuts_popup_menu),
3740 popup_position_func, impl->browse_shortcuts_tree_view,
3741 0, GDK_CURRENT_TIME);
3742 gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu),
3747 /* Callback used for the GtkWidget::popup-menu signal of the shortcuts list */
3749 shortcuts_popup_menu_cb (GtkWidget *widget,
3750 GtkFileChooserDefault *impl)
3752 shortcuts_popup_menu (impl, NULL);
3756 /* Callback used when a button is pressed on the shortcuts list.
3757 * We trap button 3 to bring up a popup menu.
3760 shortcuts_button_press_event_cb (GtkWidget *widget,
3761 GdkEventButton *event,
3762 GtkFileChooserDefault *impl)
3764 static gboolean in_press = FALSE;
3770 if (event->button != 3)
3774 handled = gtk_widget_event (impl->browse_shortcuts_tree_view, (GdkEvent *) event);
3780 shortcuts_popup_menu (impl, event);
3785 shortcuts_edited (GtkCellRenderer *cell,
3788 GtkFileChooserDefault *impl)
3794 g_object_set (cell, "editable", FALSE, NULL);
3796 path = gtk_tree_path_new_from_string (path_string);
3797 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
3798 g_assert_not_reached ();
3800 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
3801 SHORTCUTS_COL_DATA, &shortcut,
3803 gtk_tree_path_free (path);
3805 _gtk_file_system_set_bookmark_label (impl->file_system, shortcut, new_text);
3809 shortcuts_editing_canceled (GtkCellRenderer *cell,
3810 GtkFileChooserDefault *impl)
3812 g_object_set (cell, "editable", FALSE, NULL);
3815 /* Creates the widgets for the shortcuts and bookmarks tree */
3817 shortcuts_list_create (GtkFileChooserDefault *impl)
3820 GtkTreeSelection *selection;
3821 GtkTreeViewColumn *column;
3822 GtkCellRenderer *renderer;
3824 /* Target types for dragging a row to/from the shortcuts list */
3825 const GtkTargetEntry tree_model_row_targets[] = {
3826 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }
3829 /* Scrolled window */
3831 swin = gtk_scrolled_window_new (NULL, NULL);
3832 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
3833 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3834 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
3836 gtk_widget_show (swin);
3840 impl->browse_shortcuts_tree_view = gtk_tree_view_new ();
3841 #ifdef PROFILE_FILE_CHOOSER
3842 g_object_set_data (G_OBJECT (impl->browse_shortcuts_tree_view), "fmq-name", "shortcuts");
3844 g_signal_connect (impl->browse_shortcuts_tree_view, "key_press_event",
3845 G_CALLBACK (tree_view_keybinding_cb), impl);
3846 g_signal_connect (impl->browse_shortcuts_tree_view, "popup_menu",
3847 G_CALLBACK (shortcuts_popup_menu_cb), impl);
3848 g_signal_connect (impl->browse_shortcuts_tree_view, "button_press_event",
3849 G_CALLBACK (shortcuts_button_press_event_cb), impl);
3850 /* Accessible object name for the file chooser's shortcuts pane */
3851 atk_object_set_name (gtk_widget_get_accessible (impl->browse_shortcuts_tree_view), _("Places"));
3853 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), impl->shortcuts_pane_filter_model);
3855 gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3857 tree_model_row_targets,
3858 G_N_ELEMENTS (tree_model_row_targets),
3861 gtk_drag_dest_set (impl->browse_shortcuts_tree_view,
3862 GTK_DEST_DEFAULT_ALL,
3863 tree_model_row_targets,
3864 G_N_ELEMENTS (tree_model_row_targets),
3865 GDK_ACTION_COPY | GDK_ACTION_MOVE);
3866 gtk_drag_dest_add_uri_targets (impl->browse_shortcuts_tree_view);
3868 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
3869 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
3870 gtk_tree_selection_set_select_function (selection,
3871 shortcuts_select_func,
3874 g_signal_connect (selection, "changed",
3875 G_CALLBACK (shortcuts_selection_changed_cb), impl);
3877 g_signal_connect (impl->browse_shortcuts_tree_view, "key_press_event",
3878 G_CALLBACK (shortcuts_key_press_event_cb), impl);
3880 g_signal_connect (impl->browse_shortcuts_tree_view, "drag_begin",
3881 G_CALLBACK (shortcuts_drag_begin_cb), impl);
3882 g_signal_connect (impl->browse_shortcuts_tree_view, "drag_end",
3883 G_CALLBACK (shortcuts_drag_end_cb), impl);
3884 g_signal_connect (impl->browse_shortcuts_tree_view, "drag_data_delete",
3885 G_CALLBACK (shortcuts_drag_data_delete_cb), impl);
3887 g_signal_connect (impl->browse_shortcuts_tree_view, "drag_leave",
3888 G_CALLBACK (shortcuts_drag_leave_cb), impl);
3889 g_signal_connect (impl->browse_shortcuts_tree_view, "drag_motion",
3890 G_CALLBACK (shortcuts_drag_motion_cb), impl);
3891 g_signal_connect (impl->browse_shortcuts_tree_view, "drag_drop",
3892 G_CALLBACK (shortcuts_drag_drop_cb), impl);
3893 g_signal_connect (impl->browse_shortcuts_tree_view, "drag_data_received",
3894 G_CALLBACK (shortcuts_drag_data_received_cb), impl);
3896 /* Support tooltips */
3897 gtk_widget_set_has_tooltip (impl->browse_shortcuts_tree_view, TRUE);
3898 g_signal_connect (impl->browse_shortcuts_tree_view, "query-tooltip",
3899 G_CALLBACK (shortcuts_query_tooltip_cb), impl);
3901 gtk_container_add (GTK_CONTAINER (swin), impl->browse_shortcuts_tree_view);
3902 gtk_widget_show (impl->browse_shortcuts_tree_view);
3906 column = gtk_tree_view_column_new ();
3907 /* Column header for the file chooser's shortcuts pane */
3908 gtk_tree_view_column_set_title (column, _("_Places"));
3910 renderer = gtk_cell_renderer_pixbuf_new ();
3911 gtk_tree_view_column_pack_start (column, renderer, FALSE);
3912 gtk_tree_view_column_set_attributes (column, renderer,
3913 "pixbuf", SHORTCUTS_COL_PIXBUF,
3914 "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
3917 renderer = gtk_cell_renderer_text_new ();
3918 g_signal_connect (renderer, "edited",
3919 G_CALLBACK (shortcuts_edited), impl);
3920 g_signal_connect (renderer, "editing-canceled",
3921 G_CALLBACK (shortcuts_editing_canceled), impl);
3922 gtk_tree_view_column_pack_start (column, renderer, TRUE);
3923 gtk_tree_view_column_set_attributes (column, renderer,
3924 "text", SHORTCUTS_COL_NAME,
3927 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3928 shortcuts_row_separator_func,
3931 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), column);
3936 /* Creates the widgets for the shortcuts/bookmarks pane */
3938 shortcuts_pane_create (GtkFileChooserDefault *impl,
3939 GtkSizeGroup *size_group)
3945 vbox = gtk_vbox_new (FALSE, 6);
3946 gtk_widget_show (vbox);
3948 /* Shortcuts tree */
3950 widget = shortcuts_list_create (impl);
3951 gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
3953 /* Box for buttons */
3955 hbox = gtk_hbox_new (TRUE, 6);
3956 gtk_size_group_add_widget (size_group, hbox);
3957 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
3958 gtk_widget_show (hbox);
3960 /* Add bookmark button */
3962 impl->browse_shortcuts_add_button = button_new (impl,
3967 G_CALLBACK (add_bookmark_button_clicked_cb));
3968 gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_add_button, TRUE, TRUE, 0);
3969 gtk_widget_set_tooltip_text (impl->browse_shortcuts_add_button,
3970 _("Add the selected folder to the Bookmarks"));
3972 /* Remove bookmark button */
3974 impl->browse_shortcuts_remove_button = button_new (impl,
3979 G_CALLBACK (remove_bookmark_button_clicked_cb));
3980 gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_remove_button, TRUE, TRUE, 0);
3981 gtk_widget_set_tooltip_text (impl->browse_shortcuts_remove_button,
3982 _("Remove the selected bookmark"));
3987 /* Handles key press events on the file list, so that we can trap Enter to
3988 * activate the default button on our own. Also, checks to see if '/' has been
3989 * pressed. See comment by tree_view_keybinding_cb() for more details.
3992 trap_activate_cb (GtkWidget *widget,
3996 GtkFileChooserDefault *impl;
3999 impl = (GtkFileChooserDefault *) data;
4001 modifiers = gtk_accelerator_get_default_mod_mask ();
4003 if ((event->keyval == GDK_slash
4004 || event->keyval == GDK_KP_Divide
4006 || event->keyval == GDK_asciitilde
4008 ) && ! (event->state & (~GDK_SHIFT_MASK & modifiers)))
4010 location_popup_handler (impl, event->string);
4014 if ((event->keyval == GDK_Return
4015 || event->keyval == GDK_ISO_Enter
4016 || event->keyval == GDK_KP_Enter
4017 || event->keyval == GDK_space
4018 || event->keyval == GDK_KP_Space)
4019 && ((event->state & modifiers) == 0)
4020 && !(impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
4021 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
4025 window = get_toplevel (widget);
4027 && widget != window->default_widget
4028 && !(widget == window->focus_widget &&
4029 (!window->default_widget || !GTK_WIDGET_SENSITIVE (window->default_widget))))
4031 gtk_window_activate_default (window);
4039 /* Callback used when the file list's popup menu is detached */
4041 popup_menu_detach_cb (GtkWidget *attach_widget,
4044 GtkFileChooserDefault *impl;
4046 impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
4047 g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
4049 impl->browse_files_popup_menu = NULL;
4050 impl->browse_files_popup_menu_add_shortcut_item = NULL;
4051 impl->browse_files_popup_menu_hidden_files_item = NULL;
4054 /* Callback used when the "Add to Bookmarks" menu item is activated */
4056 add_to_shortcuts_cb (GtkMenuItem *item,
4057 GtkFileChooserDefault *impl)
4059 bookmarks_add_selected_folder (impl);
4062 /* Callback used when the "Show Hidden Files" menu item is toggled */
4064 show_hidden_toggled_cb (GtkCheckMenuItem *item,
4065 GtkFileChooserDefault *impl)
4068 "show-hidden", gtk_check_menu_item_get_active (item),
4072 /* Shows an error dialog about not being able to select a dragged file */
4074 error_selecting_dragged_file_dialog (GtkFileChooserDefault *impl,
4079 _("Could not select file"),
4084 file_list_drag_data_select_uris (GtkFileChooserDefault *impl,
4089 GtkFileChooser *chooser = GTK_FILE_CHOOSER (impl);
4091 for (i = 1; uris[i]; i++)
4094 GError *error = NULL;
4097 file = g_file_new_for_uri (uri);
4099 gtk_file_chooser_default_select_file (chooser, file, &error);
4101 error_selecting_dragged_file_dialog (impl, file, error);
4103 g_object_unref (file);
4107 struct FileListDragData
4109 GtkFileChooserDefault *impl;
4115 file_list_drag_data_received_get_info_cb (GCancellable *cancellable,
4117 const GError *error,
4120 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
4121 struct FileListDragData *data = user_data;
4122 GtkFileChooser *chooser = GTK_FILE_CHOOSER (data->impl);
4124 if (cancellable != data->impl->file_list_drag_data_received_cancellable)
4127 data->impl->file_list_drag_data_received_cancellable = NULL;
4129 if (cancelled || error)
4132 if ((data->impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
4133 data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) &&
4134 data->uris[1] == 0 && !error &&
4135 g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
4136 change_folder_and_display_error (data->impl, data->file, FALSE);
4139 GError *error = NULL;
4141 gtk_file_chooser_default_unselect_all (chooser);
4142 gtk_file_chooser_default_select_file (chooser, data->file, &error);
4144 error_selecting_dragged_file_dialog (data->impl, data->file, error);
4146 browse_files_center_selected_row (data->impl);
4149 if (data->impl->select_multiple)
4150 file_list_drag_data_select_uris (data->impl, data->uris);
4153 g_object_unref (data->impl);
4154 g_strfreev (data->uris);
4155 g_object_unref (data->file);
4158 g_object_unref (cancellable);
4162 file_list_drag_data_received_cb (GtkWidget *widget,
4163 GdkDragContext *context,
4166 GtkSelectionData *selection_data,
4171 GtkFileChooserDefault *impl;
4172 GtkFileChooser *chooser;
4177 impl = GTK_FILE_CHOOSER_DEFAULT (data);
4178 chooser = GTK_FILE_CHOOSER (data);
4180 /* Allow only drags from other widgets; see bug #533891. */
4181 if (gtk_drag_get_source_widget (context) == widget)
4184 /* Parse the text/uri-list string, navigate to the first one */
4185 uris = gtk_selection_data_get_uris (selection_data);
4186 if (uris && uris[0])
4188 struct FileListDragData *data;
4191 file = g_file_new_for_uri (uri);
4193 data = g_new0 (struct FileListDragData, 1);
4194 data->impl = g_object_ref (impl);
4198 if (impl->file_list_drag_data_received_cancellable)
4199 g_cancellable_cancel (impl->file_list_drag_data_received_cancellable);
4201 impl->file_list_drag_data_received_cancellable =
4202 _gtk_file_system_get_info (impl->file_system, file,
4204 file_list_drag_data_received_get_info_cb,
4208 g_signal_stop_emission_by_name (widget, "drag_data_received");
4211 /* Don't do anything with the drag_drop signal */
4213 file_list_drag_drop_cb (GtkWidget *widget,
4214 GdkDragContext *context,
4218 GtkFileChooserDefault *impl)
4220 g_signal_stop_emission_by_name (widget, "drag_drop");
4224 /* Disable the normal tree drag motion handler, it makes it look like you're
4225 dropping the dragged item onto a tree item */
4227 file_list_drag_motion_cb (GtkWidget *widget,
4228 GdkDragContext *context,
4232 GtkFileChooserDefault *impl)
4234 g_signal_stop_emission_by_name (widget, "drag_motion");
4238 /* Constructs the popup menu for the file list if needed */
4240 file_list_build_popup_menu (GtkFileChooserDefault *impl)
4244 if (impl->browse_files_popup_menu)
4247 impl->browse_files_popup_menu = gtk_menu_new ();
4248 gtk_menu_attach_to_widget (GTK_MENU (impl->browse_files_popup_menu),
4249 impl->browse_files_tree_view,
4250 popup_menu_detach_cb);
4252 item = gtk_image_menu_item_new_with_mnemonic (_("_Add to Bookmarks"));
4253 impl->browse_files_popup_menu_add_shortcut_item = item;
4254 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
4255 gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU));
4256 gtk_widget_set_sensitive (item, FALSE);
4257 g_signal_connect (item, "activate",
4258 G_CALLBACK (add_to_shortcuts_cb), impl);
4259 gtk_widget_show (item);
4260 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4262 item = gtk_separator_menu_item_new ();
4263 gtk_widget_show (item);
4264 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4266 item = gtk_check_menu_item_new_with_mnemonic (_("Show _Hidden Files"));
4267 impl->browse_files_popup_menu_hidden_files_item = item;
4268 g_signal_connect (item, "toggled",
4269 G_CALLBACK (show_hidden_toggled_cb), impl);
4270 gtk_widget_show (item);
4271 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4274 /* Updates the popup menu for the file list, creating it if necessary */
4276 file_list_update_popup_menu (GtkFileChooserDefault *impl)
4278 file_list_build_popup_menu (impl);
4280 /* FIXME - handle OPERATION_MODE_SEARCH and OPERATION_MODE_RECENT */
4282 /* The sensitivity of the Add to Bookmarks item is set in
4283 * bookmarks_check_add_sensitivity()
4286 g_signal_handlers_block_by_func (impl->browse_files_popup_menu_hidden_files_item,
4287 G_CALLBACK (show_hidden_toggled_cb), impl);
4288 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (impl->browse_files_popup_menu_hidden_files_item),
4290 g_signal_handlers_unblock_by_func (impl->browse_files_popup_menu_hidden_files_item,
4291 G_CALLBACK (show_hidden_toggled_cb), impl);
4295 popup_position_func (GtkMenu *menu,
4301 GtkWidget *widget = GTK_WIDGET (user_data);
4302 GdkScreen *screen = gtk_widget_get_screen (widget);
4305 GdkRectangle monitor;
4307 g_return_if_fail (GTK_WIDGET_REALIZED (widget));
4309 gdk_window_get_origin (widget->window, x, y);
4311 gtk_widget_size_request (GTK_WIDGET (menu), &req);
4313 *x += (widget->allocation.width - req.width) / 2;
4314 *y += (widget->allocation.height - req.height) / 2;
4316 monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
4317 gtk_menu_set_monitor (menu, monitor_num);
4318 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
4320 *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
4321 *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
4327 file_list_popup_menu (GtkFileChooserDefault *impl,
4328 GdkEventButton *event)
4330 file_list_update_popup_menu (impl);
4332 gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
4333 NULL, NULL, NULL, NULL,
4334 event->button, event->time);
4337 gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
4339 popup_position_func, impl->browse_files_tree_view,
4340 0, GDK_CURRENT_TIME);
4341 gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->browse_files_popup_menu),
4347 /* Callback used for the GtkWidget::popup-menu signal of the file list */
4349 list_popup_menu_cb (GtkWidget *widget,
4350 GtkFileChooserDefault *impl)
4352 file_list_popup_menu (impl, NULL);
4356 /* Callback used when a button is pressed on the file list. We trap button 3 to
4357 * bring up a popup menu.
4360 list_button_press_event_cb (GtkWidget *widget,
4361 GdkEventButton *event,
4362 GtkFileChooserDefault *impl)
4364 static gboolean in_press = FALSE;
4370 if (event->button != 3)
4374 handled = gtk_widget_event (impl->browse_files_tree_view, (GdkEvent *) event);
4377 file_list_popup_menu (impl, event);
4381 /* Sets the sort column IDs for the file list based on the operation mode */
4383 file_list_set_sort_column_ids (GtkFileChooserDefault *impl)
4385 int name_id, mtime_id;
4387 name_id = mtime_id = 0;
4389 switch (impl->operation_mode)
4391 case OPERATION_MODE_BROWSE:
4392 name_id = FILE_LIST_COL_NAME;
4393 mtime_id = FILE_LIST_COL_MTIME;
4395 case OPERATION_MODE_SEARCH:
4396 name_id = SEARCH_MODEL_COL_FILE;
4397 mtime_id = SEARCH_MODEL_COL_STAT;
4399 case OPERATION_MODE_RECENT:
4400 name_id = RECENT_MODEL_COL_FILE;
4401 mtime_id = RECENT_MODEL_COL_INFO;
4405 gtk_tree_view_column_set_sort_column_id (impl->list_name_column, name_id);
4406 gtk_tree_view_column_set_sort_column_id (impl->list_mtime_column, mtime_id);
4410 file_list_query_tooltip_cb (GtkWidget *widget,
4413 gboolean keyboard_tip,
4414 GtkTooltip *tooltip,
4417 GtkFileChooserDefault *impl = user_data;
4418 GtkTreeIter iter, child_iter;
4419 GtkTreePath *path = NULL;
4423 if (impl->operation_mode == OPERATION_MODE_BROWSE)
4427 gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (impl->browse_files_tree_view),
4435 switch (impl->operation_mode)
4437 case OPERATION_MODE_SEARCH:
4438 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->search_model_sort), &iter, path))
4440 gtk_tree_path_free (path);
4444 search_get_valid_child_iter (impl, &child_iter, &iter);
4445 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter,
4446 SEARCH_MODEL_COL_FILE, &file,
4450 case OPERATION_MODE_RECENT:
4451 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->recent_model_sort), &iter, path))
4453 gtk_tree_path_free (path);
4457 recent_get_valid_child_iter (impl, &child_iter, &iter);
4458 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter,
4459 RECENT_MODEL_COL_FILE, &file,
4463 case OPERATION_MODE_BROWSE:
4464 g_assert_not_reached ();
4470 gtk_tree_path_free (path);
4474 filename = g_file_get_path (file);
4475 gtk_tooltip_set_text (tooltip, filename);
4476 gtk_tree_view_set_tooltip_row (GTK_TREE_VIEW (impl->browse_files_tree_view),
4481 gtk_tree_path_free (path);
4486 /* Creates the widgets for the file list */
4488 create_file_list (GtkFileChooserDefault *impl)
4491 GtkTreeSelection *selection;
4492 GtkTreeViewColumn *column;
4493 GtkCellRenderer *renderer;
4495 /* Scrolled window */
4496 swin = gtk_scrolled_window_new (NULL, NULL);
4497 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
4498 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
4499 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
4502 /* Tree/list view */
4504 impl->browse_files_tree_view = gtk_tree_view_new ();
4505 #ifdef PROFILE_FILE_CHOOSER
4506 g_object_set_data (G_OBJECT (impl->browse_files_tree_view), "fmq-name", "file_list");
4508 g_object_set_data (G_OBJECT (impl->browse_files_tree_view), I_("GtkFileChooserDefault"), impl);
4509 atk_object_set_name (gtk_widget_get_accessible (impl->browse_files_tree_view), _("Files"));
4511 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->browse_files_tree_view), TRUE);
4512 gtk_container_add (GTK_CONTAINER (swin), impl->browse_files_tree_view);
4514 gtk_drag_dest_set (impl->browse_files_tree_view,
4515 GTK_DEST_DEFAULT_ALL,
4517 GDK_ACTION_COPY | GDK_ACTION_MOVE);
4518 gtk_drag_dest_add_uri_targets (impl->browse_files_tree_view);
4520 g_signal_connect (impl->browse_files_tree_view, "row_activated",
4521 G_CALLBACK (list_row_activated), impl);
4522 g_signal_connect (impl->browse_files_tree_view, "key_press_event",
4523 G_CALLBACK (trap_activate_cb), impl);
4524 g_signal_connect (impl->browse_files_tree_view, "popup_menu",
4525 G_CALLBACK (list_popup_menu_cb), impl);
4526 g_signal_connect (impl->browse_files_tree_view, "button_press_event",
4527 G_CALLBACK (list_button_press_event_cb), impl);
4529 g_signal_connect (impl->browse_files_tree_view, "drag_data_received",
4530 G_CALLBACK (file_list_drag_data_received_cb), impl);
4531 g_signal_connect (impl->browse_files_tree_view, "drag_drop",
4532 G_CALLBACK (file_list_drag_drop_cb), impl);
4533 g_signal_connect (impl->browse_files_tree_view, "drag_motion",
4534 G_CALLBACK (file_list_drag_motion_cb), impl);
4536 g_object_set (impl->browse_files_tree_view, "has-tooltip", TRUE, NULL);
4537 g_signal_connect (impl->browse_files_tree_view, "query-tooltip",
4538 G_CALLBACK (file_list_query_tooltip_cb), impl);
4540 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4541 gtk_tree_selection_set_select_function (selection,
4544 gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_files_tree_view),
4547 GDK_ACTION_COPY | GDK_ACTION_MOVE);
4548 gtk_drag_source_add_uri_targets (impl->browse_files_tree_view);
4550 g_signal_connect (selection, "changed",
4551 G_CALLBACK (list_selection_changed), impl);
4553 /* Filename column */
4555 impl->list_name_column = gtk_tree_view_column_new ();
4556 gtk_tree_view_column_set_expand (impl->list_name_column, TRUE);
4557 gtk_tree_view_column_set_resizable (impl->list_name_column, TRUE);
4558 gtk_tree_view_column_set_title (impl->list_name_column, _("Name"));
4559 gtk_tree_view_column_set_sort_column_id (impl->list_name_column, FILE_LIST_COL_NAME);
4561 renderer = gtk_cell_renderer_pixbuf_new ();
4562 gtk_tree_view_column_pack_start (impl->list_name_column, renderer, FALSE);
4563 gtk_tree_view_column_set_cell_data_func (impl->list_name_column, renderer,
4564 list_icon_data_func, impl, NULL);
4566 impl->list_name_renderer = gtk_cell_renderer_text_new ();
4567 g_object_set (impl->list_name_renderer,
4568 "ellipsize", PANGO_ELLIPSIZE_END,
4570 g_signal_connect (impl->list_name_renderer, "edited",
4571 G_CALLBACK (renderer_edited_cb), impl);
4572 g_signal_connect (impl->list_name_renderer, "editing_canceled",
4573 G_CALLBACK (renderer_editing_canceled_cb), impl);
4574 gtk_tree_view_column_pack_start (impl->list_name_column, impl->list_name_renderer, TRUE);
4575 gtk_tree_view_column_set_cell_data_func (impl->list_name_column, impl->list_name_renderer,
4576 list_name_data_func, impl, NULL);
4578 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), impl->list_name_column);
4582 column = gtk_tree_view_column_new ();
4583 gtk_tree_view_column_set_title (column, _("Size"));
4585 renderer = gtk_cell_renderer_text_new ();
4586 gtk_tree_view_column_pack_start (column, renderer, TRUE); /* bug: it doesn't expand */
4587 gtk_tree_view_column_set_cell_data_func (column, renderer,
4588 list_size_data_func, impl, NULL);
4589 gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_SIZE);
4590 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
4593 /* Modification time column */
4595 column = gtk_tree_view_column_new ();
4596 gtk_tree_view_column_set_resizable (column, TRUE);
4597 gtk_tree_view_column_set_title (column, _("Modified"));
4599 renderer = gtk_cell_renderer_text_new ();
4600 gtk_tree_view_column_pack_start (column, renderer, TRUE);
4601 gtk_tree_view_column_set_cell_data_func (column, renderer,
4602 list_mtime_data_func, impl, NULL);
4603 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
4604 impl->list_mtime_column = column;
4606 file_list_set_sort_column_ids (impl);
4608 gtk_widget_show_all (swin);
4614 create_path_bar (GtkFileChooserDefault *impl)
4616 GtkWidget *path_bar;
4618 path_bar = g_object_new (GTK_TYPE_PATH_BAR, NULL);
4619 _gtk_path_bar_set_file_system (GTK_PATH_BAR (path_bar), impl->file_system);
4624 /* Creates the widgets for the files/folders pane */
4626 file_pane_create (GtkFileChooserDefault *impl,
4627 GtkSizeGroup *size_group)
4633 vbox = gtk_vbox_new (FALSE, 6);
4634 gtk_widget_show (vbox);
4636 /* Box for lists and preview */
4638 hbox = gtk_hbox_new (FALSE, PREVIEW_HBOX_SPACING);
4639 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
4640 gtk_widget_show (hbox);
4644 widget = create_file_list (impl);
4645 gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
4649 impl->preview_box = gtk_vbox_new (FALSE, 12);
4650 gtk_box_pack_start (GTK_BOX (hbox), impl->preview_box, FALSE, FALSE, 0);
4651 /* Don't show preview box initially */
4655 impl->filter_combo_hbox = gtk_hbox_new (FALSE, 12);
4657 widget = filter_create (impl);
4659 gtk_widget_show (widget);
4660 gtk_box_pack_end (GTK_BOX (impl->filter_combo_hbox), widget, FALSE, FALSE, 0);
4662 gtk_size_group_add_widget (size_group, impl->filter_combo_hbox);
4663 gtk_box_pack_end (GTK_BOX (vbox), impl->filter_combo_hbox, FALSE, FALSE, 0);
4668 /* Callback used when the "Browse for more folders" expander is toggled */
4670 expander_changed_cb (GtkExpander *expander,
4672 GtkFileChooserDefault *impl)
4674 impl->expand_folders = gtk_expander_get_expanded(GTK_EXPANDER (impl->save_expander));
4675 update_appearance (impl);
4678 /* Callback used when the selection changes in the save folder combo box */
4680 save_folder_combo_changed_cb (GtkComboBox *combo,
4681 GtkFileChooserDefault *impl)
4685 if (impl->changing_folder)
4688 if (gtk_combo_box_get_active_iter (combo, &iter))
4690 GtkTreeIter child_iter;
4692 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model),
4695 shortcuts_activate_iter (impl, &child_iter);
4699 /* Filter function used to filter out the Search item and its separator.
4700 * Used for the "Save in folder" combo box, so that these items do not appear in it.
4703 shortcuts_combo_filter_func (GtkTreeModel *model,
4707 GtkFileChooserDefault *impl;
4708 GtkTreePath *tree_path;
4713 impl = GTK_FILE_CHOOSER_DEFAULT (data);
4715 g_assert (model == GTK_TREE_MODEL (impl->shortcuts_model));
4717 tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), iter);
4718 g_assert (tree_path != NULL);
4720 indices = gtk_tree_path_get_indices (tree_path);
4724 if (impl->has_search)
4726 idx = shortcuts_get_index (impl, SHORTCUTS_SEARCH);
4727 if (idx == indices[0])
4731 if (impl->has_recent)
4733 idx = shortcuts_get_index (impl, SHORTCUTS_RECENT);
4734 if (idx == indices[0])
4738 idx = shortcuts_get_index (impl, SHORTCUTS_RECENT_SEPARATOR);
4739 if (idx == indices[0])
4744 gtk_tree_path_free (tree_path);
4749 /* Creates the combo box with the save folders */
4751 save_folder_combo_create (GtkFileChooserDefault *impl)
4754 GtkCellRenderer *cell;
4756 impl->shortcuts_combo_filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (impl->shortcuts_model), NULL);
4757 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model),
4758 shortcuts_combo_filter_func,
4762 combo = g_object_new (GTK_TYPE_COMBO_BOX,
4763 "model", impl->shortcuts_combo_filter_model,
4764 "focus-on-click", FALSE,
4766 gtk_widget_show (combo);
4768 cell = gtk_cell_renderer_pixbuf_new ();
4769 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE);
4770 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
4771 "pixbuf", SHORTCUTS_COL_PIXBUF,
4772 "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
4773 "sensitive", SHORTCUTS_COL_PIXBUF_VISIBLE,
4776 cell = gtk_cell_renderer_text_new ();
4777 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
4778 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
4779 "text", SHORTCUTS_COL_NAME,
4780 "sensitive", SHORTCUTS_COL_PIXBUF_VISIBLE,
4783 gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo),
4784 shortcuts_row_separator_func,
4787 g_signal_connect (combo, "changed",
4788 G_CALLBACK (save_folder_combo_changed_cb), impl);
4793 /* Creates the widgets specific to Save mode */
4795 save_widgets_create (GtkFileChooserDefault *impl)
4800 GtkWidget *alignment;
4802 if (impl->save_widgets != NULL)
4805 location_switch_to_path_bar (impl);
4807 vbox = gtk_vbox_new (FALSE, 12);
4809 table = gtk_table_new (2, 2, FALSE);
4810 gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
4811 gtk_widget_show (table);
4812 gtk_table_set_row_spacings (GTK_TABLE (table), 12);
4813 gtk_table_set_col_spacings (GTK_TABLE (table), 12);
4817 widget = gtk_label_new_with_mnemonic (_("_Name:"));
4818 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
4819 gtk_table_attach (GTK_TABLE (table), widget,
4823 gtk_widget_show (widget);
4825 /* Location entry */
4827 impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
4828 _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
4830 gtk_entry_set_width_chars (GTK_ENTRY (impl->location_entry), 45);
4831 gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
4832 gtk_table_attach (GTK_TABLE (table), impl->location_entry,
4834 GTK_EXPAND | GTK_FILL, 0,
4836 gtk_widget_show (impl->location_entry);
4837 gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->location_entry);
4840 impl->save_folder_label = gtk_label_new (NULL);
4841 gtk_misc_set_alignment (GTK_MISC (impl->save_folder_label), 0.0, 0.5);
4842 gtk_table_attach (GTK_TABLE (table), impl->save_folder_label,
4846 gtk_widget_show (impl->save_folder_label);
4848 impl->save_folder_combo = save_folder_combo_create (impl);
4849 gtk_table_attach (GTK_TABLE (table), impl->save_folder_combo,
4851 GTK_EXPAND | GTK_FILL, GTK_FILL,
4853 gtk_label_set_mnemonic_widget (GTK_LABEL (impl->save_folder_label), impl->save_folder_combo);
4856 alignment = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
4857 gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
4859 impl->save_expander = gtk_expander_new_with_mnemonic (_("_Browse for other folders"));
4860 gtk_container_add (GTK_CONTAINER (alignment), impl->save_expander);
4861 g_signal_connect (impl->save_expander, "notify::expanded",
4862 G_CALLBACK (expander_changed_cb),
4864 gtk_widget_show_all (alignment);
4866 impl->save_widgets = vbox;
4867 gtk_box_pack_start (GTK_BOX (impl), impl->save_widgets, FALSE, FALSE, 0);
4868 gtk_box_reorder_child (GTK_BOX (impl), impl->save_widgets, 0);
4869 gtk_widget_show (impl->save_widgets);
4872 /* Destroys the widgets specific to Save mode */
4874 save_widgets_destroy (GtkFileChooserDefault *impl)
4876 if (impl->save_widgets == NULL)
4879 gtk_widget_destroy (impl->save_widgets);
4880 impl->save_widgets = NULL;
4881 impl->location_entry = NULL;
4882 impl->save_folder_label = NULL;
4883 impl->save_folder_combo = NULL;
4884 impl->save_expander = NULL;
4887 /* Turns on the path bar widget. Can be called even if we are already in that
4891 location_switch_to_path_bar (GtkFileChooserDefault *impl)
4893 if (impl->location_entry)
4895 gtk_widget_destroy (impl->location_entry);
4896 impl->location_entry = NULL;
4899 gtk_widget_hide (impl->location_entry_box);
4902 /* Sets the full path of the current folder as the text in the location entry. */
4904 location_entry_set_initial_text (GtkFileChooserDefault *impl)
4906 gchar *text, *filename;
4908 if (!impl->current_folder)
4911 filename = g_file_get_path (impl->current_folder);
4915 text = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
4919 text = g_file_get_uri (impl->current_folder);
4923 gboolean need_slash;
4926 len = strlen (text);
4927 need_slash = (text[len - 1] != G_DIR_SEPARATOR);
4933 slash_text = g_new (char, len + 2);
4934 strcpy (slash_text, text);
4935 slash_text[len] = G_DIR_SEPARATOR;
4936 slash_text[len + 1] = 0;
4942 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), text);
4949 /* Turns on the location entry. Can be called even if we are already in that
4953 location_switch_to_filename_entry (GtkFileChooserDefault *impl)
4955 /* when in search or recent files mode, we are not showing the
4956 * location_entry_box container, so there's no point in switching
4959 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
4960 impl->operation_mode == OPERATION_MODE_RECENT)
4963 if (impl->location_entry)
4964 gtk_widget_destroy (impl->location_entry);
4968 gtk_widget_show (impl->location_entry_box);
4972 impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
4973 _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
4975 gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
4976 _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action);
4978 gtk_box_pack_start (GTK_BOX (impl->location_entry_box), impl->location_entry, TRUE, TRUE, 0);
4979 gtk_label_set_mnemonic_widget (GTK_LABEL (impl->location_label), impl->location_entry);
4981 /* Configure the entry */
4983 _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->current_folder);
4987 gtk_widget_show (impl->location_entry);
4988 gtk_widget_grab_focus (impl->location_entry);
4991 /* Sets a new location mode. set_buttons determines whether the toggle button
4992 * for the mode will also be changed.
4995 location_mode_set (GtkFileChooserDefault *impl,
4996 LocationMode new_mode,
4997 gboolean set_button)
4999 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
5000 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5002 GtkWindow *toplevel;
5003 GtkWidget *current_focus;
5004 gboolean button_active;
5005 gboolean switch_to_file_list;
5009 case LOCATION_MODE_PATH_BAR:
5010 button_active = FALSE;
5012 /* The location_entry will disappear when we switch to path bar mode. So,
5013 * we'll focus the file list in that case, to avoid having a window with
5014 * no focused widget.
5016 toplevel = get_toplevel (GTK_WIDGET (impl));
5017 switch_to_file_list = FALSE;
5020 current_focus = gtk_window_get_focus (toplevel);
5021 if (!current_focus || current_focus == impl->location_entry)
5022 switch_to_file_list = TRUE;
5025 location_switch_to_path_bar (impl);
5027 if (switch_to_file_list)
5028 gtk_widget_grab_focus (impl->browse_files_tree_view);
5032 case LOCATION_MODE_FILENAME_ENTRY:
5033 button_active = TRUE;
5034 location_switch_to_filename_entry (impl);
5038 g_assert_not_reached ();
5044 g_signal_handlers_block_by_func (impl->location_button,
5045 G_CALLBACK (location_button_toggled_cb), impl);
5047 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (impl->location_button), button_active);
5049 g_signal_handlers_unblock_by_func (impl->location_button,
5050 G_CALLBACK (location_button_toggled_cb), impl);
5054 impl->location_mode = new_mode;
5058 location_toggle_popup_handler (GtkFileChooserDefault *impl)
5060 /* when in search or recent files mode, we are not showing the
5061 * location_entry_box container, so there's no point in switching
5064 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
5065 impl->operation_mode == OPERATION_MODE_RECENT)
5068 /* If the file entry is not visible, show it.
5069 * If it is visible, turn it off only if it is focused. Otherwise, switch to the entry.
5071 if (impl->location_mode == LOCATION_MODE_PATH_BAR)
5073 location_mode_set (impl, LOCATION_MODE_FILENAME_ENTRY, TRUE);
5075 else if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
5077 if (GTK_WIDGET_HAS_FOCUS (impl->location_entry))
5079 location_mode_set (impl, LOCATION_MODE_PATH_BAR, TRUE);
5083 gtk_widget_grab_focus (impl->location_entry);
5088 /* Callback used when one of the location mode buttons is toggled */
5090 location_button_toggled_cb (GtkToggleButton *toggle,
5091 GtkFileChooserDefault *impl)
5094 LocationMode new_mode;
5096 is_active = gtk_toggle_button_get_active (toggle);
5100 g_assert (impl->location_mode == LOCATION_MODE_PATH_BAR);
5101 new_mode = LOCATION_MODE_FILENAME_ENTRY;
5105 g_assert (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY);
5106 new_mode = LOCATION_MODE_PATH_BAR;
5109 location_mode_set (impl, new_mode, FALSE);
5112 /* Creates a toggle button for the location entry. */
5114 location_button_create (GtkFileChooserDefault *impl)
5119 image = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON);
5120 gtk_widget_show (image);
5122 impl->location_button = g_object_new (GTK_TYPE_TOGGLE_BUTTON,
5126 g_signal_connect (impl->location_button, "toggled",
5127 G_CALLBACK (location_button_toggled_cb), impl);
5129 str = _("Type a file name");
5131 gtk_widget_set_tooltip_text (impl->location_button, str);
5132 atk_object_set_name (gtk_widget_get_accessible (impl->location_button), str);
5135 /* Creates the main hpaned with the widgets shared by Open and Save mode */
5137 browse_widgets_create (GtkFileChooserDefault *impl)
5143 GtkSizeGroup *size_group;
5145 /* size group is used by the [+][-] buttons and the filter combo */
5146 size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
5147 vbox = gtk_vbox_new (FALSE, 12);
5149 /* Location widgets */
5150 hbox = gtk_hbox_new (FALSE, 12);
5151 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
5152 gtk_widget_show (hbox);
5153 impl->browse_path_bar_hbox = hbox;
5155 location_button_create (impl);
5156 gtk_box_pack_start (GTK_BOX (hbox), impl->location_button, FALSE, FALSE, 0);
5160 impl->browse_path_bar = create_path_bar (impl);
5161 g_signal_connect (impl->browse_path_bar, "path-clicked", G_CALLBACK (path_bar_clicked), impl);
5162 gtk_widget_show_all (impl->browse_path_bar);
5163 gtk_box_pack_start (GTK_BOX (hbox), impl->browse_path_bar, TRUE, TRUE, 0);
5166 impl->browse_new_folder_button = gtk_button_new_with_mnemonic (_("Create Fo_lder"));
5167 g_signal_connect (impl->browse_new_folder_button, "clicked",
5168 G_CALLBACK (new_folder_button_clicked), impl);
5169 gtk_box_pack_end (GTK_BOX (hbox), impl->browse_new_folder_button, FALSE, FALSE, 0);
5171 /* Box for the location label and entry */
5173 impl->location_entry_box = gtk_hbox_new (FALSE, 12);
5174 gtk_box_pack_start (GTK_BOX (vbox), impl->location_entry_box, FALSE, FALSE, 0);
5176 impl->location_label = gtk_label_new_with_mnemonic (_("_Location:"));
5177 gtk_widget_show (impl->location_label);
5178 gtk_box_pack_start (GTK_BOX (impl->location_entry_box), impl->location_label, FALSE, FALSE, 0);
5181 hpaned = gtk_hpaned_new ();
5182 gtk_widget_show (hpaned);
5183 gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
5185 widget = shortcuts_pane_create (impl, size_group);
5186 gtk_paned_pack1 (GTK_PANED (hpaned), widget, FALSE, FALSE);
5187 widget = file_pane_create (impl, size_group);
5188 gtk_paned_pack2 (GTK_PANED (hpaned), widget, TRUE, FALSE);
5190 g_object_unref (size_group);
5196 gtk_file_chooser_default_constructor (GType type,
5197 guint n_construct_properties,
5198 GObjectConstructParam *construct_params)
5200 GtkFileChooserDefault *impl;
5203 profile_start ("start", NULL);
5205 object = G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->constructor (type,
5206 n_construct_properties,
5208 impl = GTK_FILE_CHOOSER_DEFAULT (object);
5210 g_assert (impl->file_system);
5212 gtk_widget_push_composite_child ();
5214 /* Shortcuts model */
5215 shortcuts_model_create (impl);
5217 /* The browse widgets */
5218 impl->browse_widgets = browse_widgets_create (impl);
5219 gtk_box_pack_start (GTK_BOX (impl), impl->browse_widgets, TRUE, TRUE, 0);
5221 /* Alignment to hold extra widget */
5222 impl->extra_align = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
5223 gtk_box_pack_start (GTK_BOX (impl), impl->extra_align, FALSE, FALSE, 0);
5225 gtk_widget_pop_composite_child ();
5226 update_appearance (impl);
5228 profile_end ("end", NULL);
5233 /* Sets the extra_widget by packing it in the appropriate place */
5235 set_extra_widget (GtkFileChooserDefault *impl,
5236 GtkWidget *extra_widget)
5240 g_object_ref (extra_widget);
5241 /* FIXME: is this right ? */
5242 gtk_widget_show (extra_widget);
5245 if (impl->extra_widget)
5247 gtk_container_remove (GTK_CONTAINER (impl->extra_align), impl->extra_widget);
5248 g_object_unref (impl->extra_widget);
5251 impl->extra_widget = extra_widget;
5252 if (impl->extra_widget)
5254 gtk_container_add (GTK_CONTAINER (impl->extra_align), impl->extra_widget);
5255 gtk_widget_show (impl->extra_align);
5258 gtk_widget_hide (impl->extra_align);
5262 set_local_only (GtkFileChooserDefault *impl,
5263 gboolean local_only)
5265 if (local_only != impl->local_only)
5267 impl->local_only = local_only;
5269 if (impl->shortcuts_model && impl->file_system)
5271 shortcuts_add_volumes (impl);
5272 shortcuts_add_bookmarks (impl);
5275 if (local_only && !g_file_is_native (impl->current_folder))
5277 /* If we are pointing to a non-local folder, make an effort to change
5278 * back to a local folder, but it's really up to the app to not cause
5279 * such a situation, so we ignore errors.
5281 const gchar *home = g_get_home_dir ();
5287 home_file = g_file_new_for_path (home);
5289 _gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (impl), home_file, NULL);
5291 g_object_unref (home_file);
5297 volumes_bookmarks_changed_cb (GtkFileSystem *file_system,
5298 GtkFileChooserDefault *impl)
5300 shortcuts_add_volumes (impl);
5301 shortcuts_add_bookmarks (impl);
5303 bookmarks_check_add_sensitivity (impl);
5304 bookmarks_check_remove_sensitivity (impl);
5305 shortcuts_check_popup_sensitivity (impl);
5308 /* Sets the file chooser to multiple selection mode */
5310 set_select_multiple (GtkFileChooserDefault *impl,
5311 gboolean select_multiple,
5312 gboolean property_notify)
5314 GtkTreeSelection *selection;
5315 GtkSelectionMode mode;
5317 if (select_multiple == impl->select_multiple)
5320 mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE;
5322 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5323 gtk_tree_selection_set_mode (selection, mode);
5325 gtk_tree_view_set_rubber_banding (GTK_TREE_VIEW (impl->browse_files_tree_view), select_multiple);
5327 impl->select_multiple = select_multiple;
5328 g_object_notify (G_OBJECT (impl), "select-multiple");
5330 check_preview_change (impl);
5334 set_file_system_backend (GtkFileChooserDefault *impl)
5336 profile_start ("start for backend", "default");
5338 impl->file_system = _gtk_file_system_new ();
5340 g_signal_connect (impl->file_system, "volumes-changed",
5341 G_CALLBACK (volumes_bookmarks_changed_cb), impl);
5342 g_signal_connect (impl->file_system, "bookmarks-changed",
5343 G_CALLBACK (volumes_bookmarks_changed_cb), impl);
5345 profile_end ("end", NULL);
5348 /* This function is basically a do_all function.
5350 * It sets the visibility on all the widgets based on the current state, and
5351 * moves the custom_widget if needed.
5354 update_appearance (GtkFileChooserDefault *impl)
5356 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
5357 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5361 gtk_widget_hide (impl->location_button);
5362 save_widgets_create (impl);
5364 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
5365 text = _("Save in _folder:");
5367 text = _("Create in _folder:");
5369 gtk_label_set_text_with_mnemonic (GTK_LABEL (impl->save_folder_label), text);
5371 if (gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
5373 gtk_widget_set_sensitive (impl->save_folder_label, FALSE);
5374 gtk_widget_set_sensitive (impl->save_folder_combo, FALSE);
5375 gtk_widget_show (impl->browse_widgets);
5379 gtk_widget_set_sensitive (impl->save_folder_label, TRUE);
5380 gtk_widget_set_sensitive (impl->save_folder_combo, TRUE);
5381 gtk_widget_hide (impl->browse_widgets);
5384 gtk_widget_show (impl->browse_new_folder_button);
5386 if (impl->select_multiple)
5388 g_warning ("Save mode cannot be set in conjunction with multiple selection mode. "
5389 "Re-setting to single selection mode.");
5390 set_select_multiple (impl, FALSE, TRUE);
5393 else if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
5394 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5396 gtk_widget_show (impl->location_button);
5397 save_widgets_destroy (impl);
5398 gtk_widget_show (impl->browse_widgets);
5399 location_mode_set (impl, impl->location_mode, TRUE);
5402 if (impl->location_entry)
5403 _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action);
5405 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
5406 gtk_widget_hide (impl->browse_new_folder_button);
5408 gtk_widget_show (impl->browse_new_folder_button);
5410 /* This *is* needed; we need to redraw the file list because the "sensitivity"
5411 * of files may change depending whether we are in a file or folder-only mode.
5413 gtk_widget_queue_draw (impl->browse_files_tree_view);
5415 g_signal_emit_by_name (impl, "default-size-changed");
5419 gtk_file_chooser_default_set_property (GObject *object,
5421 const GValue *value,
5425 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
5429 case GTK_FILE_CHOOSER_PROP_ACTION:
5431 GtkFileChooserAction action = g_value_get_enum (value);
5433 if (action != impl->action)
5435 gtk_file_chooser_default_unselect_all (GTK_FILE_CHOOSER (impl));
5437 if ((action == GTK_FILE_CHOOSER_ACTION_SAVE ||
5438 action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5439 && impl->select_multiple)
5441 g_warning ("Tried to change the file chooser action to SAVE or CREATE_FOLDER, but "
5442 "this is not allowed in multiple selection mode. Resetting the file chooser "
5443 "to single selection mode.");
5444 set_select_multiple (impl, FALSE, TRUE);
5446 impl->action = action;
5447 update_appearance (impl);
5448 settings_load (impl);
5453 case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
5454 /* Ignore property */
5457 case GTK_FILE_CHOOSER_PROP_FILTER:
5458 set_current_filter (impl, g_value_get_object (value));
5461 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
5462 set_local_only (impl, g_value_get_boolean (value));
5465 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
5466 set_preview_widget (impl, g_value_get_object (value));
5469 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
5470 impl->preview_widget_active = g_value_get_boolean (value);
5471 update_preview_widget_visibility (impl);
5474 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
5475 impl->use_preview_label = g_value_get_boolean (value);
5476 update_preview_widget_visibility (impl);
5479 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
5480 set_extra_widget (impl, g_value_get_object (value));
5483 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
5485 gboolean select_multiple = g_value_get_boolean (value);
5486 if ((impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
5487 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5490 g_warning ("Tried to set the file chooser to multiple selection mode, but this is "
5491 "not allowed in SAVE or CREATE_FOLDER modes. Ignoring the change and "
5492 "leaving the file chooser in single selection mode.");
5496 set_select_multiple (impl, select_multiple, FALSE);
5500 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
5502 gboolean show_hidden = g_value_get_boolean (value);
5503 if (show_hidden != impl->show_hidden)
5505 impl->show_hidden = show_hidden;
5507 if (impl->browse_files_model)
5508 _gtk_file_system_model_set_show_hidden (impl->browse_files_model, show_hidden);
5513 case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
5515 gboolean do_overwrite_confirmation = g_value_get_boolean (value);
5516 impl->do_overwrite_confirmation = do_overwrite_confirmation;
5521 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5527 gtk_file_chooser_default_get_property (GObject *object,
5532 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
5536 case GTK_FILE_CHOOSER_PROP_ACTION:
5537 g_value_set_enum (value, impl->action);
5540 case GTK_FILE_CHOOSER_PROP_FILTER:
5541 g_value_set_object (value, impl->current_filter);
5544 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
5545 g_value_set_boolean (value, impl->local_only);
5548 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
5549 g_value_set_object (value, impl->preview_widget);
5552 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
5553 g_value_set_boolean (value, impl->preview_widget_active);
5556 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
5557 g_value_set_boolean (value, impl->use_preview_label);
5560 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
5561 g_value_set_object (value, impl->extra_widget);
5564 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
5565 g_value_set_boolean (value, impl->select_multiple);
5568 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
5569 g_value_set_boolean (value, impl->show_hidden);
5572 case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
5573 g_value_set_boolean (value, impl->do_overwrite_confirmation);
5577 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5582 /* Removes the settings signal handler. It's safe to call multiple times */
5584 remove_settings_signal (GtkFileChooserDefault *impl,
5587 if (impl->settings_signal_id)
5589 GtkSettings *settings;
5591 settings = gtk_settings_get_for_screen (screen);
5592 g_signal_handler_disconnect (settings,
5593 impl->settings_signal_id);
5594 impl->settings_signal_id = 0;
5599 gtk_file_chooser_default_dispose (GObject *object)
5602 GtkFileChooserDefault *impl = (GtkFileChooserDefault *) object;
5604 if (impl->extra_widget)
5606 g_object_unref (impl->extra_widget);
5607 impl->extra_widget = NULL;
5610 pending_select_files_free (impl);
5612 /* cancel all pending operations */
5613 if (impl->pending_cancellables)
5615 for (l = impl->pending_cancellables; l; l = l->next)
5617 GCancellable *cancellable = G_CANCELLABLE (l->data);
5618 g_cancellable_cancel (cancellable);
5620 g_slist_free (impl->pending_cancellables);
5621 impl->pending_cancellables = NULL;
5624 if (impl->reload_icon_cancellables)
5626 for (l = impl->reload_icon_cancellables; l; l = l->next)
5628 GCancellable *cancellable = G_CANCELLABLE (l->data);
5629 g_cancellable_cancel (cancellable);
5631 g_slist_free (impl->reload_icon_cancellables);
5632 impl->reload_icon_cancellables = NULL;
5635 if (impl->loading_shortcuts)
5637 for (l = impl->loading_shortcuts; l; l = l->next)
5639 GCancellable *cancellable = G_CANCELLABLE (l->data);
5640 g_cancellable_cancel (cancellable);
5642 g_slist_free (impl->loading_shortcuts);
5643 impl->loading_shortcuts = NULL;
5646 if (impl->file_list_drag_data_received_cancellable)
5648 g_cancellable_cancel (impl->file_list_drag_data_received_cancellable);
5649 impl->file_list_drag_data_received_cancellable = NULL;
5652 if (impl->update_current_folder_cancellable)
5654 g_cancellable_cancel (impl->update_current_folder_cancellable);
5655 impl->update_current_folder_cancellable = NULL;
5658 if (impl->show_and_select_files_cancellable)
5660 g_cancellable_cancel (impl->show_and_select_files_cancellable);
5661 impl->show_and_select_files_cancellable = NULL;
5664 if (impl->should_respond_get_info_cancellable)
5666 g_cancellable_cancel (impl->should_respond_get_info_cancellable);
5667 impl->should_respond_get_info_cancellable = NULL;
5670 if (impl->update_from_entry_cancellable)
5672 g_cancellable_cancel (impl->update_from_entry_cancellable);
5673 impl->update_from_entry_cancellable = NULL;
5676 if (impl->shortcuts_activate_iter_cancellable)
5678 g_cancellable_cancel (impl->shortcuts_activate_iter_cancellable);
5679 impl->shortcuts_activate_iter_cancellable = NULL;
5682 search_stop_searching (impl, TRUE);
5683 recent_stop_loading (impl);
5685 remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl)));
5687 G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->dispose (object);
5690 /* We override show-all since we have internal widgets that
5691 * shouldn't be shown when you call show_all(), like the filter
5695 gtk_file_chooser_default_show_all (GtkWidget *widget)
5697 GtkFileChooserDefault *impl = (GtkFileChooserDefault *) widget;
5699 gtk_widget_show (widget);
5701 if (impl->extra_widget)
5702 gtk_widget_show_all (impl->extra_widget);
5705 /* Handler for GtkWindow::set-focus; this is where we save the last-focused
5706 * widget on our toplevel. See gtk_file_chooser_default_hierarchy_changed()
5709 toplevel_set_focus_cb (GtkWindow *window,
5711 GtkFileChooserDefault *impl)
5713 impl->toplevel_last_focus_widget = gtk_window_get_focus (window);
5716 /* We monitor the focus widget on our toplevel to be able to know which widget
5717 * was last focused at the time our "should_respond" method gets called.
5720 gtk_file_chooser_default_hierarchy_changed (GtkWidget *widget,
5721 GtkWidget *previous_toplevel)
5723 GtkFileChooserDefault *impl;
5724 GtkWidget *toplevel;
5726 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5728 if (previous_toplevel)
5730 g_assert (impl->toplevel_set_focus_id != 0);
5731 g_signal_handler_disconnect (previous_toplevel, impl->toplevel_set_focus_id);
5732 impl->toplevel_set_focus_id = 0;
5733 impl->toplevel_last_focus_widget = NULL;
5736 g_assert (impl->toplevel_set_focus_id == 0);
5738 toplevel = gtk_widget_get_toplevel (widget);
5739 if (GTK_IS_WINDOW (toplevel))
5741 impl->toplevel_set_focus_id = g_signal_connect (toplevel, "set_focus",
5742 G_CALLBACK (toplevel_set_focus_cb), impl);
5743 impl->toplevel_last_focus_widget = gtk_window_get_focus (GTK_WINDOW (toplevel));
5747 /* Changes the icons wherever it is needed */
5749 change_icon_theme (GtkFileChooserDefault *impl)
5751 GtkSettings *settings;
5754 profile_start ("start", NULL);
5756 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
5758 if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU, &width, &height))
5759 impl->icon_size = MAX (width, height);
5761 impl->icon_size = FALLBACK_ICON_SIZE;
5763 shortcuts_reload_icons (impl);
5764 gtk_widget_queue_resize (impl->browse_files_tree_view);
5766 profile_end ("end", NULL);
5769 /* Callback used when a GtkSettings value changes */
5771 settings_notify_cb (GObject *object,
5773 GtkFileChooserDefault *impl)
5777 profile_start ("start", NULL);
5779 name = g_param_spec_get_name (pspec);
5781 if (strcmp (name, "gtk-icon-theme-name") == 0 ||
5782 strcmp (name, "gtk-icon-sizes") == 0)
5783 change_icon_theme (impl);
5785 profile_end ("end", NULL);
5788 /* Installs a signal handler for GtkSettings so that we can monitor changes in
5792 check_icon_theme (GtkFileChooserDefault *impl)
5794 GtkSettings *settings;
5796 profile_start ("start", NULL);
5798 if (impl->settings_signal_id)
5800 profile_end ("end", NULL);
5804 if (gtk_widget_has_screen (GTK_WIDGET (impl)))
5806 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
5807 impl->settings_signal_id = g_signal_connect (settings, "notify",
5808 G_CALLBACK (settings_notify_cb), impl);
5810 change_icon_theme (impl);
5813 profile_end ("end", NULL);
5817 gtk_file_chooser_default_style_set (GtkWidget *widget,
5818 GtkStyle *previous_style)
5820 GtkFileChooserDefault *impl;
5822 profile_start ("start", NULL);
5824 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5826 profile_msg (" parent class style_set start", NULL);
5827 if (GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->style_set)
5828 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->style_set (widget, previous_style);
5829 profile_msg (" parent class style_set end", NULL);
5831 if (gtk_widget_has_screen (GTK_WIDGET (impl)))
5832 change_icon_theme (impl);
5834 profile_msg (" emit default-size-changed start", NULL);
5835 g_signal_emit_by_name (widget, "default-size-changed");
5836 profile_msg (" emit default-size-changed end", NULL);
5838 profile_end ("end", NULL);
5842 gtk_file_chooser_default_screen_changed (GtkWidget *widget,
5843 GdkScreen *previous_screen)
5845 GtkFileChooserDefault *impl;
5847 profile_start ("start", NULL);
5849 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5851 if (GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->screen_changed)
5852 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->screen_changed (widget, previous_screen);
5854 remove_settings_signal (impl, previous_screen);
5855 check_icon_theme (impl);
5857 g_signal_emit_by_name (widget, "default-size-changed");
5859 profile_end ("end", NULL);
5863 gtk_file_chooser_default_size_allocate (GtkWidget *widget,
5864 GtkAllocation *allocation)
5866 GtkFileChooserDefault *impl;
5868 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5870 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->size_allocate (widget, allocation);
5872 impl->default_width = allocation->width;
5873 impl->default_height = allocation->height;
5875 if (impl->preview_widget_active &&
5876 impl->preview_widget &&
5877 GTK_WIDGET_DRAWABLE (impl->preview_widget))
5878 impl->default_width -= impl->preview_widget->allocation.width + PREVIEW_HBOX_SPACING;
5880 if (impl->extra_widget &&
5881 GTK_WIDGET_DRAWABLE (impl->extra_widget))
5882 impl->default_height -= GTK_BOX (widget)->spacing + impl->extra_widget->allocation.height;
5886 get_is_file_filtered (GtkFileChooserDefault *impl,
5888 GFileInfo *file_info)
5890 GtkFileFilterInfo filter_info;
5891 GtkFileFilterFlags needed;
5894 if (!impl->current_filter)
5897 filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
5899 needed = gtk_file_filter_get_needed (impl->current_filter);
5901 filter_info.display_name = g_file_info_get_display_name (file_info);
5902 filter_info.mime_type = g_file_info_get_content_type (file_info);
5904 if (needed & GTK_FILE_FILTER_FILENAME)
5906 filter_info.filename = g_file_get_path (file);
5907 if (filter_info.filename)
5908 filter_info.contains |= GTK_FILE_FILTER_FILENAME;
5911 filter_info.filename = NULL;
5913 if (needed & GTK_FILE_FILTER_URI)
5915 filter_info.uri = g_file_get_uri (file);
5916 if (filter_info.uri)
5917 filter_info.contains |= GTK_FILE_FILTER_URI;
5920 filter_info.uri = NULL;
5922 result = gtk_file_filter_filter (impl->current_filter, &filter_info);
5924 if (filter_info.filename)
5925 g_free ((gchar *)filter_info.filename);
5926 if (filter_info.uri)
5927 g_free ((gchar *)filter_info.uri);
5933 settings_load (GtkFileChooserDefault *impl)
5935 GtkFileChooserSettings *settings;
5936 LocationMode location_mode;
5937 gboolean show_hidden;
5938 gboolean expand_folders;
5940 settings = _gtk_file_chooser_settings_new ();
5942 location_mode = _gtk_file_chooser_settings_get_location_mode (settings);
5943 show_hidden = _gtk_file_chooser_settings_get_show_hidden (settings);
5944 expand_folders = _gtk_file_chooser_settings_get_expand_folders (settings);
5946 g_object_unref (settings);
5948 location_mode_set (impl, location_mode, TRUE);
5949 gtk_file_chooser_set_show_hidden (GTK_FILE_CHOOSER (impl), show_hidden);
5950 impl->expand_folders = expand_folders;
5951 if (impl->save_expander)
5952 gtk_expander_set_expanded (GTK_EXPANDER (impl->save_expander), expand_folders);
5956 settings_save (GtkFileChooserDefault *impl)
5958 GtkFileChooserSettings *settings;
5960 settings = _gtk_file_chooser_settings_new ();
5962 _gtk_file_chooser_settings_set_location_mode (settings, impl->location_mode);
5963 _gtk_file_chooser_settings_set_show_hidden (settings, gtk_file_chooser_get_show_hidden (GTK_FILE_CHOOSER (impl)));
5964 _gtk_file_chooser_settings_set_expand_folders (settings, impl->expand_folders);
5967 _gtk_file_chooser_settings_save (settings, NULL);
5969 g_object_unref (settings);
5972 /* GtkWidget::map method */
5974 gtk_file_chooser_default_map (GtkWidget *widget)
5976 GtkFileChooserDefault *impl;
5977 char *current_working_dir;
5979 profile_start ("start", NULL);
5981 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5983 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->map (widget);
5985 if (impl->operation_mode == OPERATION_MODE_BROWSE)
5987 switch (impl->reload_state)
5990 /* The user didn't explicitly give us a folder to
5991 * display, so we'll use the cwd
5993 current_working_dir = g_get_current_dir ();
5994 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (impl),
5995 current_working_dir);
5996 g_free (current_working_dir);
5999 case RELOAD_HAS_FOLDER:
6000 /* Nothing; we are already loading or loaded, so we
6001 * don't need to reload
6005 case RELOAD_WAS_UNMAPPED:
6006 /* Just reload the current folder; else continue
6009 if (impl->current_folder)
6011 pending_select_files_store_selection (impl);
6012 change_folder_and_display_error (impl, impl->current_folder, FALSE);
6017 g_assert_not_reached ();
6021 volumes_bookmarks_changed_cb (impl->file_system, impl);
6023 settings_load (impl);
6025 profile_end ("end", NULL);
6028 /* GtkWidget::unmap method */
6030 gtk_file_chooser_default_unmap (GtkWidget *widget)
6032 GtkFileChooserDefault *impl;
6034 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
6036 settings_save (impl);
6038 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->unmap (widget);
6040 impl->reload_state = RELOAD_WAS_UNMAPPED;
6044 list_model_filter_func (GtkFileSystemModel *model,
6046 GFileInfo *file_info,
6049 GtkFileChooserDefault *impl = user_data;
6051 if (!impl->current_filter)
6054 if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY)
6057 return !get_is_file_filtered (impl, file, file_info);
6061 install_list_model_filter (GtkFileChooserDefault *impl)
6063 GtkFileSystemModelFilter filter;
6066 g_assert (impl->browse_files_model != NULL);
6068 if (impl->current_filter)
6070 filter = list_model_filter_func;
6079 _gtk_file_system_model_set_filter (impl->browse_files_model,
6084 #define COMPARE_DIRECTORIES \
6085 GtkFileChooserDefault *impl = user_data; \
6086 GFileInfo *info_a = _gtk_file_system_model_get_info (impl->browse_files_model, a); \
6087 GFileInfo *info_b = _gtk_file_system_model_get_info (impl->browse_files_model, b); \
6088 gboolean dir_a, dir_b; \
6091 dir_a = (g_file_info_get_file_type (info_a) == G_FILE_TYPE_DIRECTORY); \
6093 return impl->list_sort_ascending ? -1 : 1; \
6096 dir_b = (g_file_info_get_file_type (info_b) == G_FILE_TYPE_DIRECTORY); \
6098 return impl->list_sort_ascending ? 1 : -1; \
6100 if (dir_a != dir_b) \
6101 return impl->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */
6103 /* Sort callback for the filename column */
6105 name_sort_func (GtkTreeModel *model,
6110 COMPARE_DIRECTORIES;
6113 gchar *key_a, *key_b;
6116 key_a = g_utf8_collate_key_for_filename (g_file_info_get_display_name (info_a), -1);
6117 key_b = g_utf8_collate_key_for_filename (g_file_info_get_display_name (info_b), -1);
6118 result = strcmp (key_a, key_b);
6127 /* Sort callback for the size column */
6129 size_sort_func (GtkTreeModel *model,
6134 COMPARE_DIRECTORIES;
6137 goffset size_a = g_file_info_get_size (info_a);
6138 goffset size_b = g_file_info_get_size (info_b);
6140 return size_a > size_b ? -1 : (size_a == size_b ? 0 : 1);
6144 /* Sort callback for the mtime column */
6146 mtime_sort_func (GtkTreeModel *model,
6151 COMPARE_DIRECTORIES;
6156 g_file_info_get_modification_time (info_a, &ta);
6157 g_file_info_get_modification_time (info_b, &tb);
6159 return ta.tv_sec > tb.tv_sec ? -1 : (ta.tv_sec == tb.tv_sec ? 0 : 1);
6163 /* Callback used when the sort column changes. We cache the sort order for use
6164 * in name_sort_func().
6167 list_sort_column_changed_cb (GtkTreeSortable *sortable,
6168 GtkFileChooserDefault *impl)
6170 GtkSortType sort_type;
6172 if (gtk_tree_sortable_get_sort_column_id (sortable, NULL, &sort_type))
6173 impl->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
6177 set_busy_cursor (GtkFileChooserDefault *impl,
6180 GtkWindow *toplevel;
6181 GdkDisplay *display;
6184 toplevel = get_toplevel (GTK_WIDGET (impl));
6185 if (!toplevel || !GTK_WIDGET_REALIZED (toplevel))
6188 display = gtk_widget_get_display (GTK_WIDGET (toplevel));
6191 cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
6195 gdk_window_set_cursor (GTK_WIDGET (toplevel)->window, cursor);
6196 gdk_display_flush (display);
6199 gdk_cursor_unref (cursor);
6202 /* Creates a sort model to wrap the file system model and sets it on the tree view */
6204 load_set_model (GtkFileChooserDefault *impl)
6206 profile_start ("start", NULL);
6208 g_assert (impl->browse_files_model != NULL);
6209 g_assert (impl->sort_model == NULL);
6211 profile_msg (" gtk_tree_model_sort_new_with_model start", NULL);
6212 impl->sort_model = (GtkTreeModelSort *)gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (impl->browse_files_model));
6213 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, name_sort_func, impl, NULL);
6214 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_SIZE, size_sort_func, impl, NULL);
6215 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_MTIME, mtime_sort_func, impl, NULL);
6216 gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->sort_model), NULL, NULL, NULL);
6217 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, GTK_SORT_ASCENDING);
6218 impl->list_sort_ascending = TRUE;
6219 profile_msg (" gtk_tree_model_sort_new_with_model end", NULL);
6221 g_signal_connect (impl->sort_model, "sort_column_changed",
6222 G_CALLBACK (list_sort_column_changed_cb), impl);
6224 profile_msg (" gtk_tree_view_set_model start", NULL);
6225 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
6226 GTK_TREE_MODEL (impl->sort_model));
6227 gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->browse_files_tree_view));
6228 gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_files_tree_view),
6229 GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
6230 profile_msg (" gtk_tree_view_set_model end", NULL);
6232 profile_end ("end", NULL);
6235 /* Timeout callback used when the loading timer expires */
6237 load_timeout_cb (gpointer data)
6239 GtkFileChooserDefault *impl;
6241 profile_start ("start", NULL);
6243 impl = GTK_FILE_CHOOSER_DEFAULT (data);
6244 g_assert (impl->load_state == LOAD_PRELOAD);
6245 g_assert (impl->load_timeout_id != 0);
6246 g_assert (impl->browse_files_model != NULL);
6248 impl->load_timeout_id = 0;
6249 impl->load_state = LOAD_LOADING;
6251 load_set_model (impl);
6253 profile_end ("end", NULL);
6258 /* Sets up a new load timer for the model and switches to the LOAD_PRELOAD state */
6260 load_setup_timer (GtkFileChooserDefault *impl)
6262 g_assert (impl->load_timeout_id == 0);
6263 g_assert (impl->load_state != LOAD_PRELOAD);
6265 impl->load_timeout_id = gdk_threads_add_timeout (MAX_LOADING_TIME, load_timeout_cb, impl);
6266 impl->load_state = LOAD_PRELOAD;
6269 /* Removes the load timeout and switches to the LOAD_FINISHED state */
6271 load_remove_timer (GtkFileChooserDefault *impl)
6273 if (impl->load_timeout_id != 0)
6275 g_assert (impl->load_state == LOAD_PRELOAD);
6277 g_source_remove (impl->load_timeout_id);
6278 impl->load_timeout_id = 0;
6279 impl->load_state = LOAD_EMPTY;
6282 g_assert (impl->load_state == LOAD_EMPTY ||
6283 impl->load_state == LOAD_LOADING ||
6284 impl->load_state == LOAD_FINISHED);
6287 /* Selects the first row in the file list */
6289 browse_files_select_first_row (GtkFileChooserDefault *impl)
6292 GtkTreeIter dummy_iter;
6293 GtkTreeModel *tree_model;
6295 if (!impl->sort_model)
6298 path = gtk_tree_path_new_from_indices (0, -1);
6299 tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view));
6301 /* If the list is empty, do nothing. */
6302 if (gtk_tree_model_get_iter (tree_model, &dummy_iter, path))
6303 gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), path, NULL, FALSE);
6305 gtk_tree_path_free (path);
6308 struct center_selected_row_closure {
6309 GtkFileChooserDefault *impl;
6310 gboolean already_centered;
6313 /* Callback used from gtk_tree_selection_selected_foreach(); centers the
6314 * selected row in the tree view.
6317 center_selected_row_foreach_cb (GtkTreeModel *model,
6322 struct center_selected_row_closure *closure;
6325 if (closure->already_centered)
6328 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (closure->impl->browse_files_tree_view), path, NULL, TRUE, 0.5, 0.0);
6329 closure->already_centered = TRUE;
6332 /* Centers the selected row in the tree view */
6334 browse_files_center_selected_row (GtkFileChooserDefault *impl)
6336 struct center_selected_row_closure closure;
6337 GtkTreeSelection *selection;
6339 closure.impl = impl;
6340 closure.already_centered = FALSE;
6342 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6343 gtk_tree_selection_selected_foreach (selection, center_selected_row_foreach_cb, &closure);
6346 struct ShowAndSelectPathsData
6348 GtkFileChooserDefault *impl;
6353 show_and_select_files_finished_loading (GtkFolder *folder,
6356 gboolean have_hidden;
6357 gboolean have_filtered;
6359 struct ShowAndSelectPathsData *data = user_data;
6361 have_hidden = FALSE;
6362 have_filtered = FALSE;
6364 for (l = data->files; l; l = l->next)
6371 info = _gtk_folder_get_info (folder, file);
6375 have_hidden = g_file_info_get_is_hidden (info);
6378 have_filtered = (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY) &&
6379 get_is_file_filtered (data->impl, file, info);
6381 g_object_unref (info);
6383 if (have_hidden && have_filtered)
6384 break; /* we now have all the information we need */
6388 g_signal_handlers_disconnect_by_func (folder,
6389 show_and_select_files_finished_loading,
6393 g_object_set (data->impl, "show-hidden", TRUE, NULL);
6396 set_current_filter (data->impl, NULL);
6398 for (l = data->files; l; l = l->next)
6403 _gtk_file_system_model_path_do (data->impl->browse_files_model, file,
6404 select_func, data->impl);
6407 browse_files_center_selected_row (data->impl);
6409 g_object_unref (data->impl);
6410 g_slist_foreach (data->files, (GFunc) g_object_unref, NULL);
6411 g_slist_free (data->files);
6416 show_and_select_files_get_folder_cb (GCancellable *cancellable,
6418 const GError *error,
6421 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
6422 struct ShowAndSelectPathsData *data = user_data;
6424 if (data->impl->show_and_select_files_cancellable != cancellable)
6427 data->impl->show_and_select_files_cancellable = NULL;
6429 if (cancelled || error)
6432 g_object_unref (cancellable);
6434 if (_gtk_folder_is_finished_loading (folder))
6435 show_and_select_files_finished_loading (folder, user_data);
6437 g_signal_connect (folder, "finished-loading",
6438 G_CALLBACK (show_and_select_files_finished_loading),
6444 g_object_unref (data->impl);
6445 g_slist_foreach (data->files, (GFunc) g_object_unref, NULL);
6446 g_slist_free (data->files);
6449 g_object_unref (cancellable);
6453 show_and_select_files (GtkFileChooserDefault *impl,
6458 struct ShowAndSelectPathsData *info;
6460 profile_start ("start", NULL);
6464 profile_end ("end", NULL);
6468 info = g_new (struct ShowAndSelectPathsData, 1);
6469 info->impl = g_object_ref (impl);
6470 info->files = g_slist_copy (files);
6471 g_slist_foreach (info->files, (GFunc) g_object_ref, NULL);
6473 if (impl->show_and_select_files_cancellable)
6474 g_cancellable_cancel (impl->show_and_select_files_cancellable);
6476 impl->show_and_select_files_cancellable =
6477 _gtk_file_system_get_folder (impl->file_system, parent_file,
6478 "standard::is-hidden,standard::type,standard::name",
6479 show_and_select_files_get_folder_cb, info);
6481 profile_end ("end", NULL);
6485 /* Processes the pending operation when a folder is finished loading */
6487 pending_select_files_process (GtkFileChooserDefault *impl)
6489 g_assert (impl->load_state == LOAD_FINISHED);
6490 g_assert (impl->browse_files_model != NULL);
6491 g_assert (impl->sort_model != NULL);
6493 if (impl->pending_select_files)
6496 show_and_select_files (impl, impl->current_folder, impl->pending_select_files, NULL);
6497 pending_select_files_free (impl);
6498 browse_files_center_selected_row (impl);
6502 /* We only select the first row if the chooser is actually mapped ---
6503 * selecting the first row is to help the user when he is interacting with
6504 * the chooser, but sometimes a chooser works not on behalf of the user,
6505 * but rather on behalf of something else like GtkFileChooserButton. In
6506 * that case, the chooser's selection should be what the caller expects,
6507 * as the user can't see that something else got selected. See bug #165264.
6509 if (GTK_WIDGET_MAPPED (impl) && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
6510 browse_files_select_first_row (impl);
6513 g_assert (impl->pending_select_files == NULL);
6516 /* Callback used when the file system model finishes loading */
6518 browse_files_model_finished_loading_cb (GtkFileSystemModel *model,
6519 GtkFileChooserDefault *impl)
6521 profile_start ("start", NULL);
6523 if (impl->load_state == LOAD_PRELOAD)
6525 load_remove_timer (impl);
6526 load_set_model (impl);
6528 else if (impl->load_state == LOAD_LOADING)
6534 /* We can't g_assert_not_reached(), as something other than us may have
6535 * initiated a folder reload. See #165556.
6537 profile_end ("end", NULL);
6541 g_assert (impl->load_timeout_id == 0);
6543 impl->load_state = LOAD_FINISHED;
6545 pending_select_files_process (impl);
6546 set_busy_cursor (impl, FALSE);
6547 #ifdef PROFILE_FILE_CHOOSER
6548 access ("MARK: *** FINISHED LOADING", F_OK);
6551 profile_end ("end", NULL);
6555 stop_loading_and_clear_list_model (GtkFileChooserDefault *impl)
6557 load_remove_timer (impl); /* This changes the state to LOAD_EMPTY */
6559 if (impl->browse_files_model)
6561 g_object_unref (impl->browse_files_model);
6562 impl->browse_files_model = NULL;
6565 if (impl->sort_model)
6567 g_object_unref (impl->sort_model);
6568 impl->sort_model = NULL;
6571 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
6574 /* Gets rid of the old list model and creates a new one for the current folder */
6576 set_list_model (GtkFileChooserDefault *impl,
6579 g_assert (impl->current_folder != NULL);
6581 profile_start ("start", NULL);
6583 stop_loading_and_clear_list_model (impl);
6585 set_busy_cursor (impl, TRUE);
6586 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
6588 impl->browse_files_model = _gtk_file_system_model_new (impl->file_system,
6589 impl->current_folder, 0,
6590 "standard,time,thumbnail::*",
6592 if (!impl->browse_files_model)
6594 set_busy_cursor (impl, FALSE);
6595 profile_end ("end", NULL);
6599 load_setup_timer (impl); /* This changes the state to LOAD_PRELOAD */
6601 g_signal_connect (impl->browse_files_model, "finished-loading",
6602 G_CALLBACK (browse_files_model_finished_loading_cb), impl);
6604 _gtk_file_system_model_set_show_hidden (impl->browse_files_model, impl->show_hidden);
6606 install_list_model_filter (impl);
6608 profile_end ("end", NULL);
6613 struct update_chooser_entry_selected_foreach_closure {
6615 GtkTreeIter first_selected_iter;
6619 compare_utf8_filenames (const gchar *a,
6622 gchar *a_folded, *b_folded;
6625 a_folded = g_utf8_strdown (a, -1);
6626 b_folded = g_utf8_strdown (b, -1);
6628 retval = strcmp (a_folded, b_folded);
6637 update_chooser_entry_selected_foreach (GtkTreeModel *model,
6642 struct update_chooser_entry_selected_foreach_closure *closure;
6645 closure->num_selected++;
6647 if (closure->num_selected == 1)
6648 closure->first_selected_iter = *iter;
6652 update_chooser_entry (GtkFileChooserDefault *impl)
6654 GtkTreeSelection *selection;
6655 struct update_chooser_entry_selected_foreach_closure closure;
6656 const char *file_part;
6658 /* no need to update the file chooser's entry if there's no entry */
6659 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
6660 impl->operation_mode == OPERATION_MODE_RECENT ||
6661 !impl->location_entry)
6664 if (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6665 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
6666 || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
6667 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
6668 && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)))
6671 g_assert (impl->location_entry != NULL);
6673 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6674 closure.num_selected = 0;
6675 gtk_tree_selection_selected_foreach (selection, update_chooser_entry_selected_foreach, &closure);
6679 if (closure.num_selected == 0)
6681 goto maybe_clear_entry;
6683 else if (closure.num_selected == 1)
6685 GtkTreeIter child_iter;
6687 if (impl->operation_mode == OPERATION_MODE_BROWSE)
6690 gboolean change_entry;
6692 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
6694 &closure.first_selected_iter);
6696 info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
6698 /* If the cursor moved to the row of the newly created folder,
6699 * retrieving info will return NULL.
6704 g_free (impl->browse_files_last_selected_name);
6705 impl->browse_files_last_selected_name =
6706 g_strdup (g_file_info_get_display_name (info));
6708 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
6709 impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
6710 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6712 /* We don't want the name to change when clicking on a folder... */
6713 change_entry = (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY);
6716 change_entry = TRUE; /* ... unless we are in SELECT_FOLDER mode */
6720 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->browse_files_last_selected_name);
6722 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
6723 _gtk_file_chooser_entry_select_filename (GTK_FILE_CHOOSER_ENTRY (impl->location_entry));
6731 g_assert (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
6732 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER));
6734 /* Multiple selection, so just clear the entry. */
6736 g_free (impl->browse_files_last_selected_name);
6737 impl->browse_files_last_selected_name = NULL;
6739 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
6745 if ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
6746 && impl->browse_files_last_selected_name)
6748 const char *entry_text;
6750 gboolean clear_entry;
6752 entry_text = gtk_entry_get_text (GTK_ENTRY (impl->location_entry));
6753 len = strlen (entry_text);
6756 /* The file chooser entry may have appended a "/" to its text. So
6757 * take it out, and compare the result to the old selection.
6759 if (entry_text[len - 1] == G_DIR_SEPARATOR)
6763 tmp = g_strndup (entry_text, len - 1);
6764 clear_entry = (compare_utf8_filenames (impl->browse_files_last_selected_name, tmp) == 0);
6768 clear_entry = (compare_utf8_filenames (impl->browse_files_last_selected_name, entry_text) == 0);
6771 clear_entry = FALSE;
6774 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
6779 gtk_file_chooser_default_set_current_folder (GtkFileChooser *chooser,
6783 return gtk_file_chooser_default_update_current_folder (chooser, file, FALSE, FALSE, error);
6787 struct UpdateCurrentFolderData
6789 GtkFileChooserDefault *impl;
6791 gboolean keep_trail;
6792 gboolean clear_entry;
6793 GFile *original_file;
6794 GError *original_error;
6798 update_current_folder_get_info_cb (GCancellable *cancellable,
6800 const GError *error,
6803 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
6804 struct UpdateCurrentFolderData *data = user_data;
6805 GtkFileChooserDefault *impl = data->impl;
6807 if (cancellable != impl->update_current_folder_cancellable)
6810 impl->update_current_folder_cancellable = NULL;
6811 impl->reload_state = RELOAD_EMPTY;
6813 set_busy_cursor (impl, FALSE);
6822 if (!data->original_file)
6824 data->original_file = g_object_ref (data->file);
6825 data->original_error = g_error_copy (error);
6828 parent_file = g_file_get_parent (data->file);
6830 /* get parent path and try to change the folder to that */
6833 g_object_unref (data->file);
6834 data->file = parent_file;
6836 g_object_unref (cancellable);
6838 /* restart the update current folder operation */
6839 impl->reload_state = RELOAD_HAS_FOLDER;
6841 impl->update_current_folder_cancellable =
6842 _gtk_file_system_get_info (impl->file_system, data->file,
6844 update_current_folder_get_info_cb,
6847 set_busy_cursor (impl, TRUE);
6853 /* error and bail out */
6854 error_changing_folder_dialog (impl, data->original_file, data->original_error);
6855 g_object_unref (data->original_file);
6861 if (data->original_file)
6863 error_changing_folder_dialog (impl, data->original_file, data->original_error);
6865 g_object_unref (data->original_file);
6868 if (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY)
6871 if (!_gtk_path_bar_set_file (GTK_PATH_BAR (impl->browse_path_bar), data->file, data->keep_trail, NULL))
6874 if (impl->current_folder != data->file)
6876 if (impl->current_folder)
6877 g_object_unref (impl->current_folder);
6879 impl->current_folder = g_object_ref (data->file);
6881 impl->reload_state = RELOAD_HAS_FOLDER;
6884 /* Update the widgets that may trigger a folder change themselves. */
6886 if (!impl->changing_folder)
6888 impl->changing_folder = TRUE;
6890 shortcuts_update_current_folder (impl);
6892 impl->changing_folder = FALSE;
6895 /* Set the folder on the save entry */
6897 if (impl->location_entry)
6899 _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
6900 impl->current_folder);
6902 if (data->clear_entry)
6903 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
6906 /* Create a new list model. This is slightly evil; we store the result value
6907 * but perform more actions rather than returning immediately even if it
6908 * generates an error.
6910 set_list_model (impl, NULL);
6912 /* Refresh controls */
6914 shortcuts_find_current_folder (impl);
6916 g_signal_emit_by_name (impl, "current-folder-changed", 0);
6918 check_preview_change (impl);
6919 bookmarks_check_add_sensitivity (impl);
6921 g_signal_emit_by_name (impl, "selection-changed", 0);
6924 g_object_unref (data->file);
6927 g_object_unref (cancellable);
6931 gtk_file_chooser_default_update_current_folder (GtkFileChooser *chooser,
6933 gboolean keep_trail,
6934 gboolean clear_entry,
6937 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
6938 struct UpdateCurrentFolderData *data;
6940 profile_start ("start", NULL);
6942 g_object_ref (file);
6944 switch (impl->operation_mode)
6946 case OPERATION_MODE_SEARCH:
6947 search_switch_to_browse_mode (impl);
6949 case OPERATION_MODE_RECENT:
6950 recent_switch_to_browse_mode (impl);
6952 case OPERATION_MODE_BROWSE:
6956 if (impl->local_only && !g_file_is_native (file))
6958 g_set_error_literal (error,
6959 GTK_FILE_CHOOSER_ERROR,
6960 GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
6961 _("Cannot change to folder because it is not local"));
6963 g_object_unref (file);
6964 profile_end ("end - not local", NULL);
6968 if (impl->update_current_folder_cancellable)
6969 g_cancellable_cancel (impl->update_current_folder_cancellable);
6971 /* Test validity of path here. */
6972 data = g_new0 (struct UpdateCurrentFolderData, 1);
6974 data->file = g_object_ref (file);
6975 data->keep_trail = keep_trail;
6976 data->clear_entry = clear_entry;
6978 impl->reload_state = RELOAD_HAS_FOLDER;
6980 impl->update_current_folder_cancellable =
6981 _gtk_file_system_get_info (impl->file_system, file,
6983 update_current_folder_get_info_cb,
6986 set_busy_cursor (impl, TRUE);
6987 g_object_unref (file);
6989 profile_end ("end", NULL);
6994 gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser)
6996 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
6998 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
6999 impl->operation_mode == OPERATION_MODE_RECENT)
7002 if (impl->reload_state == RELOAD_EMPTY)
7004 char *current_working_dir;
7007 /* We are unmapped, or we had an error while loading the last folder. We'll return
7008 * the $cwd since once we get (re)mapped, we'll load $cwd anyway unless the caller
7009 * explicitly calls set_current_folder() on us.
7011 current_working_dir = g_get_current_dir ();
7012 file = g_file_new_for_path (current_working_dir);
7013 g_free (current_working_dir);
7017 if (impl->current_folder)
7018 return g_object_ref (impl->current_folder);
7024 gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
7027 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7029 g_return_if_fail (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
7030 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
7032 pending_select_files_free (impl);
7033 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), name);
7037 select_func (GtkFileSystemModel *model,
7042 GtkFileChooserDefault *impl = user_data;
7043 GtkTreeSelection *selection;
7044 GtkTreeIter sorted_iter;
7046 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7048 gtk_tree_model_sort_convert_child_iter_to_iter (impl->sort_model, &sorted_iter, iter);
7049 gtk_tree_selection_select_iter (selection, &sorted_iter);
7053 gtk_file_chooser_default_select_file (GtkFileChooser *chooser,
7057 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7061 parent_file = g_file_get_parent (file);
7064 return _gtk_file_chooser_set_current_folder_file (chooser, file, error);
7066 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
7067 impl->operation_mode == OPERATION_MODE_RECENT ||
7068 impl->load_state == LOAD_EMPTY)
7074 g_assert (impl->current_folder != NULL);
7076 same_path = g_file_equal (parent_file, impl->current_folder);
7079 if (same_path && impl->load_state == LOAD_FINISHED)
7084 files.data = (gpointer) file;
7087 result = show_and_select_files (impl, parent_file, &files, error);
7088 g_object_unref (parent_file);
7092 pending_select_files_add (impl, file);
7098 result = _gtk_file_chooser_set_current_folder_file (chooser, parent_file, error);
7099 g_object_unref (parent_file);
7103 g_object_unref (parent_file);
7108 unselect_func (GtkFileSystemModel *model,
7113 GtkFileChooserDefault *impl = user_data;
7114 GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
7115 GtkTreePath *sorted_path;
7117 sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model,
7119 gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (tree_view),
7121 gtk_tree_path_free (sorted_path);
7125 gtk_file_chooser_default_unselect_file (GtkFileChooser *chooser,
7128 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7130 if (!impl->browse_files_model)
7133 _gtk_file_system_model_path_do (impl->browse_files_model, file,
7134 unselect_func, impl);
7138 maybe_select (GtkTreeModel *model,
7143 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (data);
7144 GtkTreeSelection *selection;
7148 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7150 info = get_list_file_info (impl, iter);
7151 is_folder = (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY);
7153 if ((is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) ||
7154 (!is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN))
7155 gtk_tree_selection_select_iter (selection, iter);
7157 gtk_tree_selection_unselect_iter (selection, iter);
7163 gtk_file_chooser_default_select_all (GtkFileChooser *chooser)
7165 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7167 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
7168 impl->operation_mode == OPERATION_MODE_RECENT)
7170 GtkTreeSelection *selection;
7172 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7173 gtk_tree_selection_select_all (selection);
7177 if (impl->select_multiple)
7178 gtk_tree_model_foreach (GTK_TREE_MODEL (impl->sort_model),
7179 maybe_select, impl);
7183 gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser)
7185 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7186 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7188 gtk_tree_selection_unselect_all (selection);
7189 pending_select_files_free (impl);
7192 /* Checks whether the filename entry for the Save modes contains a well-formed filename.
7194 * is_well_formed_ret - whether what the user typed passes gkt_file_system_make_path()
7196 * is_empty_ret - whether the file entry is totally empty
7198 * is_file_part_empty_ret - whether the file part is empty (will be if user types "foobar/", and
7199 * the path will be "$cwd/foobar")
7202 check_save_entry (GtkFileChooserDefault *impl,
7204 gboolean *is_well_formed_ret,
7205 gboolean *is_empty_ret,
7206 gboolean *is_file_part_empty_ret,
7207 gboolean *is_folder)
7209 GtkFileChooserEntry *chooser_entry;
7210 GFile *current_folder;
7211 const char *file_part;
7215 g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
7216 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
7217 || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
7218 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
7219 && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY));
7221 chooser_entry = GTK_FILE_CHOOSER_ENTRY (impl->location_entry);
7223 if (strlen (gtk_entry_get_text (GTK_ENTRY (chooser_entry))) == 0)
7226 *is_well_formed_ret = TRUE;
7227 *is_empty_ret = TRUE;
7228 *is_file_part_empty_ret = TRUE;
7234 *is_empty_ret = FALSE;
7236 current_folder = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
7237 if (!current_folder)
7240 *is_well_formed_ret = FALSE;
7241 *is_file_part_empty_ret = FALSE;
7247 file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
7249 if (!file_part || file_part[0] == '\0')
7251 *file_ret = g_object_ref (current_folder);
7252 *is_well_formed_ret = TRUE;
7253 *is_file_part_empty_ret = TRUE;
7259 *is_file_part_empty_ret = FALSE;
7262 file = g_file_get_child_for_display_name (current_folder, file_part, &error);
7266 error_building_filename_dialog (impl, error);
7268 *is_well_formed_ret = FALSE;
7275 *is_well_formed_ret = TRUE;
7276 *is_folder = _gtk_file_chooser_entry_get_is_folder (chooser_entry, file);
7279 struct get_files_closure {
7280 GtkFileChooserDefault *impl;
7282 GFile *file_from_entry;
7286 get_files_foreach (GtkTreeModel *model,
7291 struct get_files_closure *info;
7293 GtkFileSystemModel *fs_model;
7294 GtkTreeIter sel_iter;
7297 fs_model = info->impl->browse_files_model;
7298 gtk_tree_model_sort_convert_iter_to_child_iter (info->impl->sort_model, &sel_iter, iter);
7300 file = _gtk_file_system_model_get_file (fs_model, &sel_iter);
7302 return; /* We are on the editable row */
7304 if (!info->file_from_entry || !g_file_equal (info->file_from_entry, file))
7305 info->result = g_slist_prepend (info->result, g_object_ref (file));
7309 gtk_file_chooser_default_get_files (GtkFileChooser *chooser)
7311 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7312 struct get_files_closure info;
7313 GtkWindow *toplevel;
7314 GtkWidget *current_focus;
7315 gboolean file_list_seen;
7317 if (impl->operation_mode == OPERATION_MODE_SEARCH)
7318 return search_get_selected_files (impl);
7320 if (impl->operation_mode == OPERATION_MODE_RECENT)
7321 return recent_get_selected_files (impl);
7325 info.file_from_entry = NULL;
7327 toplevel = get_toplevel (GTK_WIDGET (impl));
7329 current_focus = gtk_window_get_focus (toplevel);
7331 current_focus = NULL;
7333 file_list_seen = FALSE;
7334 if (current_focus == impl->browse_files_tree_view)
7336 GtkTreeSelection *selection;
7340 file_list_seen = TRUE;
7341 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7342 gtk_tree_selection_selected_foreach (selection, get_files_foreach, &info);
7344 /* If there is no selection in the file list, we probably have this situation:
7346 * 1. The user typed a filename in the SAVE filename entry ("foo.txt").
7347 * 2. He then double-clicked on a folder ("bar") in the file list
7349 * So we want the selection to be "bar/foo.txt". Jump to the case for the
7350 * filename entry to see if that is the case.
7352 if (info.result == NULL && impl->location_entry)
7355 else if (impl->location_entry && current_focus == impl->location_entry)
7357 gboolean is_well_formed, is_empty, is_file_part_empty, is_folder;
7361 check_save_entry (impl, &info.file_from_entry, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder);
7366 if (!is_well_formed)
7369 if (is_file_part_empty && impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
7371 g_object_unref (info.file_from_entry);
7375 if (info.file_from_entry)
7376 info.result = g_slist_prepend (info.result, info.file_from_entry);
7377 else if (!file_list_seen)
7382 else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
7384 else if (impl->location_entry && impl->toplevel_last_focus_widget == impl->location_entry)
7388 /* The focus is on a dialog's action area button or something else */
7389 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
7390 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
7398 /* If there's no folder selected, and we're in SELECT_FOLDER mode, then we
7399 * fall back to the current directory */
7400 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
7401 info.result == NULL)
7403 GFile *current_folder;
7405 current_folder = _gtk_file_chooser_get_current_folder_file (chooser);
7408 info.result = g_slist_prepend (info.result, current_folder);
7411 return g_slist_reverse (info.result);
7415 gtk_file_chooser_default_get_preview_file (GtkFileChooser *chooser)
7417 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7419 if (impl->preview_file)
7420 return g_object_ref (impl->preview_file);
7425 static GtkFileSystem *
7426 gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser)
7428 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7430 return impl->file_system;
7433 /* Shows or hides the filter widgets */
7435 show_filters (GtkFileChooserDefault *impl,
7439 gtk_widget_show (impl->filter_combo_hbox);
7441 gtk_widget_hide (impl->filter_combo_hbox);
7445 gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
7446 GtkFileFilter *filter)
7448 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7451 if (g_slist_find (impl->filters, filter))
7453 g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
7457 g_object_ref_sink (filter);
7458 impl->filters = g_slist_append (impl->filters, filter);
7460 name = gtk_file_filter_get_name (filter);
7462 name = "Untitled filter"; /* Place-holder, doesn't need to be marked for translation */
7464 gtk_combo_box_append_text (GTK_COMBO_BOX (impl->filter_combo), name);
7466 if (!g_slist_find (impl->filters, impl->current_filter))
7467 set_current_filter (impl, filter);
7469 show_filters (impl, TRUE);
7473 gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
7474 GtkFileFilter *filter)
7476 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7477 GtkTreeModel *model;
7481 filter_index = g_slist_index (impl->filters, filter);
7483 if (filter_index < 0)
7485 g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
7489 impl->filters = g_slist_remove (impl->filters, filter);
7491 if (filter == impl->current_filter)
7494 set_current_filter (impl, impl->filters->data);
7496 set_current_filter (impl, NULL);
7499 /* Remove row from the combo box */
7500 model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->filter_combo));
7501 if (!gtk_tree_model_iter_nth_child (model, &iter, NULL, filter_index))
7502 g_assert_not_reached ();
7504 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
7506 g_object_unref (filter);
7509 show_filters (impl, FALSE);
7513 gtk_file_chooser_default_list_filters (GtkFileChooser *chooser)
7515 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7517 return g_slist_copy (impl->filters);
7520 /* Returns the position in the shortcuts tree where the nth specified shortcut would appear */
7522 shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl,
7525 return pos + shortcuts_get_index (impl, SHORTCUTS_SHORTCUTS);
7528 struct AddShortcutData
7530 GtkFileChooserDefault *impl;
7535 add_shortcut_get_info_cb (GCancellable *cancellable,
7537 const GError *error,
7541 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
7542 struct AddShortcutData *data = user_data;
7544 if (!g_slist_find (data->impl->loading_shortcuts, cancellable))
7547 data->impl->loading_shortcuts = g_slist_remove (data->impl->loading_shortcuts, cancellable);
7549 if (cancelled || error || g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY)
7552 pos = shortcuts_get_pos_for_shortcut_folder (data->impl, data->impl->num_shortcuts);
7554 shortcuts_insert_file (data->impl, pos, SHORTCUT_TYPE_FILE, NULL, data->file, NULL, FALSE, SHORTCUTS_SHORTCUTS);
7557 g_object_unref (data->impl);
7558 g_object_unref (data->file);
7561 g_object_unref (cancellable);
7565 gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser *chooser,
7569 GCancellable *cancellable;
7570 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7571 struct AddShortcutData *data;
7575 /* Avoid adding duplicates */
7576 pos = shortcut_find_position (impl, file);
7577 if (pos >= 0 && pos < shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR))
7581 uri = g_file_get_uri (file);
7582 /* translators, "Shortcut" means "Bookmark" here */
7584 GTK_FILE_CHOOSER_ERROR,
7585 GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
7586 _("Shortcut %s already exists"),
7593 for (l = impl->loading_shortcuts; l; l = l->next)
7595 GCancellable *c = l->data;
7598 f = g_object_get_data (G_OBJECT (c), "add-shortcut-path-key");
7599 if (f && g_file_equal (file, f))
7603 uri = g_file_get_uri (file);
7605 GTK_FILE_CHOOSER_ERROR,
7606 GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
7607 _("Shortcut %s already exists"),
7615 data = g_new0 (struct AddShortcutData, 1);
7616 data->impl = g_object_ref (impl);
7617 data->file = g_object_ref (file);
7619 cancellable = _gtk_file_system_get_info (impl->file_system, file,
7621 add_shortcut_get_info_cb, data);
7626 impl->loading_shortcuts = g_slist_append (impl->loading_shortcuts, cancellable);
7627 g_object_set_data (G_OBJECT (cancellable), "add-shortcut-path-key", data->file);
7633 gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser *chooser,
7637 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7644 for (l = impl->loading_shortcuts; l; l = l->next)
7646 GCancellable *c = l->data;
7649 f = g_object_get_data (G_OBJECT (c), "add-shortcut-path-key");
7650 if (f && g_file_equal (file, f))
7652 impl->loading_shortcuts = g_slist_remove (impl->loading_shortcuts, c);
7653 g_cancellable_cancel (c);
7658 if (impl->num_shortcuts == 0)
7661 pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
7662 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
7663 g_assert_not_reached ();
7665 for (i = 0; i < impl->num_shortcuts; i++)
7668 ShortcutType shortcut_type;
7671 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
7672 SHORTCUTS_COL_DATA, &col_data,
7673 SHORTCUTS_COL_TYPE, &shortcut_type,
7675 g_assert (col_data != NULL);
7676 g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
7678 shortcut = col_data;
7679 if (g_file_equal (shortcut, file))
7681 shortcuts_remove_rows (impl, pos + i, 1);
7682 impl->num_shortcuts--;
7686 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
7687 g_assert_not_reached ();
7692 uri = g_file_get_uri (file);
7693 /* translators, "Shortcut" means "Bookmark" here */
7695 GTK_FILE_CHOOSER_ERROR,
7696 GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
7697 _("Shortcut %s does not exist"),
7705 gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser)
7707 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7713 if (impl->num_shortcuts == 0)
7716 pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
7717 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
7718 g_assert_not_reached ();
7722 for (i = 0; i < impl->num_shortcuts; i++)
7725 ShortcutType shortcut_type;
7728 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
7729 SHORTCUTS_COL_DATA, &col_data,
7730 SHORTCUTS_COL_TYPE, &shortcut_type,
7732 g_assert (col_data != NULL);
7733 g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
7735 shortcut = col_data;
7736 list = g_slist_prepend (list, g_object_ref (shortcut));
7738 if (i != impl->num_shortcuts - 1)
7740 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
7741 g_assert_not_reached ();
7745 return g_slist_reverse (list);
7748 /* Guesses a size based upon font sizes */
7750 find_good_size_from_style (GtkWidget *widget,
7754 GtkFileChooserDefault *impl;
7759 g_assert (widget->style != NULL);
7760 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
7762 if (impl->default_width == 0 &&
7763 impl->default_height == 0)
7765 screen = gtk_widget_get_screen (widget);
7768 resolution = gdk_screen_get_resolution (screen);
7769 if (resolution < 0.0) /* will be -1 if the resolution is not defined in the GdkScreen */
7773 resolution = 96.0; /* wheeee */
7775 font_size = pango_font_description_get_size (widget->style->font_desc);
7776 font_size = PANGO_PIXELS (font_size) * resolution / 72.0;
7778 impl->default_width = font_size * NUM_CHARS;
7779 impl->default_height = font_size * NUM_LINES;
7782 *width = impl->default_width;
7783 *height = impl->default_height;
7787 gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
7788 gint *default_width,
7789 gint *default_height)
7791 GtkFileChooserDefault *impl;
7794 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
7795 find_good_size_from_style (GTK_WIDGET (chooser_embed), default_width, default_height);
7797 if (impl->preview_widget_active &&
7798 impl->preview_widget &&
7799 GTK_WIDGET_VISIBLE (impl->preview_widget))
7801 gtk_widget_size_request (impl->preview_box, &req);
7802 *default_width += PREVIEW_HBOX_SPACING + req.width;
7805 if (impl->extra_widget &&
7806 GTK_WIDGET_VISIBLE (impl->extra_widget))
7808 gtk_widget_size_request (impl->extra_align, &req);
7809 *default_height += GTK_BOX (chooser_embed)->spacing + req.height;
7813 struct switch_folder_closure {
7814 GtkFileChooserDefault *impl;
7819 /* Used from gtk_tree_selection_selected_foreach() in switch_to_selected_folder() */
7821 switch_folder_foreach_cb (GtkTreeModel *model,
7826 struct switch_folder_closure *closure;
7827 GtkTreeIter child_iter;
7831 gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
7833 closure->file = _gtk_file_system_model_get_file (closure->impl->browse_files_model, &child_iter);
7834 closure->num_selected++;
7837 /* Changes to the selected folder in the list view */
7839 switch_to_selected_folder (GtkFileChooserDefault *impl)
7841 GtkTreeSelection *selection;
7842 struct switch_folder_closure closure;
7844 /* We do this with foreach() rather than get_selected() as we may be in
7845 * multiple selection mode
7848 closure.impl = impl;
7849 closure.file = NULL;
7850 closure.num_selected = 0;
7852 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7853 gtk_tree_selection_selected_foreach (selection, switch_folder_foreach_cb, &closure);
7855 g_assert (closure.file && closure.num_selected == 1);
7857 change_folder_and_display_error (impl, closure.file, FALSE);
7860 /* Gets the GFileInfo for the selected row in the file list; assumes single
7864 get_selected_file_info_from_file_list (GtkFileChooserDefault *impl,
7865 gboolean *had_selection)
7867 GtkTreeSelection *selection;
7868 GtkTreeIter iter, child_iter;
7871 g_assert (!impl->select_multiple);
7872 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7873 if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
7875 *had_selection = FALSE;
7879 *had_selection = TRUE;
7881 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
7885 info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
7889 /* Gets the display name of the selected file in the file list; assumes single
7890 * selection mode and that something is selected.
7892 static const gchar *
7893 get_display_name_from_file_list (GtkFileChooserDefault *impl)
7896 gboolean had_selection;
7898 info = get_selected_file_info_from_file_list (impl, &had_selection);
7899 g_assert (had_selection);
7900 g_assert (info != NULL);
7902 return g_file_info_get_display_name (info);
7906 add_custom_button_to_dialog (GtkDialog *dialog,
7907 const gchar *mnemonic_label,
7908 const gchar *stock_id,
7913 button = gtk_button_new_with_mnemonic (mnemonic_label);
7914 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
7915 gtk_button_set_image (GTK_BUTTON (button),
7916 gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON));
7917 gtk_widget_show (button);
7919 gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, response_id);
7922 /* Presents an overwrite confirmation dialog; returns whether we should accept
7926 confirm_dialog_should_accept_filename (GtkFileChooserDefault *impl,
7927 const gchar *file_part,
7928 const gchar *folder_display_name)
7930 GtkWindow *toplevel;
7934 toplevel = get_toplevel (GTK_WIDGET (impl));
7936 dialog = gtk_message_dialog_new (toplevel,
7937 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
7938 GTK_MESSAGE_QUESTION,
7940 _("A file named \"%s\" already exists. Do you want to replace it?"),
7942 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
7943 _("The file already exists in \"%s\". Replacing it will "
7944 "overwrite its contents."),
7945 folder_display_name);
7947 gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
7948 add_custom_button_to_dialog (GTK_DIALOG (dialog), _("_Replace"),
7949 GTK_STOCK_SAVE_AS, GTK_RESPONSE_ACCEPT);
7950 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
7951 GTK_RESPONSE_ACCEPT,
7952 GTK_RESPONSE_CANCEL,
7954 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
7956 if (toplevel->group)
7957 gtk_window_group_add_window (toplevel->group, GTK_WINDOW (dialog));
7959 response = gtk_dialog_run (GTK_DIALOG (dialog));
7961 gtk_widget_destroy (dialog);
7963 return (response == GTK_RESPONSE_ACCEPT);
7966 struct GetDisplayNameData
7968 GtkFileChooserDefault *impl;
7973 confirmation_confirm_get_info_cb (GCancellable *cancellable,
7975 const GError *error,
7978 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
7979 gboolean should_respond = FALSE;
7980 struct GetDisplayNameData *data = user_data;
7982 if (cancellable != data->impl->should_respond_get_info_cancellable)
7985 data->impl->should_respond_get_info_cancellable = NULL;
7991 /* Huh? Did the folder disappear? Let the caller deal with it */
7992 should_respond = TRUE;
7994 should_respond = confirm_dialog_should_accept_filename (data->impl, data->file_part, g_file_info_get_display_name (info));
7996 set_busy_cursor (data->impl, FALSE);
7998 g_signal_emit_by_name (data->impl, "response-requested");
8001 g_object_unref (data->impl);
8002 g_free (data->file_part);
8005 g_object_unref (cancellable);
8008 /* Does overwrite confirmation if appropriate, and returns whether the dialog
8009 * should respond. Can get the file part from the file list or the save entry.
8012 should_respond_after_confirm_overwrite (GtkFileChooserDefault *impl,
8013 const gchar *file_part,
8016 GtkFileChooserConfirmation conf;
8018 if (!impl->do_overwrite_confirmation)
8021 conf = GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM;
8023 g_signal_emit_by_name (impl, "confirm-overwrite", &conf);
8027 case GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM:
8029 struct GetDisplayNameData *data;
8031 g_assert (file_part != NULL);
8033 data = g_new0 (struct GetDisplayNameData, 1);
8034 data->impl = g_object_ref (impl);
8035 data->file_part = g_strdup (file_part);
8037 if (impl->should_respond_get_info_cancellable)
8038 g_cancellable_cancel (impl->should_respond_get_info_cancellable);
8040 impl->should_respond_get_info_cancellable =
8041 _gtk_file_system_get_info (impl->file_system, parent_file,
8042 "standard::display-name",
8043 confirmation_confirm_get_info_cb,
8045 set_busy_cursor (data->impl, TRUE);
8049 case GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME:
8052 case GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN:
8056 g_assert_not_reached ();
8061 struct FileExistsData
8063 GtkFileChooserDefault *impl;
8064 gboolean file_exists_and_is_not_folder;
8070 save_entry_get_info_cb (GCancellable *cancellable,
8072 const GError *error,
8075 gboolean parent_is_folder;
8076 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
8077 struct FileExistsData *data = user_data;
8079 if (cancellable != data->impl->should_respond_get_info_cancellable)
8082 data->impl->should_respond_get_info_cancellable = NULL;
8084 set_busy_cursor (data->impl, FALSE);
8090 parent_is_folder = FALSE;
8092 parent_is_folder = (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY);
8094 if (parent_is_folder)
8096 if (data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8098 if (data->file_exists_and_is_not_folder)
8101 const char *file_part;
8103 file_part = _gtk_file_chooser_entry_get_file_part (GTK_FILE_CHOOSER_ENTRY (data->impl->location_entry));
8104 retval = should_respond_after_confirm_overwrite (data->impl, file_part, data->parent_file);
8107 g_signal_emit_by_name (data->impl, "response-requested");
8110 g_signal_emit_by_name (data->impl, "response-requested");
8112 else /* GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER */
8114 GError *error = NULL;
8116 set_busy_cursor (data->impl, TRUE);
8117 g_file_make_directory (data->file, NULL, &error);
8118 set_busy_cursor (data->impl, FALSE);
8121 g_signal_emit_by_name (data->impl, "response-requested");
8123 error_creating_folder_dialog (data->impl, data->file, error);
8128 /* This will display an error, which is what we want */
8129 change_folder_and_display_error (data->impl, data->parent_file, FALSE);
8133 g_object_unref (data->impl);
8134 g_object_unref (data->file);
8135 g_object_unref (data->parent_file);
8138 g_object_unref (cancellable);
8142 file_exists_get_info_cb (GCancellable *cancellable,
8144 const GError *error,
8147 gboolean data_ownership_taken = FALSE;
8148 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
8149 gboolean file_exists_and_is_not_folder;
8150 struct FileExistsData *data = user_data;
8152 if (cancellable != data->impl->file_exists_get_info_cancellable)
8155 data->impl->file_exists_get_info_cancellable = NULL;
8157 set_busy_cursor (data->impl, FALSE);
8162 file_exists_and_is_not_folder = info && (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY);
8164 if (data->impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
8165 /* user typed a filename; we are done */
8166 g_signal_emit_by_name (data->impl, "response-requested");
8167 else if (data->impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
8168 && file_exists_and_is_not_folder)
8170 /* Oops, the user typed the name of an existing path which is not
8173 error_creating_folder_over_existing_file_dialog (data->impl, data->file,
8174 g_error_copy (error));
8178 /* check that everything up to the last component exists */
8180 data->file_exists_and_is_not_folder = file_exists_and_is_not_folder;
8181 data_ownership_taken = TRUE;
8183 if (data->impl->should_respond_get_info_cancellable)
8184 g_cancellable_cancel (data->impl->should_respond_get_info_cancellable);
8186 data->impl->should_respond_get_info_cancellable =
8187 _gtk_file_system_get_info (data->impl->file_system,
8190 save_entry_get_info_cb,
8192 set_busy_cursor (data->impl, TRUE);
8196 if (!data_ownership_taken)
8198 g_object_unref (data->impl);
8199 g_object_unref (data->file);
8200 g_object_unref (data->parent_file);
8204 g_object_unref (cancellable);
8208 paste_text_received (GtkClipboard *clipboard,
8210 GtkFileChooserDefault *impl)
8217 file = g_file_new_for_uri (text);
8219 if (!gtk_file_chooser_default_select_file (GTK_FILE_CHOOSER (impl), file, NULL))
8220 location_popup_handler (impl, text);
8222 g_object_unref (file);
8225 /* Handler for the "location-popup-on-paste" keybinding signal */
8227 location_popup_on_paste_handler (GtkFileChooserDefault *impl)
8229 GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (impl),
8230 GDK_SELECTION_CLIPBOARD);
8231 gtk_clipboard_request_text (clipboard,
8232 (GtkClipboardTextReceivedFunc) paste_text_received,
8237 /* Implementation for GtkFileChooserEmbed::should_respond() */
8239 gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
8241 GtkFileChooserDefault *impl;
8242 GtkWidget *toplevel;
8243 GtkWidget *current_focus;
8245 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
8247 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
8248 g_assert (GTK_IS_WINDOW (toplevel));
8250 current_focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
8252 if (current_focus == impl->browse_files_tree_view)
8254 /* The following array encodes what we do based on the impl->action and the
8255 * number of files selected.
8258 NOOP, /* Do nothing (don't respond) */
8259 RESPOND, /* Respond immediately */
8260 RESPOND_OR_SWITCH, /* Respond immediately if the selected item is a file; switch to it if it is a folder */
8261 ALL_FILES, /* Respond only if everything selected is a file */
8262 ALL_FOLDERS, /* Respond only if everything selected is a folder */
8263 SAVE_ENTRY, /* Go to the code for handling the save entry */
8264 NOT_REACHED /* Sanity check */
8266 static const ActionToTake what_to_do[4][3] = {
8267 /* 0 selected 1 selected many selected */
8268 /* ACTION_OPEN */ { NOOP, RESPOND_OR_SWITCH, ALL_FILES },
8269 /* ACTION_SAVE */ { SAVE_ENTRY, RESPOND_OR_SWITCH, NOT_REACHED },
8270 /* ACTION_SELECT_FOLDER */ { RESPOND, ALL_FOLDERS, ALL_FOLDERS },
8271 /* ACTION_CREATE_FOLDER */ { SAVE_ENTRY, ALL_FOLDERS, NOT_REACHED }
8275 gboolean all_files, all_folders;
8277 ActionToTake action;
8281 g_assert (impl->action >= GTK_FILE_CHOOSER_ACTION_OPEN && impl->action <= GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
8283 if (impl->operation_mode == OPERATION_MODE_SEARCH)
8284 return search_should_respond (impl);
8286 if (impl->operation_mode == OPERATION_MODE_RECENT)
8287 return recent_should_respond (impl);
8289 selection_check (impl, &num_selected, &all_files, &all_folders);
8291 if (num_selected > 2)
8296 action = what_to_do [impl->action] [k];
8306 case RESPOND_OR_SWITCH:
8307 g_assert (num_selected == 1);
8311 switch_to_selected_folder (impl);
8314 else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8315 return should_respond_after_confirm_overwrite (impl,
8316 get_display_name_from_file_list (impl),
8317 impl->current_folder);
8331 g_assert_not_reached ();
8334 else if ((impl->location_entry != NULL) && (current_focus == impl->location_entry))
8337 gboolean is_well_formed, is_empty, is_file_part_empty;
8340 GtkFileChooserEntry *entry;
8345 g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
8346 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
8347 || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
8348 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8349 && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY));
8351 entry = GTK_FILE_CHOOSER_ENTRY (impl->location_entry);
8352 check_save_entry (impl, &file, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder);
8354 if (is_empty || !is_well_formed)
8357 g_assert (file != NULL);
8362 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
8363 impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8365 change_folder_and_display_error (impl, file, TRUE);
8368 else if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
8369 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8371 /* The folder already exists, so we do not need to create it.
8372 * Just respond to terminate the dialog.
8378 g_assert_not_reached ();
8384 struct FileExistsData *data;
8386 /* We need to check whether file exists and is not a folder */
8388 data = g_new0 (struct FileExistsData, 1);
8389 data->impl = g_object_ref (impl);
8390 data->file = g_object_ref (file);
8391 data->parent_file = g_object_ref (_gtk_file_chooser_entry_get_current_folder (entry));
8393 if (impl->file_exists_get_info_cancellable)
8394 g_cancellable_cancel (impl->file_exists_get_info_cancellable);
8396 impl->file_exists_get_info_cancellable =
8397 _gtk_file_system_get_info (impl->file_system, file,
8399 file_exists_get_info_cb,
8402 set_busy_cursor (impl, TRUE);
8406 g_error_free (error);
8409 g_object_unref (file);
8412 else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
8414 /* The focus is on a dialog's action area button, *and* the widget that
8415 * was focused immediately before it is the file list.
8419 else if (impl->operation_mode == OPERATION_MODE_SEARCH && impl->toplevel_last_focus_widget == impl->search_entry)
8421 search_entry_activate_cb (GTK_ENTRY (impl->search_entry), impl);
8424 else if (impl->location_entry && impl->toplevel_last_focus_widget == impl->location_entry)
8426 /* The focus is on a dialog's action area button, *and* the widget that
8427 * was focused immediately before it is the location entry.
8432 /* The focus is on a dialog's action area button or something else */
8433 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
8434 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8439 g_assert_not_reached ();
8443 /* Implementation for GtkFileChooserEmbed::initial_focus() */
8445 gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed)
8447 GtkFileChooserDefault *impl;
8450 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
8452 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
8453 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8455 if (impl->location_mode == LOCATION_MODE_PATH_BAR)
8456 widget = impl->browse_files_tree_view;
8458 widget = impl->location_entry;
8460 else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
8461 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8462 widget = impl->location_entry;
8465 g_assert_not_reached ();
8469 g_assert (widget != NULL);
8470 gtk_widget_grab_focus (widget);
8473 /* Callback used from gtk_tree_selection_selected_foreach(); gets the selected GFiles */
8475 search_selected_foreach_get_file_cb (GtkTreeModel *model,
8485 gtk_tree_model_get (model, iter, SEARCH_MODEL_COL_FILE, &file, -1);
8486 *list = g_slist_prepend (*list, file);
8489 /* Constructs a list of the selected paths in search mode */
8491 search_get_selected_files (GtkFileChooserDefault *impl)
8494 GtkTreeSelection *selection;
8498 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
8499 gtk_tree_selection_selected_foreach (selection, search_selected_foreach_get_file_cb, &result);
8500 result = g_slist_reverse (result);
8505 /* Called from ::should_respond(). We return whether there are selected files
8506 * in the search list.
8509 search_should_respond (GtkFileChooserDefault *impl)
8511 GtkTreeSelection *selection;
8513 g_assert (impl->operation_mode == OPERATION_MODE_SEARCH);
8515 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
8516 return (gtk_tree_selection_count_selected_rows (selection) != 0);
8519 struct SearchHitInsertRequest
8521 GtkFileChooserDefault *impl;
8523 GtkTreeRowReference *row_ref;
8527 search_hit_get_info_cb (GCancellable *cancellable,
8529 const GError *error,
8532 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
8533 GdkPixbuf *pixbuf = NULL;
8536 GCancellable *model_cancellable;
8537 gboolean is_folder = FALSE;
8540 struct SearchHitInsertRequest *request = data;
8542 if (!request->impl->search_model)
8545 path = gtk_tree_row_reference_get_path (request->row_ref);
8549 gtk_tree_model_get_iter (GTK_TREE_MODEL (request->impl->search_model),
8551 gtk_tree_path_free (path);
8553 gtk_tree_model_get (GTK_TREE_MODEL (request->impl->search_model), &iter,
8554 SEARCH_MODEL_COL_CANCELLABLE, &model_cancellable,
8556 if (cancellable != model_cancellable)
8559 /* set the cancellable to NULL in the model */
8560 gtk_list_store_set (request->impl->search_model, &iter,
8561 SEARCH_MODEL_COL_CANCELLABLE, NULL,
8569 gtk_list_store_remove (request->impl->search_model, &iter);
8573 display_name = g_strdup (g_file_info_get_display_name (info));
8574 mime_type = g_strdup (g_file_info_get_content_type (info));
8575 is_folder = (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY);
8576 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (request->impl),
8577 request->impl->icon_size);
8579 gtk_list_store_set (request->impl->search_model, &iter,
8580 SEARCH_MODEL_COL_PIXBUF, pixbuf,
8581 SEARCH_MODEL_COL_DISPLAY_NAME, display_name,
8582 SEARCH_MODEL_COL_MIME_TYPE, mime_type,
8583 SEARCH_MODEL_COL_IS_FOLDER, is_folder,
8587 g_object_unref (pixbuf);
8590 g_object_unref (request->impl);
8591 g_object_unref (request->file);
8592 gtk_tree_row_reference_free (request->row_ref);
8595 g_object_unref (cancellable);
8598 /* Adds one hit from the search engine to the search_model */
8600 search_add_hit (GtkFileChooserDefault *impl,
8606 char *collation_key;
8607 struct stat statbuf;
8608 struct stat *statbuf_copy;
8611 GCancellable *cancellable;
8612 struct SearchHitInsertRequest *request;
8614 file = g_file_new_for_uri (uri);
8616 if (!g_file_is_native (file))
8618 g_object_unref (file);
8622 filename = g_file_get_path (file);
8624 if (stat (filename, &statbuf) != 0)
8626 g_object_unref (file);
8631 statbuf_copy = g_new (struct stat, 1);
8632 *statbuf_copy = statbuf;
8634 tmp = g_file_get_parse_name (file);
8635 collation_key = g_utf8_collate_key_for_filename (tmp, -1);
8638 request = g_new0 (struct SearchHitInsertRequest, 1);
8639 request->impl = g_object_ref (impl);
8640 request->file = g_object_ref (file);
8642 gtk_list_store_append (impl->search_model, &iter);
8643 p = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->search_model), &iter);
8645 request->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->search_model), p);
8646 gtk_tree_path_free (p);
8648 cancellable = _gtk_file_system_get_info (impl->file_system, file,
8649 "standard::type,standard::icon,"
8650 "standard::content-type,standard::display-name",
8651 search_hit_get_info_cb,
8654 gtk_list_store_set (impl->search_model, &iter,
8655 SEARCH_MODEL_COL_FILE, file,
8656 SEARCH_MODEL_COL_COLLATION_KEY, collation_key,
8657 SEARCH_MODEL_COL_STAT, statbuf_copy,
8658 SEARCH_MODEL_COL_CANCELLABLE, cancellable,
8661 g_object_unref (file);
8665 /* Callback used from GtkSearchEngine when we get new hits */
8667 search_engine_hits_added_cb (GtkSearchEngine *engine,
8671 GtkFileChooserDefault *impl;
8674 impl = GTK_FILE_CHOOSER_DEFAULT (data);
8676 for (l = hits; l; l = l->next)
8677 search_add_hit (impl, (gchar*)l->data);
8680 /* Callback used from GtkSearchEngine when the query is done running */
8682 search_engine_finished_cb (GtkSearchEngine *engine,
8685 GtkFileChooserDefault *impl;
8687 impl = GTK_FILE_CHOOSER_DEFAULT (data);
8690 /* EB: setting the model here will avoid loads of row events,
8691 * but it'll make the search look like blocked.
8693 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
8694 GTK_TREE_MODEL (impl->search_model_filter));
8697 /* FMQ: if search was empty, say that we got no hits */
8698 set_busy_cursor (impl, FALSE);
8701 /* Displays a generic error when we cannot create a GtkSearchEngine.
8702 * It would be better if _gtk_search_engine_new() gave us a GError
8703 * with a better message, but it doesn't do that right now.
8706 search_error_could_not_create_client (GtkFileChooserDefault *impl)
8708 error_message (impl,
8709 _("Could not start the search process"),
8710 _("The program was not able to create a connection to the indexer "
8711 "daemon. Please make sure it is running."));
8715 search_engine_error_cb (GtkSearchEngine *engine,
8716 const gchar *message,
8719 GtkFileChooserDefault *impl;
8721 impl = GTK_FILE_CHOOSER_DEFAULT (data);
8723 search_stop_searching (impl, TRUE);
8724 error_message (impl, _("Could not send the search request"), message);
8726 set_busy_cursor (impl, FALSE);
8729 /* Frees the data in the search_model */
8731 search_clear_model (GtkFileChooserDefault *impl,
8732 gboolean remove_from_treeview)
8734 GtkTreeModel *model;
8737 if (!impl->search_model)
8740 model = GTK_TREE_MODEL (impl->search_model);
8742 if (gtk_tree_model_get_iter_first (model, &iter))
8745 GCancellable *cancellable;
8747 gtk_tree_model_get (model, &iter,
8748 SEARCH_MODEL_COL_CANCELLABLE, &cancellable,
8752 g_cancellable_cancel (cancellable);
8754 while (gtk_tree_model_iter_next (model, &iter));
8756 g_object_unref (impl->search_model);
8757 impl->search_model = NULL;
8759 g_object_unref (impl->search_model_filter);
8760 impl->search_model_filter = NULL;
8762 g_object_unref (impl->search_model_sort);
8763 impl->search_model_sort = NULL;
8765 if (remove_from_treeview)
8766 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
8769 /* Stops any ongoing searches; does not touch the search_model */
8771 search_stop_searching (GtkFileChooserDefault *impl,
8772 gboolean remove_query)
8774 if (remove_query && impl->search_query)
8776 g_object_unref (impl->search_query);
8777 impl->search_query = NULL;
8780 if (impl->search_engine)
8782 _gtk_search_engine_stop (impl->search_engine);
8784 g_object_unref (impl->search_engine);
8785 impl->search_engine = NULL;
8789 /* Stops any pending searches, clears the file list, and switches back to OPERATION_MODE_BROWSE */
8791 search_switch_to_browse_mode (GtkFileChooserDefault *impl)
8793 g_assert (impl->operation_mode != OPERATION_MODE_BROWSE);
8795 search_stop_searching (impl, FALSE);
8796 search_clear_model (impl, TRUE);
8798 gtk_widget_destroy (impl->search_hbox);
8799 impl->search_hbox = NULL;
8800 impl->search_entry = NULL;
8802 gtk_widget_show (impl->browse_path_bar);
8803 gtk_widget_show (impl->browse_new_folder_button);
8805 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
8806 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8808 gtk_widget_show (impl->location_button);
8810 if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
8811 gtk_widget_show (impl->location_entry_box);
8814 impl->operation_mode = OPERATION_MODE_BROWSE;
8816 file_list_set_sort_column_ids (impl);
8819 /* Sort callback from the path column */
8821 search_column_path_sort_func (GtkTreeModel *model,
8826 GtkFileChooserDefault *impl = user_data;
8827 GtkTreeIter child_a, child_b;
8828 const char *collation_key_a, *collation_key_b;
8829 gboolean is_folder_a, is_folder_b;
8831 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_a, a);
8832 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_b, b);
8834 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_a,
8835 SEARCH_MODEL_COL_IS_FOLDER, &is_folder_a,
8836 SEARCH_MODEL_COL_COLLATION_KEY, &collation_key_a,
8838 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_b,
8839 SEARCH_MODEL_COL_IS_FOLDER, &is_folder_b,
8840 SEARCH_MODEL_COL_COLLATION_KEY, &collation_key_b,
8843 if (!collation_key_a)
8846 if (!collation_key_b)
8849 /* always show folders first */
8850 if (is_folder_a != is_folder_b)
8851 return is_folder_a ? 1 : -1;
8853 return strcmp (collation_key_a, collation_key_b);
8856 /* Sort callback from the modification time column */
8858 search_column_mtime_sort_func (GtkTreeModel *model,
8863 GtkFileChooserDefault *impl = user_data;
8864 GtkTreeIter child_a, child_b;
8865 const struct stat *statbuf_a, *statbuf_b;
8866 gboolean is_folder_a, is_folder_b;
8868 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_a, a);
8869 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_b, b);
8871 /* Note that although we store a whole struct stat in the model, we only
8872 * compare the mtime here. If we add another column relative to a struct stat
8873 * (e.g. a file size column), we'll want another sort callback similar to this
8876 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_a,
8877 SEARCH_MODEL_COL_IS_FOLDER, &is_folder_a,
8878 SEARCH_MODEL_COL_STAT, &statbuf_a,
8880 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_b,
8881 SEARCH_MODEL_COL_IS_FOLDER, &is_folder_b,
8882 SEARCH_MODEL_COL_STAT, &statbuf_b,
8891 if (is_folder_a != is_folder_b)
8892 return is_folder_a ? 1 : -1;
8894 if (statbuf_a->st_mtime < statbuf_b->st_mtime)
8896 else if (statbuf_a->st_mtime > statbuf_b->st_mtime)
8903 search_get_is_filtered (GtkFileChooserDefault *impl,
8905 const gchar *display_name,
8906 const gchar *mime_type)
8908 GtkFileFilterInfo filter_info;
8909 GtkFileFilterFlags needed;
8912 if (!impl->current_filter)
8915 filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
8916 needed = gtk_file_filter_get_needed (impl->current_filter);
8918 filter_info.display_name = display_name;
8919 filter_info.mime_type = mime_type;
8921 if (needed & GTK_FILE_FILTER_FILENAME)
8923 filter_info.filename = g_file_get_path (file);
8924 if (filter_info.filename)
8925 filter_info.contains |= GTK_FILE_FILTER_FILENAME;
8928 filter_info.filename = NULL;
8930 if (needed & GTK_FILE_FILTER_URI)
8932 filter_info.uri = g_file_get_uri (file);
8933 if (filter_info.uri)
8934 filter_info.contains |= GTK_FILE_FILTER_URI;
8937 filter_info.uri = NULL;
8939 result = gtk_file_filter_filter (impl->current_filter, &filter_info);
8941 if (filter_info.filename)
8942 g_free ((gchar *) filter_info.filename);
8943 if (filter_info.uri)
8944 g_free ((gchar *) filter_info.uri);
8950 /* Visibility function for the recent filter model */
8952 search_model_visible_func (GtkTreeModel *model,
8956 GtkFileChooserDefault *impl = user_data;
8958 gchar *display_name, *mime_type;
8961 if (!impl->current_filter)
8964 gtk_tree_model_get (model, iter,
8965 SEARCH_MODEL_COL_FILE, &file,
8966 SEARCH_MODEL_COL_IS_FOLDER, &is_folder,
8967 SEARCH_MODEL_COL_DISPLAY_NAME, &display_name,
8968 SEARCH_MODEL_COL_MIME_TYPE, &mime_type,
8977 return !search_get_is_filtered (impl, file, display_name, mime_type);
8980 /* Creates the search_model and puts it in the tree view */
8982 search_setup_model (GtkFileChooserDefault *impl)
8984 g_assert (impl->search_model == NULL);
8985 g_assert (impl->search_model_filter == NULL);
8986 g_assert (impl->search_model_sort == NULL);
8988 /* We store these columns in the search model:
8990 * SEARCH_MODEL_COL_FILE - a GFile for the hit's URI, stored
8991 * as a pointer not as a G_TYPE_FILE
8992 * SEARCH_MODEL_COL_DISPLAY_NAME - a string with the display name, stored
8993 * as a pointer not as a G_TYPE_STRING
8994 * SEARCH_MODEL_COL_COLLATION_KEY - collation key for the filename, stored
8995 * as a pointer not as a G_TYPE_STRING
8996 * SEARCH_MODEL_COL_STAT - pointer to a struct stat
8997 * SEARCH_MODEL_COL_CANCELLABLE - cancellable used when getting the hit's info
8998 * SEARCH_MODEL_COL_PIXBUF - GdkPixbuf for the hit's icon
8999 * SEARCH_MODEL_COL_MIME_TYPE - a string with the hit's MIME type
9000 * SEARCH_MODEL_COL_IS_FOLDER - a boolean flag for folders
9002 * Keep this in sync with the enumeration defined near the beginning
9005 impl->search_model = gtk_list_store_new (SEARCH_MODEL_COL_NUM_COLUMNS,
9015 impl->search_model_filter =
9016 GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (GTK_TREE_MODEL (impl->search_model), NULL));
9017 gtk_tree_model_filter_set_visible_func (impl->search_model_filter,
9018 search_model_visible_func,
9021 impl->search_model_sort =
9022 GTK_TREE_MODEL_SORT (search_model_sort_new (impl, GTK_TREE_MODEL (impl->search_model_filter)));
9023 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model_sort),
9024 SEARCH_MODEL_COL_FILE,
9025 search_column_path_sort_func,
9027 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model_sort),
9028 SEARCH_MODEL_COL_STAT,
9029 search_column_mtime_sort_func,
9031 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->search_model_sort),
9032 SEARCH_MODEL_COL_STAT,
9033 GTK_SORT_DESCENDING);
9035 /* EB: setting the model here will make the hits list update feel
9036 * more "alive" than setting the model at the end of the search
9039 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
9040 GTK_TREE_MODEL (impl->search_model_sort));
9044 search_get_valid_child_iter (GtkFileChooserDefault *impl,
9045 GtkTreeIter *child_iter,
9050 if (!impl->search_model)
9053 if (!impl->search_model_filter || !impl->search_model_sort)
9056 /* pass 1: get the iterator in the filter model */
9057 gtk_tree_model_sort_convert_iter_to_child_iter (impl->search_model_sort,
9060 /* pass 2: get the iterator in the real model */
9061 gtk_tree_model_filter_convert_iter_to_child_iter (impl->search_model_filter,
9062 child_iter, &middle);
9065 /* Creates a new query with the specified text and launches it */
9067 search_start_query (GtkFileChooserDefault *impl,
9068 const gchar *query_text)
9070 search_stop_searching (impl, FALSE);
9071 search_clear_model (impl, TRUE);
9072 search_setup_model (impl);
9073 set_busy_cursor (impl, TRUE);
9075 if (impl->search_engine == NULL)
9076 impl->search_engine = _gtk_search_engine_new ();
9078 if (!impl->search_engine)
9080 set_busy_cursor (impl, FALSE);
9081 search_error_could_not_create_client (impl); /* lame; we don't get an error code or anything */
9085 if (!impl->search_query)
9087 impl->search_query = _gtk_query_new ();
9088 _gtk_query_set_text (impl->search_query, query_text);
9091 _gtk_search_engine_set_query (impl->search_engine, impl->search_query);
9093 g_signal_connect (impl->search_engine, "hits-added",
9094 G_CALLBACK (search_engine_hits_added_cb), impl);
9095 g_signal_connect (impl->search_engine, "finished",
9096 G_CALLBACK (search_engine_finished_cb), impl);
9097 g_signal_connect (impl->search_engine, "error",
9098 G_CALLBACK (search_engine_error_cb), impl);
9100 _gtk_search_engine_start (impl->search_engine);
9103 /* Callback used when the user presses Enter while typing on the search
9104 * entry; starts the query
9107 search_entry_activate_cb (GtkEntry *entry,
9110 GtkFileChooserDefault *impl;
9113 impl = GTK_FILE_CHOOSER_DEFAULT (data);
9115 text = gtk_entry_get_text (GTK_ENTRY (impl->search_entry));
9116 if (strlen (text) == 0)
9119 /* reset any existing query object */
9120 if (impl->search_query)
9122 g_object_unref (impl->search_query);
9123 impl->search_query = NULL;
9126 search_start_query (impl, text);
9129 /* Hides the path bar and creates the search entry */
9131 search_setup_widgets (GtkFileChooserDefault *impl)
9135 impl->search_hbox = gtk_hbox_new (FALSE, 12);
9139 label = gtk_label_new_with_mnemonic (_("_Search:"));
9140 gtk_box_pack_start (GTK_BOX (impl->search_hbox), label, FALSE, FALSE, 0);
9144 impl->search_entry = gtk_entry_new ();
9145 gtk_label_set_mnemonic_widget (GTK_LABEL (label), impl->search_entry);
9146 g_signal_connect (impl->search_entry, "activate",
9147 G_CALLBACK (search_entry_activate_cb),
9149 gtk_box_pack_start (GTK_BOX (impl->search_hbox), impl->search_entry, TRUE, TRUE, 0);
9151 /* if there already is a query, restart it */
9152 if (impl->search_query)
9154 gchar *query = _gtk_query_get_text (impl->search_query);
9158 gtk_entry_set_text (GTK_ENTRY (impl->search_entry), query);
9159 search_start_query (impl, query);
9165 g_object_unref (impl->search_query);
9166 impl->search_query = NULL;
9170 gtk_widget_hide (impl->browse_path_bar);
9171 gtk_widget_hide (impl->browse_new_folder_button);
9173 /* Box for search widgets */
9174 gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->search_hbox, TRUE, TRUE, 0);
9175 gtk_widget_show_all (impl->search_hbox);
9177 /* Hide the location widgets temporarily */
9179 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
9180 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
9182 gtk_widget_hide (impl->location_button);
9183 gtk_widget_hide (impl->location_entry_box);
9186 gtk_widget_grab_focus (impl->search_entry);
9188 /* FMQ: hide the filter combo? */
9191 /* Main entry point to the searching functions; this gets called when the user
9192 * activates the Search shortcut.
9195 search_activate (GtkFileChooserDefault *impl)
9197 OperationMode previous_mode;
9199 if (impl->operation_mode == OPERATION_MODE_SEARCH)
9201 gtk_widget_grab_focus (impl->search_entry);
9205 previous_mode = impl->operation_mode;
9206 impl->operation_mode = OPERATION_MODE_SEARCH;
9208 switch (previous_mode)
9210 case OPERATION_MODE_RECENT:
9211 recent_stop_loading (impl);
9212 recent_clear_model (impl, TRUE);
9215 case OPERATION_MODE_BROWSE:
9216 stop_loading_and_clear_list_model (impl);
9219 case OPERATION_MODE_SEARCH:
9220 g_assert_not_reached ();
9224 g_assert (impl->search_hbox == NULL);
9225 g_assert (impl->search_entry == NULL);
9226 g_assert (impl->search_model == NULL);
9227 g_assert (impl->search_model_filter == NULL);
9229 search_setup_widgets (impl);
9230 file_list_set_sort_column_ids (impl);
9234 * Recent files support
9237 /* Frees the data in the recent_model */
9239 recent_clear_model (GtkFileChooserDefault *impl,
9240 gboolean remove_from_treeview)
9242 GtkTreeModel *model;
9245 if (!impl->recent_model)
9248 model = GTK_TREE_MODEL (impl->recent_model);
9250 if (remove_from_treeview)
9251 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
9253 if (gtk_tree_model_get_iter_first (model, &iter))
9258 GCancellable *cancellable;
9259 GtkRecentInfo *recent_info;
9260 gchar *display_name;
9262 gtk_tree_model_get (model, &iter,
9263 RECENT_MODEL_COL_DISPLAY_NAME, &display_name,
9264 RECENT_MODEL_COL_FILE, &file,
9265 RECENT_MODEL_COL_CANCELLABLE, &cancellable,
9266 RECENT_MODEL_COL_INFO, &recent_info,
9270 g_cancellable_cancel (cancellable);
9272 g_object_unref (file);
9273 gtk_recent_info_unref (recent_info);
9274 g_free (display_name);
9276 while (gtk_tree_model_iter_next (model, &iter));
9279 g_object_unref (impl->recent_model);
9280 impl->recent_model = NULL;
9282 g_object_unref (impl->recent_model_filter);
9283 impl->recent_model_filter = NULL;
9285 g_object_unref (impl->recent_model_sort);
9286 impl->recent_model_sort = NULL;
9289 /* Stops any ongoing loading of the recent files list; does
9290 * not touch the recent_model
9293 recent_stop_loading (GtkFileChooserDefault *impl)
9295 if (impl->load_recent_id)
9297 g_source_remove (impl->load_recent_id);
9298 impl->load_recent_id = 0;
9302 /* Stops any pending load, clears the file list, and switches
9303 * back to OPERATION_MODE_BROWSE
9306 recent_switch_to_browse_mode (GtkFileChooserDefault *impl)
9308 g_assert (impl->operation_mode != OPERATION_MODE_BROWSE);
9310 recent_stop_loading (impl);
9311 recent_clear_model (impl, TRUE);
9313 gtk_widget_show (impl->browse_path_bar);
9314 gtk_widget_show (impl->browse_new_folder_button);
9316 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
9317 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
9319 gtk_widget_show (impl->location_button);
9321 if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
9322 gtk_widget_show (impl->location_entry_box);
9325 impl->operation_mode = OPERATION_MODE_BROWSE;
9327 file_list_set_sort_column_ids (impl);
9330 /* Sort callback from the modification time column */
9332 recent_column_mtime_sort_func (GtkTreeModel *model,
9337 GtkFileChooserDefault *impl = user_data;
9338 GtkTreeIter child_a, child_b;
9339 GtkRecentInfo *info_a, *info_b;
9340 gboolean is_folder_a, is_folder_b;
9342 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_a, a);
9343 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_b, b);
9345 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_a,
9346 RECENT_MODEL_COL_IS_FOLDER, &is_folder_a,
9347 RECENT_MODEL_COL_INFO, &info_a,
9349 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_b,
9350 RECENT_MODEL_COL_IS_FOLDER, &is_folder_b,
9351 RECENT_MODEL_COL_INFO, &info_b,
9360 /* folders always go first */
9361 if (is_folder_a != is_folder_b)
9362 return is_folder_a ? 1 : -1;
9364 if (gtk_recent_info_get_modified (info_a) < gtk_recent_info_get_modified (info_b))
9366 else if (gtk_recent_info_get_modified (info_a) > gtk_recent_info_get_modified (info_b))
9373 recent_column_path_sort_func (GtkTreeModel *model,
9378 GtkFileChooserDefault *impl = user_data;
9379 GtkTreeIter child_a, child_b;
9380 gboolean is_folder_a, is_folder_b;
9381 gchar *name_a, *name_b;
9383 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_a, a);
9384 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_b, b);
9386 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_a,
9387 RECENT_MODEL_COL_IS_FOLDER, &is_folder_a,
9388 RECENT_MODEL_COL_DISPLAY_NAME, &name_a,
9390 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_b,
9391 RECENT_MODEL_COL_IS_FOLDER, &is_folder_b,
9392 RECENT_MODEL_COL_DISPLAY_NAME, &name_b,
9401 if (is_folder_a != is_folder_b)
9402 return is_folder_a ? 1 : -1;
9404 return strcmp (name_a, name_b);
9408 recent_get_is_filtered (GtkFileChooserDefault *impl,
9410 GtkRecentInfo *recent_info)
9412 GtkFileFilterInfo filter_info;
9413 GtkFileFilterFlags needed;
9416 if (!impl->current_filter)
9419 filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
9420 needed = gtk_file_filter_get_needed (impl->current_filter);
9422 filter_info.display_name = gtk_recent_info_get_display_name (recent_info);
9423 filter_info.mime_type = gtk_recent_info_get_mime_type (recent_info);
9425 if (needed & GTK_FILE_FILTER_FILENAME)
9427 filter_info.filename = g_file_get_path (file);
9428 if (filter_info.filename)
9429 filter_info.contains |= GTK_FILE_FILTER_FILENAME;
9432 filter_info.filename = NULL;
9434 if (needed & GTK_FILE_FILTER_URI)
9436 filter_info.uri = g_file_get_uri (file);
9437 if (filter_info.uri)
9438 filter_info.contains |= GTK_FILE_FILTER_URI;
9441 filter_info.uri = NULL;
9443 result = gtk_file_filter_filter (impl->current_filter, &filter_info);
9445 if (filter_info.filename)
9446 g_free ((gchar *) filter_info.filename);
9447 if (filter_info.uri)
9448 g_free ((gchar *) filter_info.uri);
9453 /* Visibility function for the recent filter model */
9455 recent_model_visible_func (GtkTreeModel *model,
9459 GtkFileChooserDefault *impl = user_data;
9461 GtkRecentInfo *recent_info;
9464 if (!impl->current_filter)
9467 gtk_tree_model_get (model, iter,
9468 RECENT_MODEL_COL_INFO, &recent_info,
9469 RECENT_MODEL_COL_FILE, &file,
9470 RECENT_MODEL_COL_IS_FOLDER, &is_folder,
9479 return !recent_get_is_filtered (impl, file, recent_info);
9483 recent_setup_model (GtkFileChooserDefault *impl)
9485 g_assert (impl->recent_model == NULL);
9486 g_assert (impl->recent_model_filter == NULL);
9487 g_assert (impl->recent_model_sort == NULL);
9489 /* We store these columns in the search model:
9491 * RECENT_MODEL_COL_FILE - a pointer to GFile for the hit's URI,
9492 * stored as a pointer and not as a G_TYPE_FILE;
9493 * RECENT_MODEL_COL_DISPLAY_NAME - a string with the display name,
9494 * stored as a pointer and not as a G_TYPE_STRING;
9495 * RECENT_MODEL_COL_INFO - GtkRecentInfo, stored as a pointer and not
9496 * as a GTK_TYPE_RECENT_INFO;
9497 * RECENT_MODEL_COL_IS_FOLDER - boolean flag;
9498 * RECENT_MODEL_COL_CANCELLABLE - GCancellable, stored as a pointer
9499 * and not as a G_TYPE_CANCELLABLE;
9501 * Keep this in sync with the enumeration defined near the beginning of
9504 impl->recent_model = gtk_list_store_new (RECENT_MODEL_COL_NUM_COLUMNS,
9511 impl->recent_model_filter =
9512 GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (GTK_TREE_MODEL (impl->recent_model), NULL));
9513 gtk_tree_model_filter_set_visible_func (impl->recent_model_filter,
9514 recent_model_visible_func,
9518 /* this is the model that will actually be added to
9519 * the browse_files_tree_view widget; remember: we are
9520 * stuffing the real model into a filter model and then
9521 * into a sort model; this means we'll have to translate
9522 * the child iterator *twice* to get from a path or an
9523 * iterator coming from the tree view widget to the
9524 * real data inside the model.
9526 impl->recent_model_sort =
9527 GTK_TREE_MODEL_SORT (recent_model_sort_new (impl, GTK_TREE_MODEL (impl->recent_model_filter)));
9528 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model_sort),
9529 RECENT_MODEL_COL_FILE,
9530 recent_column_path_sort_func,
9532 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model_sort),
9533 RECENT_MODEL_COL_INFO,
9534 recent_column_mtime_sort_func,
9536 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->recent_model_sort),
9537 RECENT_MODEL_COL_INFO,
9538 GTK_SORT_DESCENDING);
9543 GtkFileChooserDefault *impl;
9546 gint n_loaded_items;
9547 guint needs_sorting : 1;
9551 recent_idle_cleanup (gpointer data)
9553 RecentLoadData *load_data = data;
9554 GtkFileChooserDefault *impl = load_data->impl;
9556 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
9557 GTK_TREE_MODEL (impl->recent_model_sort));
9559 set_busy_cursor (impl, FALSE);
9561 impl->load_recent_id = 0;
9563 if (load_data->items)
9565 g_list_foreach (load_data->items, (GFunc) gtk_recent_info_unref, NULL);
9566 g_list_free (load_data->items);
9572 struct RecentItemInsertRequest
9574 GtkFileChooserDefault *impl;
9576 GtkTreeRowReference *row_ref;
9580 recent_item_get_info_cb (GCancellable *cancellable,
9582 const GError *error,
9585 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
9588 GCancellable *model_cancellable;
9589 gboolean is_folder = FALSE;
9590 struct RecentItemInsertRequest *request = data;
9592 if (!request->impl->recent_model)
9595 path = gtk_tree_row_reference_get_path (request->row_ref);
9599 gtk_tree_model_get_iter (GTK_TREE_MODEL (request->impl->recent_model),
9601 gtk_tree_path_free (path);
9603 gtk_tree_model_get (GTK_TREE_MODEL (request->impl->recent_model), &iter,
9604 RECENT_MODEL_COL_CANCELLABLE, &model_cancellable,
9606 if (cancellable != model_cancellable)
9609 gtk_list_store_set (request->impl->recent_model, &iter,
9610 RECENT_MODEL_COL_CANCELLABLE, NULL,
9618 gtk_list_store_remove (request->impl->recent_model, &iter);
9622 is_folder = (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY);
9624 gtk_list_store_set (request->impl->recent_model, &iter,
9625 RECENT_MODEL_COL_IS_FOLDER, is_folder,
9629 g_object_unref (request->impl);
9630 g_object_unref (request->file);
9631 gtk_tree_row_reference_free (request->row_ref);
9634 g_object_unref (cancellable);
9638 recent_sort_mru (gconstpointer a,
9641 GtkRecentInfo *info_a = (GtkRecentInfo *) a;
9642 GtkRecentInfo *info_b = (GtkRecentInfo *) b;
9644 return (gtk_recent_info_get_modified (info_b) - gtk_recent_info_get_modified (info_a));
9648 get_recent_files_limit (GtkWidget *widget)
9650 GtkSettings *settings;
9653 if (gtk_widget_has_screen (widget))
9654 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (widget));
9656 settings = gtk_settings_get_default ();
9658 g_object_get (G_OBJECT (settings), "gtk-recent-files-limit", &limit, NULL);
9664 recent_idle_load (gpointer data)
9666 RecentLoadData *load_data = data;
9667 GtkFileChooserDefault *impl = load_data->impl;
9670 GtkRecentInfo *info;
9671 const gchar *uri, *display_name;
9673 GCancellable *cancellable;
9674 struct RecentItemInsertRequest *request;
9676 if (!impl->recent_manager)
9679 /* first iteration: load all the items */
9680 if (!load_data->items)
9682 load_data->items = gtk_recent_manager_get_items (impl->recent_manager);
9683 if (!load_data->items)
9686 load_data->needs_sorting = TRUE;
9691 /* second iteration: preliminary MRU sorting and clamping */
9692 if (load_data->needs_sorting)
9696 load_data->items = g_list_sort (load_data->items, recent_sort_mru);
9697 load_data->n_items = g_list_length (load_data->items);
9699 limit = get_recent_files_limit (GTK_WIDGET (impl));
9701 if (limit != -1 && (load_data->n_items > limit))
9705 clamp = g_list_nth (load_data->items, limit - 1);
9706 if (G_LIKELY (clamp))
9711 g_list_foreach (l, (GFunc) gtk_recent_info_unref, NULL);
9714 load_data->n_items = limit;
9718 load_data->n_loaded_items = 0;
9719 load_data->needs_sorting = FALSE;
9724 info = g_list_nth_data (load_data->items, load_data->n_loaded_items);
9725 g_assert (info != NULL);
9727 uri = gtk_recent_info_get_uri (info);
9728 display_name = gtk_recent_info_get_display_name (info);
9729 file = g_file_new_for_uri (uri);
9731 gtk_list_store_append (impl->recent_model, &iter);
9732 p = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->recent_model), &iter);
9734 request = g_new0 (struct RecentItemInsertRequest, 1);
9735 request->impl = g_object_ref (impl);
9736 request->file = g_object_ref (file);
9737 request->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->recent_model), p);
9738 gtk_tree_path_free (p);
9740 cancellable = _gtk_file_system_get_info (impl->file_system, file,
9742 recent_item_get_info_cb,
9745 gtk_list_store_set (impl->recent_model, &iter,
9746 RECENT_MODEL_COL_FILE, file,
9747 RECENT_MODEL_COL_DISPLAY_NAME, g_strdup (display_name),
9748 RECENT_MODEL_COL_INFO, gtk_recent_info_ref (info),
9749 RECENT_MODEL_COL_CANCELLABLE, cancellable,
9752 load_data->n_loaded_items += 1;
9754 /* finished loading items */
9755 if (load_data->n_loaded_items == load_data->n_items)
9757 g_list_foreach (load_data->items, (GFunc) gtk_recent_info_unref, NULL);
9758 g_list_free (load_data->items);
9759 load_data->items = NULL;
9768 recent_start_loading (GtkFileChooserDefault *impl)
9770 RecentLoadData *load_data;
9772 recent_stop_loading (impl);
9773 recent_clear_model (impl, TRUE);
9774 recent_setup_model (impl);
9775 set_busy_cursor (impl, TRUE);
9777 g_assert (impl->load_recent_id == 0);
9779 load_data = g_new (RecentLoadData, 1);
9780 load_data->impl = impl;
9781 load_data->items = NULL;
9782 load_data->n_items = 0;
9783 load_data->n_loaded_items = 0;
9784 load_data->needs_sorting = TRUE;
9786 /* begin lazy loading the recent files into the model */
9787 impl->load_recent_id = gdk_threads_add_idle_full (G_PRIORITY_HIGH_IDLE + 30,
9790 recent_idle_cleanup);
9794 recent_selected_foreach_get_file_cb (GtkTreeModel *model,
9804 gtk_tree_model_get (model, iter, RECENT_MODEL_COL_FILE, &file, -1);
9805 *list = g_slist_prepend (*list, g_object_ref (file));
9808 /* Constructs a list of the selected paths in recent files mode */
9810 recent_get_selected_files (GtkFileChooserDefault *impl)
9813 GtkTreeSelection *selection;
9817 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
9818 gtk_tree_selection_selected_foreach (selection, recent_selected_foreach_get_file_cb, &result);
9819 result = g_slist_reverse (result);
9824 /* Called from ::should_respond(). We return whether there are selected
9825 * files in the recent files list.
9828 recent_should_respond (GtkFileChooserDefault *impl)
9830 GtkTreeSelection *selection;
9832 g_assert (impl->operation_mode == OPERATION_MODE_RECENT);
9834 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
9835 return (gtk_tree_selection_count_selected_rows (selection) != 0);
9838 /* Hide the location widgets temporarily */
9840 recent_hide_entry (GtkFileChooserDefault *impl)
9842 gtk_widget_hide (impl->browse_path_bar);
9843 gtk_widget_hide (impl->browse_new_folder_button);
9845 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
9846 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
9848 gtk_widget_hide (impl->location_button);
9849 gtk_widget_hide (impl->location_entry_box);
9853 /* Main entry point to the recent files functions; this gets called when
9854 * the user activates the Recently Used shortcut.
9857 recent_activate (GtkFileChooserDefault *impl)
9859 OperationMode previous_mode;
9861 if (impl->operation_mode == OPERATION_MODE_RECENT)
9864 previous_mode = impl->operation_mode;
9865 impl->operation_mode = OPERATION_MODE_RECENT;
9867 switch (previous_mode)
9869 case OPERATION_MODE_SEARCH:
9870 search_stop_searching (impl, FALSE);
9871 search_clear_model (impl, TRUE);
9873 gtk_widget_destroy (impl->search_hbox);
9874 impl->search_hbox = NULL;
9875 impl->search_entry = NULL;
9878 case OPERATION_MODE_BROWSE:
9879 stop_loading_and_clear_list_model (impl);
9882 case OPERATION_MODE_RECENT:
9883 g_assert_not_reached ();
9887 recent_hide_entry (impl);
9888 file_list_set_sort_column_ids (impl);
9889 recent_start_loading (impl);
9892 /* convert an iterator coming from the model bound to
9893 * browse_files_tree_view to an interator inside the
9897 recent_get_valid_child_iter (GtkFileChooserDefault *impl,
9898 GtkTreeIter *child_iter,
9903 if (!impl->recent_model)
9906 if (!impl->recent_model_filter || !impl->recent_model_sort)
9909 /* pass 1: get the iterator in the filter model */
9910 gtk_tree_model_sort_convert_iter_to_child_iter (impl->recent_model_sort,
9913 /* pass 2: get the iterator in the real model */
9914 gtk_tree_model_filter_convert_iter_to_child_iter (impl->recent_model_filter,
9921 set_current_filter (GtkFileChooserDefault *impl,
9922 GtkFileFilter *filter)
9924 if (impl->current_filter != filter)
9928 /* NULL filters are allowed to reset to non-filtered status
9930 filter_index = g_slist_index (impl->filters, filter);
9931 if (impl->filters && filter && filter_index < 0)
9934 if (impl->current_filter)
9935 g_object_unref (impl->current_filter);
9936 impl->current_filter = filter;
9937 if (impl->current_filter)
9939 g_object_ref_sink (impl->current_filter);
9943 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
9946 if (impl->browse_files_model)
9947 install_list_model_filter (impl);
9949 if (impl->search_model_filter)
9950 gtk_tree_model_filter_refilter (impl->search_model_filter);
9952 if (impl->recent_model_filter)
9953 gtk_tree_model_filter_refilter (impl->recent_model_filter);
9955 g_object_notify (G_OBJECT (impl), "filter");
9960 filter_combo_changed (GtkComboBox *combo_box,
9961 GtkFileChooserDefault *impl)
9963 gint new_index = gtk_combo_box_get_active (combo_box);
9964 GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
9966 set_current_filter (impl, new_filter);
9970 check_preview_change (GtkFileChooserDefault *impl)
9972 GtkTreePath *cursor_path;
9974 const char *new_display_name;
9976 gtk_tree_view_get_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), &cursor_path, NULL);
9978 new_display_name = NULL;
9981 GtkTreeIter child_iter;
9983 if (impl->operation_mode == OPERATION_MODE_BROWSE)
9985 if (impl->sort_model)
9988 GFileInfo *new_info;
9990 gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, cursor_path);
9991 gtk_tree_path_free (cursor_path);
9993 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
9995 new_file = _gtk_file_system_model_get_file (impl->browse_files_model, &child_iter);
9996 new_info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
9998 new_display_name = g_file_info_get_display_name (new_info);
10001 else if (impl->operation_mode == OPERATION_MODE_SEARCH)
10005 gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->search_model_sort),
10006 &iter, cursor_path);
10007 gtk_tree_path_free (cursor_path);
10009 search_get_valid_child_iter (impl, &child_iter, &iter);
10010 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter,
10011 SEARCH_MODEL_COL_FILE, &new_file,
10012 SEARCH_MODEL_COL_DISPLAY_NAME, &new_display_name,
10015 else if (impl->operation_mode == OPERATION_MODE_RECENT)
10019 gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->recent_model_sort),
10020 &iter, cursor_path);
10021 gtk_tree_path_free (cursor_path);
10023 recent_get_valid_child_iter (impl, &child_iter, &iter);
10024 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter,
10025 RECENT_MODEL_COL_FILE, &new_file,
10026 RECENT_MODEL_COL_DISPLAY_NAME, &new_display_name,
10031 if (new_file != impl->preview_file &&
10032 !(new_file && impl->preview_file &&
10033 g_file_equal (new_file, impl->preview_file)))
10035 if (impl->preview_file)
10037 g_object_unref (impl->preview_file);
10038 g_free (impl->preview_display_name);
10043 impl->preview_file = g_object_ref (new_file);
10044 impl->preview_display_name = g_strdup (new_display_name);
10048 impl->preview_file = NULL;
10049 impl->preview_display_name = NULL;
10052 if (impl->use_preview_label && impl->preview_label)
10053 gtk_label_set_text (GTK_LABEL (impl->preview_label), impl->preview_display_name);
10055 g_signal_emit_by_name (impl, "update-preview");
10060 shortcuts_activate_volume_mount_cb (GCancellable *cancellable,
10061 GtkFileSystemVolume *volume,
10062 const GError *error,
10066 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
10067 GtkFileChooserDefault *impl = data;
10069 if (cancellable != impl->shortcuts_activate_iter_cancellable)
10072 impl->shortcuts_activate_iter_cancellable = NULL;
10074 set_busy_cursor (impl, FALSE);
10081 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED))
10085 name = _gtk_file_system_volume_get_display_name (volume);
10086 msg = g_strdup_printf (_("Could not mount %s"), name);
10088 error_message (impl, msg, error->message);
10097 file = _gtk_file_system_volume_get_root (volume);
10100 change_folder_and_display_error (impl, file, FALSE);
10101 g_object_unref (file);
10105 g_object_unref (impl);
10106 g_object_unref (cancellable);
10110 /* Activates a volume by mounting it if necessary and then switching to its
10114 shortcuts_activate_volume (GtkFileChooserDefault *impl,
10115 GtkFileSystemVolume *volume)
10119 switch (impl->operation_mode)
10121 case OPERATION_MODE_BROWSE:
10123 case OPERATION_MODE_SEARCH:
10124 search_switch_to_browse_mode (impl);
10126 case OPERATION_MODE_RECENT:
10127 recent_switch_to_browse_mode (impl);
10131 /* We ref the file chooser since volume_mount() may run a main loop, and the
10132 * user could close the file chooser window in the meantime.
10134 g_object_ref (impl);
10136 if (!_gtk_file_system_volume_is_mounted (volume))
10138 set_busy_cursor (impl, TRUE);
10140 impl->shortcuts_activate_iter_cancellable =
10141 _gtk_file_system_mount_volume (impl->file_system, volume, NULL,
10142 shortcuts_activate_volume_mount_cb,
10143 g_object_ref (impl));
10147 file = _gtk_file_system_volume_get_root (volume);
10150 change_folder_and_display_error (impl, file, FALSE);
10151 g_object_unref (file);
10155 g_object_unref (impl);
10158 /* Opens the folder or volume at the specified iter in the shortcuts model */
10159 struct ShortcutsActivateData
10161 GtkFileChooserDefault *impl;
10166 shortcuts_activate_get_info_cb (GCancellable *cancellable,
10168 const GError *error,
10169 gpointer user_data)
10171 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
10172 struct ShortcutsActivateData *data = user_data;
10174 if (cancellable != data->impl->shortcuts_activate_iter_cancellable)
10177 data->impl->shortcuts_activate_iter_cancellable = NULL;
10182 if (!error && g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
10183 change_folder_and_display_error (data->impl, data->file, FALSE);
10185 gtk_file_chooser_default_select_file (GTK_FILE_CHOOSER (data->impl),
10190 g_object_unref (data->impl);
10191 g_object_unref (data->file);
10194 g_object_unref (cancellable);
10198 shortcuts_activate_mount_enclosing_volume (GCancellable *cancellable,
10199 GtkFileSystemVolume *volume,
10200 const GError *error,
10201 gpointer user_data)
10203 struct ShortcutsActivateData *data = user_data;
10207 error_changing_folder_dialog (data->impl, data->file, g_error_copy (error));
10209 g_object_unref (data->impl);
10210 g_object_unref (data->file);
10216 data->impl->shortcuts_activate_iter_cancellable =
10217 _gtk_file_system_get_info (data->impl->file_system, data->file,
10219 shortcuts_activate_get_info_cb, data);
10223 shortcuts_activate_iter (GtkFileChooserDefault *impl,
10227 ShortcutType shortcut_type;
10229 if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY
10230 && !(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
10231 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
10232 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
10234 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
10235 SHORTCUTS_COL_DATA, &col_data,
10236 SHORTCUTS_COL_TYPE, &shortcut_type,
10239 if (impl->shortcuts_activate_iter_cancellable)
10241 g_cancellable_cancel (impl->shortcuts_activate_iter_cancellable);
10242 impl->shortcuts_activate_iter_cancellable = NULL;
10245 if (shortcut_type == SHORTCUT_TYPE_SEPARATOR)
10247 else if (shortcut_type == SHORTCUT_TYPE_VOLUME)
10249 GtkFileSystemVolume *volume;
10253 shortcuts_activate_volume (impl, volume);
10255 else if (shortcut_type == SHORTCUT_TYPE_FILE)
10257 struct ShortcutsActivateData *data;
10258 GtkFileSystemVolume *volume;
10260 volume = _gtk_file_system_get_volume_for_file (impl->file_system, col_data);
10262 data = g_new0 (struct ShortcutsActivateData, 1);
10263 data->impl = g_object_ref (impl);
10264 data->file = g_object_ref (col_data);
10266 if (!volume || !_gtk_file_system_volume_is_mounted (volume))
10268 GMountOperation *mount_operation;
10269 GtkWidget *toplevel;
10271 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
10273 mount_operation = gtk_mount_operation_new (GTK_WINDOW (toplevel));
10275 impl->shortcuts_activate_iter_cancellable =
10276 _gtk_file_system_mount_enclosing_volume (impl->file_system, col_data,
10278 shortcuts_activate_mount_enclosing_volume,
10283 impl->shortcuts_activate_iter_cancellable =
10284 _gtk_file_system_get_info (impl->file_system, data->file,
10286 shortcuts_activate_get_info_cb, data);
10289 else if (shortcut_type == SHORTCUT_TYPE_SEARCH)
10291 search_activate (impl);
10293 else if (shortcut_type == SHORTCUT_TYPE_RECENT)
10295 recent_activate (impl);
10299 /* Handler for GtkWidget::key-press-event on the shortcuts list */
10301 shortcuts_key_press_event_cb (GtkWidget *widget,
10302 GdkEventKey *event,
10303 GtkFileChooserDefault *impl)
10307 modifiers = gtk_accelerator_get_default_mod_mask ();
10309 if ((event->keyval == GDK_BackSpace
10310 || event->keyval == GDK_Delete
10311 || event->keyval == GDK_KP_Delete)
10312 && (event->state & modifiers) == 0)
10314 remove_selected_bookmarks (impl);
10318 if ((event->keyval == GDK_F2)
10319 && (event->state & modifiers) == 0)
10321 rename_selected_bookmark (impl);
10329 shortcuts_select_func (GtkTreeSelection *selection,
10330 GtkTreeModel *model,
10332 gboolean path_currently_selected,
10335 GtkFileChooserDefault *impl = data;
10336 GtkTreeIter filter_iter;
10337 ShortcutType shortcut_type;
10339 if (!gtk_tree_model_get_iter (impl->shortcuts_pane_filter_model, &filter_iter, path))
10340 g_assert_not_reached ();
10342 gtk_tree_model_get (impl->shortcuts_pane_filter_model, &filter_iter, SHORTCUTS_COL_TYPE, &shortcut_type, -1);
10344 return shortcut_type != SHORTCUT_TYPE_SEPARATOR;
10348 list_select_func (GtkTreeSelection *selection,
10349 GtkTreeModel *model,
10351 gboolean path_currently_selected,
10354 GtkFileChooserDefault *impl = data;
10356 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
10357 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
10359 GtkTreeIter iter, child_iter;
10361 switch (impl->operation_mode)
10363 case OPERATION_MODE_SEARCH:
10365 gboolean is_folder;
10367 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->search_model_sort), &iter, path))
10370 search_get_valid_child_iter (impl, &child_iter, &iter);
10371 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter,
10372 SEARCH_MODEL_COL_IS_FOLDER, &is_folder,
10379 case OPERATION_MODE_RECENT:
10381 gboolean is_folder;
10383 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->recent_model_sort), &iter, path))
10386 recent_get_valid_child_iter (impl, &child_iter, &iter);
10387 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter,
10388 RECENT_MODEL_COL_IS_FOLDER, &is_folder,
10395 case OPERATION_MODE_BROWSE:
10399 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
10402 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
10403 info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
10404 if (info && g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY)
10415 list_selection_changed (GtkTreeSelection *selection,
10416 GtkFileChooserDefault *impl)
10418 /* See if we are in the new folder editable row for Save mode */
10419 if (impl->operation_mode == OPERATION_MODE_BROWSE &&
10420 impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
10423 gboolean had_selection;
10425 info = get_selected_file_info_from_file_list (impl, &had_selection);
10426 if (!had_selection)
10427 goto out; /* normal processing */
10430 return; /* We are on the editable row for New Folder */
10435 if (impl->location_entry)
10436 update_chooser_entry (impl);
10438 check_preview_change (impl);
10439 bookmarks_check_add_sensitivity (impl);
10441 g_signal_emit_by_name (impl, "selection-changed", 0);
10444 /* Callback used when a row in the file list is activated */
10446 list_row_activated (GtkTreeView *tree_view,
10448 GtkTreeViewColumn *column,
10449 GtkFileChooserDefault *impl)
10452 GtkTreeIter child_iter;
10454 switch (impl->operation_mode)
10456 case OPERATION_MODE_SEARCH:
10459 gboolean is_folder;
10461 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->search_model_sort), &iter, path))
10464 search_get_valid_child_iter (impl, &child_iter, &iter);
10465 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter,
10466 SEARCH_MODEL_COL_FILE, &file,
10467 SEARCH_MODEL_COL_IS_FOLDER, &is_folder,
10472 change_folder_and_display_error (impl, file, FALSE);
10476 g_signal_emit_by_name (impl, "file-activated");
10480 case OPERATION_MODE_RECENT:
10483 gboolean is_folder;
10485 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->recent_model_sort), &iter, path))
10488 recent_get_valid_child_iter (impl, &child_iter, &iter);
10489 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter,
10490 RECENT_MODEL_COL_FILE, &file,
10491 RECENT_MODEL_COL_IS_FOLDER, &is_folder,
10496 change_folder_and_display_error (impl, file, FALSE);
10500 g_signal_emit_by_name (impl, "file-activated");
10504 case OPERATION_MODE_BROWSE:
10508 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
10511 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
10512 &child_iter, &iter);
10513 info = _gtk_file_system_model_get_info (impl->browse_files_model,
10516 if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
10520 file = _gtk_file_system_model_get_file (impl->browse_files_model, &child_iter);
10521 change_folder_and_display_error (impl, file, FALSE);
10525 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
10526 impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
10527 g_signal_emit_by_name (impl, "file-activated");
10534 path_bar_clicked (GtkPathBar *path_bar,
10537 gboolean child_is_hidden,
10538 GtkFileChooserDefault *impl)
10541 pending_select_files_add (impl, child_file);
10543 if (!change_folder_and_display_error (impl, file, FALSE))
10546 /* Say we have "/foo/bar/[.baz]" and the user clicks on "bar". We should then
10547 * show hidden files so that ".baz" appears in the file list, as it will still
10548 * be shown in the path bar: "/foo/[bar]/.baz"
10550 if (child_is_hidden)
10551 g_object_set (impl, "show-hidden", TRUE, NULL);
10555 get_list_file_info (GtkFileChooserDefault *impl,
10558 GtkTreeIter child_iter;
10560 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
10564 return _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
10568 list_icon_data_func (GtkTreeViewColumn *tree_column,
10569 GtkCellRenderer *cell,
10570 GtkTreeModel *tree_model,
10574 GtkFileChooserDefault *impl = data;
10575 GtkTreeIter child_iter;
10576 GdkPixbuf *pixbuf = NULL;
10577 gboolean sensitive = TRUE;
10579 profile_start ("start", NULL);
10581 switch (impl->operation_mode)
10583 case OPERATION_MODE_SEARCH:
10585 GtkTreeIter child_iter;
10586 gboolean is_folder;
10588 search_get_valid_child_iter (impl, &child_iter, iter);
10589 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter,
10590 SEARCH_MODEL_COL_PIXBUF, &pixbuf,
10591 SEARCH_MODEL_COL_IS_FOLDER, &is_folder,
10594 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
10595 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
10596 sensitive = is_folder;
10600 case OPERATION_MODE_RECENT:
10602 GtkTreeIter child_iter;
10603 GtkRecentInfo *info;
10604 gboolean is_folder;
10606 recent_get_valid_child_iter (impl, &child_iter, iter);
10607 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter,
10608 RECENT_MODEL_COL_INFO, &info,
10609 RECENT_MODEL_COL_IS_FOLDER, &is_folder,
10612 pixbuf = gtk_recent_info_get_icon (info, impl->icon_size);
10614 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
10615 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
10616 sensitive = is_folder;
10620 case OPERATION_MODE_BROWSE:
10625 info = get_list_file_info (impl, iter);
10627 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
10630 file = _gtk_file_system_model_get_file (impl->browse_files_model, &child_iter);
10635 /* FIXME: NULL GError */
10636 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (impl), impl->icon_size);
10641 /* We are on the editable row */
10646 (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
10647 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
10648 sensitive = (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY);
10653 g_object_set (cell,
10655 "sensitive", sensitive,
10659 g_object_unref (pixbuf);
10661 profile_end ("end", NULL);
10665 list_name_data_func (GtkTreeViewColumn *tree_column,
10666 GtkCellRenderer *cell,
10667 GtkTreeModel *tree_model,
10671 GtkFileChooserDefault *impl = data;
10673 gboolean sensitive = TRUE;
10675 if (impl->operation_mode == OPERATION_MODE_SEARCH)
10677 GtkTreeIter child_iter;
10678 gchar *display_name;
10679 gboolean is_folder;
10681 search_get_valid_child_iter (impl, &child_iter, iter);
10682 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter,
10683 SEARCH_MODEL_COL_DISPLAY_NAME, &display_name,
10684 SEARCH_MODEL_COL_IS_FOLDER, &is_folder,
10687 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
10688 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
10690 sensitive = is_folder;
10693 g_object_set (cell,
10694 "text", display_name,
10695 "sensitive", sensitive,
10696 "ellipsize", PANGO_ELLIPSIZE_END,
10702 if (impl->operation_mode == OPERATION_MODE_RECENT)
10704 GtkTreeIter child_iter;
10705 GtkRecentInfo *recent_info;
10706 gchar *display_name;
10707 gboolean is_folder;
10709 recent_get_valid_child_iter (impl, &child_iter, iter);
10710 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter,
10711 RECENT_MODEL_COL_INFO, &recent_info,
10712 RECENT_MODEL_COL_IS_FOLDER, &is_folder,
10715 display_name = gtk_recent_info_get_short_name (recent_info);
10717 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
10718 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
10720 sensitive = is_folder;
10723 g_object_set (cell,
10724 "text", display_name,
10725 "sensitive", sensitive,
10726 "ellipsize", PANGO_ELLIPSIZE_END,
10729 g_free (display_name);
10734 info = get_list_file_info (impl, iter);
10739 g_object_set (cell,
10740 "text", _("Type name of new folder"),
10742 "ellipsize", PANGO_ELLIPSIZE_NONE,
10749 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
10750 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
10752 sensitive = (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY);
10755 g_object_set (cell,
10756 "text", g_file_info_get_display_name (info),
10757 "sensitive", sensitive,
10758 "ellipsize", PANGO_ELLIPSIZE_END,
10764 list_size_data_func (GtkTreeViewColumn *tree_column,
10765 GtkCellRenderer *cell,
10766 GtkTreeModel *tree_model,
10770 GtkFileChooserDefault *impl = data;
10771 GFileInfo *info = get_list_file_info (impl, iter);
10774 gboolean sensitive = TRUE;
10776 if (!info || g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
10778 g_object_set (cell,
10780 "sensitive", sensitive,
10785 size = gtk_file_info_get_size (info);
10787 if (size < (gint64)1024)
10788 str = g_strdup_printf (g_dngettext (GETTEXT_DOMAIN, "%d byte", "%d bytes", (gint)size), (gint)size);
10789 else if (size < (gint64)1024*1024)
10790 str = g_strdup_printf (_("%.1f KB"), size / (1024.));
10791 else if (size < (gint64)1024*1024*1024)
10792 str = g_strdup_printf (_("%.1f MB"), size / (1024.*1024.));
10794 str = g_strdup_printf (_("%.1f GB"), size / (1024.*1024.*1024.));
10796 str = g_strdup_printf ("%" G_GINT64_FORMAT, size);
10797 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
10798 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
10801 g_object_set (cell,
10803 "sensitive", sensitive,
10804 "alignment", PANGO_ALIGN_RIGHT,
10811 /* Tree column data callback for the file list; fetches the mtime of a file */
10813 list_mtime_data_func (GtkTreeViewColumn *tree_column,
10814 GtkCellRenderer *cell,
10815 GtkTreeModel *tree_model,
10819 GtkFileChooserDefault *impl;
10820 GTimeVal timeval = { 0, };
10822 gchar *date_str = NULL;
10823 gboolean sensitive = TRUE;
10825 const char *locale, *dot = NULL;
10826 gint64 codepage = -1;
10832 if (impl->operation_mode == OPERATION_MODE_SEARCH)
10834 GtkTreeIter child_iter;
10835 struct stat *statbuf;
10836 gboolean is_folder;
10838 search_get_valid_child_iter (impl, &child_iter, iter);
10839 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter,
10840 SEARCH_MODEL_COL_STAT, &statbuf,
10841 SEARCH_MODEL_COL_IS_FOLDER, &is_folder,
10844 time_mtime = statbuf->st_mtime;
10849 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
10850 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
10851 sensitive = is_folder;
10853 else if (impl->operation_mode == OPERATION_MODE_RECENT)
10855 GtkTreeIter child_iter;
10856 GtkRecentInfo *info;
10857 gboolean is_folder;
10859 recent_get_valid_child_iter (impl, &child_iter, iter);
10860 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter,
10861 RECENT_MODEL_COL_INFO, &info,
10862 RECENT_MODEL_COL_IS_FOLDER, &is_folder,
10866 time_mtime = gtk_recent_info_get_modified (info);
10870 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
10871 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
10872 sensitive = is_folder;
10878 info = get_list_file_info (impl, iter);
10881 g_object_set (cell,
10888 g_file_info_get_modification_time (info, &timeval);
10889 time_mtime = timeval.tv_sec;
10891 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
10892 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
10893 sensitive = (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY);
10896 if (G_UNLIKELY (time_mtime == 0))
10897 date_str = g_strdup (_("Unknown"));
10902 struct tm tm_mtime;
10904 const gchar *format;
10905 gchar *locale_format = NULL;
10908 #ifdef HAVE_LOCALTIME_R
10909 localtime_r ((time_t *) &time_mtime, &tm_mtime);
10912 struct tm *ptm = localtime ((time_t *) &timeval.tv_sec);
10916 g_warning ("ptm != NULL failed");
10918 g_object_set (cell,
10919 "text", _("Unknown"),
10920 "sensitive", sensitive,
10925 memcpy ((void *) &tm_mtime, (void *) ptm, sizeof (struct tm));
10927 #endif /* HAVE_LOCALTIME_R */
10929 g_date_set_time_t (&mtime, time_mtime);
10930 time_now = time (NULL);
10931 g_date_set_time_t (&now, time_now);
10933 days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
10935 /* Translators: %H means "hours" and %M means "minutes" */
10936 if (days_diff == 0)
10937 format = _("%H:%M");
10938 else if (days_diff == 1)
10939 format = _("Yesterday at %H:%M");
10942 if (days_diff > 1 && days_diff < 7)
10943 format = "%A"; /* Days from last week */
10945 format = "%x"; /* Any other date */
10949 /* g_locale_from_utf8() returns a string in the system
10950 * code-page, which is not always the same as that used by the C
10951 * library. For instance when running a GTK+ program with
10952 * LANG=ko on an English version of Windows, the system
10953 * code-page is 1252, but the code-page used by the C library is
10954 * 949. (It's GTK+ itself that sets the C library locale when it
10955 * notices the LANG environment variable. See gtkmain.c The
10956 * Microsoft C library doesn't look at any locale environment
10957 * variables.) We need to pass strftime() a string in the C
10958 * library's code-page. See bug #509885.
10960 locale = setlocale (LC_ALL, NULL);
10961 if (locale != NULL)
10962 dot = strchr (locale, '.');
10965 codepage = g_ascii_strtoll (dot+1, NULL, 10);
10967 /* All codepages should fit in 16 bits AFAIK */
10968 if (codepage > 0 && codepage < 65536)
10970 sprintf (charset, "CP%u", (guint) codepage);
10971 locale_format = g_convert (format, -1, charset, "UTF-8", NULL, NULL, NULL);
10975 locale_format = g_locale_from_utf8 (format, -1, NULL, NULL, NULL);
10977 if (locale_format != NULL &&
10978 strftime (buf, sizeof (buf), locale_format, &tm_mtime) != 0)
10981 /* As above but in opposite direction... */
10982 if (codepage > 0 && codepage < 65536)
10983 date_str = g_convert (buf, -1, "UTF-8", charset, NULL, NULL, NULL);
10985 date_str = g_locale_to_utf8 (buf, -1, NULL, NULL, NULL);
10989 if (date_str == NULL)
10990 date_str = g_strdup (_("Unknown"));
10992 g_free (locale_format);
10995 g_object_set (cell,
10997 "sensitive", sensitive,
11003 _gtk_file_chooser_default_new (const char *file_system)
11005 return g_object_new (GTK_TYPE_FILE_CHOOSER_DEFAULT,
11006 "file-system-backend", file_system,
11011 location_set_user_text (GtkFileChooserDefault *impl,
11014 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), path);
11015 gtk_editable_set_position (GTK_EDITABLE (impl->location_entry), -1);
11019 location_popup_handler (GtkFileChooserDefault *impl,
11022 if (impl->operation_mode != OPERATION_MODE_BROWSE)
11024 GtkWidget *widget_to_focus;
11026 /* This will give us the location widgets back */
11027 switch (impl->operation_mode)
11029 case OPERATION_MODE_SEARCH:
11030 search_switch_to_browse_mode (impl);
11032 case OPERATION_MODE_RECENT:
11033 recent_switch_to_browse_mode (impl);
11035 case OPERATION_MODE_BROWSE:
11036 g_assert_not_reached ();
11040 if (impl->current_folder)
11041 change_folder_and_display_error (impl, impl->current_folder, FALSE);
11043 if (impl->location_mode == LOCATION_MODE_PATH_BAR)
11044 widget_to_focus = impl->browse_files_tree_view;
11046 widget_to_focus = impl->location_entry;
11048 gtk_widget_grab_focus (widget_to_focus);
11052 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
11053 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
11055 LocationMode new_mode;
11059 /* since the user typed something, we unconditionally want to turn on the entry */
11060 new_mode = LOCATION_MODE_FILENAME_ENTRY;
11062 else if (impl->location_mode == LOCATION_MODE_PATH_BAR)
11063 new_mode = LOCATION_MODE_FILENAME_ENTRY;
11064 else if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
11065 new_mode = LOCATION_MODE_PATH_BAR;
11068 g_assert_not_reached ();
11072 location_mode_set (impl, new_mode, TRUE);
11073 if (new_mode == LOCATION_MODE_FILENAME_ENTRY)
11076 location_set_user_text (impl, path);
11079 location_entry_set_initial_text (impl);
11080 gtk_editable_select_region (GTK_EDITABLE (impl->location_entry), 0, -1);
11084 else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
11085 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
11087 gtk_widget_grab_focus (impl->location_entry);
11089 location_set_user_text (impl, path);
11092 g_assert_not_reached ();
11095 /* Handler for the "up-folder" keybinding signal */
11097 up_folder_handler (GtkFileChooserDefault *impl)
11099 _gtk_path_bar_up (GTK_PATH_BAR (impl->browse_path_bar));
11102 /* Handler for the "down-folder" keybinding signal */
11104 down_folder_handler (GtkFileChooserDefault *impl)
11106 _gtk_path_bar_down (GTK_PATH_BAR (impl->browse_path_bar));
11109 /* Switches to the shortcut in the specified index */
11111 switch_to_shortcut (GtkFileChooserDefault *impl,
11116 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
11117 g_assert_not_reached ();
11119 shortcuts_activate_iter (impl, &iter);
11122 /* Handler for the "home-folder" keybinding signal */
11124 home_folder_handler (GtkFileChooserDefault *impl)
11126 if (impl->has_home)
11127 switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_HOME));
11130 /* Handler for the "desktop-folder" keybinding signal */
11132 desktop_folder_handler (GtkFileChooserDefault *impl)
11134 if (impl->has_desktop)
11135 switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_DESKTOP));
11138 /* Handler for the "search-shortcut" keybinding signal */
11140 search_shortcut_handler (GtkFileChooserDefault *impl)
11142 if (impl->has_search)
11144 switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_SEARCH));
11146 /* we want the entry widget to grab the focus the first
11147 * time, not the browse_files_tree_view widget.
11149 if (impl->search_entry)
11150 gtk_widget_grab_focus (impl->search_entry);
11154 /* Handler for the "recent-shortcut" keybinding signal */
11156 recent_shortcut_handler (GtkFileChooserDefault *impl)
11158 if (impl->has_recent)
11159 switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_RECENT));
11163 quick_bookmark_handler (GtkFileChooserDefault *impl,
11164 gint bookmark_index)
11169 if (bookmark_index < 0 || bookmark_index >= impl->num_bookmarks)
11172 bookmark_pos = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS) + bookmark_index;
11174 path = gtk_tree_path_new_from_indices (bookmark_pos, -1);
11175 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
11178 gtk_tree_path_free (path);
11180 switch_to_shortcut (impl, bookmark_pos);
11184 show_hidden_handler (GtkFileChooserDefault *impl)
11186 g_object_set (impl,
11187 "show-hidden", !impl->show_hidden,
11192 /* Drag and drop interfaces */
11195 _shortcuts_pane_model_filter_class_init (ShortcutsPaneModelFilterClass *class)
11200 _shortcuts_pane_model_filter_init (ShortcutsPaneModelFilter *model)
11202 model->impl = NULL;
11205 /* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */
11207 shortcuts_pane_model_filter_row_draggable (GtkTreeDragSource *drag_source,
11210 ShortcutsPaneModelFilter *model;
11214 model = SHORTCUTS_PANE_MODEL_FILTER (drag_source);
11216 pos = *gtk_tree_path_get_indices (path);
11217 bookmarks_pos = shortcuts_get_index (model->impl, SHORTCUTS_BOOKMARKS);
11219 return (pos >= bookmarks_pos && pos < bookmarks_pos + model->impl->num_bookmarks);
11222 /* GtkTreeDragSource::drag_data_get implementation for the shortcuts filter model */
11224 shortcuts_pane_model_filter_drag_data_get (GtkTreeDragSource *drag_source,
11226 GtkSelectionData *selection_data)
11228 ShortcutsPaneModelFilter *model;
11230 model = SHORTCUTS_PANE_MODEL_FILTER (drag_source);
11237 /* Fill the GtkTreeDragSourceIface vtable */
11239 shortcuts_pane_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface)
11241 iface->row_draggable = shortcuts_pane_model_filter_row_draggable;
11242 iface->drag_data_get = shortcuts_pane_model_filter_drag_data_get;
11246 /* Fill the GtkTreeDragDestIface vtable */
11248 shortcuts_pane_model_filter_drag_dest_iface_init (GtkTreeDragDestIface *iface)
11250 iface->drag_data_received = shortcuts_pane_model_filter_drag_data_received;
11251 iface->row_drop_possible = shortcuts_pane_model_filter_row_drop_possible;
11255 static GtkTreeModel *
11256 shortcuts_pane_model_filter_new (GtkFileChooserDefault *impl,
11257 GtkTreeModel *child_model,
11260 ShortcutsPaneModelFilter *model;
11262 model = g_object_new (SHORTCUTS_PANE_MODEL_FILTER_TYPE,
11263 "child-model", child_model,
11264 "virtual-root", root,
11267 model->impl = impl;
11269 return GTK_TREE_MODEL (model);
11275 recent_model_sort_row_draggable (GtkTreeDragSource *drag_source,
11278 RecentModelSort *model;
11279 GtkTreeIter iter, child_iter;
11280 gboolean is_folder;
11282 model = RECENT_MODEL_SORT (drag_source);
11283 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
11286 recent_get_valid_child_iter (model->impl, &child_iter, &iter);
11287 gtk_tree_model_get (GTK_TREE_MODEL (model->impl->recent_model), &child_iter,
11288 RECENT_MODEL_COL_IS_FOLDER, &is_folder,
11295 recent_model_sort_drag_data_get (GtkTreeDragSource *drag_source,
11297 GtkSelectionData *selection_data)
11299 RecentModelSort *model;
11300 GtkTreeIter iter, child_iter;
11304 model = RECENT_MODEL_SORT (drag_source);
11305 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
11308 recent_get_valid_child_iter (model->impl, &child_iter, &iter);
11309 gtk_tree_model_get (GTK_TREE_MODEL (model->impl->recent_model), &child_iter,
11310 RECENT_MODEL_COL_FILE, &file,
11312 g_assert (file != NULL);
11314 uris[0] = g_file_get_uri (file);
11317 gtk_selection_data_set_uris (selection_data, uris);
11325 recent_model_sort_drag_source_iface_init (GtkTreeDragSourceIface *iface)
11327 iface->row_draggable = recent_model_sort_row_draggable;
11328 iface->drag_data_get = recent_model_sort_drag_data_get;
11332 _recent_model_sort_class_init (RecentModelSortClass *klass)
11338 _recent_model_sort_init (RecentModelSort *model)
11340 model->impl = NULL;
11343 static GtkTreeModel *
11344 recent_model_sort_new (GtkFileChooserDefault *impl,
11345 GtkTreeModel *child_model)
11347 RecentModelSort *model;
11349 model = g_object_new (RECENT_MODEL_SORT_TYPE,
11350 "model", child_model,
11352 model->impl = impl;
11354 return GTK_TREE_MODEL (model);
11360 search_model_sort_row_draggable (GtkTreeDragSource *drag_source,
11363 SearchModelSort *model;
11364 GtkTreeIter iter, child_iter;
11365 gboolean is_folder;
11367 model = SEARCH_MODEL_SORT (drag_source);
11368 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
11371 search_get_valid_child_iter (model->impl, &child_iter, &iter);
11372 gtk_tree_model_get (GTK_TREE_MODEL (model->impl->search_model), &child_iter,
11373 SEARCH_MODEL_COL_IS_FOLDER, &is_folder,
11380 search_model_sort_drag_data_get (GtkTreeDragSource *drag_source,
11382 GtkSelectionData *selection_data)
11384 SearchModelSort *model;
11385 GtkTreeIter iter, child_iter;
11389 model = SEARCH_MODEL_SORT (drag_source);
11390 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
11393 search_get_valid_child_iter (model->impl, &child_iter, &iter);
11394 gtk_tree_model_get (GTK_TREE_MODEL (model->impl->search_model), &child_iter,
11395 RECENT_MODEL_COL_FILE, &file,
11397 g_assert (file != NULL);
11399 uris[0] = g_file_get_uri (file);
11402 gtk_selection_data_set_uris (selection_data, uris);
11410 search_model_sort_drag_source_iface_init (GtkTreeDragSourceIface *iface)
11412 iface->row_draggable = search_model_sort_row_draggable;
11413 iface->drag_data_get = search_model_sort_drag_data_get;
11417 _search_model_sort_class_init (SearchModelSortClass *klass)
11423 _search_model_sort_init (SearchModelSort *model)
11425 model->impl = NULL;
11428 static GtkTreeModel *
11429 search_model_sort_new (GtkFileChooserDefault *impl,
11430 GtkTreeModel *child_model)
11432 SearchModelSort *model;
11434 model = g_object_new (SEARCH_MODEL_SORT_TYPE,
11435 "model", child_model,
11437 model->impl = impl;
11439 return GTK_TREE_MODEL (model);