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 "gtkfilechooserdialog.h"
38 #include "gtkfilechooserembed.h"
39 #include "gtkfilechooserentry.h"
40 #include "gtkfilechoosersettings.h"
41 #include "gtkfilechooserutils.h"
42 #include "gtkfilechooser.h"
43 #include "gtkfilesystem.h"
44 #include "gtkfilesystemmodel.h"
47 #include "gtkhpaned.h"
48 #include "gtkiconfactory.h"
49 #include "gtkicontheme.h"
51 #include "gtkimagemenuitem.h"
53 #include "gtkmarshalers.h"
54 #include "gtkmessagedialog.h"
55 #include "gtkmountoperation.h"
56 #include "gtkpathbar.h"
57 #include "gtkprivate.h"
58 #include "gtkradiobutton.h"
59 #include "gtkrecentfilter.h"
60 #include "gtkrecentmanager.h"
61 #include "gtkscrolledwindow.h"
62 #include "gtkseparatormenuitem.h"
63 #include "gtksizegroup.h"
66 #include "gtktooltip.h"
67 #include "gtktreednd.h"
68 #include "gtktreeprivate.h"
69 #include "gtktreeselection.h"
79 #include <sys/types.h>
90 #undef PROFILE_FILE_CHOOSER
91 #ifdef PROFILE_FILE_CHOOSER
98 #define PROFILE_INDENT 4
99 static int profile_indent;
102 profile_add_indent (int indent)
104 profile_indent += indent;
105 if (profile_indent < 0)
106 g_error ("You screwed up your indentation");
110 _gtk_file_chooser_profile_log (const char *func, int indent, const char *msg1, const char *msg2)
115 profile_add_indent (indent);
117 if (profile_indent == 0)
118 str = g_strdup_printf ("MARK: %s %s %s", func ? func : "", msg1 ? msg1 : "", msg2 ? msg2 : "");
120 str = g_strdup_printf ("MARK: %*c %s %s %s", profile_indent - 1, ' ', func ? func : "", msg1 ? msg1 : "", msg2 ? msg2 : "");
126 profile_add_indent (indent);
129 #define profile_start(x, y) _gtk_file_chooser_profile_log (G_STRFUNC, PROFILE_INDENT, x, y)
130 #define profile_end(x, y) _gtk_file_chooser_profile_log (G_STRFUNC, -PROFILE_INDENT, x, y)
131 #define profile_msg(x, y) _gtk_file_chooser_profile_log (NULL, 0, x, y)
133 #define profile_start(x, y)
134 #define profile_end(x, y)
135 #define profile_msg(x, y)
140 typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
142 #define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
143 #define GTK_IS_FILE_CHOOSER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT))
144 #define GTK_FILE_CHOOSER_DEFAULT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
146 #define MAX_LOADING_TIME 500
148 struct _GtkFileChooserDefaultClass
150 GtkVBoxClass parent_class;
156 LOCATION_POPUP_ON_PASTE,
162 LOCATION_TOGGLE_POPUP,
170 static guint signals[LAST_SIGNAL] = { 0 };
172 /* Column numbers for the shortcuts tree. Keep these in sync with shortcuts_model_create() */
174 SHORTCUTS_COL_PIXBUF,
178 SHORTCUTS_COL_REMOVABLE,
179 SHORTCUTS_COL_PIXBUF_VISIBLE,
180 SHORTCUTS_COL_CANCELLABLE,
181 SHORTCUTS_COL_NUM_COLUMNS
186 SHORTCUT_TYPE_VOLUME,
187 SHORTCUT_TYPE_SEPARATOR,
188 SHORTCUT_TYPE_SEARCH,
192 /* Column numbers for the file list */
197 FILE_LIST_COL_NUM_COLUMNS
200 /* Column numbers for the search model.
201 * Keep this in sync with search_setup_model()
204 SEARCH_MODEL_COL_FILE,
205 SEARCH_MODEL_COL_DISPLAY_NAME,
206 SEARCH_MODEL_COL_COLLATION_KEY,
207 SEARCH_MODEL_COL_MTIME,
208 SEARCH_MODEL_COL_SIZE,
209 SEARCH_MODEL_COL_CANCELLABLE,
210 SEARCH_MODEL_COL_PIXBUF,
211 SEARCH_MODEL_COL_MIME_TYPE,
212 SEARCH_MODEL_COL_IS_FOLDER,
213 SEARCH_MODEL_COL_NUM_COLUMNS
217 RECENT_MODEL_COL_FILE,
218 RECENT_MODEL_COL_DISPLAY_NAME,
219 RECENT_MODEL_COL_INFO,
220 RECENT_MODEL_COL_IS_FOLDER,
221 RECENT_MODEL_COL_CANCELLABLE,
222 RECENT_MODEL_COL_NUM_COLUMNS
225 /* Identifiers for target types */
231 search_is_possible (GtkFileChooserDefault *impl)
233 if (impl->search_engine == NULL)
234 impl->search_engine = _gtk_search_engine_new ();
236 return impl->search_engine != NULL;
239 /* Interesting places in the shortcuts bar */
243 SHORTCUTS_RECENT_SEPARATOR,
248 SHORTCUTS_BOOKMARKS_SEPARATOR,
250 SHORTCUTS_CURRENT_FOLDER_SEPARATOR,
251 SHORTCUTS_CURRENT_FOLDER
254 /* Icon size for if we can't get it from the theme */
255 #define FALLBACK_ICON_SIZE 16
257 #define PREVIEW_HBOX_SPACING 12
261 static void gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface);
262 static void gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface);
264 static GObject* gtk_file_chooser_default_constructor (GType type,
265 guint n_construct_properties,
266 GObjectConstructParam *construct_params);
267 static void gtk_file_chooser_default_finalize (GObject *object);
268 static void gtk_file_chooser_default_set_property (GObject *object,
272 static void gtk_file_chooser_default_get_property (GObject *object,
276 static void gtk_file_chooser_default_dispose (GObject *object);
277 static void gtk_file_chooser_default_show_all (GtkWidget *widget);
278 static void gtk_file_chooser_default_realize (GtkWidget *widget);
279 static void gtk_file_chooser_default_map (GtkWidget *widget);
280 static void gtk_file_chooser_default_unmap (GtkWidget *widget);
281 static void gtk_file_chooser_default_hierarchy_changed (GtkWidget *widget,
282 GtkWidget *previous_toplevel);
283 static void gtk_file_chooser_default_style_set (GtkWidget *widget,
284 GtkStyle *previous_style);
285 static void gtk_file_chooser_default_screen_changed (GtkWidget *widget,
286 GdkScreen *previous_screen);
287 static void gtk_file_chooser_default_size_allocate (GtkWidget *widget,
288 GtkAllocation *allocation);
290 static gboolean gtk_file_chooser_default_set_current_folder (GtkFileChooser *chooser,
293 static gboolean gtk_file_chooser_default_update_current_folder (GtkFileChooser *chooser,
296 gboolean clear_entry,
298 static GFile * gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser);
299 static void gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
301 static gboolean gtk_file_chooser_default_select_file (GtkFileChooser *chooser,
304 static void gtk_file_chooser_default_unselect_file (GtkFileChooser *chooser,
306 static void gtk_file_chooser_default_select_all (GtkFileChooser *chooser);
307 static void gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser);
308 static GSList * gtk_file_chooser_default_get_files (GtkFileChooser *chooser);
309 static GFile * gtk_file_chooser_default_get_preview_file (GtkFileChooser *chooser);
310 static GtkFileSystem *gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser);
311 static void gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
312 GtkFileFilter *filter);
313 static void gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
314 GtkFileFilter *filter);
315 static GSList * gtk_file_chooser_default_list_filters (GtkFileChooser *chooser);
316 static gboolean gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser *chooser,
319 static gboolean gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser *chooser,
322 static GSList * gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser);
324 static void gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
326 gint *default_height);
327 static gboolean gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed);
328 static void gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed);
330 static void location_popup_handler (GtkFileChooserDefault *impl,
332 static void location_popup_on_paste_handler (GtkFileChooserDefault *impl);
333 static void location_toggle_popup_handler (GtkFileChooserDefault *impl);
334 static void up_folder_handler (GtkFileChooserDefault *impl);
335 static void down_folder_handler (GtkFileChooserDefault *impl);
336 static void home_folder_handler (GtkFileChooserDefault *impl);
337 static void desktop_folder_handler (GtkFileChooserDefault *impl);
338 static void quick_bookmark_handler (GtkFileChooserDefault *impl,
339 gint bookmark_index);
340 static void show_hidden_handler (GtkFileChooserDefault *impl);
341 static void search_shortcut_handler (GtkFileChooserDefault *impl);
342 static void recent_shortcut_handler (GtkFileChooserDefault *impl);
343 static void update_appearance (GtkFileChooserDefault *impl);
345 static void set_current_filter (GtkFileChooserDefault *impl,
346 GtkFileFilter *filter);
347 static void check_preview_change (GtkFileChooserDefault *impl);
349 static void filter_combo_changed (GtkComboBox *combo_box,
350 GtkFileChooserDefault *impl);
352 static gboolean shortcuts_key_press_event_cb (GtkWidget *widget,
354 GtkFileChooserDefault *impl);
356 static gboolean shortcuts_select_func (GtkTreeSelection *selection,
359 gboolean path_currently_selected,
361 static gboolean shortcuts_get_selected (GtkFileChooserDefault *impl,
363 static void shortcuts_activate_iter (GtkFileChooserDefault *impl,
365 static int shortcuts_get_index (GtkFileChooserDefault *impl,
366 ShortcutsIndex where);
367 static int shortcut_find_position (GtkFileChooserDefault *impl,
370 static void bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl);
372 static gboolean list_select_func (GtkTreeSelection *selection,
375 gboolean path_currently_selected,
378 static void list_selection_changed (GtkTreeSelection *tree_selection,
379 GtkFileChooserDefault *impl);
380 static void list_row_activated (GtkTreeView *tree_view,
382 GtkTreeViewColumn *column,
383 GtkFileChooserDefault *impl);
385 static void select_func (GtkFileSystemModel *model,
390 static void path_bar_clicked (GtkPathBar *path_bar,
393 gboolean child_is_hidden,
394 GtkFileChooserDefault *impl);
396 static void add_bookmark_button_clicked_cb (GtkButton *button,
397 GtkFileChooserDefault *impl);
398 static void remove_bookmark_button_clicked_cb (GtkButton *button,
399 GtkFileChooserDefault *impl);
400 static void save_folder_combo_changed_cb (GtkComboBox *combo,
401 GtkFileChooserDefault *impl);
403 static void list_icon_data_func (GtkTreeViewColumn *tree_column,
404 GtkCellRenderer *cell,
405 GtkTreeModel *tree_model,
408 static void list_name_data_func (GtkTreeViewColumn *tree_column,
409 GtkCellRenderer *cell,
410 GtkTreeModel *tree_model,
413 static void list_size_data_func (GtkTreeViewColumn *tree_column,
414 GtkCellRenderer *cell,
415 GtkTreeModel *tree_model,
418 static void list_mtime_data_func (GtkTreeViewColumn *tree_column,
419 GtkCellRenderer *cell,
420 GtkTreeModel *tree_model,
424 static GFileInfo *get_list_file_info (GtkFileChooserDefault *impl,
427 static void load_remove_timer (GtkFileChooserDefault *impl);
428 static void browse_files_center_selected_row (GtkFileChooserDefault *impl);
430 static void location_button_toggled_cb (GtkToggleButton *toggle,
431 GtkFileChooserDefault *impl);
432 static void location_switch_to_path_bar (GtkFileChooserDefault *impl);
434 static void search_stop_searching (GtkFileChooserDefault *impl,
435 gboolean remove_query);
436 static void search_clear_model_row (GtkTreeModel *model,
438 static void search_clear_model (GtkFileChooserDefault *impl,
439 gboolean remove_from_treeview);
440 static gboolean search_should_respond (GtkFileChooserDefault *impl);
441 static void search_switch_to_browse_mode (GtkFileChooserDefault *impl);
442 static GSList *search_get_selected_files (GtkFileChooserDefault *impl);
443 static void search_entry_activate_cb (GtkEntry *entry,
445 static void settings_load (GtkFileChooserDefault *impl);
446 static void search_get_valid_child_iter (GtkFileChooserDefault *impl,
447 GtkTreeIter *child_iter,
450 static void recent_stop_loading (GtkFileChooserDefault *impl);
451 static void recent_clear_model (GtkFileChooserDefault *impl,
452 gboolean remove_from_treeview);
453 static gboolean recent_should_respond (GtkFileChooserDefault *impl);
454 static void recent_switch_to_browse_mode (GtkFileChooserDefault *impl);
455 static GSList * recent_get_selected_files (GtkFileChooserDefault *impl);
456 static void recent_get_valid_child_iter (GtkFileChooserDefault *impl,
457 GtkTreeIter *child_iter,
459 static void set_file_system_backend (GtkFileChooserDefault *impl);
460 static void unset_file_system_backend (GtkFileChooserDefault *impl);
466 /* Drag and drop interface declarations */
469 GtkTreeModelFilter parent;
471 GtkFileChooserDefault *impl;
472 } ShortcutsPaneModelFilter;
475 GtkTreeModelFilterClass parent_class;
476 } ShortcutsPaneModelFilterClass;
478 #define SHORTCUTS_PANE_MODEL_FILTER_TYPE (_shortcuts_pane_model_filter_get_type ())
479 #define SHORTCUTS_PANE_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_PANE_MODEL_FILTER_TYPE, ShortcutsPaneModelFilter))
481 static void shortcuts_pane_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface);
483 G_DEFINE_TYPE_WITH_CODE (ShortcutsPaneModelFilter,
484 _shortcuts_pane_model_filter,
485 GTK_TYPE_TREE_MODEL_FILTER,
486 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
487 shortcuts_pane_model_filter_drag_source_iface_init))
489 static GtkTreeModel *shortcuts_pane_model_filter_new (GtkFileChooserDefault *impl,
490 GtkTreeModel *child_model,
495 GtkTreeModelSort parent;
497 GtkFileChooserDefault *impl;
501 GtkTreeModelSortClass parent_class;
502 } RecentModelSortClass;
504 #define RECENT_MODEL_SORT_TYPE (_recent_model_sort_get_type ())
505 #define RECENT_MODEL_SORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), RECENT_MODEL_SORT_TYPE, RecentModelSort))
507 static void recent_model_sort_drag_source_iface_init (GtkTreeDragSourceIface *iface);
509 G_DEFINE_TYPE_WITH_CODE (RecentModelSort,
511 GTK_TYPE_TREE_MODEL_SORT,
512 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
513 recent_model_sort_drag_source_iface_init));
515 static GtkTreeModel *recent_model_sort_new (GtkFileChooserDefault *impl,
516 GtkTreeModel *child_model);
520 GtkTreeModelSort parent;
522 GtkFileChooserDefault *impl;
526 GtkTreeModelSortClass parent_class;
527 } SearchModelSortClass;
529 #define SEARCH_MODEL_SORT_TYPE (_search_model_sort_get_type ())
530 #define SEARCH_MODEL_SORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEARCH_MODEL_SORT_TYPE, SearchModelSort))
532 static void search_model_sort_drag_source_iface_init (GtkTreeDragSourceIface *iface);
534 G_DEFINE_TYPE_WITH_CODE (SearchModelSort,
536 GTK_TYPE_TREE_MODEL_SORT,
537 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
538 search_model_sort_drag_source_iface_init));
540 static GtkTreeModel *search_model_sort_new (GtkFileChooserDefault *impl,
541 GtkTreeModel *child_model);
545 G_DEFINE_TYPE_WITH_CODE (GtkFileChooserDefault, _gtk_file_chooser_default, GTK_TYPE_VBOX,
546 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER,
547 gtk_file_chooser_default_iface_init)
548 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER_EMBED,
549 gtk_file_chooser_embed_default_iface_init));
552 _gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
554 static const guint quick_bookmark_keyvals[10] = {
555 GDK_1, GDK_2, GDK_3, GDK_4, GDK_5, GDK_6, GDK_7, GDK_8, GDK_9, GDK_0
557 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
558 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
559 GtkBindingSet *binding_set;
562 gobject_class->finalize = gtk_file_chooser_default_finalize;
563 gobject_class->constructor = gtk_file_chooser_default_constructor;
564 gobject_class->set_property = gtk_file_chooser_default_set_property;
565 gobject_class->get_property = gtk_file_chooser_default_get_property;
566 gobject_class->dispose = gtk_file_chooser_default_dispose;
568 widget_class->show_all = gtk_file_chooser_default_show_all;
569 widget_class->realize = gtk_file_chooser_default_realize;
570 widget_class->map = gtk_file_chooser_default_map;
571 widget_class->unmap = gtk_file_chooser_default_unmap;
572 widget_class->hierarchy_changed = gtk_file_chooser_default_hierarchy_changed;
573 widget_class->style_set = gtk_file_chooser_default_style_set;
574 widget_class->screen_changed = gtk_file_chooser_default_screen_changed;
575 widget_class->size_allocate = gtk_file_chooser_default_size_allocate;
577 signals[LOCATION_POPUP] =
578 g_signal_new_class_handler (I_("location-popup"),
579 G_OBJECT_CLASS_TYPE (class),
580 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
581 G_CALLBACK (location_popup_handler),
583 _gtk_marshal_VOID__STRING,
584 G_TYPE_NONE, 1, G_TYPE_STRING);
586 signals[LOCATION_POPUP_ON_PASTE] =
587 g_signal_new_class_handler (I_("location-popup-on-paste"),
588 G_OBJECT_CLASS_TYPE (class),
589 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
590 G_CALLBACK (location_popup_on_paste_handler),
592 _gtk_marshal_VOID__VOID,
595 signals[LOCATION_TOGGLE_POPUP] =
596 g_signal_new_class_handler (I_("location-toggle-popup"),
597 G_OBJECT_CLASS_TYPE (class),
598 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
599 G_CALLBACK (location_toggle_popup_handler),
601 _gtk_marshal_VOID__VOID,
605 g_signal_new_class_handler (I_("up-folder"),
606 G_OBJECT_CLASS_TYPE (class),
607 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
608 G_CALLBACK (up_folder_handler),
610 _gtk_marshal_VOID__VOID,
613 signals[DOWN_FOLDER] =
614 g_signal_new_class_handler (I_("down-folder"),
615 G_OBJECT_CLASS_TYPE (class),
616 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
617 G_CALLBACK (down_folder_handler),
619 _gtk_marshal_VOID__VOID,
622 signals[HOME_FOLDER] =
623 g_signal_new_class_handler (I_("home-folder"),
624 G_OBJECT_CLASS_TYPE (class),
625 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
626 G_CALLBACK (home_folder_handler),
628 _gtk_marshal_VOID__VOID,
631 signals[DESKTOP_FOLDER] =
632 g_signal_new_class_handler (I_("desktop-folder"),
633 G_OBJECT_CLASS_TYPE (class),
634 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
635 G_CALLBACK (desktop_folder_handler),
637 _gtk_marshal_VOID__VOID,
640 signals[QUICK_BOOKMARK] =
641 g_signal_new_class_handler (I_("quick-bookmark"),
642 G_OBJECT_CLASS_TYPE (class),
643 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
644 G_CALLBACK (quick_bookmark_handler),
646 _gtk_marshal_VOID__INT,
647 G_TYPE_NONE, 1, G_TYPE_INT);
649 signals[SHOW_HIDDEN] =
650 g_signal_new_class_handler (I_("show-hidden"),
651 G_OBJECT_CLASS_TYPE (class),
652 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
653 G_CALLBACK (show_hidden_handler),
655 _gtk_marshal_VOID__VOID,
658 signals[SEARCH_SHORTCUT] =
659 g_signal_new_class_handler (I_("search-shortcut"),
660 G_OBJECT_CLASS_TYPE (class),
661 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
662 G_CALLBACK (search_shortcut_handler),
664 _gtk_marshal_VOID__VOID,
667 signals[RECENT_SHORTCUT] =
668 g_signal_new_class_handler (I_("recent-shortcut"),
669 G_OBJECT_CLASS_TYPE (class),
670 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
671 G_CALLBACK (recent_shortcut_handler),
673 _gtk_marshal_VOID__VOID,
676 binding_set = gtk_binding_set_by_class (class);
678 gtk_binding_entry_add_signal (binding_set,
679 GDK_l, GDK_CONTROL_MASK,
680 "location-toggle-popup",
683 gtk_binding_entry_add_signal (binding_set,
686 1, G_TYPE_STRING, "/");
687 gtk_binding_entry_add_signal (binding_set,
690 1, G_TYPE_STRING, "/");
693 gtk_binding_entry_add_signal (binding_set,
696 1, G_TYPE_STRING, "~");
699 gtk_binding_entry_add_signal (binding_set,
700 GDK_v, GDK_CONTROL_MASK,
701 "location-popup-on-paste",
703 gtk_binding_entry_add_signal (binding_set,
704 GDK_Up, GDK_MOD1_MASK,
707 gtk_binding_entry_add_signal (binding_set,
711 gtk_binding_entry_add_signal (binding_set,
712 GDK_KP_Up, GDK_MOD1_MASK,
716 gtk_binding_entry_add_signal (binding_set,
717 GDK_Down, GDK_MOD1_MASK,
720 gtk_binding_entry_add_signal (binding_set,
721 GDK_KP_Down, GDK_MOD1_MASK,
725 gtk_binding_entry_add_signal (binding_set,
726 GDK_Home, GDK_MOD1_MASK,
729 gtk_binding_entry_add_signal (binding_set,
730 GDK_KP_Home, GDK_MOD1_MASK,
733 gtk_binding_entry_add_signal (binding_set,
734 GDK_d, GDK_MOD1_MASK,
737 gtk_binding_entry_add_signal (binding_set,
738 GDK_h, GDK_CONTROL_MASK,
741 gtk_binding_entry_add_signal (binding_set,
742 GDK_s, GDK_MOD1_MASK,
745 gtk_binding_entry_add_signal (binding_set,
746 GDK_r, GDK_MOD1_MASK,
750 for (i = 0; i < 10; i++)
751 gtk_binding_entry_add_signal (binding_set,
752 quick_bookmark_keyvals[i], GDK_MOD1_MASK,
756 _gtk_file_chooser_install_properties (gobject_class);
760 gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
762 iface->select_file = gtk_file_chooser_default_select_file;
763 iface->unselect_file = gtk_file_chooser_default_unselect_file;
764 iface->select_all = gtk_file_chooser_default_select_all;
765 iface->unselect_all = gtk_file_chooser_default_unselect_all;
766 iface->get_files = gtk_file_chooser_default_get_files;
767 iface->get_preview_file = gtk_file_chooser_default_get_preview_file;
768 iface->get_file_system = gtk_file_chooser_default_get_file_system;
769 iface->set_current_folder = gtk_file_chooser_default_set_current_folder;
770 iface->get_current_folder = gtk_file_chooser_default_get_current_folder;
771 iface->set_current_name = gtk_file_chooser_default_set_current_name;
772 iface->add_filter = gtk_file_chooser_default_add_filter;
773 iface->remove_filter = gtk_file_chooser_default_remove_filter;
774 iface->list_filters = gtk_file_chooser_default_list_filters;
775 iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder;
776 iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder;
777 iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders;
781 gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
783 iface->get_default_size = gtk_file_chooser_default_get_default_size;
784 iface->should_respond = gtk_file_chooser_default_should_respond;
785 iface->initial_focus = gtk_file_chooser_default_initial_focus;
789 _gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
791 profile_start ("start", NULL);
792 #ifdef PROFILE_FILE_CHOOSER
793 access ("MARK: *** CREATE FILE CHOOSER", F_OK);
795 impl->local_only = TRUE;
796 impl->preview_widget_active = TRUE;
797 impl->use_preview_label = TRUE;
798 impl->select_multiple = FALSE;
799 impl->show_hidden = FALSE;
800 impl->show_size_column = FALSE;
801 impl->icon_size = FALLBACK_ICON_SIZE;
802 impl->load_state = LOAD_EMPTY;
803 impl->reload_state = RELOAD_EMPTY;
804 impl->pending_select_files = NULL;
805 impl->location_mode = LOCATION_MODE_PATH_BAR;
806 impl->operation_mode = OPERATION_MODE_BROWSE;
807 impl->recent_manager = gtk_recent_manager_get_default ();
809 gtk_box_set_spacing (GTK_BOX (impl), 12);
811 set_file_system_backend (impl);
813 profile_end ("end", NULL);
816 /* Frees the data columns for the specified iter in the shortcuts model*/
818 shortcuts_free_row_data (GtkFileChooserDefault *impl,
822 ShortcutType shortcut_type;
823 GCancellable *cancellable;
825 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
826 SHORTCUTS_COL_DATA, &col_data,
827 SHORTCUTS_COL_TYPE, &shortcut_type,
828 SHORTCUTS_COL_CANCELLABLE, &cancellable,
832 g_cancellable_cancel (cancellable);
834 if (!(shortcut_type == SHORTCUT_TYPE_FILE ||
835 shortcut_type == SHORTCUT_TYPE_VOLUME) ||
839 if (shortcut_type == SHORTCUT_TYPE_VOLUME)
841 GtkFileSystemVolume *volume;
844 _gtk_file_system_volume_free (volume);
850 g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
853 g_object_unref (file);
857 /* Frees all the data columns in the shortcuts model */
859 shortcuts_free (GtkFileChooserDefault *impl)
863 if (!impl->shortcuts_model)
866 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
869 shortcuts_free_row_data (impl, &iter);
871 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter));
873 g_object_unref (impl->shortcuts_model);
874 impl->shortcuts_model = NULL;
878 pending_select_files_free (GtkFileChooserDefault *impl)
880 g_slist_foreach (impl->pending_select_files, (GFunc) g_object_unref, NULL);
881 g_slist_free (impl->pending_select_files);
882 impl->pending_select_files = NULL;
886 pending_select_files_add (GtkFileChooserDefault *impl,
889 impl->pending_select_files =
890 g_slist_prepend (impl->pending_select_files, g_object_ref (file));
893 /* Used from gtk_tree_selection_selected_foreach() */
895 store_selection_foreach (GtkTreeModel *model,
900 GtkFileChooserDefault *impl;
901 GtkTreeIter child_iter;
904 impl = GTK_FILE_CHOOSER_DEFAULT (data);
906 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter);
908 file = _gtk_file_system_model_get_file (impl->browse_files_model, &child_iter);
909 pending_select_files_add (impl, file);
912 /* Stores the current selection in the list of paths to select; this is used to
913 * preserve the selection when reloading the current folder.
916 pending_select_files_store_selection (GtkFileChooserDefault *impl)
918 GtkTreeSelection *selection;
920 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
921 gtk_tree_selection_selected_foreach (selection, store_selection_foreach, impl);
925 gtk_file_chooser_default_finalize (GObject *object)
927 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
930 unset_file_system_backend (impl);
932 if (impl->shortcuts_pane_filter_model)
933 g_object_unref (impl->shortcuts_pane_filter_model);
935 if (impl->shortcuts_combo_filter_model)
936 g_object_unref (impl->shortcuts_combo_filter_model);
938 shortcuts_free (impl);
940 g_free (impl->browse_files_last_selected_name);
942 for (l = impl->filters; l; l = l->next)
944 GtkFileFilter *filter;
946 filter = GTK_FILE_FILTER (l->data);
947 g_object_unref (filter);
949 g_slist_free (impl->filters);
951 if (impl->current_filter)
952 g_object_unref (impl->current_filter);
954 if (impl->current_volume_file)
955 g_object_unref (impl->current_volume_file);
957 if (impl->current_folder)
958 g_object_unref (impl->current_folder);
960 if (impl->preview_file)
961 g_object_unref (impl->preview_file);
963 if (impl->browse_path_bar_size_group)
964 g_object_unref (impl->browse_path_bar_size_group);
966 load_remove_timer (impl);
968 /* Free all the Models we have */
969 if (impl->browse_files_model)
970 g_object_unref (impl->browse_files_model);
972 if (impl->sort_model)
973 g_object_unref (impl->sort_model);
975 search_clear_model (impl, FALSE);
976 recent_clear_model (impl, FALSE);
978 g_free (impl->preview_display_name);
980 g_free (impl->edited_new_text);
982 G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->finalize (object);
985 /* Shows an error dialog set as transient for the specified window */
987 error_message_with_parent (GtkWindow *parent,
993 dialog = gtk_message_dialog_new (parent,
994 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
999 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
1002 if (parent && parent->group)
1003 gtk_window_group_add_window (parent->group, GTK_WINDOW (dialog));
1005 gtk_dialog_run (GTK_DIALOG (dialog));
1006 gtk_widget_destroy (dialog);
1009 /* Returns a toplevel GtkWindow, or NULL if none */
1011 get_toplevel (GtkWidget *widget)
1013 GtkWidget *toplevel;
1015 toplevel = gtk_widget_get_toplevel (widget);
1016 if (!GTK_WIDGET_TOPLEVEL (toplevel))
1019 return GTK_WINDOW (toplevel);
1022 /* Shows an error dialog for the file chooser */
1024 error_message (GtkFileChooserDefault *impl,
1028 error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail);
1031 /* Shows a simple error dialog relative to a path. Frees the GError as well. */
1033 error_dialog (GtkFileChooserDefault *impl,
1044 uri = g_file_get_uri (file);
1045 text = g_strdup_printf (msg, uri);
1046 error_message (impl, text, error->message);
1049 g_error_free (error);
1053 /* Displays an error message about not being able to get information for a file.
1054 * Frees the GError as well.
1057 error_getting_info_dialog (GtkFileChooserDefault *impl,
1062 _("Could not retrieve information about the file"),
1066 /* Shows an error dialog about not being able to add a bookmark */
1068 error_adding_bookmark_dialog (GtkFileChooserDefault *impl,
1073 _("Could not add a bookmark"),
1077 /* Shows an error dialog about not being able to remove a bookmark */
1079 error_removing_bookmark_dialog (GtkFileChooserDefault *impl,
1084 _("Could not remove bookmark"),
1088 /* Shows an error dialog about not being able to create a folder */
1090 error_creating_folder_dialog (GtkFileChooserDefault *impl,
1095 _("The folder could not be created"),
1099 /* Shows an error about not being able to create a folder because a file with
1100 * the same name is already there.
1103 error_creating_folder_over_existing_file_dialog (GtkFileChooserDefault *impl,
1108 _("The folder could not be created, as a file with the same "
1109 "name already exists. Try using a different name for the "
1110 "folder, or rename the file first."),
1114 /* Shows an error dialog about not being able to create a filename */
1116 error_building_filename_dialog (GtkFileChooserDefault *impl,
1119 error_dialog (impl, _("Invalid file name"),
1123 /* Shows an error dialog when we cannot switch to a folder */
1125 error_changing_folder_dialog (GtkFileChooserDefault *impl,
1129 error_dialog (impl, _("The folder contents could not be displayed"),
1133 /* Changes folders, displaying an error dialog if this fails */
1135 change_folder_and_display_error (GtkFileChooserDefault *impl,
1137 gboolean clear_entry)
1142 g_return_val_if_fail (G_IS_FILE (file), FALSE);
1144 /* We copy the path because of this case:
1146 * list_row_activated()
1147 * fetches path from model; path belongs to the model (*)
1148 * calls change_folder_and_display_error()
1149 * calls gtk_file_chooser_set_current_folder_file()
1150 * changing folders fails, sets model to NULL, thus freeing the path in (*)
1154 result = gtk_file_chooser_default_update_current_folder (GTK_FILE_CHOOSER (impl), file, TRUE, clear_entry, &error);
1157 error_changing_folder_dialog (impl, file, error);
1163 emit_default_size_changed (GtkFileChooserDefault *impl)
1165 profile_msg (" emit default-size-changed start", NULL);
1166 g_signal_emit_by_name (impl, "default-size-changed");
1167 profile_msg (" emit default-size-changed end", NULL);
1171 update_preview_widget_visibility (GtkFileChooserDefault *impl)
1173 if (impl->use_preview_label)
1175 if (!impl->preview_label)
1177 impl->preview_label = gtk_label_new (impl->preview_display_name);
1178 gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_label, FALSE, FALSE, 0);
1179 gtk_box_reorder_child (GTK_BOX (impl->preview_box), impl->preview_label, 0);
1180 gtk_label_set_ellipsize (GTK_LABEL (impl->preview_label), PANGO_ELLIPSIZE_MIDDLE);
1181 gtk_widget_show (impl->preview_label);
1186 if (impl->preview_label)
1188 gtk_widget_destroy (impl->preview_label);
1189 impl->preview_label = NULL;
1193 if (impl->preview_widget_active && impl->preview_widget)
1194 gtk_widget_show (impl->preview_box);
1196 gtk_widget_hide (impl->preview_box);
1198 if (!GTK_WIDGET_MAPPED (impl))
1199 emit_default_size_changed (impl);
1203 set_preview_widget (GtkFileChooserDefault *impl,
1204 GtkWidget *preview_widget)
1206 if (preview_widget == impl->preview_widget)
1209 if (impl->preview_widget)
1210 gtk_container_remove (GTK_CONTAINER (impl->preview_box),
1211 impl->preview_widget);
1213 impl->preview_widget = preview_widget;
1214 if (impl->preview_widget)
1216 gtk_widget_show (impl->preview_widget);
1217 gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_widget, TRUE, TRUE, 0);
1218 gtk_box_reorder_child (GTK_BOX (impl->preview_box),
1219 impl->preview_widget,
1220 (impl->use_preview_label && impl->preview_label) ? 1 : 0);
1223 update_preview_widget_visibility (impl);
1226 /* Renders a "Search" icon at an appropriate size for a tree view */
1228 render_search_icon (GtkFileChooserDefault *impl)
1230 return gtk_widget_render_icon (GTK_WIDGET (impl), GTK_STOCK_FIND, GTK_ICON_SIZE_MENU, NULL);
1234 render_recent_icon (GtkFileChooserDefault *impl)
1236 GtkIconTheme *theme;
1239 if (gtk_widget_has_screen (GTK_WIDGET (impl)))
1240 theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
1242 theme = gtk_icon_theme_get_default ();
1244 retval = gtk_icon_theme_load_icon (theme, "document-open-recent",
1250 retval = gtk_widget_render_icon (GTK_WIDGET (impl), GTK_STOCK_FILE, GTK_ICON_SIZE_MENU, NULL);
1256 /* Re-reads all the icons for the shortcuts, used when the theme changes */
1257 struct ReloadIconsData
1259 GtkFileChooserDefault *impl;
1260 GtkTreeRowReference *row_ref;
1264 shortcuts_reload_icons_get_info_cb (GCancellable *cancellable,
1266 const GError *error,
1272 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1273 struct ReloadIconsData *data = user_data;
1275 if (!g_slist_find (data->impl->reload_icon_cancellables, cancellable))
1278 data->impl->reload_icon_cancellables = g_slist_remove (data->impl->reload_icon_cancellables, cancellable);
1280 if (cancelled || error)
1283 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->impl), data->impl->icon_size);
1285 path = gtk_tree_row_reference_get_path (data->row_ref);
1286 gtk_tree_model_get_iter (GTK_TREE_MODEL (data->impl->shortcuts_model), &iter, path);
1287 gtk_list_store_set (data->impl->shortcuts_model, &iter,
1288 SHORTCUTS_COL_PIXBUF, pixbuf,
1290 gtk_tree_path_free (path);
1293 g_object_unref (pixbuf);
1296 gtk_tree_row_reference_free (data->row_ref);
1297 g_object_unref (data->impl);
1300 g_object_unref (cancellable);
1304 shortcuts_reload_icons (GtkFileChooserDefault *impl)
1309 profile_start ("start", NULL);
1311 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
1314 for (l = impl->reload_icon_cancellables; l; l = l->next)
1316 GCancellable *cancellable = G_CANCELLABLE (l->data);
1317 g_cancellable_cancel (cancellable);
1319 g_slist_free (impl->reload_icon_cancellables);
1320 impl->reload_icon_cancellables = NULL;
1325 ShortcutType shortcut_type;
1326 gboolean pixbuf_visible;
1329 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
1330 SHORTCUTS_COL_DATA, &data,
1331 SHORTCUTS_COL_TYPE, &shortcut_type,
1332 SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible,
1338 if (shortcut_type == SHORTCUT_TYPE_VOLUME)
1340 GtkFileSystemVolume *volume;
1343 pixbuf = _gtk_file_system_volume_render_icon (volume, GTK_WIDGET (impl),
1344 impl->icon_size, NULL);
1346 else if (shortcut_type == SHORTCUT_TYPE_FILE)
1348 if (g_file_is_native (G_FILE (data)))
1351 struct ReloadIconsData *info;
1352 GtkTreePath *tree_path;
1353 GCancellable *cancellable;
1357 info = g_new0 (struct ReloadIconsData, 1);
1358 info->impl = g_object_ref (impl);
1359 tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
1360 info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->shortcuts_model), tree_path);
1361 gtk_tree_path_free (tree_path);
1363 cancellable = _gtk_file_system_get_info (impl->file_system, file,
1365 shortcuts_reload_icons_get_info_cb,
1367 impl->reload_icon_cancellables = g_slist_append (impl->reload_icon_cancellables, cancellable);
1371 GtkIconTheme *icon_theme;
1373 /* Don't call get_info for remote paths to avoid latency and
1375 * If we switch to a better bookmarks file format (XBEL), we
1376 * should use mime info to get a better icon.
1378 icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
1379 pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote",
1380 impl->icon_size, 0, NULL);
1383 else if (shortcut_type == SHORTCUT_TYPE_SEARCH)
1385 pixbuf = render_search_icon (impl);
1387 else if (shortcut_type == SHORTCUT_TYPE_RECENT)
1389 pixbuf = render_recent_icon (impl);
1392 gtk_list_store_set (impl->shortcuts_model, &iter,
1393 SHORTCUTS_COL_PIXBUF, pixbuf,
1397 g_object_unref (pixbuf);
1401 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
1405 profile_end ("end", NULL);
1409 shortcuts_find_folder (GtkFileChooserDefault *impl,
1412 GtkTreeSelection *selection;
1416 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1418 g_assert (folder != NULL);
1419 pos = shortcut_find_position (impl, folder);
1422 gtk_tree_selection_unselect_all (selection);
1426 path = gtk_tree_path_new_from_indices (pos, -1);
1427 gtk_tree_selection_select_path (selection, path);
1428 gtk_tree_path_free (path);
1431 /* If a shortcut corresponds to the current folder, selects it */
1433 shortcuts_find_current_folder (GtkFileChooserDefault *impl)
1435 shortcuts_find_folder (impl, impl->current_folder);
1438 /* Removes the specified number of rows from the shortcuts list */
1440 shortcuts_remove_rows (GtkFileChooserDefault *impl,
1446 path = gtk_tree_path_new_from_indices (start_row, -1);
1448 for (; n_rows; n_rows--)
1452 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
1453 g_assert_not_reached ();
1455 shortcuts_free_row_data (impl, &iter);
1456 gtk_list_store_remove (impl->shortcuts_model, &iter);
1459 gtk_tree_path_free (path);
1463 shortcuts_update_count (GtkFileChooserDefault *impl,
1464 ShortcutsIndex type,
1469 case SHORTCUTS_HOME:
1471 impl->has_home = FALSE;
1473 impl->has_home = TRUE;
1476 case SHORTCUTS_DESKTOP:
1478 impl->has_desktop = FALSE;
1480 impl->has_desktop = TRUE;
1483 case SHORTCUTS_VOLUMES:
1484 impl->num_volumes += value;
1487 case SHORTCUTS_SHORTCUTS:
1488 impl->num_shortcuts += value;
1491 case SHORTCUTS_BOOKMARKS:
1492 impl->num_bookmarks += value;
1495 case SHORTCUTS_CURRENT_FOLDER:
1497 impl->shortcuts_current_folder_active = FALSE;
1499 impl->shortcuts_current_folder_active = TRUE;
1508 struct ShortcutsInsertRequest
1510 GtkFileChooserDefault *impl;
1514 GtkTreeRowReference *row_ref;
1515 ShortcutsIndex type;
1521 get_file_info_finished (GCancellable *cancellable,
1523 const GError *error,
1527 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1531 GCancellable *model_cancellable;
1532 struct ShortcutsInsertRequest *request = data;
1534 path = gtk_tree_row_reference_get_path (request->row_ref);
1536 /* Handle doesn't exist anymore in the model */
1539 pos = gtk_tree_path_get_indices (path)[0];
1540 gtk_tree_model_get_iter (GTK_TREE_MODEL (request->impl->shortcuts_model),
1542 gtk_tree_path_free (path);
1544 /* validate cancellable, else goto out */
1545 gtk_tree_model_get (GTK_TREE_MODEL (request->impl->shortcuts_model), &iter,
1546 SHORTCUTS_COL_CANCELLABLE, &model_cancellable,
1548 if (cancellable != model_cancellable)
1551 /* set the cancellable to NULL in the model (we unref later on) */
1552 gtk_list_store_set (request->impl->shortcuts_model, &iter,
1553 SHORTCUTS_COL_CANCELLABLE, NULL,
1561 gtk_list_store_remove (request->impl->shortcuts_model, &iter);
1562 shortcuts_update_count (request->impl, request->type, -1);
1564 if (request->type == SHORTCUTS_HOME)
1568 home = g_file_new_for_path (g_get_home_dir ());
1569 error_getting_info_dialog (request->impl, home, g_error_copy (error));
1570 g_object_unref (home);
1572 else if (request->type == SHORTCUTS_CURRENT_FOLDER)
1574 /* Remove the current folder separator */
1575 gint separator_pos = shortcuts_get_index (request->impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1576 shortcuts_remove_rows (request->impl, separator_pos, 1);
1582 if (!request->label_copy)
1583 request->label_copy = g_strdup (g_file_info_get_display_name (info));
1584 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (request->impl),
1585 request->impl->icon_size);
1587 gtk_list_store_set (request->impl->shortcuts_model, &iter,
1588 SHORTCUTS_COL_PIXBUF, pixbuf,
1589 SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1590 SHORTCUTS_COL_NAME, request->label_copy,
1591 SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_FILE,
1592 SHORTCUTS_COL_REMOVABLE, request->removable,
1595 if (request->impl->shortcuts_pane_filter_model)
1596 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (request->impl->shortcuts_pane_filter_model));
1598 if (request->impl->shortcuts_combo_filter_model)
1599 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (request->impl->shortcuts_combo_filter_model));
1601 if (request->type == SHORTCUTS_CURRENT_FOLDER &&
1602 request->impl->save_folder_combo != NULL)
1604 /* The current folder is updated via _activate_iter(), don't
1605 * have save_folder_combo_changed_cb() call _activate_iter()
1608 g_signal_handlers_block_by_func (request->impl->save_folder_combo,
1609 G_CALLBACK (save_folder_combo_changed_cb),
1612 if (request->impl->has_search)
1615 if (request->impl->has_recent)
1618 gtk_combo_box_set_active (GTK_COMBO_BOX (request->impl->save_folder_combo), pos);
1619 g_signal_handlers_unblock_by_func (request->impl->save_folder_combo,
1620 G_CALLBACK (save_folder_combo_changed_cb),
1625 g_object_unref (pixbuf);
1628 g_object_unref (request->impl);
1629 g_object_unref (request->file);
1630 gtk_tree_row_reference_free (request->row_ref);
1631 g_free (request->label_copy);
1634 g_object_unref (cancellable);
1637 /* FIXME: GtkFileSystem needs a function to split a remote path
1638 * into hostname and path components, or maybe just have a
1639 * gtk_file_system_path_get_display_name().
1641 * This function is also used in gtkfilechooserbutton.c
1644 _gtk_file_chooser_label_for_file (GFile *file)
1646 const gchar *path, *start, *end, *p;
1647 gchar *uri, *host, *label;
1649 uri = g_file_get_uri (file);
1651 start = strstr (uri, "://");
1655 path = strchr (start, '/');
1660 end = uri + strlen (uri);
1664 /* strip username */
1665 p = strchr (start, '@');
1669 p = strchr (start, ':');
1673 host = g_strndup (start, end - start);
1675 /* Translators: the first string is a path and the second string
1676 * is a hostname. Nautilus and the panel contain the same string
1679 label = g_strdup_printf (_("%1$s on %2$s"), path, host);
1685 label = g_strdup (uri);
1693 /* Inserts a path in the shortcuts tree, making a copy of it; alternatively,
1694 * inserts a volume. A position of -1 indicates the end of the tree.
1697 shortcuts_insert_file (GtkFileChooserDefault *impl,
1699 ShortcutType shortcut_type,
1700 GtkFileSystemVolume *volume,
1704 ShortcutsIndex type)
1707 GdkPixbuf *pixbuf = NULL;
1708 gpointer data = NULL;
1710 GtkIconTheme *icon_theme;
1712 profile_start ("start shortcut", NULL);
1714 if (shortcut_type == SHORTCUT_TYPE_VOLUME)
1717 label_copy = _gtk_file_system_volume_get_display_name (volume);
1718 pixbuf = _gtk_file_system_volume_render_icon (volume, GTK_WIDGET (impl),
1719 impl->icon_size, NULL);
1721 else if (shortcut_type == SHORTCUT_TYPE_FILE)
1723 if (g_file_is_native (file))
1725 struct ShortcutsInsertRequest *request;
1726 GCancellable *cancellable;
1729 request = g_new0 (struct ShortcutsInsertRequest, 1);
1730 request->impl = g_object_ref (impl);
1731 request->file = g_object_ref (file);
1732 request->name_only = TRUE;
1733 request->removable = removable;
1735 request->type = type;
1737 request->label_copy = g_strdup (label);
1740 gtk_list_store_append (impl->shortcuts_model, &iter);
1742 gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
1744 p = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
1745 request->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->shortcuts_model), p);
1746 gtk_tree_path_free (p);
1748 cancellable = _gtk_file_system_get_info (request->impl->file_system, request->file,
1749 "standard::is-hidden,standard::is-backup,standard::display-name,standard::icon",
1750 get_file_info_finished, request);
1752 gtk_list_store_set (impl->shortcuts_model, &iter,
1753 SHORTCUTS_COL_DATA, g_object_ref (file),
1754 SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_FILE,
1755 SHORTCUTS_COL_CANCELLABLE, cancellable,
1758 shortcuts_update_count (impl, type, 1);
1764 /* Don't call get_info for remote paths to avoid latency and
1767 data = g_object_ref (file);
1769 label_copy = g_strdup (label);
1771 label_copy = _gtk_file_chooser_label_for_file (file);
1773 /* If we switch to a better bookmarks file format (XBEL), we
1774 * should use mime info to get a better icon.
1776 icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
1777 pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote",
1778 impl->icon_size, 0, NULL);
1783 g_assert_not_reached ();
1789 gtk_list_store_append (impl->shortcuts_model, &iter);
1791 gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
1793 shortcuts_update_count (impl, type, 1);
1795 gtk_list_store_set (impl->shortcuts_model, &iter,
1796 SHORTCUTS_COL_PIXBUF, pixbuf,
1797 SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1798 SHORTCUTS_COL_NAME, label_copy,
1799 SHORTCUTS_COL_DATA, data,
1800 SHORTCUTS_COL_TYPE, shortcut_type,
1801 SHORTCUTS_COL_REMOVABLE, removable,
1802 SHORTCUTS_COL_CANCELLABLE, NULL,
1805 if (impl->shortcuts_pane_filter_model)
1806 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model));
1808 if (impl->shortcuts_combo_filter_model)
1809 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model));
1811 if (type == SHORTCUTS_CURRENT_FOLDER && impl->save_folder_combo != NULL)
1813 /* The current folder is updated via _activate_iter(), don't
1814 * have save_folder_combo_changed_cb() call _activate_iter()
1817 gint combo_pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER);
1819 if (impl->has_search)
1822 if (impl->has_recent)
1825 g_signal_handlers_block_by_func (impl->save_folder_combo,
1826 G_CALLBACK (save_folder_combo_changed_cb),
1829 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), combo_pos);
1830 g_signal_handlers_unblock_by_func (impl->save_folder_combo,
1831 G_CALLBACK (save_folder_combo_changed_cb),
1835 g_free (label_copy);
1838 g_object_unref (pixbuf);
1840 profile_end ("end", NULL);
1844 shortcuts_append_search (GtkFileChooserDefault *impl)
1849 pixbuf = render_search_icon (impl);
1851 gtk_list_store_append (impl->shortcuts_model, &iter);
1852 gtk_list_store_set (impl->shortcuts_model, &iter,
1853 SHORTCUTS_COL_PIXBUF, pixbuf,
1854 SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1855 SHORTCUTS_COL_NAME, _("Search"),
1856 SHORTCUTS_COL_DATA, NULL,
1857 SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_SEARCH,
1858 SHORTCUTS_COL_REMOVABLE, FALSE,
1862 g_object_unref (pixbuf);
1864 impl->has_search = TRUE;
1868 shortcuts_append_recent (GtkFileChooserDefault *impl)
1873 pixbuf = render_recent_icon (impl);
1875 gtk_list_store_append (impl->shortcuts_model, &iter);
1876 gtk_list_store_set (impl->shortcuts_model, &iter,
1877 SHORTCUTS_COL_PIXBUF, pixbuf,
1878 SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1879 SHORTCUTS_COL_NAME, _("Recently Used"),
1880 SHORTCUTS_COL_DATA, NULL,
1881 SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_RECENT,
1882 SHORTCUTS_COL_REMOVABLE, FALSE,
1886 g_object_unref (pixbuf);
1888 impl->has_recent = TRUE;
1891 /* Appends an item for the user's home directory to the shortcuts model */
1893 shortcuts_append_home (GtkFileChooserDefault *impl)
1895 const char *home_path;
1898 profile_start ("start", NULL);
1900 home_path = g_get_home_dir ();
1901 if (home_path == NULL)
1903 profile_end ("end - no home directory!?", NULL);
1907 home = g_file_new_for_path (home_path);
1908 shortcuts_insert_file (impl, -1, SHORTCUT_TYPE_FILE, NULL, home, NULL, FALSE, SHORTCUTS_HOME);
1909 impl->has_home = TRUE;
1911 g_object_unref (home);
1913 profile_end ("end", NULL);
1916 /* Appends the ~/Desktop directory to the shortcuts model */
1918 shortcuts_append_desktop (GtkFileChooserDefault *impl)
1923 profile_start ("start", NULL);
1925 name = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
1926 /* "To disable a directory, point it to the homedir."
1927 * See http://freedesktop.org/wiki/Software/xdg-user-dirs
1929 if (!g_strcmp0 (name, g_get_home_dir ()))
1931 profile_end ("end", NULL);
1935 file = g_file_new_for_path (name);
1936 shortcuts_insert_file (impl, -1, SHORTCUT_TYPE_FILE, NULL, file, _("Desktop"), FALSE, SHORTCUTS_DESKTOP);
1937 impl->has_desktop = TRUE;
1939 /* We do not actually pop up an error dialog if there is no desktop directory
1940 * because some people may really not want to have one.
1943 g_object_unref (file);
1945 profile_end ("end", NULL);
1948 /* Appends a list of GFile to the shortcuts model; returns how many were inserted */
1950 shortcuts_append_bookmarks (GtkFileChooserDefault *impl,
1957 profile_start ("start", NULL);
1959 start_row = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR) + 1;
1962 for (; bookmarks; bookmarks = bookmarks->next)
1966 file = bookmarks->data;
1968 if (impl->local_only && !g_file_is_native (file))
1971 if (shortcut_find_position (impl, file) != -1)
1974 label = _gtk_file_system_get_bookmark_label (impl->file_system, file);
1976 shortcuts_insert_file (impl, start_row + num_inserted, SHORTCUT_TYPE_FILE, NULL, file, label, TRUE, SHORTCUTS_BOOKMARKS);
1982 profile_end ("end", NULL);
1984 return num_inserted;
1987 /* Returns the index for the corresponding item in the shortcuts bar */
1989 shortcuts_get_index (GtkFileChooserDefault *impl,
1990 ShortcutsIndex where)
1996 if (where == SHORTCUTS_SEARCH)
1999 n += impl->has_search ? 1 : 0;
2001 if (where == SHORTCUTS_RECENT)
2004 n += impl->has_recent ? 1 : 0;
2006 if (where == SHORTCUTS_RECENT_SEPARATOR)
2009 n += impl->has_recent ? 1 : 0;
2011 if (where == SHORTCUTS_HOME)
2014 n += impl->has_home ? 1 : 0;
2016 if (where == SHORTCUTS_DESKTOP)
2019 n += impl->has_desktop ? 1 : 0;
2021 if (where == SHORTCUTS_VOLUMES)
2024 n += impl->num_volumes;
2026 if (where == SHORTCUTS_SHORTCUTS)
2029 n += impl->num_shortcuts;
2031 if (where == SHORTCUTS_BOOKMARKS_SEPARATOR)
2034 /* If there are no bookmarks there won't be a separator */
2035 n += (impl->num_bookmarks > 0) ? 1 : 0;
2037 if (where == SHORTCUTS_BOOKMARKS)
2040 n += impl->num_bookmarks;
2042 if (where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR)
2047 if (where == SHORTCUTS_CURRENT_FOLDER)
2050 g_assert_not_reached ();
2057 /* Adds all the file system volumes to the shortcuts model */
2059 shortcuts_add_volumes (GtkFileChooserDefault *impl)
2064 gboolean old_changing_folders;
2066 profile_start ("start", NULL);
2068 old_changing_folders = impl->changing_folder;
2069 impl->changing_folder = TRUE;
2071 start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
2072 shortcuts_remove_rows (impl, start_row, impl->num_volumes);
2073 impl->num_volumes = 0;
2075 list = _gtk_file_system_list_volumes (impl->file_system);
2079 for (l = list; l; l = l->next)
2081 GtkFileSystemVolume *volume;
2085 if (impl->local_only)
2087 if (_gtk_file_system_volume_is_mounted (volume))
2090 gboolean base_is_native = TRUE;
2092 base_file = _gtk_file_system_volume_get_root (volume);
2093 if (base_file != NULL)
2095 base_is_native = g_file_is_native (base_file);
2096 g_object_unref (base_file);
2099 if (!base_is_native)
2104 shortcuts_insert_file (impl, start_row + n, SHORTCUT_TYPE_VOLUME, volume, NULL, NULL, FALSE, SHORTCUTS_VOLUMES);
2108 impl->num_volumes = n;
2109 g_slist_free (list);
2111 if (impl->shortcuts_pane_filter_model)
2112 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model));
2114 if (impl->shortcuts_combo_filter_model)
2115 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model));
2117 impl->changing_folder = old_changing_folders;
2119 profile_end ("end", NULL);
2122 /* Inserts a separator node in the shortcuts list */
2124 shortcuts_insert_separator (GtkFileChooserDefault *impl,
2125 ShortcutsIndex where)
2129 g_assert (where == SHORTCUTS_RECENT_SEPARATOR ||
2130 where == SHORTCUTS_BOOKMARKS_SEPARATOR ||
2131 where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2133 gtk_list_store_insert (impl->shortcuts_model, &iter,
2134 shortcuts_get_index (impl, where));
2135 gtk_list_store_set (impl->shortcuts_model, &iter,
2136 SHORTCUTS_COL_PIXBUF, NULL,
2137 SHORTCUTS_COL_PIXBUF_VISIBLE, FALSE,
2138 SHORTCUTS_COL_NAME, NULL,
2139 SHORTCUTS_COL_DATA, NULL,
2140 SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_SEPARATOR,
2144 /* Updates the list of bookmarks */
2146 shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
2149 gboolean old_changing_folders;
2151 GFile *list_selected = NULL;
2152 GFile *combo_selected = NULL;
2153 ShortcutType shortcut_type;
2156 profile_start ("start", NULL);
2158 old_changing_folders = impl->changing_folder;
2159 impl->changing_folder = TRUE;
2161 if (shortcuts_get_selected (impl, &iter))
2163 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model),
2165 SHORTCUTS_COL_DATA, &col_data,
2166 SHORTCUTS_COL_TYPE, &shortcut_type,
2169 if (col_data && shortcut_type == SHORTCUT_TYPE_FILE)
2170 list_selected = g_object_ref (col_data);
2173 if (impl->save_folder_combo &&
2174 gtk_combo_box_get_active_iter (GTK_COMBO_BOX (impl->save_folder_combo),
2177 GtkTreeIter child_iter;
2179 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model),
2182 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model),
2184 SHORTCUTS_COL_DATA, &col_data,
2185 SHORTCUTS_COL_TYPE, &shortcut_type,
2188 if (col_data && shortcut_type == SHORTCUT_TYPE_FILE)
2189 combo_selected = g_object_ref (col_data);
2192 if (impl->num_bookmarks > 0)
2193 shortcuts_remove_rows (impl,
2194 shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR),
2195 impl->num_bookmarks + 1);
2197 impl->num_bookmarks = 0;
2198 shortcuts_insert_separator (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
2200 bookmarks = _gtk_file_system_list_bookmarks (impl->file_system);
2201 shortcuts_append_bookmarks (impl, bookmarks);
2202 g_slist_foreach (bookmarks, (GFunc) g_object_unref, NULL);
2203 g_slist_free (bookmarks);
2205 if (impl->num_bookmarks == 0)
2206 shortcuts_remove_rows (impl, shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR), 1);
2208 if (impl->shortcuts_pane_filter_model)
2209 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model));
2211 if (impl->shortcuts_combo_filter_model)
2212 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model));
2216 shortcuts_find_folder (impl, list_selected);
2217 g_object_unref (list_selected);
2224 pos = shortcut_find_position (impl, combo_selected);
2227 if (impl->has_search)
2230 if (impl->has_recent)
2233 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), pos);
2236 g_object_unref (combo_selected);
2239 impl->changing_folder = old_changing_folders;
2241 profile_end ("end", NULL);
2244 /* Appends a separator and a row to the shortcuts list for the current folder */
2246 shortcuts_add_current_folder (GtkFileChooserDefault *impl)
2251 g_assert (!impl->shortcuts_current_folder_active);
2255 g_assert (impl->current_folder != NULL);
2257 pos = shortcut_find_position (impl, impl->current_folder);
2260 GtkFileSystemVolume *volume;
2265 shortcuts_insert_separator (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2269 pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER);
2271 volume = _gtk_file_system_get_volume_for_file (impl->file_system, impl->current_folder);
2273 base_file = _gtk_file_system_volume_get_root (volume);
2277 if (base_file && g_file_equal (base_file, impl->current_folder))
2278 shortcuts_insert_file (impl, pos, SHORTCUT_TYPE_VOLUME, volume, NULL, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER);
2280 shortcuts_insert_file (impl, pos, SHORTCUT_TYPE_FILE, NULL, impl->current_folder, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER);
2283 g_object_unref (base_file);
2285 else if (impl->save_folder_combo != NULL)
2287 if (impl->has_search)
2290 if (impl->has_recent)
2291 pos -= 2; /* + separator */
2293 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), pos);
2297 /* Updates the current folder row in the shortcuts model */
2299 shortcuts_update_current_folder (GtkFileChooserDefault *impl)
2303 pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2305 if (impl->shortcuts_current_folder_active)
2307 shortcuts_remove_rows (impl, pos, 2);
2308 impl->shortcuts_current_folder_active = FALSE;
2311 shortcuts_add_current_folder (impl);
2314 /* Filter function used for the shortcuts filter model */
2316 shortcuts_pane_filter_cb (GtkTreeModel *model,
2320 GtkFileChooserDefault *impl;
2324 impl = GTK_FILE_CHOOSER_DEFAULT (data);
2326 path = gtk_tree_model_get_path (model, iter);
2330 pos = *gtk_tree_path_get_indices (path);
2331 gtk_tree_path_free (path);
2333 return (pos < shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR));
2336 /* Creates the list model for shortcuts */
2338 shortcuts_model_create (GtkFileChooserDefault *impl)
2340 /* Keep this order in sync with the SHORCUTS_COL_* enum values */
2341 impl->shortcuts_model = gtk_list_store_new (SHORTCUTS_COL_NUM_COLUMNS,
2342 GDK_TYPE_PIXBUF, /* pixbuf */
2343 G_TYPE_STRING, /* name */
2344 G_TYPE_POINTER, /* path or volume */
2345 G_TYPE_INT, /* ShortcutType */
2346 G_TYPE_BOOLEAN, /* removable */
2347 G_TYPE_BOOLEAN, /* pixbuf cell visibility */
2348 G_TYPE_POINTER); /* GCancellable */
2350 if (search_is_possible (impl))
2352 shortcuts_append_search (impl);
2355 if (impl->recent_manager)
2357 shortcuts_append_recent (impl);
2358 shortcuts_insert_separator (impl, SHORTCUTS_RECENT_SEPARATOR);
2361 if (impl->file_system)
2363 shortcuts_append_home (impl);
2364 shortcuts_append_desktop (impl);
2365 shortcuts_add_volumes (impl);
2368 impl->shortcuts_pane_filter_model = shortcuts_pane_model_filter_new (impl,
2369 GTK_TREE_MODEL (impl->shortcuts_model),
2372 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
2373 shortcuts_pane_filter_cb,
2378 /* Callback used when the "New Folder" button is clicked */
2380 new_folder_button_clicked (GtkButton *button,
2381 GtkFileChooserDefault *impl)
2386 if (!impl->browse_files_model)
2387 return; /* FIXME: this sucks. Disable the New Folder button or something. */
2389 /* Prevent button from being clicked twice */
2390 gtk_widget_set_sensitive (impl->browse_new_folder_button, FALSE);
2392 _gtk_file_system_model_add_editable (impl->browse_files_model, &iter);
2394 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->browse_files_model), &iter);
2395 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_files_tree_view),
2396 path, impl->list_name_column,
2399 g_object_set (impl->list_name_renderer, "editable", TRUE, NULL);
2400 gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view),
2402 impl->list_name_column,
2405 gtk_tree_path_free (path);
2408 /* Idle handler for creating a new folder after editing its name cell, or for
2409 * canceling the editing.
2412 edited_idle_cb (GtkFileChooserDefault *impl)
2414 GDK_THREADS_ENTER ();
2416 g_source_destroy (impl->edited_idle);
2417 impl->edited_idle = NULL;
2419 _gtk_file_system_model_remove_editable (impl->browse_files_model);
2420 g_object_set (impl->list_name_renderer, "editable", FALSE, NULL);
2422 gtk_widget_set_sensitive (impl->browse_new_folder_button, TRUE);
2424 if (impl->edited_new_text) /* not cancelled? */
2426 GError *error = NULL;
2429 file = g_file_get_child_for_display_name (impl->current_folder,
2430 impl->edited_new_text,
2434 GError *error = NULL;
2436 if (g_file_make_directory (file, NULL, &error))
2437 change_folder_and_display_error (impl, file, FALSE);
2439 error_creating_folder_dialog (impl, file, error);
2441 g_object_unref (file);
2444 error_creating_folder_dialog (impl, file, error);
2446 g_free (impl->edited_new_text);
2447 impl->edited_new_text = NULL;
2450 GDK_THREADS_LEAVE ();
2456 queue_edited_idle (GtkFileChooserDefault *impl,
2457 const gchar *new_text)
2459 /* We create the folder in an idle handler so that we don't modify the tree
2463 if (!impl->edited_idle)
2465 impl->edited_idle = g_idle_source_new ();
2466 g_source_set_closure (impl->edited_idle,
2467 g_cclosure_new_object (G_CALLBACK (edited_idle_cb),
2469 g_source_attach (impl->edited_idle, NULL);
2472 g_free (impl->edited_new_text);
2473 impl->edited_new_text = g_strdup (new_text);
2476 /* Callback used from the text cell renderer when the new folder is named */
2478 renderer_edited_cb (GtkCellRendererText *cell_renderer_text,
2480 const gchar *new_text,
2481 GtkFileChooserDefault *impl)
2483 /* work around bug #154921 */
2484 g_object_set (cell_renderer_text,
2485 "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
2486 queue_edited_idle (impl, new_text);
2489 /* Callback used from the text cell renderer when the new folder edition gets
2493 renderer_editing_canceled_cb (GtkCellRendererText *cell_renderer_text,
2494 GtkFileChooserDefault *impl)
2496 /* work around bug #154921 */
2497 g_object_set (cell_renderer_text,
2498 "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
2499 queue_edited_idle (impl, NULL);
2502 /* Creates the widgets for the filter combo box */
2504 filter_create (GtkFileChooserDefault *impl)
2506 impl->filter_combo = gtk_combo_box_new_text ();
2507 gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (impl->filter_combo), FALSE);
2509 g_signal_connect (impl->filter_combo, "changed",
2510 G_CALLBACK (filter_combo_changed), impl);
2512 gtk_widget_set_tooltip_text (impl->filter_combo,
2513 _("Select which types of files are shown"));
2515 return impl->filter_combo;
2519 button_new (GtkFileChooserDefault *impl,
2521 const char *stock_id,
2529 button = gtk_button_new_with_mnemonic (text);
2530 image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
2531 gtk_button_set_image (GTK_BUTTON (button), image);
2533 gtk_widget_set_sensitive (button, sensitive);
2534 g_signal_connect (button, "clicked", callback, impl);
2537 gtk_widget_show (button);
2542 /* Looks for a path among the shortcuts; returns its index or -1 if it doesn't exist */
2544 shortcut_find_position (GtkFileChooserDefault *impl,
2549 int current_folder_separator_idx;
2551 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
2554 current_folder_separator_idx = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2557 /* FIXME: is this still needed? */
2558 if (current_folder_separator_idx >= impl->shortcuts_model->length)
2562 for (i = 0; i < current_folder_separator_idx; i++)
2565 ShortcutType shortcut_type;
2567 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2568 SHORTCUTS_COL_DATA, &col_data,
2569 SHORTCUTS_COL_TYPE, &shortcut_type,
2574 if (shortcut_type == SHORTCUT_TYPE_VOLUME)
2576 GtkFileSystemVolume *volume;
2581 base_file = _gtk_file_system_volume_get_root (volume);
2583 exists = base_file && g_file_equal (file, base_file);
2586 g_object_unref (base_file);
2591 else if (shortcut_type == SHORTCUT_TYPE_FILE)
2595 model_file = col_data;
2597 if (model_file && g_file_equal (model_file, file))
2602 if (i < current_folder_separator_idx - 1)
2604 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
2605 g_assert_not_reached ();
2612 /* Tries to add a bookmark from a path name */
2614 shortcuts_add_bookmark_from_file (GtkFileChooserDefault *impl,
2620 g_return_val_if_fail (G_IS_FILE (file), FALSE);
2622 if (shortcut_find_position (impl, file) != -1)
2626 if (!_gtk_file_system_insert_bookmark (impl->file_system, file, pos, &error))
2628 error_adding_bookmark_dialog (impl, file, error);
2636 add_bookmark_foreach_cb (GtkTreeModel *model,
2641 GtkFileChooserDefault *impl;
2642 GtkFileSystemModel *fs_model;
2643 GtkTreeIter child_iter;
2646 impl = (GtkFileChooserDefault *) data;
2648 switch (impl->operation_mode)
2650 case OPERATION_MODE_BROWSE:
2651 fs_model = impl->browse_files_model;
2652 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter);
2653 file = _gtk_file_system_model_get_file (fs_model, &child_iter);
2656 case OPERATION_MODE_SEARCH:
2657 search_get_valid_child_iter (impl, &child_iter, iter);
2658 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter,
2659 SEARCH_MODEL_COL_FILE, &file,
2663 case OPERATION_MODE_RECENT:
2664 recent_get_valid_child_iter (impl, &child_iter, iter);
2665 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter,
2666 RECENT_MODEL_COL_FILE, &file,
2671 shortcuts_add_bookmark_from_file (impl, file, -1);
2674 /* Adds a bookmark from the currently selected item in the file list */
2676 bookmarks_add_selected_folder (GtkFileChooserDefault *impl)
2678 GtkTreeSelection *selection;
2680 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2682 if (gtk_tree_selection_count_selected_rows (selection) == 0)
2683 shortcuts_add_bookmark_from_file (impl, impl->current_folder, -1);
2685 gtk_tree_selection_selected_foreach (selection,
2686 add_bookmark_foreach_cb,
2690 /* Callback used when the "Add bookmark" button is clicked */
2692 add_bookmark_button_clicked_cb (GtkButton *button,
2693 GtkFileChooserDefault *impl)
2695 bookmarks_add_selected_folder (impl);
2698 /* Returns TRUE plus an iter in the shortcuts_model if a row is selected;
2699 * returns FALSE if no shortcut is selected.
2702 shortcuts_get_selected (GtkFileChooserDefault *impl,
2705 GtkTreeSelection *selection;
2706 GtkTreeIter parent_iter;
2708 if (!impl->browse_shortcuts_tree_view)
2711 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
2713 if (!gtk_tree_selection_get_selected (selection, NULL, &parent_iter))
2716 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
2722 /* Removes the selected bookmarks */
2724 remove_selected_bookmarks (GtkFileChooserDefault *impl)
2732 if (!shortcuts_get_selected (impl, &iter))
2735 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2736 SHORTCUTS_COL_DATA, &col_data,
2737 SHORTCUTS_COL_REMOVABLE, &removable,
2743 g_assert (col_data != NULL);
2748 if (!_gtk_file_system_remove_bookmark (impl->file_system, file, &error))
2749 error_removing_bookmark_dialog (impl, file, error);
2752 /* Callback used when the "Remove bookmark" button is clicked */
2754 remove_bookmark_button_clicked_cb (GtkButton *button,
2755 GtkFileChooserDefault *impl)
2757 remove_selected_bookmarks (impl);
2760 struct selection_check_closure {
2761 GtkFileChooserDefault *impl;
2764 gboolean all_folders;
2767 /* Used from gtk_tree_selection_selected_foreach() */
2769 selection_check_foreach_cb (GtkTreeModel *model,
2774 struct selection_check_closure *closure;
2775 GtkTreeIter child_iter;
2780 closure->num_selected++;
2782 switch (closure->impl->operation_mode)
2784 case OPERATION_MODE_BROWSE:
2785 gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
2786 info = _gtk_file_system_model_get_info (closure->impl->browse_files_model, &child_iter);
2787 is_folder = info ? (_gtk_file_info_consider_as_directory (info)) : FALSE;
2790 case OPERATION_MODE_SEARCH:
2791 search_get_valid_child_iter (closure->impl, &child_iter, iter);
2792 gtk_tree_model_get (GTK_TREE_MODEL (closure->impl->search_model), &child_iter,
2793 SEARCH_MODEL_COL_IS_FOLDER, &is_folder,
2797 case OPERATION_MODE_RECENT:
2798 recent_get_valid_child_iter (closure->impl, &child_iter, iter);
2799 gtk_tree_model_get (GTK_TREE_MODEL (closure->impl->recent_model), &child_iter,
2800 RECENT_MODEL_COL_IS_FOLDER, &is_folder,
2805 closure->all_folders = closure->all_folders && is_folder;
2806 closure->all_files = closure->all_files && !is_folder;
2809 /* Checks whether the selected items in the file list are all files or all folders */
2811 selection_check (GtkFileChooserDefault *impl,
2813 gboolean *all_files,
2814 gboolean *all_folders)
2816 struct selection_check_closure closure;
2817 GtkTreeSelection *selection;
2819 closure.impl = impl;
2820 closure.num_selected = 0;
2821 closure.all_files = TRUE;
2822 closure.all_folders = TRUE;
2824 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2825 gtk_tree_selection_selected_foreach (selection,
2826 selection_check_foreach_cb,
2829 g_assert (closure.num_selected == 0 || !(closure.all_files && closure.all_folders));
2832 *num_selected = closure.num_selected;
2835 *all_files = closure.all_files;
2838 *all_folders = closure.all_folders;
2841 struct get_selected_file_closure {
2842 GtkFileChooserDefault *impl;
2847 get_selected_file_foreach_cb (GtkTreeModel *model,
2852 struct get_selected_file_closure *closure;
2853 GtkTreeIter child_iter;
2857 switch (closure->impl->operation_mode)
2859 case OPERATION_MODE_BROWSE:
2860 gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
2861 closure->file = _gtk_file_system_model_get_file (closure->impl->browse_files_model, &child_iter);
2864 case OPERATION_MODE_SEARCH:
2865 search_get_valid_child_iter (closure->impl, &child_iter, iter);
2866 gtk_tree_model_get (GTK_TREE_MODEL (closure->impl->search_model), &child_iter,
2867 SEARCH_MODEL_COL_FILE, &closure->file,
2871 case OPERATION_MODE_RECENT:
2872 recent_get_valid_child_iter (closure->impl, &child_iter, iter);
2873 gtk_tree_model_get (GTK_TREE_MODEL (closure->impl->recent_model), &child_iter,
2874 RECENT_MODEL_COL_FILE, &closure->file,
2880 /* Returns a selected path from the file list */
2882 get_selected_file (GtkFileChooserDefault *impl)
2884 struct get_selected_file_closure closure;
2885 GtkTreeSelection *selection;
2887 closure.impl = impl;
2888 closure.file = NULL;
2890 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2891 gtk_tree_selection_selected_foreach (selection,
2892 get_selected_file_foreach_cb,
2895 return closure.file;
2899 GtkFileChooserDefault *impl;
2901 } UpdateTooltipData;
2904 update_tooltip (GtkTreeModel *model,
2909 UpdateTooltipData *udata = data;
2910 GtkTreeIter child_iter;
2913 if (udata->tip == NULL)
2915 const gchar *display_name;
2917 switch (udata->impl->operation_mode)
2919 case OPERATION_MODE_BROWSE:
2920 gtk_tree_model_sort_convert_iter_to_child_iter (udata->impl->sort_model,
2923 info = _gtk_file_system_model_get_info (udata->impl->browse_files_model, &child_iter);
2924 display_name = g_file_info_get_display_name (info);
2927 case OPERATION_MODE_SEARCH:
2928 search_get_valid_child_iter (udata->impl, &child_iter, iter);
2929 gtk_tree_model_get (GTK_TREE_MODEL (udata->impl->search_model), &child_iter,
2930 SEARCH_MODEL_COL_DISPLAY_NAME, &display_name,
2934 case OPERATION_MODE_RECENT:
2935 recent_get_valid_child_iter (udata->impl, &child_iter, iter);
2936 gtk_tree_model_get (GTK_TREE_MODEL (udata->impl->recent_model), &child_iter,
2937 RECENT_MODEL_COL_DISPLAY_NAME, &display_name,
2942 udata->tip = g_strdup_printf (_("Add the folder '%s' to the bookmarks"),
2948 /* Sensitize the "add bookmark" button if all the selected items are folders, or
2949 * if there are no selected items *and* the current folder is not in the
2950 * bookmarks list. De-sensitize the button otherwise.
2953 bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl)
2956 gboolean all_folders;
2960 selection_check (impl, &num_selected, NULL, &all_folders);
2962 if (num_selected == 0)
2963 active = (impl->current_folder != NULL) && (shortcut_find_position (impl, impl->current_folder) == -1);
2964 else if (num_selected == 1)
2968 file = get_selected_file (impl);
2969 active = all_folders && (shortcut_find_position (impl, file) == -1);
2972 active = all_folders;
2974 gtk_widget_set_sensitive (impl->browse_shortcuts_add_button, active);
2976 if (impl->browse_files_popup_menu_add_shortcut_item)
2977 gtk_widget_set_sensitive (impl->browse_files_popup_menu_add_shortcut_item,
2978 (num_selected == 0) ? FALSE : active);
2982 if (num_selected == 0)
2983 tip = g_strdup_printf (_("Add the current folder to the bookmarks"));
2984 else if (num_selected > 1)
2985 tip = g_strdup_printf (_("Add the selected folders to the bookmarks"));
2988 GtkTreeSelection *selection;
2989 UpdateTooltipData data;
2991 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2994 gtk_tree_selection_selected_foreach (selection, update_tooltip, &data);
2998 gtk_widget_set_tooltip_text (impl->browse_shortcuts_add_button, tip);
3003 /* Sets the sensitivity of the "remove bookmark" button depending on whether a
3004 * bookmark row is selected in the shortcuts tree.
3007 bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl)
3010 gboolean removable = FALSE;
3013 if (shortcuts_get_selected (impl, &iter))
3014 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
3015 SHORTCUTS_COL_REMOVABLE, &removable,
3016 SHORTCUTS_COL_NAME, &name,
3019 gtk_widget_set_sensitive (impl->browse_shortcuts_remove_button, removable);
3025 tip = g_strdup_printf (_("Remove the bookmark '%s'"), name);
3026 gtk_widget_set_tooltip_text (impl->browse_shortcuts_remove_button, tip);
3034 shortcuts_check_popup_sensitivity (GtkFileChooserDefault *impl)
3037 gboolean removable = FALSE;
3039 if (impl->browse_shortcuts_popup_menu == NULL)
3042 if (shortcuts_get_selected (impl, &iter))
3043 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
3044 SHORTCUTS_COL_REMOVABLE, &removable,
3047 gtk_widget_set_sensitive (impl->browse_shortcuts_popup_menu_remove_item, removable);
3048 gtk_widget_set_sensitive (impl->browse_shortcuts_popup_menu_rename_item, removable);
3051 /* GtkWidget::drag-begin handler for the shortcuts list. */
3053 shortcuts_drag_begin_cb (GtkWidget *widget,
3054 GdkDragContext *context,
3055 GtkFileChooserDefault *impl)
3058 impl->shortcuts_drag_context = g_object_ref (context);
3063 /* Removes the idle handler for outside drags */
3065 shortcuts_cancel_drag_outside_idle (GtkFileChooserDefault *impl)
3067 if (!impl->shortcuts_drag_outside_idle)
3070 g_source_destroy (impl->shortcuts_drag_outside_idle);
3071 impl->shortcuts_drag_outside_idle = NULL;
3075 /* GtkWidget::drag-end handler for the shortcuts list. */
3077 shortcuts_drag_end_cb (GtkWidget *widget,
3078 GdkDragContext *context,
3079 GtkFileChooserDefault *impl)
3082 g_object_unref (impl->shortcuts_drag_context);
3084 shortcuts_cancel_drag_outside_idle (impl);
3086 if (!impl->shortcuts_drag_outside)
3089 gtk_button_clicked (GTK_BUTTON (impl->browse_shortcuts_remove_button));
3091 impl->shortcuts_drag_outside = FALSE;
3095 /* GtkWidget::drag-data-delete handler for the shortcuts list. */
3097 shortcuts_drag_data_delete_cb (GtkWidget *widget,
3098 GdkDragContext *context,
3099 GtkFileChooserDefault *impl)
3101 g_signal_stop_emission_by_name (widget, "drag-data-delete");
3105 /* Creates a suitable drag cursor to indicate that the selected bookmark will be
3109 shortcuts_drag_set_delete_cursor (GtkFileChooserDefault *impl,
3112 GtkTreeView *tree_view;
3115 GdkPixmap *row_pixmap;
3120 tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
3122 /* Find the selected path and get its drag pixmap */
3124 if (!shortcuts_get_selected (impl, &iter))
3125 g_assert_not_reached ();
3127 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
3129 row_pixmap = gtk_tree_view_create_row_drag_icon (tree_view, path);
3130 gtk_tree_path_free (path);
3139 pixbuf = gtk_widget_render_icon (impl->browse_shortcuts_tree_view,
3145 GdkPixmap *composite;
3146 int row_pixmap_width, row_pixmap_height;
3147 int pixbuf_width, pixbuf_height;
3148 int composite_width, composite_height;
3149 int pixbuf_x, pixbuf_y;
3150 GdkGC *gc, *mask_gc;
3152 GdkBitmap *pixbuf_mask;
3154 /* Create pixmap and mask for composite image */
3156 gdk_drawable_get_size (row_pixmap, &row_pixmap_width, &row_pixmap_height);
3157 pixbuf_width = gdk_pixbuf_get_width (pixbuf);
3158 pixbuf_height = gdk_pixbuf_get_height (pixbuf);
3160 composite_width = MAX (row_pixmap_width, pixbuf_width);
3161 composite_height = MAX (row_pixmap_height, pixbuf_height);
3163 row_pixmap_y = (composite_height - row_pixmap_height) / 2;
3165 if (gtk_widget_get_direction (impl->browse_shortcuts_tree_view) == GTK_TEXT_DIR_RTL)
3168 pixbuf_x = composite_width - pixbuf_width;
3170 pixbuf_y = (composite_height - pixbuf_height) / 2;
3172 composite = gdk_pixmap_new (row_pixmap, composite_width, composite_height, -1);
3173 gc = gdk_gc_new (composite);
3175 mask = gdk_pixmap_new (row_pixmap, composite_width, composite_height, 1);
3176 mask_gc = gdk_gc_new (mask);
3178 gdk_gc_set_foreground (mask_gc, &color);
3179 gdk_draw_rectangle (mask, mask_gc, TRUE, 0, 0, composite_width, composite_height);
3182 color.green = 0xffff;
3183 color.blue = 0xffff;
3184 gdk_gc_set_rgb_fg_color (gc, &color);
3185 gdk_draw_rectangle (composite, gc, TRUE, 0, 0, composite_width, composite_height);
3187 /* Composite the row pixmap and the pixbuf */
3189 gdk_pixbuf_render_pixmap_and_mask_for_colormap
3191 gtk_widget_get_colormap (impl->browse_shortcuts_tree_view),
3192 NULL, &pixbuf_mask, 128);
3193 gdk_draw_drawable (mask, mask_gc, pixbuf_mask,
3196 pixbuf_width, pixbuf_height);
3197 g_object_unref (pixbuf_mask);
3199 gdk_draw_drawable (composite, gc, row_pixmap,
3202 row_pixmap_width, row_pixmap_height);
3204 gdk_gc_set_foreground (mask_gc, &color);
3205 gdk_draw_rectangle (mask, mask_gc, TRUE, 0, row_pixmap_y, row_pixmap_width, row_pixmap_height);
3207 gdk_draw_pixbuf (composite, gc, pixbuf,
3210 pixbuf_width, pixbuf_height,
3214 g_object_unref (pixbuf);
3215 g_object_unref (row_pixmap);
3217 row_pixmap = composite;
3221 /* The hotspot offsets here are copied from gtk_tree_view_drag_begin(), ugh */
3223 gtk_tree_view_get_path_at_pos (tree_view,
3224 tree_view->priv->press_start_x,
3225 tree_view->priv->press_start_y,
3231 gtk_drag_set_icon_pixmap (impl->shortcuts_drag_context,
3232 gdk_drawable_get_colormap (row_pixmap),
3235 tree_view->priv->press_start_x + 1,
3236 row_pixmap_y + cell_y + 1);
3238 g_object_unref (row_pixmap);
3240 g_object_unref (mask);
3243 /* We set the delete cursor and the shortcuts_drag_outside flag in an idle
3244 * handler so that we can tell apart the drag_leave event that comes right
3245 * before a drag_drop, from a normal drag_leave. We don't want to set the
3246 * cursor nor the flag in the latter case.
3249 shortcuts_drag_outside_idle_cb (GtkFileChooserDefault *impl)
3251 GDK_THREADS_ENTER ();
3253 shortcuts_drag_set_delete_cursor (impl, TRUE);
3254 impl->shortcuts_drag_outside = TRUE;
3256 shortcuts_cancel_drag_outside_idle (impl);
3258 GDK_THREADS_LEAVE ();
3264 /* GtkWidget::drag-leave handler for the shortcuts list. We unhighlight the
3268 shortcuts_drag_leave_cb (GtkWidget *widget,
3269 GdkDragContext *context,
3271 GtkFileChooserDefault *impl)
3274 if (gtk_drag_get_source_widget (context) == widget && !impl->shortcuts_drag_outside_idle)
3276 impl->shortcuts_drag_outside_idle = g_idle_source_new ();
3277 g_source_set_closure (impl->shortcuts_drag_outside_idle,
3278 g_cclosure_new_object (G_CALLBACK (shortcuts_drag_outside_idle_cb),
3280 g_source_attach (impl->shortcuts_drag_outside_idle, NULL);
3284 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3286 GTK_TREE_VIEW_DROP_BEFORE);
3288 g_signal_stop_emission_by_name (widget, "drag-leave");
3291 /* Computes the appropriate row and position for dropping */
3293 shortcuts_compute_drop_position (GtkFileChooserDefault *impl,
3297 GtkTreeViewDropPosition *pos)
3299 GtkTreeView *tree_view;
3300 GtkTreeViewColumn *column;
3304 int bookmarks_index;
3306 tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
3308 bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
3310 if (!gtk_tree_view_get_path_at_pos (tree_view,
3312 y - TREE_VIEW_HEADER_HEIGHT (tree_view),
3318 row = bookmarks_index + impl->num_bookmarks - 1;
3319 *path = gtk_tree_path_new_from_indices (row, -1);
3320 *pos = GTK_TREE_VIEW_DROP_AFTER;
3324 row = *gtk_tree_path_get_indices (*path);
3325 gtk_tree_view_get_background_area (tree_view, *path, column, &cell);
3326 gtk_tree_path_free (*path);
3328 if (row < bookmarks_index)
3330 row = bookmarks_index;
3331 *pos = GTK_TREE_VIEW_DROP_BEFORE;
3333 else if (row > bookmarks_index + impl->num_bookmarks - 1)
3335 row = bookmarks_index + impl->num_bookmarks - 1;
3336 *pos = GTK_TREE_VIEW_DROP_AFTER;
3340 if (cell_y < cell.height / 2)
3341 *pos = GTK_TREE_VIEW_DROP_BEFORE;
3343 *pos = GTK_TREE_VIEW_DROP_AFTER;
3346 *path = gtk_tree_path_new_from_indices (row, -1);
3349 /* GtkWidget::drag-motion handler for the shortcuts list. We basically
3350 * implement the destination side of DnD by hand, due to limitations in
3351 * GtkTreeView's DnD API.
3354 shortcuts_drag_motion_cb (GtkWidget *widget,
3355 GdkDragContext *context,
3359 GtkFileChooserDefault *impl)
3362 GtkTreeViewDropPosition pos;
3363 GdkDragAction action;
3366 if (gtk_drag_get_source_widget (context) == widget)
3368 shortcuts_cancel_drag_outside_idle (impl);
3370 if (impl->shortcuts_drag_outside)
3372 shortcuts_drag_set_delete_cursor (impl, FALSE);
3373 impl->shortcuts_drag_outside = FALSE;
3378 if (context->suggested_action == GDK_ACTION_COPY ||
3379 (context->actions & GDK_ACTION_COPY) != 0)
3380 action = GDK_ACTION_COPY;
3381 else if (context->suggested_action == GDK_ACTION_MOVE ||
3382 (context->actions & GDK_ACTION_MOVE) != 0)
3383 action = GDK_ACTION_MOVE;
3390 shortcuts_compute_drop_position (impl, x, y, &path, &pos);
3391 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), path, pos);
3392 gtk_tree_path_free (path);
3396 g_signal_stop_emission_by_name (widget, "drag-motion");
3400 gdk_drag_status (context, action, time_);
3407 /* GtkWidget::drag-drop handler for the shortcuts list. */
3409 shortcuts_drag_drop_cb (GtkWidget *widget,
3410 GdkDragContext *context,
3414 GtkFileChooserDefault *impl)
3417 shortcuts_cancel_drag_outside_idle (impl);
3420 g_signal_stop_emission_by_name (widget, "drag-drop");
3424 /* Parses a "text/uri-list" string and inserts its URIs as bookmarks */
3426 shortcuts_drop_uris (GtkFileChooserDefault *impl,
3427 GtkSelectionData *selection_data,
3433 uris = gtk_selection_data_get_uris (selection_data);
3437 for (i = 0; uris[i]; i++)
3443 file = g_file_new_for_uri (uri);
3445 if (shortcuts_add_bookmark_from_file (impl, file, position))
3448 g_object_unref (file);
3454 /* Reorders the selected bookmark to the specified position */
3456 shortcuts_reorder (GtkFileChooserDefault *impl,
3461 ShortcutType shortcut_type;
3464 int bookmarks_index;
3469 /* Get the selected path */
3471 if (!shortcuts_get_selected (impl, &iter))
3472 g_assert_not_reached ();
3474 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
3475 old_position = *gtk_tree_path_get_indices (path);
3476 gtk_tree_path_free (path);
3478 bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
3479 old_position -= bookmarks_index;
3480 g_assert (old_position >= 0 && old_position < impl->num_bookmarks);
3482 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
3483 SHORTCUTS_COL_NAME, &name,
3484 SHORTCUTS_COL_DATA, &col_data,
3485 SHORTCUTS_COL_TYPE, &shortcut_type,
3487 g_assert (col_data != NULL);
3488 g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
3491 g_object_ref (file); /* removal below will free file, so we need a new ref */
3493 /* Remove the path from the old position and insert it in the new one */
3495 if (new_position > old_position)
3498 if (old_position == new_position)
3502 if (_gtk_file_system_remove_bookmark (impl->file_system, file, &error))
3504 shortcuts_add_bookmark_from_file (impl, file, new_position);
3505 _gtk_file_system_set_bookmark_label (impl->file_system, file, name);
3508 error_adding_bookmark_dialog (impl, file, error);
3512 g_object_unref (file);
3515 /* Callback used when we get the drag data for the bookmarks list. We add the
3516 * received URIs as bookmarks if they are folders.
3519 shortcuts_drag_data_received_cb (GtkWidget *widget,
3520 GdkDragContext *context,
3523 GtkSelectionData *selection_data,
3528 GtkFileChooserDefault *impl;
3529 GtkTreePath *tree_path;
3530 GtkTreeViewDropPosition tree_pos;
3532 int bookmarks_index;
3534 impl = GTK_FILE_CHOOSER_DEFAULT (data);
3536 /* Compute position */
3538 bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
3540 shortcuts_compute_drop_position (impl, x, y, &tree_path, &tree_pos);
3541 position = *gtk_tree_path_get_indices (tree_path);
3542 gtk_tree_path_free (tree_path);
3544 if (tree_pos == GTK_TREE_VIEW_DROP_AFTER)
3547 g_assert (position >= bookmarks_index);
3548 position -= bookmarks_index;
3550 if (gtk_targets_include_uri (&selection_data->target, 1))
3551 shortcuts_drop_uris (impl, selection_data, position);
3552 else if (selection_data->target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
3553 shortcuts_reorder (impl, position);
3555 g_signal_stop_emission_by_name (widget, "drag-data-received");
3558 /* Callback used to display a tooltip in the shortcuts tree */
3560 shortcuts_query_tooltip_cb (GtkWidget *widget,
3563 gboolean keyboard_mode,
3564 GtkTooltip *tooltip,
3565 GtkFileChooserDefault *impl)
3567 GtkTreeModel *model;
3570 if (gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget),
3578 ShortcutType shortcut_type;
3580 gtk_tree_model_get (model, &iter,
3581 SHORTCUTS_COL_DATA, &col_data,
3582 SHORTCUTS_COL_TYPE, &shortcut_type,
3585 if (shortcut_type == SHORTCUT_TYPE_SEPARATOR)
3587 else if (shortcut_type == SHORTCUT_TYPE_VOLUME)
3591 else if (shortcut_type == SHORTCUT_TYPE_FILE)
3596 file = G_FILE (col_data);
3597 parse_name = g_file_get_parse_name (file);
3599 gtk_tooltip_set_text (tooltip, parse_name);
3601 g_free (parse_name);
3605 else if (shortcut_type == SHORTCUT_TYPE_SEARCH)
3609 else if (shortcut_type == SHORTCUT_TYPE_RECENT)
3619 /* Callback used when the selection in the shortcuts tree changes */
3621 shortcuts_selection_changed_cb (GtkTreeSelection *selection,
3622 GtkFileChooserDefault *impl)
3625 GtkTreeIter child_iter;
3627 bookmarks_check_remove_sensitivity (impl);
3628 shortcuts_check_popup_sensitivity (impl);
3630 if (impl->changing_folder)
3633 if (gtk_tree_selection_get_selected(selection, NULL, &iter))
3635 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
3638 shortcuts_activate_iter (impl, &child_iter);
3643 shortcuts_row_separator_func (GtkTreeModel *model,
3647 ShortcutType shortcut_type;
3649 gtk_tree_model_get (model, iter, SHORTCUTS_COL_TYPE, &shortcut_type, -1);
3651 return shortcut_type == SHORTCUT_TYPE_SEPARATOR;
3654 /* Since GtkTreeView has a keybinding attached to '/', we need to catch
3655 * keypresses before the TreeView gets them.
3658 tree_view_keybinding_cb (GtkWidget *tree_view,
3660 GtkFileChooserDefault *impl)
3662 if ((event->keyval == GDK_slash
3663 || event->keyval == GDK_KP_Divide
3665 || event->keyval == GDK_asciitilde
3667 ) && ! (event->state & (~GDK_SHIFT_MASK & gtk_accelerator_get_default_mod_mask ())))
3669 location_popup_handler (impl, event->string);
3676 /* Callback used when the file list's popup menu is detached */
3678 shortcuts_popup_menu_detach_cb (GtkWidget *attach_widget,
3681 GtkFileChooserDefault *impl;
3683 impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
3684 g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
3686 impl->browse_shortcuts_popup_menu = NULL;
3687 impl->browse_shortcuts_popup_menu_remove_item = NULL;
3688 impl->browse_shortcuts_popup_menu_rename_item = NULL;
3692 remove_shortcut_cb (GtkMenuItem *item,
3693 GtkFileChooserDefault *impl)
3695 remove_selected_bookmarks (impl);
3698 /* Rename the selected bookmark */
3700 rename_selected_bookmark (GtkFileChooserDefault *impl)
3704 GtkTreeViewColumn *column;
3705 GtkCellRenderer *cell;
3708 if (shortcuts_get_selected (impl, &iter))
3710 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
3711 column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), 0);
3712 renderers = gtk_tree_view_column_get_cell_renderers (column);
3713 cell = g_list_nth_data (renderers, 1);
3714 g_list_free (renderers);
3715 g_object_set (cell, "editable", TRUE, NULL);
3716 gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3717 path, column, cell, TRUE);
3718 gtk_tree_path_free (path);
3723 rename_shortcut_cb (GtkMenuItem *item,
3724 GtkFileChooserDefault *impl)
3726 rename_selected_bookmark (impl);
3729 /* Constructs the popup menu for the file list if needed */
3731 shortcuts_build_popup_menu (GtkFileChooserDefault *impl)
3735 if (impl->browse_shortcuts_popup_menu)
3738 impl->browse_shortcuts_popup_menu = gtk_menu_new ();
3739 gtk_menu_attach_to_widget (GTK_MENU (impl->browse_shortcuts_popup_menu),
3740 impl->browse_shortcuts_tree_view,
3741 shortcuts_popup_menu_detach_cb);
3743 item = gtk_image_menu_item_new_with_label (_("Remove"));
3744 impl->browse_shortcuts_popup_menu_remove_item = item;
3745 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
3746 gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU));
3747 g_signal_connect (item, "activate",
3748 G_CALLBACK (remove_shortcut_cb), impl);
3749 gtk_widget_show (item);
3750 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu), item);
3752 item = gtk_menu_item_new_with_label (_("Rename..."));
3753 impl->browse_shortcuts_popup_menu_rename_item = item;
3754 g_signal_connect (item, "activate",
3755 G_CALLBACK (rename_shortcut_cb), impl);
3756 gtk_widget_show (item);
3757 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu), item);
3761 shortcuts_update_popup_menu (GtkFileChooserDefault *impl)
3763 shortcuts_build_popup_menu (impl);
3764 shortcuts_check_popup_sensitivity (impl);
3768 popup_position_func (GtkMenu *menu,
3772 gpointer user_data);
3775 shortcuts_popup_menu (GtkFileChooserDefault *impl,
3776 GdkEventButton *event)
3778 shortcuts_update_popup_menu (impl);
3780 gtk_menu_popup (GTK_MENU (impl->browse_shortcuts_popup_menu),
3781 NULL, NULL, NULL, NULL,
3782 event->button, event->time);
3785 gtk_menu_popup (GTK_MENU (impl->browse_shortcuts_popup_menu),
3787 popup_position_func, impl->browse_shortcuts_tree_view,
3788 0, GDK_CURRENT_TIME);
3789 gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu),
3794 /* Callback used for the GtkWidget::popup-menu signal of the shortcuts list */
3796 shortcuts_popup_menu_cb (GtkWidget *widget,
3797 GtkFileChooserDefault *impl)
3799 shortcuts_popup_menu (impl, NULL);
3803 /* Callback used when a button is pressed on the shortcuts list.
3804 * We trap button 3 to bring up a popup menu.
3807 shortcuts_button_press_event_cb (GtkWidget *widget,
3808 GdkEventButton *event,
3809 GtkFileChooserDefault *impl)
3811 static gboolean in_press = FALSE;
3817 if (event->button != 3)
3821 handled = gtk_widget_event (impl->browse_shortcuts_tree_view, (GdkEvent *) event);
3827 shortcuts_popup_menu (impl, event);
3832 shortcuts_edited (GtkCellRenderer *cell,
3835 GtkFileChooserDefault *impl)
3841 g_object_set (cell, "editable", FALSE, NULL);
3843 path = gtk_tree_path_new_from_string (path_string);
3844 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
3845 g_assert_not_reached ();
3847 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
3848 SHORTCUTS_COL_DATA, &shortcut,
3850 gtk_tree_path_free (path);
3852 _gtk_file_system_set_bookmark_label (impl->file_system, shortcut, new_text);
3856 shortcuts_editing_canceled (GtkCellRenderer *cell,
3857 GtkFileChooserDefault *impl)
3859 g_object_set (cell, "editable", FALSE, NULL);
3862 /* Creates the widgets for the shortcuts and bookmarks tree */
3864 shortcuts_list_create (GtkFileChooserDefault *impl)
3867 GtkTreeSelection *selection;
3868 GtkTreeViewColumn *column;
3869 GtkCellRenderer *renderer;
3871 /* Target types for dragging a row to/from the shortcuts list */
3872 const GtkTargetEntry tree_model_row_targets[] = {
3873 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }
3876 /* Scrolled window */
3878 swin = gtk_scrolled_window_new (NULL, NULL);
3879 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
3880 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
3881 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
3883 gtk_widget_show (swin);
3887 impl->browse_shortcuts_tree_view = gtk_tree_view_new ();
3888 #ifdef PROFILE_FILE_CHOOSER
3889 g_object_set_data (G_OBJECT (impl->browse_shortcuts_tree_view), "fmq-name", "shortcuts");
3891 g_signal_connect (impl->browse_shortcuts_tree_view, "key-press-event",
3892 G_CALLBACK (tree_view_keybinding_cb), impl);
3893 g_signal_connect (impl->browse_shortcuts_tree_view, "popup-menu",
3894 G_CALLBACK (shortcuts_popup_menu_cb), impl);
3895 g_signal_connect (impl->browse_shortcuts_tree_view, "button-press-event",
3896 G_CALLBACK (shortcuts_button_press_event_cb), impl);
3897 /* Accessible object name for the file chooser's shortcuts pane */
3898 atk_object_set_name (gtk_widget_get_accessible (impl->browse_shortcuts_tree_view), _("Places"));
3900 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), impl->shortcuts_pane_filter_model);
3902 gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3904 tree_model_row_targets,
3905 G_N_ELEMENTS (tree_model_row_targets),
3908 gtk_drag_dest_set (impl->browse_shortcuts_tree_view,
3909 GTK_DEST_DEFAULT_ALL,
3910 tree_model_row_targets,
3911 G_N_ELEMENTS (tree_model_row_targets),
3912 GDK_ACTION_COPY | GDK_ACTION_MOVE);
3913 gtk_drag_dest_add_uri_targets (impl->browse_shortcuts_tree_view);
3915 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
3916 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
3917 gtk_tree_selection_set_select_function (selection,
3918 shortcuts_select_func,
3921 g_signal_connect (selection, "changed",
3922 G_CALLBACK (shortcuts_selection_changed_cb), impl);
3924 g_signal_connect (impl->browse_shortcuts_tree_view, "key-press-event",
3925 G_CALLBACK (shortcuts_key_press_event_cb), impl);
3927 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-begin",
3928 G_CALLBACK (shortcuts_drag_begin_cb), impl);
3929 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-end",
3930 G_CALLBACK (shortcuts_drag_end_cb), impl);
3931 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-delete",
3932 G_CALLBACK (shortcuts_drag_data_delete_cb), impl);
3934 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-leave",
3935 G_CALLBACK (shortcuts_drag_leave_cb), impl);
3936 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-motion",
3937 G_CALLBACK (shortcuts_drag_motion_cb), impl);
3938 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-drop",
3939 G_CALLBACK (shortcuts_drag_drop_cb), impl);
3940 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-received",
3941 G_CALLBACK (shortcuts_drag_data_received_cb), impl);
3943 /* Support tooltips */
3944 gtk_widget_set_has_tooltip (impl->browse_shortcuts_tree_view, TRUE);
3945 g_signal_connect (impl->browse_shortcuts_tree_view, "query-tooltip",
3946 G_CALLBACK (shortcuts_query_tooltip_cb), impl);
3948 gtk_container_add (GTK_CONTAINER (swin), impl->browse_shortcuts_tree_view);
3949 gtk_widget_show (impl->browse_shortcuts_tree_view);
3953 column = gtk_tree_view_column_new ();
3954 /* Column header for the file chooser's shortcuts pane */
3955 gtk_tree_view_column_set_title (column, _("_Places"));
3957 renderer = gtk_cell_renderer_pixbuf_new ();
3958 gtk_tree_view_column_pack_start (column, renderer, FALSE);
3959 gtk_tree_view_column_set_attributes (column, renderer,
3960 "pixbuf", SHORTCUTS_COL_PIXBUF,
3961 "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
3964 renderer = gtk_cell_renderer_text_new ();
3965 g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
3966 g_signal_connect (renderer, "edited",
3967 G_CALLBACK (shortcuts_edited), impl);
3968 g_signal_connect (renderer, "editing-canceled",
3969 G_CALLBACK (shortcuts_editing_canceled), impl);
3970 gtk_tree_view_column_pack_start (column, renderer, TRUE);
3971 gtk_tree_view_column_set_attributes (column, renderer,
3972 "text", SHORTCUTS_COL_NAME,
3975 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3976 shortcuts_row_separator_func,
3979 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), column);
3984 /* Creates the widgets for the shortcuts/bookmarks pane */
3986 shortcuts_pane_create (GtkFileChooserDefault *impl,
3987 GtkSizeGroup *size_group)
3993 vbox = gtk_vbox_new (FALSE, 6);
3994 gtk_widget_show (vbox);
3996 /* Shortcuts tree */
3998 widget = shortcuts_list_create (impl);
3999 gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
4001 /* Box for buttons */
4003 hbox = gtk_hbox_new (TRUE, 6);
4004 gtk_size_group_add_widget (size_group, hbox);
4005 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
4006 gtk_widget_show (hbox);
4008 /* Add bookmark button */
4010 impl->browse_shortcuts_add_button = button_new (impl,
4015 G_CALLBACK (add_bookmark_button_clicked_cb));
4016 gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_add_button, TRUE, TRUE, 0);
4017 gtk_widget_set_tooltip_text (impl->browse_shortcuts_add_button,
4018 _("Add the selected folder to the Bookmarks"));
4020 /* Remove bookmark button */
4022 impl->browse_shortcuts_remove_button = button_new (impl,
4027 G_CALLBACK (remove_bookmark_button_clicked_cb));
4028 gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_remove_button, TRUE, TRUE, 0);
4029 gtk_widget_set_tooltip_text (impl->browse_shortcuts_remove_button,
4030 _("Remove the selected bookmark"));
4035 /* Handles key press events on the file list, so that we can trap Enter to
4036 * activate the default button on our own. Also, checks to see if '/' has been
4037 * pressed. See comment by tree_view_keybinding_cb() for more details.
4040 trap_activate_cb (GtkWidget *widget,
4044 GtkFileChooserDefault *impl;
4047 impl = (GtkFileChooserDefault *) data;
4049 modifiers = gtk_accelerator_get_default_mod_mask ();
4051 if ((event->keyval == GDK_slash
4052 || event->keyval == GDK_KP_Divide
4054 || event->keyval == GDK_asciitilde
4056 ) && ! (event->state & (~GDK_SHIFT_MASK & modifiers)))
4058 location_popup_handler (impl, event->string);
4062 if ((event->keyval == GDK_Return
4063 || event->keyval == GDK_ISO_Enter
4064 || event->keyval == GDK_KP_Enter
4065 || event->keyval == GDK_space
4066 || event->keyval == GDK_KP_Space)
4067 && ((event->state & modifiers) == 0)
4068 && !(impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
4069 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
4073 window = get_toplevel (widget);
4075 && widget != window->default_widget
4076 && !(widget == window->focus_widget &&
4077 (!window->default_widget || !GTK_WIDGET_SENSITIVE (window->default_widget))))
4079 gtk_window_activate_default (window);
4087 /* Callback used when the file list's popup menu is detached */
4089 popup_menu_detach_cb (GtkWidget *attach_widget,
4092 GtkFileChooserDefault *impl;
4094 impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
4095 g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
4097 impl->browse_files_popup_menu = NULL;
4098 impl->browse_files_popup_menu_add_shortcut_item = NULL;
4099 impl->browse_files_popup_menu_hidden_files_item = NULL;
4102 /* Callback used when the "Add to Bookmarks" menu item is activated */
4104 add_to_shortcuts_cb (GtkMenuItem *item,
4105 GtkFileChooserDefault *impl)
4107 bookmarks_add_selected_folder (impl);
4110 /* Callback used when the "Show Hidden Files" menu item is toggled */
4112 show_hidden_toggled_cb (GtkCheckMenuItem *item,
4113 GtkFileChooserDefault *impl)
4116 "show-hidden", gtk_check_menu_item_get_active (item),
4120 /* Callback used when the "Show Size Column" menu item is toggled */
4122 show_size_column_toggled_cb (GtkCheckMenuItem *item,
4123 GtkFileChooserDefault *impl)
4125 impl->show_size_column = gtk_check_menu_item_get_active (item);
4127 if (impl->list_size_column)
4128 gtk_tree_view_column_set_visible (impl->list_size_column,
4129 impl->show_size_column);
4132 /* Shows an error dialog about not being able to select a dragged file */
4134 error_selecting_dragged_file_dialog (GtkFileChooserDefault *impl,
4139 _("Could not select file"),
4144 file_list_drag_data_select_uris (GtkFileChooserDefault *impl,
4149 GtkFileChooser *chooser = GTK_FILE_CHOOSER (impl);
4151 for (i = 1; uris[i]; i++)
4154 GError *error = NULL;
4157 file = g_file_new_for_uri (uri);
4159 gtk_file_chooser_default_select_file (chooser, file, &error);
4161 error_selecting_dragged_file_dialog (impl, file, error);
4163 g_object_unref (file);
4167 struct FileListDragData
4169 GtkFileChooserDefault *impl;
4175 file_list_drag_data_received_get_info_cb (GCancellable *cancellable,
4177 const GError *error,
4180 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
4181 struct FileListDragData *data = user_data;
4182 GtkFileChooser *chooser = GTK_FILE_CHOOSER (data->impl);
4184 if (cancellable != data->impl->file_list_drag_data_received_cancellable)
4187 data->impl->file_list_drag_data_received_cancellable = NULL;
4189 if (cancelled || error)
4192 if ((data->impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
4193 data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) &&
4194 data->uris[1] == 0 && !error && _gtk_file_info_consider_as_directory (info))
4195 change_folder_and_display_error (data->impl, data->file, FALSE);
4198 GError *error = NULL;
4200 gtk_file_chooser_default_unselect_all (chooser);
4201 gtk_file_chooser_default_select_file (chooser, data->file, &error);
4203 error_selecting_dragged_file_dialog (data->impl, data->file, error);
4205 browse_files_center_selected_row (data->impl);
4208 if (data->impl->select_multiple)
4209 file_list_drag_data_select_uris (data->impl, data->uris);
4212 g_object_unref (data->impl);
4213 g_strfreev (data->uris);
4214 g_object_unref (data->file);
4217 g_object_unref (cancellable);
4221 file_list_drag_data_received_cb (GtkWidget *widget,
4222 GdkDragContext *context,
4225 GtkSelectionData *selection_data,
4230 GtkFileChooserDefault *impl;
4231 GtkFileChooser *chooser;
4236 impl = GTK_FILE_CHOOSER_DEFAULT (data);
4237 chooser = GTK_FILE_CHOOSER (data);
4239 /* Allow only drags from other widgets; see bug #533891. */
4240 if (gtk_drag_get_source_widget (context) == widget)
4242 g_signal_stop_emission_by_name (widget, "drag-data-received");
4246 /* Parse the text/uri-list string, navigate to the first one */
4247 uris = gtk_selection_data_get_uris (selection_data);
4248 if (uris && uris[0])
4250 struct FileListDragData *data;
4253 file = g_file_new_for_uri (uri);
4255 data = g_new0 (struct FileListDragData, 1);
4256 data->impl = g_object_ref (impl);
4260 if (impl->file_list_drag_data_received_cancellable)
4261 g_cancellable_cancel (impl->file_list_drag_data_received_cancellable);
4263 impl->file_list_drag_data_received_cancellable =
4264 _gtk_file_system_get_info (impl->file_system, file,
4266 file_list_drag_data_received_get_info_cb,
4270 g_signal_stop_emission_by_name (widget, "drag-data-received");
4273 /* Don't do anything with the drag_drop signal */
4275 file_list_drag_drop_cb (GtkWidget *widget,
4276 GdkDragContext *context,
4280 GtkFileChooserDefault *impl)
4282 g_signal_stop_emission_by_name (widget, "drag-drop");
4286 /* Disable the normal tree drag motion handler, it makes it look like you're
4287 dropping the dragged item onto a tree item */
4289 file_list_drag_motion_cb (GtkWidget *widget,
4290 GdkDragContext *context,
4294 GtkFileChooserDefault *impl)
4296 g_signal_stop_emission_by_name (widget, "drag-motion");
4300 /* Constructs the popup menu for the file list if needed */
4302 file_list_build_popup_menu (GtkFileChooserDefault *impl)
4306 if (impl->browse_files_popup_menu)
4309 impl->browse_files_popup_menu = gtk_menu_new ();
4310 gtk_menu_attach_to_widget (GTK_MENU (impl->browse_files_popup_menu),
4311 impl->browse_files_tree_view,
4312 popup_menu_detach_cb);
4314 item = gtk_image_menu_item_new_with_mnemonic (_("_Add to Bookmarks"));
4315 impl->browse_files_popup_menu_add_shortcut_item = item;
4316 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
4317 gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU));
4318 gtk_widget_set_sensitive (item, FALSE);
4319 g_signal_connect (item, "activate",
4320 G_CALLBACK (add_to_shortcuts_cb), impl);
4321 gtk_widget_show (item);
4322 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4324 item = gtk_separator_menu_item_new ();
4325 gtk_widget_show (item);
4326 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4328 item = gtk_check_menu_item_new_with_mnemonic (_("Show _Hidden Files"));
4329 impl->browse_files_popup_menu_hidden_files_item = item;
4330 g_signal_connect (item, "toggled",
4331 G_CALLBACK (show_hidden_toggled_cb), impl);
4332 gtk_widget_show (item);
4333 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4335 item = gtk_check_menu_item_new_with_mnemonic (_("Show _Size Column"));
4336 impl->browse_files_popup_menu_size_column_item = item;
4337 g_signal_connect (item, "toggled",
4338 G_CALLBACK (show_size_column_toggled_cb), impl);
4339 gtk_widget_show (item);
4340 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4343 /* Updates the popup menu for the file list, creating it if necessary */
4345 file_list_update_popup_menu (GtkFileChooserDefault *impl)
4347 file_list_build_popup_menu (impl);
4349 /* FIXME - handle OPERATION_MODE_SEARCH and OPERATION_MODE_RECENT */
4351 /* The sensitivity of the Add to Bookmarks item is set in
4352 * bookmarks_check_add_sensitivity()
4355 /* 'Show Hidden Files' */
4356 g_signal_handlers_block_by_func (impl->browse_files_popup_menu_hidden_files_item,
4357 G_CALLBACK (show_hidden_toggled_cb), impl);
4358 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (impl->browse_files_popup_menu_hidden_files_item),
4360 g_signal_handlers_unblock_by_func (impl->browse_files_popup_menu_hidden_files_item,
4361 G_CALLBACK (show_hidden_toggled_cb), impl);
4363 /* 'Show Size Column' */
4364 g_signal_handlers_block_by_func (impl->browse_files_popup_menu_size_column_item,
4365 G_CALLBACK (show_size_column_toggled_cb), impl);
4366 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (impl->browse_files_popup_menu_size_column_item),
4367 impl->show_size_column);
4368 g_signal_handlers_unblock_by_func (impl->browse_files_popup_menu_size_column_item,
4369 G_CALLBACK (show_size_column_toggled_cb), impl);
4373 popup_position_func (GtkMenu *menu,
4379 GtkWidget *widget = GTK_WIDGET (user_data);
4380 GdkScreen *screen = gtk_widget_get_screen (widget);
4383 GdkRectangle monitor;
4385 g_return_if_fail (GTK_WIDGET_REALIZED (widget));
4387 gdk_window_get_origin (widget->window, x, y);
4389 gtk_widget_size_request (GTK_WIDGET (menu), &req);
4391 *x += (widget->allocation.width - req.width) / 2;
4392 *y += (widget->allocation.height - req.height) / 2;
4394 monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
4395 gtk_menu_set_monitor (menu, monitor_num);
4396 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
4398 *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
4399 *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
4405 file_list_popup_menu (GtkFileChooserDefault *impl,
4406 GdkEventButton *event)
4408 file_list_update_popup_menu (impl);
4410 gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
4411 NULL, NULL, NULL, NULL,
4412 event->button, event->time);
4415 gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
4417 popup_position_func, impl->browse_files_tree_view,
4418 0, GDK_CURRENT_TIME);
4419 gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->browse_files_popup_menu),
4425 /* Callback used for the GtkWidget::popup-menu signal of the file list */
4427 list_popup_menu_cb (GtkWidget *widget,
4428 GtkFileChooserDefault *impl)
4430 file_list_popup_menu (impl, NULL);
4434 /* Callback used when a button is pressed on the file list. We trap button 3 to
4435 * bring up a popup menu.
4438 list_button_press_event_cb (GtkWidget *widget,
4439 GdkEventButton *event,
4440 GtkFileChooserDefault *impl)
4442 static gboolean in_press = FALSE;
4448 if (event->button != 3)
4452 handled = gtk_widget_event (impl->browse_files_tree_view, (GdkEvent *) event);
4455 file_list_popup_menu (impl, event);
4459 /* Sets the sort column IDs for the file list based on the operation mode */
4461 file_list_set_sort_column_ids (GtkFileChooserDefault *impl)
4463 int name_id, mtime_id, size_id;
4465 name_id = mtime_id = size_id = 0;
4467 switch (impl->operation_mode)
4469 case OPERATION_MODE_BROWSE:
4470 name_id = FILE_LIST_COL_NAME;
4471 mtime_id = FILE_LIST_COL_MTIME;
4472 size_id = FILE_LIST_COL_SIZE;
4474 case OPERATION_MODE_SEARCH:
4475 name_id = SEARCH_MODEL_COL_FILE;
4476 mtime_id = SEARCH_MODEL_COL_MTIME;
4477 size_id = SEARCH_MODEL_COL_SIZE;
4479 case OPERATION_MODE_RECENT:
4480 name_id = RECENT_MODEL_COL_FILE;
4481 mtime_id = RECENT_MODEL_COL_INFO;
4485 gtk_tree_view_column_set_sort_column_id (impl->list_name_column, name_id);
4486 gtk_tree_view_column_set_sort_column_id (impl->list_mtime_column, mtime_id);
4487 gtk_tree_view_column_set_sort_column_id (impl->list_size_column, size_id);
4491 file_list_query_tooltip_cb (GtkWidget *widget,
4494 gboolean keyboard_tip,
4495 GtkTooltip *tooltip,
4498 GtkFileChooserDefault *impl = user_data;
4499 GtkTreeIter iter, child_iter;
4500 GtkTreePath *path = NULL;
4504 if (impl->operation_mode == OPERATION_MODE_BROWSE)
4508 gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (impl->browse_files_tree_view),
4516 switch (impl->operation_mode)
4518 case OPERATION_MODE_SEARCH:
4519 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->search_model_sort), &iter, path))
4521 gtk_tree_path_free (path);
4525 search_get_valid_child_iter (impl, &child_iter, &iter);
4526 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter,
4527 SEARCH_MODEL_COL_FILE, &file,
4531 case OPERATION_MODE_RECENT:
4532 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->recent_model_sort), &iter, path))
4534 gtk_tree_path_free (path);
4538 recent_get_valid_child_iter (impl, &child_iter, &iter);
4539 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter,
4540 RECENT_MODEL_COL_FILE, &file,
4544 case OPERATION_MODE_BROWSE:
4545 g_assert_not_reached ();
4551 gtk_tree_path_free (path);
4555 filename = g_file_get_path (file);
4556 gtk_tooltip_set_text (tooltip, filename);
4557 gtk_tree_view_set_tooltip_row (GTK_TREE_VIEW (impl->browse_files_tree_view),
4562 gtk_tree_path_free (path);
4567 /* Creates the widgets for the file list */
4569 create_file_list (GtkFileChooserDefault *impl)
4572 GtkTreeSelection *selection;
4573 GtkTreeViewColumn *column;
4574 GtkCellRenderer *renderer;
4576 /* Scrolled window */
4577 swin = gtk_scrolled_window_new (NULL, NULL);
4578 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
4579 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
4580 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
4583 /* Tree/list view */
4585 impl->browse_files_tree_view = gtk_tree_view_new ();
4586 #ifdef PROFILE_FILE_CHOOSER
4587 g_object_set_data (G_OBJECT (impl->browse_files_tree_view), "fmq-name", "file_list");
4589 g_object_set_data (G_OBJECT (impl->browse_files_tree_view), I_("GtkFileChooserDefault"), impl);
4590 atk_object_set_name (gtk_widget_get_accessible (impl->browse_files_tree_view), _("Files"));
4592 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->browse_files_tree_view), TRUE);
4593 gtk_container_add (GTK_CONTAINER (swin), impl->browse_files_tree_view);
4595 gtk_drag_dest_set (impl->browse_files_tree_view,
4596 GTK_DEST_DEFAULT_ALL,
4598 GDK_ACTION_COPY | GDK_ACTION_MOVE);
4599 gtk_drag_dest_add_uri_targets (impl->browse_files_tree_view);
4601 g_signal_connect (impl->browse_files_tree_view, "row-activated",
4602 G_CALLBACK (list_row_activated), impl);
4603 g_signal_connect (impl->browse_files_tree_view, "key-press-event",
4604 G_CALLBACK (trap_activate_cb), impl);
4605 g_signal_connect (impl->browse_files_tree_view, "popup-menu",
4606 G_CALLBACK (list_popup_menu_cb), impl);
4607 g_signal_connect (impl->browse_files_tree_view, "button-press-event",
4608 G_CALLBACK (list_button_press_event_cb), impl);
4610 g_signal_connect (impl->browse_files_tree_view, "drag-data-received",
4611 G_CALLBACK (file_list_drag_data_received_cb), impl);
4612 g_signal_connect (impl->browse_files_tree_view, "drag-drop",
4613 G_CALLBACK (file_list_drag_drop_cb), impl);
4614 g_signal_connect (impl->browse_files_tree_view, "drag-motion",
4615 G_CALLBACK (file_list_drag_motion_cb), impl);
4617 g_object_set (impl->browse_files_tree_view, "has-tooltip", TRUE, NULL);
4618 g_signal_connect (impl->browse_files_tree_view, "query-tooltip",
4619 G_CALLBACK (file_list_query_tooltip_cb), impl);
4621 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4622 gtk_tree_selection_set_select_function (selection,
4625 gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_files_tree_view),
4628 GDK_ACTION_COPY | GDK_ACTION_MOVE);
4629 gtk_drag_source_add_uri_targets (impl->browse_files_tree_view);
4631 g_signal_connect (selection, "changed",
4632 G_CALLBACK (list_selection_changed), impl);
4634 /* Filename column */
4636 impl->list_name_column = gtk_tree_view_column_new ();
4637 gtk_tree_view_column_set_expand (impl->list_name_column, TRUE);
4638 gtk_tree_view_column_set_resizable (impl->list_name_column, TRUE);
4639 gtk_tree_view_column_set_title (impl->list_name_column, _("Name"));
4640 gtk_tree_view_column_set_sort_column_id (impl->list_name_column, FILE_LIST_COL_NAME);
4642 renderer = gtk_cell_renderer_pixbuf_new ();
4643 gtk_tree_view_column_pack_start (impl->list_name_column, renderer, FALSE);
4644 gtk_tree_view_column_set_cell_data_func (impl->list_name_column, renderer,
4645 list_icon_data_func, impl, NULL);
4647 impl->list_name_renderer = gtk_cell_renderer_text_new ();
4648 g_object_set (impl->list_name_renderer,
4649 "ellipsize", PANGO_ELLIPSIZE_END,
4651 g_signal_connect (impl->list_name_renderer, "edited",
4652 G_CALLBACK (renderer_edited_cb), impl);
4653 g_signal_connect (impl->list_name_renderer, "editing-canceled",
4654 G_CALLBACK (renderer_editing_canceled_cb), impl);
4655 gtk_tree_view_column_pack_start (impl->list_name_column, impl->list_name_renderer, TRUE);
4656 gtk_tree_view_column_set_cell_data_func (impl->list_name_column, impl->list_name_renderer,
4657 list_name_data_func, impl, NULL);
4659 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), impl->list_name_column);
4663 column = gtk_tree_view_column_new ();
4664 gtk_tree_view_column_set_title (column, _("Size"));
4666 renderer = gtk_cell_renderer_text_new ();
4667 gtk_tree_view_column_pack_start (column, renderer, TRUE); /* bug: it doesn't expand */
4668 gtk_tree_view_column_set_cell_data_func (column, renderer,
4669 list_size_data_func, impl, NULL);
4670 gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_SIZE);
4671 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
4672 impl->list_size_column = column;
4674 /* Modification time column */
4676 column = gtk_tree_view_column_new ();
4677 gtk_tree_view_column_set_resizable (column, TRUE);
4678 gtk_tree_view_column_set_title (column, _("Modified"));
4680 renderer = gtk_cell_renderer_text_new ();
4681 gtk_tree_view_column_pack_start (column, renderer, TRUE);
4682 gtk_tree_view_column_set_cell_data_func (column, renderer,
4683 list_mtime_data_func, impl, NULL);
4684 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
4685 impl->list_mtime_column = column;
4687 file_list_set_sort_column_ids (impl);
4689 gtk_widget_show_all (swin);
4695 create_path_bar (GtkFileChooserDefault *impl)
4697 GtkWidget *path_bar;
4699 path_bar = g_object_new (GTK_TYPE_PATH_BAR, NULL);
4700 _gtk_path_bar_set_file_system (GTK_PATH_BAR (path_bar), impl->file_system);
4705 /* Creates the widgets for the files/folders pane */
4707 file_pane_create (GtkFileChooserDefault *impl,
4708 GtkSizeGroup *size_group)
4714 vbox = gtk_vbox_new (FALSE, 6);
4715 gtk_widget_show (vbox);
4717 /* Box for lists and preview */
4719 hbox = gtk_hbox_new (FALSE, PREVIEW_HBOX_SPACING);
4720 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
4721 gtk_widget_show (hbox);
4725 widget = create_file_list (impl);
4726 gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
4730 impl->preview_box = gtk_vbox_new (FALSE, 12);
4731 gtk_box_pack_start (GTK_BOX (hbox), impl->preview_box, FALSE, FALSE, 0);
4732 /* Don't show preview box initially */
4736 impl->filter_combo_hbox = gtk_hbox_new (FALSE, 12);
4738 widget = filter_create (impl);
4740 gtk_widget_show (widget);
4741 gtk_box_pack_end (GTK_BOX (impl->filter_combo_hbox), widget, FALSE, FALSE, 0);
4743 gtk_size_group_add_widget (size_group, impl->filter_combo_hbox);
4744 gtk_box_pack_end (GTK_BOX (vbox), impl->filter_combo_hbox, FALSE, FALSE, 0);
4749 /* Callback used when the "Browse for more folders" expander is toggled */
4751 expander_changed_cb (GtkExpander *expander,
4753 GtkFileChooserDefault *impl)
4755 impl->expand_folders = gtk_expander_get_expanded(GTK_EXPANDER (impl->save_expander));
4756 update_appearance (impl);
4759 /* Callback used when the selection changes in the save folder combo box */
4761 save_folder_combo_changed_cb (GtkComboBox *combo,
4762 GtkFileChooserDefault *impl)
4766 if (impl->changing_folder)
4769 if (gtk_combo_box_get_active_iter (combo, &iter))
4771 GtkTreeIter child_iter;
4773 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model),
4776 shortcuts_activate_iter (impl, &child_iter);
4780 /* Filter function used to filter out the Search item and its separator.
4781 * Used for the "Save in folder" combo box, so that these items do not appear in it.
4784 shortcuts_combo_filter_func (GtkTreeModel *model,
4788 GtkFileChooserDefault *impl;
4789 GtkTreePath *tree_path;
4794 impl = GTK_FILE_CHOOSER_DEFAULT (data);
4796 g_assert (model == GTK_TREE_MODEL (impl->shortcuts_model));
4798 tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), iter);
4799 g_assert (tree_path != NULL);
4801 indices = gtk_tree_path_get_indices (tree_path);
4805 if (impl->has_search)
4807 idx = shortcuts_get_index (impl, SHORTCUTS_SEARCH);
4808 if (idx == indices[0])
4812 if (impl->has_recent)
4814 idx = shortcuts_get_index (impl, SHORTCUTS_RECENT);
4815 if (idx == indices[0])
4819 idx = shortcuts_get_index (impl, SHORTCUTS_RECENT_SEPARATOR);
4820 if (idx == indices[0])
4825 gtk_tree_path_free (tree_path);
4830 /* Creates the combo box with the save folders */
4832 save_folder_combo_create (GtkFileChooserDefault *impl)
4835 GtkCellRenderer *cell;
4837 impl->shortcuts_combo_filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (impl->shortcuts_model), NULL);
4838 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model),
4839 shortcuts_combo_filter_func,
4843 combo = g_object_new (GTK_TYPE_COMBO_BOX,
4844 "model", impl->shortcuts_combo_filter_model,
4845 "focus-on-click", FALSE,
4847 gtk_widget_show (combo);
4849 cell = gtk_cell_renderer_pixbuf_new ();
4850 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE);
4851 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
4852 "pixbuf", SHORTCUTS_COL_PIXBUF,
4853 "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
4854 "sensitive", SHORTCUTS_COL_PIXBUF_VISIBLE,
4857 cell = gtk_cell_renderer_text_new ();
4858 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
4859 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
4860 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
4861 "text", SHORTCUTS_COL_NAME,
4862 "sensitive", SHORTCUTS_COL_PIXBUF_VISIBLE,
4865 gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo),
4866 shortcuts_row_separator_func,
4869 g_signal_connect (combo, "changed",
4870 G_CALLBACK (save_folder_combo_changed_cb), impl);
4875 /* Creates the widgets specific to Save mode */
4877 save_widgets_create (GtkFileChooserDefault *impl)
4882 GtkWidget *alignment;
4884 if (impl->save_widgets != NULL)
4887 location_switch_to_path_bar (impl);
4889 vbox = gtk_vbox_new (FALSE, 12);
4891 table = gtk_table_new (2, 2, FALSE);
4892 gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
4893 gtk_widget_show (table);
4894 gtk_table_set_row_spacings (GTK_TABLE (table), 12);
4895 gtk_table_set_col_spacings (GTK_TABLE (table), 12);
4899 widget = gtk_label_new_with_mnemonic (_("_Name:"));
4900 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
4901 gtk_table_attach (GTK_TABLE (table), widget,
4905 gtk_widget_show (widget);
4907 /* Location entry */
4909 impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
4910 _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
4912 _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->local_only);
4913 gtk_entry_set_width_chars (GTK_ENTRY (impl->location_entry), 45);
4914 gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
4915 gtk_table_attach (GTK_TABLE (table), impl->location_entry,
4917 GTK_EXPAND | GTK_FILL, 0,
4919 gtk_widget_show (impl->location_entry);
4920 gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->location_entry);
4923 impl->save_folder_label = gtk_label_new (NULL);
4924 gtk_misc_set_alignment (GTK_MISC (impl->save_folder_label), 0.0, 0.5);
4925 gtk_table_attach (GTK_TABLE (table), impl->save_folder_label,
4929 gtk_widget_show (impl->save_folder_label);
4931 impl->save_folder_combo = save_folder_combo_create (impl);
4932 gtk_table_attach (GTK_TABLE (table), impl->save_folder_combo,
4934 GTK_EXPAND | GTK_FILL, GTK_FILL,
4936 gtk_label_set_mnemonic_widget (GTK_LABEL (impl->save_folder_label), impl->save_folder_combo);
4939 alignment = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
4940 gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
4942 impl->save_expander = gtk_expander_new_with_mnemonic (_("_Browse for other folders"));
4943 gtk_container_add (GTK_CONTAINER (alignment), impl->save_expander);
4944 g_signal_connect (impl->save_expander, "notify::expanded",
4945 G_CALLBACK (expander_changed_cb),
4947 gtk_widget_show_all (alignment);
4949 impl->save_widgets = vbox;
4950 gtk_box_pack_start (GTK_BOX (impl), impl->save_widgets, FALSE, FALSE, 0);
4951 gtk_box_reorder_child (GTK_BOX (impl), impl->save_widgets, 0);
4952 gtk_widget_show (impl->save_widgets);
4955 /* Destroys the widgets specific to Save mode */
4957 save_widgets_destroy (GtkFileChooserDefault *impl)
4959 if (impl->save_widgets == NULL)
4962 gtk_widget_destroy (impl->save_widgets);
4963 impl->save_widgets = NULL;
4964 impl->location_entry = NULL;
4965 impl->save_folder_label = NULL;
4966 impl->save_folder_combo = NULL;
4967 impl->save_expander = NULL;
4970 /* Turns on the path bar widget. Can be called even if we are already in that
4974 location_switch_to_path_bar (GtkFileChooserDefault *impl)
4976 if (impl->location_entry)
4978 gtk_widget_destroy (impl->location_entry);
4979 impl->location_entry = NULL;
4982 gtk_widget_hide (impl->location_entry_box);
4985 /* Sets the full path of the current folder as the text in the location entry. */
4987 location_entry_set_initial_text (GtkFileChooserDefault *impl)
4989 gchar *text, *filename;
4991 if (!impl->current_folder)
4994 filename = g_file_get_path (impl->current_folder);
4998 text = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
5002 text = g_file_get_uri (impl->current_folder);
5006 gboolean need_slash;
5009 len = strlen (text);
5010 need_slash = (text[len - 1] != G_DIR_SEPARATOR);
5016 slash_text = g_new (char, len + 2);
5017 strcpy (slash_text, text);
5018 slash_text[len] = G_DIR_SEPARATOR;
5019 slash_text[len + 1] = 0;
5025 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), text);
5032 /* Turns on the location entry. Can be called even if we are already in that
5036 location_switch_to_filename_entry (GtkFileChooserDefault *impl)
5038 /* when in search or recent files mode, we are not showing the
5039 * location_entry_box container, so there's no point in switching
5042 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
5043 impl->operation_mode == OPERATION_MODE_RECENT)
5046 if (impl->location_entry)
5047 gtk_widget_destroy (impl->location_entry);
5051 gtk_widget_show (impl->location_entry_box);
5055 impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
5056 _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
5058 gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
5059 _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action);
5061 gtk_box_pack_start (GTK_BOX (impl->location_entry_box), impl->location_entry, TRUE, TRUE, 0);
5062 gtk_label_set_mnemonic_widget (GTK_LABEL (impl->location_label), impl->location_entry);
5064 /* Configure the entry */
5066 _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->current_folder);
5070 gtk_widget_show (impl->location_entry);
5071 gtk_widget_grab_focus (impl->location_entry);
5074 /* Sets a new location mode. set_buttons determines whether the toggle button
5075 * for the mode will also be changed.
5078 location_mode_set (GtkFileChooserDefault *impl,
5079 LocationMode new_mode,
5080 gboolean set_button)
5082 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
5083 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5085 GtkWindow *toplevel;
5086 GtkWidget *current_focus;
5087 gboolean button_active;
5088 gboolean switch_to_file_list;
5092 case LOCATION_MODE_PATH_BAR:
5093 button_active = FALSE;
5095 /* The location_entry will disappear when we switch to path bar mode. So,
5096 * we'll focus the file list in that case, to avoid having a window with
5097 * no focused widget.
5099 toplevel = get_toplevel (GTK_WIDGET (impl));
5100 switch_to_file_list = FALSE;
5103 current_focus = gtk_window_get_focus (toplevel);
5104 if (!current_focus || current_focus == impl->location_entry)
5105 switch_to_file_list = TRUE;
5108 location_switch_to_path_bar (impl);
5110 if (switch_to_file_list)
5111 gtk_widget_grab_focus (impl->browse_files_tree_view);
5115 case LOCATION_MODE_FILENAME_ENTRY:
5116 button_active = TRUE;
5117 location_switch_to_filename_entry (impl);
5121 g_assert_not_reached ();
5127 g_signal_handlers_block_by_func (impl->location_button,
5128 G_CALLBACK (location_button_toggled_cb), impl);
5130 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (impl->location_button), button_active);
5132 g_signal_handlers_unblock_by_func (impl->location_button,
5133 G_CALLBACK (location_button_toggled_cb), impl);
5137 impl->location_mode = new_mode;
5141 location_toggle_popup_handler (GtkFileChooserDefault *impl)
5143 /* when in search or recent files mode, we are not showing the
5144 * location_entry_box container, so there's no point in switching
5147 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
5148 impl->operation_mode == OPERATION_MODE_RECENT)
5151 /* If the file entry is not visible, show it.
5152 * If it is visible, turn it off only if it is focused. Otherwise, switch to the entry.
5154 if (impl->location_mode == LOCATION_MODE_PATH_BAR)
5156 location_mode_set (impl, LOCATION_MODE_FILENAME_ENTRY, TRUE);
5158 else if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
5160 if (GTK_WIDGET_HAS_FOCUS (impl->location_entry))
5162 location_mode_set (impl, LOCATION_MODE_PATH_BAR, TRUE);
5166 gtk_widget_grab_focus (impl->location_entry);
5171 /* Callback used when one of the location mode buttons is toggled */
5173 location_button_toggled_cb (GtkToggleButton *toggle,
5174 GtkFileChooserDefault *impl)
5177 LocationMode new_mode;
5179 is_active = gtk_toggle_button_get_active (toggle);
5183 g_assert (impl->location_mode == LOCATION_MODE_PATH_BAR);
5184 new_mode = LOCATION_MODE_FILENAME_ENTRY;
5188 g_assert (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY);
5189 new_mode = LOCATION_MODE_PATH_BAR;
5192 location_mode_set (impl, new_mode, FALSE);
5195 /* Creates a toggle button for the location entry. */
5197 location_button_create (GtkFileChooserDefault *impl)
5202 image = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON);
5203 gtk_widget_show (image);
5205 impl->location_button = g_object_new (GTK_TYPE_TOGGLE_BUTTON,
5209 gtk_size_group_add_widget (impl->browse_path_bar_size_group, impl->location_button);
5211 g_signal_connect (impl->location_button, "toggled",
5212 G_CALLBACK (location_button_toggled_cb), impl);
5214 str = _("Type a file name");
5216 gtk_widget_set_tooltip_text (impl->location_button, str);
5217 atk_object_set_name (gtk_widget_get_accessible (impl->location_button), str);
5220 /* Creates the main hpaned with the widgets shared by Open and Save mode */
5222 browse_widgets_create (GtkFileChooserDefault *impl)
5227 GtkSizeGroup *size_group;
5229 /* size group is used by the [+][-] buttons and the filter combo */
5230 size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
5231 vbox = gtk_vbox_new (FALSE, 12);
5233 /* Location widgets */
5234 impl->browse_path_bar_hbox = gtk_hbox_new (FALSE, 12);
5235 gtk_box_pack_start (GTK_BOX (vbox), impl->browse_path_bar_hbox, FALSE, FALSE, 0);
5236 gtk_widget_show (impl->browse_path_bar_hbox);
5238 /* Size group that allows the path bar to be the same size between modes */
5239 impl->browse_path_bar_size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
5240 gtk_size_group_set_ignore_hidden (impl->browse_path_bar_size_group, FALSE);
5242 /* Location button */
5244 location_button_create (impl);
5245 gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->location_button, FALSE, FALSE, 0);
5249 impl->browse_path_bar = create_path_bar (impl);
5250 g_signal_connect (impl->browse_path_bar, "path-clicked", G_CALLBACK (path_bar_clicked), impl);
5251 gtk_widget_show_all (impl->browse_path_bar);
5252 gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->browse_path_bar, TRUE, TRUE, 0);
5255 impl->browse_new_folder_button = gtk_button_new_with_mnemonic (_("Create Fo_lder"));
5256 g_signal_connect (impl->browse_new_folder_button, "clicked",
5257 G_CALLBACK (new_folder_button_clicked), impl);
5258 gtk_box_pack_end (GTK_BOX (impl->browse_path_bar_hbox), impl->browse_new_folder_button, FALSE, FALSE, 0);
5260 /* Box for the location label and entry */
5262 impl->location_entry_box = gtk_hbox_new (FALSE, 12);
5263 gtk_box_pack_start (GTK_BOX (vbox), impl->location_entry_box, FALSE, FALSE, 0);
5265 impl->location_label = gtk_label_new_with_mnemonic (_("_Location:"));
5266 gtk_widget_show (impl->location_label);
5267 gtk_box_pack_start (GTK_BOX (impl->location_entry_box), impl->location_label, FALSE, FALSE, 0);
5270 hpaned = gtk_hpaned_new ();
5271 gtk_widget_show (hpaned);
5272 gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
5274 widget = shortcuts_pane_create (impl, size_group);
5275 gtk_paned_pack1 (GTK_PANED (hpaned), widget, FALSE, FALSE);
5276 widget = file_pane_create (impl, size_group);
5277 gtk_paned_pack2 (GTK_PANED (hpaned), widget, TRUE, FALSE);
5279 g_object_unref (size_group);
5285 gtk_file_chooser_default_constructor (GType type,
5286 guint n_construct_properties,
5287 GObjectConstructParam *construct_params)
5289 GtkFileChooserDefault *impl;
5292 profile_start ("start", NULL);
5294 object = G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->constructor (type,
5295 n_construct_properties,
5297 impl = GTK_FILE_CHOOSER_DEFAULT (object);
5299 g_assert (impl->file_system);
5301 gtk_widget_push_composite_child ();
5303 /* Shortcuts model */
5304 shortcuts_model_create (impl);
5306 /* The browse widgets */
5307 impl->browse_widgets = browse_widgets_create (impl);
5308 gtk_box_pack_start (GTK_BOX (impl), impl->browse_widgets, TRUE, TRUE, 0);
5310 /* Alignment to hold extra widget */
5311 impl->extra_align = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
5312 gtk_box_pack_start (GTK_BOX (impl), impl->extra_align, FALSE, FALSE, 0);
5314 gtk_widget_pop_composite_child ();
5315 update_appearance (impl);
5317 profile_end ("end", NULL);
5322 /* Sets the extra_widget by packing it in the appropriate place */
5324 set_extra_widget (GtkFileChooserDefault *impl,
5325 GtkWidget *extra_widget)
5329 g_object_ref (extra_widget);
5330 /* FIXME: is this right ? */
5331 gtk_widget_show (extra_widget);
5334 if (impl->extra_widget)
5336 gtk_container_remove (GTK_CONTAINER (impl->extra_align), impl->extra_widget);
5337 g_object_unref (impl->extra_widget);
5340 impl->extra_widget = extra_widget;
5341 if (impl->extra_widget)
5343 gtk_container_add (GTK_CONTAINER (impl->extra_align), impl->extra_widget);
5344 gtk_widget_show (impl->extra_align);
5347 gtk_widget_hide (impl->extra_align);
5351 set_local_only (GtkFileChooserDefault *impl,
5352 gboolean local_only)
5354 if (local_only != impl->local_only)
5356 impl->local_only = local_only;
5358 if (impl->location_entry)
5359 _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), local_only);
5361 if (impl->shortcuts_model && impl->file_system)
5363 shortcuts_add_volumes (impl);
5364 shortcuts_add_bookmarks (impl);
5367 if (local_only && impl->current_folder &&
5368 !g_file_is_native (impl->current_folder))
5370 /* If we are pointing to a non-local folder, make an effort to change
5371 * back to a local folder, but it's really up to the app to not cause
5372 * such a situation, so we ignore errors.
5374 const gchar *home = g_get_home_dir ();
5380 home_file = g_file_new_for_path (home);
5382 gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (impl), home_file, NULL);
5384 g_object_unref (home_file);
5390 volumes_bookmarks_changed_cb (GtkFileSystem *file_system,
5391 GtkFileChooserDefault *impl)
5393 shortcuts_add_volumes (impl);
5394 shortcuts_add_bookmarks (impl);
5396 bookmarks_check_add_sensitivity (impl);
5397 bookmarks_check_remove_sensitivity (impl);
5398 shortcuts_check_popup_sensitivity (impl);
5401 /* Sets the file chooser to multiple selection mode */
5403 set_select_multiple (GtkFileChooserDefault *impl,
5404 gboolean select_multiple,
5405 gboolean property_notify)
5407 GtkTreeSelection *selection;
5408 GtkSelectionMode mode;
5410 if (select_multiple == impl->select_multiple)
5413 mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE;
5415 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5416 gtk_tree_selection_set_mode (selection, mode);
5418 gtk_tree_view_set_rubber_banding (GTK_TREE_VIEW (impl->browse_files_tree_view), select_multiple);
5420 impl->select_multiple = select_multiple;
5421 g_object_notify (G_OBJECT (impl), "select-multiple");
5423 check_preview_change (impl);
5427 set_file_system_backend (GtkFileChooserDefault *impl)
5429 profile_start ("start for backend", "default");
5431 impl->file_system = _gtk_file_system_new ();
5433 g_signal_connect (impl->file_system, "volumes-changed",
5434 G_CALLBACK (volumes_bookmarks_changed_cb), impl);
5435 g_signal_connect (impl->file_system, "bookmarks-changed",
5436 G_CALLBACK (volumes_bookmarks_changed_cb), impl);
5438 profile_end ("end", NULL);
5442 unset_file_system_backend (GtkFileChooserDefault *impl)
5444 g_signal_handlers_disconnect_by_func (impl->file_system,
5445 G_CALLBACK (volumes_bookmarks_changed_cb), impl);
5447 g_object_unref (impl->file_system);
5449 impl->file_system = NULL;
5452 /* This function is basically a do_all function.
5454 * It sets the visibility on all the widgets based on the current state, and
5455 * moves the custom_widget if needed.
5458 update_appearance (GtkFileChooserDefault *impl)
5460 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
5461 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5465 gtk_widget_hide (impl->location_button);
5466 save_widgets_create (impl);
5468 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
5469 text = _("Save in _folder:");
5471 text = _("Create in _folder:");
5473 gtk_label_set_text_with_mnemonic (GTK_LABEL (impl->save_folder_label), text);
5475 if (gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
5477 gtk_widget_set_sensitive (impl->save_folder_label, FALSE);
5478 gtk_widget_set_sensitive (impl->save_folder_combo, FALSE);
5479 gtk_widget_show (impl->browse_widgets);
5483 gtk_widget_set_sensitive (impl->save_folder_label, TRUE);
5484 gtk_widget_set_sensitive (impl->save_folder_combo, TRUE);
5485 gtk_widget_hide (impl->browse_widgets);
5488 gtk_widget_show (impl->browse_new_folder_button);
5490 if (impl->select_multiple)
5492 g_warning ("Save mode cannot be set in conjunction with multiple selection mode. "
5493 "Re-setting to single selection mode.");
5494 set_select_multiple (impl, FALSE, TRUE);
5497 else if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
5498 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5500 gtk_widget_show (impl->location_button);
5501 save_widgets_destroy (impl);
5502 gtk_widget_show (impl->browse_widgets);
5503 location_mode_set (impl, impl->location_mode, TRUE);
5506 if (impl->location_entry)
5507 _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action);
5509 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
5510 gtk_widget_hide (impl->browse_new_folder_button);
5512 gtk_widget_show (impl->browse_new_folder_button);
5514 /* This *is* needed; we need to redraw the file list because the "sensitivity"
5515 * of files may change depending whether we are in a file or folder-only mode.
5517 gtk_widget_queue_draw (impl->browse_files_tree_view);
5519 emit_default_size_changed (impl);
5523 gtk_file_chooser_default_set_property (GObject *object,
5525 const GValue *value,
5529 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
5533 case GTK_FILE_CHOOSER_PROP_ACTION:
5535 GtkFileChooserAction action = g_value_get_enum (value);
5537 if (action != impl->action)
5539 gtk_file_chooser_default_unselect_all (GTK_FILE_CHOOSER (impl));
5541 if ((action == GTK_FILE_CHOOSER_ACTION_SAVE ||
5542 action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5543 && impl->select_multiple)
5545 g_warning ("Tried to change the file chooser action to SAVE or CREATE_FOLDER, but "
5546 "this is not allowed in multiple selection mode. Resetting the file chooser "
5547 "to single selection mode.");
5548 set_select_multiple (impl, FALSE, TRUE);
5550 impl->action = action;
5551 update_appearance (impl);
5552 settings_load (impl);
5557 case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
5558 /* Ignore property */
5561 case GTK_FILE_CHOOSER_PROP_FILTER:
5562 set_current_filter (impl, g_value_get_object (value));
5565 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
5566 set_local_only (impl, g_value_get_boolean (value));
5569 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
5570 set_preview_widget (impl, g_value_get_object (value));
5573 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
5574 impl->preview_widget_active = g_value_get_boolean (value);
5575 update_preview_widget_visibility (impl);
5578 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
5579 impl->use_preview_label = g_value_get_boolean (value);
5580 update_preview_widget_visibility (impl);
5583 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
5584 set_extra_widget (impl, g_value_get_object (value));
5587 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
5589 gboolean select_multiple = g_value_get_boolean (value);
5590 if ((impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
5591 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5594 g_warning ("Tried to set the file chooser to multiple selection mode, but this is "
5595 "not allowed in SAVE or CREATE_FOLDER modes. Ignoring the change and "
5596 "leaving the file chooser in single selection mode.");
5600 set_select_multiple (impl, select_multiple, FALSE);
5604 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
5606 gboolean show_hidden = g_value_get_boolean (value);
5607 if (show_hidden != impl->show_hidden)
5609 impl->show_hidden = show_hidden;
5611 if (impl->browse_files_model)
5612 _gtk_file_system_model_set_show_hidden (impl->browse_files_model, show_hidden);
5617 case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
5619 gboolean do_overwrite_confirmation = g_value_get_boolean (value);
5620 impl->do_overwrite_confirmation = do_overwrite_confirmation;
5625 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5631 gtk_file_chooser_default_get_property (GObject *object,
5636 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
5640 case GTK_FILE_CHOOSER_PROP_ACTION:
5641 g_value_set_enum (value, impl->action);
5644 case GTK_FILE_CHOOSER_PROP_FILTER:
5645 g_value_set_object (value, impl->current_filter);
5648 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
5649 g_value_set_boolean (value, impl->local_only);
5652 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
5653 g_value_set_object (value, impl->preview_widget);
5656 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
5657 g_value_set_boolean (value, impl->preview_widget_active);
5660 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
5661 g_value_set_boolean (value, impl->use_preview_label);
5664 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
5665 g_value_set_object (value, impl->extra_widget);
5668 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
5669 g_value_set_boolean (value, impl->select_multiple);
5672 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
5673 g_value_set_boolean (value, impl->show_hidden);
5676 case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
5677 g_value_set_boolean (value, impl->do_overwrite_confirmation);
5681 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5686 /* Removes the settings signal handler. It's safe to call multiple times */
5688 remove_settings_signal (GtkFileChooserDefault *impl,
5691 if (impl->settings_signal_id)
5693 GtkSettings *settings;
5695 settings = gtk_settings_get_for_screen (screen);
5696 g_signal_handler_disconnect (settings,
5697 impl->settings_signal_id);
5698 impl->settings_signal_id = 0;
5703 gtk_file_chooser_default_dispose (GObject *object)
5706 GtkFileChooserDefault *impl = (GtkFileChooserDefault *) object;
5708 if (impl->extra_widget)
5710 g_object_unref (impl->extra_widget);
5711 impl->extra_widget = NULL;
5714 pending_select_files_free (impl);
5716 /* cancel all pending operations */
5717 if (impl->pending_cancellables)
5719 for (l = impl->pending_cancellables; l; l = l->next)
5721 GCancellable *cancellable = G_CANCELLABLE (l->data);
5722 g_cancellable_cancel (cancellable);
5724 g_slist_free (impl->pending_cancellables);
5725 impl->pending_cancellables = NULL;
5728 if (impl->reload_icon_cancellables)
5730 for (l = impl->reload_icon_cancellables; l; l = l->next)
5732 GCancellable *cancellable = G_CANCELLABLE (l->data);
5733 g_cancellable_cancel (cancellable);
5735 g_slist_free (impl->reload_icon_cancellables);
5736 impl->reload_icon_cancellables = NULL;
5739 if (impl->loading_shortcuts)
5741 for (l = impl->loading_shortcuts; l; l = l->next)
5743 GCancellable *cancellable = G_CANCELLABLE (l->data);
5744 g_cancellable_cancel (cancellable);
5746 g_slist_free (impl->loading_shortcuts);
5747 impl->loading_shortcuts = NULL;
5750 if (impl->file_list_drag_data_received_cancellable)
5752 g_cancellable_cancel (impl->file_list_drag_data_received_cancellable);
5753 impl->file_list_drag_data_received_cancellable = NULL;
5756 if (impl->update_current_folder_cancellable)
5758 g_cancellable_cancel (impl->update_current_folder_cancellable);
5759 impl->update_current_folder_cancellable = NULL;
5762 if (impl->show_and_select_files_cancellable)
5764 g_cancellable_cancel (impl->show_and_select_files_cancellable);
5765 impl->show_and_select_files_cancellable = NULL;
5768 if (impl->should_respond_get_info_cancellable)
5770 g_cancellable_cancel (impl->should_respond_get_info_cancellable);
5771 impl->should_respond_get_info_cancellable = NULL;
5774 if (impl->update_from_entry_cancellable)
5776 g_cancellable_cancel (impl->update_from_entry_cancellable);
5777 impl->update_from_entry_cancellable = NULL;
5780 if (impl->shortcuts_activate_iter_cancellable)
5782 g_cancellable_cancel (impl->shortcuts_activate_iter_cancellable);
5783 impl->shortcuts_activate_iter_cancellable = NULL;
5786 search_stop_searching (impl, TRUE);
5787 recent_stop_loading (impl);
5789 remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl)));
5791 G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->dispose (object);
5794 /* We override show-all since we have internal widgets that
5795 * shouldn't be shown when you call show_all(), like the filter
5799 gtk_file_chooser_default_show_all (GtkWidget *widget)
5801 GtkFileChooserDefault *impl = (GtkFileChooserDefault *) widget;
5803 gtk_widget_show (widget);
5805 if (impl->extra_widget)
5806 gtk_widget_show_all (impl->extra_widget);
5809 /* Handler for GtkWindow::set-focus; this is where we save the last-focused
5810 * widget on our toplevel. See gtk_file_chooser_default_hierarchy_changed()
5813 toplevel_set_focus_cb (GtkWindow *window,
5815 GtkFileChooserDefault *impl)
5817 impl->toplevel_last_focus_widget = gtk_window_get_focus (window);
5820 /* We monitor the focus widget on our toplevel to be able to know which widget
5821 * was last focused at the time our "should_respond" method gets called.
5824 gtk_file_chooser_default_hierarchy_changed (GtkWidget *widget,
5825 GtkWidget *previous_toplevel)
5827 GtkFileChooserDefault *impl;
5828 GtkWidget *toplevel;
5830 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5832 if (previous_toplevel)
5834 g_assert (impl->toplevel_set_focus_id != 0);
5835 g_signal_handler_disconnect (previous_toplevel,
5836 impl->toplevel_set_focus_id);
5837 impl->toplevel_set_focus_id = 0;
5838 impl->toplevel_last_focus_widget = NULL;
5841 g_assert (impl->toplevel_set_focus_id == 0);
5843 toplevel = gtk_widget_get_toplevel (widget);
5844 if (GTK_IS_WINDOW (toplevel))
5846 impl->toplevel_set_focus_id = g_signal_connect (toplevel, "set-focus",
5847 G_CALLBACK (toplevel_set_focus_cb), impl);
5848 impl->toplevel_last_focus_widget = gtk_window_get_focus (GTK_WINDOW (toplevel));
5852 /* Changes the icons wherever it is needed */
5854 change_icon_theme (GtkFileChooserDefault *impl)
5856 GtkSettings *settings;
5859 profile_start ("start", NULL);
5861 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
5863 if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU, &width, &height))
5864 impl->icon_size = MAX (width, height);
5866 impl->icon_size = FALLBACK_ICON_SIZE;
5868 shortcuts_reload_icons (impl);
5869 gtk_widget_queue_resize (impl->browse_files_tree_view);
5871 profile_end ("end", NULL);
5874 /* Callback used when a GtkSettings value changes */
5876 settings_notify_cb (GObject *object,
5878 GtkFileChooserDefault *impl)
5882 profile_start ("start", NULL);
5884 name = g_param_spec_get_name (pspec);
5886 if (strcmp (name, "gtk-icon-theme-name") == 0 ||
5887 strcmp (name, "gtk-icon-sizes") == 0)
5888 change_icon_theme (impl);
5890 profile_end ("end", NULL);
5893 /* Installs a signal handler for GtkSettings so that we can monitor changes in
5897 check_icon_theme (GtkFileChooserDefault *impl)
5899 GtkSettings *settings;
5901 profile_start ("start", NULL);
5903 if (impl->settings_signal_id)
5905 profile_end ("end", NULL);
5909 if (gtk_widget_has_screen (GTK_WIDGET (impl)))
5911 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
5912 impl->settings_signal_id = g_signal_connect (settings, "notify",
5913 G_CALLBACK (settings_notify_cb), impl);
5915 change_icon_theme (impl);
5918 profile_end ("end", NULL);
5922 gtk_file_chooser_default_style_set (GtkWidget *widget,
5923 GtkStyle *previous_style)
5925 GtkFileChooserDefault *impl;
5927 profile_start ("start", NULL);
5929 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5931 profile_msg (" parent class style_set start", NULL);
5932 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->style_set (widget, previous_style);
5933 profile_msg (" parent class style_set end", NULL);
5935 if (gtk_widget_has_screen (GTK_WIDGET (impl)))
5936 change_icon_theme (impl);
5938 emit_default_size_changed (impl);
5940 profile_end ("end", NULL);
5944 gtk_file_chooser_default_screen_changed (GtkWidget *widget,
5945 GdkScreen *previous_screen)
5947 GtkFileChooserDefault *impl;
5949 profile_start ("start", NULL);
5951 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5953 if (GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->screen_changed)
5954 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->screen_changed (widget, previous_screen);
5956 remove_settings_signal (impl, previous_screen);
5957 check_icon_theme (impl);
5959 emit_default_size_changed (impl);
5961 profile_end ("end", NULL);
5965 gtk_file_chooser_default_size_allocate (GtkWidget *widget,
5966 GtkAllocation *allocation)
5968 GtkFileChooserDefault *impl;
5970 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5972 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->size_allocate (widget, allocation);
5976 get_is_file_filtered (GtkFileChooserDefault *impl,
5978 GFileInfo *file_info)
5980 GtkFileFilterInfo filter_info;
5981 GtkFileFilterFlags needed;
5984 if (!impl->current_filter)
5987 filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
5989 needed = gtk_file_filter_get_needed (impl->current_filter);
5991 filter_info.display_name = g_file_info_get_display_name (file_info);
5992 filter_info.mime_type = g_content_type_get_mime_type (g_file_info_get_content_type (file_info));
5994 if (needed & GTK_FILE_FILTER_FILENAME)
5996 filter_info.filename = g_file_get_path (file);
5997 if (filter_info.filename)
5998 filter_info.contains |= GTK_FILE_FILTER_FILENAME;
6001 filter_info.filename = NULL;
6003 if (needed & GTK_FILE_FILTER_URI)
6005 filter_info.uri = g_file_get_uri (file);
6006 if (filter_info.uri)
6007 filter_info.contains |= GTK_FILE_FILTER_URI;
6010 filter_info.uri = NULL;
6012 result = gtk_file_filter_filter (impl->current_filter, &filter_info);
6014 g_free ((gchar *)filter_info.filename);
6015 g_free ((gchar *)filter_info.uri);
6016 g_free ((gchar *)filter_info.mime_type);
6022 settings_load (GtkFileChooserDefault *impl)
6024 GtkFileChooserSettings *settings;
6025 LocationMode location_mode;
6026 gboolean show_hidden;
6027 gboolean expand_folders;
6028 gboolean show_size_column;
6030 settings = _gtk_file_chooser_settings_new ();
6032 location_mode = _gtk_file_chooser_settings_get_location_mode (settings);
6033 show_hidden = _gtk_file_chooser_settings_get_show_hidden (settings);
6034 expand_folders = _gtk_file_chooser_settings_get_expand_folders (settings);
6035 show_size_column = _gtk_file_chooser_settings_get_show_size_column (settings);
6037 g_object_unref (settings);
6039 location_mode_set (impl, location_mode, TRUE);
6041 gtk_file_chooser_set_show_hidden (GTK_FILE_CHOOSER (impl), show_hidden);
6043 impl->expand_folders = expand_folders;
6044 if (impl->save_expander)
6045 gtk_expander_set_expanded (GTK_EXPANDER (impl->save_expander), expand_folders);
6047 impl->show_size_column = show_size_column;
6048 if (impl->list_size_column)
6049 gtk_tree_view_column_set_visible (impl->list_size_column, show_size_column);
6053 save_dialog_geometry (GtkFileChooserDefault *impl, GtkFileChooserSettings *settings)
6055 GtkWindow *toplevel;
6056 int x, y, width, height;
6058 /* We don't save the geometry in non-expanded "save" mode, so that the "little
6059 * dialog" won't make future Open dialogs too small.
6061 if (!(impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
6062 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
6063 || impl->expand_folders))
6066 toplevel = get_toplevel (GTK_WIDGET (impl));
6068 if (!(toplevel && GTK_IS_FILE_CHOOSER_DIALOG (toplevel)))
6071 gtk_window_get_position (toplevel, &x, &y);
6072 gtk_window_get_size (toplevel, &width, &height);
6074 _gtk_file_chooser_settings_set_geometry (settings, x, y, width, height);
6078 settings_save (GtkFileChooserDefault *impl)
6080 GtkFileChooserSettings *settings;
6082 settings = _gtk_file_chooser_settings_new ();
6084 _gtk_file_chooser_settings_set_location_mode (settings, impl->location_mode);
6085 _gtk_file_chooser_settings_set_show_hidden (settings, gtk_file_chooser_get_show_hidden (GTK_FILE_CHOOSER (impl)));
6086 _gtk_file_chooser_settings_set_expand_folders (settings, impl->expand_folders);
6087 _gtk_file_chooser_settings_set_show_size_column (settings, impl->show_size_column);
6089 save_dialog_geometry (impl, settings);
6092 _gtk_file_chooser_settings_save (settings, NULL);
6094 g_object_unref (settings);
6097 /* GtkWidget::realize method */
6099 gtk_file_chooser_default_realize (GtkWidget *widget)
6101 GtkFileChooserDefault *impl;
6102 char *current_working_dir;
6104 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
6106 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->realize (widget);
6108 emit_default_size_changed (impl);
6111 /* GtkWidget::map method */
6113 gtk_file_chooser_default_map (GtkWidget *widget)
6115 GtkFileChooserDefault *impl;
6116 char *current_working_dir;
6118 profile_start ("start", NULL);
6120 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
6122 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->map (widget);
6124 if (impl->operation_mode == OPERATION_MODE_BROWSE)
6126 switch (impl->reload_state)
6129 /* The user didn't explicitly give us a folder to
6130 * display, so we'll use the cwd
6132 current_working_dir = g_get_current_dir ();
6133 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (impl),
6134 current_working_dir);
6135 g_free (current_working_dir);
6138 case RELOAD_HAS_FOLDER:
6139 /* Nothing; we are already loading or loaded, so we
6140 * don't need to reload
6144 case RELOAD_WAS_UNMAPPED:
6145 /* Just reload the current folder; else continue
6148 if (impl->current_folder)
6150 pending_select_files_store_selection (impl);
6151 change_folder_and_display_error (impl, impl->current_folder, FALSE);
6156 g_assert_not_reached ();
6160 volumes_bookmarks_changed_cb (impl->file_system, impl);
6162 settings_load (impl);
6164 profile_end ("end", NULL);
6167 /* GtkWidget::unmap method */
6169 gtk_file_chooser_default_unmap (GtkWidget *widget)
6171 GtkFileChooserDefault *impl;
6173 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
6175 settings_save (impl);
6177 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->unmap (widget);
6179 impl->reload_state = RELOAD_WAS_UNMAPPED;
6183 list_model_filter_func (GtkFileSystemModel *model,
6185 GFileInfo *file_info,
6188 GtkFileChooserDefault *impl = user_data;
6190 if (!impl->current_filter)
6193 if (_gtk_file_info_consider_as_directory (file_info))
6196 return !get_is_file_filtered (impl, file, file_info);
6200 install_list_model_filter (GtkFileChooserDefault *impl)
6202 GtkFileSystemModelFilter filter;
6205 g_assert (impl->browse_files_model != NULL);
6207 if (impl->current_filter)
6209 filter = list_model_filter_func;
6218 _gtk_file_system_model_set_filter (impl->browse_files_model,
6223 #define COMPARE_DIRECTORIES \
6224 GtkFileChooserDefault *impl = user_data; \
6225 GFileInfo *info_a = _gtk_file_system_model_get_info (impl->browse_files_model, a); \
6226 GFileInfo *info_b = _gtk_file_system_model_get_info (impl->browse_files_model, b); \
6227 gboolean dir_a, dir_b; \
6230 dir_a = _gtk_file_info_consider_as_directory (info_a); \
6232 return impl->list_sort_ascending ? -1 : 1; \
6235 dir_b = _gtk_file_info_consider_as_directory (info_b); \
6237 return impl->list_sort_ascending ? 1 : -1; \
6239 if (dir_a != dir_b) \
6240 return impl->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */
6242 /* Sort callback for the filename column */
6244 name_sort_func (GtkTreeModel *model,
6249 COMPARE_DIRECTORIES;
6252 gchar *key_a, *key_b;
6255 key_a = g_utf8_collate_key_for_filename (g_file_info_get_display_name (info_a), -1);
6256 key_b = g_utf8_collate_key_for_filename (g_file_info_get_display_name (info_b), -1);
6257 result = strcmp (key_a, key_b);
6266 /* Sort callback for the size column */
6268 size_sort_func (GtkTreeModel *model,
6273 COMPARE_DIRECTORIES;
6276 goffset size_a = g_file_info_get_size (info_a);
6277 goffset size_b = g_file_info_get_size (info_b);
6279 return size_a > size_b ? -1 : (size_a == size_b ? 0 : 1);
6283 /* Sort callback for the mtime column */
6285 mtime_sort_func (GtkTreeModel *model,
6290 COMPARE_DIRECTORIES;
6295 g_file_info_get_modification_time (info_a, &ta);
6296 g_file_info_get_modification_time (info_b, &tb);
6298 return ta.tv_sec > tb.tv_sec ? -1 : (ta.tv_sec == tb.tv_sec ? 0 : 1);
6302 /* Callback used when the sort column changes. We cache the sort order for use
6303 * in name_sort_func().
6306 list_sort_column_changed_cb (GtkTreeSortable *sortable,
6307 GtkFileChooserDefault *impl)
6309 GtkSortType sort_type;
6311 if (gtk_tree_sortable_get_sort_column_id (sortable, NULL, &sort_type))
6312 impl->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
6316 set_busy_cursor (GtkFileChooserDefault *impl,
6319 GtkWindow *toplevel;
6320 GdkDisplay *display;
6323 toplevel = get_toplevel (GTK_WIDGET (impl));
6324 if (!toplevel || !GTK_WIDGET_REALIZED (toplevel))
6327 display = gtk_widget_get_display (GTK_WIDGET (toplevel));
6330 cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
6334 gdk_window_set_cursor (GTK_WIDGET (toplevel)->window, cursor);
6335 gdk_display_flush (display);
6338 gdk_cursor_unref (cursor);
6341 /* Creates a sort model to wrap the file system model and sets it on the tree view */
6343 load_set_model (GtkFileChooserDefault *impl)
6345 profile_start ("start", NULL);
6347 g_assert (impl->browse_files_model != NULL);
6348 g_assert (impl->sort_model == NULL);
6350 profile_msg (" gtk_tree_model_sort_new_with_model start", NULL);
6351 impl->sort_model = (GtkTreeModelSort *)gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (impl->browse_files_model));
6352 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, name_sort_func, impl, NULL);
6353 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_SIZE, size_sort_func, impl, NULL);
6354 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_MTIME, mtime_sort_func, impl, NULL);
6355 gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->sort_model), NULL, NULL, NULL);
6356 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, GTK_SORT_ASCENDING);
6357 impl->list_sort_ascending = TRUE;
6358 profile_msg (" gtk_tree_model_sort_new_with_model end", NULL);
6360 g_signal_connect (impl->sort_model, "sort-column-changed",
6361 G_CALLBACK (list_sort_column_changed_cb), impl);
6363 profile_msg (" gtk_tree_view_set_model start", NULL);
6364 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
6365 GTK_TREE_MODEL (impl->sort_model));
6366 gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->browse_files_tree_view));
6367 gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_files_tree_view),
6368 GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);
6369 profile_msg (" gtk_tree_view_set_model end", NULL);
6371 profile_end ("end", NULL);
6374 /* Timeout callback used when the loading timer expires */
6376 load_timeout_cb (gpointer data)
6378 GtkFileChooserDefault *impl;
6380 profile_start ("start", NULL);
6382 impl = GTK_FILE_CHOOSER_DEFAULT (data);
6383 g_assert (impl->load_state == LOAD_PRELOAD);
6384 g_assert (impl->load_timeout_id != 0);
6385 g_assert (impl->browse_files_model != NULL);
6387 impl->load_timeout_id = 0;
6388 impl->load_state = LOAD_LOADING;
6390 load_set_model (impl);
6392 profile_end ("end", NULL);
6397 /* Sets up a new load timer for the model and switches to the LOAD_PRELOAD state */
6399 load_setup_timer (GtkFileChooserDefault *impl)
6401 g_assert (impl->load_timeout_id == 0);
6402 g_assert (impl->load_state != LOAD_PRELOAD);
6404 impl->load_timeout_id = gdk_threads_add_timeout (MAX_LOADING_TIME, load_timeout_cb, impl);
6405 impl->load_state = LOAD_PRELOAD;
6408 /* Removes the load timeout and switches to the LOAD_FINISHED state */
6410 load_remove_timer (GtkFileChooserDefault *impl)
6412 if (impl->load_timeout_id != 0)
6414 g_assert (impl->load_state == LOAD_PRELOAD);
6416 g_source_remove (impl->load_timeout_id);
6417 impl->load_timeout_id = 0;
6418 impl->load_state = LOAD_EMPTY;
6421 g_assert (impl->load_state == LOAD_EMPTY ||
6422 impl->load_state == LOAD_LOADING ||
6423 impl->load_state == LOAD_FINISHED);
6426 /* Selects the first row in the file list */
6428 browse_files_select_first_row (GtkFileChooserDefault *impl)
6431 GtkTreeIter dummy_iter;
6432 GtkTreeModel *tree_model;
6434 if (!impl->sort_model)
6437 path = gtk_tree_path_new_from_indices (0, -1);
6438 tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view));
6440 /* If the list is empty, do nothing. */
6441 if (gtk_tree_model_get_iter (tree_model, &dummy_iter, path))
6442 gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), path, NULL, FALSE);
6444 gtk_tree_path_free (path);
6447 struct center_selected_row_closure {
6448 GtkFileChooserDefault *impl;
6449 gboolean already_centered;
6452 /* Callback used from gtk_tree_selection_selected_foreach(); centers the
6453 * selected row in the tree view.
6456 center_selected_row_foreach_cb (GtkTreeModel *model,
6461 struct center_selected_row_closure *closure;
6464 if (closure->already_centered)
6467 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (closure->impl->browse_files_tree_view), path, NULL, TRUE, 0.5, 0.0);
6468 closure->already_centered = TRUE;
6471 /* Centers the selected row in the tree view */
6473 browse_files_center_selected_row (GtkFileChooserDefault *impl)
6475 struct center_selected_row_closure closure;
6476 GtkTreeSelection *selection;
6478 closure.impl = impl;
6479 closure.already_centered = FALSE;
6481 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6482 gtk_tree_selection_selected_foreach (selection, center_selected_row_foreach_cb, &closure);
6485 struct ShowAndSelectPathsData
6487 GtkFileChooserDefault *impl;
6492 show_and_select_files_finished_loading (GtkFolder *folder,
6495 gboolean have_hidden;
6496 gboolean have_filtered;
6498 struct ShowAndSelectPathsData *data = user_data;
6500 have_hidden = FALSE;
6501 have_filtered = FALSE;
6503 for (l = data->files; l; l = l->next)
6510 info = _gtk_folder_get_info (folder, file);
6514 have_hidden = g_file_info_get_is_hidden (info)
6515 || g_file_info_get_is_backup (info);
6518 have_filtered = (! _gtk_file_info_consider_as_directory (info)) &&
6519 get_is_file_filtered (data->impl, file, info);
6521 g_object_unref (info);
6523 if (have_hidden && have_filtered)
6524 break; /* we now have all the information we need */
6528 g_signal_handlers_disconnect_by_func (folder,
6529 show_and_select_files_finished_loading,
6533 g_object_set (data->impl, "show-hidden", TRUE, NULL);
6536 set_current_filter (data->impl, NULL);
6538 for (l = data->files; l; l = l->next)
6543 _gtk_file_system_model_path_do (data->impl->browse_files_model, file,
6544 select_func, data->impl);
6547 browse_files_center_selected_row (data->impl);
6549 g_object_unref (data->impl);
6550 g_slist_foreach (data->files, (GFunc) g_object_unref, NULL);
6551 g_slist_free (data->files);
6556 show_and_select_files_get_folder_cb (GCancellable *cancellable,
6558 const GError *error,
6561 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
6562 struct ShowAndSelectPathsData *data = user_data;
6564 if (data->impl->show_and_select_files_cancellable != cancellable)
6567 data->impl->show_and_select_files_cancellable = NULL;
6569 if (cancelled || error)
6572 g_object_unref (cancellable);
6574 if (_gtk_folder_is_finished_loading (folder))
6575 show_and_select_files_finished_loading (folder, user_data);
6577 g_signal_connect (folder, "finished-loading",
6578 G_CALLBACK (show_and_select_files_finished_loading),
6584 g_object_unref (data->impl);
6585 g_slist_foreach (data->files, (GFunc) g_object_unref, NULL);
6586 g_slist_free (data->files);
6589 g_object_unref (cancellable);
6593 show_and_select_files (GtkFileChooserDefault *impl,
6598 struct ShowAndSelectPathsData *info;
6600 profile_start ("start", NULL);
6604 profile_end ("end", NULL);
6608 info = g_new (struct ShowAndSelectPathsData, 1);
6609 info->impl = g_object_ref (impl);
6610 info->files = g_slist_copy (files);
6611 g_slist_foreach (info->files, (GFunc) g_object_ref, NULL);
6613 if (impl->show_and_select_files_cancellable)
6614 g_cancellable_cancel (impl->show_and_select_files_cancellable);
6616 impl->show_and_select_files_cancellable =
6617 _gtk_file_system_get_folder (impl->file_system, parent_file,
6618 "standard::is-hidden,standard::is-backup,standard::type,standard::name,standard::content-type",
6619 show_and_select_files_get_folder_cb, info);
6621 profile_end ("end", NULL);
6625 /* Processes the pending operation when a folder is finished loading */
6627 pending_select_files_process (GtkFileChooserDefault *impl)
6629 g_assert (impl->load_state == LOAD_FINISHED);
6630 g_assert (impl->browse_files_model != NULL);
6631 g_assert (impl->sort_model != NULL);
6633 if (impl->pending_select_files)
6636 show_and_select_files (impl, impl->current_folder, impl->pending_select_files, NULL);
6637 pending_select_files_free (impl);
6638 browse_files_center_selected_row (impl);
6642 /* We only select the first row if the chooser is actually mapped ---
6643 * selecting the first row is to help the user when he is interacting with
6644 * the chooser, but sometimes a chooser works not on behalf of the user,
6645 * but rather on behalf of something else like GtkFileChooserButton. In
6646 * that case, the chooser's selection should be what the caller expects,
6647 * as the user can't see that something else got selected. See bug #165264.
6649 if (GTK_WIDGET_MAPPED (impl) && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
6650 browse_files_select_first_row (impl);
6653 g_assert (impl->pending_select_files == NULL);
6656 /* Callback used when the file system model finishes loading */
6658 browse_files_model_finished_loading_cb (GtkFileSystemModel *model,
6659 GtkFileChooserDefault *impl)
6661 profile_start ("start", NULL);
6663 if (impl->load_state == LOAD_PRELOAD)
6665 load_remove_timer (impl);
6666 load_set_model (impl);
6668 else if (impl->load_state == LOAD_LOADING)
6674 /* We can't g_assert_not_reached(), as something other than us may have
6675 * initiated a folder reload. See #165556.
6677 profile_end ("end", NULL);
6681 g_assert (impl->load_timeout_id == 0);
6683 impl->load_state = LOAD_FINISHED;
6685 pending_select_files_process (impl);
6686 set_busy_cursor (impl, FALSE);
6687 #ifdef PROFILE_FILE_CHOOSER
6688 access ("MARK: *** FINISHED LOADING", F_OK);
6691 profile_end ("end", NULL);
6695 stop_loading_and_clear_list_model (GtkFileChooserDefault *impl)
6697 load_remove_timer (impl); /* This changes the state to LOAD_EMPTY */
6699 if (impl->browse_files_model)
6701 g_object_unref (impl->browse_files_model);
6702 impl->browse_files_model = NULL;
6705 if (impl->sort_model)
6707 g_object_unref (impl->sort_model);
6708 impl->sort_model = NULL;
6711 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
6714 /* Gets rid of the old list model and creates a new one for the current folder */
6716 set_list_model (GtkFileChooserDefault *impl,
6719 g_assert (impl->current_folder != NULL);
6721 profile_start ("start", NULL);
6723 stop_loading_and_clear_list_model (impl);
6725 set_busy_cursor (impl, TRUE);
6726 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
6728 impl->browse_files_model = _gtk_file_system_model_new (impl->file_system,
6729 impl->current_folder, 0,
6730 "standard,time,thumbnail::*",
6732 if (!impl->browse_files_model)
6734 set_busy_cursor (impl, FALSE);
6735 profile_end ("end", NULL);
6739 load_setup_timer (impl); /* This changes the state to LOAD_PRELOAD */
6741 g_signal_connect (impl->browse_files_model, "finished-loading",
6742 G_CALLBACK (browse_files_model_finished_loading_cb), impl);
6744 _gtk_file_system_model_set_show_hidden (impl->browse_files_model, impl->show_hidden);
6746 install_list_model_filter (impl);
6748 profile_end ("end", NULL);
6753 struct update_chooser_entry_selected_foreach_closure {
6755 GtkTreeIter first_selected_iter;
6759 compare_utf8_filenames (const gchar *a,
6762 gchar *a_folded, *b_folded;
6765 a_folded = g_utf8_strdown (a, -1);
6766 b_folded = g_utf8_strdown (b, -1);
6768 retval = strcmp (a_folded, b_folded);
6777 update_chooser_entry_selected_foreach (GtkTreeModel *model,
6782 struct update_chooser_entry_selected_foreach_closure *closure;
6785 closure->num_selected++;
6787 if (closure->num_selected == 1)
6788 closure->first_selected_iter = *iter;
6792 update_chooser_entry (GtkFileChooserDefault *impl)
6794 GtkTreeSelection *selection;
6795 struct update_chooser_entry_selected_foreach_closure closure;
6796 const char *file_part;
6798 /* no need to update the file chooser's entry if there's no entry */
6799 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
6800 impl->operation_mode == OPERATION_MODE_RECENT ||
6801 !impl->location_entry)
6804 if (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6805 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
6806 || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
6807 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
6808 && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)))
6811 g_assert (impl->location_entry != NULL);
6813 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6814 closure.num_selected = 0;
6815 gtk_tree_selection_selected_foreach (selection, update_chooser_entry_selected_foreach, &closure);
6819 if (closure.num_selected == 0)
6821 goto maybe_clear_entry;
6823 else if (closure.num_selected == 1)
6825 GtkTreeIter child_iter;
6827 if (impl->operation_mode == OPERATION_MODE_BROWSE)
6830 gboolean change_entry;
6832 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
6834 &closure.first_selected_iter);
6836 info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
6838 /* If the cursor moved to the row of the newly created folder,
6839 * retrieving info will return NULL.
6844 g_free (impl->browse_files_last_selected_name);
6845 impl->browse_files_last_selected_name =
6846 g_strdup (g_file_info_get_display_name (info));
6848 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
6849 impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
6850 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6852 /* We don't want the name to change when clicking on a folder... */
6853 change_entry = (! _gtk_file_info_consider_as_directory (info));
6856 change_entry = TRUE; /* ... unless we are in SELECT_FOLDER mode */
6860 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->browse_files_last_selected_name);
6862 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
6863 _gtk_file_chooser_entry_select_filename (GTK_FILE_CHOOSER_ENTRY (impl->location_entry));
6871 g_assert (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
6872 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER));
6874 /* Multiple selection, so just clear the entry. */
6876 g_free (impl->browse_files_last_selected_name);
6877 impl->browse_files_last_selected_name = NULL;
6879 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
6885 if ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
6886 && impl->browse_files_last_selected_name)
6888 const char *entry_text;
6890 gboolean clear_entry;
6892 entry_text = gtk_entry_get_text (GTK_ENTRY (impl->location_entry));
6893 len = strlen (entry_text);
6896 /* The file chooser entry may have appended a "/" to its text. So
6897 * take it out, and compare the result to the old selection.
6899 if (entry_text[len - 1] == G_DIR_SEPARATOR)
6903 tmp = g_strndup (entry_text, len - 1);
6904 clear_entry = (compare_utf8_filenames (impl->browse_files_last_selected_name, tmp) == 0);
6908 clear_entry = (compare_utf8_filenames (impl->browse_files_last_selected_name, entry_text) == 0);
6911 clear_entry = FALSE;
6914 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
6919 gtk_file_chooser_default_set_current_folder (GtkFileChooser *chooser,
6923 return gtk_file_chooser_default_update_current_folder (chooser, file, FALSE, FALSE, error);
6927 struct UpdateCurrentFolderData
6929 GtkFileChooserDefault *impl;
6931 gboolean keep_trail;
6932 gboolean clear_entry;
6933 GFile *original_file;
6934 GError *original_error;
6938 update_current_folder_mount_enclosing_volume_cb (GCancellable *cancellable,
6939 GtkFileSystemVolume *volume,
6940 const GError *error,
6943 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
6944 struct UpdateCurrentFolderData *data = user_data;
6945 GtkFileChooserDefault *impl = data->impl;
6947 if (cancellable != impl->update_current_folder_cancellable)
6950 impl->update_current_folder_cancellable = NULL;
6951 set_busy_cursor (impl, FALSE);
6958 error_changing_folder_dialog (data->impl, data->file, g_error_copy (error));
6959 impl->reload_state = RELOAD_EMPTY;
6963 change_folder_and_display_error (impl, data->file, data->clear_entry);
6966 g_object_unref (data->file);
6969 g_object_unref (cancellable);
6973 update_current_folder_get_info_cb (GCancellable *cancellable,
6975 const GError *error,
6978 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
6979 struct UpdateCurrentFolderData *data = user_data;
6980 GtkFileChooserDefault *impl = data->impl;
6982 if (cancellable != impl->update_current_folder_cancellable)
6985 impl->update_current_folder_cancellable = NULL;
6986 impl->reload_state = RELOAD_EMPTY;
6988 set_busy_cursor (impl, FALSE);
6997 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED))
6999 GMountOperation *mount_operation;
7000 GtkWidget *toplevel;
7002 g_object_unref (cancellable);
7003 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
7005 mount_operation = gtk_mount_operation_new (GTK_WINDOW (toplevel));
7007 set_busy_cursor (impl, TRUE);
7009 impl->update_current_folder_cancellable =
7010 _gtk_file_system_mount_enclosing_volume (impl->file_system, data->file,
7012 update_current_folder_mount_enclosing_volume_cb,
7018 if (!data->original_file)
7020 data->original_file = g_object_ref (data->file);
7021 data->original_error = g_error_copy (error);
7024 parent_file = g_file_get_parent (data->file);
7026 /* get parent path and try to change the folder to that */
7029 g_object_unref (data->file);
7030 data->file = parent_file;
7032 g_object_unref (cancellable);
7034 /* restart the update current folder operation */
7035 impl->reload_state = RELOAD_HAS_FOLDER;
7037 impl->update_current_folder_cancellable =
7038 _gtk_file_system_get_info (impl->file_system, data->file,
7040 update_current_folder_get_info_cb,
7043 set_busy_cursor (impl, TRUE);
7049 /* Error and bail out, ignoring "not found" errors since they're useless:
7050 * they only happen when a program defaults to a folder that has been (re)moved.
7052 if (!g_error_matches (data->original_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
7053 error_changing_folder_dialog (impl, data->original_file, data->original_error);
7055 g_error_free (data->original_error);
7056 g_object_unref (data->original_file);
7062 if (data->original_file)
7064 error_changing_folder_dialog (impl, data->original_file, data->original_error);
7066 g_object_unref (data->original_file);
7069 if (! _gtk_file_info_consider_as_directory (info))
7072 if (!_gtk_path_bar_set_file (GTK_PATH_BAR (impl->browse_path_bar), data->file, data->keep_trail, NULL))
7075 if (impl->current_folder != data->file)
7077 if (impl->current_folder)
7078 g_object_unref (impl->current_folder);
7080 impl->current_folder = g_object_ref (data->file);
7083 impl->reload_state = RELOAD_HAS_FOLDER;
7085 /* Update the widgets that may trigger a folder change themselves. */
7087 if (!impl->changing_folder)
7089 impl->changing_folder = TRUE;
7091 shortcuts_update_current_folder (impl);
7093 impl->changing_folder = FALSE;
7096 /* Set the folder on the save entry */
7098 if (impl->location_entry)
7100 _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
7101 impl->current_folder);
7103 if (data->clear_entry)
7104 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
7107 /* Create a new list model. This is slightly evil; we store the result value
7108 * but perform more actions rather than returning immediately even if it
7109 * generates an error.
7111 set_list_model (impl, NULL);
7113 /* Refresh controls */
7115 shortcuts_find_current_folder (impl);
7117 g_signal_emit_by_name (impl, "current-folder-changed", 0);
7119 check_preview_change (impl);
7120 bookmarks_check_add_sensitivity (impl);
7122 g_signal_emit_by_name (impl, "selection-changed", 0);
7125 g_object_unref (data->file);
7128 g_object_unref (cancellable);
7132 gtk_file_chooser_default_update_current_folder (GtkFileChooser *chooser,
7134 gboolean keep_trail,
7135 gboolean clear_entry,
7138 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7139 struct UpdateCurrentFolderData *data;
7141 profile_start ("start", NULL);
7143 g_object_ref (file);
7145 switch (impl->operation_mode)
7147 case OPERATION_MODE_SEARCH:
7148 search_switch_to_browse_mode (impl);
7150 case OPERATION_MODE_RECENT:
7151 recent_switch_to_browse_mode (impl);
7153 case OPERATION_MODE_BROWSE:
7157 if (impl->local_only && !g_file_is_native (file))
7159 g_set_error_literal (error,
7160 GTK_FILE_CHOOSER_ERROR,
7161 GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
7162 _("Cannot change to folder because it is not local"));
7164 g_object_unref (file);
7165 profile_end ("end - not local", NULL);
7169 if (impl->update_current_folder_cancellable)
7170 g_cancellable_cancel (impl->update_current_folder_cancellable);
7172 /* Test validity of path here. */
7173 data = g_new0 (struct UpdateCurrentFolderData, 1);
7175 data->file = g_object_ref (file);
7176 data->keep_trail = keep_trail;
7177 data->clear_entry = clear_entry;
7179 impl->reload_state = RELOAD_HAS_FOLDER;
7181 impl->update_current_folder_cancellable =
7182 _gtk_file_system_get_info (impl->file_system, file,
7184 update_current_folder_get_info_cb,
7187 set_busy_cursor (impl, TRUE);
7188 g_object_unref (file);
7190 profile_end ("end", NULL);
7195 gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser)
7197 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7199 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
7200 impl->operation_mode == OPERATION_MODE_RECENT)
7203 if (impl->reload_state == RELOAD_EMPTY)
7205 char *current_working_dir;
7208 /* We are unmapped, or we had an error while loading the last folder. We'll return
7209 * the $cwd since once we get (re)mapped, we'll load $cwd anyway unless the caller
7210 * explicitly calls set_current_folder() on us.
7212 current_working_dir = g_get_current_dir ();
7213 file = g_file_new_for_path (current_working_dir);
7214 g_free (current_working_dir);
7218 if (impl->current_folder)
7219 return g_object_ref (impl->current_folder);
7225 gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
7228 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7230 g_return_if_fail (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
7231 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
7233 pending_select_files_free (impl);
7234 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), name);
7238 select_func (GtkFileSystemModel *model,
7243 GtkFileChooserDefault *impl = user_data;
7244 GtkTreeSelection *selection;
7245 GtkTreeIter sorted_iter;
7247 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7249 gtk_tree_model_sort_convert_child_iter_to_iter (impl->sort_model, &sorted_iter, iter);
7250 gtk_tree_selection_select_iter (selection, &sorted_iter);
7254 gtk_file_chooser_default_select_file (GtkFileChooser *chooser,
7258 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7262 parent_file = g_file_get_parent (file);
7265 return gtk_file_chooser_set_current_folder_file (chooser, file, error);
7267 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
7268 impl->operation_mode == OPERATION_MODE_RECENT ||
7269 impl->load_state == LOAD_EMPTY)
7275 g_assert (impl->current_folder != NULL);
7277 same_path = g_file_equal (parent_file, impl->current_folder);
7280 if (same_path && impl->load_state == LOAD_FINISHED)
7285 files.data = (gpointer) file;
7288 result = show_and_select_files (impl, parent_file, &files, error);
7289 g_object_unref (parent_file);
7293 pending_select_files_add (impl, file);
7299 result = gtk_file_chooser_set_current_folder_file (chooser, parent_file, error);
7300 g_object_unref (parent_file);
7304 g_object_unref (parent_file);
7309 unselect_func (GtkFileSystemModel *model,
7314 GtkFileChooserDefault *impl = user_data;
7315 GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
7316 GtkTreePath *sorted_path;
7318 sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model,
7320 gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (tree_view),
7322 gtk_tree_path_free (sorted_path);
7326 gtk_file_chooser_default_unselect_file (GtkFileChooser *chooser,
7329 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7331 if (!impl->browse_files_model)
7334 _gtk_file_system_model_path_do (impl->browse_files_model, file,
7335 unselect_func, impl);
7339 maybe_select (GtkTreeModel *model,
7344 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (data);
7345 GtkTreeSelection *selection;
7349 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7351 info = get_list_file_info (impl, iter);
7352 is_folder = _gtk_file_info_consider_as_directory (info);
7354 if ((is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) ||
7355 (!is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN))
7356 gtk_tree_selection_select_iter (selection, iter);
7358 gtk_tree_selection_unselect_iter (selection, iter);
7364 gtk_file_chooser_default_select_all (GtkFileChooser *chooser)
7366 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7368 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
7369 impl->operation_mode == OPERATION_MODE_RECENT)
7371 GtkTreeSelection *selection;
7373 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7374 gtk_tree_selection_select_all (selection);
7378 if (impl->select_multiple)
7379 gtk_tree_model_foreach (GTK_TREE_MODEL (impl->sort_model),
7380 maybe_select, impl);
7384 gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser)
7386 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7387 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7389 gtk_tree_selection_unselect_all (selection);
7390 pending_select_files_free (impl);
7393 /* Checks whether the filename entry for the Save modes contains a well-formed filename.
7395 * is_well_formed_ret - whether what the user typed passes gkt_file_system_make_path()
7397 * is_empty_ret - whether the file entry is totally empty
7399 * is_file_part_empty_ret - whether the file part is empty (will be if user types "foobar/", and
7400 * the path will be "$cwd/foobar")
7403 check_save_entry (GtkFileChooserDefault *impl,
7405 gboolean *is_well_formed_ret,
7406 gboolean *is_empty_ret,
7407 gboolean *is_file_part_empty_ret,
7408 gboolean *is_folder)
7410 GtkFileChooserEntry *chooser_entry;
7411 GFile *current_folder;
7412 const char *file_part;
7416 g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
7417 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
7418 || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
7419 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
7420 && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY));
7422 chooser_entry = GTK_FILE_CHOOSER_ENTRY (impl->location_entry);
7424 if (strlen (gtk_entry_get_text (GTK_ENTRY (chooser_entry))) == 0)
7427 *is_well_formed_ret = TRUE;
7428 *is_empty_ret = TRUE;
7429 *is_file_part_empty_ret = TRUE;
7435 *is_empty_ret = FALSE;
7437 current_folder = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
7438 if (!current_folder)
7441 *is_well_formed_ret = FALSE;
7442 *is_file_part_empty_ret = FALSE;
7448 file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
7450 if (!file_part || file_part[0] == '\0')
7452 *file_ret = g_object_ref (current_folder);
7453 *is_well_formed_ret = TRUE;
7454 *is_file_part_empty_ret = TRUE;
7460 *is_file_part_empty_ret = FALSE;
7463 file = g_file_get_child_for_display_name (current_folder, file_part, &error);
7467 error_building_filename_dialog (impl, error);
7469 *is_well_formed_ret = FALSE;
7476 *is_well_formed_ret = TRUE;
7477 *is_folder = _gtk_file_chooser_entry_get_is_folder (chooser_entry, file);
7480 struct get_files_closure {
7481 GtkFileChooserDefault *impl;
7483 GFile *file_from_entry;
7487 get_files_foreach (GtkTreeModel *model,
7492 struct get_files_closure *info;
7494 GtkFileSystemModel *fs_model;
7495 GtkTreeIter sel_iter;
7498 fs_model = info->impl->browse_files_model;
7499 gtk_tree_model_sort_convert_iter_to_child_iter (info->impl->sort_model, &sel_iter, iter);
7501 file = _gtk_file_system_model_get_file (fs_model, &sel_iter);
7503 return; /* We are on the editable row */
7505 if (!info->file_from_entry || !g_file_equal (info->file_from_entry, file))
7506 info->result = g_slist_prepend (info->result, g_object_ref (file));
7510 gtk_file_chooser_default_get_files (GtkFileChooser *chooser)
7512 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7513 struct get_files_closure info;
7514 GtkWindow *toplevel;
7515 GtkWidget *current_focus;
7516 gboolean file_list_seen;
7518 if (impl->operation_mode == OPERATION_MODE_SEARCH)
7519 return search_get_selected_files (impl);
7521 if (impl->operation_mode == OPERATION_MODE_RECENT)
7522 return recent_get_selected_files (impl);
7526 info.file_from_entry = NULL;
7528 toplevel = get_toplevel (GTK_WIDGET (impl));
7530 current_focus = gtk_window_get_focus (toplevel);
7532 current_focus = NULL;
7534 file_list_seen = FALSE;
7535 if (current_focus == impl->browse_files_tree_view)
7537 GtkTreeSelection *selection;
7541 file_list_seen = TRUE;
7542 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7543 gtk_tree_selection_selected_foreach (selection, get_files_foreach, &info);
7545 /* If there is no selection in the file list, we probably have this situation:
7547 * 1. The user typed a filename in the SAVE filename entry ("foo.txt").
7548 * 2. He then double-clicked on a folder ("bar") in the file list
7550 * So we want the selection to be "bar/foo.txt". Jump to the case for the
7551 * filename entry to see if that is the case.
7553 if (info.result == NULL && impl->location_entry)
7556 else if (impl->location_entry && current_focus == impl->location_entry)
7558 gboolean is_well_formed, is_empty, is_file_part_empty, is_folder;
7562 check_save_entry (impl, &info.file_from_entry, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder);
7567 if (!is_well_formed)
7570 if (is_file_part_empty && impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
7572 g_object_unref (info.file_from_entry);
7576 if (info.file_from_entry)
7577 info.result = g_slist_prepend (info.result, info.file_from_entry);
7578 else if (!file_list_seen)
7583 else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
7585 else if (impl->location_entry && impl->toplevel_last_focus_widget == impl->location_entry)
7589 /* The focus is on a dialog's action area button or something else */
7590 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
7591 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
7599 /* If there's no folder selected, and we're in SELECT_FOLDER mode, then we
7600 * fall back to the current directory */
7601 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
7602 info.result == NULL)
7604 GFile *current_folder;
7606 current_folder = gtk_file_chooser_get_current_folder_file (chooser);
7609 info.result = g_slist_prepend (info.result, current_folder);
7612 return g_slist_reverse (info.result);
7616 gtk_file_chooser_default_get_preview_file (GtkFileChooser *chooser)
7618 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7620 if (impl->preview_file)
7621 return g_object_ref (impl->preview_file);
7626 static GtkFileSystem *
7627 gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser)
7629 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7631 return impl->file_system;
7634 /* Shows or hides the filter widgets */
7636 show_filters (GtkFileChooserDefault *impl,
7640 gtk_widget_show (impl->filter_combo_hbox);
7642 gtk_widget_hide (impl->filter_combo_hbox);
7646 gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
7647 GtkFileFilter *filter)
7649 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7652 if (g_slist_find (impl->filters, filter))
7654 g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
7658 g_object_ref_sink (filter);
7659 impl->filters = g_slist_append (impl->filters, filter);
7661 name = gtk_file_filter_get_name (filter);
7663 name = "Untitled filter"; /* Place-holder, doesn't need to be marked for translation */
7665 gtk_combo_box_append_text (GTK_COMBO_BOX (impl->filter_combo), name);
7667 if (!g_slist_find (impl->filters, impl->current_filter))
7668 set_current_filter (impl, filter);
7670 show_filters (impl, TRUE);
7674 gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
7675 GtkFileFilter *filter)
7677 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7678 GtkTreeModel *model;
7682 filter_index = g_slist_index (impl->filters, filter);
7684 if (filter_index < 0)
7686 g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
7690 impl->filters = g_slist_remove (impl->filters, filter);
7692 if (filter == impl->current_filter)
7695 set_current_filter (impl, impl->filters->data);
7697 set_current_filter (impl, NULL);
7700 /* Remove row from the combo box */
7701 model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->filter_combo));
7702 if (!gtk_tree_model_iter_nth_child (model, &iter, NULL, filter_index))
7703 g_assert_not_reached ();
7705 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
7707 g_object_unref (filter);
7710 show_filters (impl, FALSE);
7714 gtk_file_chooser_default_list_filters (GtkFileChooser *chooser)
7716 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7718 return g_slist_copy (impl->filters);
7721 /* Returns the position in the shortcuts tree where the nth specified shortcut would appear */
7723 shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl,
7726 return pos + shortcuts_get_index (impl, SHORTCUTS_SHORTCUTS);
7729 struct AddShortcutData
7731 GtkFileChooserDefault *impl;
7736 add_shortcut_get_info_cb (GCancellable *cancellable,
7738 const GError *error,
7742 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
7743 struct AddShortcutData *data = user_data;
7745 if (!g_slist_find (data->impl->loading_shortcuts, cancellable))
7748 data->impl->loading_shortcuts = g_slist_remove (data->impl->loading_shortcuts, cancellable);
7750 if (cancelled || error || (! _gtk_file_info_consider_as_directory (info)))
7753 pos = shortcuts_get_pos_for_shortcut_folder (data->impl, data->impl->num_shortcuts);
7755 shortcuts_insert_file (data->impl, pos, SHORTCUT_TYPE_FILE, NULL, data->file, NULL, FALSE, SHORTCUTS_SHORTCUTS);
7758 g_object_unref (data->impl);
7759 g_object_unref (data->file);
7762 g_object_unref (cancellable);
7766 gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser *chooser,
7770 GCancellable *cancellable;
7771 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7772 struct AddShortcutData *data;
7776 /* Avoid adding duplicates */
7777 pos = shortcut_find_position (impl, file);
7778 if (pos >= 0 && pos < shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR))
7782 uri = g_file_get_uri (file);
7783 /* translators, "Shortcut" means "Bookmark" here */
7785 GTK_FILE_CHOOSER_ERROR,
7786 GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
7787 _("Shortcut %s already exists"),
7794 for (l = impl->loading_shortcuts; l; l = l->next)
7796 GCancellable *c = l->data;
7799 f = g_object_get_data (G_OBJECT (c), "add-shortcut-path-key");
7800 if (f && g_file_equal (file, f))
7804 uri = g_file_get_uri (file);
7806 GTK_FILE_CHOOSER_ERROR,
7807 GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
7808 _("Shortcut %s already exists"),
7816 data = g_new0 (struct AddShortcutData, 1);
7817 data->impl = g_object_ref (impl);
7818 data->file = g_object_ref (file);
7820 cancellable = _gtk_file_system_get_info (impl->file_system, file,
7822 add_shortcut_get_info_cb, data);
7827 impl->loading_shortcuts = g_slist_append (impl->loading_shortcuts, cancellable);
7828 g_object_set_data (G_OBJECT (cancellable), "add-shortcut-path-key", data->file);
7834 gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser *chooser,
7838 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7845 for (l = impl->loading_shortcuts; l; l = l->next)
7847 GCancellable *c = l->data;
7850 f = g_object_get_data (G_OBJECT (c), "add-shortcut-path-key");
7851 if (f && g_file_equal (file, f))
7853 impl->loading_shortcuts = g_slist_remove (impl->loading_shortcuts, c);
7854 g_cancellable_cancel (c);
7859 if (impl->num_shortcuts == 0)
7862 pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
7863 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
7864 g_assert_not_reached ();
7866 for (i = 0; i < impl->num_shortcuts; i++)
7869 ShortcutType shortcut_type;
7872 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
7873 SHORTCUTS_COL_DATA, &col_data,
7874 SHORTCUTS_COL_TYPE, &shortcut_type,
7876 g_assert (col_data != NULL);
7877 g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
7879 shortcut = col_data;
7880 if (g_file_equal (shortcut, file))
7882 shortcuts_remove_rows (impl, pos + i, 1);
7883 impl->num_shortcuts--;
7887 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
7888 g_assert_not_reached ();
7893 uri = g_file_get_uri (file);
7894 /* translators, "Shortcut" means "Bookmark" here */
7896 GTK_FILE_CHOOSER_ERROR,
7897 GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
7898 _("Shortcut %s does not exist"),
7906 gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser)
7908 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7914 if (impl->num_shortcuts == 0)
7917 pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
7918 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
7919 g_assert_not_reached ();
7923 for (i = 0; i < impl->num_shortcuts; i++)
7926 ShortcutType shortcut_type;
7929 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
7930 SHORTCUTS_COL_DATA, &col_data,
7931 SHORTCUTS_COL_TYPE, &shortcut_type,
7933 g_assert (col_data != NULL);
7934 g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
7936 shortcut = col_data;
7937 list = g_slist_prepend (list, g_object_ref (shortcut));
7939 if (i != impl->num_shortcuts - 1)
7941 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
7942 g_assert_not_reached ();
7946 return g_slist_reverse (list);
7949 /* Guesses a size based upon font sizes */
7951 find_good_size_from_style (GtkWidget *widget,
7955 GtkFileChooserDefault *impl;
7960 g_assert (widget->style != NULL);
7961 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
7963 screen = gtk_widget_get_screen (widget);
7966 resolution = gdk_screen_get_resolution (screen);
7967 if (resolution < 0.0) /* will be -1 if the resolution is not defined in the GdkScreen */
7971 resolution = 96.0; /* wheeee */
7973 font_size = pango_font_description_get_size (widget->style->font_desc);
7974 font_size = PANGO_PIXELS (font_size) * resolution / 72.0;
7976 *width = font_size * NUM_CHARS;
7977 *height = font_size * NUM_LINES;
7981 gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
7982 gint *default_width,
7983 gint *default_height)
7985 GtkFileChooserDefault *impl;
7988 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
7990 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
7991 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
7992 || impl->expand_folders)
7994 GtkFileChooserSettings *settings;
7995 int x, y, width, height;
7997 settings = _gtk_file_chooser_settings_new ();
7998 _gtk_file_chooser_settings_get_geometry (settings, &x, &y, &width, &height);
7999 g_object_unref (settings);
8001 if (x >= 0 && y >= 0 && width > 0 && height > 0)
8003 *default_width = width;
8004 *default_height = height;
8008 find_good_size_from_style (GTK_WIDGET (chooser_embed), default_width, default_height);
8010 if (impl->preview_widget_active &&
8011 impl->preview_widget &&
8012 GTK_WIDGET_VISIBLE (impl->preview_widget))
8014 gtk_widget_size_request (impl->preview_box, &req);
8015 *default_width += PREVIEW_HBOX_SPACING + req.width;
8018 if (impl->extra_widget &&
8019 GTK_WIDGET_VISIBLE (impl->extra_widget))
8021 gtk_widget_size_request (impl->extra_align, &req);
8022 *default_height += GTK_BOX (chooser_embed)->spacing + req.height;
8027 gtk_widget_size_request (GTK_WIDGET (impl), &req);
8028 *default_width = req.width;
8029 *default_height = req.height;
8033 struct switch_folder_closure {
8034 GtkFileChooserDefault *impl;
8039 /* Used from gtk_tree_selection_selected_foreach() in switch_to_selected_folder() */
8041 switch_folder_foreach_cb (GtkTreeModel *model,
8046 struct switch_folder_closure *closure;
8047 GtkTreeIter child_iter;
8051 gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter);
8053 closure->file = _gtk_file_system_model_get_file (closure->impl->browse_files_model, &child_iter);
8054 closure->num_selected++;
8057 /* Changes to the selected folder in the list view */
8059 switch_to_selected_folder (GtkFileChooserDefault *impl)
8061 GtkTreeSelection *selection;
8062 struct switch_folder_closure closure;
8064 /* We do this with foreach() rather than get_selected() as we may be in
8065 * multiple selection mode
8068 closure.impl = impl;
8069 closure.file = NULL;
8070 closure.num_selected = 0;
8072 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
8073 gtk_tree_selection_selected_foreach (selection, switch_folder_foreach_cb, &closure);
8075 g_assert (closure.file && closure.num_selected == 1);
8077 change_folder_and_display_error (impl, closure.file, FALSE);
8080 /* Gets the GFileInfo for the selected row in the file list; assumes single
8084 get_selected_file_info_from_file_list (GtkFileChooserDefault *impl,
8085 gboolean *had_selection)
8087 GtkTreeSelection *selection;
8088 GtkTreeIter iter, child_iter;
8091 g_assert (!impl->select_multiple);
8092 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
8093 if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
8095 *had_selection = FALSE;
8099 *had_selection = TRUE;
8101 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
8105 info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
8109 /* Gets the display name of the selected file in the file list; assumes single
8110 * selection mode and that something is selected.
8112 static const gchar *
8113 get_display_name_from_file_list (GtkFileChooserDefault *impl)
8116 gboolean had_selection;
8118 info = get_selected_file_info_from_file_list (impl, &had_selection);
8119 g_assert (had_selection);
8120 g_assert (info != NULL);
8122 return g_file_info_get_display_name (info);
8126 add_custom_button_to_dialog (GtkDialog *dialog,
8127 const gchar *mnemonic_label,
8128 const gchar *stock_id,
8133 button = gtk_button_new_with_mnemonic (mnemonic_label);
8134 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
8135 gtk_button_set_image (GTK_BUTTON (button),
8136 gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON));
8137 gtk_widget_show (button);
8139 gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, response_id);
8142 /* Presents an overwrite confirmation dialog; returns whether we should accept
8146 confirm_dialog_should_accept_filename (GtkFileChooserDefault *impl,
8147 const gchar *file_part,
8148 const gchar *folder_display_name)
8150 GtkWindow *toplevel;
8154 toplevel = get_toplevel (GTK_WIDGET (impl));
8156 dialog = gtk_message_dialog_new (toplevel,
8157 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
8158 GTK_MESSAGE_QUESTION,
8160 _("A file named \"%s\" already exists. Do you want to replace it?"),
8162 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
8163 _("The file already exists in \"%s\". Replacing it will "
8164 "overwrite its contents."),
8165 folder_display_name);
8167 gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
8168 add_custom_button_to_dialog (GTK_DIALOG (dialog), _("_Replace"),
8169 GTK_STOCK_SAVE_AS, GTK_RESPONSE_ACCEPT);
8170 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
8171 GTK_RESPONSE_ACCEPT,
8172 GTK_RESPONSE_CANCEL,
8174 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
8176 if (toplevel->group)
8177 gtk_window_group_add_window (toplevel->group, GTK_WINDOW (dialog));
8179 response = gtk_dialog_run (GTK_DIALOG (dialog));
8181 gtk_widget_destroy (dialog);
8183 return (response == GTK_RESPONSE_ACCEPT);
8186 struct GetDisplayNameData
8188 GtkFileChooserDefault *impl;
8193 confirmation_confirm_get_info_cb (GCancellable *cancellable,
8195 const GError *error,
8198 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
8199 gboolean should_respond = FALSE;
8200 struct GetDisplayNameData *data = user_data;
8202 if (cancellable != data->impl->should_respond_get_info_cancellable)
8205 data->impl->should_respond_get_info_cancellable = NULL;
8211 /* Huh? Did the folder disappear? Let the caller deal with it */
8212 should_respond = TRUE;
8214 should_respond = confirm_dialog_should_accept_filename (data->impl, data->file_part, g_file_info_get_display_name (info));
8216 set_busy_cursor (data->impl, FALSE);
8218 g_signal_emit_by_name (data->impl, "response-requested");
8221 g_object_unref (data->impl);
8222 g_free (data->file_part);
8225 g_object_unref (cancellable);
8228 /* Does overwrite confirmation if appropriate, and returns whether the dialog
8229 * should respond. Can get the file part from the file list or the save entry.
8232 should_respond_after_confirm_overwrite (GtkFileChooserDefault *impl,
8233 const gchar *file_part,
8236 GtkFileChooserConfirmation conf;
8238 if (!impl->do_overwrite_confirmation)
8241 conf = GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM;
8243 g_signal_emit_by_name (impl, "confirm-overwrite", &conf);
8247 case GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM:
8249 struct GetDisplayNameData *data;
8251 g_assert (file_part != NULL);
8253 data = g_new0 (struct GetDisplayNameData, 1);
8254 data->impl = g_object_ref (impl);
8255 data->file_part = g_strdup (file_part);
8257 if (impl->should_respond_get_info_cancellable)
8258 g_cancellable_cancel (impl->should_respond_get_info_cancellable);
8260 impl->should_respond_get_info_cancellable =
8261 _gtk_file_system_get_info (impl->file_system, parent_file,
8262 "standard::display-name",
8263 confirmation_confirm_get_info_cb,
8265 set_busy_cursor (data->impl, TRUE);
8269 case GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME:
8272 case GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN:
8276 g_assert_not_reached ();
8281 struct FileExistsData
8283 GtkFileChooserDefault *impl;
8284 gboolean file_exists_and_is_not_folder;
8290 save_entry_get_info_cb (GCancellable *cancellable,
8292 const GError *error,
8295 gboolean parent_is_folder;
8296 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
8297 struct FileExistsData *data = user_data;
8299 if (cancellable != data->impl->should_respond_get_info_cancellable)
8302 data->impl->should_respond_get_info_cancellable = NULL;
8304 set_busy_cursor (data->impl, FALSE);
8310 parent_is_folder = FALSE;
8312 parent_is_folder = _gtk_file_info_consider_as_directory (info);
8314 if (parent_is_folder)
8316 if (data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8318 if (data->file_exists_and_is_not_folder)
8323 /* Dup the string because the string may be modified
8324 * depending on what clients do in the confirm-overwrite
8325 * signal and this corrupts the pointer
8327 file_part = g_strdup (_gtk_file_chooser_entry_get_file_part (GTK_FILE_CHOOSER_ENTRY (data->impl->location_entry)));
8328 retval = should_respond_after_confirm_overwrite (data->impl, file_part, data->parent_file);
8332 g_signal_emit_by_name (data->impl, "response-requested");
8335 g_signal_emit_by_name (data->impl, "response-requested");
8337 else /* GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER */
8339 GError *error = NULL;
8341 set_busy_cursor (data->impl, TRUE);
8342 g_file_make_directory (data->file, NULL, &error);
8343 set_busy_cursor (data->impl, FALSE);
8346 g_signal_emit_by_name (data->impl, "response-requested");
8348 error_creating_folder_dialog (data->impl, data->file, error);
8353 /* This will display an error, which is what we want */
8354 change_folder_and_display_error (data->impl, data->parent_file, FALSE);
8358 g_object_unref (data->impl);
8359 g_object_unref (data->file);
8360 g_object_unref (data->parent_file);
8363 g_object_unref (cancellable);
8367 file_exists_get_info_cb (GCancellable *cancellable,
8369 const GError *error,
8372 gboolean data_ownership_taken = FALSE;
8373 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
8374 gboolean file_exists_and_is_not_folder;
8375 struct FileExistsData *data = user_data;
8377 if (cancellable != data->impl->file_exists_get_info_cancellable)
8380 data->impl->file_exists_get_info_cancellable = NULL;
8382 set_busy_cursor (data->impl, FALSE);
8387 file_exists_and_is_not_folder = info && (! _gtk_file_info_consider_as_directory (info));
8389 if (data->impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
8390 /* user typed a filename; we are done */
8391 g_signal_emit_by_name (data->impl, "response-requested");
8392 else if (data->impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
8393 && file_exists_and_is_not_folder)
8395 /* Oops, the user typed the name of an existing path which is not
8398 error_creating_folder_over_existing_file_dialog (data->impl, data->file,
8399 g_error_copy (error));
8403 /* check that everything up to the last component exists */
8405 data->file_exists_and_is_not_folder = file_exists_and_is_not_folder;
8406 data_ownership_taken = TRUE;
8408 if (data->impl->should_respond_get_info_cancellable)
8409 g_cancellable_cancel (data->impl->should_respond_get_info_cancellable);
8411 data->impl->should_respond_get_info_cancellable =
8412 _gtk_file_system_get_info (data->impl->file_system,
8415 save_entry_get_info_cb,
8417 set_busy_cursor (data->impl, TRUE);
8421 if (!data_ownership_taken)
8423 g_object_unref (data->impl);
8424 g_object_unref (data->file);
8425 g_object_unref (data->parent_file);
8429 g_object_unref (cancellable);
8433 paste_text_received (GtkClipboard *clipboard,
8435 GtkFileChooserDefault *impl)
8442 file = g_file_new_for_uri (text);
8444 if (!gtk_file_chooser_default_select_file (GTK_FILE_CHOOSER (impl), file, NULL))
8445 location_popup_handler (impl, text);
8447 g_object_unref (file);
8450 /* Handler for the "location-popup-on-paste" keybinding signal */
8452 location_popup_on_paste_handler (GtkFileChooserDefault *impl)
8454 GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (impl),
8455 GDK_SELECTION_CLIPBOARD);
8456 gtk_clipboard_request_text (clipboard,
8457 (GtkClipboardTextReceivedFunc) paste_text_received,
8462 /* Implementation for GtkFileChooserEmbed::should_respond() */
8464 gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
8466 GtkFileChooserDefault *impl;
8467 GtkWidget *toplevel;
8468 GtkWidget *current_focus;
8470 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
8472 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
8473 g_assert (GTK_IS_WINDOW (toplevel));
8475 current_focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
8477 if (current_focus == impl->browse_files_tree_view)
8479 /* The following array encodes what we do based on the impl->action and the
8480 * number of files selected.
8483 NOOP, /* Do nothing (don't respond) */
8484 RESPOND, /* Respond immediately */
8485 RESPOND_OR_SWITCH, /* Respond immediately if the selected item is a file; switch to it if it is a folder */
8486 ALL_FILES, /* Respond only if everything selected is a file */
8487 ALL_FOLDERS, /* Respond only if everything selected is a folder */
8488 SAVE_ENTRY, /* Go to the code for handling the save entry */
8489 NOT_REACHED /* Sanity check */
8491 static const ActionToTake what_to_do[4][3] = {
8492 /* 0 selected 1 selected many selected */
8493 /* ACTION_OPEN */ { NOOP, RESPOND_OR_SWITCH, ALL_FILES },
8494 /* ACTION_SAVE */ { SAVE_ENTRY, RESPOND_OR_SWITCH, NOT_REACHED },
8495 /* ACTION_SELECT_FOLDER */ { RESPOND, ALL_FOLDERS, ALL_FOLDERS },
8496 /* ACTION_CREATE_FOLDER */ { SAVE_ENTRY, ALL_FOLDERS, NOT_REACHED }
8500 gboolean all_files, all_folders;
8502 ActionToTake action;
8506 g_assert (impl->action >= GTK_FILE_CHOOSER_ACTION_OPEN && impl->action <= GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
8508 if (impl->operation_mode == OPERATION_MODE_SEARCH)
8509 return search_should_respond (impl);
8511 if (impl->operation_mode == OPERATION_MODE_RECENT)
8512 return recent_should_respond (impl);
8514 selection_check (impl, &num_selected, &all_files, &all_folders);
8516 if (num_selected > 2)
8521 action = what_to_do [impl->action] [k];
8531 case RESPOND_OR_SWITCH:
8532 g_assert (num_selected == 1);
8536 switch_to_selected_folder (impl);
8539 else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8540 return should_respond_after_confirm_overwrite (impl,
8541 get_display_name_from_file_list (impl),
8542 impl->current_folder);
8556 g_assert_not_reached ();
8559 else if ((impl->location_entry != NULL) && (current_focus == impl->location_entry))
8562 gboolean is_well_formed, is_empty, is_file_part_empty;
8565 GtkFileChooserEntry *entry;
8570 g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
8571 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
8572 || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
8573 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8574 && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY));
8576 entry = GTK_FILE_CHOOSER_ENTRY (impl->location_entry);
8577 check_save_entry (impl, &file, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder);
8579 if (is_empty || !is_well_formed)
8582 g_assert (file != NULL);
8587 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
8588 impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8590 change_folder_and_display_error (impl, file, TRUE);
8593 else if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
8594 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8596 /* The folder already exists, so we do not need to create it.
8597 * Just respond to terminate the dialog.
8603 g_assert_not_reached ();
8609 struct FileExistsData *data;
8611 /* We need to check whether file exists and is not a folder */
8613 data = g_new0 (struct FileExistsData, 1);
8614 data->impl = g_object_ref (impl);
8615 data->file = g_object_ref (file);
8616 data->parent_file = g_object_ref (_gtk_file_chooser_entry_get_current_folder (entry));
8618 if (impl->file_exists_get_info_cancellable)
8619 g_cancellable_cancel (impl->file_exists_get_info_cancellable);
8621 impl->file_exists_get_info_cancellable =
8622 _gtk_file_system_get_info (impl->file_system, file,
8624 file_exists_get_info_cb,
8627 set_busy_cursor (impl, TRUE);
8631 g_error_free (error);
8634 g_object_unref (file);
8637 else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
8639 /* The focus is on a dialog's action area button, *and* the widget that
8640 * was focused immediately before it is the file list.
8644 else if (impl->operation_mode == OPERATION_MODE_SEARCH && impl->toplevel_last_focus_widget == impl->search_entry)
8646 search_entry_activate_cb (GTK_ENTRY (impl->search_entry), impl);
8649 else if (impl->location_entry && impl->toplevel_last_focus_widget == impl->location_entry)
8651 /* The focus is on a dialog's action area button, *and* the widget that
8652 * was focused immediately before it is the location entry.
8657 /* The focus is on a dialog's action area button or something else */
8658 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
8659 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8664 g_assert_not_reached ();
8668 /* Implementation for GtkFileChooserEmbed::initial_focus() */
8670 gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed)
8672 GtkFileChooserDefault *impl;
8675 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
8677 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
8678 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8680 if (impl->location_mode == LOCATION_MODE_PATH_BAR)
8681 widget = impl->browse_files_tree_view;
8683 widget = impl->location_entry;
8685 else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
8686 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8687 widget = impl->location_entry;
8690 g_assert_not_reached ();
8694 g_assert (widget != NULL);
8695 gtk_widget_grab_focus (widget);
8698 /* Callback used from gtk_tree_selection_selected_foreach(); gets the selected GFiles */
8700 search_selected_foreach_get_file_cb (GtkTreeModel *model,
8710 gtk_tree_model_get (model, iter, SEARCH_MODEL_COL_FILE, &file, -1);
8711 *list = g_slist_prepend (*list, g_object_ref (file));
8714 /* Constructs a list of the selected paths in search mode */
8716 search_get_selected_files (GtkFileChooserDefault *impl)
8719 GtkTreeSelection *selection;
8723 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
8724 gtk_tree_selection_selected_foreach (selection, search_selected_foreach_get_file_cb, &result);
8725 result = g_slist_reverse (result);
8730 /* Called from ::should_respond(). We return whether there are selected files
8731 * in the search list.
8734 search_should_respond (GtkFileChooserDefault *impl)
8736 GtkTreeSelection *selection;
8738 g_assert (impl->operation_mode == OPERATION_MODE_SEARCH);
8740 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
8741 return (gtk_tree_selection_count_selected_rows (selection) != 0);
8744 struct SearchHitInsertRequest
8746 GtkFileChooserDefault *impl;
8748 GtkTreeRowReference *row_ref;
8752 search_hit_get_info_cb (GCancellable *cancellable,
8754 const GError *error,
8757 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
8758 GdkPixbuf *pixbuf = NULL;
8761 GCancellable *model_cancellable;
8762 gboolean is_folder = FALSE;
8764 guint64 modification_time = 0;
8768 struct SearchHitInsertRequest *request = data;
8770 if (!request->impl->search_model)
8773 path = gtk_tree_row_reference_get_path (request->row_ref);
8777 gtk_tree_model_get_iter (GTK_TREE_MODEL (request->impl->search_model),
8779 gtk_tree_path_free (path);
8781 gtk_tree_model_get (GTK_TREE_MODEL (request->impl->search_model), &iter,
8782 SEARCH_MODEL_COL_CANCELLABLE, &model_cancellable,
8784 if (cancellable != model_cancellable)
8787 /* set the cancellable to NULL in the model */
8788 gtk_list_store_set (request->impl->search_model, &iter,
8789 SEARCH_MODEL_COL_CANCELLABLE, NULL,
8797 search_clear_model_row (GTK_TREE_MODEL (request->impl->search_model), &iter);
8798 gtk_list_store_remove (request->impl->search_model, &iter);
8802 display_name = g_strdup (g_file_info_get_display_name (info));
8803 mime_type = g_content_type_get_mime_type (g_file_info_get_content_type (info));
8804 g_file_info_get_modification_time (info, &mtime);
8805 modification_time = (guint64) mtime.tv_sec;
8806 size = g_file_info_get_size (info);
8807 is_folder = _gtk_file_info_consider_as_directory (info);
8808 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (request->impl),
8809 request->impl->icon_size);
8811 gtk_list_store_set (request->impl->search_model, &iter,
8812 SEARCH_MODEL_COL_PIXBUF, pixbuf,
8813 SEARCH_MODEL_COL_DISPLAY_NAME, display_name,
8814 SEARCH_MODEL_COL_MIME_TYPE, mime_type,
8815 SEARCH_MODEL_COL_IS_FOLDER, is_folder,
8816 SEARCH_MODEL_COL_MTIME, modification_time,
8817 SEARCH_MODEL_COL_SIZE, size,
8821 g_object_unref (pixbuf);
8824 g_object_unref (request->impl);
8825 g_object_unref (request->file);
8826 gtk_tree_row_reference_free (request->row_ref);
8829 g_object_unref (cancellable);
8832 /* Adds one hit from the search engine to the search_model */
8834 search_add_hit (GtkFileChooserDefault *impl,
8839 char *collation_key;
8842 GCancellable *cancellable;
8843 struct SearchHitInsertRequest *request;
8845 file = g_file_new_for_uri (uri);
8849 if (!g_file_is_native (file))
8851 g_object_unref (file);
8855 tmp = g_file_get_parse_name (file);
8856 collation_key = g_utf8_collate_key_for_filename (tmp, -1);
8859 request = g_new0 (struct SearchHitInsertRequest, 1);
8860 request->impl = g_object_ref (impl);
8861 request->file = g_object_ref (file);
8863 gtk_list_store_append (impl->search_model, &iter);
8864 p = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->search_model), &iter);
8866 request->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->search_model), p);
8867 gtk_tree_path_free (p);
8869 cancellable = _gtk_file_system_get_info (impl->file_system, file,
8872 "standard::content-type,"
8873 "standard::display-name,"
8876 search_hit_get_info_cb,
8879 gtk_list_store_set (impl->search_model, &iter,
8880 SEARCH_MODEL_COL_FILE, file,
8881 SEARCH_MODEL_COL_COLLATION_KEY, collation_key,
8882 SEARCH_MODEL_COL_CANCELLABLE, cancellable,
8886 /* Callback used from GtkSearchEngine when we get new hits */
8888 search_engine_hits_added_cb (GtkSearchEngine *engine,
8892 GtkFileChooserDefault *impl;
8895 impl = GTK_FILE_CHOOSER_DEFAULT (data);
8897 for (l = hits; l; l = l->next)
8898 search_add_hit (impl, (gchar*)l->data);
8901 /* Callback used from GtkSearchEngine when the query is done running */
8903 search_engine_finished_cb (GtkSearchEngine *engine,
8906 GtkFileChooserDefault *impl;
8908 impl = GTK_FILE_CHOOSER_DEFAULT (data);
8911 /* EB: setting the model here will avoid loads of row events,
8912 * but it'll make the search look like blocked.
8914 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
8915 GTK_TREE_MODEL (impl->search_model_filter));
8918 /* FMQ: if search was empty, say that we got no hits */
8919 set_busy_cursor (impl, FALSE);
8922 /* Displays a generic error when we cannot create a GtkSearchEngine.
8923 * It would be better if _gtk_search_engine_new() gave us a GError
8924 * with a better message, but it doesn't do that right now.
8927 search_error_could_not_create_client (GtkFileChooserDefault *impl)
8929 error_message (impl,
8930 _("Could not start the search process"),
8931 _("The program was not able to create a connection to the indexer "
8932 "daemon. Please make sure it is running."));
8936 search_engine_error_cb (GtkSearchEngine *engine,
8937 const gchar *message,
8940 GtkFileChooserDefault *impl;
8942 impl = GTK_FILE_CHOOSER_DEFAULT (data);
8944 search_stop_searching (impl, TRUE);
8945 error_message (impl, _("Could not send the search request"), message);
8947 set_busy_cursor (impl, FALSE);
8951 search_clear_model_row (GtkTreeModel *model,
8955 gchar *display_name;
8956 gchar *collation_key;
8957 GCancellable *cancellable;
8960 gtk_tree_model_get (model, iter,
8961 SEARCH_MODEL_COL_FILE, &file,
8962 SEARCH_MODEL_COL_DISPLAY_NAME, &display_name,
8963 SEARCH_MODEL_COL_COLLATION_KEY, &collation_key,
8964 SEARCH_MODEL_COL_CANCELLABLE, &cancellable,
8965 SEARCH_MODEL_COL_MIME_TYPE, &mime_type,
8969 g_object_unref (file);
8971 g_free (display_name);
8972 g_free (collation_key);
8976 g_cancellable_cancel (cancellable);
8979 /* Frees the data in the search_model */
8981 search_clear_model (GtkFileChooserDefault *impl,
8982 gboolean remove_from_treeview)
8984 GtkTreeModel *model;
8987 if (!impl->search_model)
8990 model = GTK_TREE_MODEL (impl->search_model);
8992 if (gtk_tree_model_get_iter_first (model, &iter))
8995 search_clear_model_row (model, &iter);
8997 while (gtk_tree_model_iter_next (model, &iter));
8999 g_object_unref (impl->search_model);
9000 impl->search_model = NULL;
9002 g_object_unref (impl->search_model_filter);
9003 impl->search_model_filter = NULL;
9005 g_object_unref (impl->search_model_sort);
9006 impl->search_model_sort = NULL;
9008 if (remove_from_treeview)
9009 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
9012 /* Stops any ongoing searches; does not touch the search_model */
9014 search_stop_searching (GtkFileChooserDefault *impl,
9015 gboolean remove_query)
9017 if (remove_query && impl->search_query)
9019 g_object_unref (impl->search_query);
9020 impl->search_query = NULL;
9023 if (impl->search_engine)
9025 _gtk_search_engine_stop (impl->search_engine);
9027 g_object_unref (impl->search_engine);
9028 impl->search_engine = NULL;
9032 /* Stops any pending searches, clears the file list, and switches back to OPERATION_MODE_BROWSE */
9034 search_switch_to_browse_mode (GtkFileChooserDefault *impl)
9036 g_assert (impl->operation_mode != OPERATION_MODE_BROWSE);
9038 search_stop_searching (impl, FALSE);
9039 search_clear_model (impl, TRUE);
9041 gtk_widget_destroy (impl->search_hbox);
9042 impl->search_hbox = NULL;
9043 impl->search_entry = NULL;
9045 gtk_widget_show (impl->browse_path_bar);
9046 gtk_widget_show (impl->browse_new_folder_button);
9048 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
9049 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
9051 gtk_widget_show (impl->location_button);
9053 if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
9054 gtk_widget_show (impl->location_entry_box);
9057 impl->operation_mode = OPERATION_MODE_BROWSE;
9059 file_list_set_sort_column_ids (impl);
9062 /* Sort callback from the path column */
9064 search_column_path_sort_func (GtkTreeModel *model,
9069 GtkFileChooserDefault *impl = user_data;
9070 GtkTreeIter child_a, child_b;
9071 const char *collation_key_a, *collation_key_b;
9072 gboolean is_folder_a, is_folder_b;
9074 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_a, a);
9075 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_b, b);
9077 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_a,
9078 SEARCH_MODEL_COL_IS_FOLDER, &is_folder_a,
9079 SEARCH_MODEL_COL_COLLATION_KEY, &collation_key_a,
9081 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_b,
9082 SEARCH_MODEL_COL_IS_FOLDER, &is_folder_b,
9083 SEARCH_MODEL_COL_COLLATION_KEY, &collation_key_b,
9086 if (!collation_key_a)
9089 if (!collation_key_b)
9092 /* always show folders first */
9093 if (is_folder_a != is_folder_b)
9094 return is_folder_a ? 1 : -1;
9096 return strcmp (collation_key_a, collation_key_b);
9099 /* Sort callback from the size column */
9101 search_column_size_sort_func (GtkTreeModel *model,
9106 GtkFileChooserDefault *impl = user_data;
9107 GtkTreeIter child_a, child_b;
9108 gboolean is_folder_a, is_folder_b;
9109 goffset size_a, size_b;
9111 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_a, a);
9112 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_b, b);
9114 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_a,
9115 SEARCH_MODEL_COL_IS_FOLDER, &is_folder_a,
9116 SEARCH_MODEL_COL_SIZE, &size_a,
9118 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_b,
9119 SEARCH_MODEL_COL_IS_FOLDER, &is_folder_b,
9120 SEARCH_MODEL_COL_SIZE, &size_b,
9123 if (is_folder_a != is_folder_b)
9124 return is_folder_a ? 1 : -1;
9126 if (size_a < size_b)
9128 else if (size_a > size_b)
9134 /* Sort callback from the modification time column */
9136 search_column_mtime_sort_func (GtkTreeModel *model,
9141 GtkFileChooserDefault *impl = user_data;
9142 GtkTreeIter child_a, child_b;
9143 gboolean is_folder_a, is_folder_b;
9144 guint64 mtime_a, mtime_b;
9146 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_a, a);
9147 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_b, b);
9149 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_a,
9150 SEARCH_MODEL_COL_IS_FOLDER, &is_folder_a,
9151 SEARCH_MODEL_COL_MTIME, &mtime_a,
9153 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_b,
9154 SEARCH_MODEL_COL_IS_FOLDER, &is_folder_b,
9155 SEARCH_MODEL_COL_MTIME, &mtime_b,
9158 if (is_folder_a != is_folder_b)
9159 return is_folder_a ? 1 : -1;
9161 if (mtime_a < mtime_b)
9163 else if (mtime_a > mtime_b)
9170 search_get_is_filtered (GtkFileChooserDefault *impl,
9172 const gchar *display_name,
9173 const gchar *mime_type)
9175 GtkFileFilterInfo filter_info;
9176 GtkFileFilterFlags needed;
9179 if (!impl->current_filter)
9182 filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
9183 needed = gtk_file_filter_get_needed (impl->current_filter);
9185 filter_info.display_name = display_name;
9186 filter_info.mime_type = mime_type;
9188 if (needed & GTK_FILE_FILTER_FILENAME)
9190 filter_info.filename = g_file_get_path (file);
9191 if (filter_info.filename)
9192 filter_info.contains |= GTK_FILE_FILTER_FILENAME;
9195 filter_info.filename = NULL;
9197 if (needed & GTK_FILE_FILTER_URI)
9199 filter_info.uri = g_file_get_uri (file);
9200 if (filter_info.uri)
9201 filter_info.contains |= GTK_FILE_FILTER_URI;
9204 filter_info.uri = NULL;
9206 result = gtk_file_filter_filter (impl->current_filter, &filter_info);
9208 if (filter_info.filename)
9209 g_free ((gchar *) filter_info.filename);
9210 if (filter_info.uri)
9211 g_free ((gchar *) filter_info.uri);
9217 /* Visibility function for the recent filter model */
9219 search_model_visible_func (GtkTreeModel *model,
9223 GtkFileChooserDefault *impl = user_data;
9225 gchar *display_name, *mime_type;
9228 if (!impl->current_filter)
9231 gtk_tree_model_get (model, iter,
9232 SEARCH_MODEL_COL_FILE, &file,
9233 SEARCH_MODEL_COL_IS_FOLDER, &is_folder,
9234 SEARCH_MODEL_COL_DISPLAY_NAME, &display_name,
9235 SEARCH_MODEL_COL_MIME_TYPE, &mime_type,
9244 return !search_get_is_filtered (impl, file, display_name, mime_type);
9247 /* Creates the search_model and puts it in the tree view */
9249 search_setup_model (GtkFileChooserDefault *impl)
9251 g_assert (impl->search_model == NULL);
9252 g_assert (impl->search_model_filter == NULL);
9253 g_assert (impl->search_model_sort == NULL);
9255 /* We store these columns in the search model:
9257 * SEARCH_MODEL_COL_FILE - a GFile for the hit's URI, stored
9258 * as a pointer not as a G_TYPE_FILE
9259 * SEARCH_MODEL_COL_DISPLAY_NAME - a string with the display name, stored
9260 * as a pointer not as a G_TYPE_STRING
9261 * SEARCH_MODEL_COL_COLLATION_KEY - collation key for the filename, stored
9262 * as a pointer not as a G_TYPE_STRING
9263 * SEARCH_MODEL_COL_MTIME - G_TYPE_UINT64 for the modification time
9264 * SEARCH_MODEL_COL_SIZE - G_TYPE_INT64 for the size
9265 * SEARCH_MODEL_COL_CANCELLABLE - cancellable used when getting the hit's info
9266 * SEARCH_MODEL_COL_PIXBUF - GdkPixbuf for the hit's icon
9267 * SEARCH_MODEL_COL_MIME_TYPE - a string with the hit's MIME type
9268 * SEARCH_MODEL_COL_IS_FOLDER - a boolean flag for folders
9270 * Keep this in sync with the enumeration defined near the beginning
9273 impl->search_model = gtk_list_store_new (SEARCH_MODEL_COL_NUM_COLUMNS,
9274 G_TYPE_POINTER, /* file */
9275 G_TYPE_POINTER, /* display-name */
9276 G_TYPE_POINTER, /* collation-key */
9277 G_TYPE_UINT64, /* mtime */
9278 G_TYPE_INT64, /* size */
9279 G_TYPE_POINTER, /* cancellable */
9280 GDK_TYPE_PIXBUF, /* pixbuf */
9281 G_TYPE_POINTER, /* mime-type */
9282 G_TYPE_BOOLEAN /*is-folder */);
9284 impl->search_model_filter =
9285 GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (GTK_TREE_MODEL (impl->search_model), NULL));
9286 gtk_tree_model_filter_set_visible_func (impl->search_model_filter,
9287 search_model_visible_func,
9290 impl->search_model_sort =
9291 GTK_TREE_MODEL_SORT (search_model_sort_new (impl, GTK_TREE_MODEL (impl->search_model_filter)));
9292 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model_sort),
9293 SEARCH_MODEL_COL_FILE,
9294 search_column_path_sort_func,
9296 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model_sort),
9297 SEARCH_MODEL_COL_MTIME,
9298 search_column_mtime_sort_func,
9300 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model_sort),
9301 SEARCH_MODEL_COL_SIZE,
9302 search_column_size_sort_func,
9304 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->search_model_sort),
9305 SEARCH_MODEL_COL_MTIME,
9306 GTK_SORT_DESCENDING);
9308 /* EB: setting the model here will make the hits list update feel
9309 * more "alive" than setting the model at the end of the search
9312 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
9313 GTK_TREE_MODEL (impl->search_model_sort));
9317 search_get_valid_child_iter (GtkFileChooserDefault *impl,
9318 GtkTreeIter *child_iter,
9323 if (!impl->search_model)
9326 if (!impl->search_model_filter || !impl->search_model_sort)
9329 /* pass 1: get the iterator in the filter model */
9330 gtk_tree_model_sort_convert_iter_to_child_iter (impl->search_model_sort,
9333 /* pass 2: get the iterator in the real model */
9334 gtk_tree_model_filter_convert_iter_to_child_iter (impl->search_model_filter,
9335 child_iter, &middle);
9338 /* Creates a new query with the specified text and launches it */
9340 search_start_query (GtkFileChooserDefault *impl,
9341 const gchar *query_text)
9343 search_stop_searching (impl, FALSE);
9344 search_clear_model (impl, TRUE);
9345 search_setup_model (impl);
9346 set_busy_cursor (impl, TRUE);
9348 if (impl->search_engine == NULL)
9349 impl->search_engine = _gtk_search_engine_new ();
9351 if (!impl->search_engine)
9353 set_busy_cursor (impl, FALSE);
9354 search_error_could_not_create_client (impl); /* lame; we don't get an error code or anything */
9358 if (!impl->search_query)
9360 impl->search_query = _gtk_query_new ();
9361 _gtk_query_set_text (impl->search_query, query_text);
9364 _gtk_search_engine_set_query (impl->search_engine, impl->search_query);
9366 g_signal_connect (impl->search_engine, "hits-added",
9367 G_CALLBACK (search_engine_hits_added_cb), impl);
9368 g_signal_connect (impl->search_engine, "finished",
9369 G_CALLBACK (search_engine_finished_cb), impl);
9370 g_signal_connect (impl->search_engine, "error",
9371 G_CALLBACK (search_engine_error_cb), impl);
9373 _gtk_search_engine_start (impl->search_engine);
9376 /* Callback used when the user presses Enter while typing on the search
9377 * entry; starts the query
9380 search_entry_activate_cb (GtkEntry *entry,
9383 GtkFileChooserDefault *impl;
9386 impl = GTK_FILE_CHOOSER_DEFAULT (data);
9388 text = gtk_entry_get_text (GTK_ENTRY (impl->search_entry));
9389 if (strlen (text) == 0)
9392 /* reset any existing query object */
9393 if (impl->search_query)
9395 g_object_unref (impl->search_query);
9396 impl->search_query = NULL;
9399 search_start_query (impl, text);
9402 /* Hides the path bar and creates the search entry */
9404 search_setup_widgets (GtkFileChooserDefault *impl)
9409 impl->search_hbox = gtk_hbox_new (FALSE, 12);
9413 image = gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_BUTTON);
9414 gtk_size_group_add_widget (GTK_SIZE_GROUP (impl->browse_path_bar_size_group), image);
9415 gtk_box_pack_start (GTK_BOX (impl->search_hbox), image, FALSE, FALSE, 5);
9419 label = gtk_label_new (NULL);
9420 gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("<b>_Search:</b>"));
9421 gtk_box_pack_start (GTK_BOX (impl->search_hbox), label, FALSE, FALSE, 0);
9425 impl->search_entry = gtk_entry_new ();
9426 gtk_label_set_mnemonic_widget (GTK_LABEL (label), impl->search_entry);
9427 g_signal_connect (impl->search_entry, "activate",
9428 G_CALLBACK (search_entry_activate_cb),
9430 gtk_box_pack_start (GTK_BOX (impl->search_hbox), impl->search_entry, TRUE, TRUE, 0);
9432 /* if there already is a query, restart it */
9433 if (impl->search_query)
9435 gchar *query = _gtk_query_get_text (impl->search_query);
9439 gtk_entry_set_text (GTK_ENTRY (impl->search_entry), query);
9440 search_start_query (impl, query);
9446 g_object_unref (impl->search_query);
9447 impl->search_query = NULL;
9451 gtk_widget_hide (impl->browse_path_bar);
9452 gtk_widget_hide (impl->browse_new_folder_button);
9454 /* Box for search widgets */
9455 gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->search_hbox, TRUE, TRUE, 0);
9456 gtk_widget_show_all (impl->search_hbox);
9458 /* Hide the location widgets temporarily */
9460 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
9461 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
9463 gtk_widget_hide (impl->location_button);
9464 gtk_widget_hide (impl->location_entry_box);
9467 gtk_widget_grab_focus (impl->search_entry);
9469 /* FMQ: hide the filter combo? */
9472 /* Stops running operations like populating the browse model, searches, and the recent-files model */
9474 stop_operation (GtkFileChooserDefault *impl, OperationMode mode)
9478 case OPERATION_MODE_BROWSE:
9479 stop_loading_and_clear_list_model (impl);
9482 case OPERATION_MODE_SEARCH:
9483 search_stop_searching (impl, FALSE);
9484 search_clear_model (impl, TRUE);
9486 gtk_widget_destroy (impl->search_hbox);
9487 impl->search_hbox = NULL;
9488 impl->search_entry = NULL;
9491 case OPERATION_MODE_RECENT:
9492 recent_stop_loading (impl);
9493 recent_clear_model (impl, TRUE);
9495 gtk_widget_destroy (impl->recent_hbox);
9496 impl->recent_hbox = NULL;
9501 /* Main entry point to the searching functions; this gets called when the user
9502 * activates the Search shortcut.
9505 search_activate (GtkFileChooserDefault *impl)
9507 OperationMode previous_mode;
9509 if (impl->operation_mode == OPERATION_MODE_SEARCH)
9511 gtk_widget_grab_focus (impl->search_entry);
9515 previous_mode = impl->operation_mode;
9516 impl->operation_mode = OPERATION_MODE_SEARCH;
9518 stop_operation (impl, previous_mode);
9520 g_assert (impl->search_hbox == NULL);
9521 g_assert (impl->search_entry == NULL);
9522 g_assert (impl->search_model == NULL);
9523 g_assert (impl->search_model_filter == NULL);
9525 search_setup_widgets (impl);
9526 file_list_set_sort_column_ids (impl);
9530 * Recent files support
9533 /* Frees the data in the recent_model */
9535 recent_clear_model (GtkFileChooserDefault *impl,
9536 gboolean remove_from_treeview)
9538 GtkTreeModel *model;
9541 if (!impl->recent_model)
9544 model = GTK_TREE_MODEL (impl->recent_model);
9546 if (remove_from_treeview)
9547 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
9549 if (gtk_tree_model_get_iter_first (model, &iter))
9554 GCancellable *cancellable;
9555 GtkRecentInfo *recent_info;
9556 gchar *display_name;
9558 gtk_tree_model_get (model, &iter,
9559 RECENT_MODEL_COL_DISPLAY_NAME, &display_name,
9560 RECENT_MODEL_COL_FILE, &file,
9561 RECENT_MODEL_COL_CANCELLABLE, &cancellable,
9562 RECENT_MODEL_COL_INFO, &recent_info,
9566 g_cancellable_cancel (cancellable);
9568 g_object_unref (file);
9569 gtk_recent_info_unref (recent_info);
9570 g_free (display_name);
9572 while (gtk_tree_model_iter_next (model, &iter));
9575 g_object_unref (impl->recent_model);
9576 impl->recent_model = NULL;
9578 g_object_unref (impl->recent_model_filter);
9579 impl->recent_model_filter = NULL;
9581 g_object_unref (impl->recent_model_sort);
9582 impl->recent_model_sort = NULL;
9585 /* Stops any ongoing loading of the recent files list; does
9586 * not touch the recent_model
9589 recent_stop_loading (GtkFileChooserDefault *impl)
9591 if (impl->load_recent_id)
9593 g_source_remove (impl->load_recent_id);
9594 impl->load_recent_id = 0;
9598 /* Stops any pending load, clears the file list, and switches
9599 * back to OPERATION_MODE_BROWSE
9602 recent_switch_to_browse_mode (GtkFileChooserDefault *impl)
9604 g_assert (impl->operation_mode != OPERATION_MODE_BROWSE);
9606 recent_stop_loading (impl);
9607 recent_clear_model (impl, TRUE);
9609 gtk_widget_destroy (impl->recent_hbox);
9610 impl->recent_hbox = NULL;
9612 gtk_widget_show (impl->browse_path_bar);
9613 gtk_widget_show (impl->browse_new_folder_button);
9615 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
9616 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
9618 gtk_widget_show (impl->location_button);
9620 if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
9621 gtk_widget_show (impl->location_entry_box);
9624 gtk_tree_view_column_set_visible (impl->list_size_column, impl->show_size_column);
9626 impl->operation_mode = OPERATION_MODE_BROWSE;
9628 file_list_set_sort_column_ids (impl);
9631 /* Sort callback from the modification time column */
9633 recent_column_mtime_sort_func (GtkTreeModel *model,
9638 GtkFileChooserDefault *impl = user_data;
9639 GtkTreeIter child_a, child_b;
9640 GtkRecentInfo *info_a, *info_b;
9641 gboolean is_folder_a, is_folder_b;
9643 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_a, a);
9644 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_b, b);
9646 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_a,
9647 RECENT_MODEL_COL_IS_FOLDER, &is_folder_a,
9648 RECENT_MODEL_COL_INFO, &info_a,
9650 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_b,
9651 RECENT_MODEL_COL_IS_FOLDER, &is_folder_b,
9652 RECENT_MODEL_COL_INFO, &info_b,
9661 /* folders always go first */
9662 if (is_folder_a != is_folder_b)
9663 return is_folder_a ? 1 : -1;
9665 if (gtk_recent_info_get_modified (info_a) < gtk_recent_info_get_modified (info_b))
9667 else if (gtk_recent_info_get_modified (info_a) > gtk_recent_info_get_modified (info_b))
9674 recent_column_path_sort_func (GtkTreeModel *model,
9679 GtkFileChooserDefault *impl = user_data;
9680 GtkTreeIter child_a, child_b;
9681 gboolean is_folder_a, is_folder_b;
9682 gchar *name_a, *name_b;
9684 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_a, a);
9685 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_b, b);
9687 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_a,
9688 RECENT_MODEL_COL_IS_FOLDER, &is_folder_a,
9689 RECENT_MODEL_COL_DISPLAY_NAME, &name_a,
9691 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_b,
9692 RECENT_MODEL_COL_IS_FOLDER, &is_folder_b,
9693 RECENT_MODEL_COL_DISPLAY_NAME, &name_b,
9702 if (is_folder_a != is_folder_b)
9703 return is_folder_a ? 1 : -1;
9705 return strcmp (name_a, name_b);
9709 recent_get_is_filtered (GtkFileChooserDefault *impl,
9711 GtkRecentInfo *recent_info)
9713 GtkFileFilterInfo filter_info;
9714 GtkFileFilterFlags needed;
9717 if (!impl->current_filter)
9720 filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE;
9721 needed = gtk_file_filter_get_needed (impl->current_filter);
9723 filter_info.display_name = gtk_recent_info_get_display_name (recent_info);
9724 filter_info.mime_type = gtk_recent_info_get_mime_type (recent_info);
9726 if (needed & GTK_FILE_FILTER_FILENAME)
9728 filter_info.filename = g_file_get_path (file);
9729 if (filter_info.filename)
9730 filter_info.contains |= GTK_FILE_FILTER_FILENAME;
9733 filter_info.filename = NULL;
9735 if (needed & GTK_FILE_FILTER_URI)
9737 filter_info.uri = g_file_get_uri (file);
9738 if (filter_info.uri)
9739 filter_info.contains |= GTK_FILE_FILTER_URI;
9742 filter_info.uri = NULL;
9744 result = gtk_file_filter_filter (impl->current_filter, &filter_info);
9746 if (filter_info.filename)
9747 g_free ((gchar *) filter_info.filename);
9748 if (filter_info.uri)
9749 g_free ((gchar *) filter_info.uri);
9754 /* Visibility function for the recent filter model */
9756 recent_model_visible_func (GtkTreeModel *model,
9760 GtkFileChooserDefault *impl = user_data;
9762 GtkRecentInfo *recent_info;
9765 if (!impl->current_filter)
9768 gtk_tree_model_get (model, iter,
9769 RECENT_MODEL_COL_INFO, &recent_info,
9770 RECENT_MODEL_COL_FILE, &file,
9771 RECENT_MODEL_COL_IS_FOLDER, &is_folder,
9780 return !recent_get_is_filtered (impl, file, recent_info);
9784 recent_setup_model (GtkFileChooserDefault *impl)
9786 g_assert (impl->recent_model == NULL);
9787 g_assert (impl->recent_model_filter == NULL);
9788 g_assert (impl->recent_model_sort == NULL);
9790 /* We store these columns in the search model:
9792 * RECENT_MODEL_COL_FILE - a pointer to GFile for the hit's URI,
9793 * stored as a pointer and not as a G_TYPE_FILE;
9794 * RECENT_MODEL_COL_DISPLAY_NAME - a string with the display name,
9795 * stored as a pointer and not as a G_TYPE_STRING;
9796 * RECENT_MODEL_COL_INFO - GtkRecentInfo, stored as a pointer and not
9797 * as a GTK_TYPE_RECENT_INFO;
9798 * RECENT_MODEL_COL_IS_FOLDER - boolean flag;
9799 * RECENT_MODEL_COL_CANCELLABLE - GCancellable, stored as a pointer
9800 * and not as a G_TYPE_CANCELLABLE;
9802 * Keep this in sync with the enumeration defined near the beginning of
9805 impl->recent_model = gtk_list_store_new (RECENT_MODEL_COL_NUM_COLUMNS,
9812 impl->recent_model_filter =
9813 GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (GTK_TREE_MODEL (impl->recent_model), NULL));
9814 gtk_tree_model_filter_set_visible_func (impl->recent_model_filter,
9815 recent_model_visible_func,
9819 /* this is the model that will actually be added to
9820 * the browse_files_tree_view widget; remember: we are
9821 * stuffing the real model into a filter model and then
9822 * into a sort model; this means we'll have to translate
9823 * the child iterator *twice* to get from a path or an
9824 * iterator coming from the tree view widget to the
9825 * real data inside the model.
9827 impl->recent_model_sort =
9828 GTK_TREE_MODEL_SORT (recent_model_sort_new (impl, GTK_TREE_MODEL (impl->recent_model_filter)));
9829 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model_sort),
9830 RECENT_MODEL_COL_FILE,
9831 recent_column_path_sort_func,
9833 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model_sort),
9834 RECENT_MODEL_COL_INFO,
9835 recent_column_mtime_sort_func,
9837 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->recent_model_sort),
9838 RECENT_MODEL_COL_INFO,
9839 GTK_SORT_DESCENDING);
9844 GtkFileChooserDefault *impl;
9847 gint n_loaded_items;
9848 guint needs_sorting : 1;
9852 recent_idle_cleanup (gpointer data)
9854 RecentLoadData *load_data = data;
9855 GtkFileChooserDefault *impl = load_data->impl;
9857 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
9858 GTK_TREE_MODEL (impl->recent_model_sort));
9860 set_busy_cursor (impl, FALSE);
9862 impl->load_recent_id = 0;
9864 if (load_data->items)
9866 g_list_foreach (load_data->items, (GFunc) gtk_recent_info_unref, NULL);
9867 g_list_free (load_data->items);
9873 struct RecentItemInsertRequest
9875 GtkFileChooserDefault *impl;
9877 GtkTreeRowReference *row_ref;
9881 recent_item_get_info_cb (GCancellable *cancellable,
9883 const GError *error,
9886 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
9889 GCancellable *model_cancellable;
9890 gboolean is_folder = FALSE;
9891 struct RecentItemInsertRequest *request = data;
9893 if (!request->impl->recent_model)
9896 path = gtk_tree_row_reference_get_path (request->row_ref);
9900 gtk_tree_model_get_iter (GTK_TREE_MODEL (request->impl->recent_model),
9902 gtk_tree_path_free (path);
9904 gtk_tree_model_get (GTK_TREE_MODEL (request->impl->recent_model), &iter,
9905 RECENT_MODEL_COL_CANCELLABLE, &model_cancellable,
9907 if (cancellable != model_cancellable)
9910 gtk_list_store_set (request->impl->recent_model, &iter,
9911 RECENT_MODEL_COL_CANCELLABLE, NULL,
9919 gtk_list_store_remove (request->impl->recent_model, &iter);
9923 is_folder = _gtk_file_info_consider_as_directory (info);
9925 gtk_list_store_set (request->impl->recent_model, &iter,
9926 RECENT_MODEL_COL_IS_FOLDER, is_folder,
9930 g_object_unref (request->impl);
9931 g_object_unref (request->file);
9932 gtk_tree_row_reference_free (request->row_ref);
9935 g_object_unref (cancellable);
9939 recent_sort_mru (gconstpointer a,
9942 GtkRecentInfo *info_a = (GtkRecentInfo *) a;
9943 GtkRecentInfo *info_b = (GtkRecentInfo *) b;
9945 return (gtk_recent_info_get_modified (info_b) - gtk_recent_info_get_modified (info_a));
9949 get_recent_files_limit (GtkWidget *widget)
9951 GtkSettings *settings;
9954 if (gtk_widget_has_screen (widget))
9955 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (widget));
9957 settings = gtk_settings_get_default ();
9959 g_object_get (G_OBJECT (settings), "gtk-recent-files-limit", &limit, NULL);
9965 recent_idle_load (gpointer data)
9967 RecentLoadData *load_data = data;
9968 GtkFileChooserDefault *impl = load_data->impl;
9971 GtkRecentInfo *info;
9972 const gchar *uri, *display_name;
9974 GCancellable *cancellable;
9975 struct RecentItemInsertRequest *request;
9977 if (!impl->recent_manager)
9980 /* first iteration: load all the items */
9981 if (!load_data->items)
9983 load_data->items = gtk_recent_manager_get_items (impl->recent_manager);
9984 if (!load_data->items)
9987 load_data->needs_sorting = TRUE;
9992 /* second iteration: preliminary MRU sorting and clamping */
9993 if (load_data->needs_sorting)
9997 load_data->items = g_list_sort (load_data->items, recent_sort_mru);
9998 load_data->n_items = g_list_length (load_data->items);
10000 limit = get_recent_files_limit (GTK_WIDGET (impl));
10002 if (limit != -1 && (load_data->n_items > limit))
10006 clamp = g_list_nth (load_data->items, limit - 1);
10007 if (G_LIKELY (clamp))
10010 clamp->next = NULL;
10012 g_list_foreach (l, (GFunc) gtk_recent_info_unref, NULL);
10015 load_data->n_items = limit;
10019 load_data->n_loaded_items = 0;
10020 load_data->needs_sorting = FALSE;
10025 info = g_list_nth_data (load_data->items, load_data->n_loaded_items);
10026 g_assert (info != NULL);
10028 uri = gtk_recent_info_get_uri (info);
10029 display_name = gtk_recent_info_get_display_name (info);
10030 file = g_file_new_for_uri (uri);
10032 gtk_list_store_append (impl->recent_model, &iter);
10033 p = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->recent_model), &iter);
10035 request = g_new0 (struct RecentItemInsertRequest, 1);
10036 request->impl = g_object_ref (impl);
10037 request->file = g_object_ref (file);
10038 request->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->recent_model), p);
10039 gtk_tree_path_free (p);
10041 cancellable = _gtk_file_system_get_info (impl->file_system, file,
10043 recent_item_get_info_cb,
10046 gtk_list_store_set (impl->recent_model, &iter,
10047 RECENT_MODEL_COL_FILE, file,
10048 RECENT_MODEL_COL_DISPLAY_NAME, g_strdup (display_name),
10049 RECENT_MODEL_COL_INFO, gtk_recent_info_ref (info),
10050 RECENT_MODEL_COL_CANCELLABLE, cancellable,
10053 load_data->n_loaded_items += 1;
10055 /* finished loading items */
10056 if (load_data->n_loaded_items == load_data->n_items)
10058 g_list_foreach (load_data->items, (GFunc) gtk_recent_info_unref, NULL);
10059 g_list_free (load_data->items);
10060 load_data->items = NULL;
10069 recent_start_loading (GtkFileChooserDefault *impl)
10071 RecentLoadData *load_data;
10073 recent_stop_loading (impl);
10074 recent_clear_model (impl, TRUE);
10075 recent_setup_model (impl);
10076 set_busy_cursor (impl, TRUE);
10078 g_assert (impl->load_recent_id == 0);
10080 load_data = g_new (RecentLoadData, 1);
10081 load_data->impl = impl;
10082 load_data->items = NULL;
10083 load_data->n_items = 0;
10084 load_data->n_loaded_items = 0;
10085 load_data->needs_sorting = TRUE;
10087 /* begin lazy loading the recent files into the model */
10088 impl->load_recent_id = gdk_threads_add_idle_full (G_PRIORITY_HIGH_IDLE + 30,
10091 recent_idle_cleanup);
10095 recent_selected_foreach_get_file_cb (GtkTreeModel *model,
10105 gtk_tree_model_get (model, iter, RECENT_MODEL_COL_FILE, &file, -1);
10106 *list = g_slist_prepend (*list, g_object_ref (file));
10109 /* Constructs a list of the selected paths in recent files mode */
10111 recent_get_selected_files (GtkFileChooserDefault *impl)
10114 GtkTreeSelection *selection;
10118 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
10119 gtk_tree_selection_selected_foreach (selection, recent_selected_foreach_get_file_cb, &result);
10120 result = g_slist_reverse (result);
10125 /* Called from ::should_respond(). We return whether there are selected
10126 * files in the recent files list.
10129 recent_should_respond (GtkFileChooserDefault *impl)
10131 GtkTreeSelection *selection;
10133 g_assert (impl->operation_mode == OPERATION_MODE_RECENT);
10135 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
10136 return (gtk_tree_selection_count_selected_rows (selection) != 0);
10139 /* Hide the location widgets temporarily */
10141 recent_hide_entry (GtkFileChooserDefault *impl)
10146 impl->recent_hbox = gtk_hbox_new (FALSE, 12);
10149 image = gtk_image_new_from_icon_name ("document-open-recent", GTK_ICON_SIZE_BUTTON);
10150 gtk_size_group_add_widget (impl->browse_path_bar_size_group, image);
10151 gtk_box_pack_start (GTK_BOX (impl->recent_hbox), image, FALSE, FALSE, 5);
10154 label = gtk_label_new (NULL);
10155 gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("<b>Recently Used</b>"));
10156 gtk_box_pack_start (GTK_BOX (impl->recent_hbox), label, FALSE, FALSE, 0);
10158 gtk_widget_hide (impl->browse_path_bar);
10159 gtk_widget_hide (impl->browse_new_folder_button);
10161 /* Box for recent widgets */
10162 gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->recent_hbox, TRUE, TRUE, 0);
10163 gtk_widget_show_all (impl->recent_hbox);
10165 /* Hide the location widgets temporarily */
10166 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
10167 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
10169 gtk_widget_hide (impl->location_button);
10170 gtk_widget_hide (impl->location_entry_box);
10174 /* Main entry point to the recent files functions; this gets called when
10175 * the user activates the Recently Used shortcut.
10178 recent_activate (GtkFileChooserDefault *impl)
10180 OperationMode previous_mode;
10182 if (impl->operation_mode == OPERATION_MODE_RECENT)
10185 previous_mode = impl->operation_mode;
10186 impl->operation_mode = OPERATION_MODE_RECENT;
10188 stop_operation (impl, previous_mode);
10190 recent_hide_entry (impl);
10192 /* hide the file size column if it's visible */
10193 gtk_tree_view_column_set_visible (impl->list_size_column, FALSE);
10195 file_list_set_sort_column_ids (impl);
10196 recent_start_loading (impl);
10199 /* convert an iterator coming from the model bound to
10200 * browse_files_tree_view to an interator inside the
10201 * real recent_model
10204 recent_get_valid_child_iter (GtkFileChooserDefault *impl,
10205 GtkTreeIter *child_iter,
10208 GtkTreeIter middle;
10210 if (!impl->recent_model)
10213 if (!impl->recent_model_filter || !impl->recent_model_sort)
10216 /* pass 1: get the iterator in the filter model */
10217 gtk_tree_model_sort_convert_iter_to_child_iter (impl->recent_model_sort,
10220 /* pass 2: get the iterator in the real model */
10221 gtk_tree_model_filter_convert_iter_to_child_iter (impl->recent_model_filter,
10228 set_current_filter (GtkFileChooserDefault *impl,
10229 GtkFileFilter *filter)
10231 if (impl->current_filter != filter)
10235 /* NULL filters are allowed to reset to non-filtered status
10237 filter_index = g_slist_index (impl->filters, filter);
10238 if (impl->filters && filter && filter_index < 0)
10241 if (impl->current_filter)
10242 g_object_unref (impl->current_filter);
10243 impl->current_filter = filter;
10244 if (impl->current_filter)
10246 g_object_ref_sink (impl->current_filter);
10250 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
10253 if (impl->browse_files_model)
10254 install_list_model_filter (impl);
10256 if (impl->search_model_filter)
10257 gtk_tree_model_filter_refilter (impl->search_model_filter);
10259 if (impl->recent_model_filter)
10260 gtk_tree_model_filter_refilter (impl->recent_model_filter);
10262 g_object_notify (G_OBJECT (impl), "filter");
10267 filter_combo_changed (GtkComboBox *combo_box,
10268 GtkFileChooserDefault *impl)
10270 gint new_index = gtk_combo_box_get_active (combo_box);
10271 GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
10273 set_current_filter (impl, new_filter);
10277 check_preview_change (GtkFileChooserDefault *impl)
10279 GtkTreePath *cursor_path;
10281 const char *new_display_name;
10283 gtk_tree_view_get_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), &cursor_path, NULL);
10285 new_display_name = NULL;
10288 GtkTreeIter child_iter;
10290 if (impl->operation_mode == OPERATION_MODE_BROWSE)
10292 if (impl->sort_model)
10295 GFileInfo *new_info;
10297 gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, cursor_path);
10298 gtk_tree_path_free (cursor_path);
10300 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
10302 new_file = _gtk_file_system_model_get_file (impl->browse_files_model, &child_iter);
10303 new_info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
10305 new_display_name = g_file_info_get_display_name (new_info);
10308 else if (impl->operation_mode == OPERATION_MODE_SEARCH)
10312 gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->search_model_sort),
10313 &iter, cursor_path);
10314 gtk_tree_path_free (cursor_path);
10316 search_get_valid_child_iter (impl, &child_iter, &iter);
10317 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter,
10318 SEARCH_MODEL_COL_FILE, &new_file,
10319 SEARCH_MODEL_COL_DISPLAY_NAME, &new_display_name,
10322 else if (impl->operation_mode == OPERATION_MODE_RECENT)
10326 gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->recent_model_sort),
10327 &iter, cursor_path);
10328 gtk_tree_path_free (cursor_path);
10330 recent_get_valid_child_iter (impl, &child_iter, &iter);
10331 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter,
10332 RECENT_MODEL_COL_FILE, &new_file,
10333 RECENT_MODEL_COL_DISPLAY_NAME, &new_display_name,
10338 if (new_file != impl->preview_file &&
10339 !(new_file && impl->preview_file &&
10340 g_file_equal (new_file, impl->preview_file)))
10342 if (impl->preview_file)
10344 g_object_unref (impl->preview_file);
10345 g_free (impl->preview_display_name);
10350 impl->preview_file = g_object_ref (new_file);
10351 impl->preview_display_name = g_strdup (new_display_name);
10355 impl->preview_file = NULL;
10356 impl->preview_display_name = NULL;
10359 if (impl->use_preview_label && impl->preview_label)
10360 gtk_label_set_text (GTK_LABEL (impl->preview_label), impl->preview_display_name);
10362 g_signal_emit_by_name (impl, "update-preview");
10367 shortcuts_activate_volume_mount_cb (GCancellable *cancellable,
10368 GtkFileSystemVolume *volume,
10369 const GError *error,
10373 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
10374 GtkFileChooserDefault *impl = data;
10376 if (cancellable != impl->shortcuts_activate_iter_cancellable)
10379 impl->shortcuts_activate_iter_cancellable = NULL;
10381 set_busy_cursor (impl, FALSE);
10388 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED))
10392 name = _gtk_file_system_volume_get_display_name (volume);
10393 msg = g_strdup_printf (_("Could not mount %s"), name);
10395 error_message (impl, msg, error->message);
10404 file = _gtk_file_system_volume_get_root (volume);
10407 change_folder_and_display_error (impl, file, FALSE);
10408 g_object_unref (file);
10412 g_object_unref (impl);
10413 g_object_unref (cancellable);
10417 /* Activates a volume by mounting it if necessary and then switching to its
10421 shortcuts_activate_volume (GtkFileChooserDefault *impl,
10422 GtkFileSystemVolume *volume)
10426 switch (impl->operation_mode)
10428 case OPERATION_MODE_BROWSE:
10430 case OPERATION_MODE_SEARCH:
10431 search_switch_to_browse_mode (impl);
10433 case OPERATION_MODE_RECENT:
10434 recent_switch_to_browse_mode (impl);
10438 /* We ref the file chooser since volume_mount() may run a main loop, and the
10439 * user could close the file chooser window in the meantime.
10441 g_object_ref (impl);
10443 if (!_gtk_file_system_volume_is_mounted (volume))
10445 GtkMountOperation *mount_op;
10447 set_busy_cursor (impl, TRUE);
10449 mount_op = gtk_mount_operation_new (get_toplevel (GTK_WIDGET (impl)));
10450 impl->shortcuts_activate_iter_cancellable =
10451 _gtk_file_system_mount_volume (impl->file_system, volume, mount_op,
10452 shortcuts_activate_volume_mount_cb,
10453 g_object_ref (impl));
10454 g_object_unref (mount_op);
10458 file = _gtk_file_system_volume_get_root (volume);
10461 change_folder_and_display_error (impl, file, FALSE);
10462 g_object_unref (file);
10466 g_object_unref (impl);
10469 /* Opens the folder or volume at the specified iter in the shortcuts model */
10470 struct ShortcutsActivateData
10472 GtkFileChooserDefault *impl;
10477 shortcuts_activate_get_info_cb (GCancellable *cancellable,
10479 const GError *error,
10480 gpointer user_data)
10482 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
10483 struct ShortcutsActivateData *data = user_data;
10485 if (cancellable != data->impl->shortcuts_activate_iter_cancellable)
10488 data->impl->shortcuts_activate_iter_cancellable = NULL;
10493 if (!error && _gtk_file_info_consider_as_directory (info))
10494 change_folder_and_display_error (data->impl, data->file, FALSE);
10496 gtk_file_chooser_default_select_file (GTK_FILE_CHOOSER (data->impl),
10501 g_object_unref (data->impl);
10502 g_object_unref (data->file);
10505 g_object_unref (cancellable);
10509 shortcuts_activate_mount_enclosing_volume (GCancellable *cancellable,
10510 GtkFileSystemVolume *volume,
10511 const GError *error,
10512 gpointer user_data)
10514 struct ShortcutsActivateData *data = user_data;
10518 error_changing_folder_dialog (data->impl, data->file, g_error_copy (error));
10520 g_object_unref (data->impl);
10521 g_object_unref (data->file);
10527 data->impl->shortcuts_activate_iter_cancellable =
10528 _gtk_file_system_get_info (data->impl->file_system, data->file,
10530 shortcuts_activate_get_info_cb, data);
10534 shortcuts_activate_iter (GtkFileChooserDefault *impl,
10538 ShortcutType shortcut_type;
10540 if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY
10541 && !(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
10542 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
10543 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
10545 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
10546 SHORTCUTS_COL_DATA, &col_data,
10547 SHORTCUTS_COL_TYPE, &shortcut_type,
10550 if (impl->shortcuts_activate_iter_cancellable)
10552 g_cancellable_cancel (impl->shortcuts_activate_iter_cancellable);
10553 impl->shortcuts_activate_iter_cancellable = NULL;
10556 if (shortcut_type == SHORTCUT_TYPE_SEPARATOR)
10558 else if (shortcut_type == SHORTCUT_TYPE_VOLUME)
10560 GtkFileSystemVolume *volume;
10564 shortcuts_activate_volume (impl, volume);
10566 else if (shortcut_type == SHORTCUT_TYPE_FILE)
10568 struct ShortcutsActivateData *data;
10569 GtkFileSystemVolume *volume;
10571 volume = _gtk_file_system_get_volume_for_file (impl->file_system, col_data);
10573 data = g_new0 (struct ShortcutsActivateData, 1);
10574 data->impl = g_object_ref (impl);
10575 data->file = g_object_ref (col_data);
10577 if (!volume || !_gtk_file_system_volume_is_mounted (volume))
10579 GMountOperation *mount_operation;
10580 GtkWidget *toplevel;
10582 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
10584 mount_operation = gtk_mount_operation_new (GTK_WINDOW (toplevel));
10586 impl->shortcuts_activate_iter_cancellable =
10587 _gtk_file_system_mount_enclosing_volume (impl->file_system, col_data,
10589 shortcuts_activate_mount_enclosing_volume,
10594 impl->shortcuts_activate_iter_cancellable =
10595 _gtk_file_system_get_info (impl->file_system, data->file,
10597 shortcuts_activate_get_info_cb, data);
10600 else if (shortcut_type == SHORTCUT_TYPE_SEARCH)
10602 search_activate (impl);
10604 else if (shortcut_type == SHORTCUT_TYPE_RECENT)
10606 recent_activate (impl);
10610 /* Handler for GtkWidget::key-press-event on the shortcuts list */
10612 shortcuts_key_press_event_cb (GtkWidget *widget,
10613 GdkEventKey *event,
10614 GtkFileChooserDefault *impl)
10618 modifiers = gtk_accelerator_get_default_mod_mask ();
10620 if ((event->keyval == GDK_BackSpace
10621 || event->keyval == GDK_Delete
10622 || event->keyval == GDK_KP_Delete)
10623 && (event->state & modifiers) == 0)
10625 remove_selected_bookmarks (impl);
10629 if ((event->keyval == GDK_F2)
10630 && (event->state & modifiers) == 0)
10632 rename_selected_bookmark (impl);
10640 shortcuts_select_func (GtkTreeSelection *selection,
10641 GtkTreeModel *model,
10643 gboolean path_currently_selected,
10646 GtkFileChooserDefault *impl = data;
10647 GtkTreeIter filter_iter;
10648 ShortcutType shortcut_type;
10650 if (!gtk_tree_model_get_iter (impl->shortcuts_pane_filter_model, &filter_iter, path))
10651 g_assert_not_reached ();
10653 gtk_tree_model_get (impl->shortcuts_pane_filter_model, &filter_iter, SHORTCUTS_COL_TYPE, &shortcut_type, -1);
10655 return shortcut_type != SHORTCUT_TYPE_SEPARATOR;
10659 list_select_func (GtkTreeSelection *selection,
10660 GtkTreeModel *model,
10662 gboolean path_currently_selected,
10665 GtkFileChooserDefault *impl = data;
10667 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
10668 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
10670 GtkTreeIter iter, child_iter;
10672 switch (impl->operation_mode)
10674 case OPERATION_MODE_SEARCH:
10676 gboolean is_folder;
10678 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->search_model_sort), &iter, path))
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_IS_FOLDER, &is_folder,
10690 case OPERATION_MODE_RECENT:
10692 gboolean is_folder;
10694 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->recent_model_sort), &iter, path))
10697 recent_get_valid_child_iter (impl, &child_iter, &iter);
10698 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter,
10699 RECENT_MODEL_COL_IS_FOLDER, &is_folder,
10706 case OPERATION_MODE_BROWSE:
10710 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
10713 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter);
10714 info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
10715 if (info && (! _gtk_file_info_consider_as_directory (info)))
10726 list_selection_changed (GtkTreeSelection *selection,
10727 GtkFileChooserDefault *impl)
10729 /* See if we are in the new folder editable row for Save mode */
10730 if (impl->operation_mode == OPERATION_MODE_BROWSE &&
10731 impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
10734 gboolean had_selection;
10736 info = get_selected_file_info_from_file_list (impl, &had_selection);
10737 if (!had_selection)
10738 goto out; /* normal processing */
10741 return; /* We are on the editable row for New Folder */
10746 if (impl->location_entry)
10747 update_chooser_entry (impl);
10749 check_preview_change (impl);
10750 bookmarks_check_add_sensitivity (impl);
10752 g_signal_emit_by_name (impl, "selection-changed", 0);
10755 /* Callback used when a row in the file list is activated */
10757 list_row_activated (GtkTreeView *tree_view,
10759 GtkTreeViewColumn *column,
10760 GtkFileChooserDefault *impl)
10763 GtkTreeIter child_iter;
10765 switch (impl->operation_mode)
10767 case OPERATION_MODE_SEARCH:
10770 gboolean is_folder;
10772 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->search_model_sort), &iter, path))
10775 search_get_valid_child_iter (impl, &child_iter, &iter);
10776 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter,
10777 SEARCH_MODEL_COL_FILE, &file,
10778 SEARCH_MODEL_COL_IS_FOLDER, &is_folder,
10783 change_folder_and_display_error (impl, file, FALSE);
10787 g_signal_emit_by_name (impl, "file-activated");
10791 case OPERATION_MODE_RECENT:
10794 gboolean is_folder;
10796 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->recent_model_sort), &iter, path))
10799 recent_get_valid_child_iter (impl, &child_iter, &iter);
10800 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter,
10801 RECENT_MODEL_COL_FILE, &file,
10802 RECENT_MODEL_COL_IS_FOLDER, &is_folder,
10807 change_folder_and_display_error (impl, file, FALSE);
10811 g_signal_emit_by_name (impl, "file-activated");
10815 case OPERATION_MODE_BROWSE:
10819 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path))
10822 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
10823 &child_iter, &iter);
10824 info = _gtk_file_system_model_get_info (impl->browse_files_model,
10827 if (_gtk_file_info_consider_as_directory (info))
10829 GFile *file, *target_file;
10830 const gchar *target_uri;
10832 file = _gtk_file_system_model_get_file (impl->browse_files_model, &child_iter);
10833 if (g_file_info_get_file_type (info) == G_FILE_TYPE_MOUNTABLE ||
10834 g_file_info_get_file_type (info) == G_FILE_TYPE_SHORTCUT)
10836 target_uri = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
10839 target_file = g_file_new_for_uri (target_uri);
10842 g_object_unref (file);
10843 file = target_file;
10849 change_folder_and_display_error (impl, file, FALSE);
10853 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
10854 impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
10855 g_signal_emit_by_name (impl, "file-activated");
10862 path_bar_clicked (GtkPathBar *path_bar,
10865 gboolean child_is_hidden,
10866 GtkFileChooserDefault *impl)
10869 pending_select_files_add (impl, child_file);
10871 if (!change_folder_and_display_error (impl, file, FALSE))
10874 /* Say we have "/foo/bar/[.baz]" and the user clicks on "bar". We should then
10875 * show hidden files so that ".baz" appears in the file list, as it will still
10876 * be shown in the path bar: "/foo/[bar]/.baz"
10878 if (child_is_hidden)
10879 g_object_set (impl, "show-hidden", TRUE, NULL);
10883 get_list_file_info (GtkFileChooserDefault *impl,
10886 GtkTreeIter child_iter;
10888 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
10892 return _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
10896 list_icon_data_func (GtkTreeViewColumn *tree_column,
10897 GtkCellRenderer *cell,
10898 GtkTreeModel *tree_model,
10902 GtkFileChooserDefault *impl = data;
10903 GtkTreeIter child_iter;
10904 GdkPixbuf *pixbuf = NULL;
10905 gboolean sensitive = TRUE;
10907 profile_start ("start", NULL);
10909 switch (impl->operation_mode)
10911 case OPERATION_MODE_SEARCH:
10913 GtkTreeIter child_iter;
10914 gboolean is_folder;
10916 search_get_valid_child_iter (impl, &child_iter, iter);
10917 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter,
10918 SEARCH_MODEL_COL_PIXBUF, &pixbuf,
10919 SEARCH_MODEL_COL_IS_FOLDER, &is_folder,
10922 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
10923 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
10924 sensitive = is_folder;
10928 case OPERATION_MODE_RECENT:
10930 GtkTreeIter child_iter;
10931 GtkRecentInfo *info;
10932 gboolean is_folder;
10934 recent_get_valid_child_iter (impl, &child_iter, iter);
10935 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter,
10936 RECENT_MODEL_COL_INFO, &info,
10937 RECENT_MODEL_COL_IS_FOLDER, &is_folder,
10940 pixbuf = gtk_recent_info_get_icon (info, impl->icon_size);
10942 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
10943 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
10944 sensitive = is_folder;
10948 case OPERATION_MODE_BROWSE:
10953 info = get_list_file_info (impl, iter);
10955 gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
10958 file = _gtk_file_system_model_get_file (impl->browse_files_model, &child_iter);
10963 /* FIXME: NULL GError */
10964 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (impl), impl->icon_size);
10969 /* We are on the editable row */
10974 (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
10975 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
10976 sensitive = _gtk_file_info_consider_as_directory (info);
10981 g_object_set (cell,
10983 "sensitive", sensitive,
10987 g_object_unref (pixbuf);
10989 profile_end ("end", NULL);
10993 list_name_data_func (GtkTreeViewColumn *tree_column,
10994 GtkCellRenderer *cell,
10995 GtkTreeModel *tree_model,
10999 GtkFileChooserDefault *impl = data;
11001 gboolean sensitive = TRUE;
11003 if (impl->operation_mode == OPERATION_MODE_SEARCH)
11005 GtkTreeIter child_iter;
11006 gchar *display_name;
11007 gboolean is_folder;
11009 search_get_valid_child_iter (impl, &child_iter, iter);
11010 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter,
11011 SEARCH_MODEL_COL_DISPLAY_NAME, &display_name,
11012 SEARCH_MODEL_COL_IS_FOLDER, &is_folder,
11015 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
11016 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
11018 sensitive = is_folder;
11021 g_object_set (cell,
11022 "text", display_name,
11023 "sensitive", sensitive,
11024 "ellipsize", PANGO_ELLIPSIZE_END,
11030 if (impl->operation_mode == OPERATION_MODE_RECENT)
11032 GtkTreeIter child_iter;
11033 GtkRecentInfo *recent_info;
11034 gchar *display_name;
11035 gboolean is_folder;
11037 recent_get_valid_child_iter (impl, &child_iter, iter);
11038 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter,
11039 RECENT_MODEL_COL_INFO, &recent_info,
11040 RECENT_MODEL_COL_IS_FOLDER, &is_folder,
11043 display_name = gtk_recent_info_get_short_name (recent_info);
11045 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
11046 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
11048 sensitive = is_folder;
11051 g_object_set (cell,
11052 "text", display_name,
11053 "sensitive", sensitive,
11054 "ellipsize", PANGO_ELLIPSIZE_END,
11057 g_free (display_name);
11062 info = get_list_file_info (impl, iter);
11067 g_object_set (cell,
11068 "text", _("Type name of new folder"),
11070 "ellipsize", PANGO_ELLIPSIZE_NONE,
11077 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
11078 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
11080 sensitive = _gtk_file_info_consider_as_directory (info);
11083 g_object_set (cell,
11084 "text", g_file_info_get_display_name (info),
11085 "sensitive", sensitive,
11086 "ellipsize", PANGO_ELLIPSIZE_END,
11091 list_size_data_func (GtkTreeViewColumn *tree_column,
11092 GtkCellRenderer *cell,
11093 GtkTreeModel *tree_model,
11097 GtkFileChooserDefault *impl = data;
11101 gboolean sensitive = TRUE;
11103 if (impl->operation_mode == OPERATION_MODE_RECENT)
11106 if (impl->operation_mode == OPERATION_MODE_SEARCH)
11108 GtkTreeIter child_iter;
11109 gboolean is_folder = FALSE;
11111 search_get_valid_child_iter (impl, &child_iter, iter);
11112 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter,
11113 SEARCH_MODEL_COL_SIZE, &size,
11114 SEARCH_MODEL_COL_IS_FOLDER, &is_folder,
11117 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
11118 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
11119 sensitive = is_folder ? TRUE : FALSE;
11122 str = g_format_size_for_display (size);
11126 g_object_set (cell,
11128 "sensitive", sensitive,
11136 info = get_list_file_info (impl, iter);
11138 if (!info || _gtk_file_info_consider_as_directory (info))
11140 g_object_set (cell,
11142 "sensitive", sensitive,
11147 size = g_file_info_get_size (info);
11148 str = g_format_size_for_display (size);
11150 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
11151 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
11154 g_object_set (cell,
11156 "sensitive", sensitive,
11157 "alignment", PANGO_ALIGN_RIGHT,
11163 /* Tree column data callback for the file list; fetches the mtime of a file */
11165 list_mtime_data_func (GtkTreeViewColumn *tree_column,
11166 GtkCellRenderer *cell,
11167 GtkTreeModel *tree_model,
11171 GtkFileChooserDefault *impl;
11172 GTimeVal timeval = { 0, };
11174 gchar *date_str = NULL;
11175 gboolean sensitive = TRUE;
11177 const char *locale, *dot = NULL;
11178 gint64 codepage = -1;
11184 if (impl->operation_mode == OPERATION_MODE_SEARCH)
11186 GtkTreeIter child_iter;
11188 gboolean is_folder;
11190 search_get_valid_child_iter (impl, &child_iter, iter);
11191 gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter,
11192 SEARCH_MODEL_COL_MTIME, &mtime,
11193 SEARCH_MODEL_COL_IS_FOLDER, &is_folder,
11196 time_mtime = (time_t) mtime;
11198 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
11199 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
11200 sensitive = is_folder;
11202 else if (impl->operation_mode == OPERATION_MODE_RECENT)
11204 GtkTreeIter child_iter;
11205 GtkRecentInfo *info;
11206 gboolean is_folder;
11208 recent_get_valid_child_iter (impl, &child_iter, iter);
11209 gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter,
11210 RECENT_MODEL_COL_INFO, &info,
11211 RECENT_MODEL_COL_IS_FOLDER, &is_folder,
11215 time_mtime = gtk_recent_info_get_modified (info);
11219 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
11220 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
11221 sensitive = is_folder;
11227 info = get_list_file_info (impl, iter);
11230 g_object_set (cell,
11237 g_file_info_get_modification_time (info, &timeval);
11238 time_mtime = timeval.tv_sec;
11240 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
11241 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
11242 sensitive = _gtk_file_info_consider_as_directory (info);
11245 if (G_UNLIKELY (time_mtime == 0))
11246 date_str = g_strdup (_("Unknown"));
11251 struct tm tm_mtime;
11253 const gchar *format;
11254 gchar *locale_format = NULL;
11257 #ifdef HAVE_LOCALTIME_R
11258 localtime_r ((time_t *) &time_mtime, &tm_mtime);
11261 struct tm *ptm = localtime ((time_t *) &timeval.tv_sec);
11265 g_warning ("ptm != NULL failed");
11267 g_object_set (cell,
11268 "text", _("Unknown"),
11269 "sensitive", sensitive,
11274 memcpy ((void *) &tm_mtime, (void *) ptm, sizeof (struct tm));
11276 #endif /* HAVE_LOCALTIME_R */
11278 g_date_set_time_t (&mtime, time_mtime);
11279 time_now = time (NULL);
11280 g_date_set_time_t (&now, time_now);
11282 days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
11284 /* Translators: %H means "hours" and %M means "minutes" */
11285 if (days_diff == 0)
11286 format = _("%H:%M");
11287 else if (days_diff == 1)
11288 format = _("Yesterday at %H:%M");
11291 if (days_diff > 1 && days_diff < 7)
11292 format = "%A"; /* Days from last week */
11294 format = "%x"; /* Any other date */
11298 /* g_locale_from_utf8() returns a string in the system
11299 * code-page, which is not always the same as that used by the C
11300 * library. For instance when running a GTK+ program with
11301 * LANG=ko on an English version of Windows, the system
11302 * code-page is 1252, but the code-page used by the C library is
11303 * 949. (It's GTK+ itself that sets the C library locale when it
11304 * notices the LANG environment variable. See gtkmain.c The
11305 * Microsoft C library doesn't look at any locale environment
11306 * variables.) We need to pass strftime() a string in the C
11307 * library's code-page. See bug #509885.
11309 locale = setlocale (LC_ALL, NULL);
11310 if (locale != NULL)
11311 dot = strchr (locale, '.');
11314 codepage = g_ascii_strtoll (dot+1, NULL, 10);
11316 /* All codepages should fit in 16 bits AFAIK */
11317 if (codepage > 0 && codepage < 65536)
11319 sprintf (charset, "CP%u", (guint) codepage);
11320 locale_format = g_convert (format, -1, charset, "UTF-8", NULL, NULL, NULL);
11324 locale_format = g_locale_from_utf8 (format, -1, NULL, NULL, NULL);
11326 if (locale_format != NULL &&
11327 strftime (buf, sizeof (buf), locale_format, &tm_mtime) != 0)
11330 /* As above but in opposite direction... */
11331 if (codepage > 0 && codepage < 65536)
11332 date_str = g_convert (buf, -1, "UTF-8", charset, NULL, NULL, NULL);
11334 date_str = g_locale_to_utf8 (buf, -1, NULL, NULL, NULL);
11338 if (date_str == NULL)
11339 date_str = g_strdup (_("Unknown"));
11341 g_free (locale_format);
11344 g_object_set (cell,
11346 "sensitive", sensitive,
11352 _gtk_file_chooser_default_new (void)
11354 return g_object_new (GTK_TYPE_FILE_CHOOSER_DEFAULT, NULL);
11358 location_set_user_text (GtkFileChooserDefault *impl,
11361 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), path);
11362 gtk_editable_set_position (GTK_EDITABLE (impl->location_entry), -1);
11366 location_popup_handler (GtkFileChooserDefault *impl,
11369 if (impl->operation_mode != OPERATION_MODE_BROWSE)
11371 GtkWidget *widget_to_focus;
11373 /* This will give us the location widgets back */
11374 switch (impl->operation_mode)
11376 case OPERATION_MODE_SEARCH:
11377 search_switch_to_browse_mode (impl);
11379 case OPERATION_MODE_RECENT:
11380 recent_switch_to_browse_mode (impl);
11382 case OPERATION_MODE_BROWSE:
11383 g_assert_not_reached ();
11387 if (impl->current_folder)
11388 change_folder_and_display_error (impl, impl->current_folder, FALSE);
11390 if (impl->location_mode == LOCATION_MODE_PATH_BAR)
11391 widget_to_focus = impl->browse_files_tree_view;
11393 widget_to_focus = impl->location_entry;
11395 gtk_widget_grab_focus (widget_to_focus);
11399 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
11400 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
11402 LocationMode new_mode;
11406 /* since the user typed something, we unconditionally want to turn on the entry */
11407 new_mode = LOCATION_MODE_FILENAME_ENTRY;
11409 else if (impl->location_mode == LOCATION_MODE_PATH_BAR)
11410 new_mode = LOCATION_MODE_FILENAME_ENTRY;
11411 else if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
11412 new_mode = LOCATION_MODE_PATH_BAR;
11415 g_assert_not_reached ();
11419 location_mode_set (impl, new_mode, TRUE);
11420 if (new_mode == LOCATION_MODE_FILENAME_ENTRY)
11423 location_set_user_text (impl, path);
11426 location_entry_set_initial_text (impl);
11427 gtk_editable_select_region (GTK_EDITABLE (impl->location_entry), 0, -1);
11431 else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
11432 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
11434 gtk_widget_grab_focus (impl->location_entry);
11436 location_set_user_text (impl, path);
11439 g_assert_not_reached ();
11442 /* Handler for the "up-folder" keybinding signal */
11444 up_folder_handler (GtkFileChooserDefault *impl)
11446 _gtk_path_bar_up (GTK_PATH_BAR (impl->browse_path_bar));
11449 /* Handler for the "down-folder" keybinding signal */
11451 down_folder_handler (GtkFileChooserDefault *impl)
11453 _gtk_path_bar_down (GTK_PATH_BAR (impl->browse_path_bar));
11456 /* Switches to the shortcut in the specified index */
11458 switch_to_shortcut (GtkFileChooserDefault *impl,
11463 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
11464 g_assert_not_reached ();
11466 shortcuts_activate_iter (impl, &iter);
11469 /* Handler for the "home-folder" keybinding signal */
11471 home_folder_handler (GtkFileChooserDefault *impl)
11473 if (impl->has_home)
11474 switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_HOME));
11477 /* Handler for the "desktop-folder" keybinding signal */
11479 desktop_folder_handler (GtkFileChooserDefault *impl)
11481 if (impl->has_desktop)
11482 switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_DESKTOP));
11485 /* Handler for the "search-shortcut" keybinding signal */
11487 search_shortcut_handler (GtkFileChooserDefault *impl)
11489 if (impl->has_search)
11491 switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_SEARCH));
11493 /* we want the entry widget to grab the focus the first
11494 * time, not the browse_files_tree_view widget.
11496 if (impl->search_entry)
11497 gtk_widget_grab_focus (impl->search_entry);
11501 /* Handler for the "recent-shortcut" keybinding signal */
11503 recent_shortcut_handler (GtkFileChooserDefault *impl)
11505 if (impl->has_recent)
11506 switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_RECENT));
11510 quick_bookmark_handler (GtkFileChooserDefault *impl,
11511 gint bookmark_index)
11516 if (bookmark_index < 0 || bookmark_index >= impl->num_bookmarks)
11519 bookmark_pos = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS) + bookmark_index;
11521 path = gtk_tree_path_new_from_indices (bookmark_pos, -1);
11522 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
11525 gtk_tree_path_free (path);
11527 switch_to_shortcut (impl, bookmark_pos);
11531 show_hidden_handler (GtkFileChooserDefault *impl)
11533 g_object_set (impl,
11534 "show-hidden", !impl->show_hidden,
11539 /* Drag and drop interfaces */
11542 _shortcuts_pane_model_filter_class_init (ShortcutsPaneModelFilterClass *class)
11547 _shortcuts_pane_model_filter_init (ShortcutsPaneModelFilter *model)
11549 model->impl = NULL;
11552 /* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */
11554 shortcuts_pane_model_filter_row_draggable (GtkTreeDragSource *drag_source,
11557 ShortcutsPaneModelFilter *model;
11561 model = SHORTCUTS_PANE_MODEL_FILTER (drag_source);
11563 pos = *gtk_tree_path_get_indices (path);
11564 bookmarks_pos = shortcuts_get_index (model->impl, SHORTCUTS_BOOKMARKS);
11566 return (pos >= bookmarks_pos && pos < bookmarks_pos + model->impl->num_bookmarks);
11569 /* GtkTreeDragSource::drag_data_get implementation for the shortcuts filter model */
11571 shortcuts_pane_model_filter_drag_data_get (GtkTreeDragSource *drag_source,
11573 GtkSelectionData *selection_data)
11575 ShortcutsPaneModelFilter *model;
11577 model = SHORTCUTS_PANE_MODEL_FILTER (drag_source);
11584 /* Fill the GtkTreeDragSourceIface vtable */
11586 shortcuts_pane_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface)
11588 iface->row_draggable = shortcuts_pane_model_filter_row_draggable;
11589 iface->drag_data_get = shortcuts_pane_model_filter_drag_data_get;
11593 /* Fill the GtkTreeDragDestIface vtable */
11595 shortcuts_pane_model_filter_drag_dest_iface_init (GtkTreeDragDestIface *iface)
11597 iface->drag_data_received = shortcuts_pane_model_filter_drag_data_received;
11598 iface->row_drop_possible = shortcuts_pane_model_filter_row_drop_possible;
11602 static GtkTreeModel *
11603 shortcuts_pane_model_filter_new (GtkFileChooserDefault *impl,
11604 GtkTreeModel *child_model,
11607 ShortcutsPaneModelFilter *model;
11609 model = g_object_new (SHORTCUTS_PANE_MODEL_FILTER_TYPE,
11610 "child-model", child_model,
11611 "virtual-root", root,
11614 model->impl = impl;
11616 return GTK_TREE_MODEL (model);
11622 recent_model_sort_row_draggable (GtkTreeDragSource *drag_source,
11625 RecentModelSort *model;
11626 GtkTreeIter iter, child_iter;
11627 gboolean is_folder;
11629 model = RECENT_MODEL_SORT (drag_source);
11630 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
11633 recent_get_valid_child_iter (model->impl, &child_iter, &iter);
11634 gtk_tree_model_get (GTK_TREE_MODEL (model->impl->recent_model), &child_iter,
11635 RECENT_MODEL_COL_IS_FOLDER, &is_folder,
11642 recent_model_sort_drag_data_get (GtkTreeDragSource *drag_source,
11644 GtkSelectionData *selection_data)
11646 RecentModelSort *model;
11647 GtkTreeIter iter, child_iter;
11651 model = RECENT_MODEL_SORT (drag_source);
11652 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
11655 recent_get_valid_child_iter (model->impl, &child_iter, &iter);
11656 gtk_tree_model_get (GTK_TREE_MODEL (model->impl->recent_model), &child_iter,
11657 RECENT_MODEL_COL_FILE, &file,
11659 g_assert (file != NULL);
11661 uris[0] = g_file_get_uri (file);
11664 gtk_selection_data_set_uris (selection_data, uris);
11672 recent_model_sort_drag_source_iface_init (GtkTreeDragSourceIface *iface)
11674 iface->row_draggable = recent_model_sort_row_draggable;
11675 iface->drag_data_get = recent_model_sort_drag_data_get;
11679 _recent_model_sort_class_init (RecentModelSortClass *klass)
11685 _recent_model_sort_init (RecentModelSort *model)
11687 model->impl = NULL;
11690 static GtkTreeModel *
11691 recent_model_sort_new (GtkFileChooserDefault *impl,
11692 GtkTreeModel *child_model)
11694 RecentModelSort *model;
11696 model = g_object_new (RECENT_MODEL_SORT_TYPE,
11697 "model", child_model,
11699 model->impl = impl;
11701 return GTK_TREE_MODEL (model);
11707 search_model_sort_row_draggable (GtkTreeDragSource *drag_source,
11710 SearchModelSort *model;
11711 GtkTreeIter iter, child_iter;
11712 gboolean is_folder;
11714 model = SEARCH_MODEL_SORT (drag_source);
11715 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
11718 search_get_valid_child_iter (model->impl, &child_iter, &iter);
11719 gtk_tree_model_get (GTK_TREE_MODEL (model->impl->search_model), &child_iter,
11720 SEARCH_MODEL_COL_IS_FOLDER, &is_folder,
11727 search_model_sort_drag_data_get (GtkTreeDragSource *drag_source,
11729 GtkSelectionData *selection_data)
11731 SearchModelSort *model;
11732 GtkTreeIter iter, child_iter;
11736 model = SEARCH_MODEL_SORT (drag_source);
11737 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
11740 search_get_valid_child_iter (model->impl, &child_iter, &iter);
11741 gtk_tree_model_get (GTK_TREE_MODEL (model->impl->search_model), &child_iter,
11742 RECENT_MODEL_COL_FILE, &file,
11744 g_assert (file != NULL);
11746 uris[0] = g_file_get_uri (file);
11749 gtk_selection_data_set_uris (selection_data, uris);
11757 search_model_sort_drag_source_iface_init (GtkTreeDragSourceIface *iface)
11759 iface->row_draggable = search_model_sort_row_draggable;
11760 iface->drag_data_get = search_model_sort_drag_data_get;
11764 _search_model_sort_class_init (SearchModelSortClass *klass)
11770 _search_model_sort_init (SearchModelSort *model)
11772 model->impl = NULL;
11775 static GtkTreeModel *
11776 search_model_sort_new (GtkFileChooserDefault *impl,
11777 GtkTreeModel *child_model)
11779 SearchModelSort *model;
11781 model = g_object_new (SEARCH_MODEL_SORT_TYPE,
11782 "model", child_model,
11784 model->impl = impl;
11786 return GTK_TREE_MODEL (model);