1 /* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
2 /* GTK - The GIMP Toolkit
3 * gtkfilechooserdefault.c: Default implementation of GtkFileChooser
4 * Copyright (C) 2003, Red Hat, Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
24 #include "gdk/gdkkeysyms.h"
25 #include "gtkalignment.h"
26 #include "gtkbindings.h"
27 #include "gtkcelllayout.h"
28 #include "gtkcellrendererpixbuf.h"
29 #include "gtkcellrenderertext.h"
30 #include "gtkcheckmenuitem.h"
31 #include "gtkclipboard.h"
32 #include "gtkcombobox.h"
34 #include "gtkexpander.h"
35 #include "gtkfilechooserprivate.h"
36 #include "gtkfilechooserdefault.h"
37 #include "gtkfilechooserdialog.h"
38 #include "gtkfilechooserembed.h"
39 #include "gtkfilechooserentry.h"
40 #include "gtkfilechoosersettings.h"
41 #include "gtkfilechooserutils.h"
42 #include "gtkfilechooser.h"
43 #include "gtkfilesystem.h"
44 #include "gtkfilesystemmodel.h"
47 #include "gtkhpaned.h"
48 #include "gtkiconfactory.h"
49 #include "gtkicontheme.h"
51 #include "gtkimagemenuitem.h"
53 #include "gtkmarshalers.h"
54 #include "gtkmessagedialog.h"
55 #include "gtkmountoperation.h"
56 #include "gtkpathbar.h"
57 #include "gtkprivate.h"
58 #include "gtkradiobutton.h"
59 #include "gtkrecentfilter.h"
60 #include "gtkrecentmanager.h"
61 #include "gtkscrolledwindow.h"
62 #include "gtkseparatormenuitem.h"
63 #include "gtksizegroup.h"
66 #include "gtktooltip.h"
67 #include "gtktreednd.h"
68 #include "gtktreeprivate.h"
69 #include "gtktreeselection.h"
77 #include <sys/types.h>
88 #undef PROFILE_FILE_CHOOSER
89 #ifdef PROFILE_FILE_CHOOSER
96 #define PROFILE_INDENT 4
97 static int profile_indent;
100 profile_add_indent (int indent)
102 profile_indent += indent;
103 if (profile_indent < 0)
104 g_error ("You screwed up your indentation");
108 _gtk_file_chooser_profile_log (const char *func, int indent, const char *msg1, const char *msg2)
113 profile_add_indent (indent);
115 if (profile_indent == 0)
116 str = g_strdup_printf ("MARK: %s %s %s", func ? func : "", msg1 ? msg1 : "", msg2 ? msg2 : "");
118 str = g_strdup_printf ("MARK: %*c %s %s %s", profile_indent - 1, ' ', func ? func : "", msg1 ? msg1 : "", msg2 ? msg2 : "");
124 profile_add_indent (indent);
127 #define profile_start(x, y) _gtk_file_chooser_profile_log (G_STRFUNC, PROFILE_INDENT, x, y)
128 #define profile_end(x, y) _gtk_file_chooser_profile_log (G_STRFUNC, -PROFILE_INDENT, x, y)
129 #define profile_msg(x, y) _gtk_file_chooser_profile_log (NULL, 0, x, y)
131 #define profile_start(x, y)
132 #define profile_end(x, y)
133 #define profile_msg(x, y)
138 typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
140 #define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
141 #define GTK_IS_FILE_CHOOSER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT))
142 #define GTK_FILE_CHOOSER_DEFAULT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
144 #define MAX_LOADING_TIME 500
146 #define DEFAULT_NEW_FOLDER_NAME _("Type name of new folder")
148 struct _GtkFileChooserDefaultClass
150 GtkVBoxClass parent_class;
156 LOCATION_POPUP_ON_PASTE,
162 LOCATION_TOGGLE_POPUP,
170 static guint signals[LAST_SIGNAL] = { 0 };
172 /* Column numbers for the shortcuts tree. Keep these in sync with shortcuts_model_create() */
174 SHORTCUTS_COL_PIXBUF,
178 SHORTCUTS_COL_REMOVABLE,
179 SHORTCUTS_COL_PIXBUF_VISIBLE,
180 SHORTCUTS_COL_CANCELLABLE,
181 SHORTCUTS_COL_NUM_COLUMNS
186 SHORTCUT_TYPE_VOLUME,
187 SHORTCUT_TYPE_SEPARATOR,
188 SHORTCUT_TYPE_SEARCH,
192 #define MODEL_ATTRIBUTES "standard::name,standard::type,standard::display-name," \
193 "standard::is-hidden,standard::is-backup,standard::size," \
194 "standard::content-type,time::modified"
196 /* the first 3 must be these due to settings caching sort column */
201 MODEL_COL_NAME_COLLATED,
205 MODEL_COL_MTIME_TEXT,
207 MODEL_COL_NUM_COLUMNS
210 /* This list of types is passed to _gtk_file_system_model_new*() */
211 #define MODEL_COLUMN_TYPES \
212 MODEL_COL_NUM_COLUMNS, \
213 G_TYPE_STRING, /* MODEL_COL_NAME */ \
214 G_TYPE_INT64, /* MODEL_COL_SIZE */ \
215 G_TYPE_LONG, /* MODEL_COL_MTIME */ \
216 G_TYPE_FILE, /* MODEL_COL_FILE */ \
217 G_TYPE_STRING, /* MODEL_COL_NAME_COLLATED */ \
218 G_TYPE_BOOLEAN, /* MODEL_COL_IS_FOLDER */ \
219 GDK_TYPE_PIXBUF, /* MODEL_COL_PIXBUF */ \
220 G_TYPE_STRING, /* MODEL_COL_SIZE_TEXT */ \
221 G_TYPE_STRING, /* MODEL_COL_MTIME_TEXT */ \
222 PANGO_TYPE_ELLIPSIZE_MODE /* MODEL_COL_ELLIPSIZE */
224 /* Identifiers for target types */
229 /* Interesting places in the shortcuts bar */
233 SHORTCUTS_RECENT_SEPARATOR,
238 SHORTCUTS_BOOKMARKS_SEPARATOR,
240 SHORTCUTS_CURRENT_FOLDER_SEPARATOR,
241 SHORTCUTS_CURRENT_FOLDER
244 /* Icon size for if we can't get it from the theme */
245 #define FALLBACK_ICON_SIZE 16
247 #define PREVIEW_HBOX_SPACING 12
251 static void gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface);
252 static void gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface);
254 static GObject* gtk_file_chooser_default_constructor (GType type,
255 guint n_construct_properties,
256 GObjectConstructParam *construct_params);
257 static void gtk_file_chooser_default_finalize (GObject *object);
258 static void gtk_file_chooser_default_set_property (GObject *object,
262 static void gtk_file_chooser_default_get_property (GObject *object,
266 static void gtk_file_chooser_default_dispose (GObject *object);
267 static void gtk_file_chooser_default_show_all (GtkWidget *widget);
268 static void gtk_file_chooser_default_realize (GtkWidget *widget);
269 static void gtk_file_chooser_default_map (GtkWidget *widget);
270 static void gtk_file_chooser_default_unmap (GtkWidget *widget);
271 static void gtk_file_chooser_default_hierarchy_changed (GtkWidget *widget,
272 GtkWidget *previous_toplevel);
273 static void gtk_file_chooser_default_style_set (GtkWidget *widget,
274 GtkStyle *previous_style);
275 static void gtk_file_chooser_default_screen_changed (GtkWidget *widget,
276 GdkScreen *previous_screen);
277 static void gtk_file_chooser_default_size_allocate (GtkWidget *widget,
278 GtkAllocation *allocation);
280 static gboolean gtk_file_chooser_default_set_current_folder (GtkFileChooser *chooser,
283 static gboolean gtk_file_chooser_default_update_current_folder (GtkFileChooser *chooser,
286 gboolean clear_entry,
288 static GFile * gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser);
289 static void gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
291 static gboolean gtk_file_chooser_default_select_file (GtkFileChooser *chooser,
294 static void gtk_file_chooser_default_unselect_file (GtkFileChooser *chooser,
296 static void gtk_file_chooser_default_select_all (GtkFileChooser *chooser);
297 static void gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser);
298 static GSList * gtk_file_chooser_default_get_files (GtkFileChooser *chooser);
299 static GFile * gtk_file_chooser_default_get_preview_file (GtkFileChooser *chooser);
300 static GtkFileSystem *gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser);
301 static void gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
302 GtkFileFilter *filter);
303 static void gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
304 GtkFileFilter *filter);
305 static GSList * gtk_file_chooser_default_list_filters (GtkFileChooser *chooser);
306 static gboolean gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser *chooser,
309 static gboolean gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser *chooser,
312 static GSList * gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser);
314 static void gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
316 gint *default_height);
317 static gboolean gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed);
318 static void gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed);
320 static void location_popup_handler (GtkFileChooserDefault *impl,
322 static void location_popup_on_paste_handler (GtkFileChooserDefault *impl);
323 static void location_toggle_popup_handler (GtkFileChooserDefault *impl);
324 static void up_folder_handler (GtkFileChooserDefault *impl);
325 static void down_folder_handler (GtkFileChooserDefault *impl);
326 static void home_folder_handler (GtkFileChooserDefault *impl);
327 static void desktop_folder_handler (GtkFileChooserDefault *impl);
328 static void quick_bookmark_handler (GtkFileChooserDefault *impl,
329 gint bookmark_index);
330 static void show_hidden_handler (GtkFileChooserDefault *impl);
331 static void search_shortcut_handler (GtkFileChooserDefault *impl);
332 static void recent_shortcut_handler (GtkFileChooserDefault *impl);
333 static void update_appearance (GtkFileChooserDefault *impl);
335 static void set_current_filter (GtkFileChooserDefault *impl,
336 GtkFileFilter *filter);
337 static void check_preview_change (GtkFileChooserDefault *impl);
339 static void filter_combo_changed (GtkComboBox *combo_box,
340 GtkFileChooserDefault *impl);
342 static gboolean shortcuts_key_press_event_cb (GtkWidget *widget,
344 GtkFileChooserDefault *impl);
346 static gboolean shortcuts_select_func (GtkTreeSelection *selection,
349 gboolean path_currently_selected,
351 static gboolean shortcuts_get_selected (GtkFileChooserDefault *impl,
353 static void shortcuts_activate_iter (GtkFileChooserDefault *impl,
355 static int shortcuts_get_index (GtkFileChooserDefault *impl,
356 ShortcutsIndex where);
357 static int shortcut_find_position (GtkFileChooserDefault *impl,
360 static void bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl);
362 static gboolean list_select_func (GtkTreeSelection *selection,
365 gboolean path_currently_selected,
368 static void list_selection_changed (GtkTreeSelection *tree_selection,
369 GtkFileChooserDefault *impl);
370 static void list_row_activated (GtkTreeView *tree_view,
372 GtkTreeViewColumn *column,
373 GtkFileChooserDefault *impl);
375 static void path_bar_clicked (GtkPathBar *path_bar,
378 gboolean child_is_hidden,
379 GtkFileChooserDefault *impl);
381 static void add_bookmark_button_clicked_cb (GtkButton *button,
382 GtkFileChooserDefault *impl);
383 static void remove_bookmark_button_clicked_cb (GtkButton *button,
384 GtkFileChooserDefault *impl);
385 static void save_folder_combo_changed_cb (GtkComboBox *combo,
386 GtkFileChooserDefault *impl);
388 static void update_cell_renderer_attributes (GtkFileChooserDefault *impl);
390 static void load_remove_timer (GtkFileChooserDefault *impl);
391 static void browse_files_center_selected_row (GtkFileChooserDefault *impl);
393 static void location_button_toggled_cb (GtkToggleButton *toggle,
394 GtkFileChooserDefault *impl);
395 static void location_switch_to_path_bar (GtkFileChooserDefault *impl);
397 static void stop_loading_and_clear_list_model (GtkFileChooserDefault *impl,
398 gboolean remove_from_treeview);
399 static void search_stop_searching (GtkFileChooserDefault *impl,
400 gboolean remove_query);
401 static void search_clear_model (GtkFileChooserDefault *impl,
402 gboolean remove_from_treeview);
403 static gboolean search_should_respond (GtkFileChooserDefault *impl);
404 static void search_switch_to_browse_mode (GtkFileChooserDefault *impl);
405 static GSList *search_get_selected_files (GtkFileChooserDefault *impl);
406 static void search_entry_activate_cb (GtkEntry *entry,
408 static void settings_load (GtkFileChooserDefault *impl);
410 static void recent_stop_loading (GtkFileChooserDefault *impl);
411 static void recent_clear_model (GtkFileChooserDefault *impl,
412 gboolean remove_from_treeview);
413 static gboolean recent_should_respond (GtkFileChooserDefault *impl);
414 static void recent_switch_to_browse_mode (GtkFileChooserDefault *impl);
415 static GSList * recent_get_selected_files (GtkFileChooserDefault *impl);
416 static void set_file_system_backend (GtkFileChooserDefault *impl);
417 static void unset_file_system_backend (GtkFileChooserDefault *impl);
423 /* Drag and drop interface declarations */
426 GtkTreeModelFilter parent;
428 GtkFileChooserDefault *impl;
429 } ShortcutsPaneModelFilter;
432 GtkTreeModelFilterClass parent_class;
433 } ShortcutsPaneModelFilterClass;
435 #define SHORTCUTS_PANE_MODEL_FILTER_TYPE (_shortcuts_pane_model_filter_get_type ())
436 #define SHORTCUTS_PANE_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_PANE_MODEL_FILTER_TYPE, ShortcutsPaneModelFilter))
438 static void shortcuts_pane_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface);
440 G_DEFINE_TYPE_WITH_CODE (ShortcutsPaneModelFilter,
441 _shortcuts_pane_model_filter,
442 GTK_TYPE_TREE_MODEL_FILTER,
443 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
444 shortcuts_pane_model_filter_drag_source_iface_init))
446 static GtkTreeModel *shortcuts_pane_model_filter_new (GtkFileChooserDefault *impl,
447 GtkTreeModel *child_model,
452 G_DEFINE_TYPE_WITH_CODE (GtkFileChooserDefault, _gtk_file_chooser_default, GTK_TYPE_VBOX,
453 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER,
454 gtk_file_chooser_default_iface_init)
455 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER_EMBED,
456 gtk_file_chooser_embed_default_iface_init));
459 _gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
461 static const guint quick_bookmark_keyvals[10] = {
462 GDK_1, GDK_2, GDK_3, GDK_4, GDK_5, GDK_6, GDK_7, GDK_8, GDK_9, GDK_0
464 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
465 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
466 GtkBindingSet *binding_set;
469 gobject_class->finalize = gtk_file_chooser_default_finalize;
470 gobject_class->constructor = gtk_file_chooser_default_constructor;
471 gobject_class->set_property = gtk_file_chooser_default_set_property;
472 gobject_class->get_property = gtk_file_chooser_default_get_property;
473 gobject_class->dispose = gtk_file_chooser_default_dispose;
475 widget_class->show_all = gtk_file_chooser_default_show_all;
476 widget_class->realize = gtk_file_chooser_default_realize;
477 widget_class->map = gtk_file_chooser_default_map;
478 widget_class->unmap = gtk_file_chooser_default_unmap;
479 widget_class->hierarchy_changed = gtk_file_chooser_default_hierarchy_changed;
480 widget_class->style_set = gtk_file_chooser_default_style_set;
481 widget_class->screen_changed = gtk_file_chooser_default_screen_changed;
482 widget_class->size_allocate = gtk_file_chooser_default_size_allocate;
484 signals[LOCATION_POPUP] =
485 g_signal_new_class_handler (I_("location-popup"),
486 G_OBJECT_CLASS_TYPE (class),
487 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
488 G_CALLBACK (location_popup_handler),
490 _gtk_marshal_VOID__STRING,
491 G_TYPE_NONE, 1, G_TYPE_STRING);
493 signals[LOCATION_POPUP_ON_PASTE] =
494 g_signal_new_class_handler (I_("location-popup-on-paste"),
495 G_OBJECT_CLASS_TYPE (class),
496 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
497 G_CALLBACK (location_popup_on_paste_handler),
499 _gtk_marshal_VOID__VOID,
502 signals[LOCATION_TOGGLE_POPUP] =
503 g_signal_new_class_handler (I_("location-toggle-popup"),
504 G_OBJECT_CLASS_TYPE (class),
505 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
506 G_CALLBACK (location_toggle_popup_handler),
508 _gtk_marshal_VOID__VOID,
512 g_signal_new_class_handler (I_("up-folder"),
513 G_OBJECT_CLASS_TYPE (class),
514 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
515 G_CALLBACK (up_folder_handler),
517 _gtk_marshal_VOID__VOID,
520 signals[DOWN_FOLDER] =
521 g_signal_new_class_handler (I_("down-folder"),
522 G_OBJECT_CLASS_TYPE (class),
523 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
524 G_CALLBACK (down_folder_handler),
526 _gtk_marshal_VOID__VOID,
529 signals[HOME_FOLDER] =
530 g_signal_new_class_handler (I_("home-folder"),
531 G_OBJECT_CLASS_TYPE (class),
532 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
533 G_CALLBACK (home_folder_handler),
535 _gtk_marshal_VOID__VOID,
538 signals[DESKTOP_FOLDER] =
539 g_signal_new_class_handler (I_("desktop-folder"),
540 G_OBJECT_CLASS_TYPE (class),
541 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
542 G_CALLBACK (desktop_folder_handler),
544 _gtk_marshal_VOID__VOID,
547 signals[QUICK_BOOKMARK] =
548 g_signal_new_class_handler (I_("quick-bookmark"),
549 G_OBJECT_CLASS_TYPE (class),
550 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
551 G_CALLBACK (quick_bookmark_handler),
553 _gtk_marshal_VOID__INT,
554 G_TYPE_NONE, 1, G_TYPE_INT);
556 signals[SHOW_HIDDEN] =
557 g_signal_new_class_handler (I_("show-hidden"),
558 G_OBJECT_CLASS_TYPE (class),
559 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
560 G_CALLBACK (show_hidden_handler),
562 _gtk_marshal_VOID__VOID,
565 signals[SEARCH_SHORTCUT] =
566 g_signal_new_class_handler (I_("search-shortcut"),
567 G_OBJECT_CLASS_TYPE (class),
568 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
569 G_CALLBACK (search_shortcut_handler),
571 _gtk_marshal_VOID__VOID,
574 signals[RECENT_SHORTCUT] =
575 g_signal_new_class_handler (I_("recent-shortcut"),
576 G_OBJECT_CLASS_TYPE (class),
577 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
578 G_CALLBACK (recent_shortcut_handler),
580 _gtk_marshal_VOID__VOID,
583 binding_set = gtk_binding_set_by_class (class);
585 gtk_binding_entry_add_signal (binding_set,
586 GDK_l, GDK_CONTROL_MASK,
587 "location-toggle-popup",
590 gtk_binding_entry_add_signal (binding_set,
593 1, G_TYPE_STRING, "/");
594 gtk_binding_entry_add_signal (binding_set,
597 1, G_TYPE_STRING, "/");
600 gtk_binding_entry_add_signal (binding_set,
603 1, G_TYPE_STRING, "~");
606 gtk_binding_entry_add_signal (binding_set,
607 GDK_v, GDK_CONTROL_MASK,
608 "location-popup-on-paste",
610 gtk_binding_entry_add_signal (binding_set,
611 GDK_Up, GDK_MOD1_MASK,
614 gtk_binding_entry_add_signal (binding_set,
618 gtk_binding_entry_add_signal (binding_set,
619 GDK_KP_Up, GDK_MOD1_MASK,
623 gtk_binding_entry_add_signal (binding_set,
624 GDK_Down, GDK_MOD1_MASK,
627 gtk_binding_entry_add_signal (binding_set,
628 GDK_KP_Down, GDK_MOD1_MASK,
632 gtk_binding_entry_add_signal (binding_set,
633 GDK_Home, GDK_MOD1_MASK,
636 gtk_binding_entry_add_signal (binding_set,
637 GDK_KP_Home, GDK_MOD1_MASK,
640 gtk_binding_entry_add_signal (binding_set,
641 GDK_d, GDK_MOD1_MASK,
644 gtk_binding_entry_add_signal (binding_set,
645 GDK_h, GDK_CONTROL_MASK,
648 gtk_binding_entry_add_signal (binding_set,
649 GDK_s, GDK_MOD1_MASK,
652 gtk_binding_entry_add_signal (binding_set,
653 GDK_r, GDK_MOD1_MASK,
657 for (i = 0; i < 10; i++)
658 gtk_binding_entry_add_signal (binding_set,
659 quick_bookmark_keyvals[i], GDK_MOD1_MASK,
663 _gtk_file_chooser_install_properties (gobject_class);
667 gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
669 iface->select_file = gtk_file_chooser_default_select_file;
670 iface->unselect_file = gtk_file_chooser_default_unselect_file;
671 iface->select_all = gtk_file_chooser_default_select_all;
672 iface->unselect_all = gtk_file_chooser_default_unselect_all;
673 iface->get_files = gtk_file_chooser_default_get_files;
674 iface->get_preview_file = gtk_file_chooser_default_get_preview_file;
675 iface->get_file_system = gtk_file_chooser_default_get_file_system;
676 iface->set_current_folder = gtk_file_chooser_default_set_current_folder;
677 iface->get_current_folder = gtk_file_chooser_default_get_current_folder;
678 iface->set_current_name = gtk_file_chooser_default_set_current_name;
679 iface->add_filter = gtk_file_chooser_default_add_filter;
680 iface->remove_filter = gtk_file_chooser_default_remove_filter;
681 iface->list_filters = gtk_file_chooser_default_list_filters;
682 iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder;
683 iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder;
684 iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders;
688 gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
690 iface->get_default_size = gtk_file_chooser_default_get_default_size;
691 iface->should_respond = gtk_file_chooser_default_should_respond;
692 iface->initial_focus = gtk_file_chooser_default_initial_focus;
696 _gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
698 profile_start ("start", NULL);
699 #ifdef PROFILE_FILE_CHOOSER
700 access ("MARK: *** CREATE FILE CHOOSER", F_OK);
702 impl->local_only = TRUE;
703 impl->preview_widget_active = TRUE;
704 impl->use_preview_label = TRUE;
705 impl->select_multiple = FALSE;
706 impl->show_hidden = FALSE;
707 impl->show_size_column = TRUE;
708 impl->icon_size = FALLBACK_ICON_SIZE;
709 impl->load_state = LOAD_EMPTY;
710 impl->reload_state = RELOAD_EMPTY;
711 impl->pending_select_files = NULL;
712 impl->location_mode = LOCATION_MODE_PATH_BAR;
713 impl->operation_mode = OPERATION_MODE_BROWSE;
714 impl->sort_column = MODEL_COL_NAME;
715 impl->sort_order = GTK_SORT_ASCENDING;
716 impl->recent_manager = gtk_recent_manager_get_default ();
717 impl->create_folders = TRUE;
719 gtk_box_set_spacing (GTK_BOX (impl), 12);
721 set_file_system_backend (impl);
723 profile_end ("end", NULL);
726 /* Frees the data columns for the specified iter in the shortcuts model*/
728 shortcuts_free_row_data (GtkFileChooserDefault *impl,
732 ShortcutType shortcut_type;
733 GCancellable *cancellable;
735 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
736 SHORTCUTS_COL_DATA, &col_data,
737 SHORTCUTS_COL_TYPE, &shortcut_type,
738 SHORTCUTS_COL_CANCELLABLE, &cancellable,
742 g_cancellable_cancel (cancellable);
744 if (!(shortcut_type == SHORTCUT_TYPE_FILE ||
745 shortcut_type == SHORTCUT_TYPE_VOLUME) ||
749 if (shortcut_type == SHORTCUT_TYPE_VOLUME)
751 GtkFileSystemVolume *volume;
754 _gtk_file_system_volume_unref (volume);
756 if (shortcut_type == SHORTCUT_TYPE_FILE)
761 g_object_unref (file);
765 /* Frees all the data columns in the shortcuts model */
767 shortcuts_free (GtkFileChooserDefault *impl)
771 if (!impl->shortcuts_model)
774 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
777 shortcuts_free_row_data (impl, &iter);
779 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter));
781 g_object_unref (impl->shortcuts_model);
782 impl->shortcuts_model = NULL;
786 pending_select_files_free (GtkFileChooserDefault *impl)
788 g_slist_foreach (impl->pending_select_files, (GFunc) g_object_unref, NULL);
789 g_slist_free (impl->pending_select_files);
790 impl->pending_select_files = NULL;
794 pending_select_files_add (GtkFileChooserDefault *impl,
797 impl->pending_select_files =
798 g_slist_prepend (impl->pending_select_files, g_object_ref (file));
802 gtk_file_chooser_default_finalize (GObject *object)
804 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
807 unset_file_system_backend (impl);
809 if (impl->shortcuts_pane_filter_model)
810 g_object_unref (impl->shortcuts_pane_filter_model);
812 if (impl->shortcuts_combo_filter_model)
813 g_object_unref (impl->shortcuts_combo_filter_model);
815 shortcuts_free (impl);
817 g_free (impl->browse_files_last_selected_name);
819 for (l = impl->filters; l; l = l->next)
821 GtkFileFilter *filter;
823 filter = GTK_FILE_FILTER (l->data);
824 g_object_unref (filter);
826 g_slist_free (impl->filters);
828 if (impl->current_filter)
829 g_object_unref (impl->current_filter);
831 if (impl->current_volume_file)
832 g_object_unref (impl->current_volume_file);
834 if (impl->current_folder)
835 g_object_unref (impl->current_folder);
837 if (impl->preview_file)
838 g_object_unref (impl->preview_file);
840 if (impl->browse_path_bar_size_group)
841 g_object_unref (impl->browse_path_bar_size_group);
843 /* Free all the Models we have */
844 stop_loading_and_clear_list_model (impl, FALSE);
845 search_clear_model (impl, FALSE);
846 recent_clear_model (impl, FALSE);
848 /* stopping the load above should have cleared this */
849 g_assert (impl->load_timeout_id == 0);
851 g_free (impl->preview_display_name);
853 g_free (impl->edited_new_text);
855 G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->finalize (object);
858 /* Shows an error dialog set as transient for the specified window */
860 error_message_with_parent (GtkWindow *parent,
866 dialog = gtk_message_dialog_new (parent,
867 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
872 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
875 if (parent && parent->group)
876 gtk_window_group_add_window (parent->group, GTK_WINDOW (dialog));
878 gtk_dialog_run (GTK_DIALOG (dialog));
879 gtk_widget_destroy (dialog);
882 /* Returns a toplevel GtkWindow, or NULL if none */
884 get_toplevel (GtkWidget *widget)
888 toplevel = gtk_widget_get_toplevel (widget);
889 if (!gtk_widget_is_toplevel (toplevel))
892 return GTK_WINDOW (toplevel);
895 /* Shows an error dialog for the file chooser */
897 error_message (GtkFileChooserDefault *impl,
901 error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail);
904 /* Shows a simple error dialog relative to a path. Frees the GError as well. */
906 error_dialog (GtkFileChooserDefault *impl,
917 uri = g_file_get_uri (file);
918 text = g_strdup_printf (msg, uri);
919 error_message (impl, text, error->message);
922 g_error_free (error);
926 /* Displays an error message about not being able to get information for a file.
927 * Frees the GError as well.
930 error_getting_info_dialog (GtkFileChooserDefault *impl,
935 _("Could not retrieve information about the file"),
939 /* Shows an error dialog about not being able to add a bookmark */
941 error_adding_bookmark_dialog (GtkFileChooserDefault *impl,
946 _("Could not add a bookmark"),
950 /* Shows an error dialog about not being able to remove a bookmark */
952 error_removing_bookmark_dialog (GtkFileChooserDefault *impl,
957 _("Could not remove bookmark"),
961 /* Shows an error dialog about not being able to create a folder */
963 error_creating_folder_dialog (GtkFileChooserDefault *impl,
968 _("The folder could not be created"),
972 /* Shows an error about not being able to create a folder because a file with
973 * the same name is already there.
976 error_creating_folder_over_existing_file_dialog (GtkFileChooserDefault *impl,
981 _("The folder could not be created, as a file with the same "
982 "name already exists. Try using a different name for the "
983 "folder, or rename the file first."),
987 /* Shows an error dialog about not being able to create a filename */
989 error_building_filename_dialog (GtkFileChooserDefault *impl,
992 error_dialog (impl, _("Invalid file name"),
996 /* Shows an error dialog when we cannot switch to a folder */
998 error_changing_folder_dialog (GtkFileChooserDefault *impl,
1002 error_dialog (impl, _("The folder contents could not be displayed"),
1006 /* Changes folders, displaying an error dialog if this fails */
1008 change_folder_and_display_error (GtkFileChooserDefault *impl,
1010 gboolean clear_entry)
1015 g_return_val_if_fail (G_IS_FILE (file), FALSE);
1017 /* We copy the path because of this case:
1019 * list_row_activated()
1020 * fetches path from model; path belongs to the model (*)
1021 * calls change_folder_and_display_error()
1022 * calls gtk_file_chooser_set_current_folder_file()
1023 * changing folders fails, sets model to NULL, thus freeing the path in (*)
1027 result = gtk_file_chooser_default_update_current_folder (GTK_FILE_CHOOSER (impl), file, TRUE, clear_entry, &error);
1030 error_changing_folder_dialog (impl, file, error);
1036 emit_default_size_changed (GtkFileChooserDefault *impl)
1038 profile_msg (" emit default-size-changed start", NULL);
1039 g_signal_emit_by_name (impl, "default-size-changed");
1040 profile_msg (" emit default-size-changed end", NULL);
1044 update_preview_widget_visibility (GtkFileChooserDefault *impl)
1046 if (impl->use_preview_label)
1048 if (!impl->preview_label)
1050 impl->preview_label = gtk_label_new (impl->preview_display_name);
1051 gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_label, FALSE, FALSE, 0);
1052 gtk_box_reorder_child (GTK_BOX (impl->preview_box), impl->preview_label, 0);
1053 gtk_label_set_ellipsize (GTK_LABEL (impl->preview_label), PANGO_ELLIPSIZE_MIDDLE);
1054 gtk_widget_show (impl->preview_label);
1059 if (impl->preview_label)
1061 gtk_widget_destroy (impl->preview_label);
1062 impl->preview_label = NULL;
1066 if (impl->preview_widget_active && impl->preview_widget)
1067 gtk_widget_show (impl->preview_box);
1069 gtk_widget_hide (impl->preview_box);
1071 if (!gtk_widget_get_mapped (GTK_WIDGET (impl)))
1072 emit_default_size_changed (impl);
1076 set_preview_widget (GtkFileChooserDefault *impl,
1077 GtkWidget *preview_widget)
1079 if (preview_widget == impl->preview_widget)
1082 if (impl->preview_widget)
1083 gtk_container_remove (GTK_CONTAINER (impl->preview_box),
1084 impl->preview_widget);
1086 impl->preview_widget = preview_widget;
1087 if (impl->preview_widget)
1089 gtk_widget_show (impl->preview_widget);
1090 gtk_box_pack_start (GTK_BOX (impl->preview_box), impl->preview_widget, TRUE, TRUE, 0);
1091 gtk_box_reorder_child (GTK_BOX (impl->preview_box),
1092 impl->preview_widget,
1093 (impl->use_preview_label && impl->preview_label) ? 1 : 0);
1096 update_preview_widget_visibility (impl);
1099 /* Renders a "Search" icon at an appropriate size for a tree view */
1101 render_search_icon (GtkFileChooserDefault *impl)
1103 return gtk_widget_render_icon (GTK_WIDGET (impl), GTK_STOCK_FIND, GTK_ICON_SIZE_MENU, NULL);
1107 render_recent_icon (GtkFileChooserDefault *impl)
1109 GtkIconTheme *theme;
1112 if (gtk_widget_has_screen (GTK_WIDGET (impl)))
1113 theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
1115 theme = gtk_icon_theme_get_default ();
1117 retval = gtk_icon_theme_load_icon (theme, "document-open-recent",
1123 retval = gtk_widget_render_icon (GTK_WIDGET (impl), GTK_STOCK_FILE, GTK_ICON_SIZE_MENU, NULL);
1129 /* Re-reads all the icons for the shortcuts, used when the theme changes */
1130 struct ReloadIconsData
1132 GtkFileChooserDefault *impl;
1133 GtkTreeRowReference *row_ref;
1137 shortcuts_reload_icons_get_info_cb (GCancellable *cancellable,
1139 const GError *error,
1145 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1146 struct ReloadIconsData *data = user_data;
1148 if (!g_slist_find (data->impl->reload_icon_cancellables, cancellable))
1151 data->impl->reload_icon_cancellables = g_slist_remove (data->impl->reload_icon_cancellables, cancellable);
1153 if (cancelled || error)
1156 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->impl), data->impl->icon_size);
1158 path = gtk_tree_row_reference_get_path (data->row_ref);
1159 gtk_tree_model_get_iter (GTK_TREE_MODEL (data->impl->shortcuts_model), &iter, path);
1160 gtk_list_store_set (data->impl->shortcuts_model, &iter,
1161 SHORTCUTS_COL_PIXBUF, pixbuf,
1163 gtk_tree_path_free (path);
1166 g_object_unref (pixbuf);
1169 gtk_tree_row_reference_free (data->row_ref);
1170 g_object_unref (data->impl);
1173 g_object_unref (cancellable);
1177 shortcuts_reload_icons (GtkFileChooserDefault *impl)
1182 profile_start ("start", NULL);
1184 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
1187 for (l = impl->reload_icon_cancellables; l; l = l->next)
1189 GCancellable *cancellable = G_CANCELLABLE (l->data);
1190 g_cancellable_cancel (cancellable);
1192 g_slist_free (impl->reload_icon_cancellables);
1193 impl->reload_icon_cancellables = NULL;
1198 ShortcutType shortcut_type;
1199 gboolean pixbuf_visible;
1202 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
1203 SHORTCUTS_COL_DATA, &data,
1204 SHORTCUTS_COL_TYPE, &shortcut_type,
1205 SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible,
1211 if (shortcut_type == SHORTCUT_TYPE_VOLUME)
1213 GtkFileSystemVolume *volume;
1216 pixbuf = _gtk_file_system_volume_render_icon (volume, GTK_WIDGET (impl),
1217 impl->icon_size, NULL);
1219 else if (shortcut_type == SHORTCUT_TYPE_FILE)
1221 if (g_file_is_native (G_FILE (data)))
1224 struct ReloadIconsData *info;
1225 GtkTreePath *tree_path;
1226 GCancellable *cancellable;
1230 info = g_new0 (struct ReloadIconsData, 1);
1231 info->impl = g_object_ref (impl);
1232 tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
1233 info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->shortcuts_model), tree_path);
1234 gtk_tree_path_free (tree_path);
1236 cancellable = _gtk_file_system_get_info (impl->file_system, file,
1238 shortcuts_reload_icons_get_info_cb,
1240 impl->reload_icon_cancellables = g_slist_append (impl->reload_icon_cancellables, cancellable);
1244 GtkIconTheme *icon_theme;
1246 /* Don't call get_info for remote paths to avoid latency and
1248 * If we switch to a better bookmarks file format (XBEL), we
1249 * should use mime info to get a better icon.
1251 icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
1252 pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote",
1253 impl->icon_size, 0, NULL);
1256 else if (shortcut_type == SHORTCUT_TYPE_SEARCH)
1258 pixbuf = render_search_icon (impl);
1260 else if (shortcut_type == SHORTCUT_TYPE_RECENT)
1262 pixbuf = render_recent_icon (impl);
1265 gtk_list_store_set (impl->shortcuts_model, &iter,
1266 SHORTCUTS_COL_PIXBUF, pixbuf,
1270 g_object_unref (pixbuf);
1274 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
1278 profile_end ("end", NULL);
1282 shortcuts_find_folder (GtkFileChooserDefault *impl,
1285 GtkTreeSelection *selection;
1289 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
1291 g_assert (folder != NULL);
1292 pos = shortcut_find_position (impl, folder);
1295 gtk_tree_selection_unselect_all (selection);
1299 path = gtk_tree_path_new_from_indices (pos, -1);
1300 gtk_tree_selection_select_path (selection, path);
1301 gtk_tree_path_free (path);
1304 /* If a shortcut corresponds to the current folder, selects it */
1306 shortcuts_find_current_folder (GtkFileChooserDefault *impl)
1308 shortcuts_find_folder (impl, impl->current_folder);
1311 /* Removes the specified number of rows from the shortcuts list */
1313 shortcuts_remove_rows (GtkFileChooserDefault *impl,
1319 path = gtk_tree_path_new_from_indices (start_row, -1);
1321 for (; n_rows; n_rows--)
1325 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
1326 g_assert_not_reached ();
1328 shortcuts_free_row_data (impl, &iter);
1329 gtk_list_store_remove (impl->shortcuts_model, &iter);
1332 gtk_tree_path_free (path);
1336 shortcuts_update_count (GtkFileChooserDefault *impl,
1337 ShortcutsIndex type,
1342 case SHORTCUTS_HOME:
1344 impl->has_home = FALSE;
1346 impl->has_home = TRUE;
1349 case SHORTCUTS_DESKTOP:
1351 impl->has_desktop = FALSE;
1353 impl->has_desktop = TRUE;
1356 case SHORTCUTS_VOLUMES:
1357 impl->num_volumes += value;
1360 case SHORTCUTS_SHORTCUTS:
1361 impl->num_shortcuts += value;
1364 case SHORTCUTS_BOOKMARKS:
1365 impl->num_bookmarks += value;
1368 case SHORTCUTS_CURRENT_FOLDER:
1370 impl->shortcuts_current_folder_active = FALSE;
1372 impl->shortcuts_current_folder_active = TRUE;
1381 struct ShortcutsInsertRequest
1383 GtkFileChooserDefault *impl;
1387 GtkTreeRowReference *row_ref;
1388 ShortcutsIndex type;
1394 get_file_info_finished (GCancellable *cancellable,
1396 const GError *error,
1400 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1404 GCancellable *model_cancellable;
1405 struct ShortcutsInsertRequest *request = data;
1407 path = gtk_tree_row_reference_get_path (request->row_ref);
1409 /* Handle doesn't exist anymore in the model */
1412 pos = gtk_tree_path_get_indices (path)[0];
1413 gtk_tree_model_get_iter (GTK_TREE_MODEL (request->impl->shortcuts_model),
1415 gtk_tree_path_free (path);
1417 /* validate cancellable, else goto out */
1418 gtk_tree_model_get (GTK_TREE_MODEL (request->impl->shortcuts_model), &iter,
1419 SHORTCUTS_COL_CANCELLABLE, &model_cancellable,
1421 if (cancellable != model_cancellable)
1424 /* set the cancellable to NULL in the model (we unref later on) */
1425 gtk_list_store_set (request->impl->shortcuts_model, &iter,
1426 SHORTCUTS_COL_CANCELLABLE, NULL,
1434 gtk_list_store_remove (request->impl->shortcuts_model, &iter);
1435 shortcuts_update_count (request->impl, request->type, -1);
1437 if (request->type == SHORTCUTS_HOME)
1441 home = g_file_new_for_path (g_get_home_dir ());
1442 error_getting_info_dialog (request->impl, home, g_error_copy (error));
1443 g_object_unref (home);
1445 else if (request->type == SHORTCUTS_CURRENT_FOLDER)
1447 /* Remove the current folder separator */
1448 gint separator_pos = shortcuts_get_index (request->impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
1449 shortcuts_remove_rows (request->impl, separator_pos, 1);
1455 if (!request->label_copy)
1456 request->label_copy = g_strdup (g_file_info_get_display_name (info));
1457 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (request->impl),
1458 request->impl->icon_size);
1460 gtk_list_store_set (request->impl->shortcuts_model, &iter,
1461 SHORTCUTS_COL_PIXBUF, pixbuf,
1462 SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1463 SHORTCUTS_COL_NAME, request->label_copy,
1464 SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_FILE,
1465 SHORTCUTS_COL_REMOVABLE, request->removable,
1468 if (request->impl->shortcuts_pane_filter_model)
1469 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (request->impl->shortcuts_pane_filter_model));
1471 if (request->impl->shortcuts_combo_filter_model)
1472 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (request->impl->shortcuts_combo_filter_model));
1474 if (request->type == SHORTCUTS_CURRENT_FOLDER &&
1475 request->impl->save_folder_combo != NULL)
1477 /* The current folder is updated via _activate_iter(), don't
1478 * have save_folder_combo_changed_cb() call _activate_iter()
1481 g_signal_handlers_block_by_func (request->impl->save_folder_combo,
1482 G_CALLBACK (save_folder_combo_changed_cb),
1485 if (request->impl->has_search)
1488 if (request->impl->has_recent)
1491 gtk_combo_box_set_active (GTK_COMBO_BOX (request->impl->save_folder_combo), pos);
1492 g_signal_handlers_unblock_by_func (request->impl->save_folder_combo,
1493 G_CALLBACK (save_folder_combo_changed_cb),
1498 g_object_unref (pixbuf);
1501 g_object_unref (request->impl);
1502 g_object_unref (request->file);
1503 gtk_tree_row_reference_free (request->row_ref);
1504 g_free (request->label_copy);
1507 g_object_unref (cancellable);
1510 /* FIXME: GtkFileSystem needs a function to split a remote path
1511 * into hostname and path components, or maybe just have a
1512 * gtk_file_system_path_get_display_name().
1514 * This function is also used in gtkfilechooserbutton.c
1517 _gtk_file_chooser_label_for_file (GFile *file)
1519 const gchar *path, *start, *end, *p;
1520 gchar *uri, *host, *label;
1522 uri = g_file_get_uri (file);
1524 start = strstr (uri, "://");
1528 path = strchr (start, '/');
1533 end = uri + strlen (uri);
1537 /* strip username */
1538 p = strchr (start, '@');
1542 p = strchr (start, ':');
1546 host = g_strndup (start, end - start);
1548 /* Translators: the first string is a path and the second string
1549 * is a hostname. Nautilus and the panel contain the same string
1552 label = g_strdup_printf (_("%1$s on %2$s"), path, host);
1558 label = g_strdup (uri);
1566 /* Inserts a path in the shortcuts tree, making a copy of it; alternatively,
1567 * inserts a volume. A position of -1 indicates the end of the tree.
1570 shortcuts_insert_file (GtkFileChooserDefault *impl,
1572 ShortcutType shortcut_type,
1573 GtkFileSystemVolume *volume,
1577 ShortcutsIndex type)
1580 GdkPixbuf *pixbuf = NULL;
1581 gpointer data = NULL;
1583 GtkIconTheme *icon_theme;
1585 profile_start ("start shortcut", NULL);
1587 if (shortcut_type == SHORTCUT_TYPE_VOLUME)
1590 label_copy = _gtk_file_system_volume_get_display_name (volume);
1591 pixbuf = _gtk_file_system_volume_render_icon (volume, GTK_WIDGET (impl),
1592 impl->icon_size, NULL);
1594 else if (shortcut_type == SHORTCUT_TYPE_FILE)
1596 if (g_file_is_native (file))
1598 struct ShortcutsInsertRequest *request;
1599 GCancellable *cancellable;
1602 request = g_new0 (struct ShortcutsInsertRequest, 1);
1603 request->impl = g_object_ref (impl);
1604 request->file = g_object_ref (file);
1605 request->name_only = TRUE;
1606 request->removable = removable;
1608 request->type = type;
1610 request->label_copy = g_strdup (label);
1613 gtk_list_store_append (impl->shortcuts_model, &iter);
1615 gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
1617 p = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
1618 request->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->shortcuts_model), p);
1619 gtk_tree_path_free (p);
1621 cancellable = _gtk_file_system_get_info (request->impl->file_system, request->file,
1622 "standard::is-hidden,standard::is-backup,standard::display-name,standard::icon",
1623 get_file_info_finished, request);
1625 gtk_list_store_set (impl->shortcuts_model, &iter,
1626 SHORTCUTS_COL_DATA, g_object_ref (file),
1627 SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_FILE,
1628 SHORTCUTS_COL_CANCELLABLE, cancellable,
1631 shortcuts_update_count (impl, type, 1);
1637 /* Don't call get_info for remote paths to avoid latency and
1640 data = g_object_ref (file);
1642 label_copy = g_strdup (label);
1644 label_copy = _gtk_file_chooser_label_for_file (file);
1646 /* If we switch to a better bookmarks file format (XBEL), we
1647 * should use mime info to get a better icon.
1649 icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
1650 pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote",
1651 impl->icon_size, 0, NULL);
1656 g_assert_not_reached ();
1662 gtk_list_store_append (impl->shortcuts_model, &iter);
1664 gtk_list_store_insert (impl->shortcuts_model, &iter, pos);
1666 shortcuts_update_count (impl, type, 1);
1668 gtk_list_store_set (impl->shortcuts_model, &iter,
1669 SHORTCUTS_COL_PIXBUF, pixbuf,
1670 SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1671 SHORTCUTS_COL_NAME, label_copy,
1672 SHORTCUTS_COL_DATA, data,
1673 SHORTCUTS_COL_TYPE, shortcut_type,
1674 SHORTCUTS_COL_REMOVABLE, removable,
1675 SHORTCUTS_COL_CANCELLABLE, NULL,
1678 if (impl->shortcuts_pane_filter_model)
1679 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model));
1681 if (impl->shortcuts_combo_filter_model)
1682 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model));
1684 if (type == SHORTCUTS_CURRENT_FOLDER && impl->save_folder_combo != NULL)
1686 /* The current folder is updated via _activate_iter(), don't
1687 * have save_folder_combo_changed_cb() call _activate_iter()
1690 gint combo_pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER);
1692 if (impl->has_search)
1695 if (impl->has_recent)
1698 g_signal_handlers_block_by_func (impl->save_folder_combo,
1699 G_CALLBACK (save_folder_combo_changed_cb),
1702 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), combo_pos);
1703 g_signal_handlers_unblock_by_func (impl->save_folder_combo,
1704 G_CALLBACK (save_folder_combo_changed_cb),
1708 g_free (label_copy);
1711 g_object_unref (pixbuf);
1713 profile_end ("end", NULL);
1717 shortcuts_append_search (GtkFileChooserDefault *impl)
1722 pixbuf = render_search_icon (impl);
1724 gtk_list_store_append (impl->shortcuts_model, &iter);
1725 gtk_list_store_set (impl->shortcuts_model, &iter,
1726 SHORTCUTS_COL_PIXBUF, pixbuf,
1727 SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1728 SHORTCUTS_COL_NAME, _("Search"),
1729 SHORTCUTS_COL_DATA, NULL,
1730 SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_SEARCH,
1731 SHORTCUTS_COL_REMOVABLE, FALSE,
1735 g_object_unref (pixbuf);
1737 impl->has_search = TRUE;
1741 shortcuts_append_recent (GtkFileChooserDefault *impl)
1746 pixbuf = render_recent_icon (impl);
1748 gtk_list_store_append (impl->shortcuts_model, &iter);
1749 gtk_list_store_set (impl->shortcuts_model, &iter,
1750 SHORTCUTS_COL_PIXBUF, pixbuf,
1751 SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
1752 SHORTCUTS_COL_NAME, _("Recently Used"),
1753 SHORTCUTS_COL_DATA, NULL,
1754 SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_RECENT,
1755 SHORTCUTS_COL_REMOVABLE, FALSE,
1759 g_object_unref (pixbuf);
1761 impl->has_recent = TRUE;
1764 /* Appends an item for the user's home directory to the shortcuts model */
1766 shortcuts_append_home (GtkFileChooserDefault *impl)
1768 const char *home_path;
1771 profile_start ("start", NULL);
1773 home_path = g_get_home_dir ();
1774 if (home_path == NULL)
1776 profile_end ("end - no home directory!?", NULL);
1780 home = g_file_new_for_path (home_path);
1781 shortcuts_insert_file (impl, -1, SHORTCUT_TYPE_FILE, NULL, home, NULL, FALSE, SHORTCUTS_HOME);
1782 impl->has_home = TRUE;
1784 g_object_unref (home);
1786 profile_end ("end", NULL);
1789 /* Appends the ~/Desktop directory to the shortcuts model */
1791 shortcuts_append_desktop (GtkFileChooserDefault *impl)
1796 profile_start ("start", NULL);
1798 name = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
1799 /* "To disable a directory, point it to the homedir."
1800 * See http://freedesktop.org/wiki/Software/xdg-user-dirs
1802 if (!g_strcmp0 (name, g_get_home_dir ()))
1804 profile_end ("end", NULL);
1808 file = g_file_new_for_path (name);
1809 shortcuts_insert_file (impl, -1, SHORTCUT_TYPE_FILE, NULL, file, _("Desktop"), FALSE, SHORTCUTS_DESKTOP);
1810 impl->has_desktop = TRUE;
1812 /* We do not actually pop up an error dialog if there is no desktop directory
1813 * because some people may really not want to have one.
1816 g_object_unref (file);
1818 profile_end ("end", NULL);
1821 /* Appends a list of GFile to the shortcuts model; returns how many were inserted */
1823 shortcuts_append_bookmarks (GtkFileChooserDefault *impl,
1830 profile_start ("start", NULL);
1832 start_row = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR) + 1;
1835 for (; bookmarks; bookmarks = bookmarks->next)
1839 file = bookmarks->data;
1841 if (impl->local_only && !g_file_is_native (file))
1844 if (shortcut_find_position (impl, file) != -1)
1847 label = _gtk_file_system_get_bookmark_label (impl->file_system, file);
1849 shortcuts_insert_file (impl, start_row + num_inserted, SHORTCUT_TYPE_FILE, NULL, file, label, TRUE, SHORTCUTS_BOOKMARKS);
1855 profile_end ("end", NULL);
1857 return num_inserted;
1860 /* Returns the index for the corresponding item in the shortcuts bar */
1862 shortcuts_get_index (GtkFileChooserDefault *impl,
1863 ShortcutsIndex where)
1869 if (where == SHORTCUTS_SEARCH)
1872 n += impl->has_search ? 1 : 0;
1874 if (where == SHORTCUTS_RECENT)
1877 n += impl->has_recent ? 1 : 0;
1879 if (where == SHORTCUTS_RECENT_SEPARATOR)
1882 n += impl->has_recent ? 1 : 0;
1884 if (where == SHORTCUTS_HOME)
1887 n += impl->has_home ? 1 : 0;
1889 if (where == SHORTCUTS_DESKTOP)
1892 n += impl->has_desktop ? 1 : 0;
1894 if (where == SHORTCUTS_VOLUMES)
1897 n += impl->num_volumes;
1899 if (where == SHORTCUTS_SHORTCUTS)
1902 n += impl->num_shortcuts;
1904 if (where == SHORTCUTS_BOOKMARKS_SEPARATOR)
1907 /* If there are no bookmarks there won't be a separator */
1908 n += (impl->num_bookmarks > 0) ? 1 : 0;
1910 if (where == SHORTCUTS_BOOKMARKS)
1913 n += impl->num_bookmarks;
1915 if (where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR)
1920 if (where == SHORTCUTS_CURRENT_FOLDER)
1923 g_assert_not_reached ();
1930 /* Adds all the file system volumes to the shortcuts model */
1932 shortcuts_add_volumes (GtkFileChooserDefault *impl)
1937 gboolean old_changing_folders;
1939 profile_start ("start", NULL);
1941 old_changing_folders = impl->changing_folder;
1942 impl->changing_folder = TRUE;
1944 start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
1945 shortcuts_remove_rows (impl, start_row, impl->num_volumes);
1946 impl->num_volumes = 0;
1948 list = _gtk_file_system_list_volumes (impl->file_system);
1952 for (l = list; l; l = l->next)
1954 GtkFileSystemVolume *volume;
1958 if (impl->local_only)
1960 if (_gtk_file_system_volume_is_mounted (volume))
1963 gboolean base_is_native = TRUE;
1965 base_file = _gtk_file_system_volume_get_root (volume);
1966 if (base_file != NULL)
1968 base_is_native = g_file_is_native (base_file);
1969 g_object_unref (base_file);
1972 if (!base_is_native)
1977 shortcuts_insert_file (impl,
1979 SHORTCUT_TYPE_VOLUME,
1980 _gtk_file_system_volume_ref (volume),
1988 impl->num_volumes = n;
1989 g_slist_free (list);
1991 if (impl->shortcuts_pane_filter_model)
1992 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model));
1994 if (impl->shortcuts_combo_filter_model)
1995 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model));
1997 impl->changing_folder = old_changing_folders;
1999 profile_end ("end", NULL);
2002 /* Inserts a separator node in the shortcuts list */
2004 shortcuts_insert_separator (GtkFileChooserDefault *impl,
2005 ShortcutsIndex where)
2009 g_assert (where == SHORTCUTS_RECENT_SEPARATOR ||
2010 where == SHORTCUTS_BOOKMARKS_SEPARATOR ||
2011 where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2013 gtk_list_store_insert (impl->shortcuts_model, &iter,
2014 shortcuts_get_index (impl, where));
2015 gtk_list_store_set (impl->shortcuts_model, &iter,
2016 SHORTCUTS_COL_PIXBUF, NULL,
2017 SHORTCUTS_COL_PIXBUF_VISIBLE, FALSE,
2018 SHORTCUTS_COL_NAME, NULL,
2019 SHORTCUTS_COL_DATA, NULL,
2020 SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_SEPARATOR,
2024 /* Updates the list of bookmarks */
2026 shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
2029 gboolean old_changing_folders;
2031 GFile *list_selected = NULL;
2032 GFile *combo_selected = NULL;
2033 ShortcutType shortcut_type;
2036 profile_start ("start", NULL);
2038 old_changing_folders = impl->changing_folder;
2039 impl->changing_folder = TRUE;
2041 if (shortcuts_get_selected (impl, &iter))
2043 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model),
2045 SHORTCUTS_COL_DATA, &col_data,
2046 SHORTCUTS_COL_TYPE, &shortcut_type,
2049 if (col_data && shortcut_type == SHORTCUT_TYPE_FILE)
2050 list_selected = g_object_ref (col_data);
2053 if (impl->save_folder_combo &&
2054 gtk_combo_box_get_active_iter (GTK_COMBO_BOX (impl->save_folder_combo),
2057 GtkTreeIter child_iter;
2059 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model),
2062 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model),
2064 SHORTCUTS_COL_DATA, &col_data,
2065 SHORTCUTS_COL_TYPE, &shortcut_type,
2068 if (col_data && shortcut_type == SHORTCUT_TYPE_FILE)
2069 combo_selected = g_object_ref (col_data);
2072 if (impl->num_bookmarks > 0)
2073 shortcuts_remove_rows (impl,
2074 shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR),
2075 impl->num_bookmarks + 1);
2077 impl->num_bookmarks = 0;
2078 shortcuts_insert_separator (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
2080 bookmarks = _gtk_file_system_list_bookmarks (impl->file_system);
2081 shortcuts_append_bookmarks (impl, bookmarks);
2082 g_slist_foreach (bookmarks, (GFunc) g_object_unref, NULL);
2083 g_slist_free (bookmarks);
2085 if (impl->num_bookmarks == 0)
2086 shortcuts_remove_rows (impl, shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR), 1);
2088 if (impl->shortcuts_pane_filter_model)
2089 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model));
2091 if (impl->shortcuts_combo_filter_model)
2092 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model));
2096 shortcuts_find_folder (impl, list_selected);
2097 g_object_unref (list_selected);
2104 pos = shortcut_find_position (impl, combo_selected);
2107 if (impl->has_search)
2110 if (impl->has_recent)
2113 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), pos);
2116 g_object_unref (combo_selected);
2119 impl->changing_folder = old_changing_folders;
2121 profile_end ("end", NULL);
2124 /* Appends a separator and a row to the shortcuts list for the current folder */
2126 shortcuts_add_current_folder (GtkFileChooserDefault *impl)
2131 g_assert (!impl->shortcuts_current_folder_active);
2135 g_assert (impl->current_folder != NULL);
2137 pos = shortcut_find_position (impl, impl->current_folder);
2140 GtkFileSystemVolume *volume;
2145 shortcuts_insert_separator (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2149 pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER);
2151 volume = _gtk_file_system_get_volume_for_file (impl->file_system, impl->current_folder);
2153 base_file = _gtk_file_system_volume_get_root (volume);
2157 if (base_file && g_file_equal (base_file, impl->current_folder))
2158 shortcuts_insert_file (impl, pos, SHORTCUT_TYPE_VOLUME, volume, NULL, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER);
2160 shortcuts_insert_file (impl, pos, SHORTCUT_TYPE_FILE, NULL, impl->current_folder, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER);
2163 g_object_unref (base_file);
2165 else if (impl->save_folder_combo != NULL)
2167 if (impl->has_search)
2170 if (impl->has_recent)
2171 pos -= 2; /* + separator */
2173 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), pos);
2177 /* Updates the current folder row in the shortcuts model */
2179 shortcuts_update_current_folder (GtkFileChooserDefault *impl)
2183 pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2185 if (impl->shortcuts_current_folder_active)
2187 shortcuts_remove_rows (impl, pos, 2);
2188 impl->shortcuts_current_folder_active = FALSE;
2191 shortcuts_add_current_folder (impl);
2194 /* Filter function used for the shortcuts filter model */
2196 shortcuts_pane_filter_cb (GtkTreeModel *model,
2200 GtkFileChooserDefault *impl;
2204 impl = GTK_FILE_CHOOSER_DEFAULT (data);
2206 path = gtk_tree_model_get_path (model, iter);
2210 pos = *gtk_tree_path_get_indices (path);
2211 gtk_tree_path_free (path);
2213 return (pos < shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR));
2216 /* Creates the list model for shortcuts */
2218 shortcuts_model_create (GtkFileChooserDefault *impl)
2220 /* Keep this order in sync with the SHORCUTS_COL_* enum values */
2221 impl->shortcuts_model = gtk_list_store_new (SHORTCUTS_COL_NUM_COLUMNS,
2222 GDK_TYPE_PIXBUF, /* pixbuf */
2223 G_TYPE_STRING, /* name */
2224 G_TYPE_POINTER, /* path or volume */
2225 G_TYPE_INT, /* ShortcutType */
2226 G_TYPE_BOOLEAN, /* removable */
2227 G_TYPE_BOOLEAN, /* pixbuf cell visibility */
2228 G_TYPE_POINTER); /* GCancellable */
2230 shortcuts_append_search (impl);
2232 if (impl->recent_manager)
2234 shortcuts_append_recent (impl);
2235 shortcuts_insert_separator (impl, SHORTCUTS_RECENT_SEPARATOR);
2238 if (impl->file_system)
2240 shortcuts_append_home (impl);
2241 shortcuts_append_desktop (impl);
2242 shortcuts_add_volumes (impl);
2245 impl->shortcuts_pane_filter_model = shortcuts_pane_model_filter_new (impl,
2246 GTK_TREE_MODEL (impl->shortcuts_model),
2249 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
2250 shortcuts_pane_filter_cb,
2255 /* Callback used when the "New Folder" button is clicked */
2257 new_folder_button_clicked (GtkButton *button,
2258 GtkFileChooserDefault *impl)
2263 if (!impl->browse_files_model)
2264 return; /* FIXME: this sucks. Disable the New Folder button or something. */
2266 /* Prevent button from being clicked twice */
2267 gtk_widget_set_sensitive (impl->browse_new_folder_button, FALSE);
2269 _gtk_file_system_model_add_editable (impl->browse_files_model, &iter);
2271 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->browse_files_model), &iter);
2272 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_files_tree_view),
2273 path, impl->list_name_column,
2276 g_object_set (impl->list_name_renderer, "editable", TRUE, NULL);
2277 gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view),
2279 impl->list_name_column,
2282 gtk_tree_path_free (path);
2285 /* Idle handler for creating a new folder after editing its name cell, or for
2286 * canceling the editing.
2289 edited_idle_cb (GtkFileChooserDefault *impl)
2291 GDK_THREADS_ENTER ();
2293 g_source_destroy (impl->edited_idle);
2294 impl->edited_idle = NULL;
2296 _gtk_file_system_model_remove_editable (impl->browse_files_model);
2297 g_object_set (impl->list_name_renderer, "editable", FALSE, NULL);
2299 gtk_widget_set_sensitive (impl->browse_new_folder_button, TRUE);
2301 if (impl->edited_new_text /* not cancelled? */
2302 && (strlen (impl->edited_new_text) != 0)
2303 && (strcmp (impl->edited_new_text, DEFAULT_NEW_FOLDER_NAME) != 0)) /* Don't create folder if name is empty or has not been edited */
2305 GError *error = NULL;
2308 file = g_file_get_child_for_display_name (impl->current_folder,
2309 impl->edited_new_text,
2313 GError *error = NULL;
2315 if (g_file_make_directory (file, NULL, &error))
2316 change_folder_and_display_error (impl, file, FALSE);
2318 error_creating_folder_dialog (impl, file, error);
2320 g_object_unref (file);
2323 error_creating_folder_dialog (impl, file, error);
2325 g_free (impl->edited_new_text);
2326 impl->edited_new_text = NULL;
2329 GDK_THREADS_LEAVE ();
2335 queue_edited_idle (GtkFileChooserDefault *impl,
2336 const gchar *new_text)
2338 /* We create the folder in an idle handler so that we don't modify the tree
2342 if (!impl->edited_idle)
2344 impl->edited_idle = g_idle_source_new ();
2345 g_source_set_closure (impl->edited_idle,
2346 g_cclosure_new_object (G_CALLBACK (edited_idle_cb),
2348 g_source_attach (impl->edited_idle, NULL);
2351 g_free (impl->edited_new_text);
2352 impl->edited_new_text = g_strdup (new_text);
2355 /* Callback used from the text cell renderer when the new folder is named */
2357 renderer_edited_cb (GtkCellRendererText *cell_renderer_text,
2359 const gchar *new_text,
2360 GtkFileChooserDefault *impl)
2362 /* work around bug #154921 */
2363 g_object_set (cell_renderer_text,
2364 "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
2365 queue_edited_idle (impl, new_text);
2368 /* Callback used from the text cell renderer when the new folder edition gets
2372 renderer_editing_canceled_cb (GtkCellRendererText *cell_renderer_text,
2373 GtkFileChooserDefault *impl)
2375 /* work around bug #154921 */
2376 g_object_set (cell_renderer_text,
2377 "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
2378 queue_edited_idle (impl, NULL);
2381 /* Creates the widgets for the filter combo box */
2383 filter_create (GtkFileChooserDefault *impl)
2385 GtkCellRenderer *cell;
2388 impl->filter_combo = gtk_combo_box_new_text ();
2389 gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (impl->filter_combo), FALSE);
2391 /* Get the combo's text renderer and set ellipsize parameters */
2392 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (impl->filter_combo));
2396 g_object_set (G_OBJECT (cell),
2397 "ellipsize", PANGO_ELLIPSIZE_END,
2400 g_list_free (cells);
2402 g_signal_connect (impl->filter_combo, "changed",
2403 G_CALLBACK (filter_combo_changed), impl);
2405 gtk_widget_set_tooltip_text (impl->filter_combo,
2406 _("Select which types of files are shown"));
2408 return impl->filter_combo;
2412 button_new (GtkFileChooserDefault *impl,
2414 const char *stock_id,
2422 button = gtk_button_new_with_mnemonic (text);
2423 image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
2424 gtk_button_set_image (GTK_BUTTON (button), image);
2426 gtk_widget_set_sensitive (button, sensitive);
2427 g_signal_connect (button, "clicked", callback, impl);
2430 gtk_widget_show (button);
2435 /* Looks for a path among the shortcuts; returns its index or -1 if it doesn't exist */
2437 shortcut_find_position (GtkFileChooserDefault *impl,
2442 int current_folder_separator_idx;
2444 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
2447 current_folder_separator_idx = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
2450 /* FIXME: is this still needed? */
2451 if (current_folder_separator_idx >= impl->shortcuts_model->length)
2455 for (i = 0; i < current_folder_separator_idx; i++)
2458 ShortcutType shortcut_type;
2460 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2461 SHORTCUTS_COL_DATA, &col_data,
2462 SHORTCUTS_COL_TYPE, &shortcut_type,
2467 if (shortcut_type == SHORTCUT_TYPE_VOLUME)
2469 GtkFileSystemVolume *volume;
2474 base_file = _gtk_file_system_volume_get_root (volume);
2476 exists = base_file && g_file_equal (file, base_file);
2479 g_object_unref (base_file);
2484 else if (shortcut_type == SHORTCUT_TYPE_FILE)
2488 model_file = col_data;
2490 if (model_file && g_file_equal (model_file, file))
2495 if (i < current_folder_separator_idx - 1)
2497 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
2498 g_assert_not_reached ();
2505 /* Tries to add a bookmark from a path name */
2507 shortcuts_add_bookmark_from_file (GtkFileChooserDefault *impl,
2513 g_return_val_if_fail (G_IS_FILE (file), FALSE);
2515 if (shortcut_find_position (impl, file) != -1)
2519 if (!_gtk_file_system_insert_bookmark (impl->file_system, file, pos, &error))
2521 error_adding_bookmark_dialog (impl, file, error);
2529 add_bookmark_foreach_cb (GtkTreeModel *model,
2534 GtkFileChooserDefault *impl;
2537 impl = (GtkFileChooserDefault *) data;
2539 gtk_tree_model_get (model, iter,
2540 MODEL_COL_FILE, &file,
2543 shortcuts_add_bookmark_from_file (impl, file, -1);
2545 g_object_unref (file);
2548 /* Adds a bookmark from the currently selected item in the file list */
2550 bookmarks_add_selected_folder (GtkFileChooserDefault *impl)
2552 GtkTreeSelection *selection;
2554 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2556 if (gtk_tree_selection_count_selected_rows (selection) == 0)
2557 shortcuts_add_bookmark_from_file (impl, impl->current_folder, -1);
2559 gtk_tree_selection_selected_foreach (selection,
2560 add_bookmark_foreach_cb,
2564 /* Callback used when the "Add bookmark" button is clicked */
2566 add_bookmark_button_clicked_cb (GtkButton *button,
2567 GtkFileChooserDefault *impl)
2569 bookmarks_add_selected_folder (impl);
2572 /* Returns TRUE plus an iter in the shortcuts_model if a row is selected;
2573 * returns FALSE if no shortcut is selected.
2576 shortcuts_get_selected (GtkFileChooserDefault *impl,
2579 GtkTreeSelection *selection;
2580 GtkTreeIter parent_iter;
2582 if (!impl->browse_shortcuts_tree_view)
2585 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
2587 if (!gtk_tree_selection_get_selected (selection, NULL, &parent_iter))
2590 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
2596 /* Removes the selected bookmarks */
2598 remove_selected_bookmarks (GtkFileChooserDefault *impl)
2606 if (!shortcuts_get_selected (impl, &iter))
2609 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2610 SHORTCUTS_COL_DATA, &col_data,
2611 SHORTCUTS_COL_REMOVABLE, &removable,
2617 g_assert (col_data != NULL);
2622 if (!_gtk_file_system_remove_bookmark (impl->file_system, file, &error))
2623 error_removing_bookmark_dialog (impl, file, error);
2626 /* Callback used when the "Remove bookmark" button is clicked */
2628 remove_bookmark_button_clicked_cb (GtkButton *button,
2629 GtkFileChooserDefault *impl)
2631 remove_selected_bookmarks (impl);
2634 struct selection_check_closure {
2635 GtkFileChooserDefault *impl;
2638 gboolean all_folders;
2641 /* Used from gtk_tree_selection_selected_foreach() */
2643 selection_check_foreach_cb (GtkTreeModel *model,
2648 struct selection_check_closure *closure;
2652 gtk_tree_model_get (model, iter,
2653 MODEL_COL_FILE, &file,
2654 MODEL_COL_IS_FOLDER, &is_folder,
2660 g_object_unref (file);
2663 closure->num_selected++;
2665 closure->all_folders = closure->all_folders && is_folder;
2666 closure->all_files = closure->all_files && !is_folder;
2669 /* Checks whether the selected items in the file list are all files or all folders */
2671 selection_check (GtkFileChooserDefault *impl,
2673 gboolean *all_files,
2674 gboolean *all_folders)
2676 struct selection_check_closure closure;
2677 GtkTreeSelection *selection;
2679 closure.impl = impl;
2680 closure.num_selected = 0;
2681 closure.all_files = TRUE;
2682 closure.all_folders = TRUE;
2684 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2685 gtk_tree_selection_selected_foreach (selection,
2686 selection_check_foreach_cb,
2689 g_assert (closure.num_selected == 0 || !(closure.all_files && closure.all_folders));
2692 *num_selected = closure.num_selected;
2695 *all_files = closure.all_files;
2698 *all_folders = closure.all_folders;
2701 struct get_selected_file_closure {
2702 GtkFileChooserDefault *impl;
2707 get_selected_file_foreach_cb (GtkTreeModel *model,
2712 struct get_selected_file_closure *closure = data;
2716 /* Just in case this function gets run more than once with a multiple selection; we only care about one file */
2717 g_object_unref (closure->file);
2718 closure->file = NULL;
2721 gtk_tree_model_get (model, iter,
2722 MODEL_COL_FILE, &closure->file, /* this will give us a reffed file */
2726 /* Returns a selected path from the file list */
2728 get_selected_file (GtkFileChooserDefault *impl)
2730 struct get_selected_file_closure closure;
2731 GtkTreeSelection *selection;
2733 closure.impl = impl;
2734 closure.file = NULL;
2736 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2737 gtk_tree_selection_selected_foreach (selection,
2738 get_selected_file_foreach_cb,
2741 return closure.file;
2745 GtkFileChooserDefault *impl;
2747 } UpdateTooltipData;
2750 update_tooltip (GtkTreeModel *model,
2755 UpdateTooltipData *udata = data;
2757 if (udata->tip == NULL)
2759 gchar *display_name;
2761 gtk_tree_model_get (model, iter,
2762 MODEL_COL_NAME, &display_name,
2765 udata->tip = g_strdup_printf (_("Add the folder '%s' to the bookmarks"),
2767 g_free (display_name);
2772 /* Sensitize the "add bookmark" button if all the selected items are folders, or
2773 * if there are no selected items *and* the current folder is not in the
2774 * bookmarks list. De-sensitize the button otherwise.
2777 bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl)
2780 gboolean all_folders;
2784 selection_check (impl, &num_selected, NULL, &all_folders);
2786 if (num_selected == 0)
2787 active = (impl->current_folder != NULL) && (shortcut_find_position (impl, impl->current_folder) == -1);
2788 else if (num_selected == 1)
2792 file = get_selected_file (impl);
2793 active = file && all_folders && (shortcut_find_position (impl, file) == -1);
2795 g_object_unref (file);
2798 active = all_folders;
2800 gtk_widget_set_sensitive (impl->browse_shortcuts_add_button, active);
2802 if (impl->browse_files_popup_menu_add_shortcut_item)
2803 gtk_widget_set_sensitive (impl->browse_files_popup_menu_add_shortcut_item,
2804 (num_selected == 0) ? FALSE : active);
2808 if (num_selected == 0)
2809 tip = g_strdup_printf (_("Add the current folder to the bookmarks"));
2810 else if (num_selected > 1)
2811 tip = g_strdup_printf (_("Add the selected folders to the bookmarks"));
2814 GtkTreeSelection *selection;
2815 UpdateTooltipData data;
2817 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
2820 gtk_tree_selection_selected_foreach (selection, update_tooltip, &data);
2824 gtk_widget_set_tooltip_text (impl->browse_shortcuts_add_button, tip);
2829 /* Sets the sensitivity of the "remove bookmark" button depending on whether a
2830 * bookmark row is selected in the shortcuts tree.
2833 bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl)
2836 gboolean removable = FALSE;
2840 if (shortcuts_get_selected (impl, &iter))
2842 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2843 SHORTCUTS_COL_REMOVABLE, &removable,
2844 SHORTCUTS_COL_NAME, &name,
2846 gtk_widget_set_sensitive (impl->browse_shortcuts_remove_button, removable);
2849 tip = g_strdup_printf (_("Remove the bookmark '%s'"), name);
2851 tip = g_strdup_printf (_("Bookmark '%s' cannot be removed"), name);
2853 gtk_widget_set_tooltip_text (impl->browse_shortcuts_remove_button, tip);
2857 gtk_widget_set_tooltip_text (impl->browse_shortcuts_remove_button,
2858 _("Remove the selected bookmark"));
2863 shortcuts_check_popup_sensitivity (GtkFileChooserDefault *impl)
2866 gboolean removable = FALSE;
2868 if (impl->browse_shortcuts_popup_menu == NULL)
2871 if (shortcuts_get_selected (impl, &iter))
2872 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
2873 SHORTCUTS_COL_REMOVABLE, &removable,
2876 gtk_widget_set_sensitive (impl->browse_shortcuts_popup_menu_remove_item, removable);
2877 gtk_widget_set_sensitive (impl->browse_shortcuts_popup_menu_rename_item, removable);
2880 /* GtkWidget::drag-begin handler for the shortcuts list. */
2882 shortcuts_drag_begin_cb (GtkWidget *widget,
2883 GdkDragContext *context,
2884 GtkFileChooserDefault *impl)
2887 impl->shortcuts_drag_context = g_object_ref (context);
2892 /* Removes the idle handler for outside drags */
2894 shortcuts_cancel_drag_outside_idle (GtkFileChooserDefault *impl)
2896 if (!impl->shortcuts_drag_outside_idle)
2899 g_source_destroy (impl->shortcuts_drag_outside_idle);
2900 impl->shortcuts_drag_outside_idle = NULL;
2904 /* GtkWidget::drag-end handler for the shortcuts list. */
2906 shortcuts_drag_end_cb (GtkWidget *widget,
2907 GdkDragContext *context,
2908 GtkFileChooserDefault *impl)
2911 g_object_unref (impl->shortcuts_drag_context);
2913 shortcuts_cancel_drag_outside_idle (impl);
2915 if (!impl->shortcuts_drag_outside)
2918 gtk_button_clicked (GTK_BUTTON (impl->browse_shortcuts_remove_button));
2920 impl->shortcuts_drag_outside = FALSE;
2924 /* GtkWidget::drag-data-delete handler for the shortcuts list. */
2926 shortcuts_drag_data_delete_cb (GtkWidget *widget,
2927 GdkDragContext *context,
2928 GtkFileChooserDefault *impl)
2930 g_signal_stop_emission_by_name (widget, "drag-data-delete");
2933 /* GtkWidget::drag-leave handler for the shortcuts list. We unhighlight the
2937 shortcuts_drag_leave_cb (GtkWidget *widget,
2938 GdkDragContext *context,
2940 GtkFileChooserDefault *impl)
2943 if (gtk_drag_get_source_widget (context) == widget && !impl->shortcuts_drag_outside_idle)
2945 impl->shortcuts_drag_outside_idle = g_idle_source_new ();
2946 g_source_set_closure (impl->shortcuts_drag_outside_idle,
2947 g_cclosure_new_object (G_CALLBACK (shortcuts_drag_outside_idle_cb),
2949 g_source_attach (impl->shortcuts_drag_outside_idle, NULL);
2953 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
2955 GTK_TREE_VIEW_DROP_BEFORE);
2957 g_signal_stop_emission_by_name (widget, "drag-leave");
2960 /* Computes the appropriate row and position for dropping */
2962 shortcuts_compute_drop_position (GtkFileChooserDefault *impl,
2966 GtkTreeViewDropPosition *pos)
2968 GtkTreeView *tree_view;
2969 GtkTreeViewColumn *column;
2973 int bookmarks_index;
2975 tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
2977 bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
2979 if (!gtk_tree_view_get_path_at_pos (tree_view,
2981 y - TREE_VIEW_HEADER_HEIGHT (tree_view),
2987 row = bookmarks_index + impl->num_bookmarks - 1;
2988 *path = gtk_tree_path_new_from_indices (row, -1);
2989 *pos = GTK_TREE_VIEW_DROP_AFTER;
2993 row = *gtk_tree_path_get_indices (*path);
2994 gtk_tree_view_get_background_area (tree_view, *path, column, &cell);
2995 gtk_tree_path_free (*path);
2997 if (row < bookmarks_index)
2999 row = bookmarks_index;
3000 *pos = GTK_TREE_VIEW_DROP_BEFORE;
3002 else if (row > bookmarks_index + impl->num_bookmarks - 1)
3004 row = bookmarks_index + impl->num_bookmarks - 1;
3005 *pos = GTK_TREE_VIEW_DROP_AFTER;
3009 if (cell_y < cell.height / 2)
3010 *pos = GTK_TREE_VIEW_DROP_BEFORE;
3012 *pos = GTK_TREE_VIEW_DROP_AFTER;
3015 *path = gtk_tree_path_new_from_indices (row, -1);
3018 /* GtkWidget::drag-motion handler for the shortcuts list. We basically
3019 * implement the destination side of DnD by hand, due to limitations in
3020 * GtkTreeView's DnD API.
3023 shortcuts_drag_motion_cb (GtkWidget *widget,
3024 GdkDragContext *context,
3028 GtkFileChooserDefault *impl)
3031 GtkTreeViewDropPosition pos;
3032 GdkDragAction action;
3035 if (gtk_drag_get_source_widget (context) == widget)
3037 shortcuts_cancel_drag_outside_idle (impl);
3039 if (impl->shortcuts_drag_outside)
3041 shortcuts_drag_set_delete_cursor (impl, FALSE);
3042 impl->shortcuts_drag_outside = FALSE;
3047 if (context->suggested_action == GDK_ACTION_COPY ||
3048 (context->actions & GDK_ACTION_COPY) != 0)
3049 action = GDK_ACTION_COPY;
3050 else if (context->suggested_action == GDK_ACTION_MOVE ||
3051 (context->actions & GDK_ACTION_MOVE) != 0)
3052 action = GDK_ACTION_MOVE;
3059 shortcuts_compute_drop_position (impl, x, y, &path, &pos);
3060 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), path, pos);
3061 gtk_tree_path_free (path);
3065 g_signal_stop_emission_by_name (widget, "drag-motion");
3069 gdk_drag_status (context, action, time_);
3076 /* GtkWidget::drag-drop handler for the shortcuts list. */
3078 shortcuts_drag_drop_cb (GtkWidget *widget,
3079 GdkDragContext *context,
3083 GtkFileChooserDefault *impl)
3086 shortcuts_cancel_drag_outside_idle (impl);
3089 g_signal_stop_emission_by_name (widget, "drag-drop");
3093 /* Parses a "text/uri-list" string and inserts its URIs as bookmarks */
3095 shortcuts_drop_uris (GtkFileChooserDefault *impl,
3096 GtkSelectionData *selection_data,
3102 uris = gtk_selection_data_get_uris (selection_data);
3106 for (i = 0; uris[i]; i++)
3112 file = g_file_new_for_uri (uri);
3114 if (shortcuts_add_bookmark_from_file (impl, file, position))
3117 g_object_unref (file);
3123 /* Reorders the selected bookmark to the specified position */
3125 shortcuts_reorder (GtkFileChooserDefault *impl,
3130 ShortcutType shortcut_type;
3133 int bookmarks_index;
3138 /* Get the selected path */
3140 if (!shortcuts_get_selected (impl, &iter))
3141 g_assert_not_reached ();
3143 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
3144 old_position = *gtk_tree_path_get_indices (path);
3145 gtk_tree_path_free (path);
3147 bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
3148 old_position -= bookmarks_index;
3149 g_assert (old_position >= 0 && old_position < impl->num_bookmarks);
3151 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
3152 SHORTCUTS_COL_NAME, &name,
3153 SHORTCUTS_COL_DATA, &col_data,
3154 SHORTCUTS_COL_TYPE, &shortcut_type,
3156 g_assert (col_data != NULL);
3157 g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
3160 g_object_ref (file); /* removal below will free file, so we need a new ref */
3162 /* Remove the path from the old position and insert it in the new one */
3164 if (new_position > old_position)
3167 if (old_position == new_position)
3171 if (_gtk_file_system_remove_bookmark (impl->file_system, file, &error))
3173 shortcuts_add_bookmark_from_file (impl, file, new_position);
3174 _gtk_file_system_set_bookmark_label (impl->file_system, file, name);
3177 error_adding_bookmark_dialog (impl, file, error);
3181 g_object_unref (file);
3184 /* Callback used when we get the drag data for the bookmarks list. We add the
3185 * received URIs as bookmarks if they are folders.
3188 shortcuts_drag_data_received_cb (GtkWidget *widget,
3189 GdkDragContext *context,
3192 GtkSelectionData *selection_data,
3197 GtkFileChooserDefault *impl;
3198 GtkTreePath *tree_path;
3199 GtkTreeViewDropPosition tree_pos;
3201 int bookmarks_index;
3203 impl = GTK_FILE_CHOOSER_DEFAULT (data);
3205 /* Compute position */
3207 bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
3209 shortcuts_compute_drop_position (impl, x, y, &tree_path, &tree_pos);
3210 position = *gtk_tree_path_get_indices (tree_path);
3211 gtk_tree_path_free (tree_path);
3213 if (tree_pos == GTK_TREE_VIEW_DROP_AFTER)
3216 g_assert (position >= bookmarks_index);
3217 position -= bookmarks_index;
3219 if (gtk_targets_include_uri (&selection_data->target, 1))
3220 shortcuts_drop_uris (impl, selection_data, position);
3221 else if (selection_data->target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
3222 shortcuts_reorder (impl, position);
3224 g_signal_stop_emission_by_name (widget, "drag-data-received");
3227 /* Callback used to display a tooltip in the shortcuts tree */
3229 shortcuts_query_tooltip_cb (GtkWidget *widget,
3232 gboolean keyboard_mode,
3233 GtkTooltip *tooltip,
3234 GtkFileChooserDefault *impl)
3236 GtkTreeModel *model;
3239 if (gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget),
3247 ShortcutType shortcut_type;
3249 gtk_tree_model_get (model, &iter,
3250 SHORTCUTS_COL_DATA, &col_data,
3251 SHORTCUTS_COL_TYPE, &shortcut_type,
3254 if (shortcut_type == SHORTCUT_TYPE_SEPARATOR)
3256 else if (shortcut_type == SHORTCUT_TYPE_VOLUME)
3258 else if (shortcut_type == SHORTCUT_TYPE_FILE)
3263 file = G_FILE (col_data);
3264 parse_name = g_file_get_parse_name (file);
3266 gtk_tooltip_set_text (tooltip, parse_name);
3268 g_free (parse_name);
3272 else if (shortcut_type == SHORTCUT_TYPE_SEARCH)
3276 else if (shortcut_type == SHORTCUT_TYPE_RECENT)
3286 /* Callback used when the selection in the shortcuts tree changes */
3288 shortcuts_selection_changed_cb (GtkTreeSelection *selection,
3289 GtkFileChooserDefault *impl)
3292 GtkTreeIter child_iter;
3294 bookmarks_check_remove_sensitivity (impl);
3295 shortcuts_check_popup_sensitivity (impl);
3297 if (impl->changing_folder)
3300 if (gtk_tree_selection_get_selected(selection, NULL, &iter))
3302 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
3305 shortcuts_activate_iter (impl, &child_iter);
3310 shortcuts_row_separator_func (GtkTreeModel *model,
3314 ShortcutType shortcut_type;
3316 gtk_tree_model_get (model, iter, SHORTCUTS_COL_TYPE, &shortcut_type, -1);
3318 return shortcut_type == SHORTCUT_TYPE_SEPARATOR;
3322 shortcuts_key_press_event_after_cb (GtkWidget *tree_view,
3324 GtkFileChooserDefault *impl)
3328 /* don't screw up focus switching with Tab */
3329 if (event->keyval == GDK_Tab
3330 || event->keyval == GDK_KP_Tab
3331 || event->keyval == GDK_ISO_Left_Tab
3332 || event->length < 1)
3335 if (impl->location_entry)
3336 entry = impl->location_entry;
3337 else if (impl->search_entry)
3338 entry = impl->search_entry;
3344 gtk_widget_grab_focus (entry);
3345 return gtk_widget_event (entry, (GdkEvent *) event);
3351 /* Callback used when the file list's popup menu is detached */
3353 shortcuts_popup_menu_detach_cb (GtkWidget *attach_widget,
3356 GtkFileChooserDefault *impl;
3358 impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
3359 g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
3361 impl->browse_shortcuts_popup_menu = NULL;
3362 impl->browse_shortcuts_popup_menu_remove_item = NULL;
3363 impl->browse_shortcuts_popup_menu_rename_item = NULL;
3367 remove_shortcut_cb (GtkMenuItem *item,
3368 GtkFileChooserDefault *impl)
3370 remove_selected_bookmarks (impl);
3373 /* Rename the selected bookmark */
3375 rename_selected_bookmark (GtkFileChooserDefault *impl)
3379 GtkTreeViewColumn *column;
3380 GtkCellRenderer *cell;
3383 if (shortcuts_get_selected (impl, &iter))
3385 path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);
3386 column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), 0);
3387 renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
3388 cell = g_list_nth_data (renderers, 1);
3389 g_list_free (renderers);
3390 g_object_set (cell, "editable", TRUE, NULL);
3391 gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3392 path, column, cell, TRUE);
3393 gtk_tree_path_free (path);
3398 rename_shortcut_cb (GtkMenuItem *item,
3399 GtkFileChooserDefault *impl)
3401 rename_selected_bookmark (impl);
3404 /* Constructs the popup menu for the file list if needed */
3406 shortcuts_build_popup_menu (GtkFileChooserDefault *impl)
3410 if (impl->browse_shortcuts_popup_menu)
3413 impl->browse_shortcuts_popup_menu = gtk_menu_new ();
3414 gtk_menu_attach_to_widget (GTK_MENU (impl->browse_shortcuts_popup_menu),
3415 impl->browse_shortcuts_tree_view,
3416 shortcuts_popup_menu_detach_cb);
3418 item = gtk_image_menu_item_new_with_label (_("Remove"));
3419 impl->browse_shortcuts_popup_menu_remove_item = item;
3420 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
3421 gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU));
3422 g_signal_connect (item, "activate",
3423 G_CALLBACK (remove_shortcut_cb), impl);
3424 gtk_widget_show (item);
3425 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu), item);
3427 item = gtk_menu_item_new_with_label (_("Rename..."));
3428 impl->browse_shortcuts_popup_menu_rename_item = item;
3429 g_signal_connect (item, "activate",
3430 G_CALLBACK (rename_shortcut_cb), impl);
3431 gtk_widget_show (item);
3432 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu), item);
3436 shortcuts_update_popup_menu (GtkFileChooserDefault *impl)
3438 shortcuts_build_popup_menu (impl);
3439 shortcuts_check_popup_sensitivity (impl);
3443 popup_position_func (GtkMenu *menu,
3447 gpointer user_data);
3450 shortcuts_popup_menu (GtkFileChooserDefault *impl,
3451 GdkEventButton *event)
3453 shortcuts_update_popup_menu (impl);
3455 gtk_menu_popup (GTK_MENU (impl->browse_shortcuts_popup_menu),
3456 NULL, NULL, NULL, NULL,
3457 event->button, event->time);
3460 gtk_menu_popup (GTK_MENU (impl->browse_shortcuts_popup_menu),
3462 popup_position_func, impl->browse_shortcuts_tree_view,
3463 0, GDK_CURRENT_TIME);
3464 gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->browse_shortcuts_popup_menu),
3469 /* Callback used for the GtkWidget::popup-menu signal of the shortcuts list */
3471 shortcuts_popup_menu_cb (GtkWidget *widget,
3472 GtkFileChooserDefault *impl)
3474 shortcuts_popup_menu (impl, NULL);
3478 /* Callback used when a button is pressed on the shortcuts list.
3479 * We trap button 3 to bring up a popup menu.
3482 shortcuts_button_press_event_cb (GtkWidget *widget,
3483 GdkEventButton *event,
3484 GtkFileChooserDefault *impl)
3486 static gboolean in_press = FALSE;
3492 if (event->button != 3)
3496 handled = gtk_widget_event (impl->browse_shortcuts_tree_view, (GdkEvent *) event);
3502 shortcuts_popup_menu (impl, event);
3507 shortcuts_edited (GtkCellRenderer *cell,
3510 GtkFileChooserDefault *impl)
3516 g_object_set (cell, "editable", FALSE, NULL);
3518 path = gtk_tree_path_new_from_string (path_string);
3519 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
3520 g_assert_not_reached ();
3522 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
3523 SHORTCUTS_COL_DATA, &shortcut,
3525 gtk_tree_path_free (path);
3527 _gtk_file_system_set_bookmark_label (impl->file_system, shortcut, new_text);
3531 shortcuts_editing_canceled (GtkCellRenderer *cell,
3532 GtkFileChooserDefault *impl)
3534 g_object_set (cell, "editable", FALSE, NULL);
3537 /* Creates the widgets for the shortcuts and bookmarks tree */
3539 shortcuts_list_create (GtkFileChooserDefault *impl)
3542 GtkTreeSelection *selection;
3543 GtkTreeViewColumn *column;
3544 GtkCellRenderer *renderer;
3546 /* Target types for dragging a row to/from the shortcuts list */
3547 const GtkTargetEntry tree_model_row_targets[] = {
3548 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }
3551 /* Scrolled window */
3553 swin = gtk_scrolled_window_new (NULL, NULL);
3554 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
3555 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
3556 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
3558 gtk_widget_show (swin);
3562 impl->browse_shortcuts_tree_view = gtk_tree_view_new ();
3563 gtk_tree_view_set_enable_search (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), FALSE);
3564 #ifdef PROFILE_FILE_CHOOSER
3565 g_object_set_data (G_OBJECT (impl->browse_shortcuts_tree_view), "fmq-name", "shortcuts");
3568 /* Connect "after" to key-press-event on the shortcuts pane. We want this action to be possible:
3570 * 1. user brings up a SAVE dialog
3571 * 2. user clicks on a shortcut in the shortcuts pane
3572 * 3. user starts typing a filename
3574 * Normally, the user's typing would be ignored, as the shortcuts treeview doesn't
3575 * support interactive search. However, we'd rather focus the location entry
3576 * so that the user can type *there*.
3578 * To preserve keyboard navigation in the shortcuts pane, we don't focus the
3579 * filename entry if one clicks on a shortcut; rather, we focus the entry only
3580 * if the user starts typing while the focus is in the shortcuts pane.
3582 g_signal_connect_after (impl->browse_shortcuts_tree_view, "key-press-event",
3583 G_CALLBACK (shortcuts_key_press_event_after_cb), impl);
3585 g_signal_connect (impl->browse_shortcuts_tree_view, "popup-menu",
3586 G_CALLBACK (shortcuts_popup_menu_cb), impl);
3587 g_signal_connect (impl->browse_shortcuts_tree_view, "button-press-event",
3588 G_CALLBACK (shortcuts_button_press_event_cb), impl);
3589 /* Accessible object name for the file chooser's shortcuts pane */
3590 atk_object_set_name (gtk_widget_get_accessible (impl->browse_shortcuts_tree_view), _("Places"));
3592 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), impl->shortcuts_pane_filter_model);
3594 gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3596 tree_model_row_targets,
3597 G_N_ELEMENTS (tree_model_row_targets),
3600 gtk_drag_dest_set (impl->browse_shortcuts_tree_view,
3601 GTK_DEST_DEFAULT_ALL,
3602 tree_model_row_targets,
3603 G_N_ELEMENTS (tree_model_row_targets),
3604 GDK_ACTION_COPY | GDK_ACTION_MOVE);
3605 gtk_drag_dest_add_uri_targets (impl->browse_shortcuts_tree_view);
3607 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
3608 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
3609 gtk_tree_selection_set_select_function (selection,
3610 shortcuts_select_func,
3613 g_signal_connect (selection, "changed",
3614 G_CALLBACK (shortcuts_selection_changed_cb), impl);
3616 g_signal_connect (impl->browse_shortcuts_tree_view, "key-press-event",
3617 G_CALLBACK (shortcuts_key_press_event_cb), impl);
3619 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-begin",
3620 G_CALLBACK (shortcuts_drag_begin_cb), impl);
3621 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-end",
3622 G_CALLBACK (shortcuts_drag_end_cb), impl);
3623 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-delete",
3624 G_CALLBACK (shortcuts_drag_data_delete_cb), impl);
3626 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-leave",
3627 G_CALLBACK (shortcuts_drag_leave_cb), impl);
3628 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-motion",
3629 G_CALLBACK (shortcuts_drag_motion_cb), impl);
3630 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-drop",
3631 G_CALLBACK (shortcuts_drag_drop_cb), impl);
3632 g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-received",
3633 G_CALLBACK (shortcuts_drag_data_received_cb), impl);
3635 /* Support tooltips */
3636 gtk_widget_set_has_tooltip (impl->browse_shortcuts_tree_view, TRUE);
3637 g_signal_connect (impl->browse_shortcuts_tree_view, "query-tooltip",
3638 G_CALLBACK (shortcuts_query_tooltip_cb), impl);
3640 gtk_container_add (GTK_CONTAINER (swin), impl->browse_shortcuts_tree_view);
3641 gtk_widget_show (impl->browse_shortcuts_tree_view);
3645 column = gtk_tree_view_column_new ();
3646 /* Column header for the file chooser's shortcuts pane */
3647 gtk_tree_view_column_set_title (column, _("_Places"));
3649 renderer = gtk_cell_renderer_pixbuf_new ();
3650 gtk_tree_view_column_pack_start (column, renderer, FALSE);
3651 gtk_tree_view_column_set_attributes (column, renderer,
3652 "pixbuf", SHORTCUTS_COL_PIXBUF,
3653 "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
3656 renderer = gtk_cell_renderer_text_new ();
3657 g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
3658 g_signal_connect (renderer, "edited",
3659 G_CALLBACK (shortcuts_edited), impl);
3660 g_signal_connect (renderer, "editing-canceled",
3661 G_CALLBACK (shortcuts_editing_canceled), impl);
3662 gtk_tree_view_column_pack_start (column, renderer, TRUE);
3663 gtk_tree_view_column_set_attributes (column, renderer,
3664 "text", SHORTCUTS_COL_NAME,
3667 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
3668 shortcuts_row_separator_func,
3671 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), column);
3676 /* Creates the widgets for the shortcuts/bookmarks pane */
3678 shortcuts_pane_create (GtkFileChooserDefault *impl,
3679 GtkSizeGroup *size_group)
3685 vbox = gtk_vbox_new (FALSE, 6);
3686 gtk_widget_show (vbox);
3688 /* Shortcuts tree */
3690 widget = shortcuts_list_create (impl);
3691 gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
3693 /* Box for buttons */
3695 hbox = gtk_hbox_new (TRUE, 6);
3696 gtk_size_group_add_widget (size_group, hbox);
3697 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
3698 gtk_widget_show (hbox);
3700 /* Add bookmark button */
3702 impl->browse_shortcuts_add_button = button_new (impl,
3707 G_CALLBACK (add_bookmark_button_clicked_cb));
3708 gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_add_button, TRUE, TRUE, 0);
3709 gtk_widget_set_tooltip_text (impl->browse_shortcuts_add_button,
3710 _("Add the selected folder to the Bookmarks"));
3712 /* Remove bookmark button */
3714 impl->browse_shortcuts_remove_button = button_new (impl,
3719 G_CALLBACK (remove_bookmark_button_clicked_cb));
3720 gtk_box_pack_start (GTK_BOX (hbox), impl->browse_shortcuts_remove_button, TRUE, TRUE, 0);
3721 gtk_widget_set_tooltip_text (impl->browse_shortcuts_remove_button,
3722 _("Remove the selected bookmark"));
3728 key_is_left_or_right (GdkEventKey *event)
3732 modifiers = gtk_accelerator_get_default_mod_mask ();
3734 return ((event->keyval == GDK_Right
3735 || event->keyval == GDK_KP_Right
3736 || event->keyval == GDK_Left
3737 || event->keyval == GDK_KP_Left)
3738 && (event->state & modifiers) == 0);
3741 /* Handles key press events on the file list, so that we can trap Enter to
3742 * activate the default button on our own. Also, checks to see if '/' has been
3743 * pressed. See comment by tree_view_keybinding_cb() for more details.
3746 browse_files_key_press_event_cb (GtkWidget *widget,
3750 GtkFileChooserDefault *impl;
3753 impl = (GtkFileChooserDefault *) data;
3755 modifiers = gtk_accelerator_get_default_mod_mask ();
3757 if ((event->keyval == GDK_slash
3758 || event->keyval == GDK_KP_Divide
3760 || event->keyval == GDK_asciitilde
3762 ) && ! (event->state & (~GDK_SHIFT_MASK & modifiers)))
3764 location_popup_handler (impl, event->string);
3768 if (key_is_left_or_right (event))
3770 gtk_widget_grab_focus (impl->browse_shortcuts_tree_view);
3774 if ((event->keyval == GDK_Return
3775 || event->keyval == GDK_ISO_Enter
3776 || event->keyval == GDK_KP_Enter
3777 || event->keyval == GDK_space
3778 || event->keyval == GDK_KP_Space)
3779 && ((event->state & modifiers) == 0)
3780 && !(impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
3781 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
3785 window = get_toplevel (widget);
3787 && widget != window->default_widget
3788 && !(widget == window->focus_widget &&
3789 (!window->default_widget || !gtk_widget_get_sensitive (window->default_widget))))
3791 gtk_window_activate_default (window);
3799 /* Callback used when the file list's popup menu is detached */
3801 popup_menu_detach_cb (GtkWidget *attach_widget,
3804 GtkFileChooserDefault *impl;
3806 impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
3807 g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
3809 impl->browse_files_popup_menu = NULL;
3810 impl->browse_files_popup_menu_add_shortcut_item = NULL;
3811 impl->browse_files_popup_menu_hidden_files_item = NULL;
3814 /* Callback used when the "Add to Bookmarks" menu item is activated */
3816 add_to_shortcuts_cb (GtkMenuItem *item,
3817 GtkFileChooserDefault *impl)
3819 bookmarks_add_selected_folder (impl);
3822 /* Callback used when the "Show Hidden Files" menu item is toggled */
3824 show_hidden_toggled_cb (GtkCheckMenuItem *item,
3825 GtkFileChooserDefault *impl)
3828 "show-hidden", gtk_check_menu_item_get_active (item),
3832 /* Callback used when the "Show Size Column" menu item is toggled */
3834 show_size_column_toggled_cb (GtkCheckMenuItem *item,
3835 GtkFileChooserDefault *impl)
3837 impl->show_size_column = gtk_check_menu_item_get_active (item);
3839 gtk_tree_view_column_set_visible (impl->list_size_column,
3840 impl->show_size_column);
3843 /* Shows an error dialog about not being able to select a dragged file */
3845 error_selecting_dragged_file_dialog (GtkFileChooserDefault *impl,
3850 _("Could not select file"),
3855 file_list_drag_data_select_uris (GtkFileChooserDefault *impl,
3860 GtkFileChooser *chooser = GTK_FILE_CHOOSER (impl);
3862 for (i = 1; uris[i]; i++)
3865 GError *error = NULL;
3868 file = g_file_new_for_uri (uri);
3870 gtk_file_chooser_default_select_file (chooser, file, &error);
3872 error_selecting_dragged_file_dialog (impl, file, error);
3874 g_object_unref (file);
3878 struct FileListDragData
3880 GtkFileChooserDefault *impl;
3886 file_list_drag_data_received_get_info_cb (GCancellable *cancellable,
3888 const GError *error,
3891 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
3892 struct FileListDragData *data = user_data;
3893 GtkFileChooser *chooser = GTK_FILE_CHOOSER (data->impl);
3895 if (cancellable != data->impl->file_list_drag_data_received_cancellable)
3898 data->impl->file_list_drag_data_received_cancellable = NULL;
3900 if (cancelled || error)
3903 if ((data->impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
3904 data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) &&
3905 data->uris[1] == 0 && !error && _gtk_file_info_consider_as_directory (info))
3906 change_folder_and_display_error (data->impl, data->file, FALSE);
3909 GError *error = NULL;
3911 gtk_file_chooser_default_unselect_all (chooser);
3912 gtk_file_chooser_default_select_file (chooser, data->file, &error);
3914 error_selecting_dragged_file_dialog (data->impl, data->file, error);
3916 browse_files_center_selected_row (data->impl);
3919 if (data->impl->select_multiple)
3920 file_list_drag_data_select_uris (data->impl, data->uris);
3923 g_object_unref (data->impl);
3924 g_strfreev (data->uris);
3925 g_object_unref (data->file);
3928 g_object_unref (cancellable);
3932 file_list_drag_data_received_cb (GtkWidget *widget,
3933 GdkDragContext *context,
3936 GtkSelectionData *selection_data,
3941 GtkFileChooserDefault *impl;
3942 GtkFileChooser *chooser;
3947 impl = GTK_FILE_CHOOSER_DEFAULT (data);
3948 chooser = GTK_FILE_CHOOSER (data);
3950 /* Allow only drags from other widgets; see bug #533891. */
3951 if (gtk_drag_get_source_widget (context) == widget)
3953 g_signal_stop_emission_by_name (widget, "drag-data-received");
3957 /* Parse the text/uri-list string, navigate to the first one */
3958 uris = gtk_selection_data_get_uris (selection_data);
3959 if (uris && uris[0])
3961 struct FileListDragData *data;
3964 file = g_file_new_for_uri (uri);
3966 data = g_new0 (struct FileListDragData, 1);
3967 data->impl = g_object_ref (impl);
3971 if (impl->file_list_drag_data_received_cancellable)
3972 g_cancellable_cancel (impl->file_list_drag_data_received_cancellable);
3974 impl->file_list_drag_data_received_cancellable =
3975 _gtk_file_system_get_info (impl->file_system, file,
3977 file_list_drag_data_received_get_info_cb,
3981 g_signal_stop_emission_by_name (widget, "drag-data-received");
3984 /* Don't do anything with the drag_drop signal */
3986 file_list_drag_drop_cb (GtkWidget *widget,
3987 GdkDragContext *context,
3991 GtkFileChooserDefault *impl)
3993 g_signal_stop_emission_by_name (widget, "drag-drop");
3997 /* Disable the normal tree drag motion handler, it makes it look like you're
3998 dropping the dragged item onto a tree item */
4000 file_list_drag_motion_cb (GtkWidget *widget,
4001 GdkDragContext *context,
4005 GtkFileChooserDefault *impl)
4007 g_signal_stop_emission_by_name (widget, "drag-motion");
4011 /* Constructs the popup menu for the file list if needed */
4013 file_list_build_popup_menu (GtkFileChooserDefault *impl)
4017 if (impl->browse_files_popup_menu)
4020 impl->browse_files_popup_menu = gtk_menu_new ();
4021 gtk_menu_attach_to_widget (GTK_MENU (impl->browse_files_popup_menu),
4022 impl->browse_files_tree_view,
4023 popup_menu_detach_cb);
4025 item = gtk_image_menu_item_new_with_mnemonic (_("_Add to Bookmarks"));
4026 impl->browse_files_popup_menu_add_shortcut_item = item;
4027 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
4028 gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU));
4029 g_signal_connect (item, "activate",
4030 G_CALLBACK (add_to_shortcuts_cb), impl);
4031 gtk_widget_show (item);
4032 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4034 item = gtk_separator_menu_item_new ();
4035 gtk_widget_show (item);
4036 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4038 item = gtk_check_menu_item_new_with_mnemonic (_("Show _Hidden Files"));
4039 impl->browse_files_popup_menu_hidden_files_item = item;
4040 g_signal_connect (item, "toggled",
4041 G_CALLBACK (show_hidden_toggled_cb), impl);
4042 gtk_widget_show (item);
4043 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4045 item = gtk_check_menu_item_new_with_mnemonic (_("Show _Size Column"));
4046 impl->browse_files_popup_menu_size_column_item = item;
4047 g_signal_connect (item, "toggled",
4048 G_CALLBACK (show_size_column_toggled_cb), impl);
4049 gtk_widget_show (item);
4050 gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
4052 bookmarks_check_add_sensitivity (impl);
4055 /* Updates the popup menu for the file list, creating it if necessary */
4057 file_list_update_popup_menu (GtkFileChooserDefault *impl)
4059 file_list_build_popup_menu (impl);
4061 /* FIXME - handle OPERATION_MODE_SEARCH and OPERATION_MODE_RECENT */
4063 /* The sensitivity of the Add to Bookmarks item is set in
4064 * bookmarks_check_add_sensitivity()
4067 /* 'Show Hidden Files' */
4068 g_signal_handlers_block_by_func (impl->browse_files_popup_menu_hidden_files_item,
4069 G_CALLBACK (show_hidden_toggled_cb), impl);
4070 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (impl->browse_files_popup_menu_hidden_files_item),
4072 g_signal_handlers_unblock_by_func (impl->browse_files_popup_menu_hidden_files_item,
4073 G_CALLBACK (show_hidden_toggled_cb), impl);
4075 /* 'Show Size Column' */
4076 g_signal_handlers_block_by_func (impl->browse_files_popup_menu_size_column_item,
4077 G_CALLBACK (show_size_column_toggled_cb), impl);
4078 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (impl->browse_files_popup_menu_size_column_item),
4079 impl->show_size_column);
4080 g_signal_handlers_unblock_by_func (impl->browse_files_popup_menu_size_column_item,
4081 G_CALLBACK (show_size_column_toggled_cb), impl);
4085 popup_position_func (GtkMenu *menu,
4091 GtkAllocation allocation;
4092 GtkWidget *widget = GTK_WIDGET (user_data);
4093 GdkScreen *screen = gtk_widget_get_screen (widget);
4096 GdkRectangle monitor;
4098 g_return_if_fail (gtk_widget_get_realized (widget));
4100 gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
4102 gtk_widget_size_request (GTK_WIDGET (menu), &req);
4104 gtk_widget_get_allocation (widget, &allocation);
4105 *x += (allocation.width - req.width) / 2;
4106 *y += (allocation.height - req.height) / 2;
4108 monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
4109 gtk_menu_set_monitor (menu, monitor_num);
4110 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
4112 *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
4113 *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
4119 file_list_popup_menu (GtkFileChooserDefault *impl,
4120 GdkEventButton *event)
4122 file_list_update_popup_menu (impl);
4124 gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
4125 NULL, NULL, NULL, NULL,
4126 event->button, event->time);
4129 gtk_menu_popup (GTK_MENU (impl->browse_files_popup_menu),
4131 popup_position_func, impl->browse_files_tree_view,
4132 0, GDK_CURRENT_TIME);
4133 gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->browse_files_popup_menu),
4139 /* Callback used for the GtkWidget::popup-menu signal of the file list */
4141 list_popup_menu_cb (GtkWidget *widget,
4142 GtkFileChooserDefault *impl)
4144 file_list_popup_menu (impl, NULL);
4148 /* Callback used when a button is pressed on the file list. We trap button 3 to
4149 * bring up a popup menu.
4152 list_button_press_event_cb (GtkWidget *widget,
4153 GdkEventButton *event,
4154 GtkFileChooserDefault *impl)
4156 static gboolean in_press = FALSE;
4162 if (event->button != 3)
4166 handled = gtk_widget_event (impl->browse_files_tree_view, (GdkEvent *) event);
4169 file_list_popup_menu (impl, event);
4174 OperationMode operation_mode;
4175 gint general_column;
4179 /* Sets the sort column IDs for the file list based on the operation mode */
4181 file_list_set_sort_column_ids (GtkFileChooserDefault *impl)
4183 gtk_tree_view_column_set_sort_column_id (impl->list_name_column, MODEL_COL_NAME);
4184 gtk_tree_view_column_set_sort_column_id (impl->list_mtime_column, MODEL_COL_MTIME);
4185 gtk_tree_view_column_set_sort_column_id (impl->list_size_column, MODEL_COL_SIZE);
4189 file_list_query_tooltip_cb (GtkWidget *widget,
4192 gboolean keyboard_tip,
4193 GtkTooltip *tooltip,
4196 GtkFileChooserDefault *impl = user_data;
4197 GtkTreeModel *model;
4203 if (impl->operation_mode == OPERATION_MODE_BROWSE)
4207 if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (impl->browse_files_tree_view),
4210 &model, &path, &iter))
4213 gtk_tree_model_get (model, &iter,
4214 MODEL_COL_FILE, &file,
4219 gtk_tree_path_free (path);
4223 filename = g_file_get_path (file);
4224 gtk_tooltip_set_text (tooltip, filename);
4225 gtk_tree_view_set_tooltip_row (GTK_TREE_VIEW (impl->browse_files_tree_view),
4230 g_object_unref (file);
4231 gtk_tree_path_free (path);
4237 set_icon_cell_renderer_fixed_size (GtkFileChooserDefault *impl, GtkCellRenderer *renderer)
4241 gtk_cell_renderer_get_padding (renderer, &xpad, &ypad);
4242 gtk_cell_renderer_set_fixed_size (renderer,
4243 xpad * 2 + impl->icon_size,
4244 ypad * 2 + impl->icon_size);
4247 /* Creates the widgets for the file list */
4249 create_file_list (GtkFileChooserDefault *impl)
4252 GtkTreeSelection *selection;
4253 GtkTreeViewColumn *column;
4254 GtkCellRenderer *renderer;
4256 /* Scrolled window */
4257 swin = gtk_scrolled_window_new (NULL, NULL);
4258 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
4259 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
4260 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
4263 /* Tree/list view */
4265 impl->browse_files_tree_view = gtk_tree_view_new ();
4266 #ifdef PROFILE_FILE_CHOOSER
4267 g_object_set_data (G_OBJECT (impl->browse_files_tree_view), "fmq-name", "file_list");
4269 g_object_set_data (G_OBJECT (impl->browse_files_tree_view), I_("GtkFileChooserDefault"), impl);
4270 atk_object_set_name (gtk_widget_get_accessible (impl->browse_files_tree_view), _("Files"));
4272 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->browse_files_tree_view), TRUE);
4273 gtk_container_add (GTK_CONTAINER (swin), impl->browse_files_tree_view);
4275 gtk_drag_dest_set (impl->browse_files_tree_view,
4276 GTK_DEST_DEFAULT_ALL,
4278 GDK_ACTION_COPY | GDK_ACTION_MOVE);
4279 gtk_drag_dest_add_uri_targets (impl->browse_files_tree_view);
4281 g_signal_connect (impl->browse_files_tree_view, "row-activated",
4282 G_CALLBACK (list_row_activated), impl);
4283 g_signal_connect (impl->browse_files_tree_view, "key-press-event",
4284 G_CALLBACK (browse_files_key_press_event_cb), impl);
4285 g_signal_connect (impl->browse_files_tree_view, "popup-menu",
4286 G_CALLBACK (list_popup_menu_cb), impl);
4287 g_signal_connect (impl->browse_files_tree_view, "button-press-event",
4288 G_CALLBACK (list_button_press_event_cb), impl);
4290 g_signal_connect (impl->browse_files_tree_view, "drag-data-received",
4291 G_CALLBACK (file_list_drag_data_received_cb), impl);
4292 g_signal_connect (impl->browse_files_tree_view, "drag-drop",
4293 G_CALLBACK (file_list_drag_drop_cb), impl);
4294 g_signal_connect (impl->browse_files_tree_view, "drag-motion",
4295 G_CALLBACK (file_list_drag_motion_cb), impl);
4297 g_object_set (impl->browse_files_tree_view, "has-tooltip", TRUE, NULL);
4298 g_signal_connect (impl->browse_files_tree_view, "query-tooltip",
4299 G_CALLBACK (file_list_query_tooltip_cb), impl);
4301 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
4302 gtk_tree_selection_set_select_function (selection,
4305 gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_files_tree_view),
4308 GDK_ACTION_COPY | GDK_ACTION_MOVE);
4309 gtk_drag_source_add_uri_targets (impl->browse_files_tree_view);
4311 g_signal_connect (selection, "changed",
4312 G_CALLBACK (list_selection_changed), impl);
4314 /* Keep the column order in sync with update_cell_renderer_attributes() */
4316 /* Filename column */
4318 impl->list_name_column = gtk_tree_view_column_new ();
4319 gtk_tree_view_column_set_expand (impl->list_name_column, TRUE);
4320 gtk_tree_view_column_set_resizable (impl->list_name_column, TRUE);
4321 gtk_tree_view_column_set_title (impl->list_name_column, _("Name"));
4323 renderer = gtk_cell_renderer_pixbuf_new ();
4324 /* We set a fixed size so that we get an empty slot even if no icons are loaded yet */
4325 set_icon_cell_renderer_fixed_size (impl, renderer);
4326 gtk_tree_view_column_pack_start (impl->list_name_column, renderer, FALSE);
4328 impl->list_name_renderer = gtk_cell_renderer_text_new ();
4329 g_object_set (impl->list_name_renderer,
4330 "ellipsize", PANGO_ELLIPSIZE_END,
4332 g_signal_connect (impl->list_name_renderer, "edited",
4333 G_CALLBACK (renderer_edited_cb), impl);
4334 g_signal_connect (impl->list_name_renderer, "editing-canceled",
4335 G_CALLBACK (renderer_editing_canceled_cb), impl);
4336 gtk_tree_view_column_pack_start (impl->list_name_column, impl->list_name_renderer, TRUE);
4338 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), impl->list_name_column);
4342 column = gtk_tree_view_column_new ();
4343 gtk_tree_view_column_set_resizable (column, TRUE);
4344 gtk_tree_view_column_set_title (column, _("Size"));
4346 renderer = gtk_cell_renderer_text_new ();
4347 g_object_set (renderer,
4348 "alignment", PANGO_ALIGN_RIGHT,
4350 gtk_tree_view_column_pack_start (column, renderer, TRUE); /* bug: it doesn't expand */
4351 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
4352 impl->list_size_column = column;
4354 /* Modification time column */
4356 column = gtk_tree_view_column_new ();
4357 gtk_tree_view_column_set_resizable (column, TRUE);
4358 gtk_tree_view_column_set_title (column, _("Modified"));
4360 renderer = gtk_cell_renderer_text_new ();
4361 gtk_tree_view_column_pack_start (column, renderer, TRUE);
4362 gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column);
4363 impl->list_mtime_column = column;
4365 file_list_set_sort_column_ids (impl);
4366 update_cell_renderer_attributes (impl);
4368 gtk_widget_show_all (swin);
4374 create_path_bar (GtkFileChooserDefault *impl)
4376 GtkWidget *path_bar;
4378 path_bar = g_object_new (GTK_TYPE_PATH_BAR, NULL);
4379 _gtk_path_bar_set_file_system (GTK_PATH_BAR (path_bar), impl->file_system);
4384 /* Creates the widgets for the files/folders pane */
4386 file_pane_create (GtkFileChooserDefault *impl,
4387 GtkSizeGroup *size_group)
4393 vbox = gtk_vbox_new (FALSE, 6);
4394 gtk_widget_show (vbox);
4396 /* Box for lists and preview */
4398 hbox = gtk_hbox_new (FALSE, PREVIEW_HBOX_SPACING);
4399 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
4400 gtk_widget_show (hbox);
4404 widget = create_file_list (impl);
4405 gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
4409 impl->preview_box = gtk_vbox_new (FALSE, 12);
4410 gtk_box_pack_start (GTK_BOX (hbox), impl->preview_box, FALSE, FALSE, 0);
4411 /* Don't show preview box initially */
4415 impl->filter_combo_hbox = gtk_hbox_new (FALSE, 12);
4417 widget = filter_create (impl);
4419 gtk_widget_show (widget);
4420 gtk_box_pack_end (GTK_BOX (impl->filter_combo_hbox), widget, FALSE, FALSE, 0);
4422 gtk_size_group_add_widget (size_group, impl->filter_combo_hbox);
4423 gtk_box_pack_end (GTK_BOX (vbox), impl->filter_combo_hbox, FALSE, FALSE, 0);
4428 /* Callback used when the "Browse for more folders" expander is toggled */
4430 expander_changed_cb (GtkExpander *expander,
4432 GtkFileChooserDefault *impl)
4434 impl->expand_folders = gtk_expander_get_expanded(GTK_EXPANDER (impl->save_expander));
4435 update_appearance (impl);
4438 /* Callback used when the selection changes in the save folder combo box */
4440 save_folder_combo_changed_cb (GtkComboBox *combo,
4441 GtkFileChooserDefault *impl)
4445 if (impl->changing_folder)
4448 if (gtk_combo_box_get_active_iter (combo, &iter))
4450 GtkTreeIter child_iter;
4452 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model),
4455 shortcuts_activate_iter (impl, &child_iter);
4460 save_folder_update_tooltip (GtkComboBox *combo,
4461 GtkFileChooserDefault *impl)
4468 if (gtk_combo_box_get_active_iter (combo, &iter))
4470 GtkTreeIter child_iter;
4472 ShortcutType shortcut_type;
4474 gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model),
4477 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &child_iter,
4478 SHORTCUTS_COL_DATA, &col_data,
4479 SHORTCUTS_COL_TYPE, &shortcut_type,
4482 if (shortcut_type == SHORTCUT_TYPE_FILE)
4483 tooltip = g_file_get_parse_name (G_FILE (col_data));
4486 gtk_widget_set_tooltip_text (GTK_WIDGET (combo), tooltip);
4487 gtk_widget_set_has_tooltip (GTK_WIDGET (combo),
4488 gtk_widget_get_sensitive (GTK_WIDGET (combo)));
4492 /* Filter function used to filter out the Search item and its separator.
4493 * Used for the "Save in folder" combo box, so that these items do not appear in it.
4496 shortcuts_combo_filter_func (GtkTreeModel *model,
4500 GtkFileChooserDefault *impl;
4501 GtkTreePath *tree_path;
4506 impl = GTK_FILE_CHOOSER_DEFAULT (data);
4508 g_assert (model == GTK_TREE_MODEL (impl->shortcuts_model));
4510 tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), iter);
4511 g_assert (tree_path != NULL);
4513 indices = gtk_tree_path_get_indices (tree_path);
4517 if (impl->has_search)
4519 idx = shortcuts_get_index (impl, SHORTCUTS_SEARCH);
4520 if (idx == indices[0])
4524 if (impl->has_recent)
4526 idx = shortcuts_get_index (impl, SHORTCUTS_RECENT);
4527 if (idx == indices[0])
4531 idx = shortcuts_get_index (impl, SHORTCUTS_RECENT_SEPARATOR);
4532 if (idx == indices[0])
4537 gtk_tree_path_free (tree_path);
4542 /* Creates the combo box with the save folders */
4544 save_folder_combo_create (GtkFileChooserDefault *impl)
4547 GtkCellRenderer *cell;
4549 impl->shortcuts_combo_filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (impl->shortcuts_model), NULL);
4550 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model),
4551 shortcuts_combo_filter_func,
4555 combo = g_object_new (GTK_TYPE_COMBO_BOX,
4556 "model", impl->shortcuts_combo_filter_model,
4557 "focus-on-click", FALSE,
4559 gtk_widget_show (combo);
4561 cell = gtk_cell_renderer_pixbuf_new ();
4562 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE);
4563 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
4564 "pixbuf", SHORTCUTS_COL_PIXBUF,
4565 "visible", SHORTCUTS_COL_PIXBUF_VISIBLE,
4566 "sensitive", SHORTCUTS_COL_PIXBUF_VISIBLE,
4569 cell = gtk_cell_renderer_text_new ();
4570 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
4571 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
4572 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
4573 "text", SHORTCUTS_COL_NAME,
4574 "sensitive", SHORTCUTS_COL_PIXBUF_VISIBLE,
4577 gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo),
4578 shortcuts_row_separator_func,
4581 g_signal_connect (combo, "changed",
4582 G_CALLBACK (save_folder_combo_changed_cb), impl);
4583 g_signal_connect (combo, "changed",
4584 G_CALLBACK (save_folder_update_tooltip), impl);
4589 /* Creates the widgets specific to Save mode */
4591 save_widgets_create (GtkFileChooserDefault *impl)
4596 GtkWidget *alignment;
4598 if (impl->save_widgets != NULL)
4601 location_switch_to_path_bar (impl);
4603 vbox = gtk_vbox_new (FALSE, 12);
4605 table = gtk_table_new (2, 2, FALSE);
4606 gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
4607 gtk_widget_show (table);
4608 gtk_table_set_row_spacings (GTK_TABLE (table), 12);
4609 gtk_table_set_col_spacings (GTK_TABLE (table), 12);
4613 widget = gtk_label_new_with_mnemonic (_("_Name:"));
4614 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
4615 gtk_table_attach (GTK_TABLE (table), widget,
4619 gtk_widget_show (widget);
4621 /* Location entry */
4623 impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
4624 _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
4626 _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->local_only);
4627 gtk_entry_set_width_chars (GTK_ENTRY (impl->location_entry), 45);
4628 gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
4629 gtk_table_attach (GTK_TABLE (table), impl->location_entry,
4631 GTK_EXPAND | GTK_FILL, 0,
4633 gtk_widget_show (impl->location_entry);
4634 gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->location_entry);
4637 impl->save_folder_label = gtk_label_new (NULL);
4638 gtk_misc_set_alignment (GTK_MISC (impl->save_folder_label), 0.0, 0.5);
4639 gtk_table_attach (GTK_TABLE (table), impl->save_folder_label,
4643 gtk_widget_show (impl->save_folder_label);
4645 impl->save_folder_combo = save_folder_combo_create (impl);
4646 gtk_table_attach (GTK_TABLE (table), impl->save_folder_combo,
4648 GTK_EXPAND | GTK_FILL, GTK_FILL,
4650 gtk_label_set_mnemonic_widget (GTK_LABEL (impl->save_folder_label), impl->save_folder_combo);
4653 alignment = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
4654 gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
4656 impl->save_expander = gtk_expander_new_with_mnemonic (_("_Browse for other folders"));
4657 gtk_container_add (GTK_CONTAINER (alignment), impl->save_expander);
4658 g_signal_connect (impl->save_expander, "notify::expanded",
4659 G_CALLBACK (expander_changed_cb),
4661 gtk_widget_show_all (alignment);
4663 impl->save_widgets = vbox;
4664 gtk_box_pack_start (GTK_BOX (impl), impl->save_widgets, FALSE, FALSE, 0);
4665 gtk_box_reorder_child (GTK_BOX (impl), impl->save_widgets, 0);
4666 gtk_widget_show (impl->save_widgets);
4669 /* Destroys the widgets specific to Save mode */
4671 save_widgets_destroy (GtkFileChooserDefault *impl)
4673 if (impl->save_widgets == NULL)
4676 gtk_widget_destroy (impl->save_widgets);
4677 impl->save_widgets = NULL;
4678 impl->location_entry = NULL;
4679 impl->save_folder_label = NULL;
4680 impl->save_folder_combo = NULL;
4681 impl->save_expander = NULL;
4684 /* Turns on the path bar widget. Can be called even if we are already in that
4688 location_switch_to_path_bar (GtkFileChooserDefault *impl)
4690 if (impl->location_entry)
4692 gtk_widget_destroy (impl->location_entry);
4693 impl->location_entry = NULL;
4696 gtk_widget_hide (impl->location_entry_box);
4699 /* Sets the full path of the current folder as the text in the location entry. */
4701 location_entry_set_initial_text (GtkFileChooserDefault *impl)
4703 gchar *text, *filename;
4705 if (!impl->current_folder)
4708 filename = g_file_get_path (impl->current_folder);
4712 text = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
4716 text = g_file_get_uri (impl->current_folder);
4720 gboolean need_slash;
4723 len = strlen (text);
4724 need_slash = (text[len - 1] != G_DIR_SEPARATOR);
4730 slash_text = g_new (char, len + 2);
4731 strcpy (slash_text, text);
4732 slash_text[len] = G_DIR_SEPARATOR;
4733 slash_text[len + 1] = 0;
4739 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), text);
4746 /* Turns on the location entry. Can be called even if we are already in that
4750 location_switch_to_filename_entry (GtkFileChooserDefault *impl)
4752 /* when in search or recent files mode, we are not showing the
4753 * location_entry_box container, so there's no point in switching
4756 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
4757 impl->operation_mode == OPERATION_MODE_RECENT)
4760 if (impl->location_entry)
4761 gtk_widget_destroy (impl->location_entry);
4765 gtk_widget_show (impl->location_entry_box);
4769 impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
4770 _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
4772 gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
4773 _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action);
4775 gtk_box_pack_start (GTK_BOX (impl->location_entry_box), impl->location_entry, TRUE, TRUE, 0);
4776 gtk_label_set_mnemonic_widget (GTK_LABEL (impl->location_label), impl->location_entry);
4778 /* Configure the entry */
4780 _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->current_folder);
4784 gtk_widget_show (impl->location_entry);
4785 gtk_widget_grab_focus (impl->location_entry);
4788 /* Sets a new location mode. set_buttons determines whether the toggle button
4789 * for the mode will also be changed.
4792 location_mode_set (GtkFileChooserDefault *impl,
4793 LocationMode new_mode,
4794 gboolean set_button)
4796 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
4797 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
4799 GtkWindow *toplevel;
4800 GtkWidget *current_focus;
4801 gboolean button_active;
4802 gboolean switch_to_file_list;
4806 case LOCATION_MODE_PATH_BAR:
4807 button_active = FALSE;
4809 /* The location_entry will disappear when we switch to path bar mode. So,
4810 * we'll focus the file list in that case, to avoid having a window with
4811 * no focused widget.
4813 toplevel = get_toplevel (GTK_WIDGET (impl));
4814 switch_to_file_list = FALSE;
4817 current_focus = gtk_window_get_focus (toplevel);
4818 if (!current_focus || current_focus == impl->location_entry)
4819 switch_to_file_list = TRUE;
4822 location_switch_to_path_bar (impl);
4824 if (switch_to_file_list)
4825 gtk_widget_grab_focus (impl->browse_files_tree_view);
4829 case LOCATION_MODE_FILENAME_ENTRY:
4830 button_active = TRUE;
4831 location_switch_to_filename_entry (impl);
4835 g_assert_not_reached ();
4841 g_signal_handlers_block_by_func (impl->location_button,
4842 G_CALLBACK (location_button_toggled_cb), impl);
4844 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (impl->location_button), button_active);
4846 g_signal_handlers_unblock_by_func (impl->location_button,
4847 G_CALLBACK (location_button_toggled_cb), impl);
4851 impl->location_mode = new_mode;
4855 location_toggle_popup_handler (GtkFileChooserDefault *impl)
4857 /* when in search or recent files mode, we are not showing the
4858 * location_entry_box container, so there's no point in switching
4861 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
4862 impl->operation_mode == OPERATION_MODE_RECENT)
4865 /* If the file entry is not visible, show it.
4866 * If it is visible, turn it off only if it is focused. Otherwise, switch to the entry.
4868 if (impl->location_mode == LOCATION_MODE_PATH_BAR)
4870 location_mode_set (impl, LOCATION_MODE_FILENAME_ENTRY, TRUE);
4872 else if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
4874 if (gtk_widget_has_focus (impl->location_entry))
4876 location_mode_set (impl, LOCATION_MODE_PATH_BAR, TRUE);
4880 gtk_widget_grab_focus (impl->location_entry);
4885 /* Callback used when one of the location mode buttons is toggled */
4887 location_button_toggled_cb (GtkToggleButton *toggle,
4888 GtkFileChooserDefault *impl)
4891 LocationMode new_mode;
4893 is_active = gtk_toggle_button_get_active (toggle);
4897 g_assert (impl->location_mode == LOCATION_MODE_PATH_BAR);
4898 new_mode = LOCATION_MODE_FILENAME_ENTRY;
4902 g_assert (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY);
4903 new_mode = LOCATION_MODE_PATH_BAR;
4906 location_mode_set (impl, new_mode, FALSE);
4909 /* Creates a toggle button for the location entry. */
4911 location_button_create (GtkFileChooserDefault *impl)
4916 image = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON);
4917 gtk_widget_show (image);
4919 impl->location_button = g_object_new (GTK_TYPE_TOGGLE_BUTTON,
4923 gtk_size_group_add_widget (impl->browse_path_bar_size_group, impl->location_button);
4925 g_signal_connect (impl->location_button, "toggled",
4926 G_CALLBACK (location_button_toggled_cb), impl);
4928 str = _("Type a file name");
4930 gtk_widget_set_tooltip_text (impl->location_button, str);
4931 atk_object_set_name (gtk_widget_get_accessible (impl->location_button), str);
4934 /* Creates the main hpaned with the widgets shared by Open and Save mode */
4936 browse_widgets_create (GtkFileChooserDefault *impl)
4941 GtkSizeGroup *size_group;
4943 /* size group is used by the [+][-] buttons and the filter combo */
4944 size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
4945 vbox = gtk_vbox_new (FALSE, 12);
4947 /* Location widgets */
4948 impl->browse_path_bar_hbox = gtk_hbox_new (FALSE, 12);
4949 gtk_box_pack_start (GTK_BOX (vbox), impl->browse_path_bar_hbox, FALSE, FALSE, 0);
4950 gtk_widget_show (impl->browse_path_bar_hbox);
4952 /* Size group that allows the path bar to be the same size between modes */
4953 impl->browse_path_bar_size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
4954 gtk_size_group_set_ignore_hidden (impl->browse_path_bar_size_group, FALSE);
4956 /* Location button */
4958 location_button_create (impl);
4959 gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->location_button, FALSE, FALSE, 0);
4963 impl->browse_path_bar = create_path_bar (impl);
4964 g_signal_connect (impl->browse_path_bar, "path-clicked", G_CALLBACK (path_bar_clicked), impl);
4965 gtk_widget_show_all (impl->browse_path_bar);
4966 gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->browse_path_bar, TRUE, TRUE, 0);
4969 impl->browse_new_folder_button = gtk_button_new_with_mnemonic (_("Create Fo_lder"));
4970 g_signal_connect (impl->browse_new_folder_button, "clicked",
4971 G_CALLBACK (new_folder_button_clicked), impl);
4972 gtk_box_pack_end (GTK_BOX (impl->browse_path_bar_hbox), impl->browse_new_folder_button, FALSE, FALSE, 0);
4974 /* Box for the location label and entry */
4976 impl->location_entry_box = gtk_hbox_new (FALSE, 12);
4977 gtk_box_pack_start (GTK_BOX (vbox), impl->location_entry_box, FALSE, FALSE, 0);
4979 impl->location_label = gtk_label_new_with_mnemonic (_("_Location:"));
4980 gtk_widget_show (impl->location_label);
4981 gtk_box_pack_start (GTK_BOX (impl->location_entry_box), impl->location_label, FALSE, FALSE, 0);
4984 hpaned = gtk_hpaned_new ();
4985 gtk_widget_show (hpaned);
4986 gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
4988 widget = shortcuts_pane_create (impl, size_group);
4989 gtk_paned_pack1 (GTK_PANED (hpaned), widget, FALSE, FALSE);
4990 widget = file_pane_create (impl, size_group);
4991 gtk_paned_pack2 (GTK_PANED (hpaned), widget, TRUE, FALSE);
4993 g_object_unref (size_group);
4999 gtk_file_chooser_default_constructor (GType type,
5000 guint n_construct_properties,
5001 GObjectConstructParam *construct_params)
5003 GtkFileChooserDefault *impl;
5006 profile_start ("start", NULL);
5008 object = G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->constructor (type,
5009 n_construct_properties,
5011 impl = GTK_FILE_CHOOSER_DEFAULT (object);
5013 g_assert (impl->file_system);
5015 gtk_widget_push_composite_child ();
5017 /* Shortcuts model */
5018 shortcuts_model_create (impl);
5020 /* The browse widgets */
5021 impl->browse_widgets = browse_widgets_create (impl);
5022 gtk_box_pack_start (GTK_BOX (impl), impl->browse_widgets, TRUE, TRUE, 0);
5024 /* Alignment to hold extra widget */
5025 impl->extra_align = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
5026 gtk_box_pack_start (GTK_BOX (impl), impl->extra_align, FALSE, FALSE, 0);
5028 gtk_widget_pop_composite_child ();
5029 update_appearance (impl);
5031 profile_end ("end", NULL);
5036 /* Sets the extra_widget by packing it in the appropriate place */
5038 set_extra_widget (GtkFileChooserDefault *impl,
5039 GtkWidget *extra_widget)
5043 g_object_ref (extra_widget);
5044 /* FIXME: is this right ? */
5045 gtk_widget_show (extra_widget);
5048 if (impl->extra_widget)
5050 gtk_container_remove (GTK_CONTAINER (impl->extra_align), impl->extra_widget);
5051 g_object_unref (impl->extra_widget);
5054 impl->extra_widget = extra_widget;
5055 if (impl->extra_widget)
5057 gtk_container_add (GTK_CONTAINER (impl->extra_align), impl->extra_widget);
5058 gtk_widget_show (impl->extra_align);
5061 gtk_widget_hide (impl->extra_align);
5065 set_local_only (GtkFileChooserDefault *impl,
5066 gboolean local_only)
5068 if (local_only != impl->local_only)
5070 impl->local_only = local_only;
5072 if (impl->location_entry)
5073 _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), local_only);
5075 if (impl->shortcuts_model && impl->file_system)
5077 shortcuts_add_volumes (impl);
5078 shortcuts_add_bookmarks (impl);
5081 if (local_only && impl->current_folder &&
5082 !g_file_is_native (impl->current_folder))
5084 /* If we are pointing to a non-local folder, make an effort to change
5085 * back to a local folder, but it's really up to the app to not cause
5086 * such a situation, so we ignore errors.
5088 const gchar *home = g_get_home_dir ();
5094 home_file = g_file_new_for_path (home);
5096 gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (impl), home_file, NULL);
5098 g_object_unref (home_file);
5104 volumes_bookmarks_changed_cb (GtkFileSystem *file_system,
5105 GtkFileChooserDefault *impl)
5107 shortcuts_add_volumes (impl);
5108 shortcuts_add_bookmarks (impl);
5110 bookmarks_check_add_sensitivity (impl);
5111 bookmarks_check_remove_sensitivity (impl);
5112 shortcuts_check_popup_sensitivity (impl);
5115 /* Sets the file chooser to multiple selection mode */
5117 set_select_multiple (GtkFileChooserDefault *impl,
5118 gboolean select_multiple,
5119 gboolean property_notify)
5121 GtkTreeSelection *selection;
5122 GtkSelectionMode mode;
5124 if (select_multiple == impl->select_multiple)
5127 mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE;
5129 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
5130 gtk_tree_selection_set_mode (selection, mode);
5132 gtk_tree_view_set_rubber_banding (GTK_TREE_VIEW (impl->browse_files_tree_view), select_multiple);
5134 impl->select_multiple = select_multiple;
5135 g_object_notify (G_OBJECT (impl), "select-multiple");
5137 check_preview_change (impl);
5141 set_file_system_backend (GtkFileChooserDefault *impl)
5143 profile_start ("start for backend", "default");
5145 impl->file_system = _gtk_file_system_new ();
5147 g_signal_connect (impl->file_system, "volumes-changed",
5148 G_CALLBACK (volumes_bookmarks_changed_cb), impl);
5149 g_signal_connect (impl->file_system, "bookmarks-changed",
5150 G_CALLBACK (volumes_bookmarks_changed_cb), impl);
5152 profile_end ("end", NULL);
5156 unset_file_system_backend (GtkFileChooserDefault *impl)
5158 g_signal_handlers_disconnect_by_func (impl->file_system,
5159 G_CALLBACK (volumes_bookmarks_changed_cb), impl);
5161 g_object_unref (impl->file_system);
5163 impl->file_system = NULL;
5166 /* This function is basically a do_all function.
5168 * It sets the visibility on all the widgets based on the current state, and
5169 * moves the custom_widget if needed.
5172 update_appearance (GtkFileChooserDefault *impl)
5174 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
5175 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5179 gtk_widget_hide (impl->location_button);
5180 save_widgets_create (impl);
5182 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
5183 text = _("Save in _folder:");
5185 text = _("Create in _folder:");
5187 gtk_label_set_text_with_mnemonic (GTK_LABEL (impl->save_folder_label), text);
5189 if (gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
5191 gtk_widget_set_sensitive (impl->save_folder_label, FALSE);
5192 gtk_widget_set_sensitive (impl->save_folder_combo, FALSE);
5193 gtk_widget_set_has_tooltip (impl->save_folder_combo, FALSE);
5194 gtk_widget_show (impl->browse_widgets);
5198 gtk_widget_set_sensitive (impl->save_folder_label, TRUE);
5199 gtk_widget_set_sensitive (impl->save_folder_combo, TRUE);
5200 gtk_widget_set_has_tooltip (impl->save_folder_combo, TRUE);
5201 gtk_widget_hide (impl->browse_widgets);
5204 if (impl->select_multiple)
5206 g_warning ("Save mode cannot be set in conjunction with multiple selection mode. "
5207 "Re-setting to single selection mode.");
5208 set_select_multiple (impl, FALSE, TRUE);
5211 else if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
5212 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
5214 gtk_widget_show (impl->location_button);
5215 save_widgets_destroy (impl);
5216 gtk_widget_show (impl->browse_widgets);
5217 location_mode_set (impl, impl->location_mode, TRUE);
5220 if (impl->location_entry)
5221 _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action);
5223 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN || !impl->create_folders)
5224 gtk_widget_hide (impl->browse_new_folder_button);
5226 gtk_widget_show (impl->browse_new_folder_button);
5228 /* This *is* needed; we need to redraw the file list because the "sensitivity"
5229 * of files may change depending whether we are in a file or folder-only mode.
5231 gtk_widget_queue_draw (impl->browse_files_tree_view);
5233 emit_default_size_changed (impl);
5237 gtk_file_chooser_default_set_property (GObject *object,
5239 const GValue *value,
5243 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
5247 case GTK_FILE_CHOOSER_PROP_ACTION:
5249 GtkFileChooserAction action = g_value_get_enum (value);
5251 if (action != impl->action)
5253 gtk_file_chooser_default_unselect_all (GTK_FILE_CHOOSER (impl));
5255 if ((action == GTK_FILE_CHOOSER_ACTION_SAVE ||
5256 action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5257 && impl->select_multiple)
5259 g_warning ("Tried to change the file chooser action to SAVE or CREATE_FOLDER, but "
5260 "this is not allowed in multiple selection mode. Resetting the file chooser "
5261 "to single selection mode.");
5262 set_select_multiple (impl, FALSE, TRUE);
5264 impl->action = action;
5265 update_cell_renderer_attributes (impl);
5266 update_appearance (impl);
5267 settings_load (impl);
5272 case GTK_FILE_CHOOSER_PROP_FILTER:
5273 set_current_filter (impl, g_value_get_object (value));
5276 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
5277 set_local_only (impl, g_value_get_boolean (value));
5280 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
5281 set_preview_widget (impl, g_value_get_object (value));
5284 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
5285 impl->preview_widget_active = g_value_get_boolean (value);
5286 update_preview_widget_visibility (impl);
5289 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
5290 impl->use_preview_label = g_value_get_boolean (value);
5291 update_preview_widget_visibility (impl);
5294 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
5295 set_extra_widget (impl, g_value_get_object (value));
5298 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
5300 gboolean select_multiple = g_value_get_boolean (value);
5301 if ((impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
5302 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
5305 g_warning ("Tried to set the file chooser to multiple selection mode, but this is "
5306 "not allowed in SAVE or CREATE_FOLDER modes. Ignoring the change and "
5307 "leaving the file chooser in single selection mode.");
5311 set_select_multiple (impl, select_multiple, FALSE);
5315 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
5317 gboolean show_hidden = g_value_get_boolean (value);
5318 if (show_hidden != impl->show_hidden)
5320 impl->show_hidden = show_hidden;
5322 if (impl->browse_files_model)
5323 _gtk_file_system_model_set_show_hidden (impl->browse_files_model, show_hidden);
5328 case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
5330 gboolean do_overwrite_confirmation = g_value_get_boolean (value);
5331 impl->do_overwrite_confirmation = do_overwrite_confirmation;
5335 case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
5337 gboolean create_folders = g_value_get_boolean (value);
5338 impl->create_folders = create_folders;
5339 update_appearance (impl);
5344 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5350 gtk_file_chooser_default_get_property (GObject *object,
5355 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
5359 case GTK_FILE_CHOOSER_PROP_ACTION:
5360 g_value_set_enum (value, impl->action);
5363 case GTK_FILE_CHOOSER_PROP_FILTER:
5364 g_value_set_object (value, impl->current_filter);
5367 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
5368 g_value_set_boolean (value, impl->local_only);
5371 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
5372 g_value_set_object (value, impl->preview_widget);
5375 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
5376 g_value_set_boolean (value, impl->preview_widget_active);
5379 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
5380 g_value_set_boolean (value, impl->use_preview_label);
5383 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
5384 g_value_set_object (value, impl->extra_widget);
5387 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
5388 g_value_set_boolean (value, impl->select_multiple);
5391 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
5392 g_value_set_boolean (value, impl->show_hidden);
5395 case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
5396 g_value_set_boolean (value, impl->do_overwrite_confirmation);
5399 case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
5400 g_value_set_boolean (value, impl->create_folders);
5404 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5409 /* Removes the settings signal handler. It's safe to call multiple times */
5411 remove_settings_signal (GtkFileChooserDefault *impl,
5414 if (impl->settings_signal_id)
5416 GtkSettings *settings;
5418 settings = gtk_settings_get_for_screen (screen);
5419 g_signal_handler_disconnect (settings,
5420 impl->settings_signal_id);
5421 impl->settings_signal_id = 0;
5426 gtk_file_chooser_default_dispose (GObject *object)
5429 GtkFileChooserDefault *impl = (GtkFileChooserDefault *) object;
5431 if (impl->extra_widget)
5433 g_object_unref (impl->extra_widget);
5434 impl->extra_widget = NULL;
5437 pending_select_files_free (impl);
5439 /* cancel all pending operations */
5440 if (impl->pending_cancellables)
5442 for (l = impl->pending_cancellables; l; l = l->next)
5444 GCancellable *cancellable = G_CANCELLABLE (l->data);
5445 g_cancellable_cancel (cancellable);
5447 g_slist_free (impl->pending_cancellables);
5448 impl->pending_cancellables = NULL;
5451 if (impl->reload_icon_cancellables)
5453 for (l = impl->reload_icon_cancellables; l; l = l->next)
5455 GCancellable *cancellable = G_CANCELLABLE (l->data);
5456 g_cancellable_cancel (cancellable);
5458 g_slist_free (impl->reload_icon_cancellables);
5459 impl->reload_icon_cancellables = NULL;
5462 if (impl->loading_shortcuts)
5464 for (l = impl->loading_shortcuts; l; l = l->next)
5466 GCancellable *cancellable = G_CANCELLABLE (l->data);
5467 g_cancellable_cancel (cancellable);
5469 g_slist_free (impl->loading_shortcuts);
5470 impl->loading_shortcuts = NULL;
5473 if (impl->file_list_drag_data_received_cancellable)
5475 g_cancellable_cancel (impl->file_list_drag_data_received_cancellable);
5476 impl->file_list_drag_data_received_cancellable = NULL;
5479 if (impl->update_current_folder_cancellable)
5481 g_cancellable_cancel (impl->update_current_folder_cancellable);
5482 impl->update_current_folder_cancellable = NULL;
5485 if (impl->should_respond_get_info_cancellable)
5487 g_cancellable_cancel (impl->should_respond_get_info_cancellable);
5488 impl->should_respond_get_info_cancellable = NULL;
5491 if (impl->update_from_entry_cancellable)
5493 g_cancellable_cancel (impl->update_from_entry_cancellable);
5494 impl->update_from_entry_cancellable = NULL;
5497 if (impl->shortcuts_activate_iter_cancellable)
5499 g_cancellable_cancel (impl->shortcuts_activate_iter_cancellable);
5500 impl->shortcuts_activate_iter_cancellable = NULL;
5503 search_stop_searching (impl, TRUE);
5504 recent_stop_loading (impl);
5506 remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl)));
5508 G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->dispose (object);
5511 /* We override show-all since we have internal widgets that
5512 * shouldn't be shown when you call show_all(), like the filter
5516 gtk_file_chooser_default_show_all (GtkWidget *widget)
5518 GtkFileChooserDefault *impl = (GtkFileChooserDefault *) widget;
5520 gtk_widget_show (widget);
5522 if (impl->extra_widget)
5523 gtk_widget_show_all (impl->extra_widget);
5526 /* Handler for GtkWindow::set-focus; this is where we save the last-focused
5527 * widget on our toplevel. See gtk_file_chooser_default_hierarchy_changed()
5530 toplevel_set_focus_cb (GtkWindow *window,
5532 GtkFileChooserDefault *impl)
5534 impl->toplevel_last_focus_widget = gtk_window_get_focus (window);
5537 /* We monitor the focus widget on our toplevel to be able to know which widget
5538 * was last focused at the time our "should_respond" method gets called.
5541 gtk_file_chooser_default_hierarchy_changed (GtkWidget *widget,
5542 GtkWidget *previous_toplevel)
5544 GtkFileChooserDefault *impl;
5545 GtkWidget *toplevel;
5547 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5549 if (previous_toplevel)
5551 g_assert (impl->toplevel_set_focus_id != 0);
5552 g_signal_handler_disconnect (previous_toplevel,
5553 impl->toplevel_set_focus_id);
5554 impl->toplevel_set_focus_id = 0;
5555 impl->toplevel_last_focus_widget = NULL;
5558 g_assert (impl->toplevel_set_focus_id == 0);
5560 toplevel = gtk_widget_get_toplevel (widget);
5561 if (GTK_IS_WINDOW (toplevel))
5563 impl->toplevel_set_focus_id = g_signal_connect (toplevel, "set-focus",
5564 G_CALLBACK (toplevel_set_focus_cb), impl);
5565 impl->toplevel_last_focus_widget = gtk_window_get_focus (GTK_WINDOW (toplevel));
5569 /* Changes the icons wherever it is needed */
5571 change_icon_theme (GtkFileChooserDefault *impl)
5573 GtkSettings *settings;
5575 GtkCellRenderer *renderer;
5578 profile_start ("start", NULL);
5580 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
5582 if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU, &width, &height))
5583 impl->icon_size = MAX (width, height);
5585 impl->icon_size = FALLBACK_ICON_SIZE;
5587 shortcuts_reload_icons (impl);
5588 /* the first cell in the first column is the icon column, and we have a fixed size there */
5589 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (
5590 gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_files_tree_view), 0)));
5591 renderer = GTK_CELL_RENDERER (cells->data);
5592 set_icon_cell_renderer_fixed_size (impl, renderer);
5593 g_list_free (cells);
5594 if (impl->browse_files_model)
5595 _gtk_file_system_model_clear_cache (impl->browse_files_model, MODEL_COL_PIXBUF);
5596 gtk_widget_queue_resize (impl->browse_files_tree_view);
5598 profile_end ("end", NULL);
5601 /* Callback used when a GtkSettings value changes */
5603 settings_notify_cb (GObject *object,
5605 GtkFileChooserDefault *impl)
5609 profile_start ("start", NULL);
5611 name = g_param_spec_get_name (pspec);
5613 if (strcmp (name, "gtk-icon-theme-name") == 0 ||
5614 strcmp (name, "gtk-icon-sizes") == 0)
5615 change_icon_theme (impl);
5617 profile_end ("end", NULL);
5620 /* Installs a signal handler for GtkSettings so that we can monitor changes in
5624 check_icon_theme (GtkFileChooserDefault *impl)
5626 GtkSettings *settings;
5628 profile_start ("start", NULL);
5630 if (impl->settings_signal_id)
5632 profile_end ("end", NULL);
5636 if (gtk_widget_has_screen (GTK_WIDGET (impl)))
5638 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
5639 impl->settings_signal_id = g_signal_connect (settings, "notify",
5640 G_CALLBACK (settings_notify_cb), impl);
5642 change_icon_theme (impl);
5645 profile_end ("end", NULL);
5649 gtk_file_chooser_default_style_set (GtkWidget *widget,
5650 GtkStyle *previous_style)
5652 GtkFileChooserDefault *impl;
5654 profile_start ("start", NULL);
5656 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5658 profile_msg (" parent class style_set start", NULL);
5659 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->style_set (widget, previous_style);
5660 profile_msg (" parent class style_set end", NULL);
5662 if (gtk_widget_has_screen (GTK_WIDGET (impl)))
5663 change_icon_theme (impl);
5665 emit_default_size_changed (impl);
5667 profile_end ("end", NULL);
5671 gtk_file_chooser_default_screen_changed (GtkWidget *widget,
5672 GdkScreen *previous_screen)
5674 GtkFileChooserDefault *impl;
5676 profile_start ("start", NULL);
5678 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5680 if (GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->screen_changed)
5681 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->screen_changed (widget, previous_screen);
5683 remove_settings_signal (impl, previous_screen);
5684 check_icon_theme (impl);
5686 emit_default_size_changed (impl);
5688 profile_end ("end", NULL);
5692 gtk_file_chooser_default_size_allocate (GtkWidget *widget,
5693 GtkAllocation *allocation)
5695 GtkFileChooserDefault *impl;
5697 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5699 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->size_allocate (widget, allocation);
5703 set_sort_column (GtkFileChooserDefault *impl)
5705 GtkTreeSortable *sortable;
5707 sortable = GTK_TREE_SORTABLE (gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view)));
5708 /* can happen when we're still populating the model */
5709 if (sortable == NULL)
5712 gtk_tree_sortable_set_sort_column_id (sortable,
5718 settings_load (GtkFileChooserDefault *impl)
5720 GtkFileChooserSettings *settings;
5721 LocationMode location_mode;
5722 gboolean show_hidden;
5723 gboolean expand_folders;
5724 gboolean show_size_column;
5726 GtkSortType sort_order;
5728 settings = _gtk_file_chooser_settings_new ();
5730 location_mode = _gtk_file_chooser_settings_get_location_mode (settings);
5731 show_hidden = _gtk_file_chooser_settings_get_show_hidden (settings);
5732 expand_folders = _gtk_file_chooser_settings_get_expand_folders (settings);
5733 show_size_column = _gtk_file_chooser_settings_get_show_size_column (settings);
5734 sort_column = _gtk_file_chooser_settings_get_sort_column (settings);
5735 sort_order = _gtk_file_chooser_settings_get_sort_order (settings);
5737 g_object_unref (settings);
5739 location_mode_set (impl, location_mode, TRUE);
5741 gtk_file_chooser_set_show_hidden (GTK_FILE_CHOOSER (impl), show_hidden);
5743 impl->expand_folders = expand_folders;
5744 if (impl->save_expander)
5745 gtk_expander_set_expanded (GTK_EXPANDER (impl->save_expander), expand_folders);
5747 impl->show_size_column = show_size_column;
5748 gtk_tree_view_column_set_visible (impl->list_size_column, show_size_column);
5750 impl->sort_column = sort_column;
5751 impl->sort_order = sort_order;
5752 /* We don't call set_sort_column() here as the models may not have been
5753 * created yet. The individual functions that create and set the models will
5754 * call set_sort_column() themselves.
5759 save_dialog_geometry (GtkFileChooserDefault *impl, GtkFileChooserSettings *settings)
5761 GtkWindow *toplevel;
5762 int x, y, width, height;
5764 /* We don't save the geometry in non-expanded "save" mode, so that the "little
5765 * dialog" won't make future Open dialogs too small.
5767 if (!(impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
5768 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
5769 || impl->expand_folders))
5772 toplevel = get_toplevel (GTK_WIDGET (impl));
5774 if (!(toplevel && GTK_IS_FILE_CHOOSER_DIALOG (toplevel)))
5777 gtk_window_get_position (toplevel, &x, &y);
5778 gtk_window_get_size (toplevel, &width, &height);
5780 _gtk_file_chooser_settings_set_geometry (settings, x, y, width, height);
5784 settings_save (GtkFileChooserDefault *impl)
5786 GtkFileChooserSettings *settings;
5788 settings = _gtk_file_chooser_settings_new ();
5790 _gtk_file_chooser_settings_set_location_mode (settings, impl->location_mode);
5791 _gtk_file_chooser_settings_set_show_hidden (settings, gtk_file_chooser_get_show_hidden (GTK_FILE_CHOOSER (impl)));
5792 _gtk_file_chooser_settings_set_expand_folders (settings, impl->expand_folders);
5793 _gtk_file_chooser_settings_set_show_size_column (settings, impl->show_size_column);
5794 _gtk_file_chooser_settings_set_sort_column (settings, impl->sort_column);
5795 _gtk_file_chooser_settings_set_sort_order (settings, impl->sort_order);
5797 save_dialog_geometry (impl, settings);
5800 _gtk_file_chooser_settings_save (settings, NULL);
5802 g_object_unref (settings);
5805 /* GtkWidget::realize method */
5807 gtk_file_chooser_default_realize (GtkWidget *widget)
5809 GtkFileChooserDefault *impl;
5811 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5813 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->realize (widget);
5815 emit_default_size_changed (impl);
5818 /* GtkWidget::map method */
5820 gtk_file_chooser_default_map (GtkWidget *widget)
5822 GtkFileChooserDefault *impl;
5823 char *current_working_dir;
5825 profile_start ("start", NULL);
5827 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5829 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->map (widget);
5831 if (impl->operation_mode == OPERATION_MODE_BROWSE)
5833 switch (impl->reload_state)
5836 /* The user didn't explicitly give us a folder to
5837 * display, so we'll use the cwd
5839 current_working_dir = g_get_current_dir ();
5840 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (impl),
5841 current_working_dir);
5842 g_free (current_working_dir);
5845 case RELOAD_HAS_FOLDER:
5846 /* Nothing; we are already loading or loaded, so we
5847 * don't need to reload
5852 g_assert_not_reached ();
5856 volumes_bookmarks_changed_cb (impl->file_system, impl);
5858 settings_load (impl);
5860 profile_end ("end", NULL);
5863 /* GtkWidget::unmap method */
5865 gtk_file_chooser_default_unmap (GtkWidget *widget)
5867 GtkFileChooserDefault *impl;
5869 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
5871 settings_save (impl);
5873 GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->unmap (widget);
5877 install_list_model_filter (GtkFileChooserDefault *impl)
5879 _gtk_file_system_model_set_filter (impl->browse_files_model,
5880 impl->current_filter);
5883 #define COMPARE_DIRECTORIES \
5884 GtkFileChooserDefault *impl = user_data; \
5885 GtkFileSystemModel *fs_model = GTK_FILE_SYSTEM_MODEL (model); \
5886 gboolean dir_a, dir_b; \
5888 dir_a = g_value_get_boolean (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_IS_FOLDER)); \
5889 dir_b = g_value_get_boolean (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_IS_FOLDER)); \
5891 if (dir_a != dir_b) \
5892 return impl->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */
5894 /* Sort callback for the filename column */
5896 name_sort_func (GtkTreeModel *model,
5901 COMPARE_DIRECTORIES;
5904 const char *key_a, *key_b;
5907 key_a = g_value_get_string (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_NAME_COLLATED));
5908 key_b = g_value_get_string (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_NAME_COLLATED));
5911 result = strcmp (key_a, key_b);
5923 /* Sort callback for the size column */
5925 size_sort_func (GtkTreeModel *model,
5930 COMPARE_DIRECTORIES;
5933 gint64 size_a, size_b;
5935 size_a = g_value_get_int64 (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_SIZE));
5936 size_b = g_value_get_int64 (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_SIZE));
5938 return size_a < size_b ? -1 : (size_a == size_b ? 0 : 1);
5942 /* Sort callback for the mtime column */
5944 mtime_sort_func (GtkTreeModel *model,
5949 COMPARE_DIRECTORIES;
5954 ta = g_value_get_long (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_MTIME));
5955 tb = g_value_get_long (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_MTIME));
5957 return ta < tb ? -1 : (ta == tb ? 0 : 1);
5961 /* Callback used when the sort column changes. We cache the sort order for use
5962 * in name_sort_func().
5965 list_sort_column_changed_cb (GtkTreeSortable *sortable,
5966 GtkFileChooserDefault *impl)
5968 gint sort_column_id;
5969 GtkSortType sort_type;
5971 if (gtk_tree_sortable_get_sort_column_id (sortable, &sort_column_id, &sort_type))
5973 impl->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
5974 impl->sort_column = sort_column_id;
5975 impl->sort_order = sort_type;
5980 set_busy_cursor (GtkFileChooserDefault *impl,
5984 GtkWindow *toplevel;
5985 GdkDisplay *display;
5988 toplevel = get_toplevel (GTK_WIDGET (impl));
5989 widget = GTK_WIDGET (toplevel);
5990 if (!toplevel || !gtk_widget_get_realized (widget))
5993 display = gtk_widget_get_display (widget);
5996 cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
6000 gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
6001 gdk_display_flush (display);
6004 gdk_cursor_unref (cursor);
6007 /* Creates a sort model to wrap the file system model and sets it on the tree view */
6009 load_set_model (GtkFileChooserDefault *impl)
6011 profile_start ("start", NULL);
6013 g_assert (impl->browse_files_model != NULL);
6015 profile_msg (" gtk_tree_view_set_model start", NULL);
6016 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
6017 GTK_TREE_MODEL (impl->browse_files_model));
6018 gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->browse_files_tree_view));
6019 gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_files_tree_view),
6021 set_sort_column (impl);
6022 profile_msg (" gtk_tree_view_set_model end", NULL);
6023 impl->list_sort_ascending = TRUE;
6025 profile_end ("end", NULL);
6028 /* Timeout callback used when the loading timer expires */
6030 load_timeout_cb (gpointer data)
6032 GtkFileChooserDefault *impl;
6034 profile_start ("start", NULL);
6036 impl = GTK_FILE_CHOOSER_DEFAULT (data);
6037 g_assert (impl->load_state == LOAD_PRELOAD);
6038 g_assert (impl->load_timeout_id != 0);
6039 g_assert (impl->browse_files_model != NULL);
6041 impl->load_timeout_id = 0;
6042 impl->load_state = LOAD_LOADING;
6044 load_set_model (impl);
6046 profile_end ("end", NULL);
6051 /* Sets up a new load timer for the model and switches to the LOAD_PRELOAD state */
6053 load_setup_timer (GtkFileChooserDefault *impl)
6055 g_assert (impl->load_timeout_id == 0);
6056 g_assert (impl->load_state != LOAD_PRELOAD);
6058 impl->load_timeout_id = gdk_threads_add_timeout (MAX_LOADING_TIME, load_timeout_cb, impl);
6059 impl->load_state = LOAD_PRELOAD;
6062 /* Removes the load timeout and switches to the LOAD_FINISHED state */
6064 load_remove_timer (GtkFileChooserDefault *impl)
6066 if (impl->load_timeout_id != 0)
6068 g_assert (impl->load_state == LOAD_PRELOAD);
6070 g_source_remove (impl->load_timeout_id);
6071 impl->load_timeout_id = 0;
6072 impl->load_state = LOAD_EMPTY;
6075 g_assert (impl->load_state == LOAD_EMPTY ||
6076 impl->load_state == LOAD_LOADING ||
6077 impl->load_state == LOAD_FINISHED);
6080 /* Selects the first row in the file list */
6082 browse_files_select_first_row (GtkFileChooserDefault *impl)
6085 GtkTreeIter dummy_iter;
6086 GtkTreeModel *tree_model;
6088 tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view));
6093 path = gtk_tree_path_new_from_indices (0, -1);
6095 /* If the list is empty, do nothing. */
6096 if (gtk_tree_model_get_iter (tree_model, &dummy_iter, path))
6097 gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), path, NULL, FALSE);
6099 gtk_tree_path_free (path);
6102 struct center_selected_row_closure {
6103 GtkFileChooserDefault *impl;
6104 gboolean already_centered;
6107 /* Callback used from gtk_tree_selection_selected_foreach(); centers the
6108 * selected row in the tree view.
6111 center_selected_row_foreach_cb (GtkTreeModel *model,
6116 struct center_selected_row_closure *closure;
6119 if (closure->already_centered)
6122 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (closure->impl->browse_files_tree_view), path, NULL, TRUE, 0.5, 0.0);
6123 closure->already_centered = TRUE;
6126 /* Centers the selected row in the tree view */
6128 browse_files_center_selected_row (GtkFileChooserDefault *impl)
6130 struct center_selected_row_closure closure;
6131 GtkTreeSelection *selection;
6133 closure.impl = impl;
6134 closure.already_centered = FALSE;
6136 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6137 gtk_tree_selection_selected_foreach (selection, center_selected_row_foreach_cb, &closure);
6141 show_and_select_files (GtkFileChooserDefault *impl,
6144 GtkTreeSelection *selection;
6145 GtkFileSystemModel *fsmodel;
6146 gboolean can_have_hidden, can_have_filtered, selected_a_file;
6149 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6150 fsmodel = GTK_FILE_SYSTEM_MODEL (gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view)));
6151 can_have_hidden = !impl->show_hidden;
6152 can_have_filtered = impl->current_filter != NULL;
6153 selected_a_file = FALSE;
6155 for (walk = files; walk && (can_have_hidden || can_have_filtered); walk = walk->next)
6157 GFile *file = walk->data;
6160 if (!_gtk_file_system_model_get_iter_for_file (fsmodel, &iter, file))
6163 if (!_gtk_file_system_model_iter_is_visible (fsmodel, &iter))
6165 GFileInfo *info = _gtk_file_system_model_get_info (fsmodel, &iter);
6167 if (can_have_hidden &&
6168 (g_file_info_get_is_hidden (info) ||
6169 g_file_info_get_is_backup (info)))
6171 g_object_set (impl, "show-hidden", TRUE, NULL);
6172 can_have_hidden = FALSE;
6175 if (can_have_filtered)
6177 set_current_filter (impl, NULL);
6178 can_have_filtered = FALSE;
6182 if (_gtk_file_system_model_iter_is_visible (fsmodel, &iter))
6184 gtk_tree_selection_select_iter (selection, &iter);
6185 selected_a_file = TRUE;
6189 browse_files_center_selected_row (impl);
6191 return selected_a_file;
6194 /* Processes the pending operation when a folder is finished loading */
6196 pending_select_files_process (GtkFileChooserDefault *impl)
6198 g_assert (impl->load_state == LOAD_FINISHED);
6199 g_assert (impl->browse_files_model != NULL);
6201 if (impl->pending_select_files)
6203 show_and_select_files (impl, impl->pending_select_files);
6204 pending_select_files_free (impl);
6205 browse_files_center_selected_row (impl);
6209 /* We only select the first row if the chooser is actually mapped ---
6210 * selecting the first row is to help the user when he is interacting with
6211 * the chooser, but sometimes a chooser works not on behalf of the user,
6212 * but rather on behalf of something else like GtkFileChooserButton. In
6213 * that case, the chooser's selection should be what the caller expects,
6214 * as the user can't see that something else got selected. See bug #165264.
6216 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN &&
6217 gtk_widget_get_mapped (GTK_WIDGET (impl)))
6218 browse_files_select_first_row (impl);
6221 g_assert (impl->pending_select_files == NULL);
6225 show_error_on_reading_current_folder (GtkFileChooserDefault *impl, GError *error)
6230 info = g_file_query_info (impl->current_folder,
6231 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
6232 G_FILE_QUERY_INFO_NONE,
6237 msg = g_strdup_printf (_("Could not read the contents of %s"), g_file_info_get_display_name (info));
6238 g_object_unref (info);
6241 msg = g_strdup (_("Could not read the contents of the folder"));
6243 error_message (impl, msg, error->message);
6247 /* Callback used when the file system model finishes loading */
6249 browse_files_model_finished_loading_cb (GtkFileSystemModel *model,
6251 GtkFileChooserDefault *impl)
6253 profile_start ("start", NULL);
6256 show_error_on_reading_current_folder (impl, error);
6258 if (impl->load_state == LOAD_PRELOAD)
6260 load_remove_timer (impl);
6261 load_set_model (impl);
6263 else if (impl->load_state == LOAD_LOADING)
6269 /* We can't g_assert_not_reached(), as something other than us may have
6270 * initiated a folder reload. See #165556.
6272 profile_end ("end", NULL);
6276 g_assert (impl->load_timeout_id == 0);
6278 impl->load_state = LOAD_FINISHED;
6280 pending_select_files_process (impl);
6281 set_busy_cursor (impl, FALSE);
6282 #ifdef PROFILE_FILE_CHOOSER
6283 access ("MARK: *** FINISHED LOADING", F_OK);
6286 profile_end ("end", NULL);
6290 stop_loading_and_clear_list_model (GtkFileChooserDefault *impl,
6291 gboolean remove_from_treeview)
6293 load_remove_timer (impl); /* This changes the state to LOAD_EMPTY */
6295 if (impl->browse_files_model)
6297 g_object_unref (impl->browse_files_model);
6298 impl->browse_files_model = NULL;
6301 if (remove_from_treeview)
6302 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
6306 my_g_format_time_for_display (glong secs)
6311 time_t time_mtime, time_now;
6312 const gchar *format;
6313 gchar *locale_format = NULL;
6315 char *date_str = NULL;
6317 const char *locale, *dot = NULL;
6318 gint64 codepage = -1;
6324 #ifdef HAVE_LOCALTIME_R
6325 localtime_r ((time_t *) &time_mtime, &tm_mtime);
6328 struct tm *ptm = localtime ((time_t *) &time_mtime);
6332 g_warning ("ptm != NULL failed");
6334 return g_strdup (_("Unknown"));
6337 memcpy ((void *) &tm_mtime, (void *) ptm, sizeof (struct tm));
6339 #endif /* HAVE_LOCALTIME_R */
6341 g_date_set_time_t (&mtime, time_mtime);
6342 time_now = time (NULL);
6343 g_date_set_time_t (&now, time_now);
6345 days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
6347 /* Translators: %H means "hours" and %M means "minutes" */
6349 format = _("%H:%M");
6350 else if (days_diff == 1)
6351 format = _("Yesterday at %H:%M");
6354 if (days_diff > 1 && days_diff < 7)
6355 format = "%A"; /* Days from last week */
6357 format = "%x"; /* Any other date */
6361 /* g_locale_from_utf8() returns a string in the system
6362 * code-page, which is not always the same as that used by the C
6363 * library. For instance when running a GTK+ program with
6364 * LANG=ko on an English version of Windows, the system
6365 * code-page is 1252, but the code-page used by the C library is
6366 * 949. (It's GTK+ itself that sets the C library locale when it
6367 * notices the LANG environment variable. See gtkmain.c The
6368 * Microsoft C library doesn't look at any locale environment
6369 * variables.) We need to pass strftime() a string in the C
6370 * library's code-page. See bug #509885.
6372 locale = setlocale (LC_ALL, NULL);
6374 dot = strchr (locale, '.');
6377 codepage = g_ascii_strtoll (dot+1, NULL, 10);
6379 /* All codepages should fit in 16 bits AFAIK */
6380 if (codepage > 0 && codepage < 65536)
6382 sprintf (charset, "CP%u", (guint) codepage);
6383 locale_format = g_convert (format, -1, charset, "UTF-8", NULL, NULL, NULL);
6387 locale_format = g_locale_from_utf8 (format, -1, NULL, NULL, NULL);
6389 if (locale_format != NULL &&
6390 strftime (buf, sizeof (buf), locale_format, &tm_mtime) != 0)
6393 /* As above but in opposite direction... */
6394 if (codepage > 0 && codepage < 65536)
6395 date_str = g_convert (buf, -1, "UTF-8", charset, NULL, NULL, NULL);
6397 date_str = g_locale_to_utf8 (buf, -1, NULL, NULL, NULL);
6401 if (date_str == NULL)
6402 date_str = g_strdup (_("Unknown"));
6404 g_free (locale_format);
6409 copy_attribute (GFileInfo *to, GFileInfo *from, const char *attribute)
6411 GFileAttributeType type;
6414 if (g_file_info_get_attribute_data (from, attribute, &type, &value, NULL))
6415 g_file_info_set_attribute (to, attribute, type, value);
6419 file_system_model_got_thumbnail (GObject *object, GAsyncResult *res, gpointer data)
6421 GtkFileSystemModel *model = data; /* might be unreffed if operation was cancelled */
6422 GFile *file = G_FILE (object);
6423 GFileInfo *queried, *info;
6426 queried = g_file_query_info_finish (file, res, NULL);
6427 if (queried == NULL)
6430 /* now we know model is valid */
6432 /* file was deleted */
6433 if (!_gtk_file_system_model_get_iter_for_file (model, &iter, file))
6436 info = g_file_info_dup (_gtk_file_system_model_get_info (model, &iter));
6438 copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
6439 copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED);
6440 copy_attribute (info, queried, G_FILE_ATTRIBUTE_STANDARD_ICON);
6442 _gtk_file_system_model_update_file (model, file, info, FALSE);
6444 g_object_unref (info);
6448 file_system_model_set (GtkFileSystemModel *model,
6455 GtkFileChooserDefault *impl = data;
6459 case MODEL_COL_FILE:
6460 g_value_set_object (value, file);
6462 case MODEL_COL_NAME:
6464 g_value_set_string (value, DEFAULT_NEW_FOLDER_NAME);
6466 g_value_set_string (value, g_file_info_get_display_name (info));
6468 case MODEL_COL_NAME_COLLATED:
6470 g_value_take_string (value, g_utf8_collate_key_for_filename (DEFAULT_NEW_FOLDER_NAME, -1));
6472 g_value_take_string (value, g_utf8_collate_key_for_filename (g_file_info_get_display_name (info), -1));
6474 case MODEL_COL_IS_FOLDER:
6475 g_value_set_boolean (value, info == NULL || _gtk_file_info_consider_as_directory (info));
6477 case MODEL_COL_PIXBUF:
6480 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_ICON))
6482 g_value_take_object (value, _gtk_file_info_render_icon (info, GTK_WIDGET (impl), impl->icon_size));
6486 GtkTreeModel *tree_model;
6487 GtkTreePath *path, *start, *end;
6490 if (impl->browse_files_tree_view == NULL ||
6491 g_file_info_has_attribute (info, "filechooser::queried"))
6494 tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view));
6495 if (tree_model != GTK_TREE_MODEL (model))
6498 if (!_gtk_file_system_model_get_iter_for_file (model,
6501 g_assert_not_reached ();
6502 if (!gtk_tree_view_get_visible_range (GTK_TREE_VIEW (impl->browse_files_tree_view), &start, &end))
6504 path = gtk_tree_model_get_path (tree_model, &iter);
6505 if (gtk_tree_path_compare (start, path) != 1 &&
6506 gtk_tree_path_compare (path, end) != 1)
6508 g_file_info_set_attribute_boolean (info, "filechooser::queried", TRUE);
6509 g_file_query_info_async (file,
6510 G_FILE_ATTRIBUTE_THUMBNAIL_PATH ","
6511 G_FILE_ATTRIBUTE_THUMBNAILING_FAILED ","
6512 G_FILE_ATTRIBUTE_STANDARD_ICON,
6513 G_FILE_QUERY_INFO_NONE,
6515 _gtk_file_system_model_get_cancellable (model),
6516 file_system_model_got_thumbnail,
6519 gtk_tree_path_free (path);
6520 gtk_tree_path_free (start);
6521 gtk_tree_path_free (end);
6526 g_value_set_object (value, NULL);
6528 case MODEL_COL_SIZE:
6529 g_value_set_int64 (value, info ? g_file_info_get_size (info) : 0);
6531 case MODEL_COL_SIZE_TEXT:
6532 if (info == NULL || _gtk_file_info_consider_as_directory (info))
6533 g_value_set_string (value, NULL);
6535 g_value_take_string (value, g_format_size_for_display (g_file_info_get_size (info)));
6537 case MODEL_COL_MTIME:
6538 case MODEL_COL_MTIME_TEXT:
6543 g_file_info_get_modification_time (info, &tv);
6544 if (column == MODEL_COL_MTIME)
6545 g_value_set_long (value, tv.tv_sec);
6546 else if (tv.tv_sec == 0)
6547 g_value_set_static_string (value, _("Unknown"));
6549 g_value_take_string (value, my_g_format_time_for_display (tv.tv_sec));
6552 case MODEL_COL_ELLIPSIZE:
6553 g_value_set_enum (value, info ? PANGO_ELLIPSIZE_END : PANGO_ELLIPSIZE_NONE);
6556 g_assert_not_reached ();
6563 /* Gets rid of the old list model and creates a new one for the current folder */
6565 set_list_model (GtkFileChooserDefault *impl,
6568 g_assert (impl->current_folder != NULL);
6570 profile_start ("start", NULL);
6572 stop_loading_and_clear_list_model (impl, TRUE);
6574 set_busy_cursor (impl, TRUE);
6576 impl->browse_files_model =
6577 _gtk_file_system_model_new_for_directory (impl->current_folder,
6579 file_system_model_set,
6581 MODEL_COLUMN_TYPES);
6583 _gtk_file_system_model_set_show_hidden (impl->browse_files_model, impl->show_hidden);
6585 profile_msg (" set sort function", NULL);
6586 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), MODEL_COL_NAME, name_sort_func, impl, NULL);
6587 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), MODEL_COL_SIZE, size_sort_func, impl, NULL);
6588 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), MODEL_COL_MTIME, mtime_sort_func, impl, NULL);
6589 gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), NULL, NULL, NULL);
6590 set_sort_column (impl);
6591 impl->list_sort_ascending = TRUE;
6592 g_signal_connect (impl->browse_files_model, "sort-column-changed",
6593 G_CALLBACK (list_sort_column_changed_cb), impl);
6595 load_setup_timer (impl); /* This changes the state to LOAD_PRELOAD */
6597 g_signal_connect (impl->browse_files_model, "finished-loading",
6598 G_CALLBACK (browse_files_model_finished_loading_cb), impl);
6600 install_list_model_filter (impl);
6602 profile_end ("end", NULL);
6607 struct update_chooser_entry_selected_foreach_closure {
6609 GtkTreeIter first_selected_iter;
6613 compare_utf8_filenames (const gchar *a,
6616 gchar *a_folded, *b_folded;
6619 a_folded = g_utf8_strdown (a, -1);
6620 b_folded = g_utf8_strdown (b, -1);
6622 retval = strcmp (a_folded, b_folded);
6631 update_chooser_entry_selected_foreach (GtkTreeModel *model,
6636 struct update_chooser_entry_selected_foreach_closure *closure;
6639 closure->num_selected++;
6641 if (closure->num_selected == 1)
6642 closure->first_selected_iter = *iter;
6646 update_chooser_entry (GtkFileChooserDefault *impl)
6648 GtkTreeSelection *selection;
6649 struct update_chooser_entry_selected_foreach_closure closure;
6650 const char *file_part;
6652 /* no need to update the file chooser's entry if there's no entry */
6653 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
6654 impl->operation_mode == OPERATION_MODE_RECENT ||
6655 !impl->location_entry)
6658 if (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
6659 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
6660 || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
6661 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
6662 && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)))
6665 g_assert (impl->location_entry != NULL);
6667 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
6668 closure.num_selected = 0;
6669 gtk_tree_selection_selected_foreach (selection, update_chooser_entry_selected_foreach, &closure);
6673 if (closure.num_selected == 0)
6675 goto maybe_clear_entry;
6677 else if (closure.num_selected == 1)
6679 if (impl->operation_mode == OPERATION_MODE_BROWSE)
6682 gboolean change_entry;
6684 info = _gtk_file_system_model_get_info (impl->browse_files_model, &closure.first_selected_iter);
6686 /* If the cursor moved to the row of the newly created folder,
6687 * retrieving info will return NULL.
6692 g_free (impl->browse_files_last_selected_name);
6693 impl->browse_files_last_selected_name =
6694 g_strdup (g_file_info_get_display_name (info));
6696 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
6697 impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
6698 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
6700 /* We don't want the name to change when clicking on a folder... */
6701 change_entry = (! _gtk_file_info_consider_as_directory (info));
6704 change_entry = TRUE; /* ... unless we are in SELECT_FOLDER mode */
6708 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->browse_files_last_selected_name);
6710 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
6711 _gtk_file_chooser_entry_select_filename (GTK_FILE_CHOOSER_ENTRY (impl->location_entry));
6719 g_assert (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
6720 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER));
6722 /* Multiple selection, so just clear the entry. */
6724 g_free (impl->browse_files_last_selected_name);
6725 impl->browse_files_last_selected_name = NULL;
6727 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
6733 if ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
6734 && impl->browse_files_last_selected_name)
6736 const char *entry_text;
6738 gboolean clear_entry;
6740 entry_text = gtk_entry_get_text (GTK_ENTRY (impl->location_entry));
6741 len = strlen (entry_text);
6744 /* The file chooser entry may have appended a "/" to its text. So
6745 * take it out, and compare the result to the old selection.
6747 if (entry_text[len - 1] == G_DIR_SEPARATOR)
6751 tmp = g_strndup (entry_text, len - 1);
6752 clear_entry = (compare_utf8_filenames (impl->browse_files_last_selected_name, tmp) == 0);
6756 clear_entry = (compare_utf8_filenames (impl->browse_files_last_selected_name, entry_text) == 0);
6759 clear_entry = FALSE;
6762 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
6767 gtk_file_chooser_default_set_current_folder (GtkFileChooser *chooser,
6771 return gtk_file_chooser_default_update_current_folder (chooser, file, FALSE, FALSE, error);
6775 struct UpdateCurrentFolderData
6777 GtkFileChooserDefault *impl;
6779 gboolean keep_trail;
6780 gboolean clear_entry;
6781 GFile *original_file;
6782 GError *original_error;
6786 update_current_folder_mount_enclosing_volume_cb (GCancellable *cancellable,
6787 GtkFileSystemVolume *volume,
6788 const GError *error,
6791 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
6792 struct UpdateCurrentFolderData *data = user_data;
6793 GtkFileChooserDefault *impl = data->impl;
6795 if (cancellable != impl->update_current_folder_cancellable)
6798 impl->update_current_folder_cancellable = NULL;
6799 set_busy_cursor (impl, FALSE);
6806 error_changing_folder_dialog (data->impl, data->file, g_error_copy (error));
6807 impl->reload_state = RELOAD_EMPTY;
6811 change_folder_and_display_error (impl, data->file, data->clear_entry);
6814 g_object_unref (data->file);
6817 g_object_unref (cancellable);
6821 update_current_folder_get_info_cb (GCancellable *cancellable,
6823 const GError *error,
6826 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
6827 struct UpdateCurrentFolderData *data = user_data;
6828 GtkFileChooserDefault *impl = data->impl;
6830 if (cancellable != impl->update_current_folder_cancellable)
6833 impl->update_current_folder_cancellable = NULL;
6834 impl->reload_state = RELOAD_EMPTY;
6836 set_busy_cursor (impl, FALSE);
6845 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED))
6847 GMountOperation *mount_operation;
6848 GtkWidget *toplevel;
6850 g_object_unref (cancellable);
6851 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
6853 mount_operation = gtk_mount_operation_new (GTK_WINDOW (toplevel));
6855 set_busy_cursor (impl, TRUE);
6857 impl->update_current_folder_cancellable =
6858 _gtk_file_system_mount_enclosing_volume (impl->file_system, data->file,
6860 update_current_folder_mount_enclosing_volume_cb,
6866 if (!data->original_file)
6868 data->original_file = g_object_ref (data->file);
6869 data->original_error = g_error_copy (error);
6872 parent_file = g_file_get_parent (data->file);
6874 /* get parent path and try to change the folder to that */
6877 g_object_unref (data->file);
6878 data->file = parent_file;
6880 g_object_unref (cancellable);
6882 /* restart the update current folder operation */
6883 impl->reload_state = RELOAD_HAS_FOLDER;
6885 impl->update_current_folder_cancellable =
6886 _gtk_file_system_get_info (impl->file_system, data->file,
6888 update_current_folder_get_info_cb,
6891 set_busy_cursor (impl, TRUE);
6897 /* Error and bail out, ignoring "not found" errors since they're useless:
6898 * they only happen when a program defaults to a folder that has been (re)moved.
6900 if (!g_error_matches (data->original_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
6901 error_changing_folder_dialog (impl, data->original_file, data->original_error);
6903 g_error_free (data->original_error);
6905 g_object_unref (data->original_file);
6911 if (data->original_file)
6913 /* Error and bail out, ignoring "not found" errors since they're useless:
6914 * they only happen when a program defaults to a folder that has been (re)moved.
6916 if (!g_error_matches (data->original_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
6917 error_changing_folder_dialog (impl, data->original_file, data->original_error);
6919 g_error_free (data->original_error);
6921 g_object_unref (data->original_file);
6924 if (! _gtk_file_info_consider_as_directory (info))
6927 if (!_gtk_path_bar_set_file (GTK_PATH_BAR (impl->browse_path_bar), data->file, data->keep_trail, NULL))
6930 if (impl->current_folder != data->file)
6932 if (impl->current_folder)
6933 g_object_unref (impl->current_folder);
6935 impl->current_folder = g_object_ref (data->file);
6938 impl->reload_state = RELOAD_HAS_FOLDER;
6940 /* Update the widgets that may trigger a folder change themselves. */
6942 if (!impl->changing_folder)
6944 impl->changing_folder = TRUE;
6946 shortcuts_update_current_folder (impl);
6948 impl->changing_folder = FALSE;
6951 /* Set the folder on the save entry */
6953 if (impl->location_entry)
6955 _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
6956 impl->current_folder);
6958 if (data->clear_entry)
6959 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
6962 /* Create a new list model. This is slightly evil; we store the result value
6963 * but perform more actions rather than returning immediately even if it
6964 * generates an error.
6966 set_list_model (impl, NULL);
6968 /* Refresh controls */
6970 shortcuts_find_current_folder (impl);
6972 g_signal_emit_by_name (impl, "current-folder-changed", 0);
6974 check_preview_change (impl);
6975 bookmarks_check_add_sensitivity (impl);
6977 g_signal_emit_by_name (impl, "selection-changed", 0);
6980 g_object_unref (data->file);
6983 g_object_unref (cancellable);
6987 gtk_file_chooser_default_update_current_folder (GtkFileChooser *chooser,
6989 gboolean keep_trail,
6990 gboolean clear_entry,
6993 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
6994 struct UpdateCurrentFolderData *data;
6996 profile_start ("start", NULL);
6998 g_object_ref (file);
7000 switch (impl->operation_mode)
7002 case OPERATION_MODE_SEARCH:
7003 search_switch_to_browse_mode (impl);
7005 case OPERATION_MODE_RECENT:
7006 recent_switch_to_browse_mode (impl);
7008 case OPERATION_MODE_BROWSE:
7012 if (impl->local_only && !g_file_is_native (file))
7014 g_set_error_literal (error,
7015 GTK_FILE_CHOOSER_ERROR,
7016 GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
7017 _("Cannot change to folder because it is not local"));
7019 g_object_unref (file);
7020 profile_end ("end - not local", NULL);
7024 if (impl->update_current_folder_cancellable)
7025 g_cancellable_cancel (impl->update_current_folder_cancellable);
7027 /* Test validity of path here. */
7028 data = g_new0 (struct UpdateCurrentFolderData, 1);
7030 data->file = g_object_ref (file);
7031 data->keep_trail = keep_trail;
7032 data->clear_entry = clear_entry;
7034 impl->reload_state = RELOAD_HAS_FOLDER;
7036 impl->update_current_folder_cancellable =
7037 _gtk_file_system_get_info (impl->file_system, file,
7039 update_current_folder_get_info_cb,
7042 set_busy_cursor (impl, TRUE);
7043 g_object_unref (file);
7045 profile_end ("end", NULL);
7050 gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser)
7052 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7054 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
7055 impl->operation_mode == OPERATION_MODE_RECENT)
7058 if (impl->reload_state == RELOAD_EMPTY)
7060 char *current_working_dir;
7063 /* We are unmapped, or we had an error while loading the last folder. We'll return
7064 * the $cwd since once we get (re)mapped, we'll load $cwd anyway unless the caller
7065 * explicitly calls set_current_folder() on us.
7067 current_working_dir = g_get_current_dir ();
7068 file = g_file_new_for_path (current_working_dir);
7069 g_free (current_working_dir);
7073 if (impl->current_folder)
7074 return g_object_ref (impl->current_folder);
7080 gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
7083 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7085 g_return_if_fail (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
7086 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
7088 pending_select_files_free (impl);
7089 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), name);
7093 gtk_file_chooser_default_select_file (GtkFileChooser *chooser,
7097 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7101 parent_file = g_file_get_parent (file);
7104 return gtk_file_chooser_set_current_folder_file (chooser, file, error);
7106 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
7107 impl->operation_mode == OPERATION_MODE_RECENT ||
7108 impl->load_state == LOAD_EMPTY)
7114 g_assert (impl->current_folder != NULL);
7116 same_path = g_file_equal (parent_file, impl->current_folder);
7119 if (same_path && impl->load_state == LOAD_FINISHED)
7124 files.data = (gpointer) file;
7127 result = show_and_select_files (impl, &files);
7128 g_object_unref (parent_file);
7132 pending_select_files_add (impl, file);
7138 result = gtk_file_chooser_set_current_folder_file (chooser, parent_file, error);
7139 g_object_unref (parent_file);
7143 g_object_unref (parent_file);
7148 gtk_file_chooser_default_unselect_file (GtkFileChooser *chooser,
7151 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7152 GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
7155 if (!impl->browse_files_model)
7158 if (!_gtk_file_system_model_get_iter_for_file (impl->browse_files_model,
7163 gtk_tree_selection_unselect_iter (gtk_tree_view_get_selection (tree_view),
7168 maybe_select (GtkTreeModel *model,
7173 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (data);
7174 GtkTreeSelection *selection;
7177 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7179 gtk_tree_model_get (model, iter,
7180 MODEL_COL_IS_FOLDER, &is_folder,
7183 if ((is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) ||
7184 (!is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN))
7185 gtk_tree_selection_select_iter (selection, iter);
7187 gtk_tree_selection_unselect_iter (selection, iter);
7193 gtk_file_chooser_default_select_all (GtkFileChooser *chooser)
7195 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7197 if (impl->operation_mode == OPERATION_MODE_SEARCH ||
7198 impl->operation_mode == OPERATION_MODE_RECENT)
7200 GtkTreeSelection *selection;
7202 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7203 gtk_tree_selection_select_all (selection);
7207 if (impl->select_multiple)
7208 gtk_tree_model_foreach (GTK_TREE_MODEL (impl->browse_files_model),
7209 maybe_select, impl);
7213 gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser)
7215 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7216 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7218 gtk_tree_selection_unselect_all (selection);
7219 pending_select_files_free (impl);
7222 /* Checks whether the filename entry for the Save modes contains a well-formed filename.
7224 * is_well_formed_ret - whether what the user typed passes gkt_file_system_make_path()
7226 * is_empty_ret - whether the file entry is totally empty
7228 * is_file_part_empty_ret - whether the file part is empty (will be if user types "foobar/", and
7229 * the path will be "$cwd/foobar")
7232 check_save_entry (GtkFileChooserDefault *impl,
7234 gboolean *is_well_formed_ret,
7235 gboolean *is_empty_ret,
7236 gboolean *is_file_part_empty_ret,
7237 gboolean *is_folder)
7239 GtkFileChooserEntry *chooser_entry;
7240 GFile *current_folder;
7241 const char *file_part;
7245 g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
7246 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
7247 || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
7248 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
7249 && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY));
7251 chooser_entry = GTK_FILE_CHOOSER_ENTRY (impl->location_entry);
7253 if (strlen (gtk_entry_get_text (GTK_ENTRY (chooser_entry))) == 0)
7256 *is_well_formed_ret = TRUE;
7257 *is_empty_ret = TRUE;
7258 *is_file_part_empty_ret = TRUE;
7264 *is_empty_ret = FALSE;
7266 current_folder = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
7267 if (!current_folder)
7270 *is_well_formed_ret = FALSE;
7271 *is_file_part_empty_ret = FALSE;
7277 file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
7279 if (!file_part || file_part[0] == '\0')
7281 *file_ret = g_object_ref (current_folder);
7282 *is_well_formed_ret = TRUE;
7283 *is_file_part_empty_ret = TRUE;
7289 *is_file_part_empty_ret = FALSE;
7292 file = g_file_get_child_for_display_name (current_folder, file_part, &error);
7296 error_building_filename_dialog (impl, error);
7298 *is_well_formed_ret = FALSE;
7305 *is_well_formed_ret = TRUE;
7306 *is_folder = _gtk_file_chooser_entry_get_is_folder (chooser_entry, file);
7309 struct get_files_closure {
7310 GtkFileChooserDefault *impl;
7312 GFile *file_from_entry;
7316 get_files_foreach (GtkTreeModel *model,
7321 struct get_files_closure *info;
7323 GtkFileSystemModel *fs_model;
7326 fs_model = info->impl->browse_files_model;
7328 file = _gtk_file_system_model_get_file (fs_model, iter);
7330 return; /* We are on the editable row */
7332 if (!info->file_from_entry || !g_file_equal (info->file_from_entry, file))
7333 info->result = g_slist_prepend (info->result, g_object_ref (file));
7337 gtk_file_chooser_default_get_files (GtkFileChooser *chooser)
7339 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7340 struct get_files_closure info;
7341 GtkWindow *toplevel;
7342 GtkWidget *current_focus;
7343 gboolean file_list_seen;
7345 if (impl->operation_mode == OPERATION_MODE_SEARCH)
7346 return search_get_selected_files (impl);
7348 if (impl->operation_mode == OPERATION_MODE_RECENT)
7349 return recent_get_selected_files (impl);
7353 info.file_from_entry = NULL;
7355 toplevel = get_toplevel (GTK_WIDGET (impl));
7357 current_focus = gtk_window_get_focus (toplevel);
7359 current_focus = NULL;
7361 file_list_seen = FALSE;
7362 if (current_focus == impl->browse_files_tree_view)
7364 GtkTreeSelection *selection;
7368 file_list_seen = TRUE;
7369 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7370 gtk_tree_selection_selected_foreach (selection, get_files_foreach, &info);
7372 /* If there is no selection in the file list, we probably have this situation:
7374 * 1. The user typed a filename in the SAVE filename entry ("foo.txt").
7375 * 2. He then double-clicked on a folder ("bar") in the file list
7377 * So we want the selection to be "bar/foo.txt". Jump to the case for the
7378 * filename entry to see if that is the case.
7380 if (info.result == NULL && impl->location_entry)
7383 else if (impl->location_entry && current_focus == impl->location_entry)
7385 gboolean is_well_formed, is_empty, is_file_part_empty, is_folder;
7389 check_save_entry (impl, &info.file_from_entry, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder);
7394 if (!is_well_formed)
7397 if (is_file_part_empty && impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
7399 g_object_unref (info.file_from_entry);
7403 if (info.file_from_entry)
7404 info.result = g_slist_prepend (info.result, info.file_from_entry);
7405 else if (!file_list_seen)
7410 else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
7412 else if (impl->location_entry && impl->toplevel_last_focus_widget == impl->location_entry)
7416 /* The focus is on a dialog's action area button or something else */
7417 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
7418 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
7426 /* If there's no folder selected, and we're in SELECT_FOLDER mode, then we
7427 * fall back to the current directory */
7428 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
7429 info.result == NULL)
7431 GFile *current_folder;
7433 current_folder = gtk_file_chooser_get_current_folder_file (chooser);
7436 info.result = g_slist_prepend (info.result, current_folder);
7439 return g_slist_reverse (info.result);
7443 gtk_file_chooser_default_get_preview_file (GtkFileChooser *chooser)
7445 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7447 if (impl->preview_file)
7448 return g_object_ref (impl->preview_file);
7453 static GtkFileSystem *
7454 gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser)
7456 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7458 return impl->file_system;
7461 /* Shows or hides the filter widgets */
7463 show_filters (GtkFileChooserDefault *impl,
7467 gtk_widget_show (impl->filter_combo_hbox);
7469 gtk_widget_hide (impl->filter_combo_hbox);
7473 gtk_file_chooser_default_add_filter (GtkFileChooser *chooser,
7474 GtkFileFilter *filter)
7476 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7479 if (g_slist_find (impl->filters, filter))
7481 g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n");
7485 g_object_ref_sink (filter);
7486 impl->filters = g_slist_append (impl->filters, filter);
7488 name = gtk_file_filter_get_name (filter);
7490 name = "Untitled filter"; /* Place-holder, doesn't need to be marked for translation */
7492 gtk_combo_box_append_text (GTK_COMBO_BOX (impl->filter_combo), name);
7494 if (!g_slist_find (impl->filters, impl->current_filter))
7495 set_current_filter (impl, filter);
7497 show_filters (impl, TRUE);
7501 gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser,
7502 GtkFileFilter *filter)
7504 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7505 GtkTreeModel *model;
7509 filter_index = g_slist_index (impl->filters, filter);
7511 if (filter_index < 0)
7513 g_warning ("gtk_file_chooser_remove_filter() called on filter not in list\n");
7517 impl->filters = g_slist_remove (impl->filters, filter);
7519 if (filter == impl->current_filter)
7522 set_current_filter (impl, impl->filters->data);
7524 set_current_filter (impl, NULL);
7527 /* Remove row from the combo box */
7528 model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->filter_combo));
7529 if (!gtk_tree_model_iter_nth_child (model, &iter, NULL, filter_index))
7530 g_assert_not_reached ();
7532 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
7534 g_object_unref (filter);
7537 show_filters (impl, FALSE);
7541 gtk_file_chooser_default_list_filters (GtkFileChooser *chooser)
7543 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7545 return g_slist_copy (impl->filters);
7548 /* Returns the position in the shortcuts tree where the nth specified shortcut would appear */
7550 shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl,
7553 return pos + shortcuts_get_index (impl, SHORTCUTS_SHORTCUTS);
7556 struct AddShortcutData
7558 GtkFileChooserDefault *impl;
7563 add_shortcut_get_info_cb (GCancellable *cancellable,
7565 const GError *error,
7569 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
7570 struct AddShortcutData *data = user_data;
7572 if (!g_slist_find (data->impl->loading_shortcuts, cancellable))
7575 data->impl->loading_shortcuts = g_slist_remove (data->impl->loading_shortcuts, cancellable);
7577 if (cancelled || error || (! _gtk_file_info_consider_as_directory (info)))
7580 pos = shortcuts_get_pos_for_shortcut_folder (data->impl, data->impl->num_shortcuts);
7582 shortcuts_insert_file (data->impl, pos, SHORTCUT_TYPE_FILE, NULL, data->file, NULL, FALSE, SHORTCUTS_SHORTCUTS);
7585 g_object_unref (data->impl);
7586 g_object_unref (data->file);
7589 g_object_unref (cancellable);
7593 gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser *chooser,
7597 GCancellable *cancellable;
7598 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7599 struct AddShortcutData *data;
7603 /* Avoid adding duplicates */
7604 pos = shortcut_find_position (impl, file);
7605 if (pos >= 0 && pos < shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR))
7609 uri = g_file_get_uri (file);
7610 /* translators, "Shortcut" means "Bookmark" here */
7612 GTK_FILE_CHOOSER_ERROR,
7613 GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
7614 _("Shortcut %s already exists"),
7621 for (l = impl->loading_shortcuts; l; l = l->next)
7623 GCancellable *c = l->data;
7626 f = g_object_get_data (G_OBJECT (c), "add-shortcut-path-key");
7627 if (f && g_file_equal (file, f))
7631 uri = g_file_get_uri (file);
7633 GTK_FILE_CHOOSER_ERROR,
7634 GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
7635 _("Shortcut %s already exists"),
7643 data = g_new0 (struct AddShortcutData, 1);
7644 data->impl = g_object_ref (impl);
7645 data->file = g_object_ref (file);
7647 cancellable = _gtk_file_system_get_info (impl->file_system, file,
7649 add_shortcut_get_info_cb, data);
7654 impl->loading_shortcuts = g_slist_append (impl->loading_shortcuts, cancellable);
7655 g_object_set_data (G_OBJECT (cancellable), "add-shortcut-path-key", data->file);
7661 gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser *chooser,
7665 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7672 for (l = impl->loading_shortcuts; l; l = l->next)
7674 GCancellable *c = l->data;
7677 f = g_object_get_data (G_OBJECT (c), "add-shortcut-path-key");
7678 if (f && g_file_equal (file, f))
7680 impl->loading_shortcuts = g_slist_remove (impl->loading_shortcuts, c);
7681 g_cancellable_cancel (c);
7686 if (impl->num_shortcuts == 0)
7689 pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
7690 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
7691 g_assert_not_reached ();
7693 for (i = 0; i < impl->num_shortcuts; i++)
7696 ShortcutType shortcut_type;
7699 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
7700 SHORTCUTS_COL_DATA, &col_data,
7701 SHORTCUTS_COL_TYPE, &shortcut_type,
7703 g_assert (col_data != NULL);
7704 g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
7706 shortcut = col_data;
7707 if (g_file_equal (shortcut, file))
7709 shortcuts_remove_rows (impl, pos + i, 1);
7710 impl->num_shortcuts--;
7714 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
7715 g_assert_not_reached ();
7720 uri = g_file_get_uri (file);
7721 /* translators, "Shortcut" means "Bookmark" here */
7723 GTK_FILE_CHOOSER_ERROR,
7724 GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
7725 _("Shortcut %s does not exist"),
7733 gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser)
7735 GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
7741 if (impl->num_shortcuts == 0)
7744 pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
7745 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
7746 g_assert_not_reached ();
7750 for (i = 0; i < impl->num_shortcuts; i++)
7753 ShortcutType shortcut_type;
7756 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
7757 SHORTCUTS_COL_DATA, &col_data,
7758 SHORTCUTS_COL_TYPE, &shortcut_type,
7760 g_assert (col_data != NULL);
7761 g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
7763 shortcut = col_data;
7764 list = g_slist_prepend (list, g_object_ref (shortcut));
7766 if (i != impl->num_shortcuts - 1)
7768 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
7769 g_assert_not_reached ();
7773 return g_slist_reverse (list);
7776 /* Guesses a size based upon font sizes */
7778 find_good_size_from_style (GtkWidget *widget,
7782 GtkFileChooserDefault *impl;
7788 style = gtk_widget_get_style (widget);
7790 g_assert (style != NULL);
7791 impl = GTK_FILE_CHOOSER_DEFAULT (widget);
7793 screen = gtk_widget_get_screen (widget);
7796 resolution = gdk_screen_get_resolution (screen);
7797 if (resolution < 0.0) /* will be -1 if the resolution is not defined in the GdkScreen */
7801 resolution = 96.0; /* wheeee */
7803 font_size = pango_font_description_get_size (style->font_desc);
7804 font_size = PANGO_PIXELS (font_size) * resolution / 72.0;
7806 *width = font_size * NUM_CHARS;
7807 *height = font_size * NUM_LINES;
7811 gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed,
7812 gint *default_width,
7813 gint *default_height)
7815 GtkFileChooserDefault *impl;
7818 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
7820 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
7821 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
7822 || impl->expand_folders)
7824 GtkFileChooserSettings *settings;
7825 int x, y, width, height;
7827 settings = _gtk_file_chooser_settings_new ();
7828 _gtk_file_chooser_settings_get_geometry (settings, &x, &y, &width, &height);
7829 g_object_unref (settings);
7831 if (x >= 0 && y >= 0 && width > 0 && height > 0)
7833 *default_width = width;
7834 *default_height = height;
7838 find_good_size_from_style (GTK_WIDGET (chooser_embed), default_width, default_height);
7840 if (impl->preview_widget_active &&
7841 impl->preview_widget &&
7842 gtk_widget_get_visible (impl->preview_widget))
7844 gtk_widget_size_request (impl->preview_box, &req);
7845 *default_width += PREVIEW_HBOX_SPACING + req.width;
7848 if (impl->extra_widget &&
7849 gtk_widget_get_visible (impl->extra_widget))
7851 gtk_widget_size_request (impl->extra_align, &req);
7852 *default_height += gtk_box_get_spacing (GTK_BOX (chooser_embed)) + req.height;
7857 gtk_widget_size_request (GTK_WIDGET (impl), &req);
7858 *default_width = req.width;
7859 *default_height = req.height;
7863 struct switch_folder_closure {
7864 GtkFileChooserDefault *impl;
7869 /* Used from gtk_tree_selection_selected_foreach() in switch_to_selected_folder() */
7871 switch_folder_foreach_cb (GtkTreeModel *model,
7876 struct switch_folder_closure *closure;
7880 closure->file = _gtk_file_system_model_get_file (closure->impl->browse_files_model, iter);
7881 closure->num_selected++;
7884 /* Changes to the selected folder in the list view */
7886 switch_to_selected_folder (GtkFileChooserDefault *impl)
7888 GtkTreeSelection *selection;
7889 struct switch_folder_closure closure;
7891 /* We do this with foreach() rather than get_selected() as we may be in
7892 * multiple selection mode
7895 closure.impl = impl;
7896 closure.file = NULL;
7897 closure.num_selected = 0;
7899 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7900 gtk_tree_selection_selected_foreach (selection, switch_folder_foreach_cb, &closure);
7902 g_assert (closure.file && closure.num_selected == 1);
7904 change_folder_and_display_error (impl, closure.file, FALSE);
7907 /* Gets the GFileInfo for the selected row in the file list; assumes single
7911 get_selected_file_info_from_file_list (GtkFileChooserDefault *impl,
7912 gboolean *had_selection)
7914 GtkTreeSelection *selection;
7918 g_assert (!impl->select_multiple);
7919 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
7920 if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
7922 *had_selection = FALSE;
7926 *had_selection = TRUE;
7928 info = _gtk_file_system_model_get_info (impl->browse_files_model, &iter);
7932 /* Gets the display name of the selected file in the file list; assumes single
7933 * selection mode and that something is selected.
7935 static const gchar *
7936 get_display_name_from_file_list (GtkFileChooserDefault *impl)
7939 gboolean had_selection;
7941 info = get_selected_file_info_from_file_list (impl, &had_selection);
7942 g_assert (had_selection);
7943 g_assert (info != NULL);
7945 return g_file_info_get_display_name (info);
7949 add_custom_button_to_dialog (GtkDialog *dialog,
7950 const gchar *mnemonic_label,
7951 const gchar *stock_id,
7956 button = gtk_button_new_with_mnemonic (mnemonic_label);
7957 gtk_widget_set_can_default (button, TRUE);
7958 gtk_button_set_image (GTK_BUTTON (button),
7959 gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON));
7960 gtk_widget_show (button);
7962 gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, response_id);
7965 /* Presents an overwrite confirmation dialog; returns whether we should accept
7969 confirm_dialog_should_accept_filename (GtkFileChooserDefault *impl,
7970 const gchar *file_part,
7971 const gchar *folder_display_name)
7973 GtkWindow *toplevel;
7977 toplevel = get_toplevel (GTK_WIDGET (impl));
7979 dialog = gtk_message_dialog_new (toplevel,
7980 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
7981 GTK_MESSAGE_QUESTION,
7983 _("A file named \"%s\" already exists. Do you want to replace it?"),
7985 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
7986 _("The file already exists in \"%s\". Replacing it will "
7987 "overwrite its contents."),
7988 folder_display_name);
7990 gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
7991 add_custom_button_to_dialog (GTK_DIALOG (dialog), _("_Replace"),
7992 GTK_STOCK_SAVE_AS, GTK_RESPONSE_ACCEPT);
7993 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
7994 GTK_RESPONSE_ACCEPT,
7995 GTK_RESPONSE_CANCEL,
7997 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
7999 if (toplevel->group)
8000 gtk_window_group_add_window (toplevel->group, GTK_WINDOW (dialog));
8002 response = gtk_dialog_run (GTK_DIALOG (dialog));
8004 gtk_widget_destroy (dialog);
8006 return (response == GTK_RESPONSE_ACCEPT);
8009 struct GetDisplayNameData
8011 GtkFileChooserDefault *impl;
8016 confirmation_confirm_get_info_cb (GCancellable *cancellable,
8018 const GError *error,
8021 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
8022 gboolean should_respond = FALSE;
8023 struct GetDisplayNameData *data = user_data;
8025 if (cancellable != data->impl->should_respond_get_info_cancellable)
8028 data->impl->should_respond_get_info_cancellable = NULL;
8034 /* Huh? Did the folder disappear? Let the caller deal with it */
8035 should_respond = TRUE;
8037 should_respond = confirm_dialog_should_accept_filename (data->impl, data->file_part, g_file_info_get_display_name (info));
8039 set_busy_cursor (data->impl, FALSE);
8041 g_signal_emit_by_name (data->impl, "response-requested");
8044 g_object_unref (data->impl);
8045 g_free (data->file_part);
8048 g_object_unref (cancellable);
8051 /* Does overwrite confirmation if appropriate, and returns whether the dialog
8052 * should respond. Can get the file part from the file list or the save entry.
8055 should_respond_after_confirm_overwrite (GtkFileChooserDefault *impl,
8056 const gchar *file_part,
8059 GtkFileChooserConfirmation conf;
8061 if (!impl->do_overwrite_confirmation)
8064 conf = GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM;
8066 g_signal_emit_by_name (impl, "confirm-overwrite", &conf);
8070 case GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM:
8072 struct GetDisplayNameData *data;
8074 g_assert (file_part != NULL);
8076 data = g_new0 (struct GetDisplayNameData, 1);
8077 data->impl = g_object_ref (impl);
8078 data->file_part = g_strdup (file_part);
8080 if (impl->should_respond_get_info_cancellable)
8081 g_cancellable_cancel (impl->should_respond_get_info_cancellable);
8083 impl->should_respond_get_info_cancellable =
8084 _gtk_file_system_get_info (impl->file_system, parent_file,
8085 "standard::display-name",
8086 confirmation_confirm_get_info_cb,
8088 set_busy_cursor (data->impl, TRUE);
8092 case GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME:
8095 case GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN:
8099 g_assert_not_reached ();
8104 struct FileExistsData
8106 GtkFileChooserDefault *impl;
8107 gboolean file_exists_and_is_not_folder;
8113 save_entry_get_info_cb (GCancellable *cancellable,
8115 const GError *error,
8118 gboolean parent_is_folder;
8119 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
8120 struct FileExistsData *data = user_data;
8122 if (cancellable != data->impl->should_respond_get_info_cancellable)
8125 data->impl->should_respond_get_info_cancellable = NULL;
8127 set_busy_cursor (data->impl, FALSE);
8133 parent_is_folder = FALSE;
8135 parent_is_folder = _gtk_file_info_consider_as_directory (info);
8137 if (parent_is_folder)
8139 if (data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8141 if (data->file_exists_and_is_not_folder)
8146 /* Dup the string because the string may be modified
8147 * depending on what clients do in the confirm-overwrite
8148 * signal and this corrupts the pointer
8150 file_part = g_strdup (_gtk_file_chooser_entry_get_file_part (GTK_FILE_CHOOSER_ENTRY (data->impl->location_entry)));
8151 retval = should_respond_after_confirm_overwrite (data->impl, file_part, data->parent_file);
8155 g_signal_emit_by_name (data->impl, "response-requested");
8158 g_signal_emit_by_name (data->impl, "response-requested");
8160 else /* GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER */
8162 GError *error = NULL;
8164 set_busy_cursor (data->impl, TRUE);
8165 g_file_make_directory (data->file, NULL, &error);
8166 set_busy_cursor (data->impl, FALSE);
8169 g_signal_emit_by_name (data->impl, "response-requested");
8171 error_creating_folder_dialog (data->impl, data->file, error);
8176 /* This will display an error, which is what we want */
8177 change_folder_and_display_error (data->impl, data->parent_file, FALSE);
8181 g_object_unref (data->impl);
8182 g_object_unref (data->file);
8183 g_object_unref (data->parent_file);
8186 g_object_unref (cancellable);
8190 file_exists_get_info_cb (GCancellable *cancellable,
8192 const GError *error,
8195 gboolean data_ownership_taken = FALSE;
8196 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
8197 gboolean file_exists_and_is_not_folder;
8198 struct FileExistsData *data = user_data;
8200 if (cancellable != data->impl->file_exists_get_info_cancellable)
8203 data->impl->file_exists_get_info_cancellable = NULL;
8205 set_busy_cursor (data->impl, FALSE);
8210 file_exists_and_is_not_folder = info && (! _gtk_file_info_consider_as_directory (info));
8212 if (data->impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
8213 /* user typed a filename; we are done */
8214 g_signal_emit_by_name (data->impl, "response-requested");
8215 else if (data->impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
8216 && file_exists_and_is_not_folder)
8218 /* Oops, the user typed the name of an existing path which is not
8221 error_creating_folder_over_existing_file_dialog (data->impl, data->file,
8222 g_error_copy (error));
8226 /* check that everything up to the last component exists */
8228 data->file_exists_and_is_not_folder = file_exists_and_is_not_folder;
8229 data_ownership_taken = TRUE;
8231 if (data->impl->should_respond_get_info_cancellable)
8232 g_cancellable_cancel (data->impl->should_respond_get_info_cancellable);
8234 data->impl->should_respond_get_info_cancellable =
8235 _gtk_file_system_get_info (data->impl->file_system,
8238 save_entry_get_info_cb,
8240 set_busy_cursor (data->impl, TRUE);
8244 if (!data_ownership_taken)
8246 g_object_unref (data->impl);
8247 g_object_unref (data->file);
8248 g_object_unref (data->parent_file);
8252 g_object_unref (cancellable);
8256 paste_text_received (GtkClipboard *clipboard,
8258 GtkFileChooserDefault *impl)
8265 file = g_file_new_for_uri (text);
8267 if (!gtk_file_chooser_default_select_file (GTK_FILE_CHOOSER (impl), file, NULL))
8268 location_popup_handler (impl, text);
8270 g_object_unref (file);
8273 /* Handler for the "location-popup-on-paste" keybinding signal */
8275 location_popup_on_paste_handler (GtkFileChooserDefault *impl)
8277 GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (impl),
8278 GDK_SELECTION_CLIPBOARD);
8279 gtk_clipboard_request_text (clipboard,
8280 (GtkClipboardTextReceivedFunc) paste_text_received,
8285 /* Implementation for GtkFileChooserEmbed::should_respond() */
8287 gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
8289 GtkFileChooserDefault *impl;
8290 GtkWidget *toplevel;
8291 GtkWidget *current_focus;
8293 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
8295 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
8296 g_assert (GTK_IS_WINDOW (toplevel));
8298 current_focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
8300 if (current_focus == impl->browse_files_tree_view)
8302 /* The following array encodes what we do based on the impl->action and the
8303 * number of files selected.
8306 NOOP, /* Do nothing (don't respond) */
8307 RESPOND, /* Respond immediately */
8308 RESPOND_OR_SWITCH, /* Respond immediately if the selected item is a file; switch to it if it is a folder */
8309 ALL_FILES, /* Respond only if everything selected is a file */
8310 ALL_FOLDERS, /* Respond only if everything selected is a folder */
8311 SAVE_ENTRY, /* Go to the code for handling the save entry */
8312 NOT_REACHED /* Sanity check */
8314 static const ActionToTake what_to_do[4][3] = {
8315 /* 0 selected 1 selected many selected */
8316 /* ACTION_OPEN */ { NOOP, RESPOND_OR_SWITCH, ALL_FILES },
8317 /* ACTION_SAVE */ { SAVE_ENTRY, RESPOND_OR_SWITCH, NOT_REACHED },
8318 /* ACTION_SELECT_FOLDER */ { RESPOND, ALL_FOLDERS, ALL_FOLDERS },
8319 /* ACTION_CREATE_FOLDER */ { SAVE_ENTRY, ALL_FOLDERS, NOT_REACHED }
8323 gboolean all_files, all_folders;
8325 ActionToTake action;
8329 g_assert (impl->action >= GTK_FILE_CHOOSER_ACTION_OPEN && impl->action <= GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
8331 if (impl->operation_mode == OPERATION_MODE_SEARCH)
8332 return search_should_respond (impl);
8334 if (impl->operation_mode == OPERATION_MODE_RECENT)
8335 return recent_should_respond (impl);
8337 selection_check (impl, &num_selected, &all_files, &all_folders);
8339 if (num_selected > 2)
8344 action = what_to_do [impl->action] [k];
8354 case RESPOND_OR_SWITCH:
8355 g_assert (num_selected == 1);
8359 switch_to_selected_folder (impl);
8362 else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8363 return should_respond_after_confirm_overwrite (impl,
8364 get_display_name_from_file_list (impl),
8365 impl->current_folder);
8379 g_assert_not_reached ();
8382 else if ((impl->location_entry != NULL) && (current_focus == impl->location_entry))
8385 gboolean is_well_formed, is_empty, is_file_part_empty;
8388 GtkFileChooserEntry *entry;
8393 g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
8394 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
8395 || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
8396 || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8397 && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY));
8399 entry = GTK_FILE_CHOOSER_ENTRY (impl->location_entry);
8400 check_save_entry (impl, &file, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder);
8402 if (!is_well_formed)
8407 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8413 g_assert (file != NULL);
8418 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
8419 impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
8421 change_folder_and_display_error (impl, file, TRUE);
8424 else if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
8425 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8427 /* The folder already exists, so we do not need to create it.
8428 * Just respond to terminate the dialog.
8434 g_assert_not_reached ();
8440 struct FileExistsData *data;
8442 /* We need to check whether file exists and is not a folder */
8444 data = g_new0 (struct FileExistsData, 1);
8445 data->impl = g_object_ref (impl);
8446 data->file = g_object_ref (file);
8447 data->parent_file = g_object_ref (_gtk_file_chooser_entry_get_current_folder (entry));
8449 if (impl->file_exists_get_info_cancellable)
8450 g_cancellable_cancel (impl->file_exists_get_info_cancellable);
8452 impl->file_exists_get_info_cancellable =
8453 _gtk_file_system_get_info (impl->file_system, file,
8455 file_exists_get_info_cb,
8458 set_busy_cursor (impl, TRUE);
8462 g_error_free (error);
8465 g_object_unref (file);
8468 else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
8470 /* The focus is on a dialog's action area button, *and* the widget that
8471 * was focused immediately before it is the file list.
8475 else if (impl->operation_mode == OPERATION_MODE_SEARCH && impl->toplevel_last_focus_widget == impl->search_entry)
8477 search_entry_activate_cb (GTK_ENTRY (impl->search_entry), impl);
8480 else if (impl->location_entry && impl->toplevel_last_focus_widget == impl->location_entry)
8482 /* The focus is on a dialog's action area button, *and* the widget that
8483 * was focused immediately before it is the location entry.
8488 /* The focus is on a dialog's action area button or something else */
8489 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
8490 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8495 g_assert_not_reached ();
8499 /* Implementation for GtkFileChooserEmbed::initial_focus() */
8501 gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed)
8503 GtkFileChooserDefault *impl;
8506 impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
8508 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
8509 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8511 if (impl->location_mode == LOCATION_MODE_PATH_BAR)
8512 widget = impl->browse_files_tree_view;
8514 widget = impl->location_entry;
8516 else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
8517 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
8518 widget = impl->location_entry;
8521 g_assert_not_reached ();
8525 g_assert (widget != NULL);
8526 gtk_widget_grab_focus (widget);
8529 /* Callback used from gtk_tree_selection_selected_foreach(); gets the selected GFiles */
8531 search_selected_foreach_get_file_cb (GtkTreeModel *model,
8541 gtk_tree_model_get (model, iter, MODEL_COL_FILE, &file, -1);
8542 *list = g_slist_prepend (*list, g_object_ref (file));
8545 /* Constructs a list of the selected paths in search mode */
8547 search_get_selected_files (GtkFileChooserDefault *impl)
8550 GtkTreeSelection *selection;
8554 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
8555 gtk_tree_selection_selected_foreach (selection, search_selected_foreach_get_file_cb, &result);
8556 result = g_slist_reverse (result);
8561 /* Called from ::should_respond(). We return whether there are selected files
8562 * in the search list.
8565 search_should_respond (GtkFileChooserDefault *impl)
8567 GtkTreeSelection *selection;
8569 g_assert (impl->operation_mode == OPERATION_MODE_SEARCH);
8571 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
8572 return (gtk_tree_selection_count_selected_rows (selection) != 0);
8575 /* Adds one hit from the search engine to the search_model */
8577 search_add_hit (GtkFileChooserDefault *impl,
8582 file = g_file_new_for_uri (uri);
8586 if (!g_file_is_native (file))
8588 g_object_unref (file);
8592 _gtk_file_system_model_add_and_query_file (impl->search_model,
8596 g_object_unref (file);
8599 /* Callback used from GtkSearchEngine when we get new hits */
8601 search_engine_hits_added_cb (GtkSearchEngine *engine,
8605 GtkFileChooserDefault *impl;
8608 impl = GTK_FILE_CHOOSER_DEFAULT (data);
8610 for (l = hits; l; l = l->next)
8611 search_add_hit (impl, (gchar*)l->data);
8614 /* Callback used from GtkSearchEngine when the query is done running */
8616 search_engine_finished_cb (GtkSearchEngine *engine,
8619 GtkFileChooserDefault *impl;
8621 impl = GTK_FILE_CHOOSER_DEFAULT (data);
8624 /* EB: setting the model here will avoid loads of row events,
8625 * but it'll make the search look like blocked.
8627 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
8628 GTK_TREE_MODEL (impl->search_model));
8631 /* FMQ: if search was empty, say that we got no hits */
8632 set_busy_cursor (impl, FALSE);
8635 /* Displays a generic error when we cannot create a GtkSearchEngine.
8636 * It would be better if _gtk_search_engine_new() gave us a GError
8637 * with a better message, but it doesn't do that right now.
8640 search_error_could_not_create_client (GtkFileChooserDefault *impl)
8642 error_message (impl,
8643 _("Could not start the search process"),
8644 _("The program was not able to create a connection to the indexer "
8645 "daemon. Please make sure it is running."));
8649 search_engine_error_cb (GtkSearchEngine *engine,
8650 const gchar *message,
8653 GtkFileChooserDefault *impl;
8655 impl = GTK_FILE_CHOOSER_DEFAULT (data);
8657 search_stop_searching (impl, TRUE);
8658 error_message (impl, _("Could not send the search request"), message);
8660 set_busy_cursor (impl, FALSE);
8663 /* Frees the data in the search_model */
8665 search_clear_model (GtkFileChooserDefault *impl,
8666 gboolean remove_from_treeview)
8668 if (!impl->search_model)
8671 g_object_unref (impl->search_model);
8672 impl->search_model = NULL;
8674 if (remove_from_treeview)
8675 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
8678 /* Stops any ongoing searches; does not touch the search_model */
8680 search_stop_searching (GtkFileChooserDefault *impl,
8681 gboolean remove_query)
8683 if (remove_query && impl->search_query)
8685 g_object_unref (impl->search_query);
8686 impl->search_query = NULL;
8689 if (impl->search_engine)
8691 _gtk_search_engine_stop (impl->search_engine);
8693 g_object_unref (impl->search_engine);
8694 impl->search_engine = NULL;
8698 /* Stops any pending searches, clears the file list, and switches back to OPERATION_MODE_BROWSE */
8700 search_switch_to_browse_mode (GtkFileChooserDefault *impl)
8702 g_assert (impl->operation_mode != OPERATION_MODE_BROWSE);
8704 search_stop_searching (impl, FALSE);
8705 search_clear_model (impl, TRUE);
8707 gtk_widget_destroy (impl->search_hbox);
8708 impl->search_hbox = NULL;
8709 impl->search_entry = NULL;
8711 gtk_widget_show (impl->browse_path_bar);
8712 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN || !impl->create_folders)
8713 gtk_widget_hide (impl->browse_new_folder_button);
8715 gtk_widget_show (impl->browse_new_folder_button);
8718 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
8719 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8721 gtk_widget_show (impl->location_button);
8723 if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
8724 gtk_widget_show (impl->location_entry_box);
8727 impl->operation_mode = OPERATION_MODE_BROWSE;
8729 file_list_set_sort_column_ids (impl);
8732 /* Creates the search_model and puts it in the tree view */
8734 search_setup_model (GtkFileChooserDefault *impl)
8736 g_assert (impl->search_model == NULL);
8738 impl->search_model = _gtk_file_system_model_new (file_system_model_set,
8740 MODEL_COLUMN_TYPES);
8742 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model),
8746 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model),
8750 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model),
8754 set_sort_column (impl);
8756 /* EB: setting the model here will make the hits list update feel
8757 * more "alive" than setting the model at the end of the search
8760 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
8761 GTK_TREE_MODEL (impl->search_model));
8764 /* Creates a new query with the specified text and launches it */
8766 search_start_query (GtkFileChooserDefault *impl,
8767 const gchar *query_text)
8769 search_stop_searching (impl, FALSE);
8770 search_clear_model (impl, TRUE);
8771 search_setup_model (impl);
8772 set_busy_cursor (impl, TRUE);
8774 if (impl->search_engine == NULL)
8775 impl->search_engine = _gtk_search_engine_new ();
8777 if (!impl->search_engine)
8779 set_busy_cursor (impl, FALSE);
8780 search_error_could_not_create_client (impl); /* lame; we don't get an error code or anything */
8784 if (!impl->search_query)
8786 impl->search_query = _gtk_query_new ();
8787 _gtk_query_set_text (impl->search_query, query_text);
8790 _gtk_search_engine_set_query (impl->search_engine, impl->search_query);
8792 g_signal_connect (impl->search_engine, "hits-added",
8793 G_CALLBACK (search_engine_hits_added_cb), impl);
8794 g_signal_connect (impl->search_engine, "finished",
8795 G_CALLBACK (search_engine_finished_cb), impl);
8796 g_signal_connect (impl->search_engine, "error",
8797 G_CALLBACK (search_engine_error_cb), impl);
8799 _gtk_search_engine_start (impl->search_engine);
8802 /* Callback used when the user presses Enter while typing on the search
8803 * entry; starts the query
8806 search_entry_activate_cb (GtkEntry *entry,
8809 GtkFileChooserDefault *impl;
8812 impl = GTK_FILE_CHOOSER_DEFAULT (data);
8814 text = gtk_entry_get_text (GTK_ENTRY (impl->search_entry));
8815 if (strlen (text) == 0)
8818 /* reset any existing query object */
8819 if (impl->search_query)
8821 g_object_unref (impl->search_query);
8822 impl->search_query = NULL;
8825 search_start_query (impl, text);
8828 /* Hides the path bar and creates the search entry */
8830 search_setup_widgets (GtkFileChooserDefault *impl)
8836 impl->search_hbox = gtk_hbox_new (FALSE, 12);
8840 image = gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_BUTTON);
8841 gtk_size_group_add_widget (GTK_SIZE_GROUP (impl->browse_path_bar_size_group), image);
8842 gtk_box_pack_start (GTK_BOX (impl->search_hbox), image, FALSE, FALSE, 5);
8846 label = gtk_label_new (NULL);
8847 tmp = g_strdup_printf ("<b>%s</b>", _("Search:"));
8848 gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), tmp);
8849 gtk_box_pack_start (GTK_BOX (impl->search_hbox), label, FALSE, FALSE, 0);
8854 impl->search_entry = gtk_entry_new ();
8855 gtk_label_set_mnemonic_widget (GTK_LABEL (label), impl->search_entry);
8856 g_signal_connect (impl->search_entry, "activate",
8857 G_CALLBACK (search_entry_activate_cb),
8859 gtk_box_pack_start (GTK_BOX (impl->search_hbox), impl->search_entry, TRUE, TRUE, 0);
8861 /* if there already is a query, restart it */
8862 if (impl->search_query)
8864 gchar *query = _gtk_query_get_text (impl->search_query);
8868 gtk_entry_set_text (GTK_ENTRY (impl->search_entry), query);
8869 search_start_query (impl, query);
8875 g_object_unref (impl->search_query);
8876 impl->search_query = NULL;
8880 gtk_widget_hide (impl->browse_path_bar);
8881 gtk_widget_hide (impl->browse_new_folder_button);
8883 /* Box for search widgets */
8884 gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->search_hbox, TRUE, TRUE, 0);
8885 gtk_widget_show_all (impl->search_hbox);
8887 /* Hide the location widgets temporarily */
8889 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
8890 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
8892 gtk_widget_hide (impl->location_button);
8893 gtk_widget_hide (impl->location_entry_box);
8896 gtk_widget_grab_focus (impl->search_entry);
8898 /* FMQ: hide the filter combo? */
8901 /* Stops running operations like populating the browse model, searches, and the recent-files model */
8903 stop_operation (GtkFileChooserDefault *impl, OperationMode mode)
8907 case OPERATION_MODE_BROWSE:
8908 stop_loading_and_clear_list_model (impl, TRUE);
8911 case OPERATION_MODE_SEARCH:
8912 search_stop_searching (impl, FALSE);
8913 search_clear_model (impl, TRUE);
8915 gtk_widget_destroy (impl->search_hbox);
8916 impl->search_hbox = NULL;
8917 impl->search_entry = NULL;
8920 case OPERATION_MODE_RECENT:
8921 recent_stop_loading (impl);
8922 recent_clear_model (impl, TRUE);
8924 gtk_widget_destroy (impl->recent_hbox);
8925 impl->recent_hbox = NULL;
8930 /* Main entry point to the searching functions; this gets called when the user
8931 * activates the Search shortcut.
8934 search_activate (GtkFileChooserDefault *impl)
8936 OperationMode previous_mode;
8938 if (impl->operation_mode == OPERATION_MODE_SEARCH)
8940 gtk_widget_grab_focus (impl->search_entry);
8944 previous_mode = impl->operation_mode;
8945 impl->operation_mode = OPERATION_MODE_SEARCH;
8947 stop_operation (impl, previous_mode);
8949 g_assert (impl->search_hbox == NULL);
8950 g_assert (impl->search_entry == NULL);
8951 g_assert (impl->search_model == NULL);
8953 search_setup_widgets (impl);
8954 file_list_set_sort_column_ids (impl);
8958 * Recent files support
8961 /* Frees the data in the recent_model */
8963 recent_clear_model (GtkFileChooserDefault *impl,
8964 gboolean remove_from_treeview)
8966 GtkTreeModel *model;
8968 if (!impl->recent_model)
8971 model = GTK_TREE_MODEL (impl->recent_model);
8973 if (remove_from_treeview)
8974 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
8976 g_object_unref (impl->recent_model);
8977 impl->recent_model = NULL;
8980 /* Stops any ongoing loading of the recent files list; does
8981 * not touch the recent_model
8984 recent_stop_loading (GtkFileChooserDefault *impl)
8986 if (impl->load_recent_id)
8988 g_source_remove (impl->load_recent_id);
8989 impl->load_recent_id = 0;
8993 /* Stops any pending load, clears the file list, and switches
8994 * back to OPERATION_MODE_BROWSE
8997 recent_switch_to_browse_mode (GtkFileChooserDefault *impl)
8999 g_assert (impl->operation_mode != OPERATION_MODE_BROWSE);
9001 recent_stop_loading (impl);
9002 recent_clear_model (impl, TRUE);
9004 gtk_widget_destroy (impl->recent_hbox);
9005 impl->recent_hbox = NULL;
9007 gtk_widget_show (impl->browse_path_bar);
9008 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN || !impl->create_folders)
9009 gtk_widget_hide (impl->browse_new_folder_button);
9011 gtk_widget_show (impl->browse_new_folder_button);
9013 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
9014 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
9016 gtk_widget_show (impl->location_button);
9018 if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
9019 gtk_widget_show (impl->location_entry_box);
9022 gtk_tree_view_column_set_visible (impl->list_size_column, impl->show_size_column);
9024 impl->operation_mode = OPERATION_MODE_BROWSE;
9026 file_list_set_sort_column_ids (impl);
9030 recent_setup_model (GtkFileChooserDefault *impl)
9032 g_assert (impl->recent_model == NULL);
9034 impl->recent_model = _gtk_file_system_model_new (file_system_model_set,
9036 MODEL_COLUMN_TYPES);
9038 _gtk_file_system_model_set_filter (impl->recent_model,
9039 impl->current_filter);
9040 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model),
9044 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model),
9048 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model),
9052 set_sort_column (impl);
9057 GtkFileChooserDefault *impl;
9060 gint n_loaded_items;
9061 guint needs_sorting : 1;
9065 recent_idle_cleanup (gpointer data)
9067 RecentLoadData *load_data = data;
9068 GtkFileChooserDefault *impl = load_data->impl;
9070 gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
9071 GTK_TREE_MODEL (impl->recent_model));
9073 set_busy_cursor (impl, FALSE);
9075 impl->load_recent_id = 0;
9077 if (load_data->items)
9079 g_list_foreach (load_data->items, (GFunc) gtk_recent_info_unref, NULL);
9080 g_list_free (load_data->items);
9087 recent_sort_mru (gconstpointer a,
9090 GtkRecentInfo *info_a = (GtkRecentInfo *) a;
9091 GtkRecentInfo *info_b = (GtkRecentInfo *) b;
9093 return (gtk_recent_info_get_modified (info_b) - gtk_recent_info_get_modified (info_a));
9097 get_recent_files_limit (GtkWidget *widget)
9099 GtkSettings *settings;
9102 if (gtk_widget_has_screen (widget))
9103 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (widget));
9105 settings = gtk_settings_get_default ();
9107 g_object_get (G_OBJECT (settings), "gtk-recent-files-limit", &limit, NULL);
9113 recent_idle_load (gpointer data)
9115 RecentLoadData *load_data = data;
9116 GtkFileChooserDefault *impl = load_data->impl;
9120 if (!impl->recent_manager)
9123 /* first iteration: load all the items */
9124 if (!load_data->items)
9126 load_data->items = gtk_recent_manager_get_items (impl->recent_manager);
9127 if (!load_data->items)
9130 load_data->needs_sorting = TRUE;
9135 /* second iteration: preliminary MRU sorting and clamping */
9136 if (load_data->needs_sorting)
9140 load_data->items = g_list_sort (load_data->items, recent_sort_mru);
9141 load_data->n_items = g_list_length (load_data->items);
9143 limit = get_recent_files_limit (GTK_WIDGET (impl));
9145 if (limit != -1 && (load_data->n_items > limit))
9149 clamp = g_list_nth (load_data->items, limit - 1);
9150 if (G_LIKELY (clamp))
9155 g_list_foreach (l, (GFunc) gtk_recent_info_unref, NULL);
9158 load_data->n_items = limit;
9162 load_data->n_loaded_items = 0;
9163 load_data->needs_sorting = FALSE;
9168 /* finished loading items */
9169 for (walk = load_data->items; walk; walk = walk->next)
9171 GtkRecentInfo *info = walk->data;
9172 file = g_file_new_for_uri (gtk_recent_info_get_uri (info));
9174 _gtk_file_system_model_add_and_query_file (impl->recent_model,
9177 gtk_recent_info_unref (walk->data);
9178 g_object_unref (file);
9181 g_list_free (load_data->items);
9182 load_data->items = NULL;
9188 recent_start_loading (GtkFileChooserDefault *impl)
9190 RecentLoadData *load_data;
9192 recent_stop_loading (impl);
9193 recent_clear_model (impl, TRUE);
9194 recent_setup_model (impl);
9195 set_busy_cursor (impl, TRUE);
9197 g_assert (impl->load_recent_id == 0);
9199 load_data = g_new (RecentLoadData, 1);
9200 load_data->impl = impl;
9201 load_data->items = NULL;
9202 load_data->n_items = 0;
9203 load_data->n_loaded_items = 0;
9204 load_data->needs_sorting = TRUE;
9206 /* begin lazy loading the recent files into the model */
9207 impl->load_recent_id = gdk_threads_add_idle_full (G_PRIORITY_HIGH_IDLE + 30,
9210 recent_idle_cleanup);
9214 recent_selected_foreach_get_file_cb (GtkTreeModel *model,
9224 gtk_tree_model_get (model, iter, MODEL_COL_FILE, &file, -1);
9225 *list = g_slist_prepend (*list, file);
9228 /* Constructs a list of the selected paths in recent files mode */
9230 recent_get_selected_files (GtkFileChooserDefault *impl)
9233 GtkTreeSelection *selection;
9237 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
9238 gtk_tree_selection_selected_foreach (selection, recent_selected_foreach_get_file_cb, &result);
9239 result = g_slist_reverse (result);
9244 /* Called from ::should_respond(). We return whether there are selected
9245 * files in the recent files list.
9248 recent_should_respond (GtkFileChooserDefault *impl)
9250 GtkTreeSelection *selection;
9252 g_assert (impl->operation_mode == OPERATION_MODE_RECENT);
9254 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
9255 return (gtk_tree_selection_count_selected_rows (selection) != 0);
9258 /* Hide the location widgets temporarily */
9260 recent_hide_entry (GtkFileChooserDefault *impl)
9266 impl->recent_hbox = gtk_hbox_new (FALSE, 12);
9269 image = gtk_image_new_from_icon_name ("document-open-recent", GTK_ICON_SIZE_BUTTON);
9270 gtk_size_group_add_widget (impl->browse_path_bar_size_group, image);
9271 gtk_box_pack_start (GTK_BOX (impl->recent_hbox), image, FALSE, FALSE, 5);
9274 label = gtk_label_new (NULL);
9275 tmp = g_strdup_printf ("<b>%s</b>", _("Recently Used"));
9276 gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), tmp);
9277 gtk_box_pack_start (GTK_BOX (impl->recent_hbox), label, FALSE, FALSE, 0);
9280 gtk_widget_hide (impl->browse_path_bar);
9281 gtk_widget_hide (impl->browse_new_folder_button);
9283 /* Box for recent widgets */
9284 gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->recent_hbox, TRUE, TRUE, 0);
9285 gtk_widget_show_all (impl->recent_hbox);
9287 /* Hide the location widgets temporarily */
9288 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
9289 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
9291 gtk_widget_hide (impl->location_button);
9292 gtk_widget_hide (impl->location_entry_box);
9296 /* Main entry point to the recent files functions; this gets called when
9297 * the user activates the Recently Used shortcut.
9300 recent_activate (GtkFileChooserDefault *impl)
9302 OperationMode previous_mode;
9304 if (impl->operation_mode == OPERATION_MODE_RECENT)
9307 previous_mode = impl->operation_mode;
9308 impl->operation_mode = OPERATION_MODE_RECENT;
9310 stop_operation (impl, previous_mode);
9312 recent_hide_entry (impl);
9314 file_list_set_sort_column_ids (impl);
9315 recent_start_loading (impl);
9319 set_current_filter (GtkFileChooserDefault *impl,
9320 GtkFileFilter *filter)
9322 if (impl->current_filter != filter)
9326 /* NULL filters are allowed to reset to non-filtered status
9328 filter_index = g_slist_index (impl->filters, filter);
9329 if (impl->filters && filter && filter_index < 0)
9332 if (impl->current_filter)
9333 g_object_unref (impl->current_filter);
9334 impl->current_filter = filter;
9335 if (impl->current_filter)
9337 g_object_ref_sink (impl->current_filter);
9341 gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
9344 if (impl->browse_files_model)
9345 install_list_model_filter (impl);
9347 if (impl->search_model)
9348 _gtk_file_system_model_set_filter (impl->search_model, filter);
9350 if (impl->recent_model)
9351 _gtk_file_system_model_set_filter (impl->recent_model, filter);
9353 g_object_notify (G_OBJECT (impl), "filter");
9358 filter_combo_changed (GtkComboBox *combo_box,
9359 GtkFileChooserDefault *impl)
9361 gint new_index = gtk_combo_box_get_active (combo_box);
9362 GtkFileFilter *new_filter = g_slist_nth_data (impl->filters, new_index);
9364 set_current_filter (impl, new_filter);
9368 check_preview_change (GtkFileChooserDefault *impl)
9370 GtkTreePath *cursor_path;
9372 char *new_display_name;
9373 GtkTreeModel *model;
9375 gtk_tree_view_get_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), &cursor_path, NULL);
9376 model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view));
9381 gtk_tree_model_get_iter (model, &iter, cursor_path);
9382 gtk_tree_model_get (model, &iter,
9383 MODEL_COL_FILE, &new_file,
9384 MODEL_COL_NAME, &new_display_name,
9387 gtk_tree_path_free (cursor_path);
9392 new_display_name = NULL;
9395 if (new_file != impl->preview_file &&
9396 !(new_file && impl->preview_file &&
9397 g_file_equal (new_file, impl->preview_file)))
9399 if (impl->preview_file)
9401 g_object_unref (impl->preview_file);
9402 g_free (impl->preview_display_name);
9407 impl->preview_file = new_file;
9408 impl->preview_display_name = new_display_name;
9412 impl->preview_file = NULL;
9413 impl->preview_display_name = NULL;
9414 g_free (new_display_name);
9417 if (impl->use_preview_label && impl->preview_label)
9418 gtk_label_set_text (GTK_LABEL (impl->preview_label), impl->preview_display_name);
9420 g_signal_emit_by_name (impl, "update-preview");
9425 shortcuts_activate_volume_mount_cb (GCancellable *cancellable,
9426 GtkFileSystemVolume *volume,
9427 const GError *error,
9431 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
9432 GtkFileChooserDefault *impl = data;
9434 if (cancellable != impl->shortcuts_activate_iter_cancellable)
9437 impl->shortcuts_activate_iter_cancellable = NULL;
9439 set_busy_cursor (impl, FALSE);
9446 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED))
9450 name = _gtk_file_system_volume_get_display_name (volume);
9451 msg = g_strdup_printf (_("Could not mount %s"), name);
9453 error_message (impl, msg, error->message);
9462 file = _gtk_file_system_volume_get_root (volume);
9465 change_folder_and_display_error (impl, file, FALSE);
9466 g_object_unref (file);
9470 g_object_unref (impl);
9471 g_object_unref (cancellable);
9475 /* Activates a volume by mounting it if necessary and then switching to its
9479 shortcuts_activate_volume (GtkFileChooserDefault *impl,
9480 GtkFileSystemVolume *volume)
9484 switch (impl->operation_mode)
9486 case OPERATION_MODE_BROWSE:
9488 case OPERATION_MODE_SEARCH:
9489 search_switch_to_browse_mode (impl);
9491 case OPERATION_MODE_RECENT:
9492 recent_switch_to_browse_mode (impl);
9496 /* We ref the file chooser since volume_mount() may run a main loop, and the
9497 * user could close the file chooser window in the meantime.
9499 g_object_ref (impl);
9501 if (!_gtk_file_system_volume_is_mounted (volume))
9503 GMountOperation *mount_op;
9505 set_busy_cursor (impl, TRUE);
9507 mount_op = gtk_mount_operation_new (get_toplevel (GTK_WIDGET (impl)));
9508 impl->shortcuts_activate_iter_cancellable =
9509 _gtk_file_system_mount_volume (impl->file_system, volume, mount_op,
9510 shortcuts_activate_volume_mount_cb,
9511 g_object_ref (impl));
9512 g_object_unref (mount_op);
9516 file = _gtk_file_system_volume_get_root (volume);
9519 change_folder_and_display_error (impl, file, FALSE);
9520 g_object_unref (file);
9524 g_object_unref (impl);
9527 /* Opens the folder or volume at the specified iter in the shortcuts model */
9528 struct ShortcutsActivateData
9530 GtkFileChooserDefault *impl;
9535 shortcuts_activate_get_info_cb (GCancellable *cancellable,
9537 const GError *error,
9540 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
9541 struct ShortcutsActivateData *data = user_data;
9543 if (cancellable != data->impl->shortcuts_activate_iter_cancellable)
9546 data->impl->shortcuts_activate_iter_cancellable = NULL;
9551 if (!error && _gtk_file_info_consider_as_directory (info))
9552 change_folder_and_display_error (data->impl, data->file, FALSE);
9554 gtk_file_chooser_default_select_file (GTK_FILE_CHOOSER (data->impl),
9559 g_object_unref (data->impl);
9560 g_object_unref (data->file);
9563 g_object_unref (cancellable);
9567 shortcuts_activate_mount_enclosing_volume (GCancellable *cancellable,
9568 GtkFileSystemVolume *volume,
9569 const GError *error,
9572 struct ShortcutsActivateData *data = user_data;
9576 error_changing_folder_dialog (data->impl, data->file, g_error_copy (error));
9578 g_object_unref (data->impl);
9579 g_object_unref (data->file);
9585 data->impl->shortcuts_activate_iter_cancellable =
9586 _gtk_file_system_get_info (data->impl->file_system, data->file,
9588 shortcuts_activate_get_info_cb, data);
9591 _gtk_file_system_volume_unref (volume);
9595 shortcuts_activate_iter (GtkFileChooserDefault *impl,
9599 ShortcutType shortcut_type;
9601 if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY
9602 && !(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
9603 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
9604 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
9606 gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
9607 SHORTCUTS_COL_DATA, &col_data,
9608 SHORTCUTS_COL_TYPE, &shortcut_type,
9611 if (impl->shortcuts_activate_iter_cancellable)
9613 g_cancellable_cancel (impl->shortcuts_activate_iter_cancellable);
9614 impl->shortcuts_activate_iter_cancellable = NULL;
9617 if (shortcut_type == SHORTCUT_TYPE_SEPARATOR)
9619 else if (shortcut_type == SHORTCUT_TYPE_VOLUME)
9621 GtkFileSystemVolume *volume;
9625 shortcuts_activate_volume (impl, volume);
9627 else if (shortcut_type == SHORTCUT_TYPE_FILE)
9629 struct ShortcutsActivateData *data;
9630 GtkFileSystemVolume *volume;
9632 volume = _gtk_file_system_get_volume_for_file (impl->file_system, col_data);
9634 data = g_new0 (struct ShortcutsActivateData, 1);
9635 data->impl = g_object_ref (impl);
9636 data->file = g_object_ref (col_data);
9638 if (!volume || !_gtk_file_system_volume_is_mounted (volume))
9640 GMountOperation *mount_operation;
9641 GtkWidget *toplevel;
9643 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
9645 mount_operation = gtk_mount_operation_new (GTK_WINDOW (toplevel));
9647 impl->shortcuts_activate_iter_cancellable =
9648 _gtk_file_system_mount_enclosing_volume (impl->file_system, col_data,
9650 shortcuts_activate_mount_enclosing_volume,
9655 impl->shortcuts_activate_iter_cancellable =
9656 _gtk_file_system_get_info (impl->file_system, data->file,
9658 shortcuts_activate_get_info_cb, data);
9661 else if (shortcut_type == SHORTCUT_TYPE_SEARCH)
9663 search_activate (impl);
9665 else if (shortcut_type == SHORTCUT_TYPE_RECENT)
9667 recent_activate (impl);
9671 /* Handler for GtkWidget::key-press-event on the shortcuts list */
9673 shortcuts_key_press_event_cb (GtkWidget *widget,
9675 GtkFileChooserDefault *impl)
9679 modifiers = gtk_accelerator_get_default_mod_mask ();
9681 if (key_is_left_or_right (event))
9683 gtk_widget_grab_focus (impl->browse_files_tree_view);
9687 if ((event->keyval == GDK_BackSpace
9688 || event->keyval == GDK_Delete
9689 || event->keyval == GDK_KP_Delete)
9690 && (event->state & modifiers) == 0)
9692 remove_selected_bookmarks (impl);
9696 if ((event->keyval == GDK_F2)
9697 && (event->state & modifiers) == 0)
9699 rename_selected_bookmark (impl);
9707 shortcuts_select_func (GtkTreeSelection *selection,
9708 GtkTreeModel *model,
9710 gboolean path_currently_selected,
9713 GtkFileChooserDefault *impl = data;
9714 GtkTreeIter filter_iter;
9715 ShortcutType shortcut_type;
9717 if (!gtk_tree_model_get_iter (impl->shortcuts_pane_filter_model, &filter_iter, path))
9718 g_assert_not_reached ();
9720 gtk_tree_model_get (impl->shortcuts_pane_filter_model, &filter_iter, SHORTCUTS_COL_TYPE, &shortcut_type, -1);
9722 return shortcut_type != SHORTCUT_TYPE_SEPARATOR;
9726 list_select_func (GtkTreeSelection *selection,
9727 GtkTreeModel *model,
9729 gboolean path_currently_selected,
9732 GtkFileChooserDefault *impl = data;
9734 if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
9735 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
9740 if (!gtk_tree_model_get_iter (model, &iter, path))
9742 gtk_tree_model_get (model, &iter,
9743 MODEL_COL_IS_FOLDER, &is_folder,
9753 list_selection_changed (GtkTreeSelection *selection,
9754 GtkFileChooserDefault *impl)
9756 /* See if we are in the new folder editable row for Save mode */
9757 if (impl->operation_mode == OPERATION_MODE_BROWSE &&
9758 impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
9761 gboolean had_selection;
9763 info = get_selected_file_info_from_file_list (impl, &had_selection);
9765 goto out; /* normal processing */
9768 return; /* We are on the editable row for New Folder */
9773 if (impl->location_entry)
9774 update_chooser_entry (impl);
9776 check_preview_change (impl);
9777 bookmarks_check_add_sensitivity (impl);
9779 g_signal_emit_by_name (impl, "selection-changed", 0);
9782 /* Callback used when a row in the file list is activated */
9784 list_row_activated (GtkTreeView *tree_view,
9786 GtkTreeViewColumn *column,
9787 GtkFileChooserDefault *impl)
9791 GtkTreeModel *model;
9794 model = gtk_tree_view_get_model (tree_view);
9796 if (!gtk_tree_model_get_iter (model, &iter, path))
9799 gtk_tree_model_get (model, &iter,
9800 MODEL_COL_FILE, &file,
9801 MODEL_COL_IS_FOLDER, &is_folder,
9804 if (is_folder && file)
9806 change_folder_and_display_error (impl, file, FALSE);
9810 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
9811 impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
9812 g_signal_emit_by_name (impl, "file-activated");
9815 g_object_unref (file);
9819 path_bar_clicked (GtkPathBar *path_bar,
9822 gboolean child_is_hidden,
9823 GtkFileChooserDefault *impl)
9826 pending_select_files_add (impl, child_file);
9828 if (!change_folder_and_display_error (impl, file, FALSE))
9831 /* Say we have "/foo/bar/[.baz]" and the user clicks on "bar". We should then
9832 * show hidden files so that ".baz" appears in the file list, as it will still
9833 * be shown in the path bar: "/foo/[bar]/.baz"
9835 if (child_is_hidden)
9836 g_object_set (impl, "show-hidden", TRUE, NULL);
9840 update_cell_renderer_attributes (GtkFileChooserDefault *impl)
9842 GtkTreeViewColumn *column;
9843 GtkCellRenderer *renderer;
9845 gboolean always_sensitive;
9847 always_sensitive = impl->action != GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
9848 impl->action != GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER;
9850 /* Keep the following column numbers in sync with create_file_list() */
9853 column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_files_tree_view), 0);
9854 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
9855 for (walk = list; walk; walk = walk->next)
9857 renderer = walk->data;
9858 if (GTK_IS_CELL_RENDERER_PIXBUF (renderer))
9860 gtk_tree_view_column_set_attributes (column, renderer,
9861 "pixbuf", MODEL_COL_PIXBUF,
9866 gtk_tree_view_column_set_attributes (column, renderer,
9867 "text", MODEL_COL_NAME,
9868 "ellipsize", MODEL_COL_ELLIPSIZE,
9871 if (always_sensitive)
9872 g_object_set (renderer, "sensitive", TRUE, NULL);
9874 gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_FOLDER);
9879 column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_files_tree_view), 1);
9880 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
9881 renderer = list->data;
9882 gtk_tree_view_column_set_attributes (column, renderer,
9883 "text", MODEL_COL_SIZE_TEXT,
9885 if (always_sensitive)
9886 g_object_set (renderer, "sensitive", TRUE, NULL);
9888 gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_FOLDER);
9892 column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_files_tree_view), 2);
9893 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
9894 renderer = list->data;
9895 gtk_tree_view_column_set_attributes (column, renderer,
9896 "text", MODEL_COL_MTIME_TEXT,
9898 if (always_sensitive)
9899 g_object_set (renderer, "sensitive", TRUE, NULL);
9901 gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_FOLDER);
9906 _gtk_file_chooser_default_new (void)
9908 return g_object_new (GTK_TYPE_FILE_CHOOSER_DEFAULT, NULL);
9912 location_set_user_text (GtkFileChooserDefault *impl,
9915 _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), path);
9916 gtk_editable_set_position (GTK_EDITABLE (impl->location_entry), -1);
9920 location_popup_handler (GtkFileChooserDefault *impl,
9923 if (impl->operation_mode != OPERATION_MODE_BROWSE)
9925 GtkWidget *widget_to_focus;
9927 /* This will give us the location widgets back */
9928 switch (impl->operation_mode)
9930 case OPERATION_MODE_SEARCH:
9931 search_switch_to_browse_mode (impl);
9933 case OPERATION_MODE_RECENT:
9934 recent_switch_to_browse_mode (impl);
9936 case OPERATION_MODE_BROWSE:
9937 g_assert_not_reached ();
9941 if (impl->current_folder)
9942 change_folder_and_display_error (impl, impl->current_folder, FALSE);
9944 if (impl->location_mode == LOCATION_MODE_PATH_BAR)
9945 widget_to_focus = impl->browse_files_tree_view;
9947 widget_to_focus = impl->location_entry;
9949 gtk_widget_grab_focus (widget_to_focus);
9953 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
9954 impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
9956 LocationMode new_mode;
9960 /* since the user typed something, we unconditionally want to turn on the entry */
9961 new_mode = LOCATION_MODE_FILENAME_ENTRY;
9963 else if (impl->location_mode == LOCATION_MODE_PATH_BAR)
9964 new_mode = LOCATION_MODE_FILENAME_ENTRY;
9965 else if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
9966 new_mode = LOCATION_MODE_PATH_BAR;
9969 g_assert_not_reached ();
9973 location_mode_set (impl, new_mode, TRUE);
9974 if (new_mode == LOCATION_MODE_FILENAME_ENTRY)
9977 location_set_user_text (impl, path);
9980 location_entry_set_initial_text (impl);
9981 gtk_editable_select_region (GTK_EDITABLE (impl->location_entry), 0, -1);
9985 else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
9986 impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
9988 gtk_widget_grab_focus (impl->location_entry);
9990 location_set_user_text (impl, path);
9993 g_assert_not_reached ();
9996 /* Handler for the "up-folder" keybinding signal */
9998 up_folder_handler (GtkFileChooserDefault *impl)
10000 _gtk_path_bar_up (GTK_PATH_BAR (impl->browse_path_bar));
10003 /* Handler for the "down-folder" keybinding signal */
10005 down_folder_handler (GtkFileChooserDefault *impl)
10007 _gtk_path_bar_down (GTK_PATH_BAR (impl->browse_path_bar));
10010 /* Switches to the shortcut in the specified index */
10012 switch_to_shortcut (GtkFileChooserDefault *impl,
10017 if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
10018 g_assert_not_reached ();
10020 shortcuts_activate_iter (impl, &iter);
10023 /* Handler for the "home-folder" keybinding signal */
10025 home_folder_handler (GtkFileChooserDefault *impl)
10027 if (impl->has_home)
10028 switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_HOME));
10031 /* Handler for the "desktop-folder" keybinding signal */
10033 desktop_folder_handler (GtkFileChooserDefault *impl)
10035 if (impl->has_desktop)
10036 switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_DESKTOP));
10039 /* Handler for the "search-shortcut" keybinding signal */
10041 search_shortcut_handler (GtkFileChooserDefault *impl)
10043 if (impl->has_search)
10045 switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_SEARCH));
10047 /* we want the entry widget to grab the focus the first
10048 * time, not the browse_files_tree_view widget.
10050 if (impl->search_entry)
10051 gtk_widget_grab_focus (impl->search_entry);
10055 /* Handler for the "recent-shortcut" keybinding signal */
10057 recent_shortcut_handler (GtkFileChooserDefault *impl)
10059 if (impl->has_recent)
10060 switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_RECENT));
10064 quick_bookmark_handler (GtkFileChooserDefault *impl,
10065 gint bookmark_index)
10070 if (bookmark_index < 0 || bookmark_index >= impl->num_bookmarks)
10073 bookmark_pos = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS) + bookmark_index;
10075 path = gtk_tree_path_new_from_indices (bookmark_pos, -1);
10076 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
10079 gtk_tree_path_free (path);
10081 switch_to_shortcut (impl, bookmark_pos);
10085 show_hidden_handler (GtkFileChooserDefault *impl)
10087 g_object_set (impl,
10088 "show-hidden", !impl->show_hidden,
10093 /* Drag and drop interfaces */
10096 _shortcuts_pane_model_filter_class_init (ShortcutsPaneModelFilterClass *class)
10101 _shortcuts_pane_model_filter_init (ShortcutsPaneModelFilter *model)
10103 model->impl = NULL;
10106 /* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */
10108 shortcuts_pane_model_filter_row_draggable (GtkTreeDragSource *drag_source,
10111 ShortcutsPaneModelFilter *model;
10115 model = SHORTCUTS_PANE_MODEL_FILTER (drag_source);
10117 pos = *gtk_tree_path_get_indices (path);
10118 bookmarks_pos = shortcuts_get_index (model->impl, SHORTCUTS_BOOKMARKS);
10120 return (pos >= bookmarks_pos && pos < bookmarks_pos + model->impl->num_bookmarks);
10123 /* GtkTreeDragSource::drag_data_get implementation for the shortcuts filter model */
10125 shortcuts_pane_model_filter_drag_data_get (GtkTreeDragSource *drag_source,
10127 GtkSelectionData *selection_data)
10129 ShortcutsPaneModelFilter *model;
10131 model = SHORTCUTS_PANE_MODEL_FILTER (drag_source);
10138 /* Fill the GtkTreeDragSourceIface vtable */
10140 shortcuts_pane_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface)
10142 iface->row_draggable = shortcuts_pane_model_filter_row_draggable;
10143 iface->drag_data_get = shortcuts_pane_model_filter_drag_data_get;
10147 /* Fill the GtkTreeDragDestIface vtable */
10149 shortcuts_pane_model_filter_drag_dest_iface_init (GtkTreeDragDestIface *iface)
10151 iface->drag_data_received = shortcuts_pane_model_filter_drag_data_received;
10152 iface->row_drop_possible = shortcuts_pane_model_filter_row_drop_possible;
10156 static GtkTreeModel *
10157 shortcuts_pane_model_filter_new (GtkFileChooserDefault *impl,
10158 GtkTreeModel *child_model,
10161 ShortcutsPaneModelFilter *model;
10163 model = g_object_new (SHORTCUTS_PANE_MODEL_FILTER_TYPE,
10164 "child-model", child_model,
10165 "virtual-root", root,
10168 model->impl = impl;
10170 return GTK_TREE_MODEL (model);