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 "gtkfilechooserdefault.h"
26 #include "gtkalignment.h"
27 #include "gtkbindings.h"
28 #include "gtkcelllayout.h"
29 #include "gtkcellrendererpixbuf.h"
30 #include "gtkcellrenderertext.h"
31 #include "gtkcheckmenuitem.h"
32 #include "gtkclipboard.h"
33 #include "gtkcomboboxtext.h"
35 #include "gtkexpander.h"
36 #include "gtkfilechooserprivate.h"
37 #include "gtkfilechooserdialog.h"
38 #include "gtkfilechooserembed.h"
39 #include "gtkfilechooserentry.h"
40 #include "gtkfilechooserutils.h"
41 #include "gtkfilechooser.h"
42 #include "gtkfilesystem.h"
43 #include "gtkfilesystemmodel.h"
46 #include "gtkhpaned.h"
47 #include "gtkiconfactory.h"
48 #include "gtkicontheme.h"
50 #include "gtkimagemenuitem.h"
52 #include "gtkmarshalers.h"
53 #include "gtkmessagedialog.h"
54 #include "gtkmountoperation.h"
55 #include "gtkpathbar.h"
56 #include "gtkprivate.h"
57 #include "gtkradiobutton.h"
58 #include "gtkrecentfilter.h"
59 #include "gtkrecentmanager.h"
60 #include "gtkscrolledwindow.h"
61 #include "gtkseparatormenuitem.h"
62 #include "gtksizegroup.h"
63 #include "gtksizerequest.h"
66 #include "gtktoolbar.h"
67 #include "gtktoolbutton.h"
68 #include "gtktooltip.h"
69 #include "gtktreednd.h"
70 #include "gtktreeprivate.h"
71 #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 #define DEFAULT_NEW_FOLDER_NAME _("Type name of new folder")
150 struct _GtkFileChooserDefaultClass
152 GtkVBoxClass parent_class;
158 LOCATION_POPUP_ON_PASTE,
164 LOCATION_TOGGLE_POPUP,
172 static guint signals[LAST_SIGNAL] = { 0 };
174 /* Column numbers for the shortcuts tree. Keep these in sync with shortcuts_model_create() */
176 SHORTCUTS_COL_PIXBUF,
180 SHORTCUTS_COL_REMOVABLE,
181 SHORTCUTS_COL_PIXBUF_VISIBLE,
182 SHORTCUTS_COL_CANCELLABLE,
183 SHORTCUTS_COL_NUM_COLUMNS
188 SHORTCUT_TYPE_VOLUME,
189 SHORTCUT_TYPE_SEPARATOR,
190 SHORTCUT_TYPE_SEARCH,
194 #define MODEL_ATTRIBUTES "standard::name,standard::type,standard::display-name," \
195 "standard::is-hidden,standard::is-backup,standard::size," \
196 "standard::content-type,time::modified"
198 /* the first 3 must be these due to settings caching sort column */
203 MODEL_COL_NAME_COLLATED,
207 MODEL_COL_MTIME_TEXT,
209 MODEL_COL_NUM_COLUMNS
212 /* This list of types is passed to _gtk_file_system_model_new*() */
213 #define MODEL_COLUMN_TYPES \
214 MODEL_COL_NUM_COLUMNS, \
215 G_TYPE_STRING, /* MODEL_COL_NAME */ \
216 G_TYPE_INT64, /* MODEL_COL_SIZE */ \
217 G_TYPE_LONG, /* MODEL_COL_MTIME */ \
218 G_TYPE_FILE, /* MODEL_COL_FILE */ \
219 G_TYPE_STRING, /* MODEL_COL_NAME_COLLATED */ \
220 G_TYPE_BOOLEAN, /* MODEL_COL_IS_FOLDER */ \
221 GDK_TYPE_PIXBUF, /* MODEL_COL_PIXBUF */ \
222 G_TYPE_STRING, /* MODEL_COL_SIZE_TEXT */ \
223 G_TYPE_STRING, /* MODEL_COL_MTIME_TEXT */ \
224 PANGO_TYPE_ELLIPSIZE_MODE /* MODEL_COL_ELLIPSIZE */
226 /* Identifiers for target types */
231 /* Interesting places in the shortcuts bar */
235 SHORTCUTS_RECENT_SEPARATOR,
240 SHORTCUTS_BOOKMARKS_SEPARATOR,
242 SHORTCUTS_CURRENT_FOLDER_SEPARATOR,
243 SHORTCUTS_CURRENT_FOLDER
246 /* Icon size for if we can't get it from the theme */
247 #define FALLBACK_ICON_SIZE 16
249 #define PREVIEW_HBOX_SPACING 12
253 #define SETTINGS_KEY_LOCATION_MODE "location-mode"
254 #define SETTINGS_KEY_SHOW_HIDDEN "show-hidden"
255 #define SETTINGS_KEY_EXPAND_FOLDERS "expand-folders"
256 #define SETTINGS_KEY_SHOW_SIZE_COLUMN "show-size-column"
257 #define SETTINGS_KEY_SORT_COLUMN "sort-column"
258 #define SETTINGS_KEY_SORT_ORDER "sort-order"
259 #define SETTINGS_KEY_WINDOW_POSITION "window-position"
260 #define SETTINGS_KEY_WINDOW_SIZE "window-size"
262 static void gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface);
263 static void gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface);
265 static GObject* gtk_file_chooser_default_constructor (GType type,
266 guint n_construct_properties,
267 GObjectConstructParam *construct_params);
268 static void gtk_file_chooser_default_finalize (GObject *object);
269 static void gtk_file_chooser_default_set_property (GObject *object,
273 static void gtk_file_chooser_default_get_property (GObject *object,
277 static void gtk_file_chooser_default_dispose (GObject *object);
278 static void gtk_file_chooser_default_show_all (GtkWidget *widget);
279 static void gtk_file_chooser_default_realize (GtkWidget *widget);
280 static void gtk_file_chooser_default_map (GtkWidget *widget);
281 static void gtk_file_chooser_default_unmap (GtkWidget *widget);
282 static void gtk_file_chooser_default_hierarchy_changed (GtkWidget *widget,
283 GtkWidget *previous_toplevel);
284 static void gtk_file_chooser_default_style_updated (GtkWidget *widget);
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 path_bar_clicked (GtkPathBar *path_bar,
388 gboolean child_is_hidden,
389 GtkFileChooserDefault *impl);
391 static void add_bookmark_button_clicked_cb (GtkButton *button,
392 GtkFileChooserDefault *impl);
393 static void remove_bookmark_button_clicked_cb (GtkButton *button,
394 GtkFileChooserDefault *impl);
395 static void save_folder_combo_changed_cb (GtkComboBox *combo,
396 GtkFileChooserDefault *impl);
398 static void update_cell_renderer_attributes (GtkFileChooserDefault *impl);
400 static void load_remove_timer (GtkFileChooserDefault *impl);
401 static void browse_files_center_selected_row (GtkFileChooserDefault *impl);
403 static void location_button_toggled_cb (GtkToggleButton *toggle,
404 GtkFileChooserDefault *impl);
405 static void location_switch_to_path_bar (GtkFileChooserDefault *impl);
407 static void stop_loading_and_clear_list_model (GtkFileChooserDefault *impl,
408 gboolean remove_from_treeview);
409 static void search_stop_searching (GtkFileChooserDefault *impl,
410 gboolean remove_query);
411 static void search_clear_model (GtkFileChooserDefault *impl,
412 gboolean remove_from_treeview);
413 static gboolean search_should_respond (GtkFileChooserDefault *impl);
414 static void search_switch_to_browse_mode (GtkFileChooserDefault *impl);
415 static GSList *search_get_selected_files (GtkFileChooserDefault *impl);
416 static void search_entry_activate_cb (GtkEntry *entry,
418 static void settings_load (GtkFileChooserDefault *impl);
420 static void recent_stop_loading (GtkFileChooserDefault *impl);
421 static void recent_clear_model (GtkFileChooserDefault *impl,
422 gboolean remove_from_treeview);
423 static gboolean recent_should_respond (GtkFileChooserDefault *impl);
424 static void recent_switch_to_browse_mode (GtkFileChooserDefault *impl);
425 static GSList * recent_get_selected_files (GtkFileChooserDefault *impl);
426 static void set_file_system_backend (GtkFileChooserDefault *impl);
427 static void unset_file_system_backend (GtkFileChooserDefault *impl);
433 /* Drag and drop interface declarations */
436 GtkTreeModelFilter parent;
438 GtkFileChooserDefault *impl;
439 } ShortcutsPaneModelFilter;
442 GtkTreeModelFilterClass parent_class;
443 } ShortcutsPaneModelFilterClass;
445 #define SHORTCUTS_PANE_MODEL_FILTER_TYPE (_shortcuts_pane_model_filter_get_type ())
446 #define SHORTCUTS_PANE_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_PANE_MODEL_FILTER_TYPE, ShortcutsPaneModelFilter))
448 static void shortcuts_pane_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface);
450 G_DEFINE_TYPE_WITH_CODE (ShortcutsPaneModelFilter,
451 _shortcuts_pane_model_filter,
452 GTK_TYPE_TREE_MODEL_FILTER,
453 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
454 shortcuts_pane_model_filter_drag_source_iface_init))
456 static GtkTreeModel *shortcuts_pane_model_filter_new (GtkFileChooserDefault *impl,
457 GtkTreeModel *child_model,
462 G_DEFINE_TYPE_WITH_CODE (GtkFileChooserDefault, _gtk_file_chooser_default, GTK_TYPE_VBOX,
463 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER,
464 gtk_file_chooser_default_iface_init)
465 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER_EMBED,
466 gtk_file_chooser_embed_default_iface_init));
470 add_normal_and_shifted_binding (GtkBindingSet *binding_set,
472 GdkModifierType modifiers,
473 const gchar *signal_name)
475 gtk_binding_entry_add_signal (binding_set,
479 gtk_binding_entry_add_signal (binding_set,
480 keyval, modifiers | GDK_SHIFT_MASK,
485 _gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
487 static const guint quick_bookmark_keyvals[10] = {
488 GDK_KEY_1, GDK_KEY_2, GDK_KEY_3, GDK_KEY_4, GDK_KEY_5, GDK_KEY_6, GDK_KEY_7, GDK_KEY_8, GDK_KEY_9, GDK_KEY_0
490 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
491 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
492 GtkBindingSet *binding_set;
495 gobject_class->finalize = gtk_file_chooser_default_finalize;
496 gobject_class->constructor = gtk_file_chooser_default_constructor;
497 gobject_class->set_property = gtk_file_chooser_default_set_property;
498 gobject_class->get_property = gtk_file_chooser_default_get_property;
499 gobject_class->dispose = gtk_file_chooser_default_dispose;
501 widget_class->show_all = gtk_file_chooser_default_show_all;
502 widget_class->realize = gtk_file_chooser_default_realize;
503 widget_class->map = gtk_file_chooser_default_map;
504 widget_class->unmap = gtk_file_chooser_default_unmap;
505 widget_class->hierarchy_changed = gtk_file_chooser_default_hierarchy_changed;
506 widget_class->style_updated = gtk_file_chooser_default_style_updated;
507 widget_class->screen_changed = gtk_file_chooser_default_screen_changed;
508 widget_class->size_allocate = gtk_file_chooser_default_size_allocate;
510 signals[LOCATION_POPUP] =
511 g_signal_new_class_handler (I_("location-popup"),
512 G_OBJECT_CLASS_TYPE (class),
513 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
514 G_CALLBACK (location_popup_handler),
516 _gtk_marshal_VOID__STRING,
517 G_TYPE_NONE, 1, G_TYPE_STRING);
519 signals[LOCATION_POPUP_ON_PASTE] =
520 g_signal_new_class_handler (I_("location-popup-on-paste"),
521 G_OBJECT_CLASS_TYPE (class),
522 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
523 G_CALLBACK (location_popup_on_paste_handler),
525 _gtk_marshal_VOID__VOID,
528 signals[LOCATION_TOGGLE_POPUP] =
529 g_signal_new_class_handler (I_("location-toggle-popup"),
530 G_OBJECT_CLASS_TYPE (class),
531 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
532 G_CALLBACK (location_toggle_popup_handler),
534 _gtk_marshal_VOID__VOID,
538 g_signal_new_class_handler (I_("up-folder"),
539 G_OBJECT_CLASS_TYPE (class),
540 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
541 G_CALLBACK (up_folder_handler),
543 _gtk_marshal_VOID__VOID,
546 signals[DOWN_FOLDER] =
547 g_signal_new_class_handler (I_("down-folder"),
548 G_OBJECT_CLASS_TYPE (class),
549 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
550 G_CALLBACK (down_folder_handler),
552 _gtk_marshal_VOID__VOID,
555 signals[HOME_FOLDER] =
556 g_signal_new_class_handler (I_("home-folder"),
557 G_OBJECT_CLASS_TYPE (class),
558 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
559 G_CALLBACK (home_folder_handler),
561 _gtk_marshal_VOID__VOID,
564 signals[DESKTOP_FOLDER] =
565 g_signal_new_class_handler (I_("desktop-folder"),
566 G_OBJECT_CLASS_TYPE (class),
567 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
568 G_CALLBACK (desktop_folder_handler),
570 _gtk_marshal_VOID__VOID,
573 signals[QUICK_BOOKMARK] =
574 g_signal_new_class_handler (I_("quick-bookmark"),
575 G_OBJECT_CLASS_TYPE (class),
576 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
577 G_CALLBACK (quick_bookmark_handler),
579 _gtk_marshal_VOID__INT,
580 G_TYPE_NONE, 1, G_TYPE_INT);
582 signals[SHOW_HIDDEN] =
583 g_signal_new_class_handler (I_("show-hidden"),
584 G_OBJECT_CLASS_TYPE (class),
585 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
586 G_CALLBACK (show_hidden_handler),
588 _gtk_marshal_VOID__VOID,
591 signals[SEARCH_SHORTCUT] =
592 g_signal_new_class_handler (I_("search-shortcut"),
593 G_OBJECT_CLASS_TYPE (class),
594 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
595 G_CALLBACK (search_shortcut_handler),
597 _gtk_marshal_VOID__VOID,
600 signals[RECENT_SHORTCUT] =
601 g_signal_new_class_handler (I_("recent-shortcut"),
602 G_OBJECT_CLASS_TYPE (class),
603 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
604 G_CALLBACK (recent_shortcut_handler),
606 _gtk_marshal_VOID__VOID,
609 binding_set = gtk_binding_set_by_class (class);
611 gtk_binding_entry_add_signal (binding_set,
612 GDK_KEY_l, GDK_CONTROL_MASK,
613 "location-toggle-popup",
616 gtk_binding_entry_add_signal (binding_set,
619 1, G_TYPE_STRING, "/");
620 gtk_binding_entry_add_signal (binding_set,
621 GDK_KEY_KP_Divide, 0,
623 1, G_TYPE_STRING, "/");
626 gtk_binding_entry_add_signal (binding_set,
627 GDK_KEY_asciitilde, 0,
629 1, G_TYPE_STRING, "~");
632 gtk_binding_entry_add_signal (binding_set,
633 GDK_KEY_v, GDK_CONTROL_MASK,
634 "location-popup-on-paste",
636 gtk_binding_entry_add_signal (binding_set,
637 GDK_KEY_BackSpace, 0,
641 add_normal_and_shifted_binding (binding_set,
642 GDK_KEY_Up, GDK_MOD1_MASK,
645 add_normal_and_shifted_binding (binding_set,
646 GDK_KEY_KP_Up, GDK_MOD1_MASK,
649 add_normal_and_shifted_binding (binding_set,
650 GDK_KEY_Down, GDK_MOD1_MASK,
652 add_normal_and_shifted_binding (binding_set,
653 GDK_KEY_KP_Down, GDK_MOD1_MASK,
656 gtk_binding_entry_add_signal (binding_set,
657 GDK_KEY_Home, GDK_MOD1_MASK,
660 gtk_binding_entry_add_signal (binding_set,
661 GDK_KEY_KP_Home, GDK_MOD1_MASK,
664 gtk_binding_entry_add_signal (binding_set,
665 GDK_KEY_d, GDK_MOD1_MASK,
668 gtk_binding_entry_add_signal (binding_set,
669 GDK_KEY_h, GDK_CONTROL_MASK,
672 gtk_binding_entry_add_signal (binding_set,
673 GDK_KEY_s, GDK_MOD1_MASK,
676 gtk_binding_entry_add_signal (binding_set,
677 GDK_KEY_r, GDK_MOD1_MASK,
681 for (i = 0; i < 10; i++)
682 gtk_binding_entry_add_signal (binding_set,
683 quick_bookmark_keyvals[i], GDK_MOD1_MASK,
687 _gtk_file_chooser_install_properties (gobject_class);
691 gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
693 iface->select_file = gtk_file_chooser_default_select_file;
694 iface->unselect_file = gtk_file_chooser_default_unselect_file;
695 iface->select_all = gtk_file_chooser_default_select_all;
696 iface->unselect_all = gtk_file_chooser_default_unselect_all;
697 iface->get_files = gtk_file_chooser_default_get_files;
698 iface->get_preview_file = gtk_file_chooser_default_get_preview_file;
699 iface->get_file_system = gtk_file_chooser_default_get_file_system;
700 iface->set_current_folder = gtk_file_chooser_default_set_current_folder;
701 iface->get_current_folder = gtk_file_chooser_default_get_current_folder;
702 iface->set_current_name = gtk_file_chooser_default_set_current_name;
703 iface->add_filter = gtk_file_chooser_default_add_filter;
704 iface->remove_filter = gtk_file_chooser_default_remove_filter;
705 iface->list_filters = gtk_file_chooser_default_list_filters;
706 iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder;
707 iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder;
708 iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders;
712 gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
714 iface->get_default_size = gtk_file_chooser_default_get_default_size;
715 iface->should_respond = gtk_file_chooser_default_should_respond;
716 iface->initial_focus = gtk_file_chooser_default_initial_focus;
720 _gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
722 profile_start ("start", NULL);
723 #ifdef PROFILE_FILE_CHOOSER
724 access ("MARK: *** CREATE FILE CHOOSER", F_OK);
726 impl->local_only = TRUE;
727 impl->preview_widget_active = TRUE;
728 impl->use_preview_label = TRUE;
729 impl->select_multiple = FALSE;
730 impl->show_hidden = FALSE;
731 impl->show_size_column = TRUE;
732 impl->icon_size = FALLBACK_ICON_SIZE;
733 impl->load_state = LOAD_EMPTY;
734 impl->reload_state = RELOAD_EMPTY;
735 impl->pending_select_files = NULL;
736 impl->location_mode = LOCATION_MODE_PATH_BAR;
737 impl->operation_mode = OPERATION_MODE_BROWSE;
738 impl->sort_column = MODEL_COL_NAME;
739 impl->sort_order = GTK_SORT_ASCENDING;
740 impl->recent_manager = gtk_recent_manager_get_default ();
741 impl->create_folders = TRUE;
743 gtk_box_set_spacing (GTK_BOX (impl), 12);
745 set_file_system_backend (impl);
747 profile_end ("end", NULL);
750 /* Frees the data columns for the specified iter in the shortcuts model*/
752 shortcuts_free_row_data (GtkFileChooserDefault *impl,
756 ShortcutType shortcut_type;
757 GCancellable *cancellable;
759 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
760 SHORTCUTS_COL_DATA, &col_data,
761 SHORTCUTS_COL_TYPE, &shortcut_type,
762 SHORTCUTS_COL_CANCELLABLE, &cancellable,
767 g_cancellable_cancel (cancellable);
768 g_object_unref (cancellable);
771 if (!(shortcut_type == SHORTCUT_TYPE_FILE ||
772 shortcut_type == SHORTCUT_TYPE_VOLUME) ||
776 if (shortcut_type == SHORTCUT_TYPE_VOLUME)
778 GtkFileSystemVolume *volume;
781 _gtk_file_system_volume_unref (volume);
783 if (shortcut_type == SHORTCUT_TYPE_FILE)
788 g_object_unref (file);
792 /* Frees all the data columns in the shortcuts model */
794 shortcuts_free (GtkFileChooserDefault *impl)
798 if (!impl->shortcuts_model)
801 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
804 shortcuts_free_row_data (impl, &iter);
806 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter));
808 g_object_unref (impl->shortcuts_model);
809 impl->shortcuts_model = NULL;
813 pending_select_files_free (GtkFileChooserDefault *impl)
815 g_slist_foreach (impl->pending_select_files, (GFunc) g_object_unref, NULL);
816 g_slist_free (impl->pending_select_files);
817 impl->pending_select_files = NULL;
821 pending_select_files_add (GtkFileChooserDefault *impl,
824 impl->pending_select_files =
825 g_slist_prepend (impl->pending_select_files, g_object_ref (file));
829 gtk_file_chooser_default_finalize (GObject *object)
831 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
834 unset_file_system_backend (impl);
836 if (impl->shortcuts_pane_filter_model)
837 g_object_unref (impl->shortcuts_pane_filter_model);
839 if (impl->shortcuts_combo_filter_model)
840 g_object_unref (impl->shortcuts_combo_filter_model);
842 shortcuts_free (impl);
844 g_free (impl->browse_files_last_selected_name);
846 for (l = impl->filters; l; l = l->next)
848 GtkFileFilter *filter;
850 filter = GTK_FILE_FILTER (l->data);
851 g_object_unref (filter);
853 g_slist_free (impl->filters);
855 if (impl->current_filter)
856 g_object_unref (impl->current_filter);
858 if (impl->current_volume_file)
859 g_object_unref (impl->current_volume_file);
861 if (impl->current_folder)
862 g_object_unref (impl->current_folder);
864 if (impl->preview_file)
865 g_object_unref (impl->preview_file);
867 if (impl->browse_path_bar_size_group)
868 g_object_unref (impl->browse_path_bar_size_group);
870 /* Free all the Models we have */
871 stop_loading_and_clear_list_model (impl, FALSE);
872 search_clear_model (impl, FALSE);
873 recent_clear_model (impl, FALSE);
875 /* stopping the load above should have cleared this */
876 g_assert (impl->load_timeout_id == 0);
878 g_free (impl->preview_display_name);
880 g_free (impl->edited_new_text);
882 G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->finalize (object);
885 /* Shows an error dialog set as transient for the specified window */
887 error_message_with_parent (GtkWindow *parent,
893 dialog = gtk_message_dialog_new (parent,
894 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
899 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
902 if (parent && gtk_window_has_group (parent))
903 gtk_window_group_add_window (gtk_window_get_group (parent),
904 GTK_WINDOW (dialog));
906 gtk_dialog_run (GTK_DIALOG (dialog));
907 gtk_widget_destroy (dialog);
910 /* Returns a toplevel GtkWindow, or NULL if none */
912 get_toplevel (GtkWidget *widget)
916 toplevel = gtk_widget_get_toplevel (widget);
917 if (!gtk_widget_is_toplevel (toplevel))
920 return GTK_WINDOW (toplevel);
923 /* Shows an error dialog for the file chooser */
925 error_message (GtkFileChooserDefault *impl,
929 error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail);
932 /* Shows a simple error dialog relative to a path. Frees the GError as well. */
934 error_dialog (GtkFileChooserDefault *impl,
945 uri = g_file_get_uri (file);
946 text = g_strdup_printf (msg, uri);
947 error_message (impl, text, error->message);
950 g_error_free (error);
954 /* Displays an error message about not being able to get information for a file.
955 * Frees the GError as well.
958 error_getting_info_dialog (GtkFileChooserDefault *impl,
963 _("Could not retrieve information about the file"),
967 /* Shows an error dialog about not being able to add a bookmark */
969 error_adding_bookmark_dialog (GtkFileChooserDefault *impl,
974 _("Could not add a bookmark"),
978 /* Shows an error dialog about not being able to remove a bookmark */
980 error_removing_bookmark_dialog (GtkFileChooserDefault *impl,
985 _("Could not remove bookmark"),
989 /* Shows an error dialog about not being able to create a folder */
991 error_creating_folder_dialog (GtkFileChooserDefault *impl,
996 _("The folder could not be created"),
1000 /* Shows an error about not being able to create a folder because a file with
1001 * the same name is already there.
1004 error_creating_folder_over_existing_file_dialog (GtkFileChooserDefault *impl,
1009 _("The folder could not be created, as a file with the same "
1010 "name already exists. Try using a different name for the "
1011 "folder, or rename the file first."),
1016 error_with_file_under_nonfolder (GtkFileChooserDefault *impl,
1022 g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY,
1023 _("You need to choose a valid filename."));
1026 _("Cannot create a file under %s as it is not a folder"),
1027 parent_file, error);
1030 /* Shows an error about not being able to select a folder because a file with
1031 * the same name is already there.
1034 error_selecting_folder_over_existing_file_dialog (GtkFileChooserDefault *impl,
1038 _("You may only select folders. The item that you selected is not a folder; "
1039 "try using a different item."),
1043 /* Shows an error dialog about not being able to create a filename */
1045 error_building_filename_dialog (GtkFileChooserDefault *impl,
1048 error_dialog (impl, _("Invalid file name"),
1052 /* Shows an error dialog when we cannot switch to a folder */
1054 error_changing_folder_dialog (GtkFileChooserDefault *impl,
1058 error_dialog (impl, _("The folder contents could not be displayed"),
1062 /* Changes folders, displaying an error dialog if this fails */
1064 change_folder_and_display_error (GtkFileChooserDefault *impl,
1066 gboolean clear_entry)
1071 g_return_val_if_fail (G_IS_FILE (file), FALSE);
1073 /* We copy the path because of this case:
1075 * list_row_activated()
1076 * fetches path from model; path belongs to the model (*)
1077 * calls change_folder_and_display_error()
1078 * calls gtk_file_chooser_set_current_folder_file()
1079 * changing folders fails, sets model to NULL, thus freeing the path in (*)
1083 result = gtk_file_chooser_default_update_current_folder (GTK_FILE_CHOOSER (impl), file, TRUE, clear_entry, &error);
1086 error_changing_folder_dialog (impl, file, error);
1092 emit_default_size_changed (GtkFileChooserDefault *impl)
1094 profile_msg (" emit default-size-changed start", NULL);
1095 g_signal_emit_by_name (impl, "default-size-changed");
1096 profile_msg (" emit default-size-changed end", NULL);
1100 update_preview_widget_visibility (GtkFileChooserDefault *impl)
1102 if (impl->use_preview_label)
1104 if (!impl->preview_label)
1106 impl->preview_label = gtk_label_new (impl->preview_display_name);
1107 gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_label, FALSE, FALSE, 0);
1108 gtk_box_reorder_child (GTK_BOX (impl->preview_box), impl->preview_label, 0);
1109 gtk_label_set_ellipsize (GTK_LABEL (impl->preview_label), PANGO_ELLIPSIZE_MIDDLE);
1110 gtk_widget_show (impl->preview_label);
1115 if (impl->preview_label)
1117 gtk_widget_destroy (impl->preview_label);
1118 impl->preview_label = NULL;
1122 if (impl->preview_widget_active && impl->preview_widget)
1123 gtk_widget_show (impl->preview_box);
1125 gtk_widget_hide (impl->preview_box);
1127 if (!gtk_widget_get_mapped (GTK_WIDGET (impl)))
1128 emit_default_size_changed (impl);
1132 set_preview_widget (GtkFileChooserDefault *impl,
1133 GtkWidget *preview_widget)
1135 if (preview_widget == impl->preview_widget)
1138 if (impl->preview_widget)
1139 gtk_container_remove (GTK_CONTAINER (impl->preview_box),
1140 impl->preview_widget);
1142 impl->preview_widget = preview_widget;
1143 if (impl->preview_widget)
1145 gtk_widget_show (impl->preview_widget);
1146 gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_widget, TRUE, TRUE, 0);
1147 gtk_box_reorder_child (GTK_BOX (impl->preview_box),
1148 impl->preview_widget,
1149 (impl->use_preview_label && impl->preview_label) ? 1 : 0);
1152 update_preview_widget_visibility (impl);
1155 /* Renders a "Search" icon at an appropriate size for a tree view */
1157 render_search_icon (GtkFileChooserDefault *impl)
1159 return gtk_widget_render_icon_pixbuf (GTK_WIDGET (impl), GTK_STOCK_FIND, GTK_ICON_SIZE_MENU);
1163 render_recent_icon (GtkFileChooserDefault *impl)
1165 GtkIconTheme *theme;
1168 if (gtk_widget_has_screen (GTK_WIDGET (impl)))
1169 theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
1171 theme = gtk_icon_theme_get_default ();
1173 retval = gtk_icon_theme_load_icon (theme, "document-open-recent",
1179 retval = gtk_widget_render_icon_pixbuf (GTK_WIDGET (impl), GTK_STOCK_FILE, GTK_ICON_SIZE_MENU);
1185 /* Re-reads all the icons for the shortcuts, used when the theme changes */
1186 struct ReloadIconsData
1188 GtkFileChooserDefault *impl;
1189 GtkTreeRowReference *row_ref;
1193 shortcuts_reload_icons_get_info_cb (GCancellable *cancellable,
1195 const GError *error,
1201 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1202 struct ReloadIconsData *data = user_data;
1204 if (!g_slist_find (data->impl->reload_icon_cancellables, cancellable))
1207 data->impl->reload_icon_cancellables = g_slist_remove (data->impl->reload_icon_cancellables, cancellable);
1209 if (cancelled || error)
1212 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->impl), data->impl->icon_size);
1214 path = gtk_tree_row_reference_get_path (data->row_ref);
1217 gtk_tree_model_get_iter (GTK_TREE_MODEL (data->impl->shortcuts_model), &iter, path);
1218 gtk_list_store_set (data->impl->shortcuts_model, &iter,
1219 SHORTCUTS_COL_PIXBUF, pixbuf,
1221 gtk_tree_path_free (path);
1225 g_object_unref (pixbuf);
1228 gtk_tree_row_reference_free (data->row_ref);
1229 g_object_unref (data->impl);
1232 g_object_unref (cancellable);
1236 shortcuts_reload_icons (GtkFileChooserDefault *impl)
1241 profile_start ("start", NULL);
1243 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
1246 for (l = impl->reload_icon_cancellables; l; l = l->next)
1248 GCancellable *cancellable = G_CANCELLABLE (l->data);
1249 g_cancellable_cancel (cancellable);
1251 g_slist_free (impl->reload_icon_cancellables);
1252 impl->reload_icon_cancellables = NULL;
1257 ShortcutType shortcut_type;
1258 gboolean pixbuf_visible;
1261 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
1262 SHORTCUTS_COL_DATA, &data,
1263 SHORTCUTS_COL_TYPE, &shortcut_type,
1264 SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible,
1270 if (shortcut_type == SHORTCUT_TYPE_VOLUME)
1272 GtkFileSystemVolume *volume;
1275 pixbuf = _gtk_file_system_volume_render_icon (volume, GTK_WIDGET (impl),
1276 impl->icon_size, NULL);
1278 else if (shortcut_type == SHORTCUT_TYPE_FILE)
1280 if (g_file_is_native (G_FILE (data)))
1283 struct ReloadIconsData *info;
1284 GtkTreePath *tree_path;
1285 GCancellable *cancellable;
1289 info = g_new0 (struct ReloadIconsData, 1);
1290 info->impl = g_object_ref (impl);
1291 tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
1292 info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->shortcuts_model), tree_path);
1293 gtk_tree_path_free (tree_path);
1295 cancellable = _gtk_file_system_get_info (impl->file_system, file,
1297 shortcuts_reload_icons_get_info_cb,
1299 impl->reload_icon_cancellables = g_slist_append (impl->reload_icon_cancellables, cancellable);
1303 GtkIconTheme *icon_theme;
1305 /* Don't call get_info for remote paths to avoid latency and
1307 * If we switch to a better bookmarks file format (XBEL), we
1308 * should use mime info to get a better icon.
1310 icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
1311 pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote",
1312 impl->icon_size, 0, NULL);
1315 else if (shortcut_type == SHORTCUT_TYPE_SEARCH)
1317 pixbuf = render_search_icon (impl);
1319 else if (shortcut_type == SHORTCUT_TYPE_RECENT)
1321 pixbuf = render_recent_icon (impl);
1324 gtk_list_store_set (impl->shortcuts_model, &iter,
1325 SHORTCUTS_COL_PIXBUF, pixbuf,
1329 g_object_unref (pixbuf);
1333 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
1337 profile_end ("end", NULL);
1341 shortcuts_find_folder (GtkFileChooserDefault *impl,
1344 GtkTreeSelection *selection;
1348 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1350 g_assert (folder != NULL);
1351 pos = shortcut_find_position (impl, folder);
1354 gtk_tree_selection_unselect_all (selection);
1358 path = gtk_tree_path_new_from_indices (pos, -1);
1359 gtk_tree_selection_select_path (selection, path);
1360 gtk_tree_path_free (path);
1363 /* If a shortcut corresponds to the current folder, selects it */
1365 shortcuts_find_current_folder (GtkFileChooserDefault *impl)
1367 shortcuts_find_folder (impl, impl->current_folder);
1370 /* Removes the specified number of rows from the shortcuts list */
1372 shortcuts_remove_rows (GtkFileChooserDefault *impl,
1378 path = gtk_tree_path_new_from_indices (start_row, -1);
1380 for (; n_rows; n_rows--)
1384 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
1385 g_assert_not_reached ();
1387 shortcuts_free_row_data (impl, &iter);
1388 gtk_list_store_remove (impl->shortcuts_model, &iter);
1391 gtk_tree_path_free (path);
1395 shortcuts_update_count (GtkFileChooserDefault *impl,
1396 ShortcutsIndex type,
1401 case SHORTCUTS_HOME:
1403 impl->has_home = FALSE;
1405 impl->has_home = TRUE;
1408 case SHORTCUTS_DESKTOP:
1410 impl->has_desktop = FALSE;
1412 impl->has_desktop = TRUE;
1415 case SHORTCUTS_VOLUMES:
1416 impl->num_volumes += value;
1419 case SHORTCUTS_SHORTCUTS:
1420 impl->num_shortcuts += value;
1423 case SHORTCUTS_BOOKMARKS:
1424 impl->num_bookmarks += value;
1427 case SHORTCUTS_CURRENT_FOLDER:
1429 impl->shortcuts_current_folder_active = FALSE;
1431 impl->shortcuts_current_folder_active = TRUE;
1440 struct ShortcutsInsertRequest
1442 GtkFileChooserDefault *impl;
1446 GtkTreeRowReference *row_ref;
1447 ShortcutsIndex type;
1453 get_file_info_finished (GCancellable *cancellable,
1455 const GError *error,
1459 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1463 GCancellable *model_cancellable = NULL;
1464 struct ShortcutsInsertRequest *request = data;
1466 path = gtk_tree_row_reference_get_path (request->row_ref);
1468 /* Handle doesn't exist anymore in the model */
1471 pos = gtk_tree_path_get_indices (path)[0];
1472 gtk_tree_model_get_iter (GTK_TREE_MODEL (request->impl->shortcuts_model),
1474 gtk_tree_path_free (path);
1476 /* validate cancellable, else goto out */
1477 gtk_tree_model_get (GTK_TREE_MODEL (request->impl->shortcuts_model), &iter,
1478 SHORTCUTS_COL_CANCELLABLE, &model_cancellable,
1480 if (cancellable != model_cancellable)
1483 /* set the cancellable to NULL in the model (we unref later on) */
1484 gtk_list_store_set (request->impl->shortcuts_model, &iter,
1485 SHORTCUTS_COL_CANCELLABLE, NULL,
1493 gtk_list_store_remove (request->impl->shortcuts_model, &iter);
1494 shortcuts_update_count (request->impl, request->type, -1);
1496 if (request->type == SHORTCUTS_HOME)
1500 home = g_file_new_for_path (g_get_home_dir ());
1501 error_getting_info_dialog (request->impl, home, g_error_copy (error));
1502 g_object_unref (home);
1504 else if (request->type == SHORTCUTS_CURRENT_FOLDER)
1506 /* Remove the current folder separator */
1507 gint separator_pos = shortcuts_get_index (request->impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1508 shortcuts_remove_rows (request->impl, separator_pos, 1);
1514 if (!request->label_copy)
1515 request->label_copy = g_strdup (g_file_info_get_display_name (info));
1516 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (request->impl),
1517 request->impl->icon_size);
1519 gtk_list_store_set (request->impl->shortcuts_model, &iter,
1520 SHORTCUTS_COL_PIXBUF, pixbuf,
1521 SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1522 SHORTCUTS_COL_NAME, request->label_copy,
1523 SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_FILE,
1524 SHORTCUTS_COL_REMOVABLE, request->removable,
1527 if (request->impl->shortcuts_pane_filter_model)
1528 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (request->impl->shortcuts_pane_filter_model));
1530 if (request->impl->shortcuts_combo_filter_model)
1531 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (request->impl->shortcuts_combo_filter_model));
1533 if (request->type == SHORTCUTS_CURRENT_FOLDER &&
1534 request->impl->save_folder_combo != NULL)
1536 /* The current folder is updated via _activate_iter(), don't
1537 * have save_folder_combo_changed_cb() call _activate_iter()
1540 g_signal_handlers_block_by_func (request->impl->save_folder_combo,
1541 G_CALLBACK (save_folder_combo_changed_cb),
1544 if (request->impl->has_search)
1547 if (request->impl->has_recent)
1550 gtk_combo_box_set_active (GTK_COMBO_BOX (request->impl->save_folder_combo), pos);
1551 g_signal_handlers_unblock_by_func (request->impl->save_folder_combo,
1552 G_CALLBACK (save_folder_combo_changed_cb),
1557 g_object_unref (pixbuf);
1560 g_object_unref (request->impl);
1561 g_object_unref (request->file);
1562 gtk_tree_row_reference_free (request->row_ref);
1563 g_free (request->label_copy);
1566 if (model_cancellable)
1567 g_object_unref (model_cancellable);
1570 /* FIXME: GtkFileSystem needs a function to split a remote path
1571 * into hostname and path components, or maybe just have a
1572 * gtk_file_system_path_get_display_name().
1574 * This function is also used in gtkfilechooserbutton.c
1577 _gtk_file_chooser_label_for_file (GFile *file)
1579 const gchar *path, *start, *end, *p;
1580 gchar *uri, *host, *label;
1582 uri = g_file_get_uri (file);
1584 start = strstr (uri, "://");
1588 path = strchr (start, '/');
1593 end = uri + strlen (uri);
1597 /* strip username */
1598 p = strchr (start, '@');
1602 p = strchr (start, ':');
1606 host = g_strndup (start, end - start);
1608 /* Translators: the first string is a path and the second string
1609 * is a hostname. Nautilus and the panel contain the same string
1612 label = g_strdup_printf (_("%1$s on %2$s"), path, host);
1618 label = g_strdup (uri);
1626 /* Inserts a path in the shortcuts tree, making a copy of it; alternatively,
1627 * inserts a volume. A position of -1 indicates the end of the tree.
1630 shortcuts_insert_file (GtkFileChooserDefault *impl,
1632 ShortcutType shortcut_type,
1633 GtkFileSystemVolume *volume,
1637 ShortcutsIndex type)
1640 GdkPixbuf *pixbuf = NULL;
1641 gpointer data = NULL;
1643 GtkIconTheme *icon_theme;
1645 profile_start ("start shortcut", NULL);
1647 if (shortcut_type == SHORTCUT_TYPE_VOLUME)
1650 label_copy = _gtk_file_system_volume_get_display_name (volume);
1651 pixbuf = _gtk_file_system_volume_render_icon (volume, GTK_WIDGET (impl),
1652 impl->icon_size, NULL);
1654 else if (shortcut_type == SHORTCUT_TYPE_FILE)
1656 if (g_file_is_native (file))
1658 struct ShortcutsInsertRequest *request;
1659 GCancellable *cancellable;
1662 request = g_new0 (struct ShortcutsInsertRequest, 1);
1663 request->impl = g_object_ref (impl);
1664 request->file = g_object_ref (file);
1665 request->name_only = TRUE;
1666 request->removable = removable;
1668 request->type = type;
1670 request->label_copy = g_strdup (label);
1673 gtk_list_store_append (impl->shortcuts_model, &iter);
1675 gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
1677 p = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
1678 request->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->shortcuts_model), p);
1679 gtk_tree_path_free (p);
1681 cancellable = _gtk_file_system_get_info (request->impl->file_system, request->file,
1682 "standard::is-hidden,standard::is-backup,standard::display-name,standard::icon",
1683 get_file_info_finished, request);
1685 gtk_list_store_set (impl->shortcuts_model, &iter,
1686 SHORTCUTS_COL_DATA, g_object_ref (file),
1687 SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_FILE,
1688 SHORTCUTS_COL_CANCELLABLE, cancellable,
1691 shortcuts_update_count (impl, type, 1);
1697 /* Don't call get_info for remote paths to avoid latency and
1700 data = g_object_ref (file);
1702 label_copy = g_strdup (label);
1704 label_copy = _gtk_file_chooser_label_for_file (file);
1706 /* If we switch to a better bookmarks file format (XBEL), we
1707 * should use mime info to get a better icon.
1709 icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
1710 pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote",
1711 impl->icon_size, 0, NULL);
1716 g_assert_not_reached ();
1722 gtk_list_store_append (impl->shortcuts_model, &iter);
1724 gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
1726 shortcuts_update_count (impl, type, 1);
1728 gtk_list_store_set (impl->shortcuts_model, &iter,
1729 SHORTCUTS_COL_PIXBUF, pixbuf,
1730 SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1731 SHORTCUTS_COL_NAME, label_copy,
1732 SHORTCUTS_COL_DATA, data,
1733 SHORTCUTS_COL_TYPE, shortcut_type,
1734 SHORTCUTS_COL_REMOVABLE, removable,
1735 SHORTCUTS_COL_CANCELLABLE, NULL,
1738 if (impl->shortcuts_pane_filter_model)
1739 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model));
1741 if (impl->shortcuts_combo_filter_model)
1742 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model));
1744 if (type == SHORTCUTS_CURRENT_FOLDER && impl->save_folder_combo != NULL)
1746 /* The current folder is updated via _activate_iter(), don't
1747 * have save_folder_combo_changed_cb() call _activate_iter()
1750 gint combo_pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER);
1752 if (impl->has_search)
1755 if (impl->has_recent)
1758 g_signal_handlers_block_by_func (impl->save_folder_combo,
1759 G_CALLBACK (save_folder_combo_changed_cb),
1762 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), combo_pos);
1763 g_signal_handlers_unblock_by_func (impl->save_folder_combo,
1764 G_CALLBACK (save_folder_combo_changed_cb),
1768 g_free (label_copy);
1771 g_object_unref (pixbuf);
1773 profile_end ("end", NULL);
1777 shortcuts_append_search (GtkFileChooserDefault *impl)
1782 pixbuf = render_search_icon (impl);
1784 gtk_list_store_append (impl->shortcuts_model, &iter);
1785 gtk_list_store_set (impl->shortcuts_model, &iter,
1786 SHORTCUTS_COL_PIXBUF, pixbuf,
1787 SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1788 SHORTCUTS_COL_NAME, _("Search"),
1789 SHORTCUTS_COL_DATA, NULL,
1790 SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_SEARCH,
1791 SHORTCUTS_COL_REMOVABLE, FALSE,
1795 g_object_unref (pixbuf);
1797 impl->has_search = TRUE;
1801 shortcuts_append_recent (GtkFileChooserDefault *impl)
1806 pixbuf = render_recent_icon (impl);
1808 gtk_list_store_append (impl->shortcuts_model, &iter);
1809 gtk_list_store_set (impl->shortcuts_model, &iter,
1810 SHORTCUTS_COL_PIXBUF, pixbuf,
1811 SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1812 SHORTCUTS_COL_NAME, _("Recently Used"),
1813 SHORTCUTS_COL_DATA, NULL,
1814 SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_RECENT,
1815 SHORTCUTS_COL_REMOVABLE, FALSE,
1819 g_object_unref (pixbuf);
1821 impl->has_recent = TRUE;
1824 /* Appends an item for the user's home directory to the shortcuts model */
1826 shortcuts_append_home (GtkFileChooserDefault *impl)
1828 const char *home_path;
1831 profile_start ("start", NULL);
1833 home_path = g_get_home_dir ();
1834 if (home_path == NULL)
1836 profile_end ("end - no home directory!?", NULL);
1840 home = g_file_new_for_path (home_path);
1841 shortcuts_insert_file (impl, -1, SHORTCUT_TYPE_FILE, NULL, home, NULL, FALSE, SHORTCUTS_HOME);
1842 impl->has_home = TRUE;
1844 g_object_unref (home);
1846 profile_end ("end", NULL);
1849 /* Appends the ~/Desktop directory to the shortcuts model */
1851 shortcuts_append_desktop (GtkFileChooserDefault *impl)
1856 profile_start ("start", NULL);
1858 name = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
1859 /* "To disable a directory, point it to the homedir."
1860 * See http://freedesktop.org/wiki/Software/xdg-user-dirs
1862 if (!g_strcmp0 (name, g_get_home_dir ()))
1864 profile_end ("end", NULL);
1868 file = g_file_new_for_path (name);
1869 shortcuts_insert_file (impl, -1, SHORTCUT_TYPE_FILE, NULL, file, _("Desktop"), FALSE, SHORTCUTS_DESKTOP);
1870 impl->has_desktop = TRUE;
1872 /* We do not actually pop up an error dialog if there is no desktop directory
1873 * because some people may really not want to have one.
1876 g_object_unref (file);
1878 profile_end ("end", NULL);
1881 /* Appends a list of GFile to the shortcuts model; returns how many were inserted */
1883 shortcuts_append_bookmarks (GtkFileChooserDefault *impl,
1890 profile_start ("start", NULL);
1892 start_row = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR) + 1;
1895 for (; bookmarks; bookmarks = bookmarks->next)
1899 file = bookmarks->data;
1901 if (impl->local_only && !g_file_is_native (file))
1904 if (shortcut_find_position (impl, file) != -1)
1907 label = _gtk_file_system_get_bookmark_label (impl->file_system, file);
1909 shortcuts_insert_file (impl, start_row + num_inserted, SHORTCUT_TYPE_FILE, NULL, file, label, TRUE, SHORTCUTS_BOOKMARKS);
1915 profile_end ("end", NULL);
1917 return num_inserted;
1920 /* Returns the index for the corresponding item in the shortcuts bar */
1922 shortcuts_get_index (GtkFileChooserDefault *impl,
1923 ShortcutsIndex where)
1929 if (where == SHORTCUTS_SEARCH)
1932 n += impl->has_search ? 1 : 0;
1934 if (where == SHORTCUTS_RECENT)
1937 n += impl->has_recent ? 1 : 0;
1939 if (where == SHORTCUTS_RECENT_SEPARATOR)
1942 n += impl->has_recent ? 1 : 0;
1944 if (where == SHORTCUTS_HOME)
1947 n += impl->has_home ? 1 : 0;
1949 if (where == SHORTCUTS_DESKTOP)
1952 n += impl->has_desktop ? 1 : 0;
1954 if (where == SHORTCUTS_VOLUMES)
1957 n += impl->num_volumes;
1959 if (where == SHORTCUTS_SHORTCUTS)
1962 n += impl->num_shortcuts;
1964 if (where == SHORTCUTS_BOOKMARKS_SEPARATOR)
1967 /* If there are no bookmarks there won't be a separator */
1968 n += (impl->num_bookmarks > 0) ? 1 : 0;
1970 if (where == SHORTCUTS_BOOKMARKS)
1973 n += impl->num_bookmarks;
1975 if (where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR)
1980 if (where == SHORTCUTS_CURRENT_FOLDER)
1983 g_assert_not_reached ();
1990 /* Adds all the file system volumes to the shortcuts model */
1992 shortcuts_add_volumes (GtkFileChooserDefault *impl)
1997 gboolean old_changing_folders;
1999 profile_start ("start", NULL);
2001 old_changing_folders = impl->changing_folder;
2002 impl->changing_folder = TRUE;
2004 start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
2005 shortcuts_remove_rows (impl, start_row, impl->num_volumes);
2006 impl->num_volumes = 0;
2008 list = _gtk_file_system_list_volumes (impl->file_system);
2012 for (l = list; l; l = l->next)
2014 GtkFileSystemVolume *volume;
2018 if (impl->local_only)
2020 if (_gtk_file_system_volume_is_mounted (volume))
2023 gboolean base_is_native = TRUE;
2025 base_file = _gtk_file_system_volume_get_root (volume);
2026 if (base_file != NULL)
2028 base_is_native = g_file_is_native (base_file);
2029 g_object_unref (base_file);
2032 if (!base_is_native)
2037 shortcuts_insert_file (impl,
2039 SHORTCUT_TYPE_VOLUME,
2040 _gtk_file_system_volume_ref (volume),
2048 impl->num_volumes = n;
2049 g_slist_free (list);
2051 if (impl->shortcuts_pane_filter_model)
2052 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model));
2054 if (impl->shortcuts_combo_filter_model)
2055 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model));
2057 impl->changing_folder = old_changing_folders;
2059 profile_end ("end", NULL);
2062 /* Inserts a separator node in the shortcuts list */
2064 shortcuts_insert_separator (GtkFileChooserDefault *impl,
2065 ShortcutsIndex where)
2069 g_assert (where == SHORTCUTS_RECENT_SEPARATOR ||
2070 where == SHORTCUTS_BOOKMARKS_SEPARATOR ||
2071 where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2073 gtk_list_store_insert (impl->shortcuts_model, &iter,
2074 shortcuts_get_index (impl, where));
2075 gtk_list_store_set (impl->shortcuts_model, &iter,
2076 SHORTCUTS_COL_PIXBUF, NULL,
2077 SHORTCUTS_COL_PIXBUF_VISIBLE, FALSE,
2078 SHORTCUTS_COL_NAME, NULL,
2079 SHORTCUTS_COL_DATA, NULL,
2080 SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_SEPARATOR,
2084 /* Updates the list of bookmarks */
2086 shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
2089 gboolean old_changing_folders;
2091 GFile *list_selected = NULL;
2092 GFile *combo_selected = NULL;
2093 ShortcutType shortcut_type;
2096 profile_start ("start", NULL);
2098 old_changing_folders = impl->changing_folder;
2099 impl->changing_folder = TRUE;
2101 if (shortcuts_get_selected (impl, &iter))
2103 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model),
2105 SHORTCUTS_COL_DATA, &col_data,
2106 SHORTCUTS_COL_TYPE, &shortcut_type,
2109 if (col_data && shortcut_type == SHORTCUT_TYPE_FILE)
2110 list_selected = g_object_ref (col_data);
2113 if (impl->save_folder_combo &&
2114 gtk_combo_box_get_active_iter (GTK_COMBO_BOX (impl->save_folder_combo),
2117 GtkTreeIter child_iter;
2119 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model),
2122 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model),
2124 SHORTCUTS_COL_DATA, &col_data,
2125 SHORTCUTS_COL_TYPE, &shortcut_type,
2128 if (col_data && shortcut_type == SHORTCUT_TYPE_FILE)
2129 combo_selected = g_object_ref (col_data);
2132 if (impl->num_bookmarks > 0)
2133 shortcuts_remove_rows (impl,
2134 shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR),
2135 impl->num_bookmarks + 1);
2137 impl->num_bookmarks = 0;
2138 shortcuts_insert_separator (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
2140 bookmarks = _gtk_file_system_list_bookmarks (impl->file_system);
2141 shortcuts_append_bookmarks (impl, bookmarks);
2142 g_slist_foreach (bookmarks, (GFunc) g_object_unref, NULL);
2143 g_slist_free (bookmarks);
2145 if (impl->num_bookmarks == 0)
2146 shortcuts_remove_rows (impl, shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR), 1);
2148 if (impl->shortcuts_pane_filter_model)
2149 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model));
2151 if (impl->shortcuts_combo_filter_model)
2152 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model));
2156 shortcuts_find_folder (impl, list_selected);
2157 g_object_unref (list_selected);
2164 pos = shortcut_find_position (impl, combo_selected);
2167 if (impl->has_search)
2170 if (impl->has_recent)
2173 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), pos);
2176 g_object_unref (combo_selected);
2179 impl->changing_folder = old_changing_folders;
2181 profile_end ("end", NULL);
2184 /* Appends a separator and a row to the shortcuts list for the current folder */
2186 shortcuts_add_current_folder (GtkFileChooserDefault *impl)
2190 g_assert (!impl->shortcuts_current_folder_active);
2192 g_assert (impl->current_folder != NULL);
2194 pos = shortcut_find_position (impl, impl->current_folder);
2197 GtkFileSystemVolume *volume;
2201 shortcuts_insert_separator (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2204 pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER);
2206 volume = _gtk_file_system_get_volume_for_file (impl->file_system, impl->current_folder);
2208 base_file = _gtk_file_system_volume_get_root (volume);
2212 if (base_file && g_file_equal (base_file, impl->current_folder))
2213 shortcuts_insert_file (impl, pos, SHORTCUT_TYPE_VOLUME, volume, NULL, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER);
2215 shortcuts_insert_file (impl, pos, SHORTCUT_TYPE_FILE, NULL, impl->current_folder, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER);
2218 g_object_unref (base_file);
2220 else if (impl->save_folder_combo != NULL)
2222 if (impl->has_search)
2225 if (impl->has_recent)
2226 pos -= 2; /* + separator */
2228 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), pos);
2232 /* Updates the current folder row in the shortcuts model */
2234 shortcuts_update_current_folder (GtkFileChooserDefault *impl)
2238 pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2240 if (impl->shortcuts_current_folder_active)
2242 shortcuts_remove_rows (impl, pos, 2);
2243 impl->shortcuts_current_folder_active = FALSE;
2246 shortcuts_add_current_folder (impl);
2249 /* Filter function used for the shortcuts filter model */
2251 shortcuts_pane_filter_cb (GtkTreeModel *model,
2255 GtkFileChooserDefault *impl;
2259 impl = GTK_FILE_CHOOSER_DEFAULT (data);
2261 path = gtk_tree_model_get_path (model, iter);
2265 pos = *gtk_tree_path_get_indices (path);
2266 gtk_tree_path_free (path);
2268 return (pos < shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR));
2271 /* Creates the list model for shortcuts */
2273 shortcuts_model_create (GtkFileChooserDefault *impl)
2275 /* Keep this order in sync with the SHORCUTS_COL_* enum values */
2276 impl->shortcuts_model = gtk_list_store_new (SHORTCUTS_COL_NUM_COLUMNS,
2277 GDK_TYPE_PIXBUF, /* pixbuf */
2278 G_TYPE_STRING, /* name */
2279 G_TYPE_POINTER, /* path or volume */
2280 G_TYPE_INT, /* ShortcutType */
2281 G_TYPE_BOOLEAN, /* removable */
2282 G_TYPE_BOOLEAN, /* pixbuf cell visibility */
2283 G_TYPE_POINTER); /* GCancellable */
2285 shortcuts_append_search (impl);
2287 if (impl->recent_manager)
2289 shortcuts_append_recent (impl);
2290 shortcuts_insert_separator (impl, SHORTCUTS_RECENT_SEPARATOR);
2293 if (impl->file_system)
2295 shortcuts_append_home (impl);
2296 shortcuts_append_desktop (impl);
2297 shortcuts_add_volumes (impl);
2300 impl->shortcuts_pane_filter_model = shortcuts_pane_model_filter_new (impl,
2301 GTK_TREE_MODEL (impl->shortcuts_model),
2304 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
2305 shortcuts_pane_filter_cb,
2310 /* Callback used when the "New Folder" button is clicked */
2312 new_folder_button_clicked (GtkButton *button,
2313 GtkFileChooserDefault *impl)
2318 if (!impl->browse_files_model)
2319 return; /* FIXME: this sucks. Disable the New Folder button or something. */
2321 /* Prevent button from being clicked twice */
2322 gtk_widget_set_sensitive (impl->browse_new_folder_button, FALSE);
2324 _gtk_file_system_model_add_editable (impl->browse_files_model, &iter);
2326 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->browse_files_model), &iter);
2327 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_files_tree_view),
2328 path, impl->list_name_column,
2331 g_object_set (impl->list_name_renderer, "editable", TRUE, NULL);
2332 gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view),
2334 impl->list_name_column,
2337 gtk_tree_path_free (path);
2341 add_idle_while_impl_is_alive (GtkFileChooserDefault *impl, GCallback callback)
2345 source = g_idle_source_new ();
2346 g_source_set_closure (source,
2347 g_cclosure_new_object (callback, G_OBJECT (impl)));
2348 g_source_attach (source, NULL);
2353 /* Idle handler for creating a new folder after editing its name cell, or for
2354 * canceling the editing.
2357 edited_idle_cb (GtkFileChooserDefault *impl)
2359 GDK_THREADS_ENTER ();
2361 g_source_destroy (impl->edited_idle);
2362 impl->edited_idle = NULL;
2364 _gtk_file_system_model_remove_editable (impl->browse_files_model);
2365 g_object_set (impl->list_name_renderer, "editable", FALSE, NULL);
2367 gtk_widget_set_sensitive (impl->browse_new_folder_button, TRUE);
2369 if (impl->edited_new_text /* not cancelled? */
2370 && (strlen (impl->edited_new_text) != 0)
2371 && (strcmp (impl->edited_new_text, DEFAULT_NEW_FOLDER_NAME) != 0)) /* Don't create folder if name is empty or has not been edited */
2373 GError *error = NULL;
2376 file = g_file_get_child_for_display_name (impl->current_folder,
2377 impl->edited_new_text,
2381 GError *error = NULL;
2383 if (g_file_make_directory (file, NULL, &error))
2384 change_folder_and_display_error (impl, file, FALSE);
2386 error_creating_folder_dialog (impl, file, error);
2388 g_object_unref (file);
2391 error_creating_folder_dialog (impl, file, error);
2393 g_free (impl->edited_new_text);
2394 impl->edited_new_text = NULL;
2397 GDK_THREADS_LEAVE ();
2403 queue_edited_idle (GtkFileChooserDefault *impl,
2404 const gchar *new_text)
2406 /* We create the folder in an idle handler so that we don't modify the tree
2410 if (!impl->edited_idle)
2411 impl->edited_idle = add_idle_while_impl_is_alive (impl, G_CALLBACK (edited_idle_cb));
2413 g_free (impl->edited_new_text);
2414 impl->edited_new_text = g_strdup (new_text);
2417 /* Callback used from the text cell renderer when the new folder is named */
2419 renderer_edited_cb (GtkCellRendererText *cell_renderer_text,
2421 const gchar *new_text,
2422 GtkFileChooserDefault *impl)
2424 /* work around bug #154921 */
2425 g_object_set (cell_renderer_text,
2426 "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
2427 queue_edited_idle (impl, new_text);
2430 /* Callback used from the text cell renderer when the new folder edition gets
2434 renderer_editing_canceled_cb (GtkCellRendererText *cell_renderer_text,
2435 GtkFileChooserDefault *impl)
2437 /* work around bug #154921 */
2438 g_object_set (cell_renderer_text,
2439 "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
2440 queue_edited_idle (impl, NULL);
2443 /* Creates the widgets for the filter combo box */
2445 filter_create (GtkFileChooserDefault *impl)
2447 GtkCellRenderer *cell;
2450 impl->filter_combo = gtk_combo_box_text_new ();
2451 gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (impl->filter_combo), FALSE);
2453 /* Get the combo's text renderer and set ellipsize parameters */
2454 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (impl->filter_combo));
2458 g_object_set (G_OBJECT (cell),
2459 "ellipsize", PANGO_ELLIPSIZE_END,
2462 g_list_free (cells);
2464 g_signal_connect (impl->filter_combo, "changed",
2465 G_CALLBACK (filter_combo_changed), impl);
2467 gtk_widget_set_tooltip_text (impl->filter_combo,
2468 _("Select which types of files are shown"));
2470 return impl->filter_combo;
2474 toolbutton_new (GtkFileChooserDefault *impl,
2483 item = gtk_tool_button_new (NULL, NULL);
2484 image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_SMALL_TOOLBAR);
2485 gtk_widget_show (image);
2486 gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (item), image);
2488 gtk_widget_set_sensitive (GTK_WIDGET (item), sensitive);
2489 g_signal_connect (item, "clicked", callback, impl);
2492 gtk_widget_show (GTK_WIDGET (item));
2494 return GTK_WIDGET (item);
2497 /* Looks for a path among the shortcuts; returns its index or -1 if it doesn't exist */
2499 shortcut_find_position (GtkFileChooserDefault *impl,
2504 int current_folder_separator_idx;
2506 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
2509 current_folder_separator_idx = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2512 /* FIXME: is this still needed? */
2513 if (current_folder_separator_idx >= impl->shortcuts_model->length)
2517 for (i = 0; i < current_folder_separator_idx; i++)
2520 ShortcutType shortcut_type;
2522 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2523 SHORTCUTS_COL_DATA, &col_data,
2524 SHORTCUTS_COL_TYPE, &shortcut_type,
2529 if (shortcut_type == SHORTCUT_TYPE_VOLUME)
2531 GtkFileSystemVolume *volume;
2536 base_file = _gtk_file_system_volume_get_root (volume);
2538 exists = base_file && g_file_equal (file, base_file);
2541 g_object_unref (base_file);
2546 else if (shortcut_type == SHORTCUT_TYPE_FILE)
2550 model_file = col_data;
2552 if (model_file && g_file_equal (model_file, file))
2557 if (i < current_folder_separator_idx - 1)
2559 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
2560 g_assert_not_reached ();
2567 /* Tries to add a bookmark from a path name */
2569 shortcuts_add_bookmark_from_file (GtkFileChooserDefault *impl,
2575 g_return_val_if_fail (G_IS_FILE (file), FALSE);
2577 if (shortcut_find_position (impl, file) != -1)
2581 if (!_gtk_file_system_insert_bookmark (impl->file_system, file, pos, &error))
2583 error_adding_bookmark_dialog (impl, file, error);
2591 add_bookmark_foreach_cb (GtkTreeModel *model,
2596 GtkFileChooserDefault *impl;
2599 impl = (GtkFileChooserDefault *) data;
2601 gtk_tree_model_get (model, iter,
2602 MODEL_COL_FILE, &file,
2605 shortcuts_add_bookmark_from_file (impl, file, -1);
2607 g_object_unref (file);
2610 /* Adds a bookmark from the currently selected item in the file list */
2612 bookmarks_add_selected_folder (GtkFileChooserDefault *impl)
2614 GtkTreeSelection *selection;
2616 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2618 if (gtk_tree_selection_count_selected_rows (selection) == 0)
2619 shortcuts_add_bookmark_from_file (impl, impl->current_folder, -1);
2621 gtk_tree_selection_selected_foreach (selection,
2622 add_bookmark_foreach_cb,
2626 /* Callback used when the "Add bookmark" button is clicked */
2628 add_bookmark_button_clicked_cb (GtkButton *button,
2629 GtkFileChooserDefault *impl)
2631 bookmarks_add_selected_folder (impl);
2634 /* Returns TRUE plus an iter in the shortcuts_model if a row is selected;
2635 * returns FALSE if no shortcut is selected.
2638 shortcuts_get_selected (GtkFileChooserDefault *impl,
2641 GtkTreeSelection *selection;
2642 GtkTreeIter parent_iter;
2644 if (!impl->browse_shortcuts_tree_view)
2647 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
2649 if (!gtk_tree_selection_get_selected (selection, NULL, &parent_iter))
2652 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
2658 /* Removes the selected bookmarks */
2660 remove_selected_bookmarks (GtkFileChooserDefault *impl)
2668 if (!shortcuts_get_selected (impl, &iter))
2671 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2672 SHORTCUTS_COL_DATA, &col_data,
2673 SHORTCUTS_COL_REMOVABLE, &removable,
2679 g_assert (col_data != NULL);
2684 if (!_gtk_file_system_remove_bookmark (impl->file_system, file, &error))
2685 error_removing_bookmark_dialog (impl, file, error);
2688 /* Callback used when the "Remove bookmark" button is clicked */
2690 remove_bookmark_button_clicked_cb (GtkButton *button,
2691 GtkFileChooserDefault *impl)
2693 remove_selected_bookmarks (impl);
2696 struct selection_check_closure {
2697 GtkFileChooserDefault *impl;
2700 gboolean all_folders;
2703 /* Used from gtk_tree_selection_selected_foreach() */
2705 selection_check_foreach_cb (GtkTreeModel *model,
2710 struct selection_check_closure *closure;
2714 gtk_tree_model_get (model, iter,
2715 MODEL_COL_FILE, &file,
2716 MODEL_COL_IS_FOLDER, &is_folder,
2722 g_object_unref (file);
2725 closure->num_selected++;
2727 closure->all_folders = closure->all_folders && is_folder;
2728 closure->all_files = closure->all_files && !is_folder;
2731 /* Checks whether the selected items in the file list are all files or all folders */
2733 selection_check (GtkFileChooserDefault *impl,
2735 gboolean *all_files,
2736 gboolean *all_folders)
2738 struct selection_check_closure closure;
2739 GtkTreeSelection *selection;
2741 closure.impl = impl;
2742 closure.num_selected = 0;
2743 closure.all_files = TRUE;
2744 closure.all_folders = TRUE;
2746 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2747 gtk_tree_selection_selected_foreach (selection,
2748 selection_check_foreach_cb,
2751 g_assert (closure.num_selected == 0 || !(closure.all_files && closure.all_folders));
2754 *num_selected = closure.num_selected;
2757 *all_files = closure.all_files;
2760 *all_folders = closure.all_folders;
2763 struct get_selected_file_closure {
2764 GtkFileChooserDefault *impl;
2769 get_selected_file_foreach_cb (GtkTreeModel *model,
2774 struct get_selected_file_closure *closure = data;
2778 /* Just in case this function gets run more than once with a multiple selection; we only care about one file */
2779 g_object_unref (closure->file);
2780 closure->file = NULL;
2783 gtk_tree_model_get (model, iter,
2784 MODEL_COL_FILE, &closure->file, /* this will give us a reffed file */
2788 /* Returns a selected path from the file list */
2790 get_selected_file (GtkFileChooserDefault *impl)
2792 struct get_selected_file_closure closure;
2793 GtkTreeSelection *selection;
2795 closure.impl = impl;
2796 closure.file = NULL;
2798 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2799 gtk_tree_selection_selected_foreach (selection,
2800 get_selected_file_foreach_cb,
2803 return closure.file;
2807 GtkFileChooserDefault *impl;
2809 } UpdateTooltipData;
2812 update_tooltip (GtkTreeModel *model,
2817 UpdateTooltipData *udata = data;
2819 if (udata->tip == NULL)
2821 gchar *display_name;
2823 gtk_tree_model_get (model, iter,
2824 MODEL_COL_NAME, &display_name,
2827 udata->tip = g_strdup_printf (_("Add the folder '%s' to the bookmarks"),
2829 g_free (display_name);
2834 /* Sensitize the "add bookmark" button if all the selected items are folders, or
2835 * if there are no selected items *and* the current folder is not in the
2836 * bookmarks list. De-sensitize the button otherwise.
2839 bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl)
2842 gboolean all_folders;
2846 selection_check (impl, &num_selected, NULL, &all_folders);
2848 if (num_selected == 0)
2849 active = (impl->current_folder != NULL) && (shortcut_find_position (impl, impl->current_folder) == -1);
2850 else if (num_selected == 1)
2854 file = get_selected_file (impl);
2855 active = file && all_folders && (shortcut_find_position (impl, file) == -1);
2857 g_object_unref (file);
2860 active = all_folders;
2862 gtk_widget_set_sensitive (impl->browse_shortcuts_add_button, active);
2864 if (impl->browse_files_popup_menu_add_shortcut_item)
2865 gtk_widget_set_sensitive (impl->browse_files_popup_menu_add_shortcut_item,
2866 (num_selected == 0) ? FALSE : active);
2870 if (num_selected == 0)
2871 tip = g_strdup_printf (_("Add the current folder to the bookmarks"));
2872 else if (num_selected > 1)
2873 tip = g_strdup_printf (_("Add the selected folders to the bookmarks"));
2876 GtkTreeSelection *selection;
2877 UpdateTooltipData data;
2879 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2882 gtk_tree_selection_selected_foreach (selection, update_tooltip, &data);
2886 gtk_widget_set_tooltip_text (impl->browse_shortcuts_add_button, tip);
2891 /* Sets the sensitivity of the "remove bookmark" button depending on whether a
2892 * bookmark row is selected in the shortcuts tree.
2895 bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl)
2898 gboolean removable = FALSE;
2902 if (shortcuts_get_selected (impl, &iter))
2904 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2905 SHORTCUTS_COL_REMOVABLE, &removable,
2906 SHORTCUTS_COL_NAME, &name,
2908 gtk_widget_set_sensitive (impl->browse_shortcuts_remove_button, removable);
2911 tip = g_strdup_printf (_("Remove the bookmark '%s'"), name);
2913 tip = g_strdup_printf (_("Bookmark '%s' cannot be removed"), name);
2915 gtk_widget_set_tooltip_text (impl->browse_shortcuts_remove_button, tip);
2919 gtk_widget_set_tooltip_text (impl->browse_shortcuts_remove_button,
2920 _("Remove the selected bookmark"));
2925 shortcuts_check_popup_sensitivity (GtkFileChooserDefault *impl)
2928 gboolean removable = FALSE;
2930 if (impl->browse_shortcuts_popup_menu == NULL)
2933 if (shortcuts_get_selected (impl, &iter))
2934 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2935 SHORTCUTS_COL_REMOVABLE, &removable,
2938 gtk_widget_set_sensitive (impl->browse_shortcuts_popup_menu_remove_item, removable);
2939 gtk_widget_set_sensitive (impl->browse_shortcuts_popup_menu_rename_item, removable);
2942 /* GtkWidget::drag-begin handler for the shortcuts list. */
2944 shortcuts_drag_begin_cb (GtkWidget *widget,
2945 GdkDragContext *context,
2946 GtkFileChooserDefault *impl)
2949 impl->shortcuts_drag_context = g_object_ref (context);
2954 /* Removes the idle handler for outside drags */
2956 shortcuts_cancel_drag_outside_idle (GtkFileChooserDefault *impl)
2958 if (!impl->shortcuts_drag_outside_idle)
2961 g_source_destroy (impl->shortcuts_drag_outside_idle);
2962 impl->shortcuts_drag_outside_idle = NULL;
2966 /* GtkWidget::drag-end handler for the shortcuts list. */
2968 shortcuts_drag_end_cb (GtkWidget *widget,
2969 GdkDragContext *context,
2970 GtkFileChooserDefault *impl)
2973 g_object_unref (impl->shortcuts_drag_context);
2975 shortcuts_cancel_drag_outside_idle (impl);
2977 if (!impl->shortcuts_drag_outside)
2980 gtk_button_clicked (GTK_BUTTON (impl->browse_shortcuts_remove_button));
2982 impl->shortcuts_drag_outside = FALSE;
2986 /* GtkWidget::drag-data-delete handler for the shortcuts list. */
2988 shortcuts_drag_data_delete_cb (GtkWidget *widget,
2989 GdkDragContext *context,
2990 GtkFileChooserDefault *impl)
2992 g_signal_stop_emission_by_name (widget, "drag-data-delete");
2995 /* GtkWidget::drag-leave handler for the shortcuts list. We unhighlight the
2999 shortcuts_drag_leave_cb (GtkWidget *widget,
3000 GdkDragContext *context,
3002 GtkFileChooserDefault *impl)
3005 if (gtk_drag_get_source_widget (context) == widget && !impl->shortcuts_drag_outside_idle)
3007 impl->shortcuts_drag_outside_idle = add_idle_while_impl_is_alive (impl, G_CALLBACK (shortcuts_drag_outside_idle_cb));
3011 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3013 GTK_TREE_VIEW_DROP_BEFORE);
3015 g_signal_stop_emission_by_name (widget, "drag-leave");
3018 /* Computes the appropriate row and position for dropping */
3020 shortcuts_compute_drop_position (GtkFileChooserDefault *impl,
3024 GtkTreeViewDropPosition *pos)
3026 GtkTreeView *tree_view;
3027 GtkTreeViewColumn *column;
3031 int bookmarks_index;
3032 int header_height = 0;
3034 tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
3036 if (gtk_tree_view_get_headers_visible (tree_view))
3037 header_height = _gtk_tree_view_get_header_height (tree_view);
3039 bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
3041 if (!gtk_tree_view_get_path_at_pos (tree_view,
3049 row = bookmarks_index + impl->num_bookmarks - 1;
3050 *path = gtk_tree_path_new_from_indices (row, -1);
3051 *pos = GTK_TREE_VIEW_DROP_AFTER;
3055 row = *gtk_tree_path_get_indices (*path);
3056 gtk_tree_view_get_background_area (tree_view, *path, column, &cell);
3057 gtk_tree_path_free (*path);
3059 if (row < bookmarks_index)
3061 row = bookmarks_index;
3062 *pos = GTK_TREE_VIEW_DROP_BEFORE;
3064 else if (row > bookmarks_index + impl->num_bookmarks - 1)
3066 row = bookmarks_index + impl->num_bookmarks - 1;
3067 *pos = GTK_TREE_VIEW_DROP_AFTER;
3071 if (cell_y < cell.height / 2)
3072 *pos = GTK_TREE_VIEW_DROP_BEFORE;
3074 *pos = GTK_TREE_VIEW_DROP_AFTER;
3077 *path = gtk_tree_path_new_from_indices (row, -1);
3080 /* GtkWidget::drag-motion handler for the shortcuts list. We basically
3081 * implement the destination side of DnD by hand, due to limitations in
3082 * GtkTreeView's DnD API.
3085 shortcuts_drag_motion_cb (GtkWidget *widget,
3086 GdkDragContext *context,
3090 GtkFileChooserDefault *impl)
3093 GtkTreeViewDropPosition pos;
3094 GdkDragAction action;
3097 if (gtk_drag_get_source_widget (context) == widget)
3099 shortcuts_cancel_drag_outside_idle (impl);
3101 if (impl->shortcuts_drag_outside)
3103 shortcuts_drag_set_delete_cursor (impl, FALSE);
3104 impl->shortcuts_drag_outside = FALSE;
3109 if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_COPY ||
3110 (gdk_drag_context_get_actions (context) & GDK_ACTION_COPY) != 0)
3111 action = GDK_ACTION_COPY;
3112 else if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_MOVE ||
3113 (gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
3114 action = GDK_ACTION_MOVE;
3121 shortcuts_compute_drop_position (impl, x, y, &path, &pos);
3122 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), path, pos);
3123 gtk_tree_path_free (path);
3127 g_signal_stop_emission_by_name (widget, "drag-motion");
3131 gdk_drag_status (context, action, time_);
3138 /* GtkWidget::drag-drop handler for the shortcuts list. */
3140 shortcuts_drag_drop_cb (GtkWidget *widget,
3141 GdkDragContext *context,
3145 GtkFileChooserDefault *impl)
3148 shortcuts_cancel_drag_outside_idle (impl);
3151 g_signal_stop_emission_by_name (widget, "drag-drop");
3155 /* Parses a "text/uri-list" string and inserts its URIs as bookmarks */
3157 shortcuts_drop_uris (GtkFileChooserDefault *impl,
3158 GtkSelectionData *selection_data,
3164 uris = gtk_selection_data_get_uris (selection_data);
3168 for (i = 0; uris[i]; i++)
3174 file = g_file_new_for_uri (uri);
3176 if (shortcuts_add_bookmark_from_file (impl, file, position))
3179 g_object_unref (file);
3185 /* Reorders the selected bookmark to the specified position */
3187 shortcuts_reorder (GtkFileChooserDefault *impl,
3192 ShortcutType shortcut_type;
3195 int bookmarks_index;
3200 /* Get the selected path */
3202 if (!shortcuts_get_selected (impl, &iter))
3203 g_assert_not_reached ();
3205 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
3206 old_position = *gtk_tree_path_get_indices (path);
3207 gtk_tree_path_free (path);
3209 bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
3210 old_position -= bookmarks_index;
3211 g_assert (old_position >= 0 && old_position < impl->num_bookmarks);
3213 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
3214 SHORTCUTS_COL_NAME, &name,
3215 SHORTCUTS_COL_DATA, &col_data,
3216 SHORTCUTS_COL_TYPE, &shortcut_type,
3218 g_assert (col_data != NULL);
3219 g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
3222 g_object_ref (file); /* removal below will free file, so we need a new ref */
3224 /* Remove the path from the old position and insert it in the new one */
3226 if (new_position > old_position)
3229 if (old_position == new_position)
3233 if (_gtk_file_system_remove_bookmark (impl->file_system, file, &error))
3235 shortcuts_add_bookmark_from_file (impl, file, new_position);
3236 _gtk_file_system_set_bookmark_label (impl->file_system, file, name);
3239 error_adding_bookmark_dialog (impl, file, error);
3243 g_object_unref (file);
3247 /* Callback used when we get the drag data for the bookmarks list. We add the
3248 * received URIs as bookmarks if they are folders.
3251 shortcuts_drag_data_received_cb (GtkWidget *widget,
3252 GdkDragContext *context,
3255 GtkSelectionData *selection_data,
3260 GtkFileChooserDefault *impl;
3261 GtkTreePath *tree_path;
3262 GtkTreeViewDropPosition tree_pos;
3265 int bookmarks_index;
3267 impl = GTK_FILE_CHOOSER_DEFAULT (data);
3269 /* Compute position */
3271 bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
3273 shortcuts_compute_drop_position (impl, x, y, &tree_path, &tree_pos);
3274 position = *gtk_tree_path_get_indices (tree_path);
3275 gtk_tree_path_free (tree_path);
3277 if (tree_pos == GTK_TREE_VIEW_DROP_AFTER)
3280 g_assert (position >= bookmarks_index);
3281 position -= bookmarks_index;
3283 target = gtk_selection_data_get_target (selection_data);
3285 if (gtk_targets_include_uri (&target, 1))
3286 shortcuts_drop_uris (impl, selection_data, position);
3287 else if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
3288 shortcuts_reorder (impl, position);
3290 g_signal_stop_emission_by_name (widget, "drag-data-received");
3293 /* Callback used to display a tooltip in the shortcuts tree */
3295 shortcuts_query_tooltip_cb (GtkWidget *widget,
3298 gboolean keyboard_mode,
3299 GtkTooltip *tooltip,
3300 GtkFileChooserDefault *impl)
3302 GtkTreeModel *model;
3305 if (gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget),
3313 ShortcutType shortcut_type;
3315 gtk_tree_model_get (model, &iter,
3316 SHORTCUTS_COL_DATA, &col_data,
3317 SHORTCUTS_COL_TYPE, &shortcut_type,
3320 if (shortcut_type == SHORTCUT_TYPE_SEPARATOR)
3322 else if (shortcut_type == SHORTCUT_TYPE_VOLUME)
3324 else if (shortcut_type == SHORTCUT_TYPE_FILE)
3329 file = G_FILE (col_data);
3330 parse_name = g_file_get_parse_name (file);
3332 gtk_tooltip_set_text (tooltip, parse_name);
3334 g_free (parse_name);
3338 else if (shortcut_type == SHORTCUT_TYPE_SEARCH)
3342 else if (shortcut_type == SHORTCUT_TYPE_RECENT)
3352 /* Callback used when the selection in the shortcuts tree changes */
3354 shortcuts_selection_changed_cb (GtkTreeSelection *selection,
3355 GtkFileChooserDefault *impl)
3358 GtkTreeIter child_iter;
3360 bookmarks_check_remove_sensitivity (impl);
3361 shortcuts_check_popup_sensitivity (impl);
3363 if (impl->changing_folder)
3366 if (gtk_tree_selection_get_selected(selection, NULL, &iter))
3368 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
3371 shortcuts_activate_iter (impl, &child_iter);
3376 shortcuts_row_separator_func (GtkTreeModel *model,
3380 ShortcutType shortcut_type;
3382 gtk_tree_model_get (model, iter, SHORTCUTS_COL_TYPE, &shortcut_type, -1);
3384 return shortcut_type == SHORTCUT_TYPE_SEPARATOR;
3388 shortcuts_key_press_event_after_cb (GtkWidget *tree_view,
3390 GtkFileChooserDefault *impl)
3394 /* don't screw up focus switching with Tab */
3395 if (event->keyval == GDK_KEY_Tab
3396 || event->keyval == GDK_KEY_KP_Tab
3397 || event->keyval == GDK_KEY_ISO_Left_Tab
3398 || event->length < 1)
3401 if (impl->location_entry)
3402 entry = impl->location_entry;
3403 else if (impl->search_entry)
3404 entry = impl->search_entry;
3410 gtk_widget_grab_focus (entry);
3411 return gtk_widget_event (entry, (GdkEvent *) event);
3417 /* Callback used when the file list's popup menu is detached */
3419 shortcuts_popup_menu_detach_cb (GtkWidget *attach_widget,
3422 GtkFileChooserDefault *impl;
3424 impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
3425 g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
3427 impl->browse_shortcuts_popup_menu = NULL;
3428 impl->browse_shortcuts_popup_menu_remove_item = NULL;
3429 impl->browse_shortcuts_popup_menu_rename_item = NULL;
3433 remove_shortcut_cb (GtkMenuItem *item,
3434 GtkFileChooserDefault *impl)
3436 remove_selected_bookmarks (impl);
3439 /* Rename the selected bookmark */
3441 rename_selected_bookmark (GtkFileChooserDefault *impl)
3445 GtkTreeViewColumn *column;
3446 GtkCellRenderer *cell;
3449 if (shortcuts_get_selected (impl, &iter))
3451 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
3452 column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), 0);
3453 renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
3454 cell = g_list_nth_data (renderers, 1);
3455 g_list_free (renderers);
3456 g_object_set (cell, "editable", TRUE, NULL);
3457 gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3458 path, column, cell, TRUE);
3459 gtk_tree_path_free (path);
3464 rename_shortcut_cb (GtkMenuItem *item,
3465 GtkFileChooserDefault *impl)
3467 rename_selected_bookmark (impl);
3470 /* Constructs the popup menu for the file list if needed */
3472 shortcuts_build_popup_menu (GtkFileChooserDefault *impl)
3476 if (impl->browse_shortcuts_popup_menu)
3479 impl->browse_shortcuts_popup_menu = gtk_menu_new ();
3480 gtk_menu_attach_to_widget (GTK_MENU (impl->browse_shortcuts_popup_menu),
3481 impl->browse_shortcuts_tree_view,
3482 shortcuts_popup_menu_detach_cb);
3484 item = gtk_image_menu_item_new_with_label (_("Remove"));
3485 impl->browse_shortcuts_popup_menu_remove_item = item;
3486 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
3487 gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU));
3488 g_signal_connect (item, "activate",
3489 G_CALLBACK (remove_shortcut_cb), impl);
3490 gtk_widget_show (item);
3491 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu), item);
3493 item = gtk_menu_item_new_with_label (_("Rename..."));
3494 impl->browse_shortcuts_popup_menu_rename_item = item;
3495 g_signal_connect (item, "activate",
3496 G_CALLBACK (rename_shortcut_cb), impl);
3497 gtk_widget_show (item);
3498 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu), item);
3502 shortcuts_update_popup_menu (GtkFileChooserDefault *impl)
3504 shortcuts_build_popup_menu (impl);
3505 shortcuts_check_popup_sensitivity (impl);
3509 popup_position_func (GtkMenu *menu,
3513 gpointer user_data);
3516 shortcuts_popup_menu (GtkFileChooserDefault *impl,
3517 GdkEventButton *event)
3519 shortcuts_update_popup_menu (impl);
3521 gtk_menu_popup (GTK_MENU (impl->browse_shortcuts_popup_menu),
3522 NULL, NULL, NULL, NULL,
3523 event->button, event->time);
3526 gtk_menu_popup (GTK_MENU (impl->browse_shortcuts_popup_menu),
3528 popup_position_func, impl->browse_shortcuts_tree_view,
3529 0, GDK_CURRENT_TIME);
3530 gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu),
3535 /* Callback used for the GtkWidget::popup-menu signal of the shortcuts list */
3537 shortcuts_popup_menu_cb (GtkWidget *widget,
3538 GtkFileChooserDefault *impl)
3540 shortcuts_popup_menu (impl, NULL);
3544 /* Callback used when a button is pressed on the shortcuts list.
3545 * We trap button 3 to bring up a popup menu.
3548 shortcuts_button_press_event_cb (GtkWidget *widget,
3549 GdkEventButton *event,
3550 GtkFileChooserDefault *impl)
3552 static gboolean in_press = FALSE;
3558 if (event->button != 3)
3562 handled = gtk_widget_event (impl->browse_shortcuts_tree_view, (GdkEvent *) event);
3568 shortcuts_popup_menu (impl, event);
3573 shortcuts_edited (GtkCellRenderer *cell,
3576 GtkFileChooserDefault *impl)
3582 g_object_set (cell, "editable", FALSE, NULL);
3584 path = gtk_tree_path_new_from_string (path_string);
3585 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
3586 g_assert_not_reached ();
3588 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
3589 SHORTCUTS_COL_DATA, &shortcut,
3591 gtk_tree_path_free (path);
3593 _gtk_file_system_set_bookmark_label (impl->file_system, shortcut, new_text);
3597 shortcuts_editing_canceled (GtkCellRenderer *cell,
3598 GtkFileChooserDefault *impl)
3600 g_object_set (cell, "editable", FALSE, NULL);
3603 /* Creates the widgets for the shortcuts and bookmarks tree */
3605 shortcuts_list_create (GtkFileChooserDefault *impl)
3608 GtkTreeSelection *selection;
3609 GtkTreeViewColumn *column;
3610 GtkCellRenderer *renderer;
3612 /* Target types for dragging a row to/from the shortcuts list */
3613 const GtkTargetEntry tree_model_row_targets[] = {
3614 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }
3617 /* Scrolled window */
3619 swin = gtk_scrolled_window_new (NULL, NULL);
3620 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
3621 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
3622 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
3624 gtk_widget_show (swin);
3627 impl->browse_shortcuts_tree_view = gtk_tree_view_new ();
3628 gtk_style_context_add_class (gtk_widget_get_style_context (impl->browse_shortcuts_tree_view),
3629 GTK_STYLE_CLASS_SIDEBAR);
3630 gtk_tree_view_set_enable_search (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), FALSE);
3631 #ifdef PROFILE_FILE_CHOOSER
3632 g_object_set_data (G_OBJECT (impl->browse_shortcuts_tree_view), "fmq-name", "shortcuts");
3635 /* Connect "after" to key-press-event on the shortcuts pane. We want this action to be possible:
3637 * 1. user brings up a SAVE dialog
3638 * 2. user clicks on a shortcut in the shortcuts pane
3639 * 3. user starts typing a filename
3641 * Normally, the user's typing would be ignored, as the shortcuts treeview doesn't
3642 * support interactive search. However, we'd rather focus the location entry
3643 * so that the user can type *there*.
3645 * To preserve keyboard navigation in the shortcuts pane, we don't focus the
3646 * filename entry if one clicks on a shortcut; rather, we focus the entry only
3647 * if the user starts typing while the focus is in the shortcuts pane.
3649 g_signal_connect_after (impl->browse_shortcuts_tree_view, "key-press-event",
3650 G_CALLBACK (shortcuts_key_press_event_after_cb), impl);
3652 g_signal_connect (impl->browse_shortcuts_tree_view, "popup-menu",
3653 G_CALLBACK (shortcuts_popup_menu_cb), impl);
3654 g_signal_connect (impl->browse_shortcuts_tree_view, "button-press-event",
3655 G_CALLBACK (shortcuts_button_press_event_cb), impl);
3656 /* Accessible object name for the file chooser's shortcuts pane */
3657 atk_object_set_name (gtk_widget_get_accessible (impl->browse_shortcuts_tree_view), _("Places"));
3659 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), impl->shortcuts_pane_filter_model);
3661 gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3663 tree_model_row_targets,
3664 G_N_ELEMENTS (tree_model_row_targets),
3667 gtk_drag_dest_set (impl->browse_shortcuts_tree_view,
3668 GTK_DEST_DEFAULT_ALL,
3669 tree_model_row_targets,
3670 G_N_ELEMENTS (tree_model_row_targets),
3671 GDK_ACTION_COPY | GDK_ACTION_MOVE);
3672 gtk_drag_dest_add_uri_targets (impl->browse_shortcuts_tree_view);
3674 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
3675 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
3676 gtk_tree_selection_set_select_function (selection,
3677 shortcuts_select_func,
3680 g_signal_connect (selection, "changed",
3681 G_CALLBACK (shortcuts_selection_changed_cb), impl);
3683 g_signal_connect (impl->browse_shortcuts_tree_view, "key-press-event",
3684 G_CALLBACK (shortcuts_key_press_event_cb), impl);
3686 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-begin",
3687 G_CALLBACK (shortcuts_drag_begin_cb), impl);
3688 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-end",
3689 G_CALLBACK (shortcuts_drag_end_cb), impl);
3690 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-delete",
3691 G_CALLBACK (shortcuts_drag_data_delete_cb), impl);
3693 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-leave",
3694 G_CALLBACK (shortcuts_drag_leave_cb), impl);
3695 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-motion",
3696 G_CALLBACK (shortcuts_drag_motion_cb), impl);
3697 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-drop",
3698 G_CALLBACK (shortcuts_drag_drop_cb), impl);
3699 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-received",
3700 G_CALLBACK (shortcuts_drag_data_received_cb), impl);
3702 /* Support tooltips */
3703 gtk_widget_set_has_tooltip (impl->browse_shortcuts_tree_view, TRUE);
3704 g_signal_connect (impl->browse_shortcuts_tree_view, "query-tooltip",
3705 G_CALLBACK (shortcuts_query_tooltip_cb), impl);
3707 gtk_container_add (GTK_CONTAINER (swin), impl->browse_shortcuts_tree_view);
3708 gtk_widget_show (impl->browse_shortcuts_tree_view);
3712 column = gtk_tree_view_column_new ();
3713 /* Column header for the file chooser's shortcuts pane */
3714 gtk_tree_view_column_set_title (column, _("_Places"));
3716 renderer = gtk_cell_renderer_pixbuf_new ();
3717 gtk_tree_view_column_pack_start (column, renderer, FALSE);
3718 gtk_tree_view_column_set_attributes (column, renderer,
3719 "pixbuf", SHORTCUTS_COL_PIXBUF,
3720 "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
3723 renderer = gtk_cell_renderer_text_new ();
3724 g_object_set (renderer,
3726 "ellipsize", PANGO_ELLIPSIZE_END,
3728 g_signal_connect (renderer, "edited",
3729 G_CALLBACK (shortcuts_edited), impl);
3730 g_signal_connect (renderer, "editing-canceled",
3731 G_CALLBACK (shortcuts_editing_canceled), impl);
3732 gtk_tree_view_column_pack_start (column, renderer, TRUE);
3733 gtk_tree_view_column_set_attributes (column, renderer,
3734 "text", SHORTCUTS_COL_NAME,
3737 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3738 shortcuts_row_separator_func,
3741 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), column);
3746 /* Creates the widgets for the shortcuts/bookmarks pane */
3748 shortcuts_pane_create (GtkFileChooserDefault *impl,
3749 GtkSizeGroup *size_group)
3754 GtkStyleContext *context;
3757 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3758 gtk_widget_show (vbox);
3760 /* Shortcuts tree */
3762 widget = shortcuts_list_create (impl);
3764 gtk_size_group_add_widget (size_group, widget);
3765 context = gtk_widget_get_style_context (widget);
3766 gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
3768 gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
3770 /* Box for buttons */
3772 toolbar = gtk_toolbar_new ();
3773 gtk_toolbar_set_icon_size (GTK_TOOLBAR (toolbar), GTK_ICON_SIZE_MENU);
3775 context = gtk_widget_get_style_context (toolbar);
3776 gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);
3777 gtk_style_context_add_class (context, GTK_STYLE_CLASS_INLINE_TOOLBAR);
3779 gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0);
3780 gtk_widget_show (toolbar);
3782 /* Add bookmark button */
3783 icon = g_themed_icon_new_with_default_fallbacks ("list-add-symbolic");
3784 impl->browse_shortcuts_add_button = toolbutton_new (impl,
3788 G_CALLBACK (add_bookmark_button_clicked_cb));
3789 g_object_unref (icon);
3791 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (impl->browse_shortcuts_add_button), 0);
3792 gtk_widget_set_tooltip_text (impl->browse_shortcuts_add_button,
3793 _("Add the selected folder to the Bookmarks"));
3795 /* Remove bookmark button */
3796 icon = g_themed_icon_new_with_default_fallbacks ("list-remove-symbolic");
3797 impl->browse_shortcuts_remove_button = toolbutton_new (impl,
3801 G_CALLBACK (remove_bookmark_button_clicked_cb));
3802 g_object_unref (icon);
3803 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (impl->browse_shortcuts_remove_button), 1);
3804 gtk_widget_set_tooltip_text (impl->browse_shortcuts_remove_button,
3805 _("Remove the selected bookmark"));
3811 key_is_left_or_right (GdkEventKey *event)
3815 modifiers = gtk_accelerator_get_default_mod_mask ();
3817 return ((event->keyval == GDK_KEY_Right
3818 || event->keyval == GDK_KEY_KP_Right
3819 || event->keyval == GDK_KEY_Left
3820 || event->keyval == GDK_KEY_KP_Left)
3821 && (event->state & modifiers) == 0);
3824 /* Handles key press events on the file list, so that we can trap Enter to
3825 * activate the default button on our own. Also, checks to see if '/' has been
3826 * pressed. See comment by tree_view_keybinding_cb() for more details.
3829 browse_files_key_press_event_cb (GtkWidget *widget,
3833 GtkFileChooserDefault *impl;
3836 impl = (GtkFileChooserDefault *) data;
3838 modifiers = gtk_accelerator_get_default_mod_mask ();
3840 if ((event->keyval == GDK_KEY_slash
3841 || event->keyval == GDK_KEY_KP_Divide
3843 || event->keyval == GDK_KEY_asciitilde
3845 ) && ! (event->state & (~GDK_SHIFT_MASK & modifiers)))
3847 location_popup_handler (impl, event->string);
3851 if (key_is_left_or_right (event))
3853 gtk_widget_grab_focus (impl->browse_shortcuts_tree_view);
3857 if ((event->keyval == GDK_KEY_Return
3858 || event->keyval == GDK_KEY_ISO_Enter
3859 || event->keyval == GDK_KEY_KP_Enter
3860 || event->keyval == GDK_KEY_space
3861 || event->keyval == GDK_KEY_KP_Space)
3862 && ((event->state & modifiers) == 0)
3863 && !(impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
3864 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
3868 window = get_toplevel (widget);
3871 GtkWidget *default_widget, *focus_widget;
3873 default_widget = gtk_window_get_default_widget (window);
3874 focus_widget = gtk_window_get_focus (window);
3876 if (widget != default_widget &&
3877 !(widget == focus_widget && (!default_widget || !gtk_widget_get_sensitive (default_widget))))
3879 gtk_window_activate_default (window);
3889 /* Callback used when the file list's popup menu is detached */
3891 popup_menu_detach_cb (GtkWidget *attach_widget,
3894 GtkFileChooserDefault *impl;
3896 impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
3897 g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
3899 impl->browse_files_popup_menu = NULL;
3900 impl->browse_files_popup_menu_add_shortcut_item = NULL;
3901 impl->browse_files_popup_menu_hidden_files_item = NULL;
3904 /* Callback used when the "Add to Bookmarks" menu item is activated */
3906 add_to_shortcuts_cb (GtkMenuItem *item,
3907 GtkFileChooserDefault *impl)
3909 bookmarks_add_selected_folder (impl);
3912 /* Callback used when the "Show Hidden Files" menu item is toggled */
3914 show_hidden_toggled_cb (GtkCheckMenuItem *item,
3915 GtkFileChooserDefault *impl)
3918 "show-hidden", gtk_check_menu_item_get_active (item),
3922 /* Callback used when the "Show Size Column" menu item is toggled */
3924 show_size_column_toggled_cb (GtkCheckMenuItem *item,
3925 GtkFileChooserDefault *impl)
3927 impl->show_size_column = gtk_check_menu_item_get_active (item);
3929 gtk_tree_view_column_set_visible (impl->list_size_column,
3930 impl->show_size_column);
3933 /* Shows an error dialog about not being able to select a dragged file */
3935 error_selecting_dragged_file_dialog (GtkFileChooserDefault *impl,
3940 _("Could not select file"),
3945 file_list_drag_data_select_uris (GtkFileChooserDefault *impl,
3950 GtkFileChooser *chooser = GTK_FILE_CHOOSER (impl);
3952 for (i = 1; uris[i]; i++)
3955 GError *error = NULL;
3958 file = g_file_new_for_uri (uri);
3960 gtk_file_chooser_default_select_file (chooser, file, &error);
3962 error_selecting_dragged_file_dialog (impl, file, error);
3964 g_object_unref (file);
3968 struct FileListDragData
3970 GtkFileChooserDefault *impl;
3976 file_list_drag_data_received_get_info_cb (GCancellable *cancellable,
3978 const GError *error,
3981 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
3982 struct FileListDragData *data = user_data;
3983 GtkFileChooser *chooser = GTK_FILE_CHOOSER (data->impl);
3985 if (cancellable != data->impl->file_list_drag_data_received_cancellable)
3988 data->impl->file_list_drag_data_received_cancellable = NULL;
3990 if (cancelled || error)
3993 if ((data->impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
3994 data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) &&
3995 data->uris[1] == 0 && !error && _gtk_file_info_consider_as_directory (info))
3996 change_folder_and_display_error (data->impl, data->file, FALSE);
3999 GError *error = NULL;
4001 gtk_file_chooser_default_unselect_all (chooser);
4002 gtk_file_chooser_default_select_file (chooser, data->file, &error);
4004 error_selecting_dragged_file_dialog (data->impl, data->file, error);
4006 browse_files_center_selected_row (data->impl);
4009 if (data->impl->select_multiple)
4010 file_list_drag_data_select_uris (data->impl, data->uris);
4013 g_object_unref (data->impl);
4014 g_strfreev (data->uris);
4015 g_object_unref (data->file);
4018 g_object_unref (cancellable);
4022 file_list_drag_data_received_cb (GtkWidget *widget,
4023 GdkDragContext *context,
4026 GtkSelectionData *selection_data,
4031 GtkFileChooserDefault *impl;
4036 impl = GTK_FILE_CHOOSER_DEFAULT (data);
4038 /* Allow only drags from other widgets; see bug #533891. */
4039 if (gtk_drag_get_source_widget (context) == widget)
4041 g_signal_stop_emission_by_name (widget, "drag-data-received");
4045 /* Parse the text/uri-list string, navigate to the first one */
4046 uris = gtk_selection_data_get_uris (selection_data);
4047 if (uris && uris[0])
4049 struct FileListDragData *data;
4052 file = g_file_new_for_uri (uri);
4054 data = g_new0 (struct FileListDragData, 1);
4055 data->impl = g_object_ref (impl);
4059 if (impl->file_list_drag_data_received_cancellable)
4060 g_cancellable_cancel (impl->file_list_drag_data_received_cancellable);
4062 impl->file_list_drag_data_received_cancellable =
4063 _gtk_file_system_get_info (impl->file_system, file,
4065 file_list_drag_data_received_get_info_cb,
4069 g_signal_stop_emission_by_name (widget, "drag-data-received");
4072 /* Don't do anything with the drag_drop signal */
4074 file_list_drag_drop_cb (GtkWidget *widget,
4075 GdkDragContext *context,
4079 GtkFileChooserDefault *impl)
4081 g_signal_stop_emission_by_name (widget, "drag-drop");
4085 /* Disable the normal tree drag motion handler, it makes it look like you're
4086 dropping the dragged item onto a tree item */
4088 file_list_drag_motion_cb (GtkWidget *widget,
4089 GdkDragContext *context,
4093 GtkFileChooserDefault *impl)
4095 g_signal_stop_emission_by_name (widget, "drag-motion");
4099 /* Constructs the popup menu for the file list if needed */
4101 file_list_build_popup_menu (GtkFileChooserDefault *impl)
4105 if (impl->browse_files_popup_menu)
4108 impl->browse_files_popup_menu = gtk_menu_new ();
4109 gtk_menu_attach_to_widget (GTK_MENU (impl->browse_files_popup_menu),
4110 impl->browse_files_tree_view,
4111 popup_menu_detach_cb);
4113 item = gtk_image_menu_item_new_with_mnemonic (_("_Add to Bookmarks"));
4114 impl->browse_files_popup_menu_add_shortcut_item = item;
4115 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
4116 gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU));
4117 g_signal_connect (item, "activate",
4118 G_CALLBACK (add_to_shortcuts_cb), impl);
4119 gtk_widget_show (item);
4120 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4122 item = gtk_separator_menu_item_new ();
4123 gtk_widget_show (item);
4124 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4126 item = gtk_check_menu_item_new_with_mnemonic (_("Show _Hidden Files"));
4127 impl->browse_files_popup_menu_hidden_files_item = item;
4128 g_signal_connect (item, "toggled",
4129 G_CALLBACK (show_hidden_toggled_cb), impl);
4130 gtk_widget_show (item);
4131 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4133 item = gtk_check_menu_item_new_with_mnemonic (_("Show _Size Column"));
4134 impl->browse_files_popup_menu_size_column_item = item;
4135 g_signal_connect (item, "toggled",
4136 G_CALLBACK (show_size_column_toggled_cb), impl);
4137 gtk_widget_show (item);
4138 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4140 bookmarks_check_add_sensitivity (impl);
4143 /* Updates the popup menu for the file list, creating it if necessary */
4145 file_list_update_popup_menu (GtkFileChooserDefault *impl)
4147 file_list_build_popup_menu (impl);
4149 /* FIXME - handle OPERATION_MODE_SEARCH and OPERATION_MODE_RECENT */
4151 /* The sensitivity of the Add to Bookmarks item is set in
4152 * bookmarks_check_add_sensitivity()
4155 /* 'Show Hidden Files' */
4156 g_signal_handlers_block_by_func (impl->browse_files_popup_menu_hidden_files_item,
4157 G_CALLBACK (show_hidden_toggled_cb), impl);
4158 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (impl->browse_files_popup_menu_hidden_files_item),
4160 g_signal_handlers_unblock_by_func (impl->browse_files_popup_menu_hidden_files_item,
4161 G_CALLBACK (show_hidden_toggled_cb), impl);
4163 /* 'Show Size Column' */
4164 g_signal_handlers_block_by_func (impl->browse_files_popup_menu_size_column_item,
4165 G_CALLBACK (show_size_column_toggled_cb), impl);
4166 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (impl->browse_files_popup_menu_size_column_item),
4167 impl->show_size_column);
4168 g_signal_handlers_unblock_by_func (impl->browse_files_popup_menu_size_column_item,
4169 G_CALLBACK (show_size_column_toggled_cb), impl);
4173 popup_position_func (GtkMenu *menu,
4179 GtkAllocation allocation;
4180 GtkWidget *widget = GTK_WIDGET (user_data);
4181 GdkScreen *screen = gtk_widget_get_screen (widget);
4184 GdkRectangle monitor;
4186 g_return_if_fail (gtk_widget_get_realized (widget));
4188 gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
4190 gtk_widget_get_preferred_size (GTK_WIDGET (menu),
4193 gtk_widget_get_allocation (widget, &allocation);
4194 *x += (allocation.width - req.width) / 2;
4195 *y += (allocation.height - req.height) / 2;
4197 monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
4198 gtk_menu_set_monitor (menu, monitor_num);
4199 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
4201 *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
4202 *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
4208 file_list_popup_menu (GtkFileChooserDefault *impl,
4209 GdkEventButton *event)
4211 file_list_update_popup_menu (impl);
4213 gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
4214 NULL, NULL, NULL, NULL,
4215 event->button, event->time);
4218 gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
4220 popup_position_func, impl->browse_files_tree_view,
4221 0, GDK_CURRENT_TIME);
4222 gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->browse_files_popup_menu),
4228 /* Callback used for the GtkWidget::popup-menu signal of the file list */
4230 list_popup_menu_cb (GtkWidget *widget,
4231 GtkFileChooserDefault *impl)
4233 file_list_popup_menu (impl, NULL);
4237 /* Callback used when a button is pressed on the file list. We trap button 3 to
4238 * bring up a popup menu.
4241 list_button_press_event_cb (GtkWidget *widget,
4242 GdkEventButton *event,
4243 GtkFileChooserDefault *impl)
4245 static gboolean in_press = FALSE;
4250 if (event->button != 3)
4254 gtk_widget_event (impl->browse_files_tree_view, (GdkEvent *) event);
4257 file_list_popup_menu (impl, event);
4262 OperationMode operation_mode;
4263 gint general_column;
4267 /* Sets the sort column IDs for the file list based on the operation mode */
4269 file_list_set_sort_column_ids (GtkFileChooserDefault *impl)
4271 gtk_tree_view_column_set_sort_column_id (impl->list_name_column, MODEL_COL_NAME);
4272 gtk_tree_view_column_set_sort_column_id (impl->list_mtime_column, MODEL_COL_MTIME);
4273 gtk_tree_view_column_set_sort_column_id (impl->list_size_column, MODEL_COL_SIZE);
4277 file_list_query_tooltip_cb (GtkWidget *widget,
4280 gboolean keyboard_tip,
4281 GtkTooltip *tooltip,
4284 GtkFileChooserDefault *impl = user_data;
4285 GtkTreeModel *model;
4291 if (impl->operation_mode == OPERATION_MODE_BROWSE)
4295 if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (impl->browse_files_tree_view),
4298 &model, &path, &iter))
4301 gtk_tree_model_get (model, &iter,
4302 MODEL_COL_FILE, &file,
4307 gtk_tree_path_free (path);
4311 filename = g_file_get_path (file);
4312 gtk_tooltip_set_text (tooltip, filename);
4313 gtk_tree_view_set_tooltip_row (GTK_TREE_VIEW (impl->browse_files_tree_view),
4318 g_object_unref (file);
4319 gtk_tree_path_free (path);
4325 set_icon_cell_renderer_fixed_size (GtkFileChooserDefault *impl, GtkCellRenderer *renderer)
4329 gtk_cell_renderer_get_padding (renderer, &xpad, &ypad);
4330 gtk_cell_renderer_set_fixed_size (renderer,
4331 xpad * 2 + impl->icon_size,
4332 ypad * 2 + impl->icon_size);
4335 /* Creates the widgets for the file list */
4337 create_file_list (GtkFileChooserDefault *impl)
4340 GtkTreeSelection *selection;
4341 GtkTreeViewColumn *column;
4342 GtkCellRenderer *renderer;
4344 /* Scrolled window */
4345 swin = gtk_scrolled_window_new (NULL, NULL);
4346 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
4347 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
4348 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
4351 /* Tree/list view */
4353 impl->browse_files_tree_view = gtk_tree_view_new ();
4354 #ifdef PROFILE_FILE_CHOOSER
4355 g_object_set_data (G_OBJECT (impl->browse_files_tree_view), "fmq-name", "file_list");
4357 g_object_set_data (G_OBJECT (impl->browse_files_tree_view), I_("GtkFileChooserDefault"), impl);
4358 atk_object_set_name (gtk_widget_get_accessible (impl->browse_files_tree_view), _("Files"));
4360 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->browse_files_tree_view), TRUE);
4361 gtk_container_add (GTK_CONTAINER (swin), impl->browse_files_tree_view);
4363 gtk_drag_dest_set (impl->browse_files_tree_view,
4364 GTK_DEST_DEFAULT_ALL,
4366 GDK_ACTION_COPY | GDK_ACTION_MOVE);
4367 gtk_drag_dest_add_uri_targets (impl->browse_files_tree_view);
4369 g_signal_connect (impl->browse_files_tree_view, "row-activated",
4370 G_CALLBACK (list_row_activated), impl);
4371 g_signal_connect (impl->browse_files_tree_view, "key-press-event",
4372 G_CALLBACK (browse_files_key_press_event_cb), impl);
4373 g_signal_connect (impl->browse_files_tree_view, "popup-menu",
4374 G_CALLBACK (list_popup_menu_cb), impl);
4375 g_signal_connect (impl->browse_files_tree_view, "button-press-event",
4376 G_CALLBACK (list_button_press_event_cb), impl);
4378 g_signal_connect (impl->browse_files_tree_view, "drag-data-received",
4379 G_CALLBACK (file_list_drag_data_received_cb), impl);
4380 g_signal_connect (impl->browse_files_tree_view, "drag-drop",
4381 G_CALLBACK (file_list_drag_drop_cb), impl);
4382 g_signal_connect (impl->browse_files_tree_view, "drag-motion",
4383 G_CALLBACK (file_list_drag_motion_cb), impl);
4385 g_object_set (impl->browse_files_tree_view, "has-tooltip", TRUE, NULL);
4386 g_signal_connect (impl->browse_files_tree_view, "query-tooltip",
4387 G_CALLBACK (file_list_query_tooltip_cb), impl);
4389 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4390 gtk_tree_selection_set_select_function (selection,
4393 gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_files_tree_view),
4396 GDK_ACTION_COPY | GDK_ACTION_MOVE);
4397 gtk_drag_source_add_uri_targets (impl->browse_files_tree_view);
4399 g_signal_connect (selection, "changed",
4400 G_CALLBACK (list_selection_changed), impl);
4402 /* Keep the column order in sync with update_cell_renderer_attributes() */
4404 /* Filename column */
4406 impl->list_name_column = gtk_tree_view_column_new ();
4407 gtk_tree_view_column_set_expand (impl->list_name_column, TRUE);
4408 gtk_tree_view_column_set_resizable (impl->list_name_column, TRUE);
4409 gtk_tree_view_column_set_title (impl->list_name_column, _("Name"));
4411 renderer = gtk_cell_renderer_pixbuf_new ();
4412 /* We set a fixed size so that we get an empty slot even if no icons are loaded yet */
4413 set_icon_cell_renderer_fixed_size (impl, renderer);
4414 gtk_tree_view_column_pack_start (impl->list_name_column, renderer, FALSE);
4416 impl->list_name_renderer = gtk_cell_renderer_text_new ();
4417 g_object_set (impl->list_name_renderer,
4418 "ellipsize", PANGO_ELLIPSIZE_END,
4420 g_signal_connect (impl->list_name_renderer, "edited",
4421 G_CALLBACK (renderer_edited_cb), impl);
4422 g_signal_connect (impl->list_name_renderer, "editing-canceled",
4423 G_CALLBACK (renderer_editing_canceled_cb), impl);
4424 gtk_tree_view_column_pack_start (impl->list_name_column, impl->list_name_renderer, TRUE);
4426 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), impl->list_name_column);
4430 column = gtk_tree_view_column_new ();
4431 gtk_tree_view_column_set_resizable (column, TRUE);
4432 gtk_tree_view_column_set_title (column, _("Size"));
4434 renderer = gtk_cell_renderer_text_new ();
4435 g_object_set (renderer,
4436 "alignment", PANGO_ALIGN_RIGHT,
4438 gtk_tree_view_column_pack_start (column, renderer, TRUE); /* bug: it doesn't expand */
4439 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
4440 impl->list_size_column = column;
4442 /* Modification time column */
4444 column = gtk_tree_view_column_new ();
4445 gtk_tree_view_column_set_resizable (column, TRUE);
4446 gtk_tree_view_column_set_title (column, _("Modified"));
4448 renderer = gtk_cell_renderer_text_new ();
4449 gtk_tree_view_column_pack_start (column, renderer, TRUE);
4450 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
4451 impl->list_mtime_column = column;
4453 file_list_set_sort_column_ids (impl);
4454 update_cell_renderer_attributes (impl);
4456 gtk_widget_show_all (swin);
4462 create_path_bar (GtkFileChooserDefault *impl)
4464 GtkWidget *path_bar;
4466 path_bar = g_object_new (GTK_TYPE_PATH_BAR, NULL);
4467 _gtk_path_bar_set_file_system (GTK_PATH_BAR (path_bar), impl->file_system);
4472 /* Creates the widgets for the files/folders pane */
4474 file_pane_create (GtkFileChooserDefault *impl,
4475 GtkSizeGroup *size_group)
4481 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
4482 gtk_widget_show (vbox);
4484 /* Box for lists and preview */
4486 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, PREVIEW_HBOX_SPACING);
4487 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
4488 gtk_widget_show (hbox);
4492 widget = create_file_list (impl);
4493 gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
4494 gtk_size_group_add_widget (size_group, widget);
4498 impl->preview_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
4499 gtk_box_pack_start (GTK_BOX (hbox), impl->preview_box, FALSE, FALSE, 0);
4500 /* Don't show preview box initially */
4504 impl->filter_combo_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
4506 widget = filter_create (impl);
4508 gtk_widget_show (widget);
4509 gtk_box_pack_end (GTK_BOX (impl->filter_combo_hbox), widget, FALSE, FALSE, 0);
4511 gtk_box_pack_end (GTK_BOX (vbox), impl->filter_combo_hbox, FALSE, FALSE, 0);
4516 /* Callback used when the "Browse for more folders" expander is toggled */
4518 expander_changed_cb (GtkExpander *expander,
4520 GtkFileChooserDefault *impl)
4522 impl->expand_folders = gtk_expander_get_expanded(GTK_EXPANDER (impl->save_expander));
4523 update_appearance (impl);
4526 /* Callback used when the selection changes in the save folder combo box */
4528 save_folder_combo_changed_cb (GtkComboBox *combo,
4529 GtkFileChooserDefault *impl)
4533 if (impl->changing_folder)
4536 if (gtk_combo_box_get_active_iter (combo, &iter))
4538 GtkTreeIter child_iter;
4540 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model),
4543 shortcuts_activate_iter (impl, &child_iter);
4548 save_folder_update_tooltip (GtkComboBox *combo,
4549 GtkFileChooserDefault *impl)
4556 if (gtk_combo_box_get_active_iter (combo, &iter))
4558 GtkTreeIter child_iter;
4560 ShortcutType shortcut_type;
4562 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model),
4565 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &child_iter,
4566 SHORTCUTS_COL_DATA, &col_data,
4567 SHORTCUTS_COL_TYPE, &shortcut_type,
4570 if (shortcut_type == SHORTCUT_TYPE_FILE)
4571 tooltip = g_file_get_parse_name (G_FILE (col_data));
4574 gtk_widget_set_tooltip_text (GTK_WIDGET (combo), tooltip);
4575 gtk_widget_set_has_tooltip (GTK_WIDGET (combo),
4576 gtk_widget_get_sensitive (GTK_WIDGET (combo)));
4580 /* Filter function used to filter out the Search item and its separator.
4581 * Used for the "Save in folder" combo box, so that these items do not appear in it.
4584 shortcuts_combo_filter_func (GtkTreeModel *model,
4588 GtkFileChooserDefault *impl;
4589 GtkTreePath *tree_path;
4594 impl = GTK_FILE_CHOOSER_DEFAULT (data);
4596 g_assert (model == GTK_TREE_MODEL (impl->shortcuts_model));
4598 tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), iter);
4599 g_assert (tree_path != NULL);
4601 indices = gtk_tree_path_get_indices (tree_path);
4605 if (impl->has_search)
4607 idx = shortcuts_get_index (impl, SHORTCUTS_SEARCH);
4608 if (idx == indices[0])
4612 if (impl->has_recent)
4614 idx = shortcuts_get_index (impl, SHORTCUTS_RECENT);
4615 if (idx == indices[0])
4619 idx = shortcuts_get_index (impl, SHORTCUTS_RECENT_SEPARATOR);
4620 if (idx == indices[0])
4625 gtk_tree_path_free (tree_path);
4630 /* Creates the combo box with the save folders */
4632 save_folder_combo_create (GtkFileChooserDefault *impl)
4635 GtkCellRenderer *cell;
4637 impl->shortcuts_combo_filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (impl->shortcuts_model), NULL);
4638 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model),
4639 shortcuts_combo_filter_func,
4643 combo = g_object_new (GTK_TYPE_COMBO_BOX,
4644 "model", impl->shortcuts_combo_filter_model,
4645 "focus-on-click", FALSE,
4647 gtk_widget_show (combo);
4649 cell = gtk_cell_renderer_pixbuf_new ();
4650 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE);
4651 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
4652 "pixbuf", SHORTCUTS_COL_PIXBUF,
4653 "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
4654 "sensitive", SHORTCUTS_COL_PIXBUF_VISIBLE,
4657 cell = gtk_cell_renderer_text_new ();
4658 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
4659 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
4660 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
4661 "text", SHORTCUTS_COL_NAME,
4662 "sensitive", SHORTCUTS_COL_PIXBUF_VISIBLE,
4665 gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo),
4666 shortcuts_row_separator_func,
4669 g_signal_connect (combo, "changed",
4670 G_CALLBACK (save_folder_combo_changed_cb), impl);
4671 g_signal_connect (combo, "changed",
4672 G_CALLBACK (save_folder_update_tooltip), impl);
4677 /* Creates the widgets specific to Save mode */
4679 save_widgets_create (GtkFileChooserDefault *impl)
4684 GtkWidget *alignment;
4686 if (impl->save_widgets != NULL)
4689 location_switch_to_path_bar (impl);
4691 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
4693 table = gtk_table_new (2, 2, FALSE);
4694 gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
4695 gtk_widget_show (table);
4696 gtk_table_set_row_spacings (GTK_TABLE (table), 12);
4697 gtk_table_set_col_spacings (GTK_TABLE (table), 12);
4701 widget = gtk_label_new_with_mnemonic (_("_Name:"));
4702 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
4703 gtk_table_attach (GTK_TABLE (table), widget,
4707 gtk_widget_show (widget);
4709 /* Location entry */
4711 impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
4712 _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
4714 _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->local_only);
4715 gtk_entry_set_width_chars (GTK_ENTRY (impl->location_entry), 45);
4716 gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
4717 gtk_table_attach (GTK_TABLE (table), impl->location_entry,
4719 GTK_EXPAND | GTK_FILL, 0,
4721 gtk_widget_show (impl->location_entry);
4722 gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->location_entry);
4725 impl->save_folder_label = gtk_label_new (NULL);
4726 gtk_misc_set_alignment (GTK_MISC (impl->save_folder_label), 0.0, 0.5);
4727 gtk_table_attach (GTK_TABLE (table), impl->save_folder_label,
4731 gtk_widget_show (impl->save_folder_label);
4733 impl->save_folder_combo = save_folder_combo_create (impl);
4734 gtk_table_attach (GTK_TABLE (table), impl->save_folder_combo,
4736 GTK_EXPAND | GTK_FILL, GTK_FILL,
4738 gtk_label_set_mnemonic_widget (GTK_LABEL (impl->save_folder_label), impl->save_folder_combo);
4741 alignment = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
4742 gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
4744 impl->save_expander = gtk_expander_new_with_mnemonic (_("_Browse for other folders"));
4745 gtk_container_add (GTK_CONTAINER (alignment), impl->save_expander);
4746 g_signal_connect (impl->save_expander, "notify::expanded",
4747 G_CALLBACK (expander_changed_cb),
4749 gtk_widget_show_all (alignment);
4751 impl->save_widgets = vbox;
4752 gtk_box_pack_start (GTK_BOX (impl), impl->save_widgets, FALSE, FALSE, 0);
4753 gtk_box_reorder_child (GTK_BOX (impl), impl->save_widgets, 0);
4754 gtk_widget_show (impl->save_widgets);
4757 /* Destroys the widgets specific to Save mode */
4759 save_widgets_destroy (GtkFileChooserDefault *impl)
4761 if (impl->save_widgets == NULL)
4764 gtk_widget_destroy (impl->save_widgets);
4765 impl->save_widgets = NULL;
4766 impl->location_entry = NULL;
4767 impl->save_folder_label = NULL;
4768 impl->save_folder_combo = NULL;
4769 impl->save_expander = NULL;
4772 /* Turns on the path bar widget. Can be called even if we are already in that
4776 location_switch_to_path_bar (GtkFileChooserDefault *impl)
4778 if (impl->location_entry)
4780 gtk_widget_destroy (impl->location_entry);
4781 impl->location_entry = NULL;
4784 gtk_widget_hide (impl->location_entry_box);
4787 /* Sets the full path of the current folder as the text in the location entry. */
4789 location_entry_set_initial_text (GtkFileChooserDefault *impl)
4791 gchar *text, *filename;
4793 if (!impl->current_folder)
4796 filename = g_file_get_path (impl->current_folder);
4800 text = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
4804 text = g_file_get_uri (impl->current_folder);
4808 gboolean need_slash;
4811 len = strlen (text);
4812 need_slash = (text[len - 1] != G_DIR_SEPARATOR);
4818 slash_text = g_new (char, len + 2);
4819 strcpy (slash_text, text);
4820 slash_text[len] = G_DIR_SEPARATOR;
4821 slash_text[len + 1] = 0;
4827 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), text);
4834 /* Turns on the location entry. Can be called even if we are already in that
4838 location_switch_to_filename_entry (GtkFileChooserDefault *impl)
4840 /* when in search or recent files mode, we are not showing the
4841 * location_entry_box container, so there's no point in switching
4844 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
4845 impl->operation_mode == OPERATION_MODE_RECENT)
4848 if (impl->location_entry)
4849 gtk_widget_destroy (impl->location_entry);
4853 gtk_widget_show (impl->location_entry_box);
4857 impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
4858 _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
4860 gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
4861 _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action);
4863 gtk_box_pack_start (GTK_BOX (impl->location_entry_box), impl->location_entry, TRUE, TRUE, 0);
4864 gtk_label_set_mnemonic_widget (GTK_LABEL (impl->location_label), impl->location_entry);
4866 /* Configure the entry */
4868 _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->current_folder);
4872 gtk_widget_show (impl->location_entry);
4873 gtk_widget_grab_focus (impl->location_entry);
4876 /* Sets a new location mode. set_buttons determines whether the toggle button
4877 * for the mode will also be changed.
4880 location_mode_set (GtkFileChooserDefault *impl,
4881 LocationMode new_mode,
4882 gboolean set_button)
4884 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
4885 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
4887 GtkWindow *toplevel;
4888 GtkWidget *current_focus;
4889 gboolean button_active;
4890 gboolean switch_to_file_list;
4894 case LOCATION_MODE_PATH_BAR:
4895 button_active = FALSE;
4897 /* The location_entry will disappear when we switch to path bar mode. So,
4898 * we'll focus the file list in that case, to avoid having a window with
4899 * no focused widget.
4901 toplevel = get_toplevel (GTK_WIDGET (impl));
4902 switch_to_file_list = FALSE;
4905 current_focus = gtk_window_get_focus (toplevel);
4906 if (!current_focus || current_focus == impl->location_entry)
4907 switch_to_file_list = TRUE;
4910 location_switch_to_path_bar (impl);
4912 if (switch_to_file_list)
4913 gtk_widget_grab_focus (impl->browse_files_tree_view);
4917 case LOCATION_MODE_FILENAME_ENTRY:
4918 button_active = TRUE;
4919 location_switch_to_filename_entry (impl);
4923 g_assert_not_reached ();
4929 g_signal_handlers_block_by_func (impl->location_button,
4930 G_CALLBACK (location_button_toggled_cb), impl);
4932 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (impl->location_button), button_active);
4934 g_signal_handlers_unblock_by_func (impl->location_button,
4935 G_CALLBACK (location_button_toggled_cb), impl);
4939 impl->location_mode = new_mode;
4943 location_toggle_popup_handler (GtkFileChooserDefault *impl)
4945 /* when in search or recent files mode, we are not showing the
4946 * location_entry_box container, so there's no point in switching
4949 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
4950 impl->operation_mode == OPERATION_MODE_RECENT)
4953 /* If the file entry is not visible, show it.
4954 * If it is visible, turn it off only if it is focused. Otherwise, switch to the entry.
4956 if (impl->location_mode == LOCATION_MODE_PATH_BAR)
4958 location_mode_set (impl, LOCATION_MODE_FILENAME_ENTRY, TRUE);
4960 else if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
4962 if (gtk_widget_has_focus (impl->location_entry))
4964 location_mode_set (impl, LOCATION_MODE_PATH_BAR, TRUE);
4968 gtk_widget_grab_focus (impl->location_entry);
4973 /* Callback used when one of the location mode buttons is toggled */
4975 location_button_toggled_cb (GtkToggleButton *toggle,
4976 GtkFileChooserDefault *impl)
4979 LocationMode new_mode;
4981 is_active = gtk_toggle_button_get_active (toggle);
4985 g_assert (impl->location_mode == LOCATION_MODE_PATH_BAR);
4986 new_mode = LOCATION_MODE_FILENAME_ENTRY;
4990 g_assert (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY);
4991 new_mode = LOCATION_MODE_PATH_BAR;
4994 location_mode_set (impl, new_mode, FALSE);
4997 /* Creates a toggle button for the location entry. */
4999 location_button_create (GtkFileChooserDefault *impl)
5004 image = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON);
5005 gtk_widget_show (image);
5007 impl->location_button = g_object_new (GTK_TYPE_TOGGLE_BUTTON,
5011 g_signal_connect (impl->location_button, "toggled",
5012 G_CALLBACK (location_button_toggled_cb), impl);
5014 str = _("Type a file name");
5016 gtk_widget_set_tooltip_text (impl->location_button, str);
5017 atk_object_set_name (gtk_widget_get_accessible (impl->location_button), str);
5020 /* Creates the main hpaned with the widgets shared by Open and Save mode */
5022 browse_widgets_create (GtkFileChooserDefault *impl)
5027 GtkSizeGroup *size_group;
5029 /* size group is used by the scrolled windows of the panes */
5030 size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
5031 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
5033 /* Location widgets */
5034 impl->browse_path_bar_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
5035 gtk_box_pack_start (GTK_BOX (vbox), impl->browse_path_bar_hbox, FALSE, FALSE, 0);
5036 gtk_widget_show (impl->browse_path_bar_hbox);
5038 /* Size group that allows the path bar to be the same size between modes */
5039 impl->browse_path_bar_size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
5040 gtk_size_group_set_ignore_hidden (impl->browse_path_bar_size_group, FALSE);
5042 /* Location button */
5044 location_button_create (impl);
5045 gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->location_button, FALSE, FALSE, 0);
5046 gtk_size_group_add_widget (impl->browse_path_bar_size_group, impl->location_button);
5050 impl->browse_path_bar = create_path_bar (impl);
5051 g_signal_connect (impl->browse_path_bar, "path-clicked", G_CALLBACK (path_bar_clicked), impl);
5052 gtk_widget_show_all (impl->browse_path_bar);
5053 gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->browse_path_bar, TRUE, TRUE, 0);
5054 gtk_size_group_add_widget (impl->browse_path_bar_size_group, impl->browse_path_bar);
5057 impl->browse_new_folder_button = gtk_button_new_with_mnemonic (_("Create Fo_lder"));
5058 g_signal_connect (impl->browse_new_folder_button, "clicked",
5059 G_CALLBACK (new_folder_button_clicked), impl);
5060 gtk_box_pack_end (GTK_BOX (impl->browse_path_bar_hbox), impl->browse_new_folder_button, FALSE, FALSE, 0);
5062 /* Box for the location label and entry */
5064 impl->location_entry_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
5065 gtk_box_pack_start (GTK_BOX (vbox), impl->location_entry_box, FALSE, FALSE, 0);
5067 impl->location_label = gtk_label_new_with_mnemonic (_("_Location:"));
5068 gtk_widget_show (impl->location_label);
5069 gtk_box_pack_start (GTK_BOX (impl->location_entry_box), impl->location_label, FALSE, FALSE, 0);
5073 hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
5074 gtk_widget_show (hpaned);
5075 gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
5077 widget = shortcuts_pane_create (impl, size_group);
5078 gtk_paned_pack1 (GTK_PANED (hpaned), widget, FALSE, FALSE);
5079 widget = file_pane_create (impl, size_group);
5080 gtk_paned_pack2 (GTK_PANED (hpaned), widget, TRUE, FALSE);
5081 gtk_paned_set_position (GTK_PANED (hpaned), 148);
5082 g_object_unref (size_group);
5088 gtk_file_chooser_default_constructor (GType type,
5089 guint n_construct_properties,
5090 GObjectConstructParam *construct_params)
5092 GtkFileChooserDefault *impl;
5095 profile_start ("start", NULL);
5097 object = G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->constructor (type,
5098 n_construct_properties,
5100 impl = GTK_FILE_CHOOSER_DEFAULT (object);
5102 g_assert (impl->file_system);
5104 gtk_widget_push_composite_child ();
5106 /* Shortcuts model */
5107 shortcuts_model_create (impl);
5109 /* The browse widgets */
5110 impl->browse_widgets = browse_widgets_create (impl);
5111 gtk_box_pack_start (GTK_BOX (impl), impl->browse_widgets, TRUE, TRUE, 0);
5113 /* Alignment to hold extra widget */
5114 impl->extra_align = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
5115 gtk_box_pack_start (GTK_BOX (impl), impl->extra_align, FALSE, FALSE, 0);
5117 gtk_widget_pop_composite_child ();
5118 update_appearance (impl);
5120 profile_end ("end", NULL);
5125 /* Sets the extra_widget by packing it in the appropriate place */
5127 set_extra_widget (GtkFileChooserDefault *impl,
5128 GtkWidget *extra_widget)
5132 g_object_ref (extra_widget);
5133 /* FIXME: is this right ? */
5134 gtk_widget_show (extra_widget);
5137 if (impl->extra_widget)
5139 gtk_container_remove (GTK_CONTAINER (impl->extra_align), impl->extra_widget);
5140 g_object_unref (impl->extra_widget);
5143 impl->extra_widget = extra_widget;
5144 if (impl->extra_widget)
5146 gtk_container_add (GTK_CONTAINER (impl->extra_align), impl->extra_widget);
5147 gtk_widget_show (impl->extra_align);
5150 gtk_widget_hide (impl->extra_align);
5154 set_local_only (GtkFileChooserDefault *impl,
5155 gboolean local_only)
5157 if (local_only != impl->local_only)
5159 impl->local_only = local_only;
5161 if (impl->location_entry)
5162 _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), local_only);
5164 if (impl->shortcuts_model && impl->file_system)
5166 shortcuts_add_volumes (impl);
5167 shortcuts_add_bookmarks (impl);
5170 if (local_only && impl->current_folder &&
5171 !g_file_is_native (impl->current_folder))
5173 /* If we are pointing to a non-local folder, make an effort to change
5174 * back to a local folder, but it's really up to the app to not cause
5175 * such a situation, so we ignore errors.
5177 const gchar *home = g_get_home_dir ();
5183 home_file = g_file_new_for_path (home);
5185 gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (impl), home_file, NULL);
5187 g_object_unref (home_file);
5193 volumes_bookmarks_changed_cb (GtkFileSystem *file_system,
5194 GtkFileChooserDefault *impl)
5196 shortcuts_add_volumes (impl);
5197 shortcuts_add_bookmarks (impl);
5199 bookmarks_check_add_sensitivity (impl);
5200 bookmarks_check_remove_sensitivity (impl);
5201 shortcuts_check_popup_sensitivity (impl);
5204 /* Sets the file chooser to multiple selection mode */
5206 set_select_multiple (GtkFileChooserDefault *impl,
5207 gboolean select_multiple,
5208 gboolean property_notify)
5210 GtkTreeSelection *selection;
5211 GtkSelectionMode mode;
5213 if (select_multiple == impl->select_multiple)
5216 mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE;
5218 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5219 gtk_tree_selection_set_mode (selection, mode);
5221 gtk_tree_view_set_rubber_banding (GTK_TREE_VIEW (impl->browse_files_tree_view), select_multiple);
5223 impl->select_multiple = select_multiple;
5224 g_object_notify (G_OBJECT (impl), "select-multiple");
5226 check_preview_change (impl);
5230 set_file_system_backend (GtkFileChooserDefault *impl)
5232 profile_start ("start for backend", "default");
5234 impl->file_system = _gtk_file_system_new ();
5236 g_signal_connect (impl->file_system, "volumes-changed",
5237 G_CALLBACK (volumes_bookmarks_changed_cb), impl);
5238 g_signal_connect (impl->file_system, "bookmarks-changed",
5239 G_CALLBACK (volumes_bookmarks_changed_cb), impl);
5241 profile_end ("end", NULL);
5245 unset_file_system_backend (GtkFileChooserDefault *impl)
5247 g_signal_handlers_disconnect_by_func (impl->file_system,
5248 G_CALLBACK (volumes_bookmarks_changed_cb), impl);
5250 g_object_unref (impl->file_system);
5252 impl->file_system = NULL;
5255 /* This function is basically a do_all function.
5257 * It sets the visibility on all the widgets based on the current state, and
5258 * moves the custom_widget if needed.
5261 update_appearance (GtkFileChooserDefault *impl)
5263 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
5264 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5268 gtk_widget_hide (impl->location_button);
5269 save_widgets_create (impl);
5271 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
5272 text = _("Save in _folder:");
5274 text = _("Create in _folder:");
5276 gtk_label_set_text_with_mnemonic (GTK_LABEL (impl->save_folder_label), text);
5278 if (gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
5280 gtk_widget_set_sensitive (impl->save_folder_label, FALSE);
5281 gtk_widget_set_sensitive (impl->save_folder_combo, FALSE);
5282 gtk_widget_set_has_tooltip (impl->save_folder_combo, FALSE);
5283 gtk_widget_show (impl->browse_widgets);
5287 gtk_widget_set_sensitive (impl->save_folder_label, TRUE);
5288 gtk_widget_set_sensitive (impl->save_folder_combo, TRUE);
5289 gtk_widget_set_has_tooltip (impl->save_folder_combo, TRUE);
5290 gtk_widget_hide (impl->browse_widgets);
5293 if (impl->select_multiple)
5295 g_warning ("Save mode cannot be set in conjunction with multiple selection mode. "
5296 "Re-setting to single selection mode.");
5297 set_select_multiple (impl, FALSE, TRUE);
5300 else if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
5301 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5303 gtk_widget_show (impl->location_button);
5304 save_widgets_destroy (impl);
5305 gtk_widget_show (impl->browse_widgets);
5306 location_mode_set (impl, impl->location_mode, TRUE);
5309 if (impl->location_entry)
5310 _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action);
5312 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN || !impl->create_folders)
5313 gtk_widget_hide (impl->browse_new_folder_button);
5315 gtk_widget_show (impl->browse_new_folder_button);
5317 /* This *is* needed; we need to redraw the file list because the "sensitivity"
5318 * of files may change depending whether we are in a file or folder-only mode.
5320 gtk_widget_queue_draw (impl->browse_files_tree_view);
5322 emit_default_size_changed (impl);
5326 gtk_file_chooser_default_set_property (GObject *object,
5328 const GValue *value,
5332 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
5336 case GTK_FILE_CHOOSER_PROP_ACTION:
5338 GtkFileChooserAction action = g_value_get_enum (value);
5340 if (action != impl->action)
5342 gtk_file_chooser_default_unselect_all (GTK_FILE_CHOOSER (impl));
5344 if ((action == GTK_FILE_CHOOSER_ACTION_SAVE ||
5345 action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5346 && impl->select_multiple)
5348 g_warning ("Tried to change the file chooser action to SAVE or CREATE_FOLDER, but "
5349 "this is not allowed in multiple selection mode. Resetting the file chooser "
5350 "to single selection mode.");
5351 set_select_multiple (impl, FALSE, TRUE);
5353 impl->action = action;
5354 update_cell_renderer_attributes (impl);
5355 update_appearance (impl);
5356 settings_load (impl);
5361 case GTK_FILE_CHOOSER_PROP_FILTER:
5362 set_current_filter (impl, g_value_get_object (value));
5365 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
5366 set_local_only (impl, g_value_get_boolean (value));
5369 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
5370 set_preview_widget (impl, g_value_get_object (value));
5373 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
5374 impl->preview_widget_active = g_value_get_boolean (value);
5375 update_preview_widget_visibility (impl);
5378 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
5379 impl->use_preview_label = g_value_get_boolean (value);
5380 update_preview_widget_visibility (impl);
5383 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
5384 set_extra_widget (impl, g_value_get_object (value));
5387 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
5389 gboolean select_multiple = g_value_get_boolean (value);
5390 if ((impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
5391 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5394 g_warning ("Tried to set the file chooser to multiple selection mode, but this is "
5395 "not allowed in SAVE or CREATE_FOLDER modes. Ignoring the change and "
5396 "leaving the file chooser in single selection mode.");
5400 set_select_multiple (impl, select_multiple, FALSE);
5404 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
5406 gboolean show_hidden = g_value_get_boolean (value);
5407 if (show_hidden != impl->show_hidden)
5409 impl->show_hidden = show_hidden;
5411 if (impl->browse_files_model)
5412 _gtk_file_system_model_set_show_hidden (impl->browse_files_model, show_hidden);
5417 case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
5419 gboolean do_overwrite_confirmation = g_value_get_boolean (value);
5420 impl->do_overwrite_confirmation = do_overwrite_confirmation;
5424 case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
5426 gboolean create_folders = g_value_get_boolean (value);
5427 impl->create_folders = create_folders;
5428 update_appearance (impl);
5433 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5439 gtk_file_chooser_default_get_property (GObject *object,
5444 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
5448 case GTK_FILE_CHOOSER_PROP_ACTION:
5449 g_value_set_enum (value, impl->action);
5452 case GTK_FILE_CHOOSER_PROP_FILTER:
5453 g_value_set_object (value, impl->current_filter);
5456 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
5457 g_value_set_boolean (value, impl->local_only);
5460 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
5461 g_value_set_object (value, impl->preview_widget);
5464 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
5465 g_value_set_boolean (value, impl->preview_widget_active);
5468 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
5469 g_value_set_boolean (value, impl->use_preview_label);
5472 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
5473 g_value_set_object (value, impl->extra_widget);
5476 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
5477 g_value_set_boolean (value, impl->select_multiple);
5480 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
5481 g_value_set_boolean (value, impl->show_hidden);
5484 case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
5485 g_value_set_boolean (value, impl->do_overwrite_confirmation);
5488 case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
5489 g_value_set_boolean (value, impl->create_folders);
5493 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5498 /* Removes the settings signal handler. It's safe to call multiple times */
5500 remove_settings_signal (GtkFileChooserDefault *impl,
5503 if (impl->settings_signal_id)
5505 GtkSettings *settings;
5507 settings = gtk_settings_get_for_screen (screen);
5508 g_signal_handler_disconnect (settings,
5509 impl->settings_signal_id);
5510 impl->settings_signal_id = 0;
5515 gtk_file_chooser_default_dispose (GObject *object)
5518 GtkFileChooserDefault *impl = (GtkFileChooserDefault *) object;
5520 if (impl->extra_widget)
5522 g_object_unref (impl->extra_widget);
5523 impl->extra_widget = NULL;
5526 pending_select_files_free (impl);
5528 /* cancel all pending operations */
5529 if (impl->pending_cancellables)
5531 for (l = impl->pending_cancellables; l; l = l->next)
5533 GCancellable *cancellable = G_CANCELLABLE (l->data);
5534 g_cancellable_cancel (cancellable);
5536 g_slist_free (impl->pending_cancellables);
5537 impl->pending_cancellables = NULL;
5540 if (impl->reload_icon_cancellables)
5542 for (l = impl->reload_icon_cancellables; l; l = l->next)
5544 GCancellable *cancellable = G_CANCELLABLE (l->data);
5545 g_cancellable_cancel (cancellable);
5547 g_slist_free (impl->reload_icon_cancellables);
5548 impl->reload_icon_cancellables = NULL;
5551 if (impl->loading_shortcuts)
5553 for (l = impl->loading_shortcuts; l; l = l->next)
5555 GCancellable *cancellable = G_CANCELLABLE (l->data);
5556 g_cancellable_cancel (cancellable);
5558 g_slist_free (impl->loading_shortcuts);
5559 impl->loading_shortcuts = NULL;
5562 if (impl->file_list_drag_data_received_cancellable)
5564 g_cancellable_cancel (impl->file_list_drag_data_received_cancellable);
5565 impl->file_list_drag_data_received_cancellable = NULL;
5568 if (impl->update_current_folder_cancellable)
5570 g_cancellable_cancel (impl->update_current_folder_cancellable);
5571 impl->update_current_folder_cancellable = NULL;
5574 if (impl->should_respond_get_info_cancellable)
5576 g_cancellable_cancel (impl->should_respond_get_info_cancellable);
5577 impl->should_respond_get_info_cancellable = NULL;
5580 if (impl->update_from_entry_cancellable)
5582 g_cancellable_cancel (impl->update_from_entry_cancellable);
5583 impl->update_from_entry_cancellable = NULL;
5586 if (impl->shortcuts_activate_iter_cancellable)
5588 g_cancellable_cancel (impl->shortcuts_activate_iter_cancellable);
5589 impl->shortcuts_activate_iter_cancellable = NULL;
5592 search_stop_searching (impl, TRUE);
5593 recent_stop_loading (impl);
5595 remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl)));
5597 G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->dispose (object);
5600 /* We override show-all since we have internal widgets that
5601 * shouldn't be shown when you call show_all(), like the filter
5605 gtk_file_chooser_default_show_all (GtkWidget *widget)
5607 GtkFileChooserDefault *impl = (GtkFileChooserDefault *) widget;
5609 gtk_widget_show (widget);
5611 if (impl->extra_widget)
5612 gtk_widget_show_all (impl->extra_widget);
5615 /* Handler for GtkWindow::set-focus; this is where we save the last-focused
5616 * widget on our toplevel. See gtk_file_chooser_default_hierarchy_changed()
5619 toplevel_set_focus_cb (GtkWindow *window,
5621 GtkFileChooserDefault *impl)
5623 impl->toplevel_last_focus_widget = gtk_window_get_focus (window);
5626 /* We monitor the focus widget on our toplevel to be able to know which widget
5627 * was last focused at the time our "should_respond" method gets called.
5630 gtk_file_chooser_default_hierarchy_changed (GtkWidget *widget,
5631 GtkWidget *previous_toplevel)
5633 GtkFileChooserDefault *impl;
5634 GtkWidget *toplevel;
5636 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5637 toplevel = gtk_widget_get_toplevel (widget);
5639 if (previous_toplevel &&
5640 impl->toplevel_set_focus_id != 0)
5642 g_signal_handler_disconnect (previous_toplevel,
5643 impl->toplevel_set_focus_id);
5644 impl->toplevel_set_focus_id = 0;
5645 impl->toplevel_last_focus_widget = NULL;
5648 if (gtk_widget_is_toplevel (toplevel))
5650 g_assert (impl->toplevel_set_focus_id == 0);
5651 impl->toplevel_set_focus_id = g_signal_connect (toplevel, "set-focus",
5652 G_CALLBACK (toplevel_set_focus_cb), impl);
5653 impl->toplevel_last_focus_widget = gtk_window_get_focus (GTK_WINDOW (toplevel));
5657 /* Changes the icons wherever it is needed */
5659 change_icon_theme (GtkFileChooserDefault *impl)
5661 GtkSettings *settings;
5663 GtkCellRenderer *renderer;
5666 profile_start ("start", NULL);
5668 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
5670 if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU, &width, &height))
5671 impl->icon_size = MAX (width, height);
5673 impl->icon_size = FALLBACK_ICON_SIZE;
5675 shortcuts_reload_icons (impl);
5676 /* the first cell in the first column is the icon column, and we have a fixed size there */
5677 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (
5678 gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_files_tree_view), 0)));
5679 renderer = GTK_CELL_RENDERER (cells->data);
5680 set_icon_cell_renderer_fixed_size (impl, renderer);
5681 g_list_free (cells);
5682 if (impl->browse_files_model)
5683 _gtk_file_system_model_clear_cache (impl->browse_files_model, MODEL_COL_PIXBUF);
5684 gtk_widget_queue_resize (impl->browse_files_tree_view);
5686 profile_end ("end", NULL);
5689 /* Callback used when a GtkSettings value changes */
5691 settings_notify_cb (GObject *object,
5693 GtkFileChooserDefault *impl)
5697 profile_start ("start", NULL);
5699 name = g_param_spec_get_name (pspec);
5701 if (strcmp (name, "gtk-icon-theme-name") == 0 ||
5702 strcmp (name, "gtk-icon-sizes") == 0)
5703 change_icon_theme (impl);
5705 profile_end ("end", NULL);
5708 /* Installs a signal handler for GtkSettings so that we can monitor changes in
5712 check_icon_theme (GtkFileChooserDefault *impl)
5714 GtkSettings *settings;
5716 profile_start ("start", NULL);
5718 if (impl->settings_signal_id)
5720 profile_end ("end", NULL);
5724 if (gtk_widget_has_screen (GTK_WIDGET (impl)))
5726 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
5727 impl->settings_signal_id = g_signal_connect (settings, "notify",
5728 G_CALLBACK (settings_notify_cb), impl);
5730 change_icon_theme (impl);
5733 profile_end ("end", NULL);
5737 gtk_file_chooser_default_style_updated (GtkWidget *widget)
5739 GtkFileChooserDefault *impl;
5741 profile_start ("start", NULL);
5743 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5745 profile_msg (" parent class style_udpated start", NULL);
5746 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->style_updated (widget);
5747 profile_msg (" parent class style_updated end", NULL);
5749 if (gtk_widget_has_screen (GTK_WIDGET (impl)))
5750 change_icon_theme (impl);
5752 emit_default_size_changed (impl);
5754 profile_end ("end", NULL);
5758 gtk_file_chooser_default_screen_changed (GtkWidget *widget,
5759 GdkScreen *previous_screen)
5761 GtkFileChooserDefault *impl;
5763 profile_start ("start", NULL);
5765 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5767 if (GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->screen_changed)
5768 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->screen_changed (widget, previous_screen);
5770 remove_settings_signal (impl, previous_screen);
5771 check_icon_theme (impl);
5773 emit_default_size_changed (impl);
5775 profile_end ("end", NULL);
5779 gtk_file_chooser_default_size_allocate (GtkWidget *widget,
5780 GtkAllocation *allocation)
5782 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->size_allocate (widget, allocation);
5786 set_sort_column (GtkFileChooserDefault *impl)
5788 GtkTreeSortable *sortable;
5790 sortable = GTK_TREE_SORTABLE (gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view)));
5792 /* can happen when we're still populating the model */
5793 if (sortable == NULL)
5796 gtk_tree_sortable_set_sort_column_id (sortable,
5802 settings_ensure (GtkFileChooserDefault *impl)
5804 if (impl->settings != NULL)
5807 impl->settings = g_settings_new_with_path ("org.gtk.Settings.FileChooser",
5808 "/org/gtk/settings/file-chooser/");
5809 g_settings_delay (impl->settings);
5813 settings_load (GtkFileChooserDefault *impl)
5815 LocationMode location_mode;
5816 gboolean show_hidden;
5817 gboolean expand_folders;
5818 gboolean show_size_column;
5820 GtkSortType sort_order;
5822 settings_ensure (impl);
5824 expand_folders = g_settings_get_boolean (impl->settings, SETTINGS_KEY_EXPAND_FOLDERS);
5825 location_mode = g_settings_get_enum (impl->settings, SETTINGS_KEY_LOCATION_MODE);
5826 show_hidden = g_settings_get_boolean (impl->settings, SETTINGS_KEY_SHOW_HIDDEN);
5827 show_size_column = g_settings_get_boolean (impl->settings, SETTINGS_KEY_SHOW_SIZE_COLUMN);
5828 sort_column = g_settings_get_enum (impl->settings, SETTINGS_KEY_SORT_COLUMN);
5829 sort_order = g_settings_get_enum (impl->settings, SETTINGS_KEY_SORT_ORDER);
5831 location_mode_set (impl, location_mode, TRUE);
5833 gtk_file_chooser_set_show_hidden (GTK_FILE_CHOOSER (impl), show_hidden);
5835 impl->expand_folders = expand_folders;
5836 if (impl->save_expander)
5837 gtk_expander_set_expanded (GTK_EXPANDER (impl->save_expander), expand_folders);
5839 impl->show_size_column = show_size_column;
5840 gtk_tree_view_column_set_visible (impl->list_size_column, show_size_column);
5842 impl->sort_column = sort_column;
5843 impl->sort_order = sort_order;
5844 /* We don't call set_sort_column() here as the models may not have been
5845 * created yet. The individual functions that create and set the models will
5846 * call set_sort_column() themselves.
5851 save_dialog_geometry (GtkFileChooserDefault *impl)
5853 GtkWindow *toplevel;
5854 int x, y, width, height;
5856 /* We don't save the geometry in non-expanded "save" mode, so that the "little
5857 * dialog" won't make future Open dialogs too small.
5859 if (!(impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
5860 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
5861 || impl->expand_folders))
5864 toplevel = get_toplevel (GTK_WIDGET (impl));
5866 if (!(toplevel && GTK_IS_FILE_CHOOSER_DIALOG (toplevel)))
5869 gtk_window_get_position (toplevel, &x, &y);
5870 gtk_window_get_size (toplevel, &width, &height);
5872 g_settings_set (impl->settings, "window-position", "(ii)", x, y);
5873 g_settings_set (impl->settings, "window-size", "(ii)", width, height);
5877 settings_save (GtkFileChooserDefault *impl)
5879 settings_ensure (impl);
5881 g_settings_set_enum (impl->settings, SETTINGS_KEY_LOCATION_MODE, impl->location_mode);
5882 g_settings_set_boolean (impl->settings, SETTINGS_KEY_EXPAND_FOLDERS, impl->expand_folders);
5883 g_settings_set_boolean (impl->settings, SETTINGS_KEY_SHOW_HIDDEN,
5884 gtk_file_chooser_get_show_hidden (GTK_FILE_CHOOSER (impl)));
5885 g_settings_set_boolean (impl->settings, SETTINGS_KEY_SHOW_SIZE_COLUMN, impl->show_size_column);
5886 g_settings_set_enum (impl->settings, SETTINGS_KEY_SORT_COLUMN, impl->sort_column);
5887 g_settings_set_enum (impl->settings, SETTINGS_KEY_SORT_ORDER, impl->sort_order);
5889 save_dialog_geometry (impl);
5891 /* Now apply the settings */
5892 g_settings_apply (impl->settings);
5894 g_object_unref (impl->settings);
5895 impl->settings = NULL;
5898 /* GtkWidget::realize method */
5900 gtk_file_chooser_default_realize (GtkWidget *widget)
5902 GtkFileChooserDefault *impl;
5904 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5906 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->realize (widget);
5908 emit_default_size_changed (impl);
5911 /* GtkWidget::map method */
5913 gtk_file_chooser_default_map (GtkWidget *widget)
5915 GtkFileChooserDefault *impl;
5916 char *current_working_dir;
5918 profile_start ("start", NULL);
5920 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5922 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->map (widget);
5924 if (impl->operation_mode == OPERATION_MODE_BROWSE)
5926 switch (impl->reload_state)
5929 /* The user didn't explicitly give us a folder to
5930 * display, so we'll use the cwd
5932 current_working_dir = g_get_current_dir ();
5933 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (impl),
5934 current_working_dir);
5935 g_free (current_working_dir);
5938 case RELOAD_HAS_FOLDER:
5939 /* Nothing; we are already loading or loaded, so we
5940 * don't need to reload
5945 g_assert_not_reached ();
5949 volumes_bookmarks_changed_cb (impl->file_system, impl);
5951 settings_load (impl);
5953 profile_end ("end", NULL);
5956 /* GtkWidget::unmap method */
5958 gtk_file_chooser_default_unmap (GtkWidget *widget)
5960 GtkFileChooserDefault *impl;
5962 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5964 settings_save (impl);
5966 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->unmap (widget);
5970 install_list_model_filter (GtkFileChooserDefault *impl)
5972 _gtk_file_system_model_set_filter (impl->browse_files_model,
5973 impl->current_filter);
5976 #define COMPARE_DIRECTORIES \
5977 GtkFileChooserDefault *impl = user_data; \
5978 GtkFileSystemModel *fs_model = GTK_FILE_SYSTEM_MODEL (model); \
5979 gboolean dir_a, dir_b; \
5981 dir_a = g_value_get_boolean (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_IS_FOLDER)); \
5982 dir_b = g_value_get_boolean (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_IS_FOLDER)); \
5984 if (dir_a != dir_b) \
5985 return impl->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */
5987 /* Sort callback for the filename column */
5989 name_sort_func (GtkTreeModel *model,
5994 COMPARE_DIRECTORIES;
5997 const char *key_a, *key_b;
6000 key_a = g_value_get_string (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_NAME_COLLATED));
6001 key_b = g_value_get_string (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_NAME_COLLATED));
6004 result = strcmp (key_a, key_b);
6016 /* Sort callback for the size column */
6018 size_sort_func (GtkTreeModel *model,
6023 COMPARE_DIRECTORIES;
6026 gint64 size_a, size_b;
6028 size_a = g_value_get_int64 (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_SIZE));
6029 size_b = g_value_get_int64 (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_SIZE));
6031 return size_a < size_b ? -1 : (size_a == size_b ? 0 : 1);
6035 /* Sort callback for the mtime column */
6037 mtime_sort_func (GtkTreeModel *model,
6042 COMPARE_DIRECTORIES;
6047 ta = g_value_get_long (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_MTIME));
6048 tb = g_value_get_long (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_MTIME));
6050 return ta < tb ? -1 : (ta == tb ? 0 : 1);
6054 /* Callback used when the sort column changes. We cache the sort order for use
6055 * in name_sort_func().
6058 list_sort_column_changed_cb (GtkTreeSortable *sortable,
6059 GtkFileChooserDefault *impl)
6061 gint sort_column_id;
6062 GtkSortType sort_type;
6064 if (gtk_tree_sortable_get_sort_column_id (sortable, &sort_column_id, &sort_type))
6066 impl->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
6067 impl->sort_column = sort_column_id;
6068 impl->sort_order = sort_type;
6073 set_busy_cursor (GtkFileChooserDefault *impl,
6077 GtkWindow *toplevel;
6078 GdkDisplay *display;
6081 toplevel = get_toplevel (GTK_WIDGET (impl));
6082 widget = GTK_WIDGET (toplevel);
6083 if (!toplevel || !gtk_widget_get_realized (widget))
6086 display = gtk_widget_get_display (widget);
6089 cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
6093 gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
6094 gdk_display_flush (display);
6097 g_object_unref (cursor);
6100 /* Creates a sort model to wrap the file system model and sets it on the tree view */
6102 load_set_model (GtkFileChooserDefault *impl)
6104 profile_start ("start", NULL);
6106 g_assert (impl->browse_files_model != NULL);
6108 profile_msg (" gtk_tree_view_set_model start", NULL);
6109 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
6110 GTK_TREE_MODEL (impl->browse_files_model));
6111 gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->browse_files_tree_view));
6112 gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_files_tree_view),
6114 set_sort_column (impl);
6115 profile_msg (" gtk_tree_view_set_model end", NULL);
6116 impl->list_sort_ascending = TRUE;
6118 profile_end ("end", NULL);
6121 /* Timeout callback used when the loading timer expires */
6123 load_timeout_cb (gpointer data)
6125 GtkFileChooserDefault *impl;
6127 profile_start ("start", NULL);
6129 impl = GTK_FILE_CHOOSER_DEFAULT (data);
6130 g_assert (impl->load_state == LOAD_PRELOAD);
6131 g_assert (impl->load_timeout_id != 0);
6132 g_assert (impl->browse_files_model != NULL);
6134 impl->load_timeout_id = 0;
6135 impl->load_state = LOAD_LOADING;
6137 load_set_model (impl);
6139 profile_end ("end", NULL);
6144 /* Sets up a new load timer for the model and switches to the LOAD_PRELOAD state */
6146 load_setup_timer (GtkFileChooserDefault *impl)
6148 g_assert (impl->load_timeout_id == 0);
6149 g_assert (impl->load_state != LOAD_PRELOAD);
6151 impl->load_timeout_id = gdk_threads_add_timeout (MAX_LOADING_TIME, load_timeout_cb, impl);
6152 impl->load_state = LOAD_PRELOAD;
6155 /* Removes the load timeout and switches to the LOAD_FINISHED state */
6157 load_remove_timer (GtkFileChooserDefault *impl)
6159 if (impl->load_timeout_id != 0)
6161 g_assert (impl->load_state == LOAD_PRELOAD);
6163 g_source_remove (impl->load_timeout_id);
6164 impl->load_timeout_id = 0;
6165 impl->load_state = LOAD_EMPTY;
6168 g_assert (impl->load_state == LOAD_EMPTY ||
6169 impl->load_state == LOAD_LOADING ||
6170 impl->load_state == LOAD_FINISHED);
6173 /* Selects the first row in the file list */
6175 browse_files_select_first_row (GtkFileChooserDefault *impl)
6178 GtkTreeIter dummy_iter;
6179 GtkTreeModel *tree_model;
6181 tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view));
6186 path = gtk_tree_path_new_from_indices (0, -1);
6188 /* If the list is empty, do nothing. */
6189 if (gtk_tree_model_get_iter (tree_model, &dummy_iter, path))
6190 gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), path, NULL, FALSE);
6192 gtk_tree_path_free (path);
6195 struct center_selected_row_closure {
6196 GtkFileChooserDefault *impl;
6197 gboolean already_centered;
6200 /* Callback used from gtk_tree_selection_selected_foreach(); centers the
6201 * selected row in the tree view.
6204 center_selected_row_foreach_cb (GtkTreeModel *model,
6209 struct center_selected_row_closure *closure;
6212 if (closure->already_centered)
6215 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (closure->impl->browse_files_tree_view), path, NULL, TRUE, 0.5, 0.0);
6216 closure->already_centered = TRUE;
6219 /* Centers the selected row in the tree view */
6221 browse_files_center_selected_row (GtkFileChooserDefault *impl)
6223 struct center_selected_row_closure closure;
6224 GtkTreeSelection *selection;
6226 closure.impl = impl;
6227 closure.already_centered = FALSE;
6229 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6230 gtk_tree_selection_selected_foreach (selection, center_selected_row_foreach_cb, &closure);
6234 show_and_select_files (GtkFileChooserDefault *impl,
6237 GtkTreeSelection *selection;
6238 GtkFileSystemModel *fsmodel;
6239 gboolean enabled_hidden, removed_filters;
6240 gboolean selected_a_file;
6243 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6244 fsmodel = GTK_FILE_SYSTEM_MODEL (gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view)));
6246 enabled_hidden = impl->show_hidden;
6247 removed_filters = (impl->current_filter == NULL);
6249 selected_a_file = FALSE;
6251 for (walk = files; walk; walk = walk->next)
6253 GFile *file = walk->data;
6256 /* Is it a hidden file? */
6258 if (!_gtk_file_system_model_get_iter_for_file (fsmodel, &iter, file))
6261 if (!_gtk_file_system_model_iter_is_visible (fsmodel, &iter))
6263 GFileInfo *info = _gtk_file_system_model_get_info (fsmodel, &iter);
6265 if (!enabled_hidden &&
6266 (g_file_info_get_is_hidden (info) ||
6267 g_file_info_get_is_backup (info)))
6269 g_object_set (impl, "show-hidden", TRUE, NULL);
6270 enabled_hidden = TRUE;
6274 /* Is it a filtered file? */
6276 if (!_gtk_file_system_model_get_iter_for_file (fsmodel, &iter, file))
6277 continue; /* re-get the iter as it may change when the model refilters */
6279 if (!_gtk_file_system_model_iter_is_visible (fsmodel, &iter))
6281 /* Maybe we should have a way to ask the fsmodel if it had filtered a file */
6282 if (!removed_filters)
6284 set_current_filter (impl, NULL);
6285 removed_filters = TRUE;
6289 /* Okay, can we select the file now? */
6291 if (!_gtk_file_system_model_get_iter_for_file (fsmodel, &iter, file))
6294 if (_gtk_file_system_model_iter_is_visible (fsmodel, &iter))
6298 gtk_tree_selection_select_iter (selection, &iter);
6300 path = gtk_tree_model_get_path (GTK_TREE_MODEL (fsmodel), &iter);
6301 gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view),
6303 gtk_tree_path_free (path);
6305 selected_a_file = TRUE;
6309 browse_files_center_selected_row (impl);
6311 return selected_a_file;
6314 /* Processes the pending operation when a folder is finished loading */
6316 pending_select_files_process (GtkFileChooserDefault *impl)
6318 g_assert (impl->load_state == LOAD_FINISHED);
6319 g_assert (impl->browse_files_model != NULL);
6321 if (impl->pending_select_files)
6323 show_and_select_files (impl, impl->pending_select_files);
6324 pending_select_files_free (impl);
6325 browse_files_center_selected_row (impl);
6329 /* We only select the first row if the chooser is actually mapped ---
6330 * selecting the first row is to help the user when he is interacting with
6331 * the chooser, but sometimes a chooser works not on behalf of the user,
6332 * but rather on behalf of something else like GtkFileChooserButton. In
6333 * that case, the chooser's selection should be what the caller expects,
6334 * as the user can't see that something else got selected. See bug #165264.
6336 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN &&
6337 gtk_widget_get_mapped (GTK_WIDGET (impl)))
6338 browse_files_select_first_row (impl);
6341 g_assert (impl->pending_select_files == NULL);
6345 show_error_on_reading_current_folder (GtkFileChooserDefault *impl, GError *error)
6350 info = g_file_query_info (impl->current_folder,
6351 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
6352 G_FILE_QUERY_INFO_NONE,
6357 msg = g_strdup_printf (_("Could not read the contents of %s"), g_file_info_get_display_name (info));
6358 g_object_unref (info);
6361 msg = g_strdup (_("Could not read the contents of the folder"));
6363 error_message (impl, msg, error->message);
6367 /* Callback used when the file system model finishes loading */
6369 browse_files_model_finished_loading_cb (GtkFileSystemModel *model,
6371 GtkFileChooserDefault *impl)
6373 profile_start ("start", NULL);
6376 show_error_on_reading_current_folder (impl, error);
6378 if (impl->load_state == LOAD_PRELOAD)
6380 load_remove_timer (impl);
6381 load_set_model (impl);
6383 else if (impl->load_state == LOAD_LOADING)
6389 /* We can't g_assert_not_reached(), as something other than us may have
6390 * initiated a folder reload. See #165556.
6392 profile_end ("end", NULL);
6396 g_assert (impl->load_timeout_id == 0);
6398 impl->load_state = LOAD_FINISHED;
6400 pending_select_files_process (impl);
6401 set_busy_cursor (impl, FALSE);
6402 #ifdef PROFILE_FILE_CHOOSER
6403 access ("MARK: *** FINISHED LOADING", F_OK);
6406 profile_end ("end", NULL);
6410 stop_loading_and_clear_list_model (GtkFileChooserDefault *impl,
6411 gboolean remove_from_treeview)
6413 load_remove_timer (impl); /* This changes the state to LOAD_EMPTY */
6415 if (impl->browse_files_model)
6417 g_object_unref (impl->browse_files_model);
6418 impl->browse_files_model = NULL;
6421 if (remove_from_treeview)
6422 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
6426 my_g_format_time_for_display (glong secs)
6431 time_t time_mtime, time_now;
6432 const gchar *format;
6433 gchar *locale_format = NULL;
6435 char *date_str = NULL;
6437 const char *locale, *dot = NULL;
6438 gint64 codepage = -1;
6444 #ifdef HAVE_LOCALTIME_R
6445 localtime_r ((time_t *) &time_mtime, &tm_mtime);
6448 struct tm *ptm = localtime ((time_t *) &time_mtime);
6452 g_warning ("ptm != NULL failed");
6454 return g_strdup (_("Unknown"));
6457 memcpy ((void *) &tm_mtime, (void *) ptm, sizeof (struct tm));
6459 #endif /* HAVE_LOCALTIME_R */
6461 g_date_set_time_t (&mtime, time_mtime);
6462 time_now = time (NULL);
6463 g_date_set_time_t (&now, time_now);
6465 days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
6467 /* Translators: %H means "hours" and %M means "minutes" */
6469 format = _("%H:%M");
6470 else if (days_diff == 1)
6471 format = _("Yesterday at %H:%M");
6474 if (days_diff > 1 && days_diff < 7)
6475 format = "%A"; /* Days from last week */
6477 format = "%x"; /* Any other date */
6481 /* g_locale_from_utf8() returns a string in the system
6482 * code-page, which is not always the same as that used by the C
6483 * library. For instance when running a GTK+ program with
6484 * LANG=ko on an English version of Windows, the system
6485 * code-page is 1252, but the code-page used by the C library is
6486 * 949. (It's GTK+ itself that sets the C library locale when it
6487 * notices the LANG environment variable. See gtkmain.c The
6488 * Microsoft C library doesn't look at any locale environment
6489 * variables.) We need to pass strftime() a string in the C
6490 * library's code-page. See bug #509885.
6492 locale = setlocale (LC_ALL, NULL);
6494 dot = strchr (locale, '.');
6497 codepage = g_ascii_strtoll (dot+1, NULL, 10);
6499 /* All codepages should fit in 16 bits AFAIK */
6500 if (codepage > 0 && codepage < 65536)
6502 sprintf (charset, "CP%u", (guint) codepage);
6503 locale_format = g_convert (format, -1, charset, "UTF-8", NULL, NULL, NULL);
6507 locale_format = g_locale_from_utf8 (format, -1, NULL, NULL, NULL);
6509 if (locale_format != NULL &&
6510 strftime (buf, sizeof (buf), locale_format, &tm_mtime) != 0)
6513 /* As above but in opposite direction... */
6514 if (codepage > 0 && codepage < 65536)
6515 date_str = g_convert (buf, -1, "UTF-8", charset, NULL, NULL, NULL);
6517 date_str = g_locale_to_utf8 (buf, -1, NULL, NULL, NULL);
6521 if (date_str == NULL)
6522 date_str = g_strdup (_("Unknown"));
6524 g_free (locale_format);
6529 copy_attribute (GFileInfo *to, GFileInfo *from, const char *attribute)
6531 GFileAttributeType type;
6534 if (g_file_info_get_attribute_data (from, attribute, &type, &value, NULL))
6535 g_file_info_set_attribute (to, attribute, type, value);
6539 file_system_model_got_thumbnail (GObject *object, GAsyncResult *res, gpointer data)
6541 GtkFileSystemModel *model = data; /* might be unreffed if operation was cancelled */
6542 GFile *file = G_FILE (object);
6543 GFileInfo *queried, *info;
6546 queried = g_file_query_info_finish (file, res, NULL);
6547 if (queried == NULL)
6550 GDK_THREADS_ENTER ();
6552 /* now we know model is valid */
6554 /* file was deleted */
6555 if (!_gtk_file_system_model_get_iter_for_file (model, &iter, file))
6557 GDK_THREADS_LEAVE ();
6561 info = g_file_info_dup (_gtk_file_system_model_get_info (model, &iter));
6563 copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
6564 copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED);
6565 copy_attribute (info, queried, G_FILE_ATTRIBUTE_STANDARD_ICON);
6567 _gtk_file_system_model_update_file (model, file, info, FALSE);
6569 g_object_unref (info);
6571 GDK_THREADS_LEAVE ();
6575 file_system_model_set (GtkFileSystemModel *model,
6582 GtkFileChooserDefault *impl = data;
6586 case MODEL_COL_FILE:
6587 g_value_set_object (value, file);
6589 case MODEL_COL_NAME:
6591 g_value_set_string (value, DEFAULT_NEW_FOLDER_NAME);
6593 g_value_set_string (value, g_file_info_get_display_name (info));
6595 case MODEL_COL_NAME_COLLATED:
6597 g_value_take_string (value, g_utf8_collate_key_for_filename (DEFAULT_NEW_FOLDER_NAME, -1));
6599 g_value_take_string (value, g_utf8_collate_key_for_filename (g_file_info_get_display_name (info), -1));
6601 case MODEL_COL_IS_FOLDER:
6602 g_value_set_boolean (value, info == NULL || _gtk_file_info_consider_as_directory (info));
6604 case MODEL_COL_PIXBUF:
6607 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_ICON))
6609 g_value_take_object (value, _gtk_file_info_render_icon (info, GTK_WIDGET (impl), impl->icon_size));
6613 GtkTreeModel *tree_model;
6614 GtkTreePath *path, *start, *end;
6617 if (impl->browse_files_tree_view == NULL ||
6618 g_file_info_has_attribute (info, "filechooser::queried"))
6621 tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view));
6622 if (tree_model != GTK_TREE_MODEL (model))
6625 if (!_gtk_file_system_model_get_iter_for_file (model,
6628 g_assert_not_reached ();
6629 if (!gtk_tree_view_get_visible_range (GTK_TREE_VIEW (impl->browse_files_tree_view), &start, &end))
6631 path = gtk_tree_model_get_path (tree_model, &iter);
6632 if (gtk_tree_path_compare (start, path) != 1 &&
6633 gtk_tree_path_compare (path, end) != 1)
6635 g_file_info_set_attribute_boolean (info, "filechooser::queried", TRUE);
6636 g_file_query_info_async (file,
6637 G_FILE_ATTRIBUTE_THUMBNAIL_PATH ","
6638 G_FILE_ATTRIBUTE_THUMBNAILING_FAILED ","
6639 G_FILE_ATTRIBUTE_STANDARD_ICON,
6640 G_FILE_QUERY_INFO_NONE,
6642 _gtk_file_system_model_get_cancellable (model),
6643 file_system_model_got_thumbnail,
6646 gtk_tree_path_free (path);
6647 gtk_tree_path_free (start);
6648 gtk_tree_path_free (end);
6653 g_value_set_object (value, NULL);
6655 case MODEL_COL_SIZE:
6656 g_value_set_int64 (value, info ? g_file_info_get_size (info) : 0);
6658 case MODEL_COL_SIZE_TEXT:
6659 if (info == NULL || _gtk_file_info_consider_as_directory (info))
6660 g_value_set_string (value, NULL);
6662 g_value_take_string (value, g_format_size_for_display (g_file_info_get_size (info)));
6664 case MODEL_COL_MTIME:
6665 case MODEL_COL_MTIME_TEXT:
6670 g_file_info_get_modification_time (info, &tv);
6671 if (column == MODEL_COL_MTIME)
6672 g_value_set_long (value, tv.tv_sec);
6673 else if (tv.tv_sec == 0)
6674 g_value_set_static_string (value, _("Unknown"));
6676 g_value_take_string (value, my_g_format_time_for_display (tv.tv_sec));
6679 case MODEL_COL_ELLIPSIZE:
6680 g_value_set_enum (value, info ? PANGO_ELLIPSIZE_END : PANGO_ELLIPSIZE_NONE);
6683 g_assert_not_reached ();
6690 /* Gets rid of the old list model and creates a new one for the current folder */
6692 set_list_model (GtkFileChooserDefault *impl,
6695 g_assert (impl->current_folder != NULL);
6697 profile_start ("start", NULL);
6699 stop_loading_and_clear_list_model (impl, TRUE);
6701 set_busy_cursor (impl, TRUE);
6703 impl->browse_files_model =
6704 _gtk_file_system_model_new_for_directory (impl->current_folder,
6706 file_system_model_set,
6708 MODEL_COLUMN_TYPES);
6710 _gtk_file_system_model_set_show_hidden (impl->browse_files_model, impl->show_hidden);
6712 profile_msg (" set sort function", NULL);
6713 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), MODEL_COL_NAME, name_sort_func, impl, NULL);
6714 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), MODEL_COL_SIZE, size_sort_func, impl, NULL);
6715 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), MODEL_COL_MTIME, mtime_sort_func, impl, NULL);
6716 gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), NULL, NULL, NULL);
6717 set_sort_column (impl);
6718 impl->list_sort_ascending = TRUE;
6719 g_signal_connect (impl->browse_files_model, "sort-column-changed",
6720 G_CALLBACK (list_sort_column_changed_cb), impl);
6722 load_setup_timer (impl); /* This changes the state to LOAD_PRELOAD */
6724 g_signal_connect (impl->browse_files_model, "finished-loading",
6725 G_CALLBACK (browse_files_model_finished_loading_cb), impl);
6727 install_list_model_filter (impl);
6729 profile_end ("end", NULL);
6734 struct update_chooser_entry_selected_foreach_closure {
6736 GtkTreeIter first_selected_iter;
6740 compare_utf8_filenames (const gchar *a,
6743 gchar *a_folded, *b_folded;
6746 a_folded = g_utf8_strdown (a, -1);
6747 b_folded = g_utf8_strdown (b, -1);
6749 retval = strcmp (a_folded, b_folded);
6758 update_chooser_entry_selected_foreach (GtkTreeModel *model,
6763 struct update_chooser_entry_selected_foreach_closure *closure;
6766 closure->num_selected++;
6768 if (closure->num_selected == 1)
6769 closure->first_selected_iter = *iter;
6773 update_chooser_entry (GtkFileChooserDefault *impl)
6775 GtkTreeSelection *selection;
6776 struct update_chooser_entry_selected_foreach_closure closure;
6778 /* no need to update the file chooser's entry if there's no entry */
6779 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
6780 impl->operation_mode == OPERATION_MODE_RECENT ||
6781 !impl->location_entry)
6784 if (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6785 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
6786 || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
6787 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
6788 && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)))
6791 g_assert (impl->location_entry != NULL);
6793 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6794 closure.num_selected = 0;
6795 gtk_tree_selection_selected_foreach (selection, update_chooser_entry_selected_foreach, &closure);
6797 if (closure.num_selected == 0)
6799 goto maybe_clear_entry;
6801 else if (closure.num_selected == 1)
6803 if (impl->operation_mode == OPERATION_MODE_BROWSE)
6806 gboolean change_entry;
6808 info = _gtk_file_system_model_get_info (impl->browse_files_model, &closure.first_selected_iter);
6810 /* If the cursor moved to the row of the newly created folder,
6811 * retrieving info will return NULL.
6816 g_free (impl->browse_files_last_selected_name);
6817 impl->browse_files_last_selected_name =
6818 g_strdup (g_file_info_get_display_name (info));
6820 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
6821 impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
6822 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6824 /* Don't change the name when clicking on a folder... */
6825 change_entry = (! _gtk_file_info_consider_as_directory (info));
6828 change_entry = TRUE; /* ... unless we are in SELECT_FOLDER mode */
6832 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->browse_files_last_selected_name);
6834 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
6835 _gtk_file_chooser_entry_select_filename (GTK_FILE_CHOOSER_ENTRY (impl->location_entry));
6843 g_assert (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
6844 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER));
6846 /* Multiple selection, so just clear the entry. */
6847 g_free (impl->browse_files_last_selected_name);
6848 impl->browse_files_last_selected_name = NULL;
6850 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
6856 if ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
6857 && impl->browse_files_last_selected_name)
6859 const char *entry_text;
6861 gboolean clear_entry;
6863 entry_text = gtk_entry_get_text (GTK_ENTRY (impl->location_entry));
6864 len = strlen (entry_text);
6867 /* The file chooser entry may have appended a "/" to its text.
6868 * So take it out, and compare the result to the old selection.
6870 if (entry_text[len - 1] == G_DIR_SEPARATOR)
6874 tmp = g_strndup (entry_text, len - 1);
6875 clear_entry = (compare_utf8_filenames (impl->browse_files_last_selected_name, tmp) == 0);
6879 clear_entry = (compare_utf8_filenames (impl->browse_files_last_selected_name, entry_text) == 0);
6882 clear_entry = FALSE;
6885 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
6890 gtk_file_chooser_default_set_current_folder (GtkFileChooser *chooser,
6894 return gtk_file_chooser_default_update_current_folder (chooser, file, FALSE, FALSE, error);
6898 struct UpdateCurrentFolderData
6900 GtkFileChooserDefault *impl;
6902 gboolean keep_trail;
6903 gboolean clear_entry;
6904 GFile *original_file;
6905 GError *original_error;
6909 update_current_folder_mount_enclosing_volume_cb (GCancellable *cancellable,
6910 GtkFileSystemVolume *volume,
6911 const GError *error,
6914 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
6915 struct UpdateCurrentFolderData *data = user_data;
6916 GtkFileChooserDefault *impl = data->impl;
6918 if (cancellable != impl->update_current_folder_cancellable)
6921 impl->update_current_folder_cancellable = NULL;
6922 set_busy_cursor (impl, FALSE);
6929 error_changing_folder_dialog (data->impl, data->file, g_error_copy (error));
6930 impl->reload_state = RELOAD_EMPTY;
6934 change_folder_and_display_error (impl, data->file, data->clear_entry);
6937 g_object_unref (data->file);
6940 g_object_unref (cancellable);
6944 update_current_folder_get_info_cb (GCancellable *cancellable,
6946 const GError *error,
6949 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
6950 struct UpdateCurrentFolderData *data = user_data;
6951 GtkFileChooserDefault *impl = data->impl;
6953 if (cancellable != impl->update_current_folder_cancellable)
6956 impl->update_current_folder_cancellable = NULL;
6957 impl->reload_state = RELOAD_EMPTY;
6959 set_busy_cursor (impl, FALSE);
6968 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED))
6970 GMountOperation *mount_operation;
6971 GtkWidget *toplevel;
6973 g_object_unref (cancellable);
6974 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
6976 mount_operation = gtk_mount_operation_new (GTK_WINDOW (toplevel));
6978 set_busy_cursor (impl, TRUE);
6980 impl->update_current_folder_cancellable =
6981 _gtk_file_system_mount_enclosing_volume (impl->file_system, data->file,
6983 update_current_folder_mount_enclosing_volume_cb,
6989 if (!data->original_file)
6991 data->original_file = g_object_ref (data->file);
6992 data->original_error = g_error_copy (error);
6995 parent_file = g_file_get_parent (data->file);
6997 /* get parent path and try to change the folder to that */
7000 g_object_unref (data->file);
7001 data->file = parent_file;
7003 g_object_unref (cancellable);
7005 /* restart the update current folder operation */
7006 impl->reload_state = RELOAD_HAS_FOLDER;
7008 impl->update_current_folder_cancellable =
7009 _gtk_file_system_get_info (impl->file_system, data->file,
7011 update_current_folder_get_info_cb,
7014 set_busy_cursor (impl, TRUE);
7020 /* Error and bail out, ignoring "not found" errors since they're useless:
7021 * they only happen when a program defaults to a folder that has been (re)moved.
7023 if (!g_error_matches (data->original_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
7024 error_changing_folder_dialog (impl, data->original_file, data->original_error);
7026 g_error_free (data->original_error);
7028 g_object_unref (data->original_file);
7034 if (data->original_file)
7036 /* Error and bail out, ignoring "not found" errors since they're useless:
7037 * they only happen when a program defaults to a folder that has been (re)moved.
7039 if (!g_error_matches (data->original_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
7040 error_changing_folder_dialog (impl, data->original_file, data->original_error);
7042 g_error_free (data->original_error);
7044 g_object_unref (data->original_file);
7047 if (! _gtk_file_info_consider_as_directory (info))
7050 if (!_gtk_path_bar_set_file (GTK_PATH_BAR (impl->browse_path_bar), data->file, data->keep_trail, NULL))
7053 if (impl->current_folder != data->file)
7055 if (impl->current_folder)
7056 g_object_unref (impl->current_folder);
7058 impl->current_folder = g_object_ref (data->file);
7061 impl->reload_state = RELOAD_HAS_FOLDER;
7063 /* Update the widgets that may trigger a folder change themselves. */
7065 if (!impl->changing_folder)
7067 impl->changing_folder = TRUE;
7069 shortcuts_update_current_folder (impl);
7071 impl->changing_folder = FALSE;
7074 /* Set the folder on the save entry */
7076 if (impl->location_entry)
7078 _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
7079 impl->current_folder);
7081 if (data->clear_entry)
7082 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
7085 /* Create a new list model. This is slightly evil; we store the result value
7086 * but perform more actions rather than returning immediately even if it
7087 * generates an error.
7089 set_list_model (impl, NULL);
7091 /* Refresh controls */
7093 shortcuts_find_current_folder (impl);
7095 g_signal_emit_by_name (impl, "current-folder-changed", 0);
7097 check_preview_change (impl);
7098 bookmarks_check_add_sensitivity (impl);
7100 g_signal_emit_by_name (impl, "selection-changed", 0);
7103 g_object_unref (data->file);
7106 g_object_unref (cancellable);
7110 gtk_file_chooser_default_update_current_folder (GtkFileChooser *chooser,
7112 gboolean keep_trail,
7113 gboolean clear_entry,
7116 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7117 struct UpdateCurrentFolderData *data;
7119 profile_start ("start", NULL);
7121 g_object_ref (file);
7123 switch (impl->operation_mode)
7125 case OPERATION_MODE_SEARCH:
7126 search_switch_to_browse_mode (impl);
7128 case OPERATION_MODE_RECENT:
7129 recent_switch_to_browse_mode (impl);
7131 case OPERATION_MODE_BROWSE:
7135 if (impl->local_only && !g_file_is_native (file))
7137 g_set_error_literal (error,
7138 GTK_FILE_CHOOSER_ERROR,
7139 GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
7140 _("Cannot change to folder because it is not local"));
7142 g_object_unref (file);
7143 profile_end ("end - not local", NULL);
7147 if (impl->update_current_folder_cancellable)
7148 g_cancellable_cancel (impl->update_current_folder_cancellable);
7150 /* Test validity of path here. */
7151 data = g_new0 (struct UpdateCurrentFolderData, 1);
7153 data->file = g_object_ref (file);
7154 data->keep_trail = keep_trail;
7155 data->clear_entry = clear_entry;
7157 impl->reload_state = RELOAD_HAS_FOLDER;
7159 impl->update_current_folder_cancellable =
7160 _gtk_file_system_get_info (impl->file_system, file,
7162 update_current_folder_get_info_cb,
7165 set_busy_cursor (impl, TRUE);
7166 g_object_unref (file);
7168 profile_end ("end", NULL);
7173 gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser)
7175 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7177 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
7178 impl->operation_mode == OPERATION_MODE_RECENT)
7181 if (impl->reload_state == RELOAD_EMPTY)
7183 char *current_working_dir;
7186 /* We are unmapped, or we had an error while loading the last folder. We'll return
7187 * the $cwd since once we get (re)mapped, we'll load $cwd anyway unless the caller
7188 * explicitly calls set_current_folder() on us.
7190 current_working_dir = g_get_current_dir ();
7191 file = g_file_new_for_path (current_working_dir);
7192 g_free (current_working_dir);
7196 if (impl->current_folder)
7197 return g_object_ref (impl->current_folder);
7203 gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
7206 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7208 g_return_if_fail (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
7209 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
7211 pending_select_files_free (impl);
7212 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), name);
7216 gtk_file_chooser_default_select_file (GtkFileChooser *chooser,
7220 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7224 parent_file = g_file_get_parent (file);
7227 return gtk_file_chooser_set_current_folder_file (chooser, file, error);
7229 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
7230 impl->operation_mode == OPERATION_MODE_RECENT ||
7231 impl->load_state == LOAD_EMPTY)
7237 g_assert (impl->current_folder != NULL);
7239 same_path = g_file_equal (parent_file, impl->current_folder);
7242 if (same_path && impl->load_state == LOAD_FINISHED)
7247 files.data = (gpointer) file;
7250 result = show_and_select_files (impl, &files);
7251 g_object_unref (parent_file);
7255 pending_select_files_add (impl, file);
7261 result = gtk_file_chooser_set_current_folder_file (chooser, parent_file, error);
7262 g_object_unref (parent_file);
7266 g_object_unref (parent_file);
7271 gtk_file_chooser_default_unselect_file (GtkFileChooser *chooser,
7274 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7275 GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
7278 if (!impl->browse_files_model)
7281 if (!_gtk_file_system_model_get_iter_for_file (impl->browse_files_model,
7286 gtk_tree_selection_unselect_iter (gtk_tree_view_get_selection (tree_view),
7291 maybe_select (GtkTreeModel *model,
7296 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (data);
7297 GtkTreeSelection *selection;
7300 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7302 gtk_tree_model_get (model, iter,
7303 MODEL_COL_IS_FOLDER, &is_folder,
7306 if ((is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) ||
7307 (!is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN))
7308 gtk_tree_selection_select_iter (selection, iter);
7310 gtk_tree_selection_unselect_iter (selection, iter);
7316 gtk_file_chooser_default_select_all (GtkFileChooser *chooser)
7318 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7320 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
7321 impl->operation_mode == OPERATION_MODE_RECENT)
7323 GtkTreeSelection *selection;
7325 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7326 gtk_tree_selection_select_all (selection);
7330 if (impl->select_multiple)
7331 gtk_tree_model_foreach (GTK_TREE_MODEL (impl->browse_files_model),
7332 maybe_select, impl);
7336 gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser)
7338 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7339 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7341 gtk_tree_selection_unselect_all (selection);
7342 pending_select_files_free (impl);
7345 /* Checks whether the filename entry for the Save modes contains a well-formed filename.
7347 * is_well_formed_ret - whether what the user typed passes gkt_file_system_make_path()
7349 * is_empty_ret - whether the file entry is totally empty
7351 * is_file_part_empty_ret - whether the file part is empty (will be if user types "foobar/", and
7352 * the path will be "$cwd/foobar")
7355 check_save_entry (GtkFileChooserDefault *impl,
7357 gboolean *is_well_formed_ret,
7358 gboolean *is_empty_ret,
7359 gboolean *is_file_part_empty_ret,
7360 gboolean *is_folder)
7362 GtkFileChooserEntry *chooser_entry;
7363 GFile *current_folder;
7364 const char *file_part;
7368 g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
7369 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
7370 || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
7371 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
7372 && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY));
7374 chooser_entry = GTK_FILE_CHOOSER_ENTRY (impl->location_entry);
7376 if (strlen (gtk_entry_get_text (GTK_ENTRY (chooser_entry))) == 0)
7379 *is_well_formed_ret = TRUE;
7380 *is_empty_ret = TRUE;
7381 *is_file_part_empty_ret = TRUE;
7387 *is_empty_ret = FALSE;
7389 current_folder = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
7390 if (!current_folder)
7393 *is_well_formed_ret = FALSE;
7394 *is_file_part_empty_ret = FALSE;
7400 file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
7402 if (!file_part || file_part[0] == '\0')
7404 *file_ret = g_object_ref (current_folder);
7405 *is_well_formed_ret = TRUE;
7406 *is_file_part_empty_ret = TRUE;
7412 *is_file_part_empty_ret = FALSE;
7415 file = g_file_get_child_for_display_name (current_folder, file_part, &error);
7419 error_building_filename_dialog (impl, error);
7421 *is_well_formed_ret = FALSE;
7428 *is_well_formed_ret = TRUE;
7429 *is_folder = _gtk_file_chooser_entry_get_is_folder (chooser_entry, file);
7432 struct get_files_closure {
7433 GtkFileChooserDefault *impl;
7435 GFile *file_from_entry;
7439 get_files_foreach (GtkTreeModel *model,
7444 struct get_files_closure *info;
7446 GtkFileSystemModel *fs_model;
7449 fs_model = info->impl->browse_files_model;
7451 file = _gtk_file_system_model_get_file (fs_model, iter);
7453 return; /* We are on the editable row */
7455 if (!info->file_from_entry || !g_file_equal (info->file_from_entry, file))
7456 info->result = g_slist_prepend (info->result, g_object_ref (file));
7460 gtk_file_chooser_default_get_files (GtkFileChooser *chooser)
7462 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7463 struct get_files_closure info;
7464 GtkWindow *toplevel;
7465 GtkWidget *current_focus;
7466 gboolean file_list_seen;
7468 if (impl->operation_mode == OPERATION_MODE_SEARCH)
7469 return search_get_selected_files (impl);
7471 if (impl->operation_mode == OPERATION_MODE_RECENT)
7472 return recent_get_selected_files (impl);
7476 info.file_from_entry = NULL;
7478 toplevel = get_toplevel (GTK_WIDGET (impl));
7480 current_focus = gtk_window_get_focus (toplevel);
7482 current_focus = NULL;
7484 file_list_seen = FALSE;
7485 if (current_focus == impl->browse_files_tree_view)
7487 GtkTreeSelection *selection;
7491 file_list_seen = TRUE;
7492 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7493 gtk_tree_selection_selected_foreach (selection, get_files_foreach, &info);
7495 /* If there is no selection in the file list, we probably have this situation:
7497 * 1. The user typed a filename in the SAVE filename entry ("foo.txt").
7498 * 2. He then double-clicked on a folder ("bar") in the file list
7500 * So we want the selection to be "bar/foo.txt". Jump to the case for the
7501 * filename entry to see if that is the case.
7503 if (info.result == NULL && impl->location_entry)
7506 else if (impl->location_entry && current_focus == impl->location_entry)
7508 gboolean is_well_formed, is_empty, is_file_part_empty, is_folder;
7512 check_save_entry (impl, &info.file_from_entry, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder);
7517 if (!is_well_formed)
7520 if (is_file_part_empty && impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
7522 g_object_unref (info.file_from_entry);
7526 if (info.file_from_entry)
7527 info.result = g_slist_prepend (info.result, info.file_from_entry);
7528 else if (!file_list_seen)
7533 else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
7535 else if (impl->location_entry && impl->toplevel_last_focus_widget == impl->location_entry)
7539 /* The focus is on a dialog's action area button or something else */
7540 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
7541 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
7549 /* If there's no folder selected, and we're in SELECT_FOLDER mode, then we
7550 * fall back to the current directory */
7551 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
7552 info.result == NULL)
7554 GFile *current_folder;
7556 current_folder = gtk_file_chooser_get_current_folder_file (chooser);
7559 info.result = g_slist_prepend (info.result, current_folder);
7562 return g_slist_reverse (info.result);
7566 gtk_file_chooser_default_get_preview_file (GtkFileChooser *chooser)
7568 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7570 if (impl->preview_file)
7571 return g_object_ref (impl->preview_file);
7576 static GtkFileSystem *
7577 gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser)
7579 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7581 return impl->file_system;
7584 /* Shows or hides the filter widgets */
7586 show_filters (GtkFileChooserDefault *impl,
7590 gtk_widget_show (impl->filter_combo_hbox);
7592 gtk_widget_hide (impl->filter_combo_hbox);
7596 gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
7597 GtkFileFilter *filter)
7599 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7602 if (g_slist_find (impl->filters, filter))
7604 g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
7608 g_object_ref_sink (filter);
7609 impl->filters = g_slist_append (impl->filters, filter);
7611 name = gtk_file_filter_get_name (filter);
7613 name = "Untitled filter"; /* Place-holder, doesn't need to be marked for translation */
7615 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (impl->filter_combo), name);
7617 if (!g_slist_find (impl->filters, impl->current_filter))
7618 set_current_filter (impl, filter);
7620 show_filters (impl, TRUE);
7624 gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
7625 GtkFileFilter *filter)
7627 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7628 GtkTreeModel *model;
7632 filter_index = g_slist_index (impl->filters, filter);
7634 if (filter_index < 0)
7636 g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
7640 impl->filters = g_slist_remove (impl->filters, filter);
7642 if (filter == impl->current_filter)
7645 set_current_filter (impl, impl->filters->data);
7647 set_current_filter (impl, NULL);
7650 /* Remove row from the combo box */
7651 model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->filter_combo));
7652 if (!gtk_tree_model_iter_nth_child (model, &iter, NULL, filter_index))
7653 g_assert_not_reached ();
7655 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
7657 g_object_unref (filter);
7660 show_filters (impl, FALSE);
7664 gtk_file_chooser_default_list_filters (GtkFileChooser *chooser)
7666 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7668 return g_slist_copy (impl->filters);
7671 /* Returns the position in the shortcuts tree where the nth specified shortcut would appear */
7673 shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl,
7676 return pos + shortcuts_get_index (impl, SHORTCUTS_SHORTCUTS);
7679 struct AddShortcutData
7681 GtkFileChooserDefault *impl;
7686 add_shortcut_get_info_cb (GCancellable *cancellable,
7688 const GError *error,
7692 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
7693 struct AddShortcutData *data = user_data;
7695 if (!g_slist_find (data->impl->loading_shortcuts, cancellable))
7698 data->impl->loading_shortcuts = g_slist_remove (data->impl->loading_shortcuts, cancellable);
7700 if (cancelled || error || (! _gtk_file_info_consider_as_directory (info)))
7703 pos = shortcuts_get_pos_for_shortcut_folder (data->impl, data->impl->num_shortcuts);
7705 shortcuts_insert_file (data->impl, pos, SHORTCUT_TYPE_FILE, NULL, data->file, NULL, FALSE, SHORTCUTS_SHORTCUTS);
7708 g_object_unref (data->impl);
7709 g_object_unref (data->file);
7712 g_object_unref (cancellable);
7716 gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser *chooser,
7720 GCancellable *cancellable;
7721 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7722 struct AddShortcutData *data;
7726 /* Avoid adding duplicates */
7727 pos = shortcut_find_position (impl, file);
7728 if (pos >= 0 && pos < shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR))
7732 uri = g_file_get_uri (file);
7733 /* translators, "Shortcut" means "Bookmark" here */
7735 GTK_FILE_CHOOSER_ERROR,
7736 GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
7737 _("Shortcut %s already exists"),
7744 for (l = impl->loading_shortcuts; l; l = l->next)
7746 GCancellable *c = l->data;
7749 f = g_object_get_data (G_OBJECT (c), "add-shortcut-path-key");
7750 if (f && g_file_equal (file, f))
7754 uri = g_file_get_uri (file);
7756 GTK_FILE_CHOOSER_ERROR,
7757 GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
7758 _("Shortcut %s already exists"),
7766 data = g_new0 (struct AddShortcutData, 1);
7767 data->impl = g_object_ref (impl);
7768 data->file = g_object_ref (file);
7770 cancellable = _gtk_file_system_get_info (impl->file_system, file,
7772 add_shortcut_get_info_cb, data);
7777 impl->loading_shortcuts = g_slist_append (impl->loading_shortcuts, cancellable);
7778 g_object_set_data (G_OBJECT (cancellable), "add-shortcut-path-key", data->file);
7784 gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser *chooser,
7788 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7795 for (l = impl->loading_shortcuts; l; l = l->next)
7797 GCancellable *c = l->data;
7800 f = g_object_get_data (G_OBJECT (c), "add-shortcut-path-key");
7801 if (f && g_file_equal (file, f))
7803 impl->loading_shortcuts = g_slist_remove (impl->loading_shortcuts, c);
7804 g_cancellable_cancel (c);
7809 if (impl->num_shortcuts == 0)
7812 pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
7813 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
7814 g_assert_not_reached ();
7816 for (i = 0; i < impl->num_shortcuts; i++)
7819 ShortcutType shortcut_type;
7822 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
7823 SHORTCUTS_COL_DATA, &col_data,
7824 SHORTCUTS_COL_TYPE, &shortcut_type,
7826 g_assert (col_data != NULL);
7827 g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
7829 shortcut = col_data;
7830 if (g_file_equal (shortcut, file))
7832 shortcuts_remove_rows (impl, pos + i, 1);
7833 impl->num_shortcuts--;
7837 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
7838 g_assert_not_reached ();
7843 uri = g_file_get_uri (file);
7844 /* translators, "Shortcut" means "Bookmark" here */
7846 GTK_FILE_CHOOSER_ERROR,
7847 GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
7848 _("Shortcut %s does not exist"),
7856 gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser)
7858 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7864 if (impl->num_shortcuts == 0)
7867 pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
7868 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
7869 g_assert_not_reached ();
7873 for (i = 0; i < impl->num_shortcuts; i++)
7876 ShortcutType shortcut_type;
7879 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
7880 SHORTCUTS_COL_DATA, &col_data,
7881 SHORTCUTS_COL_TYPE, &shortcut_type,
7883 g_assert (col_data != NULL);
7884 g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
7886 shortcut = col_data;
7887 list = g_slist_prepend (list, g_object_ref (shortcut));
7889 if (i != impl->num_shortcuts - 1)
7891 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
7892 g_assert_not_reached ();
7896 return g_slist_reverse (list);
7899 /* Guesses a size based upon font sizes */
7901 find_good_size_from_style (GtkWidget *widget,
7905 GtkStyleContext *context;
7906 GtkStateFlags state;
7911 context = gtk_widget_get_style_context (widget);
7912 state = gtk_widget_get_state_flags (widget);
7914 screen = gtk_widget_get_screen (widget);
7917 resolution = gdk_screen_get_resolution (screen);
7918 if (resolution < 0.0) /* will be -1 if the resolution is not defined in the GdkScreen */
7922 resolution = 96.0; /* wheeee */
7924 font_size = pango_font_description_get_size (gtk_style_context_get_font (context, state));
7925 font_size = PANGO_PIXELS (font_size) * resolution / 72.0;
7927 *width = font_size * NUM_CHARS;
7928 *height = font_size * NUM_LINES;
7932 gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
7933 gint *default_width,
7934 gint *default_height)
7936 GtkFileChooserDefault *impl;
7939 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
7941 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
7942 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
7943 || impl->expand_folders)
7945 int x, y, width, height;
7947 settings_ensure (impl);
7949 g_settings_get (impl->settings, SETTINGS_KEY_WINDOW_POSITION, "(ii)", &x, &y);
7950 g_settings_get (impl->settings, SETTINGS_KEY_WINDOW_SIZE, "(ii)", &width, &height);
7952 if (x >= 0 && y >= 0 && width > 0 && height > 0)
7954 *default_width = width;
7955 *default_height = height;
7959 find_good_size_from_style (GTK_WIDGET (chooser_embed), default_width, default_height);
7961 if (impl->preview_widget_active &&
7962 impl->preview_widget &&
7963 gtk_widget_get_visible (impl->preview_widget))
7965 gtk_widget_get_preferred_size (impl->preview_box,
7967 *default_width += PREVIEW_HBOX_SPACING + req.width;
7970 if (impl->extra_widget &&
7971 gtk_widget_get_visible (impl->extra_widget))
7973 gtk_widget_get_preferred_size (impl->extra_align,
7975 *default_height += gtk_box_get_spacing (GTK_BOX (chooser_embed)) + req.height;
7980 gtk_widget_get_preferred_size (GTK_WIDGET (impl),
7982 *default_width = req.width;
7983 *default_height = req.height;
7987 struct switch_folder_closure {
7988 GtkFileChooserDefault *impl;
7993 /* Used from gtk_tree_selection_selected_foreach() in switch_to_selected_folder() */
7995 switch_folder_foreach_cb (GtkTreeModel *model,
8000 struct switch_folder_closure *closure;
8004 closure->file = _gtk_file_system_model_get_file (closure->impl->browse_files_model, iter);
8005 closure->num_selected++;
8008 /* Changes to the selected folder in the list view */
8010 switch_to_selected_folder (GtkFileChooserDefault *impl)
8012 GtkTreeSelection *selection;
8013 struct switch_folder_closure closure;
8015 /* We do this with foreach() rather than get_selected() as we may be in
8016 * multiple selection mode
8019 closure.impl = impl;
8020 closure.file = NULL;
8021 closure.num_selected = 0;
8023 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
8024 gtk_tree_selection_selected_foreach (selection, switch_folder_foreach_cb, &closure);
8026 g_assert (closure.file && closure.num_selected == 1);
8028 change_folder_and_display_error (impl, closure.file, FALSE);
8031 /* Gets the GFileInfo for the selected row in the file list; assumes single
8035 get_selected_file_info_from_file_list (GtkFileChooserDefault *impl,
8036 gboolean *had_selection)
8038 GtkTreeSelection *selection;
8042 g_assert (!impl->select_multiple);
8043 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
8044 if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
8046 *had_selection = FALSE;
8050 *had_selection = TRUE;
8052 info = _gtk_file_system_model_get_info (impl->browse_files_model, &iter);
8056 /* Gets the display name of the selected file in the file list; assumes single
8057 * selection mode and that something is selected.
8059 static const gchar *
8060 get_display_name_from_file_list (GtkFileChooserDefault *impl)
8063 gboolean had_selection;
8065 info = get_selected_file_info_from_file_list (impl, &had_selection);
8066 g_assert (had_selection);
8067 g_assert (info != NULL);
8069 return g_file_info_get_display_name (info);
8073 add_custom_button_to_dialog (GtkDialog *dialog,
8074 const gchar *mnemonic_label,
8075 const gchar *stock_id,
8080 button = gtk_button_new_with_mnemonic (mnemonic_label);
8081 gtk_widget_set_can_default (button, TRUE);
8082 gtk_button_set_image (GTK_BUTTON (button),
8083 gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON));
8084 gtk_widget_show (button);
8086 gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, response_id);
8089 /* Presents an overwrite confirmation dialog; returns whether we should accept
8093 confirm_dialog_should_accept_filename (GtkFileChooserDefault *impl,
8094 const gchar *file_part,
8095 const gchar *folder_display_name)
8097 GtkWindow *toplevel;
8101 toplevel = get_toplevel (GTK_WIDGET (impl));
8103 dialog = gtk_message_dialog_new (toplevel,
8104 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
8105 GTK_MESSAGE_QUESTION,
8107 _("A file named \"%s\" already exists. Do you want to replace it?"),
8109 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
8110 _("The file already exists in \"%s\". Replacing it will "
8111 "overwrite its contents."),
8112 folder_display_name);
8114 gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
8115 add_custom_button_to_dialog (GTK_DIALOG (dialog), _("_Replace"),
8116 GTK_STOCK_SAVE_AS, GTK_RESPONSE_ACCEPT);
8117 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
8118 GTK_RESPONSE_ACCEPT,
8119 GTK_RESPONSE_CANCEL,
8121 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
8123 if (gtk_window_has_group (toplevel))
8124 gtk_window_group_add_window (gtk_window_get_group (toplevel),
8125 GTK_WINDOW (dialog));
8127 response = gtk_dialog_run (GTK_DIALOG (dialog));
8129 gtk_widget_destroy (dialog);
8131 return (response == GTK_RESPONSE_ACCEPT);
8134 struct GetDisplayNameData
8136 GtkFileChooserDefault *impl;
8141 confirmation_confirm_get_info_cb (GCancellable *cancellable,
8143 const GError *error,
8146 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
8147 gboolean should_respond = FALSE;
8148 struct GetDisplayNameData *data = user_data;
8150 if (cancellable != data->impl->should_respond_get_info_cancellable)
8153 data->impl->should_respond_get_info_cancellable = NULL;
8159 /* Huh? Did the folder disappear? Let the caller deal with it */
8160 should_respond = TRUE;
8162 should_respond = confirm_dialog_should_accept_filename (data->impl, data->file_part, g_file_info_get_display_name (info));
8164 set_busy_cursor (data->impl, FALSE);
8166 g_signal_emit_by_name (data->impl, "response-requested");
8169 g_object_unref (data->impl);
8170 g_free (data->file_part);
8173 g_object_unref (cancellable);
8176 /* Does overwrite confirmation if appropriate, and returns whether the dialog
8177 * should respond. Can get the file part from the file list or the save entry.
8180 should_respond_after_confirm_overwrite (GtkFileChooserDefault *impl,
8181 const gchar *file_part,
8184 GtkFileChooserConfirmation conf;
8186 if (!impl->do_overwrite_confirmation)
8189 conf = GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM;
8191 g_signal_emit_by_name (impl, "confirm-overwrite", &conf);
8195 case GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM:
8197 struct GetDisplayNameData *data;
8199 g_assert (file_part != NULL);
8201 data = g_new0 (struct GetDisplayNameData, 1);
8202 data->impl = g_object_ref (impl);
8203 data->file_part = g_strdup (file_part);
8205 if (impl->should_respond_get_info_cancellable)
8206 g_cancellable_cancel (impl->should_respond_get_info_cancellable);
8208 impl->should_respond_get_info_cancellable =
8209 _gtk_file_system_get_info (impl->file_system, parent_file,
8210 "standard::display-name",
8211 confirmation_confirm_get_info_cb,
8213 set_busy_cursor (data->impl, TRUE);
8217 case GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME:
8220 case GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN:
8224 g_assert_not_reached ();
8229 struct FileExistsData
8231 GtkFileChooserDefault *impl;
8232 gboolean file_exists_and_is_not_folder;
8238 name_entry_get_parent_info_cb (GCancellable *cancellable,
8240 const GError *error,
8243 gboolean parent_is_folder;
8244 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
8245 struct FileExistsData *data = user_data;
8247 if (cancellable != data->impl->should_respond_get_info_cancellable)
8250 data->impl->should_respond_get_info_cancellable = NULL;
8252 set_busy_cursor (data->impl, FALSE);
8258 parent_is_folder = FALSE;
8260 parent_is_folder = _gtk_file_info_consider_as_directory (info);
8262 if (parent_is_folder)
8264 if (data->impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
8266 g_signal_emit_by_name (data->impl, "response-requested"); /* even if the file doesn't exist, apps can make good use of that (e.g. Emacs) */
8268 else if (data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8270 if (data->file_exists_and_is_not_folder)
8275 /* Dup the string because the string may be modified
8276 * depending on what clients do in the confirm-overwrite
8277 * signal and this corrupts the pointer
8279 file_part = g_strdup (_gtk_file_chooser_entry_get_file_part (GTK_FILE_CHOOSER_ENTRY (data->impl->location_entry)));
8280 retval = should_respond_after_confirm_overwrite (data->impl, file_part, data->parent_file);
8284 g_signal_emit_by_name (data->impl, "response-requested");
8287 g_signal_emit_by_name (data->impl, "response-requested");
8289 else if (data->impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
8290 || data->impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8292 GError *mkdir_error = NULL;
8294 /* In both cases (SELECT_FOLDER and CREATE_FOLDER), if you type
8295 * "/blah/nonexistent" you *will* want a folder created.
8298 set_busy_cursor (data->impl, TRUE);
8299 g_file_make_directory (data->file, NULL, &mkdir_error);
8300 set_busy_cursor (data->impl, FALSE);
8303 g_signal_emit_by_name (data->impl, "response-requested");
8305 error_creating_folder_dialog (data->impl, data->file, mkdir_error);
8308 g_assert_not_reached ();
8314 /* The parent exists, but it's not a folder! Someone probably typed existing_file.txt/subfile.txt */
8315 error_with_file_under_nonfolder (data->impl, data->parent_file);
8321 /* The parent folder is not readable for some reason */
8323 error_copy = g_error_copy (error);
8324 error_changing_folder_dialog (data->impl, data->parent_file, error_copy);
8329 g_object_unref (data->impl);
8330 g_object_unref (data->file);
8331 g_object_unref (data->parent_file);
8334 g_object_unref (cancellable);
8338 file_exists_get_info_cb (GCancellable *cancellable,
8340 const GError *error,
8343 gboolean data_ownership_taken = FALSE;
8344 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
8345 gboolean file_exists;
8347 gboolean needs_parent_check = FALSE;
8348 struct FileExistsData *data = user_data;
8350 if (cancellable != data->impl->file_exists_get_info_cancellable)
8353 data->impl->file_exists_get_info_cancellable = NULL;
8355 set_busy_cursor (data->impl, FALSE);
8360 file_exists = (info != NULL);
8361 is_folder = (file_exists && _gtk_file_info_consider_as_directory (info));
8363 if (data->impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
8366 change_folder_and_display_error (data->impl, data->file, TRUE);
8370 g_signal_emit_by_name (data->impl, "response-requested"); /* user typed an existing filename; we are done */
8372 needs_parent_check = TRUE; /* file doesn't exist; see if its parent exists */
8375 else if (data->impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8377 if (file_exists && !is_folder)
8379 /* Oops, the user typed the name of an existing path which is not
8382 error_creating_folder_over_existing_file_dialog (data->impl, data->file,
8383 g_error_copy (error));
8387 needs_parent_check = TRUE;
8390 else if (data->impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8394 needs_parent_check = TRUE;
8400 /* User typed a folder; we are done */
8401 g_signal_emit_by_name (data->impl, "response-requested");
8404 error_selecting_folder_over_existing_file_dialog (data->impl, data->file);
8407 else if (data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8410 change_folder_and_display_error (data->impl, data->file, TRUE);
8412 needs_parent_check = TRUE;
8416 g_assert_not_reached();
8419 if (needs_parent_check) {
8420 /* check that everything up to the last path component exists (i.e. the parent) */
8422 data->file_exists_and_is_not_folder = file_exists && !is_folder;
8423 data_ownership_taken = TRUE;
8425 if (data->impl->should_respond_get_info_cancellable)
8426 g_cancellable_cancel (data->impl->should_respond_get_info_cancellable);
8428 data->impl->should_respond_get_info_cancellable =
8429 _gtk_file_system_get_info (data->impl->file_system,
8432 name_entry_get_parent_info_cb,
8434 set_busy_cursor (data->impl, TRUE);
8438 if (!data_ownership_taken)
8440 g_object_unref (data->impl);
8441 g_object_unref (data->file);
8442 g_object_unref (data->parent_file);
8446 g_object_unref (cancellable);
8450 paste_text_received (GtkClipboard *clipboard,
8452 GtkFileChooserDefault *impl)
8459 file = g_file_new_for_uri (text);
8461 if (!gtk_file_chooser_default_select_file (GTK_FILE_CHOOSER (impl), file, NULL))
8462 location_popup_handler (impl, text);
8464 g_object_unref (file);
8467 /* Handler for the "location-popup-on-paste" keybinding signal */
8469 location_popup_on_paste_handler (GtkFileChooserDefault *impl)
8471 GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (impl),
8472 GDK_SELECTION_CLIPBOARD);
8473 gtk_clipboard_request_text (clipboard,
8474 (GtkClipboardTextReceivedFunc) paste_text_received,
8479 /* Implementation for GtkFileChooserEmbed::should_respond() */
8481 gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
8483 GtkFileChooserDefault *impl;
8484 GtkWidget *toplevel;
8485 GtkWidget *current_focus;
8487 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
8489 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
8490 g_assert (GTK_IS_WINDOW (toplevel));
8492 current_focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
8494 if (current_focus == impl->browse_files_tree_view)
8496 /* The following array encodes what we do based on the impl->action and the
8497 * number of files selected.
8500 NOOP, /* Do nothing (don't respond) */
8501 RESPOND, /* Respond immediately */
8502 RESPOND_OR_SWITCH, /* Respond immediately if the selected item is a file; switch to it if it is a folder */
8503 ALL_FILES, /* Respond only if everything selected is a file */
8504 ALL_FOLDERS, /* Respond only if everything selected is a folder */
8505 SAVE_ENTRY, /* Go to the code for handling the save entry */
8506 NOT_REACHED /* Sanity check */
8508 static const ActionToTake what_to_do[4][3] = {
8509 /* 0 selected 1 selected many selected */
8510 /* ACTION_OPEN */ { NOOP, RESPOND_OR_SWITCH, ALL_FILES },
8511 /* ACTION_SAVE */ { SAVE_ENTRY, RESPOND_OR_SWITCH, NOT_REACHED },
8512 /* ACTION_SELECT_FOLDER */ { RESPOND, ALL_FOLDERS, ALL_FOLDERS },
8513 /* ACTION_CREATE_FOLDER */ { SAVE_ENTRY, ALL_FOLDERS, NOT_REACHED }
8517 gboolean all_files, all_folders;
8519 ActionToTake action;
8523 g_assert (impl->action >= GTK_FILE_CHOOSER_ACTION_OPEN && impl->action <= GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
8525 if (impl->operation_mode == OPERATION_MODE_SEARCH)
8526 return search_should_respond (impl);
8528 if (impl->operation_mode == OPERATION_MODE_RECENT)
8529 return recent_should_respond (impl);
8531 selection_check (impl, &num_selected, &all_files, &all_folders);
8533 if (num_selected > 2)
8538 action = what_to_do [impl->action] [k];
8548 case RESPOND_OR_SWITCH:
8549 g_assert (num_selected == 1);
8553 switch_to_selected_folder (impl);
8556 else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8557 return should_respond_after_confirm_overwrite (impl,
8558 get_display_name_from_file_list (impl),
8559 impl->current_folder);
8573 g_assert_not_reached ();
8576 else if ((impl->location_entry != NULL) && (current_focus == impl->location_entry))
8579 gboolean is_well_formed, is_empty, is_file_part_empty;
8582 GtkFileChooserEntry *entry;
8587 g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
8588 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
8589 || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
8590 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8591 && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY));
8593 entry = GTK_FILE_CHOOSER_ENTRY (impl->location_entry);
8594 check_save_entry (impl, &file, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder);
8596 if (!is_well_formed)
8601 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8607 g_assert (file != NULL);
8612 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
8613 impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8615 change_folder_and_display_error (impl, file, TRUE);
8618 else if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
8619 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8621 /* The folder already exists, so we do not need to create it.
8622 * Just respond to terminate the dialog.
8628 g_assert_not_reached ();
8634 struct FileExistsData *data;
8636 /* We need to check whether file exists and whether it is a folder -
8637 * the GtkFileChooserEntry *does* report is_folder==FALSE as a false
8638 * negative (it doesn't know yet if your last path component is a
8642 data = g_new0 (struct FileExistsData, 1);
8643 data->impl = g_object_ref (impl);
8644 data->file = g_object_ref (file);
8645 data->parent_file = g_object_ref (_gtk_file_chooser_entry_get_current_folder (entry));
8647 if (impl->file_exists_get_info_cancellable)
8648 g_cancellable_cancel (impl->file_exists_get_info_cancellable);
8650 impl->file_exists_get_info_cancellable =
8651 _gtk_file_system_get_info (impl->file_system, file,
8653 file_exists_get_info_cb,
8656 set_busy_cursor (impl, TRUE);
8660 g_error_free (error);
8663 g_object_unref (file);
8666 else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
8668 /* The focus is on a dialog's action area button, *and* the widget that
8669 * was focused immediately before it is the file list.
8673 else if (impl->operation_mode == OPERATION_MODE_SEARCH && impl->toplevel_last_focus_widget == impl->search_entry)
8675 search_entry_activate_cb (GTK_ENTRY (impl->search_entry), impl);
8678 else if (impl->location_entry && impl->toplevel_last_focus_widget == impl->location_entry)
8680 /* The focus is on a dialog's action area button, *and* the widget that
8681 * was focused immediately before it is the location entry.
8686 /* The focus is on a dialog's action area button or something else */
8687 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
8688 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8693 g_assert_not_reached ();
8697 /* Implementation for GtkFileChooserEmbed::initial_focus() */
8699 gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed)
8701 GtkFileChooserDefault *impl;
8704 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
8706 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
8707 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8709 if (impl->location_mode == LOCATION_MODE_PATH_BAR)
8710 widget = impl->browse_files_tree_view;
8712 widget = impl->location_entry;
8714 else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
8715 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8716 widget = impl->location_entry;
8719 g_assert_not_reached ();
8723 g_assert (widget != NULL);
8724 gtk_widget_grab_focus (widget);
8727 /* Callback used from gtk_tree_selection_selected_foreach(); gets the selected GFiles */
8729 search_selected_foreach_get_file_cb (GtkTreeModel *model,
8739 gtk_tree_model_get (model, iter, MODEL_COL_FILE, &file, -1);
8740 *list = g_slist_prepend (*list, file); /* The file already has a new ref courtesy of gtk_tree_model_get(); this will be unreffed by the caller */
8743 /* Constructs a list of the selected paths in search mode */
8745 search_get_selected_files (GtkFileChooserDefault *impl)
8748 GtkTreeSelection *selection;
8752 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
8753 gtk_tree_selection_selected_foreach (selection, search_selected_foreach_get_file_cb, &result);
8754 result = g_slist_reverse (result);
8759 /* Called from ::should_respond(). We return whether there are selected files
8760 * in the search list.
8763 search_should_respond (GtkFileChooserDefault *impl)
8765 GtkTreeSelection *selection;
8767 g_assert (impl->operation_mode == OPERATION_MODE_SEARCH);
8769 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
8770 return (gtk_tree_selection_count_selected_rows (selection) != 0);
8773 /* Adds one hit from the search engine to the search_model */
8775 search_add_hit (GtkFileChooserDefault *impl,
8780 file = g_file_new_for_uri (uri);
8784 if (!g_file_is_native (file))
8786 g_object_unref (file);
8790 _gtk_file_system_model_add_and_query_file (impl->search_model,
8794 g_object_unref (file);
8797 /* Callback used from GtkSearchEngine when we get new hits */
8799 search_engine_hits_added_cb (GtkSearchEngine *engine,
8803 GtkFileChooserDefault *impl;
8806 impl = GTK_FILE_CHOOSER_DEFAULT (data);
8808 for (l = hits; l; l = l->next)
8809 search_add_hit (impl, (gchar*)l->data);
8812 /* Callback used from GtkSearchEngine when the query is done running */
8814 search_engine_finished_cb (GtkSearchEngine *engine,
8817 GtkFileChooserDefault *impl;
8819 impl = GTK_FILE_CHOOSER_DEFAULT (data);
8822 /* EB: setting the model here will avoid loads of row events,
8823 * but it'll make the search look like blocked.
8825 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
8826 GTK_TREE_MODEL (impl->search_model));
8829 /* FMQ: if search was empty, say that we got no hits */
8830 set_busy_cursor (impl, FALSE);
8833 /* Displays a generic error when we cannot create a GtkSearchEngine.
8834 * It would be better if _gtk_search_engine_new() gave us a GError
8835 * with a better message, but it doesn't do that right now.
8838 search_error_could_not_create_client (GtkFileChooserDefault *impl)
8840 error_message (impl,
8841 _("Could not start the search process"),
8842 _("The program was not able to create a connection to the indexer "
8843 "daemon. Please make sure it is running."));
8847 search_engine_error_cb (GtkSearchEngine *engine,
8848 const gchar *message,
8851 GtkFileChooserDefault *impl;
8853 impl = GTK_FILE_CHOOSER_DEFAULT (data);
8855 search_stop_searching (impl, TRUE);
8856 error_message (impl, _("Could not send the search request"), message);
8858 set_busy_cursor (impl, FALSE);
8861 /* Frees the data in the search_model */
8863 search_clear_model (GtkFileChooserDefault *impl,
8864 gboolean remove_from_treeview)
8866 if (!impl->search_model)
8869 g_object_unref (impl->search_model);
8870 impl->search_model = NULL;
8872 if (remove_from_treeview)
8873 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
8876 /* Stops any ongoing searches; does not touch the search_model */
8878 search_stop_searching (GtkFileChooserDefault *impl,
8879 gboolean remove_query)
8881 if (remove_query && impl->search_query)
8883 g_object_unref (impl->search_query);
8884 impl->search_query = NULL;
8887 if (impl->search_engine)
8889 _gtk_search_engine_stop (impl->search_engine);
8891 g_object_unref (impl->search_engine);
8892 impl->search_engine = NULL;
8896 /* Stops any pending searches, clears the file list, and switches back to OPERATION_MODE_BROWSE */
8898 search_switch_to_browse_mode (GtkFileChooserDefault *impl)
8900 g_assert (impl->operation_mode != OPERATION_MODE_BROWSE);
8902 search_stop_searching (impl, FALSE);
8903 search_clear_model (impl, TRUE);
8905 gtk_widget_destroy (impl->search_hbox);
8906 impl->search_hbox = NULL;
8907 impl->search_entry = NULL;
8909 gtk_widget_show (impl->browse_path_bar);
8910 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN || !impl->create_folders)
8911 gtk_widget_hide (impl->browse_new_folder_button);
8913 gtk_widget_show (impl->browse_new_folder_button);
8916 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
8917 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8919 gtk_widget_show (impl->location_button);
8921 if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
8922 gtk_widget_show (impl->location_entry_box);
8925 impl->operation_mode = OPERATION_MODE_BROWSE;
8927 file_list_set_sort_column_ids (impl);
8930 /* Creates the search_model and puts it in the tree view */
8932 search_setup_model (GtkFileChooserDefault *impl)
8934 g_assert (impl->search_model == NULL);
8936 impl->search_model = _gtk_file_system_model_new (file_system_model_set,
8938 MODEL_COLUMN_TYPES);
8940 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model),
8944 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model),
8948 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model),
8952 set_sort_column (impl);
8954 /* EB: setting the model here will make the hits list update feel
8955 * more "alive" than setting the model at the end of the search
8958 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
8959 GTK_TREE_MODEL (impl->search_model));
8962 /* Creates a new query with the specified text and launches it */
8964 search_start_query (GtkFileChooserDefault *impl,
8965 const gchar *query_text)
8967 search_stop_searching (impl, FALSE);
8968 search_clear_model (impl, TRUE);
8969 search_setup_model (impl);
8970 set_busy_cursor (impl, TRUE);
8972 if (impl->search_engine == NULL)
8973 impl->search_engine = _gtk_search_engine_new ();
8975 if (!impl->search_engine)
8977 set_busy_cursor (impl, FALSE);
8978 search_error_could_not_create_client (impl); /* lame; we don't get an error code or anything */
8982 if (!impl->search_query)
8984 impl->search_query = _gtk_query_new ();
8985 _gtk_query_set_text (impl->search_query, query_text);
8988 _gtk_search_engine_set_query (impl->search_engine, impl->search_query);
8990 g_signal_connect (impl->search_engine, "hits-added",
8991 G_CALLBACK (search_engine_hits_added_cb), impl);
8992 g_signal_connect (impl->search_engine, "finished",
8993 G_CALLBACK (search_engine_finished_cb), impl);
8994 g_signal_connect (impl->search_engine, "error",
8995 G_CALLBACK (search_engine_error_cb), impl);
8997 _gtk_search_engine_start (impl->search_engine);
9000 /* Callback used when the user presses Enter while typing on the search
9001 * entry; starts the query
9004 search_entry_activate_cb (GtkEntry *entry,
9007 GtkFileChooserDefault *impl;
9010 impl = GTK_FILE_CHOOSER_DEFAULT (data);
9012 text = gtk_entry_get_text (GTK_ENTRY (impl->search_entry));
9013 if (strlen (text) == 0)
9016 /* reset any existing query object */
9017 if (impl->search_query)
9019 g_object_unref (impl->search_query);
9020 impl->search_query = NULL;
9023 search_start_query (impl, text);
9027 focus_entry_idle_cb (GtkFileChooserDefault *impl)
9029 GDK_THREADS_ENTER ();
9031 g_source_destroy (impl->focus_entry_idle);
9032 impl->focus_entry_idle = NULL;
9034 if (impl->search_entry)
9035 gtk_widget_grab_focus (impl->search_entry);
9037 GDK_THREADS_LEAVE ();
9043 focus_search_entry_in_idle (GtkFileChooserDefault *impl)
9045 /* bgo#634558 - When the user clicks on the Search entry in the shortcuts
9046 * pane, we get a selection-changed signal and we set up the search widgets.
9047 * However, gtk_tree_view_button_press() focuses the treeview *after* making
9048 * the change to the selection. So, we need to re-focus the search entry
9049 * after the treeview has finished doing its work; we'll do that in an idle
9053 if (!impl->focus_entry_idle)
9054 impl->focus_entry_idle = add_idle_while_impl_is_alive (impl, G_CALLBACK (focus_entry_idle_cb));
9057 /* Hides the path bar and creates the search entry */
9059 search_setup_widgets (GtkFileChooserDefault *impl)
9065 impl->search_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
9069 image = gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_BUTTON);
9070 gtk_box_pack_start (GTK_BOX (impl->search_hbox), image, FALSE, FALSE, 5);
9074 label = gtk_label_new (NULL);
9075 tmp = g_strdup_printf ("<b>%s</b>", _("Search:"));
9076 gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), tmp);
9077 gtk_box_pack_start (GTK_BOX (impl->search_hbox), label, FALSE, FALSE, 0);
9082 impl->search_entry = gtk_entry_new ();
9083 gtk_label_set_mnemonic_widget (GTK_LABEL (label), impl->search_entry);
9084 g_signal_connect (impl->search_entry, "activate",
9085 G_CALLBACK (search_entry_activate_cb),
9087 gtk_box_pack_start (GTK_BOX (impl->search_hbox), impl->search_entry, TRUE, TRUE, 0);
9089 /* if there already is a query, restart it */
9090 if (impl->search_query)
9092 gchar *query = _gtk_query_get_text (impl->search_query);
9096 gtk_entry_set_text (GTK_ENTRY (impl->search_entry), query);
9097 search_start_query (impl, query);
9103 g_object_unref (impl->search_query);
9104 impl->search_query = NULL;
9108 gtk_widget_hide (impl->browse_path_bar);
9109 gtk_widget_hide (impl->browse_new_folder_button);
9111 /* Box for search widgets */
9112 gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->search_hbox, TRUE, TRUE, 0);
9113 gtk_widget_show_all (impl->search_hbox);
9114 gtk_size_group_add_widget (GTK_SIZE_GROUP (impl->browse_path_bar_size_group), impl->search_hbox);
9116 /* Hide the location widgets temporarily */
9118 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
9119 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
9121 gtk_widget_hide (impl->location_button);
9122 gtk_widget_hide (impl->location_entry_box);
9125 focus_search_entry_in_idle (impl);
9127 /* FMQ: hide the filter combo? */
9130 /* Stops running operations like populating the browse model, searches, and the recent-files model */
9132 stop_operation (GtkFileChooserDefault *impl, OperationMode mode)
9136 case OPERATION_MODE_BROWSE:
9137 stop_loading_and_clear_list_model (impl, TRUE);
9140 case OPERATION_MODE_SEARCH:
9141 search_stop_searching (impl, FALSE);
9142 search_clear_model (impl, TRUE);
9144 gtk_widget_destroy (impl->search_hbox);
9145 impl->search_hbox = NULL;
9146 impl->search_entry = NULL;
9149 case OPERATION_MODE_RECENT:
9150 recent_stop_loading (impl);
9151 recent_clear_model (impl, TRUE);
9153 gtk_widget_destroy (impl->recent_hbox);
9154 impl->recent_hbox = NULL;
9159 /* Main entry point to the searching functions; this gets called when the user
9160 * activates the Search shortcut.
9163 search_activate (GtkFileChooserDefault *impl)
9165 OperationMode previous_mode;
9167 if (impl->operation_mode == OPERATION_MODE_SEARCH)
9169 focus_search_entry_in_idle (impl);
9173 previous_mode = impl->operation_mode;
9174 impl->operation_mode = OPERATION_MODE_SEARCH;
9176 stop_operation (impl, previous_mode);
9178 g_assert (impl->search_hbox == NULL);
9179 g_assert (impl->search_entry == NULL);
9180 g_assert (impl->search_model == NULL);
9182 search_setup_widgets (impl);
9183 file_list_set_sort_column_ids (impl);
9187 * Recent files support
9190 /* Frees the data in the recent_model */
9192 recent_clear_model (GtkFileChooserDefault *impl,
9193 gboolean remove_from_treeview)
9195 if (!impl->recent_model)
9198 if (remove_from_treeview)
9199 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
9201 g_object_unref (impl->recent_model);
9202 impl->recent_model = NULL;
9205 /* Stops any ongoing loading of the recent files list; does
9206 * not touch the recent_model
9209 recent_stop_loading (GtkFileChooserDefault *impl)
9211 if (impl->load_recent_id)
9213 g_source_remove (impl->load_recent_id);
9214 impl->load_recent_id = 0;
9218 /* Stops any pending load, clears the file list, and switches
9219 * back to OPERATION_MODE_BROWSE
9222 recent_switch_to_browse_mode (GtkFileChooserDefault *impl)
9224 g_assert (impl->operation_mode != OPERATION_MODE_BROWSE);
9226 recent_stop_loading (impl);
9227 recent_clear_model (impl, TRUE);
9229 gtk_widget_destroy (impl->recent_hbox);
9230 impl->recent_hbox = NULL;
9232 gtk_widget_show (impl->browse_path_bar);
9233 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN || !impl->create_folders)
9234 gtk_widget_hide (impl->browse_new_folder_button);
9236 gtk_widget_show (impl->browse_new_folder_button);
9238 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
9239 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
9241 gtk_widget_show (impl->location_button);
9243 if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
9244 gtk_widget_show (impl->location_entry_box);
9247 gtk_tree_view_column_set_visible (impl->list_size_column, impl->show_size_column);
9249 impl->operation_mode = OPERATION_MODE_BROWSE;
9251 file_list_set_sort_column_ids (impl);
9255 recent_setup_model (GtkFileChooserDefault *impl)
9257 g_assert (impl->recent_model == NULL);
9259 impl->recent_model = _gtk_file_system_model_new (file_system_model_set,
9261 MODEL_COLUMN_TYPES);
9263 _gtk_file_system_model_set_filter (impl->recent_model,
9264 impl->current_filter);
9265 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model),
9269 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model),
9273 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model),
9277 set_sort_column (impl);
9282 GtkFileChooserDefault *impl;
9285 gint n_loaded_items;
9286 guint needs_sorting : 1;
9290 recent_idle_cleanup (gpointer data)
9292 RecentLoadData *load_data = data;
9293 GtkFileChooserDefault *impl = load_data->impl;
9295 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
9296 GTK_TREE_MODEL (impl->recent_model));
9298 set_busy_cursor (impl, FALSE);
9300 impl->load_recent_id = 0;
9302 if (load_data->items)
9304 g_list_foreach (load_data->items, (GFunc) gtk_recent_info_unref, NULL);
9305 g_list_free (load_data->items);
9312 recent_sort_mru (gconstpointer a,
9315 GtkRecentInfo *info_a = (GtkRecentInfo *) a;
9316 GtkRecentInfo *info_b = (GtkRecentInfo *) b;
9318 return (gtk_recent_info_get_modified (info_b) - gtk_recent_info_get_modified (info_a));
9322 get_recent_files_limit (GtkWidget *widget)
9324 GtkSettings *settings;
9327 if (gtk_widget_has_screen (widget))
9328 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (widget));
9330 settings = gtk_settings_get_default ();
9332 g_object_get (G_OBJECT (settings), "gtk-recent-files-limit", &limit, NULL);
9338 recent_idle_load (gpointer data)
9340 RecentLoadData *load_data = data;
9341 GtkFileChooserDefault *impl = load_data->impl;
9345 if (!impl->recent_manager)
9348 /* first iteration: load all the items */
9349 if (!load_data->items)
9351 load_data->items = gtk_recent_manager_get_items (impl->recent_manager);
9352 if (!load_data->items)
9355 load_data->needs_sorting = TRUE;
9360 /* second iteration: preliminary MRU sorting and clamping */
9361 if (load_data->needs_sorting)
9365 load_data->items = g_list_sort (load_data->items, recent_sort_mru);
9366 load_data->n_items = g_list_length (load_data->items);
9368 limit = get_recent_files_limit (GTK_WIDGET (impl));
9370 if (limit != -1 && (load_data->n_items > limit))
9374 clamp = g_list_nth (load_data->items, limit - 1);
9375 if (G_LIKELY (clamp))
9380 g_list_foreach (l, (GFunc) gtk_recent_info_unref, NULL);
9383 load_data->n_items = limit;
9387 load_data->n_loaded_items = 0;
9388 load_data->needs_sorting = FALSE;
9393 /* finished loading items */
9394 for (walk = load_data->items; walk; walk = walk->next)
9396 GtkRecentInfo *info = walk->data;
9397 file = g_file_new_for_uri (gtk_recent_info_get_uri (info));
9399 _gtk_file_system_model_add_and_query_file (impl->recent_model,
9402 gtk_recent_info_unref (walk->data);
9403 g_object_unref (file);
9406 g_list_free (load_data->items);
9407 load_data->items = NULL;
9413 recent_start_loading (GtkFileChooserDefault *impl)
9415 RecentLoadData *load_data;
9417 recent_stop_loading (impl);
9418 recent_clear_model (impl, TRUE);
9419 recent_setup_model (impl);
9420 set_busy_cursor (impl, TRUE);
9422 g_assert (impl->load_recent_id == 0);
9424 load_data = g_new (RecentLoadData, 1);
9425 load_data->impl = impl;
9426 load_data->items = NULL;
9427 load_data->n_items = 0;
9428 load_data->n_loaded_items = 0;
9429 load_data->needs_sorting = TRUE;
9431 /* begin lazy loading the recent files into the model */
9432 impl->load_recent_id = gdk_threads_add_idle_full (G_PRIORITY_HIGH_IDLE + 30,
9435 recent_idle_cleanup);
9439 recent_selected_foreach_get_file_cb (GtkTreeModel *model,
9449 gtk_tree_model_get (model, iter, MODEL_COL_FILE, &file, -1);
9450 *list = g_slist_prepend (*list, file);
9453 /* Constructs a list of the selected paths in recent files mode */
9455 recent_get_selected_files (GtkFileChooserDefault *impl)
9458 GtkTreeSelection *selection;
9462 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
9463 gtk_tree_selection_selected_foreach (selection, recent_selected_foreach_get_file_cb, &result);
9464 result = g_slist_reverse (result);
9469 /* Called from ::should_respond(). We return whether there are selected
9470 * files in the recent files list.
9473 recent_should_respond (GtkFileChooserDefault *impl)
9475 GtkTreeSelection *selection;
9477 g_assert (impl->operation_mode == OPERATION_MODE_RECENT);
9479 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
9480 return (gtk_tree_selection_count_selected_rows (selection) != 0);
9483 /* Hide the location widgets temporarily */
9485 recent_hide_entry (GtkFileChooserDefault *impl)
9491 impl->recent_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
9494 image = gtk_image_new_from_icon_name ("document-open-recent", GTK_ICON_SIZE_BUTTON);
9495 gtk_box_pack_start (GTK_BOX (impl->recent_hbox), image, FALSE, FALSE, 5);
9498 label = gtk_label_new (NULL);
9499 tmp = g_strdup_printf ("<b>%s</b>", _("Recently Used"));
9500 gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), tmp);
9501 gtk_box_pack_start (GTK_BOX (impl->recent_hbox), label, FALSE, FALSE, 0);
9504 gtk_widget_hide (impl->browse_path_bar);
9505 gtk_widget_hide (impl->browse_new_folder_button);
9507 /* Box for recent widgets */
9508 gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->recent_hbox, TRUE, TRUE, 0);
9509 gtk_size_group_add_widget (impl->browse_path_bar_size_group, impl->recent_hbox);
9510 gtk_widget_show_all (impl->recent_hbox);
9512 /* Hide the location widgets temporarily */
9513 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
9514 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
9516 gtk_widget_hide (impl->location_button);
9517 gtk_widget_hide (impl->location_entry_box);
9521 /* Main entry point to the recent files functions; this gets called when
9522 * the user activates the Recently Used shortcut.
9525 recent_activate (GtkFileChooserDefault *impl)
9527 OperationMode previous_mode;
9529 if (impl->operation_mode == OPERATION_MODE_RECENT)
9532 previous_mode = impl->operation_mode;
9533 impl->operation_mode = OPERATION_MODE_RECENT;
9535 stop_operation (impl, previous_mode);
9537 recent_hide_entry (impl);
9539 file_list_set_sort_column_ids (impl);
9540 recent_start_loading (impl);
9544 set_current_filter (GtkFileChooserDefault *impl,
9545 GtkFileFilter *filter)
9547 if (impl->current_filter != filter)
9551 /* NULL filters are allowed to reset to non-filtered status
9553 filter_index = g_slist_index (impl->filters, filter);
9554 if (impl->filters && filter && filter_index < 0)
9557 if (impl->current_filter)
9558 g_object_unref (impl->current_filter);
9559 impl->current_filter = filter;
9560 if (impl->current_filter)
9562 g_object_ref_sink (impl->current_filter);
9566 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
9569 if (impl->browse_files_model)
9570 install_list_model_filter (impl);
9572 if (impl->search_model)
9573 _gtk_file_system_model_set_filter (impl->search_model, filter);
9575 if (impl->recent_model)
9576 _gtk_file_system_model_set_filter (impl->recent_model, filter);
9578 g_object_notify (G_OBJECT (impl), "filter");
9583 filter_combo_changed (GtkComboBox *combo_box,
9584 GtkFileChooserDefault *impl)
9586 gint new_index = gtk_combo_box_get_active (combo_box);
9587 GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
9589 set_current_filter (impl, new_filter);
9593 check_preview_change (GtkFileChooserDefault *impl)
9595 GtkTreePath *cursor_path;
9597 char *new_display_name;
9598 GtkTreeModel *model;
9600 gtk_tree_view_get_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), &cursor_path, NULL);
9601 model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view));
9606 gtk_tree_model_get_iter (model, &iter, cursor_path);
9607 gtk_tree_model_get (model, &iter,
9608 MODEL_COL_FILE, &new_file,
9609 MODEL_COL_NAME, &new_display_name,
9612 gtk_tree_path_free (cursor_path);
9617 new_display_name = NULL;
9620 if (new_file != impl->preview_file &&
9621 !(new_file && impl->preview_file &&
9622 g_file_equal (new_file, impl->preview_file)))
9624 if (impl->preview_file)
9626 g_object_unref (impl->preview_file);
9627 g_free (impl->preview_display_name);
9632 impl->preview_file = new_file;
9633 impl->preview_display_name = new_display_name;
9637 impl->preview_file = NULL;
9638 impl->preview_display_name = NULL;
9639 g_free (new_display_name);
9642 if (impl->use_preview_label && impl->preview_label)
9643 gtk_label_set_text (GTK_LABEL (impl->preview_label), impl->preview_display_name);
9645 g_signal_emit_by_name (impl, "update-preview");
9650 g_object_unref (new_file);
9652 g_free (new_display_name);
9657 shortcuts_activate_volume_mount_cb (GCancellable *cancellable,
9658 GtkFileSystemVolume *volume,
9659 const GError *error,
9663 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
9664 GtkFileChooserDefault *impl = data;
9666 if (cancellable != impl->shortcuts_activate_iter_cancellable)
9669 impl->shortcuts_activate_iter_cancellable = NULL;
9671 set_busy_cursor (impl, FALSE);
9678 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED))
9682 name = _gtk_file_system_volume_get_display_name (volume);
9683 msg = g_strdup_printf (_("Could not mount %s"), name);
9685 error_message (impl, msg, error->message);
9694 file = _gtk_file_system_volume_get_root (volume);
9697 change_folder_and_display_error (impl, file, FALSE);
9698 g_object_unref (file);
9702 g_object_unref (impl);
9703 g_object_unref (cancellable);
9707 /* Activates a volume by mounting it if necessary and then switching to its
9711 shortcuts_activate_volume (GtkFileChooserDefault *impl,
9712 GtkFileSystemVolume *volume)
9716 switch (impl->operation_mode)
9718 case OPERATION_MODE_BROWSE:
9720 case OPERATION_MODE_SEARCH:
9721 search_switch_to_browse_mode (impl);
9723 case OPERATION_MODE_RECENT:
9724 recent_switch_to_browse_mode (impl);
9728 /* We ref the file chooser since volume_mount() may run a main loop, and the
9729 * user could close the file chooser window in the meantime.
9731 g_object_ref (impl);
9733 if (!_gtk_file_system_volume_is_mounted (volume))
9735 GMountOperation *mount_op;
9737 set_busy_cursor (impl, TRUE);
9739 mount_op = gtk_mount_operation_new (get_toplevel (GTK_WIDGET (impl)));
9740 impl->shortcuts_activate_iter_cancellable =
9741 _gtk_file_system_mount_volume (impl->file_system, volume, mount_op,
9742 shortcuts_activate_volume_mount_cb,
9743 g_object_ref (impl));
9744 g_object_unref (mount_op);
9748 file = _gtk_file_system_volume_get_root (volume);
9751 change_folder_and_display_error (impl, file, FALSE);
9752 g_object_unref (file);
9756 g_object_unref (impl);
9759 /* Opens the folder or volume at the specified iter in the shortcuts model */
9760 struct ShortcutsActivateData
9762 GtkFileChooserDefault *impl;
9767 shortcuts_activate_get_info_cb (GCancellable *cancellable,
9769 const GError *error,
9772 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
9773 struct ShortcutsActivateData *data = user_data;
9775 if (cancellable != data->impl->shortcuts_activate_iter_cancellable)
9778 data->impl->shortcuts_activate_iter_cancellable = NULL;
9783 if (!error && _gtk_file_info_consider_as_directory (info))
9784 change_folder_and_display_error (data->impl, data->file, FALSE);
9786 gtk_file_chooser_default_select_file (GTK_FILE_CHOOSER (data->impl),
9791 g_object_unref (data->impl);
9792 g_object_unref (data->file);
9795 g_object_unref (cancellable);
9799 shortcuts_activate_mount_enclosing_volume (GCancellable *cancellable,
9800 GtkFileSystemVolume *volume,
9801 const GError *error,
9804 struct ShortcutsActivateData *data = user_data;
9808 error_changing_folder_dialog (data->impl, data->file, g_error_copy (error));
9810 g_object_unref (data->impl);
9811 g_object_unref (data->file);
9817 data->impl->shortcuts_activate_iter_cancellable =
9818 _gtk_file_system_get_info (data->impl->file_system, data->file,
9820 shortcuts_activate_get_info_cb, data);
9823 _gtk_file_system_volume_unref (volume);
9827 shortcuts_activate_iter (GtkFileChooserDefault *impl,
9831 ShortcutType shortcut_type;
9833 if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY
9834 && !(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
9835 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
9836 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
9838 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
9839 SHORTCUTS_COL_DATA, &col_data,
9840 SHORTCUTS_COL_TYPE, &shortcut_type,
9843 if (impl->shortcuts_activate_iter_cancellable)
9845 g_cancellable_cancel (impl->shortcuts_activate_iter_cancellable);
9846 impl->shortcuts_activate_iter_cancellable = NULL;
9849 if (shortcut_type == SHORTCUT_TYPE_SEPARATOR)
9851 else if (shortcut_type == SHORTCUT_TYPE_VOLUME)
9853 GtkFileSystemVolume *volume;
9857 shortcuts_activate_volume (impl, volume);
9859 else if (shortcut_type == SHORTCUT_TYPE_FILE)
9861 struct ShortcutsActivateData *data;
9862 GtkFileSystemVolume *volume;
9864 volume = _gtk_file_system_get_volume_for_file (impl->file_system, col_data);
9866 data = g_new0 (struct ShortcutsActivateData, 1);
9867 data->impl = g_object_ref (impl);
9868 data->file = g_object_ref (col_data);
9870 if (!volume || !_gtk_file_system_volume_is_mounted (volume))
9872 GMountOperation *mount_operation;
9873 GtkWidget *toplevel;
9875 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
9877 mount_operation = gtk_mount_operation_new (GTK_WINDOW (toplevel));
9879 impl->shortcuts_activate_iter_cancellable =
9880 _gtk_file_system_mount_enclosing_volume (impl->file_system, col_data,
9882 shortcuts_activate_mount_enclosing_volume,
9887 impl->shortcuts_activate_iter_cancellable =
9888 _gtk_file_system_get_info (impl->file_system, data->file,
9890 shortcuts_activate_get_info_cb, data);
9893 else if (shortcut_type == SHORTCUT_TYPE_SEARCH)
9895 search_activate (impl);
9897 else if (shortcut_type == SHORTCUT_TYPE_RECENT)
9899 recent_activate (impl);
9903 /* Handler for GtkWidget::key-press-event on the shortcuts list */
9905 shortcuts_key_press_event_cb (GtkWidget *widget,
9907 GtkFileChooserDefault *impl)
9911 modifiers = gtk_accelerator_get_default_mod_mask ();
9913 if (key_is_left_or_right (event))
9915 gtk_widget_grab_focus (impl->browse_files_tree_view);
9919 if ((event->keyval == GDK_KEY_BackSpace
9920 || event->keyval == GDK_KEY_Delete
9921 || event->keyval == GDK_KEY_KP_Delete)
9922 && (event->state & modifiers) == 0)
9924 remove_selected_bookmarks (impl);
9928 if ((event->keyval == GDK_KEY_F2)
9929 && (event->state & modifiers) == 0)
9931 rename_selected_bookmark (impl);
9939 shortcuts_select_func (GtkTreeSelection *selection,
9940 GtkTreeModel *model,
9942 gboolean path_currently_selected,
9945 GtkFileChooserDefault *impl = data;
9946 GtkTreeIter filter_iter;
9947 ShortcutType shortcut_type;
9949 if (!gtk_tree_model_get_iter (impl->shortcuts_pane_filter_model, &filter_iter, path))
9950 g_assert_not_reached ();
9952 gtk_tree_model_get (impl->shortcuts_pane_filter_model, &filter_iter, SHORTCUTS_COL_TYPE, &shortcut_type, -1);
9954 return shortcut_type != SHORTCUT_TYPE_SEPARATOR;
9958 list_select_func (GtkTreeSelection *selection,
9959 GtkTreeModel *model,
9961 gboolean path_currently_selected,
9964 GtkFileChooserDefault *impl = data;
9966 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
9967 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
9972 if (!gtk_tree_model_get_iter (model, &iter, path))
9974 gtk_tree_model_get (model, &iter,
9975 MODEL_COL_IS_FOLDER, &is_folder,
9985 list_selection_changed (GtkTreeSelection *selection,
9986 GtkFileChooserDefault *impl)
9988 /* See if we are in the new folder editable row for Save mode */
9989 if (impl->operation_mode == OPERATION_MODE_BROWSE &&
9990 impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
9993 gboolean had_selection;
9995 info = get_selected_file_info_from_file_list (impl, &had_selection);
9997 goto out; /* normal processing */
10000 return; /* We are on the editable row for New Folder */
10005 if (impl->location_entry)
10006 update_chooser_entry (impl);
10008 check_preview_change (impl);
10009 bookmarks_check_add_sensitivity (impl);
10011 g_signal_emit_by_name (impl, "selection-changed", 0);
10014 /* Callback used when a row in the file list is activated */
10016 list_row_activated (GtkTreeView *tree_view,
10018 GtkTreeViewColumn *column,
10019 GtkFileChooserDefault *impl)
10023 GtkTreeModel *model;
10024 gboolean is_folder;
10026 model = gtk_tree_view_get_model (tree_view);
10028 if (!gtk_tree_model_get_iter (model, &iter, path))
10031 gtk_tree_model_get (model, &iter,
10032 MODEL_COL_FILE, &file,
10033 MODEL_COL_IS_FOLDER, &is_folder,
10036 if (is_folder && file)
10038 change_folder_and_display_error (impl, file, FALSE);
10039 g_object_unref (file);
10043 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
10044 impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
10045 g_signal_emit_by_name (impl, "file-activated");
10050 g_object_unref (file);
10054 path_bar_clicked (GtkPathBar *path_bar,
10057 gboolean child_is_hidden,
10058 GtkFileChooserDefault *impl)
10061 pending_select_files_add (impl, child_file);
10063 if (!change_folder_and_display_error (impl, file, FALSE))
10066 /* Say we have "/foo/bar/[.baz]" and the user clicks on "bar". We should then
10067 * show hidden files so that ".baz" appears in the file list, as it will still
10068 * be shown in the path bar: "/foo/[bar]/.baz"
10070 if (child_is_hidden)
10071 g_object_set (impl, "show-hidden", TRUE, NULL);
10075 update_cell_renderer_attributes (GtkFileChooserDefault *impl)
10077 GtkTreeViewColumn *column;
10078 GtkCellRenderer *renderer;
10079 GList *walk, *list;
10080 gboolean always_sensitive;
10082 always_sensitive = impl->action != GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
10083 impl->action != GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER;
10085 /* Keep the following column numbers in sync with create_file_list() */
10088 column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_files_tree_view), 0);
10089 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
10090 for (walk = list; walk; walk = walk->next)
10092 renderer = walk->data;
10093 if (GTK_IS_CELL_RENDERER_PIXBUF (renderer))
10095 gtk_tree_view_column_set_attributes (column, renderer,
10096 "pixbuf", MODEL_COL_PIXBUF,
10101 gtk_tree_view_column_set_attributes (column, renderer,
10102 "text", MODEL_COL_NAME,
10103 "ellipsize", MODEL_COL_ELLIPSIZE,
10106 if (always_sensitive)
10107 g_object_set (renderer, "sensitive", TRUE, NULL);
10109 gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_FOLDER);
10111 g_list_free (list);
10114 column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_files_tree_view), 1);
10115 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
10116 renderer = list->data;
10117 gtk_tree_view_column_set_attributes (column, renderer,
10118 "text", MODEL_COL_SIZE_TEXT,
10120 if (always_sensitive)
10121 g_object_set (renderer, "sensitive", TRUE, NULL);
10123 gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_FOLDER);
10124 g_list_free (list);
10127 column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_files_tree_view), 2);
10128 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
10129 renderer = list->data;
10130 gtk_tree_view_column_set_attributes (column, renderer,
10131 "text", MODEL_COL_MTIME_TEXT,
10133 if (always_sensitive)
10134 g_object_set (renderer, "sensitive", TRUE, NULL);
10136 gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_FOLDER);
10137 g_list_free (list);
10141 _gtk_file_chooser_default_new (void)
10143 return g_object_new (GTK_TYPE_FILE_CHOOSER_DEFAULT, NULL);
10147 location_set_user_text (GtkFileChooserDefault *impl,
10150 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), path);
10151 gtk_editable_set_position (GTK_EDITABLE (impl->location_entry), -1);
10155 location_popup_handler (GtkFileChooserDefault *impl,
10158 if (impl->operation_mode != OPERATION_MODE_BROWSE)
10160 GtkWidget *widget_to_focus;
10162 /* This will give us the location widgets back */
10163 switch (impl->operation_mode)
10165 case OPERATION_MODE_SEARCH:
10166 search_switch_to_browse_mode (impl);
10168 case OPERATION_MODE_RECENT:
10169 recent_switch_to_browse_mode (impl);
10171 case OPERATION_MODE_BROWSE:
10172 g_assert_not_reached ();
10176 if (impl->current_folder)
10177 change_folder_and_display_error (impl, impl->current_folder, FALSE);
10179 if (impl->location_mode == LOCATION_MODE_PATH_BAR)
10180 widget_to_focus = impl->browse_files_tree_view;
10182 widget_to_focus = impl->location_entry;
10184 gtk_widget_grab_focus (widget_to_focus);
10188 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
10189 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
10191 LocationMode new_mode;
10195 /* since the user typed something, we unconditionally want to turn on the entry */
10196 new_mode = LOCATION_MODE_FILENAME_ENTRY;
10198 else if (impl->location_mode == LOCATION_MODE_PATH_BAR)
10199 new_mode = LOCATION_MODE_FILENAME_ENTRY;
10200 else if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
10201 new_mode = LOCATION_MODE_PATH_BAR;
10204 g_assert_not_reached ();
10208 location_mode_set (impl, new_mode, TRUE);
10209 if (new_mode == LOCATION_MODE_FILENAME_ENTRY)
10212 location_set_user_text (impl, path);
10215 location_entry_set_initial_text (impl);
10216 gtk_editable_select_region (GTK_EDITABLE (impl->location_entry), 0, -1);
10220 else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
10221 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
10223 gtk_widget_grab_focus (impl->location_entry);
10225 location_set_user_text (impl, path);
10228 g_assert_not_reached ();
10231 /* Handler for the "up-folder" keybinding signal */
10233 up_folder_handler (GtkFileChooserDefault *impl)
10235 _gtk_path_bar_up (GTK_PATH_BAR (impl->browse_path_bar));
10238 /* Handler for the "down-folder" keybinding signal */
10240 down_folder_handler (GtkFileChooserDefault *impl)
10242 _gtk_path_bar_down (GTK_PATH_BAR (impl->browse_path_bar));
10245 /* Switches to the shortcut in the specified index */
10247 switch_to_shortcut (GtkFileChooserDefault *impl,
10252 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
10253 g_assert_not_reached ();
10255 shortcuts_activate_iter (impl, &iter);
10258 /* Handler for the "home-folder" keybinding signal */
10260 home_folder_handler (GtkFileChooserDefault *impl)
10262 if (impl->has_home)
10263 switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_HOME));
10266 /* Handler for the "desktop-folder" keybinding signal */
10268 desktop_folder_handler (GtkFileChooserDefault *impl)
10270 if (impl->has_desktop)
10271 switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_DESKTOP));
10274 /* Handler for the "search-shortcut" keybinding signal */
10276 search_shortcut_handler (GtkFileChooserDefault *impl)
10278 if (impl->has_search)
10280 switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_SEARCH));
10282 /* we want the entry widget to grab the focus the first
10283 * time, not the browse_files_tree_view widget.
10285 if (impl->search_entry)
10286 gtk_widget_grab_focus (impl->search_entry);
10290 /* Handler for the "recent-shortcut" keybinding signal */
10292 recent_shortcut_handler (GtkFileChooserDefault *impl)
10294 if (impl->has_recent)
10295 switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_RECENT));
10299 quick_bookmark_handler (GtkFileChooserDefault *impl,
10300 gint bookmark_index)
10305 if (bookmark_index < 0 || bookmark_index >= impl->num_bookmarks)
10308 bookmark_pos = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS) + bookmark_index;
10310 path = gtk_tree_path_new_from_indices (bookmark_pos, -1);
10311 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
10314 gtk_tree_path_free (path);
10316 switch_to_shortcut (impl, bookmark_pos);
10320 show_hidden_handler (GtkFileChooserDefault *impl)
10322 g_object_set (impl,
10323 "show-hidden", !impl->show_hidden,
10328 /* Drag and drop interfaces */
10331 _shortcuts_pane_model_filter_class_init (ShortcutsPaneModelFilterClass *class)
10336 _shortcuts_pane_model_filter_init (ShortcutsPaneModelFilter *model)
10338 model->impl = NULL;
10341 /* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */
10343 shortcuts_pane_model_filter_row_draggable (GtkTreeDragSource *drag_source,
10346 ShortcutsPaneModelFilter *model;
10350 model = SHORTCUTS_PANE_MODEL_FILTER (drag_source);
10352 pos = *gtk_tree_path_get_indices (path);
10353 bookmarks_pos = shortcuts_get_index (model->impl, SHORTCUTS_BOOKMARKS);
10355 return (pos >= bookmarks_pos && pos < bookmarks_pos + model->impl->num_bookmarks);
10358 /* GtkTreeDragSource::drag_data_get implementation for the shortcuts
10362 shortcuts_pane_model_filter_drag_data_get (GtkTreeDragSource *drag_source,
10364 GtkSelectionData *selection_data)
10371 /* Fill the GtkTreeDragSourceIface vtable */
10373 shortcuts_pane_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface)
10375 iface->row_draggable = shortcuts_pane_model_filter_row_draggable;
10376 iface->drag_data_get = shortcuts_pane_model_filter_drag_data_get;
10380 /* Fill the GtkTreeDragDestIface vtable */
10382 shortcuts_pane_model_filter_drag_dest_iface_init (GtkTreeDragDestIface *iface)
10384 iface->drag_data_received = shortcuts_pane_model_filter_drag_data_received;
10385 iface->row_drop_possible = shortcuts_pane_model_filter_row_drop_possible;
10389 static GtkTreeModel *
10390 shortcuts_pane_model_filter_new (GtkFileChooserDefault *impl,
10391 GtkTreeModel *child_model,
10394 ShortcutsPaneModelFilter *model;
10396 model = g_object_new (SHORTCUTS_PANE_MODEL_FILTER_TYPE,
10397 "child-model", child_model,
10398 "virtual-root", root,
10401 model->impl = impl;
10403 return GTK_TREE_MODEL (model);