1 /* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
3 /* GTK+: gtkfilechooserbutton.c
5 * Copyright (c) 2004 James M. Cape <jcape@ignore-your.tv>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
23 #include <sys/types.h>
32 #include "gtkbutton.h"
33 #include "gtkcelllayout.h"
34 #include "gtkcellrenderertext.h"
35 #include "gtkcellrendererpixbuf.h"
36 #include "gtkcombobox.h"
38 #include "gtkicontheme.h"
39 #include "gtkiconfactory.h"
42 #include "gtkliststore.h"
44 #include "gtktreemodelfilter.h"
45 #include "gtkseparator.h"
46 #include "gtkfilechooserdialog.h"
47 #include "gtkfilechooserprivate.h"
48 #include "gtkfilechooserutils.h"
49 #include "gtkmarshalers.h"
51 #include "gtkfilechooserbutton.h"
53 #include "gtkorientable.h"
55 #include "gtktypebuiltins.h"
56 #include "gtkprivate.h"
57 #include "gtksettings.h"
61 * SECTION:gtkfilechooserbutton
62 * @Short_description: A button to launch a file selection dialog
63 * @Title: GtkFileChooserButton
64 * @See_also:#GtkFileChooserDialog
66 * The #GtkFileChooserButton is a widget that lets the user select a
67 * file. It implements the #GtkFileChooser interface. Visually, it is a
68 * file name with a button to bring up a #GtkFileChooserDialog.
69 * The user can then use that dialog to change the file associated with
70 * that button. This widget does not support setting the
71 * #GtkFileChooser:select-multiple property to %TRUE.
74 * <title>Create a button to let the user select a file in /etc</title>
79 * button = gtk_file_chooser_button_new (_("Select a file"),
80 * GTK_FILE_CHOOSER_ACTION_OPEN);
81 * gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (button),
87 * The #GtkFileChooserButton supports the #GtkFileChooserAction<!-- -->s
88 * %GTK_FILE_CHOOSER_ACTION_OPEN and %GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER.
91 * The #GtkFileChooserButton will ellipsize the label,
92 * and thus will thus request little horizontal space. To give the button
93 * more space, you should call gtk_widget_get_preferred_size(),
94 * gtk_file_chooser_button_set_width_chars(), or pack the button in
95 * such a way that other interface elements give space to the widget.
100 /* **************** *
102 * **************** */
104 #define DEFAULT_TITLE N_("Select a File")
105 #define DESKTOP_DISPLAY_NAME N_("Desktop")
106 #define FALLBACK_DISPLAY_NAME N_("(None)") /* this string is used in gtk+/gtk/tests/filechooser.c - change it there if you change it here */
107 #define FALLBACK_ICON_NAME "stock_unknown"
108 #define FALLBACK_ICON_SIZE 16
111 /* ********************** *
112 * Private Enumerations *
113 * ********************** */
133 /* TreeModel Columns */
145 /* TreeModel Row Types */
151 ROW_TYPE_BOOKMARK_SEPARATOR,
153 ROW_TYPE_CURRENT_FOLDER_SEPARATOR,
154 ROW_TYPE_CURRENT_FOLDER,
155 ROW_TYPE_OTHER_SEPARATOR,
157 ROW_TYPE_EMPTY_SELECTION,
159 ROW_TYPE_INVALID = -1
164 /* ******************** *
165 * Private Structures *
166 * ******************** */
168 struct _GtkFileChooserButtonPrivate
174 GtkWidget *combo_box;
175 GtkCellRenderer *icon_cell;
176 GtkCellRenderer *name_cell;
179 GtkTreeModel *filter_model;
182 GFile *selection_while_inactive;
183 GFile *current_folder_while_inactive;
185 gulong combo_box_changed_id;
186 gulong dialog_folder_changed_id;
187 gulong dialog_selection_changed_id;
188 gulong fs_volumes_changed_id;
189 gulong fs_bookmarks_changed_id;
191 GCancellable *dnd_select_folder_cancellable;
192 GCancellable *update_button_cancellable;
193 GSList *change_icon_theme_cancellables;
201 guint has_bookmark_separator : 1;
202 guint has_current_folder_separator : 1;
203 guint has_current_folder : 1;
204 guint has_other_separator : 1;
206 /* Used for hiding/showing the dialog when the button is hidden */
209 guint focus_on_click : 1;
211 /* Whether the next async callback from GIO should emit the "selection-changed" signal */
212 guint is_changing_selection : 1;
214 /* When GtkFileChooserButton's dialog is not active, any modifications to the
215 * dialog's state (as done by the calling program) will be reflected in the
216 * button immediately - the following two flags will be FALSE.
218 * But when the dialog is active, we only want the button to reflect
219 * modifications in the dialog's state if those modifications *are done by the
220 * calling program*, not by the user in the dialog. So if the program calls
221 * gtk_file_chooser_select_file() while the dialog is active, that state will
222 * need to be reflected in the button, and either of these flags will be TRUE.
223 * But if the user frobs the dialog, we don't want the button to change its
224 * state until the user actually confirms the dialog and dismisses it; in this
225 * case, the flags will be FALSE.
227 guint folder_change_needs_notification : 1;
228 guint selection_change_needs_notification : 1;
243 /* ********************* *
244 * Function Prototypes *
245 * ********************* */
247 /* GtkFileChooserIface Functions */
248 static void gtk_file_chooser_button_file_chooser_iface_init (GtkFileChooserIface *iface);
249 static gboolean gtk_file_chooser_button_set_current_folder (GtkFileChooser *chooser,
252 static GFile *gtk_file_chooser_button_get_current_folder (GtkFileChooser *chooser);
253 static gboolean gtk_file_chooser_button_select_file (GtkFileChooser *chooser,
256 static void gtk_file_chooser_button_unselect_file (GtkFileChooser *chooser,
258 static void gtk_file_chooser_button_unselect_all (GtkFileChooser *chooser);
259 static GSList *gtk_file_chooser_button_get_files (GtkFileChooser *chooser);
260 static gboolean gtk_file_chooser_button_add_shortcut_folder (GtkFileChooser *chooser,
263 static gboolean gtk_file_chooser_button_remove_shortcut_folder (GtkFileChooser *chooser,
267 /* GObject Functions */
268 static GObject *gtk_file_chooser_button_constructor (GType type,
270 GObjectConstructParam *params);
271 static void gtk_file_chooser_button_set_property (GObject *object,
275 static void gtk_file_chooser_button_get_property (GObject *object,
279 static void gtk_file_chooser_button_finalize (GObject *object);
281 /* GtkWidget Functions */
282 static void gtk_file_chooser_button_destroy (GtkWidget *widget);
283 static void gtk_file_chooser_button_drag_data_received (GtkWidget *widget,
284 GdkDragContext *context,
287 GtkSelectionData *data,
290 static void gtk_file_chooser_button_show_all (GtkWidget *widget);
291 static void gtk_file_chooser_button_show (GtkWidget *widget);
292 static void gtk_file_chooser_button_hide (GtkWidget *widget);
293 static void gtk_file_chooser_button_map (GtkWidget *widget);
294 static gboolean gtk_file_chooser_button_mnemonic_activate (GtkWidget *widget,
295 gboolean group_cycling);
296 static void gtk_file_chooser_button_style_updated (GtkWidget *widget);
297 static void gtk_file_chooser_button_screen_changed (GtkWidget *widget,
298 GdkScreen *old_screen);
300 /* Utility Functions */
301 static GtkIconTheme *get_icon_theme (GtkWidget *widget);
302 static void set_info_for_file_at_iter (GtkFileChooserButton *fs,
306 static gint model_get_type_position (GtkFileChooserButton *button,
308 static void model_free_row_data (GtkFileChooserButton *button,
310 static void model_add_special (GtkFileChooserButton *button);
311 static void model_add_other (GtkFileChooserButton *button);
312 static void model_add_empty_selection (GtkFileChooserButton *button);
313 static void model_add_volumes (GtkFileChooserButton *button,
315 static void model_add_bookmarks (GtkFileChooserButton *button,
317 static void model_update_current_folder (GtkFileChooserButton *button,
319 static void model_remove_rows (GtkFileChooserButton *button,
323 static gboolean filter_model_visible_func (GtkTreeModel *model,
327 static gboolean combo_box_row_separator_func (GtkTreeModel *model,
330 static void name_cell_data_func (GtkCellLayout *layout,
331 GtkCellRenderer *cell,
335 static void open_dialog (GtkFileChooserButton *button);
336 static void update_combo_box (GtkFileChooserButton *button);
337 static void update_label_and_image (GtkFileChooserButton *button);
339 /* Child Object Callbacks */
340 static void fs_volumes_changed_cb (GtkFileSystem *fs,
342 static void fs_bookmarks_changed_cb (GtkFileSystem *fs,
345 static void combo_box_changed_cb (GtkComboBox *combo_box,
347 static void combo_box_notify_popup_shown_cb (GObject *object,
351 static void button_clicked_cb (GtkButton *real_button,
354 static void dialog_update_preview_cb (GtkFileChooser *dialog,
356 static void dialog_selection_changed_cb (GtkFileChooser *dialog,
358 static void dialog_current_folder_changed_cb (GtkFileChooser *dialog,
360 static void dialog_notify_cb (GObject *dialog,
363 static gboolean dialog_delete_event_cb (GtkWidget *dialog,
366 static void dialog_response_cb (GtkDialog *dialog,
370 static guint file_chooser_button_signals[LAST_SIGNAL] = { 0 };
372 /* ******************* *
373 * GType Declaration *
374 * ******************* */
376 G_DEFINE_TYPE_WITH_CODE (GtkFileChooserButton, gtk_file_chooser_button, GTK_TYPE_BOX, { \
377 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER, gtk_file_chooser_button_file_chooser_iface_init) \
381 /* ***************** *
383 * ***************** */
386 gtk_file_chooser_button_class_init (GtkFileChooserButtonClass * class)
388 GObjectClass *gobject_class;
389 GtkWidgetClass *widget_class;
391 gobject_class = G_OBJECT_CLASS (class);
392 widget_class = GTK_WIDGET_CLASS (class);
394 gobject_class->constructor = gtk_file_chooser_button_constructor;
395 gobject_class->set_property = gtk_file_chooser_button_set_property;
396 gobject_class->get_property = gtk_file_chooser_button_get_property;
397 gobject_class->finalize = gtk_file_chooser_button_finalize;
399 widget_class->destroy = gtk_file_chooser_button_destroy;
400 widget_class->drag_data_received = gtk_file_chooser_button_drag_data_received;
401 widget_class->show_all = gtk_file_chooser_button_show_all;
402 widget_class->show = gtk_file_chooser_button_show;
403 widget_class->hide = gtk_file_chooser_button_hide;
404 widget_class->map = gtk_file_chooser_button_map;
405 widget_class->style_updated = gtk_file_chooser_button_style_updated;
406 widget_class->screen_changed = gtk_file_chooser_button_screen_changed;
407 widget_class->mnemonic_activate = gtk_file_chooser_button_mnemonic_activate;
410 * GtkFileChooserButton::file-set:
411 * @widget: the object which received the signal.
413 * The ::file-set signal is emitted when the user selects a file.
415 * Note that this signal is only emitted when the <emphasis>user</emphasis>
420 file_chooser_button_signals[FILE_SET] =
421 g_signal_new (I_("file-set"),
422 G_TYPE_FROM_CLASS (gobject_class),
424 G_STRUCT_OFFSET (GtkFileChooserButtonClass, file_set),
426 _gtk_marshal_VOID__VOID,
430 * GtkFileChooserButton:dialog:
432 * Instance of the #GtkFileChooserDialog associated with the button.
436 g_object_class_install_property (gobject_class, PROP_DIALOG,
437 g_param_spec_object ("dialog",
439 P_("The file chooser dialog to use."),
440 GTK_TYPE_FILE_CHOOSER,
441 (GTK_PARAM_WRITABLE |
442 G_PARAM_CONSTRUCT_ONLY)));
445 * GtkFileChooserButton:focus-on-click:
447 * Whether the #GtkFileChooserButton button grabs focus when it is clicked
452 g_object_class_install_property (gobject_class,
454 g_param_spec_boolean ("focus-on-click",
455 P_("Focus on click"),
456 P_("Whether the button grabs focus when it is clicked with the mouse"),
458 GTK_PARAM_READWRITE));
461 * GtkFileChooserButton:title:
463 * Title to put on the #GtkFileChooserDialog associated with the button.
467 g_object_class_install_property (gobject_class, PROP_TITLE,
468 g_param_spec_string ("title",
470 P_("The title of the file chooser dialog."),
472 GTK_PARAM_READWRITE));
475 * GtkFileChooserButton:width-chars:
477 * The width of the entry and label inside the button, in characters.
481 g_object_class_install_property (gobject_class, PROP_WIDTH_CHARS,
482 g_param_spec_int ("width-chars",
483 P_("Width In Characters"),
484 P_("The desired width of the button widget, in characters."),
486 GTK_PARAM_READWRITE));
488 _gtk_file_chooser_install_properties (gobject_class);
490 g_type_class_add_private (class, sizeof (GtkFileChooserButtonPrivate));
494 gtk_file_chooser_button_init (GtkFileChooserButton *button)
496 GtkFileChooserButtonPrivate *priv;
497 GtkWidget *box, *image, *sep;
498 GtkTargetList *target_list;
500 priv = button->priv = G_TYPE_INSTANCE_GET_PRIVATE (button,
501 GTK_TYPE_FILE_CHOOSER_BUTTON,
502 GtkFileChooserButtonPrivate);
504 priv->icon_size = FALLBACK_ICON_SIZE;
505 priv->focus_on_click = TRUE;
507 gtk_widget_push_composite_child ();
510 priv->button = gtk_button_new ();
511 g_signal_connect (priv->button, "clicked",
512 G_CALLBACK (button_clicked_cb), button);
513 gtk_box_pack_start (GTK_BOX (button), priv->button, TRUE, TRUE, 0);
514 gtk_widget_set_halign (priv->button, GTK_ALIGN_FILL);
515 gtk_widget_show (priv->button);
517 box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
518 gtk_container_add (GTK_CONTAINER (priv->button), box);
519 gtk_widget_show (box);
521 priv->image = gtk_image_new ();
522 gtk_box_pack_start (GTK_BOX (box), priv->image, FALSE, FALSE, 0);
523 gtk_widget_show (priv->image);
525 priv->label = gtk_label_new (_(FALLBACK_DISPLAY_NAME));
526 gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_END);
527 gtk_widget_set_halign (priv->label, GTK_ALIGN_START);
528 gtk_widget_set_valign (priv->label, GTK_ALIGN_CENTER);
529 gtk_box_pack_start (GTK_BOX (box), priv->label, TRUE, TRUE, 0);
530 //gtk_container_add (GTK_CONTAINER (box), priv->label);
531 gtk_widget_show (priv->label);
533 sep = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
534 gtk_box_pack_start (GTK_BOX (box), sep, FALSE, FALSE, 0);
535 gtk_widget_show (sep);
537 image = gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU);
538 gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 0);
539 gtk_widget_show (image);
542 /* Keep in sync with columns enum, line 88 */
544 GTK_TREE_MODEL (gtk_list_store_new (NUM_COLUMNS,
545 GDK_TYPE_PIXBUF, /* ICON_COLUMN */
546 G_TYPE_STRING, /* DISPLAY_NAME_COLUMN */
547 G_TYPE_CHAR, /* TYPE_COLUMN */
548 G_TYPE_POINTER /* DATA_COLUMN (Volume || Path) */,
549 G_TYPE_BOOLEAN /* IS_FOLDER_COLUMN */,
550 G_TYPE_POINTER /* CANCELLABLE_COLUMN */));
552 priv->combo_box = gtk_combo_box_new ();
553 priv->combo_box_changed_id = g_signal_connect (priv->combo_box, "changed",
554 G_CALLBACK (combo_box_changed_cb), button);
556 g_signal_connect (priv->combo_box, "notify::popup-shown",
557 G_CALLBACK (combo_box_notify_popup_shown_cb), button);
559 gtk_box_pack_start (GTK_BOX (button), priv->combo_box, TRUE, TRUE, 0);
560 gtk_widget_set_halign (priv->combo_box, GTK_ALIGN_FILL);
562 priv->icon_cell = gtk_cell_renderer_pixbuf_new ();
563 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->combo_box),
564 priv->icon_cell, FALSE);
565 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->combo_box),
566 priv->icon_cell, "pixbuf", ICON_COLUMN);
568 priv->name_cell = gtk_cell_renderer_text_new ();
569 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->combo_box),
570 priv->name_cell, TRUE);
571 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->combo_box),
572 priv->name_cell, "text", DISPLAY_NAME_COLUMN);
573 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->combo_box),
574 priv->name_cell, name_cell_data_func,
577 gtk_widget_pop_composite_child ();
580 gtk_drag_dest_set (GTK_WIDGET (button),
581 (GTK_DEST_DEFAULT_ALL),
584 target_list = gtk_target_list_new (NULL, 0);
585 gtk_target_list_add_uri_targets (target_list, TEXT_URI_LIST);
586 gtk_target_list_add_text_targets (target_list, TEXT_PLAIN);
587 gtk_drag_dest_set_target_list (GTK_WIDGET (button), target_list);
588 gtk_target_list_unref (target_list);
592 /* ******************************* *
593 * GtkFileChooserIface Functions *
594 * ******************************* */
596 gtk_file_chooser_button_file_chooser_iface_init (GtkFileChooserIface *iface)
598 _gtk_file_chooser_delegate_iface_init (iface);
600 iface->set_current_folder = gtk_file_chooser_button_set_current_folder;
601 iface->get_current_folder = gtk_file_chooser_button_get_current_folder;
602 iface->select_file = gtk_file_chooser_button_select_file;
603 iface->unselect_file = gtk_file_chooser_button_unselect_file;
604 iface->unselect_all = gtk_file_chooser_button_unselect_all;
605 iface->get_files = gtk_file_chooser_button_get_files;
606 iface->add_shortcut_folder = gtk_file_chooser_button_add_shortcut_folder;
607 iface->remove_shortcut_folder = gtk_file_chooser_button_remove_shortcut_folder;
611 emit_selection_changed_if_changing_selection (GtkFileChooserButton *button)
613 GtkFileChooserButtonPrivate *priv = button->priv;
615 if (priv->is_changing_selection)
617 priv->is_changing_selection = FALSE;
618 g_signal_emit_by_name (button, "selection-changed");
623 gtk_file_chooser_button_set_current_folder (GtkFileChooser *chooser,
627 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
628 GtkFileChooserButtonPrivate *priv = button->priv;
629 GtkFileChooser *delegate;
631 delegate = g_object_get_qdata (G_OBJECT (chooser),
632 GTK_FILE_CHOOSER_DELEGATE_QUARK);
636 priv->folder_change_needs_notification = TRUE;
637 return gtk_file_chooser_set_current_folder_file (delegate, file, error);
641 if (priv->current_folder_while_inactive)
642 g_object_unref (priv->current_folder_while_inactive);
644 priv->current_folder_while_inactive = g_object_ref (file);
646 update_combo_box (button);
648 g_signal_emit_by_name (button, "current-folder-changed");
655 gtk_file_chooser_button_get_current_folder (GtkFileChooser *chooser)
657 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
658 GtkFileChooserButtonPrivate *priv = button->priv;
659 GtkFileChooser *delegate;
661 delegate = g_object_get_qdata (G_OBJECT (chooser),
662 GTK_FILE_CHOOSER_DELEGATE_QUARK);
665 return gtk_file_chooser_get_current_folder_file (delegate);
668 if (priv->current_folder_while_inactive)
669 return g_object_ref (priv->current_folder_while_inactive);
676 gtk_file_chooser_button_select_file (GtkFileChooser *chooser,
680 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
681 GtkFileChooserButtonPrivate *priv = button->priv;
682 GtkFileChooser *delegate;
684 delegate = g_object_get_qdata (G_OBJECT (chooser),
685 GTK_FILE_CHOOSER_DELEGATE_QUARK);
689 priv->selection_change_needs_notification = TRUE;
690 return gtk_file_chooser_select_file (delegate, file, error);
694 if (priv->selection_while_inactive)
695 g_object_unref (priv->selection_while_inactive);
697 priv->selection_while_inactive = g_object_ref (file);
699 priv->is_changing_selection = TRUE;
701 update_label_and_image (button);
702 update_combo_box (button);
709 gtk_file_chooser_button_unselect_file (GtkFileChooser *chooser,
712 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
713 GtkFileChooserButtonPrivate *priv = button->priv;
714 GtkFileChooser *delegate;
716 delegate = g_object_get_qdata (G_OBJECT (chooser),
717 GTK_FILE_CHOOSER_DELEGATE_QUARK);
720 gtk_file_chooser_unselect_file (delegate, file);
723 if (g_file_equal (priv->selection_while_inactive, file))
725 if (priv->selection_while_inactive)
727 g_object_unref (priv->selection_while_inactive);
728 priv->selection_while_inactive = NULL;
731 priv->is_changing_selection = TRUE;
733 update_label_and_image (button);
734 update_combo_box (button);
740 gtk_file_chooser_button_unselect_all (GtkFileChooser *chooser)
742 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
743 GtkFileChooserButtonPrivate *priv = button->priv;
744 GtkFileChooser *delegate;
746 delegate = g_object_get_qdata (G_OBJECT (chooser),
747 GTK_FILE_CHOOSER_DELEGATE_QUARK);
750 gtk_file_chooser_unselect_all (delegate);
753 if (priv->selection_while_inactive)
755 g_object_unref (priv->selection_while_inactive);
756 priv->selection_while_inactive = NULL;
759 update_label_and_image (button);
760 update_combo_box (button);
765 get_selected_file (GtkFileChooserButton *button)
767 GtkFileChooserButtonPrivate *priv = button->priv;
770 return gtk_file_chooser_get_file (GTK_FILE_CHOOSER (priv->dialog));
773 if (priv->selection_while_inactive)
774 return g_object_ref (priv->selection_while_inactive);
775 else if (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)) == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
777 /* If there is no "real" selection in SELECT_FOLDER mode, then we'll just return
778 * the current folder, since that is what GtkFileChooserDefault would do.
780 if (priv->current_folder_while_inactive)
781 return g_object_ref (priv->current_folder_while_inactive);
789 gtk_file_chooser_button_get_files (GtkFileChooser *chooser)
791 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
793 return g_slist_prepend (NULL, get_selected_file (button));
797 gtk_file_chooser_button_add_shortcut_folder (GtkFileChooser *chooser,
801 GtkFileChooser *delegate;
804 delegate = g_object_get_qdata (G_OBJECT (chooser),
805 GTK_FILE_CHOOSER_DELEGATE_QUARK);
806 retval = _gtk_file_chooser_add_shortcut_folder (delegate, file, error);
810 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
811 GtkFileChooserButtonPrivate *priv = button->priv;
815 pos = model_get_type_position (button, ROW_TYPE_SHORTCUT);
816 pos += priv->n_shortcuts;
818 gtk_list_store_insert (GTK_LIST_STORE (priv->model), &iter, pos);
819 gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter,
821 DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
822 TYPE_COLUMN, ROW_TYPE_SHORTCUT,
823 DATA_COLUMN, g_object_ref (file),
824 IS_FOLDER_COLUMN, FALSE,
826 set_info_for_file_at_iter (button, file, &iter);
829 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
836 gtk_file_chooser_button_remove_shortcut_folder (GtkFileChooser *chooser,
840 GtkFileChooser *delegate;
843 delegate = g_object_get_qdata (G_OBJECT (chooser),
844 GTK_FILE_CHOOSER_DELEGATE_QUARK);
846 retval = _gtk_file_chooser_remove_shortcut_folder (delegate, file, error);
850 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
851 GtkFileChooserButtonPrivate *priv = button->priv;
856 pos = model_get_type_position (button, ROW_TYPE_SHORTCUT);
857 gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
863 gtk_tree_model_get (priv->model, &iter,
868 if (type == ROW_TYPE_SHORTCUT &&
869 data && g_file_equal (data, file))
871 model_free_row_data (GTK_FILE_CHOOSER_BUTTON (chooser), &iter);
872 gtk_list_store_remove (GTK_LIST_STORE (priv->model), &iter);
874 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
875 update_combo_box (GTK_FILE_CHOOSER_BUTTON (chooser));
879 while (type == ROW_TYPE_SHORTCUT &&
880 gtk_tree_model_iter_next (priv->model, &iter));
887 /* ******************* *
888 * GObject Functions *
889 * ******************* */
892 gtk_file_chooser_button_constructor (GType type,
894 GObjectConstructParam *params)
897 GtkFileChooserButton *button;
898 GtkFileChooserButtonPrivate *priv;
901 object = G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->constructor (type,
904 button = GTK_FILE_CHOOSER_BUTTON (object);
909 priv->dialog = gtk_file_chooser_dialog_new (NULL, NULL,
910 GTK_FILE_CHOOSER_ACTION_OPEN,
917 gtk_dialog_set_default_response (GTK_DIALOG (priv->dialog),
918 GTK_RESPONSE_ACCEPT);
919 gtk_dialog_set_alternative_button_order (GTK_DIALOG (priv->dialog),
924 gtk_file_chooser_button_set_title (button, _(DEFAULT_TITLE));
926 else if (!gtk_window_get_title (GTK_WINDOW (priv->dialog)))
928 gtk_file_chooser_button_set_title (button, _(DEFAULT_TITLE));
931 g_signal_connect (priv->dialog, "delete-event",
932 G_CALLBACK (dialog_delete_event_cb), object);
933 g_signal_connect (priv->dialog, "response",
934 G_CALLBACK (dialog_response_cb), object);
936 /* This is used, instead of the standard delegate, to ensure that signals are only
937 * delegated when the OK button is pressed. */
938 g_object_set_qdata (object, GTK_FILE_CHOOSER_DELEGATE_QUARK, priv->dialog);
939 priv->dialog_folder_changed_id =
940 g_signal_connect (priv->dialog, "current-folder-changed",
941 G_CALLBACK (dialog_current_folder_changed_cb), object);
942 priv->dialog_selection_changed_id =
943 g_signal_connect (priv->dialog, "selection-changed",
944 G_CALLBACK (dialog_selection_changed_cb), object);
945 g_signal_connect (priv->dialog, "update-preview",
946 G_CALLBACK (dialog_update_preview_cb), object);
947 g_signal_connect (priv->dialog, "notify",
948 G_CALLBACK (dialog_notify_cb), object);
949 g_object_add_weak_pointer (G_OBJECT (priv->dialog),
950 (gpointer) (&priv->dialog));
953 g_object_ref (_gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog)));
955 model_add_special (button);
957 list = _gtk_file_system_list_volumes (priv->fs);
958 model_add_volumes (button, list);
961 list = _gtk_file_system_list_bookmarks (priv->fs);
962 model_add_bookmarks (button, list);
963 g_slist_foreach (list, (GFunc) g_object_unref, NULL);
966 model_add_other (button);
968 model_add_empty_selection (button);
970 priv->filter_model = gtk_tree_model_filter_new (priv->model, NULL);
971 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->filter_model),
972 filter_model_visible_func,
975 gtk_combo_box_set_model (GTK_COMBO_BOX (priv->combo_box), priv->filter_model);
976 gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (priv->combo_box),
977 combo_box_row_separator_func,
980 /* set up the action for a user-provided dialog, this also updates
981 * the label, image and combobox
983 g_object_set (object,
984 "action", gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)),
987 priv->fs_volumes_changed_id =
988 g_signal_connect (priv->fs, "volumes-changed",
989 G_CALLBACK (fs_volumes_changed_cb), object);
990 priv->fs_bookmarks_changed_id =
991 g_signal_connect (priv->fs, "bookmarks-changed",
992 G_CALLBACK (fs_bookmarks_changed_cb), object);
994 update_label_and_image (button);
995 update_combo_box (button);
1001 gtk_file_chooser_button_set_property (GObject *object,
1003 const GValue *value,
1006 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
1007 GtkFileChooserButtonPrivate *priv = button->priv;
1012 /* Construct-only */
1013 priv->dialog = g_value_get_object (value);
1015 case PROP_FOCUS_ON_CLICK:
1016 gtk_file_chooser_button_set_focus_on_click (button, g_value_get_boolean (value));
1018 case PROP_WIDTH_CHARS:
1019 gtk_file_chooser_button_set_width_chars (GTK_FILE_CHOOSER_BUTTON (object),
1020 g_value_get_int (value));
1022 case GTK_FILE_CHOOSER_PROP_ACTION:
1023 switch (g_value_get_enum (value))
1025 case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
1026 case GTK_FILE_CHOOSER_ACTION_SAVE:
1031 eclass = g_type_class_peek (GTK_TYPE_FILE_CHOOSER_ACTION);
1032 eval = g_enum_get_value (eclass, g_value_get_enum (value));
1033 g_warning ("%s: Choosers of type `%s' do not support `%s'.",
1034 G_STRFUNC, G_OBJECT_TYPE_NAME (object), eval->value_name);
1036 g_value_set_enum ((GValue *) value, GTK_FILE_CHOOSER_ACTION_OPEN);
1041 g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
1042 update_label_and_image (GTK_FILE_CHOOSER_BUTTON (object));
1043 update_combo_box (GTK_FILE_CHOOSER_BUTTON (object));
1045 switch (g_value_get_enum (value))
1047 case GTK_FILE_CHOOSER_ACTION_OPEN:
1048 gtk_widget_hide (priv->combo_box);
1049 gtk_widget_show (priv->button);
1051 case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
1052 gtk_widget_hide (priv->button);
1053 gtk_widget_show (priv->combo_box);
1056 g_assert_not_reached ();
1062 case GTK_FILE_CHOOSER_PROP_FILTER:
1063 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
1064 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
1065 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
1066 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
1067 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
1068 case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
1069 case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
1070 g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
1073 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
1074 g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
1075 fs_volumes_changed_cb (priv->fs, button);
1076 fs_bookmarks_changed_cb (priv->fs, button);
1079 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
1080 g_warning ("%s: Choosers of type `%s` do not support selecting multiple files.",
1081 G_STRFUNC, G_OBJECT_TYPE_NAME (object));
1084 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1090 gtk_file_chooser_button_get_property (GObject *object,
1095 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
1096 GtkFileChooserButtonPrivate *priv = button->priv;
1100 case PROP_WIDTH_CHARS:
1101 g_value_set_int (value,
1102 gtk_label_get_width_chars (GTK_LABEL (priv->label)));
1104 case PROP_FOCUS_ON_CLICK:
1105 g_value_set_boolean (value,
1106 gtk_file_chooser_button_get_focus_on_click (button));
1110 case GTK_FILE_CHOOSER_PROP_ACTION:
1111 case GTK_FILE_CHOOSER_PROP_FILTER:
1112 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
1113 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
1114 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
1115 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
1116 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
1117 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
1118 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
1119 case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
1120 case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
1121 g_object_get_property (G_OBJECT (priv->dialog), pspec->name, value);
1125 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1131 gtk_file_chooser_button_finalize (GObject *object)
1133 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
1134 GtkFileChooserButtonPrivate *priv = button->priv;
1136 if (priv->selection_while_inactive)
1137 g_object_unref (priv->selection_while_inactive);
1139 if (priv->current_folder_while_inactive)
1140 g_object_unref (priv->current_folder_while_inactive);
1142 G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->finalize (object);
1145 /* ********************* *
1146 * GtkWidget Functions *
1147 * ********************* */
1150 gtk_file_chooser_button_destroy (GtkWidget *widget)
1152 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1153 GtkFileChooserButtonPrivate *priv = button->priv;
1157 if (priv->dialog != NULL)
1159 gtk_widget_destroy (priv->dialog);
1160 priv->dialog = NULL;
1163 if (priv->model && gtk_tree_model_get_iter_first (priv->model, &iter)) do
1165 model_free_row_data (button, &iter);
1167 while (gtk_tree_model_iter_next (priv->model, &iter));
1169 if (priv->dnd_select_folder_cancellable)
1171 g_cancellable_cancel (priv->dnd_select_folder_cancellable);
1172 priv->dnd_select_folder_cancellable = NULL;
1175 if (priv->update_button_cancellable)
1177 g_cancellable_cancel (priv->update_button_cancellable);
1178 priv->update_button_cancellable = NULL;
1181 if (priv->change_icon_theme_cancellables)
1183 for (l = priv->change_icon_theme_cancellables; l; l = l->next)
1185 GCancellable *cancellable = G_CANCELLABLE (l->data);
1186 g_cancellable_cancel (cancellable);
1188 g_slist_free (priv->change_icon_theme_cancellables);
1189 priv->change_icon_theme_cancellables = NULL;
1194 g_object_unref (priv->model);
1198 if (priv->filter_model)
1200 g_object_unref (priv->filter_model);
1201 priv->filter_model = NULL;
1206 g_signal_handler_disconnect (priv->fs, priv->fs_volumes_changed_id);
1207 g_signal_handler_disconnect (priv->fs, priv->fs_bookmarks_changed_id);
1208 g_object_unref (priv->fs);
1212 GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->destroy (widget);
1215 struct DndSelectFolderData
1217 GtkFileSystem *file_system;
1218 GtkFileChooserButton *button;
1219 GtkFileChooserAction action;
1227 dnd_select_folder_get_info_cb (GCancellable *cancellable,
1229 const GError *error,
1232 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1233 struct DndSelectFolderData *data = user_data;
1235 if (cancellable != data->button->priv->dnd_select_folder_cancellable)
1237 g_object_unref (data->button);
1238 g_object_unref (data->file);
1239 g_strfreev (data->uris);
1242 g_object_unref (cancellable);
1246 data->button->priv->dnd_select_folder_cancellable = NULL;
1248 if (!cancelled && !error && info != NULL)
1252 is_folder = _gtk_file_info_consider_as_directory (info);
1255 (((data->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER && is_folder) ||
1256 (data->action == GTK_FILE_CHOOSER_ACTION_OPEN && !is_folder)) &&
1257 gtk_file_chooser_select_file (GTK_FILE_CHOOSER (data->button->priv->dialog),
1261 data->selected = FALSE;
1263 if (data->selected || data->uris[++data->i] == NULL)
1265 g_signal_emit (data->button, file_chooser_button_signals[FILE_SET], 0);
1267 g_object_unref (data->button);
1268 g_object_unref (data->file);
1269 g_strfreev (data->uris);
1272 g_object_unref (cancellable);
1277 g_object_unref (data->file);
1279 data->file = g_file_new_for_uri (data->uris[data->i]);
1281 data->button->priv->dnd_select_folder_cancellable =
1282 _gtk_file_system_get_info (data->file_system, data->file,
1284 dnd_select_folder_get_info_cb, user_data);
1286 g_object_unref (cancellable);
1290 gtk_file_chooser_button_drag_data_received (GtkWidget *widget,
1291 GdkDragContext *context,
1294 GtkSelectionData *data,
1298 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1299 GtkFileChooserButtonPrivate *priv = button->priv;
1303 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->drag_data_received != NULL)
1304 GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->drag_data_received (widget,
1310 if (widget == NULL || context == NULL || data == NULL || gtk_selection_data_get_length (data) < 0)
1318 struct DndSelectFolderData *info;
1320 uris = gtk_selection_data_get_uris (data);
1325 info = g_new0 (struct DndSelectFolderData, 1);
1326 info->button = g_object_ref (button);
1329 info->selected = FALSE;
1330 info->file_system = priv->fs;
1331 g_object_get (priv->dialog, "action", &info->action, NULL);
1333 info->file = g_file_new_for_uri (info->uris[info->i]);
1335 if (priv->dnd_select_folder_cancellable)
1336 g_cancellable_cancel (priv->dnd_select_folder_cancellable);
1338 priv->dnd_select_folder_cancellable =
1339 _gtk_file_system_get_info (priv->fs, info->file,
1341 dnd_select_folder_get_info_cb, info);
1346 text = (char*) gtk_selection_data_get_text (data);
1347 file = g_file_new_for_uri (text);
1348 gtk_file_chooser_select_file (GTK_FILE_CHOOSER (priv->dialog), file,
1350 g_object_unref (file);
1352 g_signal_emit (button, file_chooser_button_signals[FILE_SET], 0);
1359 gtk_drag_finish (context, TRUE, FALSE, drag_time);
1363 gtk_file_chooser_button_show_all (GtkWidget *widget)
1365 gtk_widget_show (widget);
1369 gtk_file_chooser_button_show (GtkWidget *widget)
1371 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1372 GtkFileChooserButtonPrivate *priv = button->priv;
1374 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->show)
1375 GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->show (widget);
1378 open_dialog (GTK_FILE_CHOOSER_BUTTON (widget));
1382 gtk_file_chooser_button_hide (GtkWidget *widget)
1384 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1385 GtkFileChooserButtonPrivate *priv = button->priv;
1387 gtk_widget_hide (priv->dialog);
1389 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide)
1390 GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide (widget);
1394 gtk_file_chooser_button_map (GtkWidget *widget)
1396 GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->map (widget);
1400 gtk_file_chooser_button_mnemonic_activate (GtkWidget *widget,
1401 gboolean group_cycling)
1403 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1404 GtkFileChooserButtonPrivate *priv = button->priv;
1406 switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)))
1408 case GTK_FILE_CHOOSER_ACTION_OPEN:
1409 gtk_widget_grab_focus (priv->button);
1411 case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
1412 return gtk_widget_mnemonic_activate (priv->combo_box, group_cycling);
1415 g_assert_not_reached ();
1422 /* Changes the icons wherever it is needed */
1423 struct ChangeIconThemeData
1425 GtkFileChooserButton *button;
1426 GtkTreeRowReference *row_ref;
1430 change_icon_theme_get_info_cb (GCancellable *cancellable,
1432 const GError *error,
1435 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1437 struct ChangeIconThemeData *data = user_data;
1439 if (!g_slist_find (data->button->priv->change_icon_theme_cancellables, cancellable))
1442 data->button->priv->change_icon_theme_cancellables =
1443 g_slist_remove (data->button->priv->change_icon_theme_cancellables, cancellable);
1445 if (cancelled || error)
1448 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->button), data->button->priv->icon_size);
1456 width = MAX (width, gdk_pixbuf_get_width (pixbuf));
1458 path = gtk_tree_row_reference_get_path (data->row_ref);
1461 gtk_tree_model_get_iter (data->button->priv->model, &iter, path);
1462 gtk_tree_path_free (path);
1464 gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1465 ICON_COLUMN, pixbuf,
1468 g_object_set (data->button->priv->icon_cell,
1472 g_object_unref (pixbuf);
1476 g_object_unref (data->button);
1477 gtk_tree_row_reference_free (data->row_ref);
1480 g_object_unref (cancellable);
1484 change_icon_theme (GtkFileChooserButton *button)
1486 GtkFileChooserButtonPrivate *priv = button->priv;
1487 GtkSettings *settings;
1488 GtkIconTheme *theme;
1491 gint width = 0, height = 0;
1493 for (l = button->priv->change_icon_theme_cancellables; l; l = l->next)
1495 GCancellable *cancellable = G_CANCELLABLE (l->data);
1496 g_cancellable_cancel (cancellable);
1498 g_slist_free (button->priv->change_icon_theme_cancellables);
1499 button->priv->change_icon_theme_cancellables = NULL;
1501 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
1503 if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
1505 priv->icon_size = MAX (width, height);
1507 priv->icon_size = FALLBACK_ICON_SIZE;
1509 update_label_and_image (button);
1511 gtk_tree_model_get_iter_first (priv->model, &iter);
1513 theme = get_icon_theme (GTK_WIDGET (button));
1521 type = ROW_TYPE_INVALID;
1522 gtk_tree_model_get (priv->model, &iter,
1529 case ROW_TYPE_SPECIAL:
1530 case ROW_TYPE_SHORTCUT:
1531 case ROW_TYPE_BOOKMARK:
1532 case ROW_TYPE_CURRENT_FOLDER:
1535 if (g_file_is_native (G_FILE (data)))
1538 GCancellable *cancellable;
1539 struct ChangeIconThemeData *info;
1541 info = g_new0 (struct ChangeIconThemeData, 1);
1542 info->button = g_object_ref (button);
1543 path = gtk_tree_model_get_path (priv->model, &iter);
1544 info->row_ref = gtk_tree_row_reference_new (priv->model, path);
1545 gtk_tree_path_free (path);
1548 _gtk_file_system_get_info (priv->fs, data,
1550 change_icon_theme_get_info_cb,
1552 button->priv->change_icon_theme_cancellables =
1553 g_slist_append (button->priv->change_icon_theme_cancellables, cancellable);
1557 /* Don't call get_info for remote paths to avoid latency and
1559 * If we switch to a better bookmarks file format (XBEL), we
1560 * should use mime info to get a better icon.
1562 pixbuf = gtk_icon_theme_load_icon (theme, "folder-remote",
1563 priv->icon_size, 0, NULL);
1566 pixbuf = gtk_icon_theme_load_icon (theme, FALLBACK_ICON_NAME,
1567 priv->icon_size, 0, NULL);
1569 case ROW_TYPE_VOLUME:
1571 pixbuf = _gtk_file_system_volume_render_icon (data,
1572 GTK_WIDGET (button),
1576 pixbuf = gtk_icon_theme_load_icon (theme, FALLBACK_ICON_NAME,
1577 priv->icon_size, 0, NULL);
1585 width = MAX (width, gdk_pixbuf_get_width (pixbuf));
1587 gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter,
1588 ICON_COLUMN, pixbuf,
1592 g_object_unref (pixbuf);
1594 while (gtk_tree_model_iter_next (priv->model, &iter));
1596 g_object_set (button->priv->icon_cell,
1602 gtk_file_chooser_button_style_updated (GtkWidget *widget)
1604 GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->style_updated (widget);
1606 if (gtk_widget_has_screen (widget))
1607 change_icon_theme (GTK_FILE_CHOOSER_BUTTON (widget));
1611 gtk_file_chooser_button_screen_changed (GtkWidget *widget,
1612 GdkScreen *old_screen)
1614 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->screen_changed)
1615 GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->screen_changed (widget,
1618 change_icon_theme (GTK_FILE_CHOOSER_BUTTON (widget));
1622 /* ******************* *
1623 * Utility Functions *
1624 * ******************* */
1627 static GtkIconTheme *
1628 get_icon_theme (GtkWidget *widget)
1630 if (gtk_widget_has_screen (widget))
1631 return gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
1633 return gtk_icon_theme_get_default ();
1637 struct SetDisplayNameData
1639 GtkFileChooserButton *button;
1641 GtkTreeRowReference *row_ref;
1645 set_info_get_info_cb (GCancellable *cancellable,
1647 const GError *error,
1648 gpointer callback_data)
1650 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1654 GCancellable *model_cancellable = NULL;
1655 struct SetDisplayNameData *data = callback_data;
1658 if (!data->button->priv->model)
1659 /* button got destroyed */
1662 path = gtk_tree_row_reference_get_path (data->row_ref);
1664 /* Cancellable doesn't exist anymore in the model */
1667 gtk_tree_model_get_iter (data->button->priv->model, &iter, path);
1668 gtk_tree_path_free (path);
1670 /* Validate the cancellable */
1671 gtk_tree_model_get (data->button->priv->model, &iter,
1672 CANCELLABLE_COLUMN, &model_cancellable,
1674 if (cancellable != model_cancellable)
1677 gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1678 CANCELLABLE_COLUMN, NULL,
1681 if (cancelled || error)
1682 /* There was an error, leave the fallback name in there */
1685 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->button), data->button->priv->icon_size);
1688 data->label = g_strdup (g_file_info_get_display_name (info));
1690 is_folder = _gtk_file_info_consider_as_directory (info);
1692 gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1693 ICON_COLUMN, pixbuf,
1694 DISPLAY_NAME_COLUMN, data->label,
1695 IS_FOLDER_COLUMN, is_folder,
1699 g_object_unref (pixbuf);
1702 g_object_unref (data->button);
1703 g_free (data->label);
1704 gtk_tree_row_reference_free (data->row_ref);
1707 if (model_cancellable)
1708 g_object_unref (model_cancellable);
1712 set_info_for_file_at_iter (GtkFileChooserButton *button,
1716 struct SetDisplayNameData *data;
1717 GtkTreePath *tree_path;
1718 GCancellable *cancellable;
1720 data = g_new0 (struct SetDisplayNameData, 1);
1721 data->button = g_object_ref (button);
1722 data->label = _gtk_file_system_get_bookmark_label (button->priv->fs, file);
1724 tree_path = gtk_tree_model_get_path (button->priv->model, iter);
1725 data->row_ref = gtk_tree_row_reference_new (button->priv->model, tree_path);
1726 gtk_tree_path_free (tree_path);
1728 cancellable = _gtk_file_system_get_info (button->priv->fs, file,
1729 "standard::type,standard::icon,standard::display-name",
1730 set_info_get_info_cb, data);
1732 gtk_list_store_set (GTK_LIST_STORE (button->priv->model), iter,
1733 CANCELLABLE_COLUMN, cancellable,
1737 /* Shortcuts Model */
1739 model_get_type_position (GtkFileChooserButton *button,
1744 if (row_type == ROW_TYPE_SPECIAL)
1747 retval += button->priv->n_special;
1749 if (row_type == ROW_TYPE_VOLUME)
1752 retval += button->priv->n_volumes;
1754 if (row_type == ROW_TYPE_SHORTCUT)
1757 retval += button->priv->n_shortcuts;
1759 if (row_type == ROW_TYPE_BOOKMARK_SEPARATOR)
1762 retval += button->priv->has_bookmark_separator;
1764 if (row_type == ROW_TYPE_BOOKMARK)
1767 retval += button->priv->n_bookmarks;
1769 if (row_type == ROW_TYPE_CURRENT_FOLDER_SEPARATOR)
1772 retval += button->priv->has_current_folder_separator;
1774 if (row_type == ROW_TYPE_CURRENT_FOLDER)
1777 retval += button->priv->has_current_folder;
1779 if (row_type == ROW_TYPE_OTHER_SEPARATOR)
1782 retval += button->priv->has_other_separator;
1784 if (row_type == ROW_TYPE_OTHER)
1789 if (row_type == ROW_TYPE_EMPTY_SELECTION)
1792 g_assert_not_reached ();
1797 model_free_row_data (GtkFileChooserButton *button,
1802 GCancellable *cancellable;
1804 gtk_tree_model_get (button->priv->model, iter,
1807 CANCELLABLE_COLUMN, &cancellable,
1812 g_cancellable_cancel (cancellable);
1813 g_object_unref (cancellable);
1818 case ROW_TYPE_SPECIAL:
1819 case ROW_TYPE_SHORTCUT:
1820 case ROW_TYPE_BOOKMARK:
1821 case ROW_TYPE_CURRENT_FOLDER:
1822 g_object_unref (data);
1824 case ROW_TYPE_VOLUME:
1825 _gtk_file_system_volume_unref (data);
1833 model_add_special_get_info_cb (GCancellable *cancellable,
1835 const GError *error,
1838 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1842 GCancellable *model_cancellable = NULL;
1843 struct ChangeIconThemeData *data = user_data;
1846 if (!data->button->priv->model)
1847 /* button got destroyed */
1850 path = gtk_tree_row_reference_get_path (data->row_ref);
1852 /* Cancellable doesn't exist anymore in the model */
1855 gtk_tree_model_get_iter (data->button->priv->model, &iter, path);
1856 gtk_tree_path_free (path);
1858 gtk_tree_model_get (data->button->priv->model, &iter,
1859 CANCELLABLE_COLUMN, &model_cancellable,
1861 if (cancellable != model_cancellable)
1864 gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1865 CANCELLABLE_COLUMN, NULL,
1868 if (cancelled || error)
1871 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->button), data->button->priv->icon_size);
1875 gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1876 ICON_COLUMN, pixbuf,
1878 g_object_unref (pixbuf);
1881 gtk_tree_model_get (data->button->priv->model, &iter,
1882 DISPLAY_NAME_COLUMN, &name,
1885 gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1886 DISPLAY_NAME_COLUMN, g_file_info_get_display_name (info),
1891 g_object_unref (data->button);
1892 gtk_tree_row_reference_free (data->row_ref);
1895 if (model_cancellable)
1896 g_object_unref (model_cancellable);
1900 model_add_special (GtkFileChooserButton *button)
1902 const gchar *homedir;
1903 const gchar *desktopdir;
1904 GtkListStore *store;
1909 store = GTK_LIST_STORE (button->priv->model);
1910 pos = model_get_type_position (button, ROW_TYPE_SPECIAL);
1912 homedir = g_get_home_dir ();
1916 GtkTreePath *tree_path;
1917 GCancellable *cancellable;
1918 struct ChangeIconThemeData *info;
1920 file = g_file_new_for_path (homedir);
1921 gtk_list_store_insert (store, &iter, pos);
1924 info = g_new0 (struct ChangeIconThemeData, 1);
1925 info->button = g_object_ref (button);
1926 tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
1927 info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (store),
1929 gtk_tree_path_free (tree_path);
1931 cancellable = _gtk_file_system_get_info (button->priv->fs, file,
1932 "standard::icon,standard::display-name",
1933 model_add_special_get_info_cb, info);
1935 gtk_list_store_set (store, &iter,
1937 DISPLAY_NAME_COLUMN, NULL,
1938 TYPE_COLUMN, ROW_TYPE_SPECIAL,
1940 IS_FOLDER_COLUMN, TRUE,
1941 CANCELLABLE_COLUMN, cancellable,
1944 button->priv->n_special++;
1947 desktopdir = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
1949 /* "To disable a directory, point it to the homedir."
1950 * See http://freedesktop.org/wiki/Software/xdg-user-dirs
1952 if (g_strcmp0 (desktopdir, g_get_home_dir ()) != 0)
1954 GtkTreePath *tree_path;
1955 GCancellable *cancellable;
1956 struct ChangeIconThemeData *info;
1958 file = g_file_new_for_path (desktopdir);
1959 gtk_list_store_insert (store, &iter, pos);
1962 info = g_new0 (struct ChangeIconThemeData, 1);
1963 info->button = g_object_ref (button);
1964 tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
1965 info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (store),
1967 gtk_tree_path_free (tree_path);
1969 cancellable = _gtk_file_system_get_info (button->priv->fs, file,
1970 "standard::icon,standard::display-name",
1971 model_add_special_get_info_cb, info);
1973 gtk_list_store_set (store, &iter,
1974 TYPE_COLUMN, ROW_TYPE_SPECIAL,
1976 DISPLAY_NAME_COLUMN, _(DESKTOP_DISPLAY_NAME),
1978 IS_FOLDER_COLUMN, TRUE,
1979 CANCELLABLE_COLUMN, cancellable,
1982 button->priv->n_special++;
1987 model_add_volumes (GtkFileChooserButton *button,
1990 GtkListStore *store;
1992 gboolean local_only;
1998 store = GTK_LIST_STORE (button->priv->model);
1999 pos = model_get_type_position (button, ROW_TYPE_VOLUME);
2000 local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (button->priv->dialog));
2002 for (l = volumes; l; l = l->next)
2004 GtkFileSystemVolume *volume;
2007 gchar *display_name;
2013 if (_gtk_file_system_volume_is_mounted (volume))
2017 base_file = _gtk_file_system_volume_get_root (volume);
2018 if (base_file != NULL)
2020 if (!_gtk_file_has_native_path (base_file))
2022 g_object_unref (base_file);
2026 g_object_unref (base_file);
2031 pixbuf = _gtk_file_system_volume_render_icon (volume,
2032 GTK_WIDGET (button),
2033 button->priv->icon_size,
2035 display_name = _gtk_file_system_volume_get_display_name (volume);
2037 gtk_list_store_insert (store, &iter, pos);
2038 gtk_list_store_set (store, &iter,
2039 ICON_COLUMN, pixbuf,
2040 DISPLAY_NAME_COLUMN, display_name,
2041 TYPE_COLUMN, ROW_TYPE_VOLUME,
2042 DATA_COLUMN, _gtk_file_system_volume_ref (volume),
2043 IS_FOLDER_COLUMN, TRUE,
2047 g_object_unref (pixbuf);
2048 g_free (display_name);
2050 button->priv->n_volumes++;
2055 extern gchar * _gtk_file_chooser_label_for_file (GFile *file);
2058 model_add_bookmarks (GtkFileChooserButton *button,
2061 GtkListStore *store;
2064 gboolean local_only;
2070 store = GTK_LIST_STORE (button->priv->model);
2071 pos = model_get_type_position (button, ROW_TYPE_BOOKMARK);
2072 local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (button->priv->dialog));
2074 for (l = bookmarks; l; l = l->next)
2080 if (_gtk_file_has_native_path (file))
2082 gtk_list_store_insert (store, &iter, pos);
2083 gtk_list_store_set (store, &iter,
2085 DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
2086 TYPE_COLUMN, ROW_TYPE_BOOKMARK,
2087 DATA_COLUMN, g_object_ref (file),
2088 IS_FOLDER_COLUMN, FALSE,
2090 set_info_for_file_at_iter (button, file, &iter);
2095 GtkIconTheme *icon_theme;
2101 /* Don't call get_info for remote paths to avoid latency and
2103 * If we switch to a better bookmarks file format (XBEL), we
2104 * should use mime info to get a better icon.
2106 label = _gtk_file_system_get_bookmark_label (button->priv->fs, file);
2108 label = _gtk_file_chooser_label_for_file (file);
2110 icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
2111 pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote",
2112 button->priv->icon_size, 0, NULL);
2114 gtk_list_store_insert (store, &iter, pos);
2115 gtk_list_store_set (store, &iter,
2116 ICON_COLUMN, pixbuf,
2117 DISPLAY_NAME_COLUMN, label,
2118 TYPE_COLUMN, ROW_TYPE_BOOKMARK,
2119 DATA_COLUMN, g_object_ref (file),
2120 IS_FOLDER_COLUMN, TRUE,
2124 g_object_unref (pixbuf);
2127 button->priv->n_bookmarks++;
2131 if (button->priv->n_bookmarks > 0 &&
2132 !button->priv->has_bookmark_separator)
2134 pos = model_get_type_position (button, ROW_TYPE_BOOKMARK_SEPARATOR);
2136 gtk_list_store_insert (store, &iter, pos);
2137 gtk_list_store_set (store, &iter,
2139 DISPLAY_NAME_COLUMN, NULL,
2140 TYPE_COLUMN, ROW_TYPE_BOOKMARK_SEPARATOR,
2142 IS_FOLDER_COLUMN, FALSE,
2144 button->priv->has_bookmark_separator = TRUE;
2149 model_update_current_folder (GtkFileChooserButton *button,
2152 GtkListStore *store;
2159 store = GTK_LIST_STORE (button->priv->model);
2161 if (!button->priv->has_current_folder_separator)
2163 pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER_SEPARATOR);
2164 gtk_list_store_insert (store, &iter, pos);
2165 gtk_list_store_set (store, &iter,
2167 DISPLAY_NAME_COLUMN, NULL,
2168 TYPE_COLUMN, ROW_TYPE_CURRENT_FOLDER_SEPARATOR,
2170 IS_FOLDER_COLUMN, FALSE,
2172 button->priv->has_current_folder_separator = TRUE;
2175 pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER);
2176 if (!button->priv->has_current_folder)
2178 gtk_list_store_insert (store, &iter, pos);
2179 button->priv->has_current_folder = TRUE;
2183 gtk_tree_model_iter_nth_child (button->priv->model, &iter, NULL, pos);
2184 model_free_row_data (button, &iter);
2187 if (g_file_is_native (file))
2189 gtk_list_store_set (store, &iter,
2191 DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
2192 TYPE_COLUMN, ROW_TYPE_CURRENT_FOLDER,
2193 DATA_COLUMN, g_object_ref (file),
2194 IS_FOLDER_COLUMN, FALSE,
2196 set_info_for_file_at_iter (button, file, &iter);
2201 GtkIconTheme *icon_theme;
2204 /* Don't call get_info for remote paths to avoid latency and
2206 * If we switch to a better bookmarks file format (XBEL), we
2207 * should use mime info to get a better icon.
2209 label = _gtk_file_system_get_bookmark_label (button->priv->fs, file);
2211 label = _gtk_file_chooser_label_for_file (file);
2213 icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
2215 if (g_file_is_native (file))
2216 pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder",
2217 button->priv->icon_size, 0, NULL);
2219 pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote",
2220 button->priv->icon_size, 0, NULL);
2222 gtk_list_store_set (store, &iter,
2223 ICON_COLUMN, pixbuf,
2224 DISPLAY_NAME_COLUMN, label,
2225 TYPE_COLUMN, ROW_TYPE_CURRENT_FOLDER,
2226 DATA_COLUMN, g_object_ref (file),
2227 IS_FOLDER_COLUMN, TRUE,
2231 g_object_unref (pixbuf);
2236 model_add_other (GtkFileChooserButton *button)
2238 GtkListStore *store;
2242 store = GTK_LIST_STORE (button->priv->model);
2243 pos = model_get_type_position (button, ROW_TYPE_OTHER_SEPARATOR);
2245 gtk_list_store_insert (store, &iter, pos);
2246 gtk_list_store_set (store, &iter,
2248 DISPLAY_NAME_COLUMN, NULL,
2249 TYPE_COLUMN, ROW_TYPE_OTHER_SEPARATOR,
2251 IS_FOLDER_COLUMN, FALSE,
2253 button->priv->has_other_separator = TRUE;
2256 gtk_list_store_insert (store, &iter, pos);
2257 gtk_list_store_set (store, &iter,
2259 DISPLAY_NAME_COLUMN, _("Other…"),
2260 TYPE_COLUMN, ROW_TYPE_OTHER,
2262 IS_FOLDER_COLUMN, FALSE,
2267 model_add_empty_selection (GtkFileChooserButton *button)
2269 GtkListStore *store;
2273 store = GTK_LIST_STORE (button->priv->model);
2274 pos = model_get_type_position (button, ROW_TYPE_EMPTY_SELECTION);
2276 gtk_list_store_insert (store, &iter, pos);
2277 gtk_list_store_set (store, &iter,
2279 DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
2280 TYPE_COLUMN, ROW_TYPE_EMPTY_SELECTION,
2282 IS_FOLDER_COLUMN, FALSE,
2287 model_remove_rows (GtkFileChooserButton *button,
2291 GtkListStore *store;
2296 store = GTK_LIST_STORE (button->priv->model);
2302 if (!gtk_tree_model_iter_nth_child (button->priv->model, &iter, NULL, pos))
2303 g_assert_not_reached ();
2305 model_free_row_data (button, &iter);
2306 gtk_list_store_remove (store, &iter);
2314 test_if_file_is_visible (GtkFileSystem *fs,
2316 gboolean local_only,
2322 if (local_only && !_gtk_file_has_native_path (file))
2332 filter_model_visible_func (GtkTreeModel *model,
2336 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2337 GtkFileChooserButtonPrivate *priv = button->priv;
2340 gboolean local_only, retval, is_folder;
2342 type = ROW_TYPE_INVALID;
2344 local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->dialog));
2346 gtk_tree_model_get (model, iter,
2349 IS_FOLDER_COLUMN, &is_folder,
2354 case ROW_TYPE_CURRENT_FOLDER:
2357 case ROW_TYPE_SPECIAL:
2358 case ROW_TYPE_SHORTCUT:
2359 case ROW_TYPE_BOOKMARK:
2360 retval = test_if_file_is_visible (priv->fs, data, local_only, is_folder);
2362 case ROW_TYPE_VOLUME:
2367 if (_gtk_file_system_volume_is_mounted (data))
2371 base_file = _gtk_file_system_volume_get_root (data);
2375 if (!_gtk_file_has_native_path (base_file))
2377 g_object_unref (base_file);
2385 case ROW_TYPE_EMPTY_SELECTION:
2387 gboolean popup_shown;
2389 g_object_get (priv->combo_box,
2390 "popup-shown", &popup_shown,
2399 /* When the combo box is not popped up... */
2401 selected = get_selected_file (button);
2403 retval = FALSE; /* ... nonempty selection means the ROW_TYPE_EMPTY_SELECTION is *not* visible... */
2405 retval = TRUE; /* ... and empty selection means the ROW_TYPE_EMPTY_SELECTION *is* visible */
2408 g_object_unref (selected);
2423 name_cell_data_func (GtkCellLayout *layout,
2424 GtkCellRenderer *cell,
2425 GtkTreeModel *model,
2432 gtk_tree_model_get (model, iter,
2436 if (type == ROW_TYPE_CURRENT_FOLDER)
2437 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2439 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_NONE, NULL);
2443 combo_box_row_separator_func (GtkTreeModel *model,
2447 gchar type = ROW_TYPE_INVALID;
2449 gtk_tree_model_get (model, iter, TYPE_COLUMN, &type, -1);
2451 return (type == ROW_TYPE_BOOKMARK_SEPARATOR ||
2452 type == ROW_TYPE_CURRENT_FOLDER_SEPARATOR ||
2453 type == ROW_TYPE_OTHER_SEPARATOR);
2457 select_combo_box_row_no_notify (GtkFileChooserButton *button, int pos)
2459 GtkFileChooserButtonPrivate *priv = button->priv;
2460 GtkTreeIter iter, filter_iter;
2462 gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
2463 gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (priv->filter_model),
2464 &filter_iter, &iter);
2466 g_signal_handler_block (priv->combo_box, priv->combo_box_changed_id);
2467 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->combo_box), &filter_iter);
2468 g_signal_handler_unblock (priv->combo_box, priv->combo_box_changed_id);
2472 update_combo_box (GtkFileChooserButton *button)
2474 GtkFileChooserButtonPrivate *priv = button->priv;
2479 file = get_selected_file (button);
2483 gtk_tree_model_get_iter_first (priv->filter_model, &iter);
2490 type = ROW_TYPE_INVALID;
2493 gtk_tree_model_get (priv->filter_model, &iter,
2500 case ROW_TYPE_SPECIAL:
2501 case ROW_TYPE_SHORTCUT:
2502 case ROW_TYPE_BOOKMARK:
2503 case ROW_TYPE_CURRENT_FOLDER:
2504 row_found = (file && g_file_equal (data, file));
2506 case ROW_TYPE_VOLUME:
2510 base_file = _gtk_file_system_volume_get_root (data);
2513 row_found = (file && g_file_equal (base_file, file));
2514 g_object_unref (base_file);
2525 g_signal_handler_block (priv->combo_box, priv->combo_box_changed_id);
2526 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->combo_box),
2528 g_signal_handler_unblock (priv->combo_box,
2529 priv->combo_box_changed_id);
2532 while (!row_found && gtk_tree_model_iter_next (priv->filter_model, &iter));
2538 /* If it hasn't been found already, update & select the current-folder row. */
2541 model_update_current_folder (button, file);
2542 pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER);
2546 /* No selection; switch to that row */
2548 pos = model_get_type_position (button, ROW_TYPE_EMPTY_SELECTION);
2551 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2553 select_combo_box_row_no_notify (button, pos);
2557 g_object_unref (file);
2562 update_label_get_info_cb (GCancellable *cancellable,
2564 const GError *error,
2567 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
2569 GtkFileChooserButton *button = data;
2570 GtkFileChooserButtonPrivate *priv = button->priv;
2572 if (cancellable != priv->update_button_cancellable)
2575 priv->update_button_cancellable = NULL;
2577 if (cancelled || error)
2580 gtk_label_set_text (GTK_LABEL (priv->label), g_file_info_get_display_name (info));
2582 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (priv->image), priv->icon_size);
2585 pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (priv->image)),
2587 priv->icon_size, 0, NULL);
2589 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
2591 g_object_unref (pixbuf);
2594 emit_selection_changed_if_changing_selection (button);
2596 g_object_unref (button);
2597 g_object_unref (cancellable);
2601 update_label_and_image (GtkFileChooserButton *button)
2603 GtkFileChooserButtonPrivate *priv = button->priv;
2606 gboolean done_changing_selection;
2608 file = get_selected_file (button);
2611 done_changing_selection = FALSE;
2613 if (priv->update_button_cancellable)
2615 g_cancellable_cancel (priv->update_button_cancellable);
2616 priv->update_button_cancellable = NULL;
2621 GtkFileSystemVolume *volume = NULL;
2623 volume = _gtk_file_system_get_volume_for_file (priv->fs, file);
2628 base_file = _gtk_file_system_volume_get_root (volume);
2629 if (base_file && g_file_equal (base_file, file))
2633 label_text = _gtk_file_system_volume_get_display_name (volume);
2634 pixbuf = _gtk_file_system_volume_render_icon (volume,
2635 GTK_WIDGET (button),
2638 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
2640 g_object_unref (pixbuf);
2644 g_object_unref (base_file);
2646 _gtk_file_system_volume_unref (volume);
2650 done_changing_selection = TRUE;
2655 if (g_file_is_native (file))
2657 priv->update_button_cancellable =
2658 _gtk_file_system_get_info (priv->fs, file,
2659 "standard::icon,standard::display-name",
2660 update_label_get_info_cb,
2661 g_object_ref (button));
2667 label_text = _gtk_file_system_get_bookmark_label (button->priv->fs, file);
2668 pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (priv->image)),
2670 priv->icon_size, 0, NULL);
2671 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
2673 g_object_unref (pixbuf);
2675 done_changing_selection = TRUE;
2680 /* We know the selection is empty */
2681 done_changing_selection = TRUE;
2687 g_object_unref (file);
2691 gtk_label_set_text (GTK_LABEL (priv->label), label_text);
2692 g_free (label_text);
2696 gtk_label_set_text (GTK_LABEL (priv->label), _(FALLBACK_DISPLAY_NAME));
2697 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), NULL);
2700 if (done_changing_selection)
2701 emit_selection_changed_if_changing_selection (button);
2705 /* ************************ *
2706 * Child Object Callbacks *
2707 * ************************ */
2711 fs_volumes_changed_cb (GtkFileSystem *fs,
2714 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2715 GtkFileChooserButtonPrivate *priv = button->priv;
2718 model_remove_rows (user_data,
2719 model_get_type_position (user_data, ROW_TYPE_VOLUME),
2722 priv->n_volumes = 0;
2724 volumes = _gtk_file_system_list_volumes (fs);
2725 model_add_volumes (user_data, volumes);
2726 g_slist_free (volumes);
2728 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2730 update_label_and_image (user_data);
2731 update_combo_box (user_data);
2735 fs_bookmarks_changed_cb (GtkFileSystem *fs,
2738 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2739 GtkFileChooserButtonPrivate *priv = button->priv;
2742 bookmarks = _gtk_file_system_list_bookmarks (fs);
2743 model_remove_rows (user_data,
2744 model_get_type_position (user_data,
2745 ROW_TYPE_BOOKMARK_SEPARATOR),
2746 (priv->n_bookmarks + priv->has_bookmark_separator));
2747 priv->has_bookmark_separator = FALSE;
2748 priv->n_bookmarks = 0;
2749 model_add_bookmarks (user_data, bookmarks);
2750 g_slist_foreach (bookmarks, (GFunc) g_object_unref, NULL);
2751 g_slist_free (bookmarks);
2753 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2755 update_label_and_image (user_data);
2756 update_combo_box (user_data);
2760 save_inactive_state (GtkFileChooserButton *button)
2762 GtkFileChooserButtonPrivate *priv = button->priv;
2764 if (priv->current_folder_while_inactive)
2765 g_object_unref (priv->current_folder_while_inactive);
2767 if (priv->selection_while_inactive)
2768 g_object_unref (priv->selection_while_inactive);
2770 priv->current_folder_while_inactive = gtk_file_chooser_get_current_folder_file (GTK_FILE_CHOOSER (priv->dialog));
2771 priv->selection_while_inactive = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (priv->dialog));
2775 restore_inactive_state (GtkFileChooserButton *button)
2777 GtkFileChooserButtonPrivate *priv = button->priv;
2779 if (priv->current_folder_while_inactive)
2780 gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (priv->dialog), priv->current_folder_while_inactive, NULL);
2782 if (priv->selection_while_inactive)
2783 gtk_file_chooser_select_file (GTK_FILE_CHOOSER (priv->dialog), priv->selection_while_inactive, NULL);
2785 gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
2790 open_dialog (GtkFileChooserButton *button)
2792 GtkFileChooserButtonPrivate *priv = button->priv;
2794 /* Setup the dialog parent to be chooser button's toplevel, and be modal
2796 if (!gtk_widget_get_visible (priv->dialog))
2798 GtkWidget *toplevel;
2800 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
2802 if (gtk_widget_is_toplevel (toplevel) && GTK_IS_WINDOW (toplevel))
2804 if (GTK_WINDOW (toplevel) != gtk_window_get_transient_for (GTK_WINDOW (priv->dialog)))
2805 gtk_window_set_transient_for (GTK_WINDOW (priv->dialog),
2806 GTK_WINDOW (toplevel));
2808 gtk_window_set_modal (GTK_WINDOW (priv->dialog),
2809 gtk_window_get_modal (GTK_WINDOW (toplevel)));
2815 restore_inactive_state (button);
2816 priv->active = TRUE;
2819 gtk_widget_set_sensitive (priv->combo_box, FALSE);
2820 gtk_window_present (GTK_WINDOW (priv->dialog));
2825 combo_box_changed_cb (GtkComboBox *combo_box,
2830 if (gtk_combo_box_get_active_iter (combo_box, &iter))
2832 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2833 GtkFileChooserButtonPrivate *priv = button->priv;
2837 type = ROW_TYPE_INVALID;
2840 gtk_tree_model_get (priv->filter_model, &iter,
2847 case ROW_TYPE_SPECIAL:
2848 case ROW_TYPE_SHORTCUT:
2849 case ROW_TYPE_BOOKMARK:
2850 case ROW_TYPE_CURRENT_FOLDER:
2852 gtk_file_chooser_button_select_file (GTK_FILE_CHOOSER (button), data, NULL);
2854 case ROW_TYPE_VOLUME:
2858 base_file = _gtk_file_system_volume_get_root (data);
2861 gtk_file_chooser_button_select_file (GTK_FILE_CHOOSER (button), base_file, NULL);
2862 g_object_unref (base_file);
2866 case ROW_TYPE_OTHER:
2867 open_dialog (user_data);
2875 /* Calback for the "notify::popup-shown" signal on the combo box.
2876 * When the combo is popped up, we don't want the ROW_TYPE_EMPTY_SELECTION to be visible
2877 * at all; otherwise we would be showing a "(None)" item in the combo box's popup.
2879 * However, when the combo box is *not* popped up, we want the empty-selection row
2880 * to be visible depending on the selection.
2882 * Since all that is done through the filter_model_visible_func(), this means
2883 * that we need to refilter the model when the combo box pops up - hence the
2884 * present signal handler.
2887 combo_box_notify_popup_shown_cb (GObject *object,
2891 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2892 GtkFileChooserButtonPrivate *priv = button->priv;
2893 gboolean popup_shown;
2895 g_object_get (priv->combo_box,
2896 "popup-shown", &popup_shown,
2899 /* Indicate that the ROW_TYPE_EMPTY_SELECTION will change visibility... */
2900 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2902 /* If the combo box popup got dismissed, go back to showing the ROW_TYPE_EMPTY_SELECTION if needed */
2905 GFile *selected = get_selected_file (button);
2911 pos = model_get_type_position (button, ROW_TYPE_EMPTY_SELECTION);
2912 select_combo_box_row_no_notify (button, pos);
2915 g_object_unref (selected);
2921 button_clicked_cb (GtkButton *real_button,
2924 open_dialog (user_data);
2930 dialog_current_folder_changed_cb (GtkFileChooser *dialog,
2933 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2934 GtkFileChooserButtonPrivate *priv = button->priv;
2935 gboolean update_from_dialog;
2938 update_from_dialog = TRUE;
2939 else if (priv->folder_change_needs_notification)
2940 update_from_dialog = TRUE;
2942 update_from_dialog = FALSE;
2944 priv->folder_change_needs_notification = FALSE;
2946 if (update_from_dialog)
2948 save_inactive_state (button);
2950 update_label_and_image (button);
2951 update_combo_box (button);
2952 g_signal_emit_by_name (button, "current-folder-changed");
2957 dialog_selection_changed_cb (GtkFileChooser *dialog,
2960 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2961 GtkFileChooserButtonPrivate *priv = button->priv;
2962 gboolean update_from_dialog;
2965 update_from_dialog = TRUE;
2966 else if (priv->selection_change_needs_notification)
2967 update_from_dialog = TRUE;
2969 update_from_dialog = FALSE;
2971 priv->selection_change_needs_notification = FALSE;
2973 if (update_from_dialog)
2975 save_inactive_state (button);
2977 update_label_and_image (button);
2978 update_combo_box (button);
2979 g_signal_emit_by_name (button, "selection-changed");
2984 dialog_update_preview_cb (GtkFileChooser *dialog,
2987 g_signal_emit_by_name (user_data, "update-preview");
2991 dialog_notify_cb (GObject *dialog,
2997 iface = g_type_interface_peek (g_type_class_peek (G_OBJECT_TYPE (dialog)),
2998 GTK_TYPE_FILE_CHOOSER);
2999 if (g_object_interface_find_property (iface, pspec->name))
3000 g_object_notify (user_data, pspec->name);
3002 if (g_ascii_strcasecmp (pspec->name, "local-only") == 0)
3004 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
3005 GtkFileChooserButtonPrivate *priv = button->priv;
3007 if (priv->has_current_folder)
3013 pos = model_get_type_position (user_data,
3014 ROW_TYPE_CURRENT_FOLDER);
3015 gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
3018 gtk_tree_model_get (priv->model, &iter, DATA_COLUMN, &data, -1);
3020 /* If the path isn't local but we're in local-only mode now, remove
3021 * the custom-folder row */
3022 if (data && _gtk_file_has_native_path (G_FILE (data)) &&
3023 gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->dialog)))
3026 model_remove_rows (user_data, pos, 2);
3030 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
3031 update_combo_box (user_data);
3036 dialog_delete_event_cb (GtkWidget *dialog,
3040 g_signal_emit_by_name (dialog, "response", GTK_RESPONSE_DELETE_EVENT);
3046 dialog_response_cb (GtkDialog *dialog,
3050 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
3051 GtkFileChooserButtonPrivate *priv = button->priv;
3053 if (response == GTK_RESPONSE_ACCEPT ||
3054 response == GTK_RESPONSE_OK)
3056 save_inactive_state (button);
3058 g_signal_emit_by_name (button, "current-folder-changed");
3059 g_signal_emit_by_name (button, "selection-changed");
3063 restore_inactive_state (button);
3067 priv->active = FALSE;
3069 update_label_and_image (button);
3070 update_combo_box (button);
3072 gtk_widget_set_sensitive (priv->combo_box, TRUE);
3073 gtk_widget_hide (priv->dialog);
3075 if (response == GTK_RESPONSE_ACCEPT ||
3076 response == GTK_RESPONSE_OK)
3077 g_signal_emit (button, file_chooser_button_signals[FILE_SET], 0);
3081 /* ************************************************************************** *
3083 * ************************************************************************** */
3086 * gtk_file_chooser_button_new:
3087 * @title: the title of the browse dialog.
3088 * @action: the open mode for the widget.
3090 * Creates a new file-selecting button widget.
3092 * Returns: a new button widget.
3097 gtk_file_chooser_button_new (const gchar *title,
3098 GtkFileChooserAction action)
3100 g_return_val_if_fail (action == GTK_FILE_CHOOSER_ACTION_OPEN ||
3101 action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, NULL);
3103 return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
3105 "title", (title ? title : _(DEFAULT_TITLE)),
3110 * gtk_file_chooser_button_new_with_dialog:
3111 * @dialog: the widget to use as dialog
3113 * Creates a #GtkFileChooserButton widget which uses @dialog as its
3114 * file-picking window.
3116 * Note that @dialog must be a #GtkDialog (or subclass) which
3117 * implements the #GtkFileChooser interface and must not have
3118 * %GTK_DIALOG_DESTROY_WITH_PARENT set.
3120 * Also note that the dialog needs to have its confirmative button
3121 * added with response %GTK_RESPONSE_ACCEPT or %GTK_RESPONSE_OK in
3122 * order for the button to take over the file selected in the dialog.
3124 * Returns: a new button widget.
3129 gtk_file_chooser_button_new_with_dialog (GtkWidget *dialog)
3131 g_return_val_if_fail (GTK_IS_FILE_CHOOSER (dialog) && GTK_IS_DIALOG (dialog), NULL);
3133 return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
3139 * gtk_file_chooser_button_set_title:
3140 * @button: the button widget to modify.
3141 * @title: the new browse dialog title.
3143 * Modifies the @title of the browse dialog used by @button.
3148 gtk_file_chooser_button_set_title (GtkFileChooserButton *button,
3151 g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
3153 gtk_window_set_title (GTK_WINDOW (button->priv->dialog), title);
3154 g_object_notify (G_OBJECT (button), "title");
3158 * gtk_file_chooser_button_get_title:
3159 * @button: the button widget to examine.
3161 * Retrieves the title of the browse dialog used by @button. The returned value
3162 * should not be modified or freed.
3164 * Returns: a pointer to the browse dialog's title.
3169 gtk_file_chooser_button_get_title (GtkFileChooserButton *button)
3171 g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), NULL);
3173 return gtk_window_get_title (GTK_WINDOW (button->priv->dialog));
3177 * gtk_file_chooser_button_get_width_chars:
3178 * @button: the button widget to examine.
3180 * Retrieves the width in characters of the @button widget's entry and/or label.
3182 * Returns: an integer width (in characters) that the button will use to size itself.
3187 gtk_file_chooser_button_get_width_chars (GtkFileChooserButton *button)
3189 g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), -1);
3191 return gtk_label_get_width_chars (GTK_LABEL (button->priv->label));
3195 * gtk_file_chooser_button_set_width_chars:
3196 * @button: the button widget to examine.
3197 * @n_chars: the new width, in characters.
3199 * Sets the width (in characters) that @button will use to @n_chars.
3204 gtk_file_chooser_button_set_width_chars (GtkFileChooserButton *button,
3207 g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
3209 gtk_label_set_width_chars (GTK_LABEL (button->priv->label), n_chars);
3210 g_object_notify (G_OBJECT (button), "width-chars");
3214 * gtk_file_chooser_button_set_focus_on_click:
3215 * @button: a #GtkFileChooserButton
3216 * @focus_on_click: whether the button grabs focus when clicked with the mouse
3218 * Sets whether the button will grab focus when it is clicked with the mouse.
3219 * Making mouse clicks not grab focus is useful in places like toolbars where
3220 * you don't want the keyboard focus removed from the main area of the
3226 gtk_file_chooser_button_set_focus_on_click (GtkFileChooserButton *button,
3227 gboolean focus_on_click)
3229 GtkFileChooserButtonPrivate *priv;
3231 g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
3233 priv = button->priv;
3235 focus_on_click = focus_on_click != FALSE;
3237 if (priv->focus_on_click != focus_on_click)
3239 priv->focus_on_click = focus_on_click;
3240 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button), focus_on_click);
3241 gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (priv->combo_box), focus_on_click);
3243 g_object_notify (G_OBJECT (button), "focus-on-click");
3248 * gtk_file_chooser_button_get_focus_on_click:
3249 * @button: a #GtkFileChooserButton
3251 * Returns whether the button grabs focus when it is clicked with the mouse.
3252 * See gtk_file_chooser_button_set_focus_on_click().
3254 * Return value: %TRUE if the button grabs focus when it is clicked with
3260 gtk_file_chooser_button_get_focus_on_click (GtkFileChooserButton *button)
3262 g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), FALSE);
3264 return button->priv->focus_on_click;