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,
158 ROW_TYPE_INVALID = -1
163 /* ******************** *
164 * Private Structures *
165 * ******************** */
167 struct _GtkFileChooserButtonPrivate
173 GtkWidget *combo_box;
174 GtkCellRenderer *icon_cell;
175 GtkCellRenderer *name_cell;
178 GtkTreeModel *filter_model;
181 GFile *selection_while_inactive;
182 GFile *current_folder_while_inactive;
184 gulong combo_box_changed_id;
185 gulong dialog_file_activated_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;
224 /* ********************* *
225 * Function Prototypes *
226 * ********************* */
228 /* GtkFileChooserIface Functions */
229 static void gtk_file_chooser_button_file_chooser_iface_init (GtkFileChooserIface *iface);
230 static gboolean gtk_file_chooser_button_set_current_folder (GtkFileChooser *chooser,
233 static GFile *gtk_file_chooser_button_get_current_folder (GtkFileChooser *chooser);
234 static gboolean gtk_file_chooser_button_select_file (GtkFileChooser *chooser,
237 static void gtk_file_chooser_button_unselect_file (GtkFileChooser *chooser,
239 static void gtk_file_chooser_button_unselect_all (GtkFileChooser *chooser);
240 static GSList *gtk_file_chooser_button_get_files (GtkFileChooser *chooser);
241 static gboolean gtk_file_chooser_button_add_shortcut_folder (GtkFileChooser *chooser,
244 static gboolean gtk_file_chooser_button_remove_shortcut_folder (GtkFileChooser *chooser,
248 /* GObject Functions */
249 static GObject *gtk_file_chooser_button_constructor (GType type,
251 GObjectConstructParam *params);
252 static void gtk_file_chooser_button_set_property (GObject *object,
256 static void gtk_file_chooser_button_get_property (GObject *object,
260 static void gtk_file_chooser_button_finalize (GObject *object);
262 /* GtkWidget Functions */
263 static void gtk_file_chooser_button_destroy (GtkWidget *widget);
264 static void gtk_file_chooser_button_drag_data_received (GtkWidget *widget,
265 GdkDragContext *context,
268 GtkSelectionData *data,
271 static void gtk_file_chooser_button_show_all (GtkWidget *widget);
272 static void gtk_file_chooser_button_show (GtkWidget *widget);
273 static void gtk_file_chooser_button_hide (GtkWidget *widget);
274 static void gtk_file_chooser_button_map (GtkWidget *widget);
275 static gboolean gtk_file_chooser_button_mnemonic_activate (GtkWidget *widget,
276 gboolean group_cycling);
277 static void gtk_file_chooser_button_style_updated (GtkWidget *widget);
278 static void gtk_file_chooser_button_screen_changed (GtkWidget *widget,
279 GdkScreen *old_screen);
281 /* Utility Functions */
282 static GtkIconTheme *get_icon_theme (GtkWidget *widget);
283 static void set_info_for_file_at_iter (GtkFileChooserButton *fs,
287 static gint model_get_type_position (GtkFileChooserButton *button,
289 static void model_free_row_data (GtkFileChooserButton *button,
291 static inline void model_add_special (GtkFileChooserButton *button);
292 static inline void model_add_other (GtkFileChooserButton *button);
293 static void model_add_volumes (GtkFileChooserButton *button,
295 static void model_add_bookmarks (GtkFileChooserButton *button,
297 static void model_update_current_folder (GtkFileChooserButton *button,
299 static void model_remove_rows (GtkFileChooserButton *button,
303 static gboolean filter_model_visible_func (GtkTreeModel *model,
307 static gboolean combo_box_row_separator_func (GtkTreeModel *model,
310 static void name_cell_data_func (GtkCellLayout *layout,
311 GtkCellRenderer *cell,
315 static void open_dialog (GtkFileChooserButton *button);
316 static void update_combo_box (GtkFileChooserButton *button);
317 static void update_label_and_image (GtkFileChooserButton *button);
319 /* Child Object Callbacks */
320 static void fs_volumes_changed_cb (GtkFileSystem *fs,
322 static void fs_bookmarks_changed_cb (GtkFileSystem *fs,
325 static void combo_box_changed_cb (GtkComboBox *combo_box,
328 static void button_clicked_cb (GtkButton *real_button,
331 static void dialog_update_preview_cb (GtkFileChooser *dialog,
333 static void dialog_selection_changed_cb (GtkFileChooser *dialog,
335 static void dialog_file_activated_cb (GtkFileChooser *dialog,
337 static void dialog_current_folder_changed_cb (GtkFileChooser *dialog,
339 static void dialog_notify_cb (GObject *dialog,
342 static gboolean dialog_delete_event_cb (GtkWidget *dialog,
345 static void dialog_response_cb (GtkDialog *dialog,
349 static guint file_chooser_button_signals[LAST_SIGNAL] = { 0 };
351 /* ******************* *
352 * GType Declaration *
353 * ******************* */
355 G_DEFINE_TYPE_WITH_CODE (GtkFileChooserButton, gtk_file_chooser_button, GTK_TYPE_BOX, { \
356 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER, gtk_file_chooser_button_file_chooser_iface_init) \
360 /* ***************** *
362 * ***************** */
365 gtk_file_chooser_button_class_init (GtkFileChooserButtonClass * class)
367 GObjectClass *gobject_class;
368 GtkWidgetClass *widget_class;
370 gobject_class = G_OBJECT_CLASS (class);
371 widget_class = GTK_WIDGET_CLASS (class);
373 gobject_class->constructor = gtk_file_chooser_button_constructor;
374 gobject_class->set_property = gtk_file_chooser_button_set_property;
375 gobject_class->get_property = gtk_file_chooser_button_get_property;
376 gobject_class->finalize = gtk_file_chooser_button_finalize;
378 widget_class->destroy = gtk_file_chooser_button_destroy;
379 widget_class->drag_data_received = gtk_file_chooser_button_drag_data_received;
380 widget_class->show_all = gtk_file_chooser_button_show_all;
381 widget_class->show = gtk_file_chooser_button_show;
382 widget_class->hide = gtk_file_chooser_button_hide;
383 widget_class->map = gtk_file_chooser_button_map;
384 widget_class->style_updated = gtk_file_chooser_button_style_updated;
385 widget_class->screen_changed = gtk_file_chooser_button_screen_changed;
386 widget_class->mnemonic_activate = gtk_file_chooser_button_mnemonic_activate;
389 * GtkFileChooserButton::file-set:
390 * @widget: the object which received the signal.
392 * The ::file-set signal is emitted when the user selects a file.
394 * Note that this signal is only emitted when the <emphasis>user</emphasis>
399 file_chooser_button_signals[FILE_SET] =
400 g_signal_new (I_("file-set"),
401 G_TYPE_FROM_CLASS (gobject_class),
403 G_STRUCT_OFFSET (GtkFileChooserButtonClass, file_set),
405 _gtk_marshal_VOID__VOID,
409 * GtkFileChooserButton:dialog:
411 * Instance of the #GtkFileChooserDialog associated with the button.
415 g_object_class_install_property (gobject_class, PROP_DIALOG,
416 g_param_spec_object ("dialog",
418 P_("The file chooser dialog to use."),
419 GTK_TYPE_FILE_CHOOSER,
420 (GTK_PARAM_WRITABLE |
421 G_PARAM_CONSTRUCT_ONLY)));
424 * GtkFileChooserButton:focus-on-click:
426 * Whether the #GtkFileChooserButton button grabs focus when it is clicked
431 g_object_class_install_property (gobject_class,
433 g_param_spec_boolean ("focus-on-click",
434 P_("Focus on click"),
435 P_("Whether the button grabs focus when it is clicked with the mouse"),
437 GTK_PARAM_READWRITE));
440 * GtkFileChooserButton:title:
442 * Title to put on the #GtkFileChooserDialog associated with the button.
446 g_object_class_install_property (gobject_class, PROP_TITLE,
447 g_param_spec_string ("title",
449 P_("The title of the file chooser dialog."),
451 GTK_PARAM_READWRITE));
454 * GtkFileChooserButton:width-chars:
456 * The width of the entry and label inside the button, in characters.
460 g_object_class_install_property (gobject_class, PROP_WIDTH_CHARS,
461 g_param_spec_int ("width-chars",
462 P_("Width In Characters"),
463 P_("The desired width of the button widget, in characters."),
465 GTK_PARAM_READWRITE));
467 _gtk_file_chooser_install_properties (gobject_class);
469 g_type_class_add_private (class, sizeof (GtkFileChooserButtonPrivate));
473 gtk_file_chooser_button_init (GtkFileChooserButton *button)
475 GtkFileChooserButtonPrivate *priv;
476 GtkWidget *box, *image, *sep;
477 GtkTargetList *target_list;
479 priv = button->priv = G_TYPE_INSTANCE_GET_PRIVATE (button,
480 GTK_TYPE_FILE_CHOOSER_BUTTON,
481 GtkFileChooserButtonPrivate);
483 priv->icon_size = FALLBACK_ICON_SIZE;
484 priv->focus_on_click = TRUE;
486 gtk_widget_push_composite_child ();
489 priv->button = gtk_button_new ();
490 g_signal_connect (priv->button, "clicked",
491 G_CALLBACK (button_clicked_cb), button);
492 gtk_box_pack_start (GTK_BOX (button), priv->button, TRUE, TRUE, 0);
493 gtk_widget_set_halign (priv->button, GTK_ALIGN_FILL);
494 gtk_widget_show (priv->button);
496 box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
497 gtk_container_add (GTK_CONTAINER (priv->button), box);
498 gtk_widget_show (box);
500 priv->image = gtk_image_new ();
501 gtk_box_pack_start (GTK_BOX (box), priv->image, FALSE, FALSE, 0);
502 gtk_widget_show (priv->image);
504 priv->label = gtk_label_new (_(FALLBACK_DISPLAY_NAME));
505 gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_END);
506 gtk_widget_set_halign (priv->label, GTK_ALIGN_START);
507 gtk_widget_set_valign (priv->label, GTK_ALIGN_CENTER);
508 gtk_box_pack_start (GTK_BOX (box), priv->label, TRUE, TRUE, 0);
509 //gtk_container_add (GTK_CONTAINER (box), priv->label);
510 gtk_widget_show (priv->label);
512 sep = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
513 gtk_box_pack_start (GTK_BOX (box), sep, FALSE, FALSE, 0);
514 gtk_widget_show (sep);
516 image = gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU);
517 gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 0);
518 gtk_widget_show (image);
521 /* Keep in sync with columns enum, line 88 */
523 GTK_TREE_MODEL (gtk_list_store_new (NUM_COLUMNS,
524 GDK_TYPE_PIXBUF, /* Icon */
525 G_TYPE_STRING, /* Display Name */
526 G_TYPE_CHAR, /* Row Type */
527 G_TYPE_POINTER /* Volume || Path */,
528 G_TYPE_BOOLEAN /* Is Folder? */,
529 G_TYPE_POINTER /* cancellable */));
531 priv->combo_box = gtk_combo_box_new ();
532 priv->combo_box_changed_id =
533 g_signal_connect (priv->combo_box, "changed",
534 G_CALLBACK (combo_box_changed_cb), button);
535 gtk_box_pack_start (GTK_BOX (button), priv->combo_box, TRUE, TRUE, 0);
536 gtk_widget_set_halign (priv->combo_box, GTK_ALIGN_FILL);
538 priv->icon_cell = gtk_cell_renderer_pixbuf_new ();
539 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->combo_box),
540 priv->icon_cell, FALSE);
541 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->combo_box),
542 priv->icon_cell, "pixbuf", ICON_COLUMN);
544 priv->name_cell = gtk_cell_renderer_text_new ();
545 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->combo_box),
546 priv->name_cell, TRUE);
547 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->combo_box),
548 priv->name_cell, "text", DISPLAY_NAME_COLUMN);
549 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->combo_box),
550 priv->name_cell, name_cell_data_func,
553 gtk_widget_pop_composite_child ();
556 gtk_drag_dest_set (GTK_WIDGET (button),
557 (GTK_DEST_DEFAULT_ALL),
560 target_list = gtk_target_list_new (NULL, 0);
561 gtk_target_list_add_uri_targets (target_list, TEXT_URI_LIST);
562 gtk_target_list_add_text_targets (target_list, TEXT_PLAIN);
563 gtk_drag_dest_set_target_list (GTK_WIDGET (button), target_list);
564 gtk_target_list_unref (target_list);
568 /* ******************************* *
569 * GtkFileChooserIface Functions *
570 * ******************************* */
572 gtk_file_chooser_button_file_chooser_iface_init (GtkFileChooserIface *iface)
574 _gtk_file_chooser_delegate_iface_init (iface);
576 iface->set_current_folder = gtk_file_chooser_button_set_current_folder;
577 iface->get_current_folder = gtk_file_chooser_button_get_current_folder;
578 iface->select_file = gtk_file_chooser_button_select_file;
579 iface->unselect_file = gtk_file_chooser_button_unselect_file;
580 iface->unselect_all = gtk_file_chooser_button_unselect_all;
581 iface->get_files = gtk_file_chooser_button_get_files;
582 iface->add_shortcut_folder = gtk_file_chooser_button_add_shortcut_folder;
583 iface->remove_shortcut_folder = gtk_file_chooser_button_remove_shortcut_folder;
587 gtk_file_chooser_button_set_current_folder (GtkFileChooser *chooser,
591 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
592 GtkFileChooserButtonPrivate *priv = button->priv;
593 GtkFileChooser *delegate;
595 delegate = g_object_get_qdata (G_OBJECT (chooser),
596 GTK_FILE_CHOOSER_DELEGATE_QUARK);
599 return gtk_file_chooser_set_current_folder_file (delegate, file, error);
602 if (priv->current_folder_while_inactive)
603 g_object_unref (priv->current_folder_while_inactive);
605 priv->current_folder_while_inactive = g_object_ref (file);
607 update_combo_box (button);
609 g_signal_emit_by_name (button, "current-folder-changed");
616 gtk_file_chooser_button_get_current_folder (GtkFileChooser *chooser)
618 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
619 GtkFileChooserButtonPrivate *priv = button->priv;
620 GtkFileChooser *delegate;
622 delegate = g_object_get_qdata (G_OBJECT (chooser),
623 GTK_FILE_CHOOSER_DELEGATE_QUARK);
626 return gtk_file_chooser_get_current_folder_file (delegate);
629 if (priv->current_folder_while_inactive)
630 return g_object_ref (priv->current_folder_while_inactive);
637 gtk_file_chooser_button_select_file (GtkFileChooser *chooser,
641 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
642 GtkFileChooserButtonPrivate *priv = button->priv;
643 GtkFileChooser *delegate;
645 delegate = g_object_get_qdata (G_OBJECT (chooser),
646 GTK_FILE_CHOOSER_DELEGATE_QUARK);
649 return gtk_file_chooser_select_file (delegate, file, error);
652 if (priv->selection_while_inactive)
653 g_object_unref (priv->selection_while_inactive);
655 priv->selection_while_inactive = g_object_ref (file);
657 update_label_and_image (button);
658 update_combo_box (button);
665 gtk_file_chooser_button_unselect_file (GtkFileChooser *chooser,
668 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
669 GtkFileChooserButtonPrivate *priv = button->priv;
670 GtkFileChooser *delegate;
672 delegate = g_object_get_qdata (G_OBJECT (chooser),
673 GTK_FILE_CHOOSER_DELEGATE_QUARK);
676 gtk_file_chooser_unselect_file (delegate, file);
679 if (g_file_equal (priv->selection_while_inactive, file))
681 if (priv->selection_while_inactive)
683 g_object_unref (priv->selection_while_inactive);
684 priv->selection_while_inactive = NULL;
687 update_label_and_image (button);
688 update_combo_box (button);
694 gtk_file_chooser_button_unselect_all (GtkFileChooser *chooser)
696 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
697 GtkFileChooserButtonPrivate *priv = button->priv;
698 GtkFileChooser *delegate;
700 delegate = g_object_get_qdata (G_OBJECT (chooser),
701 GTK_FILE_CHOOSER_DELEGATE_QUARK);
704 gtk_file_chooser_unselect_all (delegate);
707 if (priv->selection_while_inactive)
709 g_object_unref (priv->selection_while_inactive);
710 priv->selection_while_inactive = NULL;
712 update_label_and_image (button);
713 update_combo_box (button);
719 get_selected_file (GtkFileChooserButton *button)
721 GtkFileChooserButtonPrivate *priv = button->priv;
724 return gtk_file_chooser_get_file (GTK_FILE_CHOOSER (priv->dialog));
727 if (priv->selection_while_inactive)
728 return g_object_ref (priv->selection_while_inactive);
729 else if (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)) == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
731 /* If there is no "real" selection in SELECT_FOLDER mode, then we'll just return
732 * the current folder, since that is what GtkFileChooserDefault would do.
734 if (priv->current_folder_while_inactive)
735 return g_object_ref (priv->current_folder_while_inactive);
743 gtk_file_chooser_button_get_files (GtkFileChooser *chooser)
745 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
747 return g_slist_prepend (NULL, get_selected_file (button));
751 gtk_file_chooser_button_add_shortcut_folder (GtkFileChooser *chooser,
755 GtkFileChooser *delegate;
758 delegate = g_object_get_qdata (G_OBJECT (chooser),
759 GTK_FILE_CHOOSER_DELEGATE_QUARK);
760 retval = _gtk_file_chooser_add_shortcut_folder (delegate, file, error);
764 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
765 GtkFileChooserButtonPrivate *priv = button->priv;
769 pos = model_get_type_position (button, ROW_TYPE_SHORTCUT);
770 pos += priv->n_shortcuts;
772 gtk_list_store_insert (GTK_LIST_STORE (priv->model), &iter, pos);
773 gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter,
775 DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
776 TYPE_COLUMN, ROW_TYPE_SHORTCUT,
777 DATA_COLUMN, g_object_ref (file),
778 IS_FOLDER_COLUMN, FALSE,
780 set_info_for_file_at_iter (button, file, &iter);
783 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
790 gtk_file_chooser_button_remove_shortcut_folder (GtkFileChooser *chooser,
794 GtkFileChooser *delegate;
797 delegate = g_object_get_qdata (G_OBJECT (chooser),
798 GTK_FILE_CHOOSER_DELEGATE_QUARK);
800 retval = _gtk_file_chooser_remove_shortcut_folder (delegate, file, error);
804 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
805 GtkFileChooserButtonPrivate *priv = button->priv;
810 pos = model_get_type_position (button, ROW_TYPE_SHORTCUT);
811 gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
817 gtk_tree_model_get (priv->model, &iter,
822 if (type == ROW_TYPE_SHORTCUT &&
823 data && g_file_equal (data, file))
825 model_free_row_data (GTK_FILE_CHOOSER_BUTTON (chooser), &iter);
826 gtk_list_store_remove (GTK_LIST_STORE (priv->model), &iter);
828 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
829 update_combo_box (GTK_FILE_CHOOSER_BUTTON (chooser));
833 while (type == ROW_TYPE_SHORTCUT &&
834 gtk_tree_model_iter_next (priv->model, &iter));
841 /* ******************* *
842 * GObject Functions *
843 * ******************* */
846 gtk_file_chooser_button_constructor (GType type,
848 GObjectConstructParam *params)
851 GtkFileChooserButton *button;
852 GtkFileChooserButtonPrivate *priv;
855 object = G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->constructor (type,
858 button = GTK_FILE_CHOOSER_BUTTON (object);
863 priv->dialog = gtk_file_chooser_dialog_new (NULL, NULL,
864 GTK_FILE_CHOOSER_ACTION_OPEN,
871 gtk_dialog_set_default_response (GTK_DIALOG (priv->dialog),
872 GTK_RESPONSE_ACCEPT);
873 gtk_dialog_set_alternative_button_order (GTK_DIALOG (priv->dialog),
878 gtk_file_chooser_button_set_title (button, _(DEFAULT_TITLE));
880 else if (!gtk_window_get_title (GTK_WINDOW (priv->dialog)))
882 gtk_file_chooser_button_set_title (button, _(DEFAULT_TITLE));
885 g_signal_connect (priv->dialog, "delete-event",
886 G_CALLBACK (dialog_delete_event_cb), object);
887 g_signal_connect (priv->dialog, "response",
888 G_CALLBACK (dialog_response_cb), object);
890 /* This is used, instead of the standard delegate, to ensure that signals are only
891 * delegated when the OK button is pressed. */
892 g_object_set_qdata (object, GTK_FILE_CHOOSER_DELEGATE_QUARK, priv->dialog);
893 priv->dialog_folder_changed_id =
894 g_signal_connect (priv->dialog, "current-folder-changed",
895 G_CALLBACK (dialog_current_folder_changed_cb), object);
896 priv->dialog_file_activated_id =
897 g_signal_connect (priv->dialog, "file-activated",
898 G_CALLBACK (dialog_file_activated_cb), object);
899 priv->dialog_selection_changed_id =
900 g_signal_connect (priv->dialog, "selection-changed",
901 G_CALLBACK (dialog_selection_changed_cb), object);
902 g_signal_connect (priv->dialog, "update-preview",
903 G_CALLBACK (dialog_update_preview_cb), object);
904 g_signal_connect (priv->dialog, "notify",
905 G_CALLBACK (dialog_notify_cb), object);
906 g_object_add_weak_pointer (G_OBJECT (priv->dialog),
907 (gpointer) (&priv->dialog));
910 g_object_ref (_gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog)));
912 model_add_special (button);
914 list = _gtk_file_system_list_volumes (priv->fs);
915 model_add_volumes (button, list);
918 list = _gtk_file_system_list_bookmarks (priv->fs);
919 model_add_bookmarks (button, list);
920 g_slist_foreach (list, (GFunc) g_object_unref, NULL);
923 model_add_other (button);
925 priv->filter_model = gtk_tree_model_filter_new (priv->model, NULL);
926 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->filter_model),
927 filter_model_visible_func,
930 gtk_combo_box_set_model (GTK_COMBO_BOX (priv->combo_box), priv->filter_model);
931 gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (priv->combo_box),
932 combo_box_row_separator_func,
935 /* set up the action for a user-provided dialog, this also updates
936 * the label, image and combobox
938 g_object_set (object,
939 "action", gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)),
942 priv->fs_volumes_changed_id =
943 g_signal_connect (priv->fs, "volumes-changed",
944 G_CALLBACK (fs_volumes_changed_cb), object);
945 priv->fs_bookmarks_changed_id =
946 g_signal_connect (priv->fs, "bookmarks-changed",
947 G_CALLBACK (fs_bookmarks_changed_cb), object);
949 update_label_and_image (button);
950 update_combo_box (button);
956 gtk_file_chooser_button_set_property (GObject *object,
961 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
962 GtkFileChooserButtonPrivate *priv = button->priv;
968 priv->dialog = g_value_get_object (value);
970 case PROP_FOCUS_ON_CLICK:
971 gtk_file_chooser_button_set_focus_on_click (button, g_value_get_boolean (value));
973 case PROP_WIDTH_CHARS:
974 gtk_file_chooser_button_set_width_chars (GTK_FILE_CHOOSER_BUTTON (object),
975 g_value_get_int (value));
977 case GTK_FILE_CHOOSER_PROP_ACTION:
978 switch (g_value_get_enum (value))
980 case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
981 case GTK_FILE_CHOOSER_ACTION_SAVE:
986 eclass = g_type_class_peek (GTK_TYPE_FILE_CHOOSER_ACTION);
987 eval = g_enum_get_value (eclass, g_value_get_enum (value));
988 g_warning ("%s: Choosers of type `%s' do not support `%s'.",
989 G_STRFUNC, G_OBJECT_TYPE_NAME (object), eval->value_name);
991 g_value_set_enum ((GValue *) value, GTK_FILE_CHOOSER_ACTION_OPEN);
996 g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
997 update_label_and_image (GTK_FILE_CHOOSER_BUTTON (object));
998 update_combo_box (GTK_FILE_CHOOSER_BUTTON (object));
1000 switch (g_value_get_enum (value))
1002 case GTK_FILE_CHOOSER_ACTION_OPEN:
1003 gtk_widget_hide (priv->combo_box);
1004 gtk_widget_show (priv->button);
1006 case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
1007 gtk_widget_hide (priv->button);
1008 gtk_widget_show (priv->combo_box);
1011 g_assert_not_reached ();
1017 case GTK_FILE_CHOOSER_PROP_FILTER:
1018 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
1019 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
1020 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
1021 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
1022 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
1023 case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
1024 case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
1025 g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
1028 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
1029 g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
1030 fs_volumes_changed_cb (priv->fs, button);
1031 fs_bookmarks_changed_cb (priv->fs, button);
1034 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
1035 g_warning ("%s: Choosers of type `%s` do not support selecting multiple files.",
1036 G_STRFUNC, G_OBJECT_TYPE_NAME (object));
1039 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1045 gtk_file_chooser_button_get_property (GObject *object,
1050 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
1051 GtkFileChooserButtonPrivate *priv = button->priv;
1055 case PROP_WIDTH_CHARS:
1056 g_value_set_int (value,
1057 gtk_label_get_width_chars (GTK_LABEL (priv->label)));
1059 case PROP_FOCUS_ON_CLICK:
1060 g_value_set_boolean (value,
1061 gtk_file_chooser_button_get_focus_on_click (button));
1065 case GTK_FILE_CHOOSER_PROP_ACTION:
1066 case GTK_FILE_CHOOSER_PROP_FILTER:
1067 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
1068 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
1069 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
1070 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
1071 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
1072 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
1073 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
1074 case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
1075 case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
1076 g_object_get_property (G_OBJECT (priv->dialog), pspec->name, value);
1080 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1086 gtk_file_chooser_button_finalize (GObject *object)
1088 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
1089 GtkFileChooserButtonPrivate *priv = button->priv;
1091 if (priv->selection_while_inactive)
1092 g_object_unref (priv->selection_while_inactive);
1094 if (priv->current_folder_while_inactive)
1095 g_object_unref (priv->current_folder_while_inactive);
1097 G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->finalize (object);
1100 /* ********************* *
1101 * GtkWidget Functions *
1102 * ********************* */
1105 gtk_file_chooser_button_destroy (GtkWidget *widget)
1107 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1108 GtkFileChooserButtonPrivate *priv = button->priv;
1112 if (priv->dialog != NULL)
1114 gtk_widget_destroy (priv->dialog);
1115 priv->dialog = NULL;
1118 if (priv->model && gtk_tree_model_get_iter_first (priv->model, &iter)) do
1120 model_free_row_data (button, &iter);
1122 while (gtk_tree_model_iter_next (priv->model, &iter));
1124 if (priv->dnd_select_folder_cancellable)
1126 g_cancellable_cancel (priv->dnd_select_folder_cancellable);
1127 priv->dnd_select_folder_cancellable = NULL;
1130 if (priv->update_button_cancellable)
1132 g_cancellable_cancel (priv->update_button_cancellable);
1133 priv->update_button_cancellable = NULL;
1136 if (priv->change_icon_theme_cancellables)
1138 for (l = priv->change_icon_theme_cancellables; l; l = l->next)
1140 GCancellable *cancellable = G_CANCELLABLE (l->data);
1141 g_cancellable_cancel (cancellable);
1143 g_slist_free (priv->change_icon_theme_cancellables);
1144 priv->change_icon_theme_cancellables = NULL;
1149 g_object_unref (priv->model);
1153 if (priv->filter_model)
1155 g_object_unref (priv->filter_model);
1156 priv->filter_model = NULL;
1161 g_signal_handler_disconnect (priv->fs, priv->fs_volumes_changed_id);
1162 g_signal_handler_disconnect (priv->fs, priv->fs_bookmarks_changed_id);
1163 g_object_unref (priv->fs);
1167 GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->destroy (widget);
1170 struct DndSelectFolderData
1172 GtkFileSystem *file_system;
1173 GtkFileChooserButton *button;
1174 GtkFileChooserAction action;
1182 dnd_select_folder_get_info_cb (GCancellable *cancellable,
1184 const GError *error,
1187 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1188 struct DndSelectFolderData *data = user_data;
1190 if (cancellable != data->button->priv->dnd_select_folder_cancellable)
1192 g_object_unref (data->button);
1193 g_object_unref (data->file);
1194 g_strfreev (data->uris);
1197 g_object_unref (cancellable);
1201 data->button->priv->dnd_select_folder_cancellable = NULL;
1203 if (!cancelled && !error && info != NULL)
1207 is_folder = _gtk_file_info_consider_as_directory (info);
1210 (((data->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER && is_folder) ||
1211 (data->action == GTK_FILE_CHOOSER_ACTION_OPEN && !is_folder)) &&
1212 gtk_file_chooser_select_file (GTK_FILE_CHOOSER (data->button->priv->dialog),
1216 data->selected = FALSE;
1218 if (data->selected || data->uris[++data->i] == NULL)
1220 g_signal_emit (data->button, file_chooser_button_signals[FILE_SET], 0);
1222 g_object_unref (data->button);
1223 g_object_unref (data->file);
1224 g_strfreev (data->uris);
1227 g_object_unref (cancellable);
1232 g_object_unref (data->file);
1234 data->file = g_file_new_for_uri (data->uris[data->i]);
1236 data->button->priv->dnd_select_folder_cancellable =
1237 _gtk_file_system_get_info (data->file_system, data->file,
1239 dnd_select_folder_get_info_cb, user_data);
1241 g_object_unref (cancellable);
1245 gtk_file_chooser_button_drag_data_received (GtkWidget *widget,
1246 GdkDragContext *context,
1249 GtkSelectionData *data,
1253 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1254 GtkFileChooserButtonPrivate *priv = button->priv;
1258 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->drag_data_received != NULL)
1259 GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->drag_data_received (widget,
1265 if (widget == NULL || context == NULL || data == NULL || gtk_selection_data_get_length (data) < 0)
1273 struct DndSelectFolderData *info;
1275 uris = gtk_selection_data_get_uris (data);
1280 info = g_new0 (struct DndSelectFolderData, 1);
1281 info->button = g_object_ref (button);
1284 info->selected = FALSE;
1285 info->file_system = priv->fs;
1286 g_object_get (priv->dialog, "action", &info->action, NULL);
1288 info->file = g_file_new_for_uri (info->uris[info->i]);
1290 if (priv->dnd_select_folder_cancellable)
1291 g_cancellable_cancel (priv->dnd_select_folder_cancellable);
1293 priv->dnd_select_folder_cancellable =
1294 _gtk_file_system_get_info (priv->fs, info->file,
1296 dnd_select_folder_get_info_cb, info);
1301 text = (char*) gtk_selection_data_get_text (data);
1302 file = g_file_new_for_uri (text);
1303 gtk_file_chooser_select_file (GTK_FILE_CHOOSER (priv->dialog), file,
1305 g_object_unref (file);
1307 g_signal_emit (button, file_chooser_button_signals[FILE_SET], 0);
1314 gtk_drag_finish (context, TRUE, FALSE, drag_time);
1318 gtk_file_chooser_button_show_all (GtkWidget *widget)
1320 gtk_widget_show (widget);
1324 gtk_file_chooser_button_show (GtkWidget *widget)
1326 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1327 GtkFileChooserButtonPrivate *priv = button->priv;
1329 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->show)
1330 GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->show (widget);
1333 open_dialog (GTK_FILE_CHOOSER_BUTTON (widget));
1337 gtk_file_chooser_button_hide (GtkWidget *widget)
1339 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1340 GtkFileChooserButtonPrivate *priv = button->priv;
1342 gtk_widget_hide (priv->dialog);
1344 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide)
1345 GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide (widget);
1349 gtk_file_chooser_button_map (GtkWidget *widget)
1351 GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->map (widget);
1355 gtk_file_chooser_button_mnemonic_activate (GtkWidget *widget,
1356 gboolean group_cycling)
1358 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1359 GtkFileChooserButtonPrivate *priv = button->priv;
1361 switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)))
1363 case GTK_FILE_CHOOSER_ACTION_OPEN:
1364 gtk_widget_grab_focus (priv->button);
1366 case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
1367 return gtk_widget_mnemonic_activate (priv->combo_box, group_cycling);
1370 g_assert_not_reached ();
1377 /* Changes the icons wherever it is needed */
1378 struct ChangeIconThemeData
1380 GtkFileChooserButton *button;
1381 GtkTreeRowReference *row_ref;
1385 change_icon_theme_get_info_cb (GCancellable *cancellable,
1387 const GError *error,
1390 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1392 struct ChangeIconThemeData *data = user_data;
1394 if (!g_slist_find (data->button->priv->change_icon_theme_cancellables, cancellable))
1397 data->button->priv->change_icon_theme_cancellables =
1398 g_slist_remove (data->button->priv->change_icon_theme_cancellables, cancellable);
1400 if (cancelled || error)
1403 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->button), data->button->priv->icon_size);
1411 width = MAX (width, gdk_pixbuf_get_width (pixbuf));
1413 path = gtk_tree_row_reference_get_path (data->row_ref);
1416 gtk_tree_model_get_iter (data->button->priv->model, &iter, path);
1417 gtk_tree_path_free (path);
1419 gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1420 ICON_COLUMN, pixbuf,
1423 g_object_set (data->button->priv->icon_cell,
1427 g_object_unref (pixbuf);
1431 g_object_unref (data->button);
1432 gtk_tree_row_reference_free (data->row_ref);
1435 g_object_unref (cancellable);
1439 change_icon_theme (GtkFileChooserButton *button)
1441 GtkFileChooserButtonPrivate *priv = button->priv;
1442 GtkSettings *settings;
1443 GtkIconTheme *theme;
1446 gint width = 0, height = 0;
1448 for (l = button->priv->change_icon_theme_cancellables; l; l = l->next)
1450 GCancellable *cancellable = G_CANCELLABLE (l->data);
1451 g_cancellable_cancel (cancellable);
1453 g_slist_free (button->priv->change_icon_theme_cancellables);
1454 button->priv->change_icon_theme_cancellables = NULL;
1456 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
1458 if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
1460 priv->icon_size = MAX (width, height);
1462 priv->icon_size = FALLBACK_ICON_SIZE;
1464 update_label_and_image (button);
1466 gtk_tree_model_get_iter_first (priv->model, &iter);
1468 theme = get_icon_theme (GTK_WIDGET (button));
1476 type = ROW_TYPE_INVALID;
1477 gtk_tree_model_get (priv->model, &iter,
1484 case ROW_TYPE_SPECIAL:
1485 case ROW_TYPE_SHORTCUT:
1486 case ROW_TYPE_BOOKMARK:
1487 case ROW_TYPE_CURRENT_FOLDER:
1490 if (g_file_is_native (G_FILE (data)))
1493 GCancellable *cancellable;
1494 struct ChangeIconThemeData *info;
1496 info = g_new0 (struct ChangeIconThemeData, 1);
1497 info->button = g_object_ref (button);
1498 path = gtk_tree_model_get_path (priv->model, &iter);
1499 info->row_ref = gtk_tree_row_reference_new (priv->model, path);
1500 gtk_tree_path_free (path);
1503 _gtk_file_system_get_info (priv->fs, data,
1505 change_icon_theme_get_info_cb,
1507 button->priv->change_icon_theme_cancellables =
1508 g_slist_append (button->priv->change_icon_theme_cancellables, cancellable);
1512 /* Don't call get_info for remote paths to avoid latency and
1514 * If we switch to a better bookmarks file format (XBEL), we
1515 * should use mime info to get a better icon.
1517 pixbuf = gtk_icon_theme_load_icon (theme, "folder-remote",
1518 priv->icon_size, 0, NULL);
1521 pixbuf = gtk_icon_theme_load_icon (theme, FALLBACK_ICON_NAME,
1522 priv->icon_size, 0, NULL);
1524 case ROW_TYPE_VOLUME:
1526 pixbuf = _gtk_file_system_volume_render_icon (data,
1527 GTK_WIDGET (button),
1531 pixbuf = gtk_icon_theme_load_icon (theme, FALLBACK_ICON_NAME,
1532 priv->icon_size, 0, NULL);
1540 width = MAX (width, gdk_pixbuf_get_width (pixbuf));
1542 gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter,
1543 ICON_COLUMN, pixbuf,
1547 g_object_unref (pixbuf);
1549 while (gtk_tree_model_iter_next (priv->model, &iter));
1551 g_object_set (button->priv->icon_cell,
1557 gtk_file_chooser_button_style_updated (GtkWidget *widget)
1559 GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->style_updated (widget);
1561 if (gtk_widget_has_screen (widget))
1562 change_icon_theme (GTK_FILE_CHOOSER_BUTTON (widget));
1566 gtk_file_chooser_button_screen_changed (GtkWidget *widget,
1567 GdkScreen *old_screen)
1569 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->screen_changed)
1570 GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->screen_changed (widget,
1573 change_icon_theme (GTK_FILE_CHOOSER_BUTTON (widget));
1577 /* ******************* *
1578 * Utility Functions *
1579 * ******************* */
1582 static GtkIconTheme *
1583 get_icon_theme (GtkWidget *widget)
1585 if (gtk_widget_has_screen (widget))
1586 return gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
1588 return gtk_icon_theme_get_default ();
1592 struct SetDisplayNameData
1594 GtkFileChooserButton *button;
1596 GtkTreeRowReference *row_ref;
1600 set_info_get_info_cb (GCancellable *cancellable,
1602 const GError *error,
1603 gpointer callback_data)
1605 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1609 GCancellable *model_cancellable = NULL;
1610 struct SetDisplayNameData *data = callback_data;
1613 if (!data->button->priv->model)
1614 /* button got destroyed */
1617 path = gtk_tree_row_reference_get_path (data->row_ref);
1619 /* Cancellable doesn't exist anymore in the model */
1622 gtk_tree_model_get_iter (data->button->priv->model, &iter, path);
1623 gtk_tree_path_free (path);
1625 /* Validate the cancellable */
1626 gtk_tree_model_get (data->button->priv->model, &iter,
1627 CANCELLABLE_COLUMN, &model_cancellable,
1629 if (cancellable != model_cancellable)
1632 gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1633 CANCELLABLE_COLUMN, NULL,
1636 if (cancelled || error)
1637 /* There was an error, leave the fallback name in there */
1640 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->button), data->button->priv->icon_size);
1643 data->label = g_strdup (g_file_info_get_display_name (info));
1645 is_folder = _gtk_file_info_consider_as_directory (info);
1647 gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1648 ICON_COLUMN, pixbuf,
1649 DISPLAY_NAME_COLUMN, data->label,
1650 IS_FOLDER_COLUMN, is_folder,
1654 g_object_unref (pixbuf);
1657 g_object_unref (data->button);
1658 g_free (data->label);
1659 gtk_tree_row_reference_free (data->row_ref);
1662 if (model_cancellable)
1663 g_object_unref (model_cancellable);
1667 set_info_for_file_at_iter (GtkFileChooserButton *button,
1671 struct SetDisplayNameData *data;
1672 GtkTreePath *tree_path;
1673 GCancellable *cancellable;
1675 data = g_new0 (struct SetDisplayNameData, 1);
1676 data->button = g_object_ref (button);
1677 data->label = _gtk_file_system_get_bookmark_label (button->priv->fs, file);
1679 tree_path = gtk_tree_model_get_path (button->priv->model, iter);
1680 data->row_ref = gtk_tree_row_reference_new (button->priv->model, tree_path);
1681 gtk_tree_path_free (tree_path);
1683 cancellable = _gtk_file_system_get_info (button->priv->fs, file,
1684 "standard::type,standard::icon,standard::display-name",
1685 set_info_get_info_cb, data);
1687 gtk_list_store_set (GTK_LIST_STORE (button->priv->model), iter,
1688 CANCELLABLE_COLUMN, cancellable,
1692 /* Shortcuts Model */
1694 model_get_type_position (GtkFileChooserButton *button,
1699 if (row_type == ROW_TYPE_SPECIAL)
1702 retval += button->priv->n_special;
1704 if (row_type == ROW_TYPE_VOLUME)
1707 retval += button->priv->n_volumes;
1709 if (row_type == ROW_TYPE_SHORTCUT)
1712 retval += button->priv->n_shortcuts;
1714 if (row_type == ROW_TYPE_BOOKMARK_SEPARATOR)
1717 retval += button->priv->has_bookmark_separator;
1719 if (row_type == ROW_TYPE_BOOKMARK)
1722 retval += button->priv->n_bookmarks;
1724 if (row_type == ROW_TYPE_CURRENT_FOLDER_SEPARATOR)
1727 retval += button->priv->has_current_folder_separator;
1729 if (row_type == ROW_TYPE_CURRENT_FOLDER)
1732 retval += button->priv->has_current_folder;
1734 if (row_type == ROW_TYPE_OTHER_SEPARATOR)
1737 retval += button->priv->has_other_separator;
1739 if (row_type == ROW_TYPE_OTHER)
1742 g_assert_not_reached ();
1747 model_free_row_data (GtkFileChooserButton *button,
1752 GCancellable *cancellable;
1754 gtk_tree_model_get (button->priv->model, iter,
1757 CANCELLABLE_COLUMN, &cancellable,
1762 g_cancellable_cancel (cancellable);
1763 g_object_unref (cancellable);
1768 case ROW_TYPE_SPECIAL:
1769 case ROW_TYPE_SHORTCUT:
1770 case ROW_TYPE_BOOKMARK:
1771 case ROW_TYPE_CURRENT_FOLDER:
1772 g_object_unref (data);
1774 case ROW_TYPE_VOLUME:
1775 _gtk_file_system_volume_unref (data);
1783 model_add_special_get_info_cb (GCancellable *cancellable,
1785 const GError *error,
1788 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1792 GCancellable *model_cancellable = NULL;
1793 struct ChangeIconThemeData *data = user_data;
1796 if (!data->button->priv->model)
1797 /* button got destroyed */
1800 path = gtk_tree_row_reference_get_path (data->row_ref);
1802 /* Cancellable doesn't exist anymore in the model */
1805 gtk_tree_model_get_iter (data->button->priv->model, &iter, path);
1806 gtk_tree_path_free (path);
1808 gtk_tree_model_get (data->button->priv->model, &iter,
1809 CANCELLABLE_COLUMN, &model_cancellable,
1811 if (cancellable != model_cancellable)
1814 gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1815 CANCELLABLE_COLUMN, NULL,
1818 if (cancelled || error)
1821 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->button), data->button->priv->icon_size);
1825 gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1826 ICON_COLUMN, pixbuf,
1828 g_object_unref (pixbuf);
1831 gtk_tree_model_get (data->button->priv->model, &iter,
1832 DISPLAY_NAME_COLUMN, &name,
1835 gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1836 DISPLAY_NAME_COLUMN, g_file_info_get_display_name (info),
1841 g_object_unref (data->button);
1842 gtk_tree_row_reference_free (data->row_ref);
1845 if (model_cancellable)
1846 g_object_unref (model_cancellable);
1850 model_add_special (GtkFileChooserButton *button)
1852 const gchar *homedir;
1853 const gchar *desktopdir;
1854 GtkListStore *store;
1859 store = GTK_LIST_STORE (button->priv->model);
1860 pos = model_get_type_position (button, ROW_TYPE_SPECIAL);
1862 homedir = g_get_home_dir ();
1866 GtkTreePath *tree_path;
1867 GCancellable *cancellable;
1868 struct ChangeIconThemeData *info;
1870 file = g_file_new_for_path (homedir);
1871 gtk_list_store_insert (store, &iter, pos);
1874 info = g_new0 (struct ChangeIconThemeData, 1);
1875 info->button = g_object_ref (button);
1876 tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
1877 info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (store),
1879 gtk_tree_path_free (tree_path);
1881 cancellable = _gtk_file_system_get_info (button->priv->fs, file,
1882 "standard::icon,standard::display-name",
1883 model_add_special_get_info_cb, info);
1885 gtk_list_store_set (store, &iter,
1887 DISPLAY_NAME_COLUMN, NULL,
1888 TYPE_COLUMN, ROW_TYPE_SPECIAL,
1890 IS_FOLDER_COLUMN, TRUE,
1891 CANCELLABLE_COLUMN, cancellable,
1894 button->priv->n_special++;
1897 desktopdir = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
1899 /* "To disable a directory, point it to the homedir."
1900 * See http://freedesktop.org/wiki/Software/xdg-user-dirs
1902 if (g_strcmp0 (desktopdir, g_get_home_dir ()) != 0)
1904 GtkTreePath *tree_path;
1905 GCancellable *cancellable;
1906 struct ChangeIconThemeData *info;
1908 file = g_file_new_for_path (desktopdir);
1909 gtk_list_store_insert (store, &iter, pos);
1912 info = g_new0 (struct ChangeIconThemeData, 1);
1913 info->button = g_object_ref (button);
1914 tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
1915 info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (store),
1917 gtk_tree_path_free (tree_path);
1919 cancellable = _gtk_file_system_get_info (button->priv->fs, file,
1920 "standard::icon,standard::display-name",
1921 model_add_special_get_info_cb, info);
1923 gtk_list_store_set (store, &iter,
1924 TYPE_COLUMN, ROW_TYPE_SPECIAL,
1926 DISPLAY_NAME_COLUMN, _(DESKTOP_DISPLAY_NAME),
1928 IS_FOLDER_COLUMN, TRUE,
1929 CANCELLABLE_COLUMN, cancellable,
1932 button->priv->n_special++;
1937 model_add_volumes (GtkFileChooserButton *button,
1940 GtkListStore *store;
1942 gboolean local_only;
1948 store = GTK_LIST_STORE (button->priv->model);
1949 pos = model_get_type_position (button, ROW_TYPE_VOLUME);
1950 local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (button->priv->dialog));
1952 for (l = volumes; l; l = l->next)
1954 GtkFileSystemVolume *volume;
1957 gchar *display_name;
1963 if (_gtk_file_system_volume_is_mounted (volume))
1967 base_file = _gtk_file_system_volume_get_root (volume);
1968 if (base_file != NULL)
1970 if (!_gtk_file_has_native_path (base_file))
1972 g_object_unref (base_file);
1976 g_object_unref (base_file);
1981 pixbuf = _gtk_file_system_volume_render_icon (volume,
1982 GTK_WIDGET (button),
1983 button->priv->icon_size,
1985 display_name = _gtk_file_system_volume_get_display_name (volume);
1987 gtk_list_store_insert (store, &iter, pos);
1988 gtk_list_store_set (store, &iter,
1989 ICON_COLUMN, pixbuf,
1990 DISPLAY_NAME_COLUMN, display_name,
1991 TYPE_COLUMN, ROW_TYPE_VOLUME,
1992 DATA_COLUMN, _gtk_file_system_volume_ref (volume),
1993 IS_FOLDER_COLUMN, TRUE,
1997 g_object_unref (pixbuf);
1998 g_free (display_name);
2000 button->priv->n_volumes++;
2005 extern gchar * _gtk_file_chooser_label_for_file (GFile *file);
2008 model_add_bookmarks (GtkFileChooserButton *button,
2011 GtkListStore *store;
2014 gboolean local_only;
2020 store = GTK_LIST_STORE (button->priv->model);
2021 pos = model_get_type_position (button, ROW_TYPE_BOOKMARK);
2022 local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (button->priv->dialog));
2024 for (l = bookmarks; l; l = l->next)
2030 if (_gtk_file_has_native_path (file))
2032 gtk_list_store_insert (store, &iter, pos);
2033 gtk_list_store_set (store, &iter,
2035 DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
2036 TYPE_COLUMN, ROW_TYPE_BOOKMARK,
2037 DATA_COLUMN, g_object_ref (file),
2038 IS_FOLDER_COLUMN, FALSE,
2040 set_info_for_file_at_iter (button, file, &iter);
2045 GtkIconTheme *icon_theme;
2051 /* Don't call get_info for remote paths to avoid latency and
2053 * If we switch to a better bookmarks file format (XBEL), we
2054 * should use mime info to get a better icon.
2056 label = _gtk_file_system_get_bookmark_label (button->priv->fs, file);
2058 label = _gtk_file_chooser_label_for_file (file);
2060 icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
2061 pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote",
2062 button->priv->icon_size, 0, NULL);
2064 gtk_list_store_insert (store, &iter, pos);
2065 gtk_list_store_set (store, &iter,
2066 ICON_COLUMN, pixbuf,
2067 DISPLAY_NAME_COLUMN, label,
2068 TYPE_COLUMN, ROW_TYPE_BOOKMARK,
2069 DATA_COLUMN, g_object_ref (file),
2070 IS_FOLDER_COLUMN, TRUE,
2074 g_object_unref (pixbuf);
2077 button->priv->n_bookmarks++;
2081 if (button->priv->n_bookmarks > 0 &&
2082 !button->priv->has_bookmark_separator)
2084 pos = model_get_type_position (button, ROW_TYPE_BOOKMARK_SEPARATOR);
2086 gtk_list_store_insert (store, &iter, pos);
2087 gtk_list_store_set (store, &iter,
2089 DISPLAY_NAME_COLUMN, NULL,
2090 TYPE_COLUMN, ROW_TYPE_BOOKMARK_SEPARATOR,
2092 IS_FOLDER_COLUMN, FALSE,
2094 button->priv->has_bookmark_separator = TRUE;
2099 model_update_current_folder (GtkFileChooserButton *button,
2102 GtkListStore *store;
2109 store = GTK_LIST_STORE (button->priv->model);
2111 if (!button->priv->has_current_folder_separator)
2113 pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER_SEPARATOR);
2114 gtk_list_store_insert (store, &iter, pos);
2115 gtk_list_store_set (store, &iter,
2117 DISPLAY_NAME_COLUMN, NULL,
2118 TYPE_COLUMN, ROW_TYPE_CURRENT_FOLDER_SEPARATOR,
2120 IS_FOLDER_COLUMN, FALSE,
2122 button->priv->has_current_folder_separator = TRUE;
2125 pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER);
2126 if (!button->priv->has_current_folder)
2128 gtk_list_store_insert (store, &iter, pos);
2129 button->priv->has_current_folder = TRUE;
2133 gtk_tree_model_iter_nth_child (button->priv->model, &iter, NULL, pos);
2134 model_free_row_data (button, &iter);
2137 if (g_file_is_native (file))
2139 gtk_list_store_set (store, &iter,
2141 DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
2142 TYPE_COLUMN, ROW_TYPE_CURRENT_FOLDER,
2143 DATA_COLUMN, g_object_ref (file),
2144 IS_FOLDER_COLUMN, FALSE,
2146 set_info_for_file_at_iter (button, file, &iter);
2151 GtkIconTheme *icon_theme;
2154 /* Don't call get_info for remote paths to avoid latency and
2156 * If we switch to a better bookmarks file format (XBEL), we
2157 * should use mime info to get a better icon.
2159 label = _gtk_file_system_get_bookmark_label (button->priv->fs, file);
2161 label = _gtk_file_chooser_label_for_file (file);
2163 icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
2165 if (g_file_is_native (file))
2166 pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder",
2167 button->priv->icon_size, 0, NULL);
2169 pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote",
2170 button->priv->icon_size, 0, NULL);
2172 gtk_list_store_set (store, &iter,
2173 ICON_COLUMN, pixbuf,
2174 DISPLAY_NAME_COLUMN, label,
2175 TYPE_COLUMN, ROW_TYPE_CURRENT_FOLDER,
2176 DATA_COLUMN, g_object_ref (file),
2177 IS_FOLDER_COLUMN, TRUE,
2181 g_object_unref (pixbuf);
2186 model_add_other (GtkFileChooserButton *button)
2188 GtkListStore *store;
2192 store = GTK_LIST_STORE (button->priv->model);
2193 pos = model_get_type_position (button, ROW_TYPE_OTHER_SEPARATOR);
2195 gtk_list_store_insert (store, &iter, pos);
2196 gtk_list_store_set (store, &iter,
2198 DISPLAY_NAME_COLUMN, NULL,
2199 TYPE_COLUMN, ROW_TYPE_OTHER_SEPARATOR,
2201 IS_FOLDER_COLUMN, FALSE,
2203 button->priv->has_other_separator = TRUE;
2206 gtk_list_store_insert (store, &iter, pos);
2207 gtk_list_store_set (store, &iter,
2209 DISPLAY_NAME_COLUMN, _("Other…"),
2210 TYPE_COLUMN, ROW_TYPE_OTHER,
2212 IS_FOLDER_COLUMN, FALSE,
2217 model_remove_rows (GtkFileChooserButton *button,
2221 GtkListStore *store;
2226 store = GTK_LIST_STORE (button->priv->model);
2232 if (!gtk_tree_model_iter_nth_child (button->priv->model, &iter, NULL, pos))
2233 g_assert_not_reached ();
2235 model_free_row_data (button, &iter);
2236 gtk_list_store_remove (store, &iter);
2243 static inline gboolean
2244 test_if_file_is_visible (GtkFileSystem *fs,
2246 gboolean local_only,
2252 if (local_only && !_gtk_file_has_native_path (file))
2262 filter_model_visible_func (GtkTreeModel *model,
2266 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2267 GtkFileChooserButtonPrivate *priv = button->priv;
2270 gboolean local_only, retval, is_folder;
2272 type = ROW_TYPE_INVALID;
2274 local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->dialog));
2276 gtk_tree_model_get (model, iter,
2279 IS_FOLDER_COLUMN, &is_folder,
2284 case ROW_TYPE_CURRENT_FOLDER:
2287 case ROW_TYPE_SPECIAL:
2288 case ROW_TYPE_SHORTCUT:
2289 case ROW_TYPE_BOOKMARK:
2290 retval = test_if_file_is_visible (priv->fs, data, local_only, is_folder);
2292 case ROW_TYPE_VOLUME:
2297 if (_gtk_file_system_volume_is_mounted (data))
2301 base_file = _gtk_file_system_volume_get_root (data);
2305 if (!_gtk_file_has_native_path (base_file))
2307 g_object_unref (base_file);
2325 name_cell_data_func (GtkCellLayout *layout,
2326 GtkCellRenderer *cell,
2327 GtkTreeModel *model,
2334 gtk_tree_model_get (model, iter,
2338 if (type == ROW_TYPE_CURRENT_FOLDER)
2339 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2341 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_NONE, NULL);
2345 combo_box_row_separator_func (GtkTreeModel *model,
2349 gchar type = ROW_TYPE_INVALID;
2351 gtk_tree_model_get (model, iter, TYPE_COLUMN, &type, -1);
2353 return (type == ROW_TYPE_BOOKMARK_SEPARATOR ||
2354 type == ROW_TYPE_CURRENT_FOLDER_SEPARATOR ||
2355 type == ROW_TYPE_OTHER_SEPARATOR);
2359 update_combo_box (GtkFileChooserButton *button)
2361 GtkFileChooserButtonPrivate *priv = button->priv;
2366 file = get_selected_file (button);
2370 gtk_tree_model_get_iter_first (priv->filter_model, &iter);
2377 type = ROW_TYPE_INVALID;
2380 gtk_tree_model_get (priv->filter_model, &iter,
2387 case ROW_TYPE_SPECIAL:
2388 case ROW_TYPE_SHORTCUT:
2389 case ROW_TYPE_BOOKMARK:
2390 case ROW_TYPE_CURRENT_FOLDER:
2391 row_found = (file && g_file_equal (data, file));
2393 case ROW_TYPE_VOLUME:
2397 base_file = _gtk_file_system_volume_get_root (data);
2400 row_found = (file && g_file_equal (base_file, file));
2401 g_object_unref (base_file);
2412 g_signal_handler_block (priv->combo_box, priv->combo_box_changed_id);
2413 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->combo_box),
2415 g_signal_handler_unblock (priv->combo_box,
2416 priv->combo_box_changed_id);
2419 while (!row_found && gtk_tree_model_iter_next (priv->filter_model, &iter));
2421 /* If it hasn't been found already, update & select the current-folder row. */
2422 if (!row_found && file)
2424 GtkTreeIter filter_iter;
2427 model_update_current_folder (button, file);
2428 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2430 pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER);
2431 gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
2433 gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (priv->filter_model),
2434 &filter_iter, &iter);
2436 g_signal_handler_block (priv->combo_box, priv->combo_box_changed_id);
2437 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->combo_box), &filter_iter);
2438 g_signal_handler_unblock (priv->combo_box, priv->combo_box_changed_id);
2442 g_object_unref (file);
2447 update_label_get_info_cb (GCancellable *cancellable,
2449 const GError *error,
2452 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
2454 GtkFileChooserButton *button = data;
2455 GtkFileChooserButtonPrivate *priv = button->priv;
2457 if (cancellable != priv->update_button_cancellable)
2460 priv->update_button_cancellable = NULL;
2462 if (cancelled || error)
2465 gtk_label_set_text (GTK_LABEL (priv->label), g_file_info_get_display_name (info));
2467 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (priv->image), priv->icon_size);
2470 pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (priv->image)),
2472 priv->icon_size, 0, NULL);
2474 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
2476 g_object_unref (pixbuf);
2479 g_object_unref (button);
2480 g_object_unref (cancellable);
2484 update_label_and_image (GtkFileChooserButton *button)
2486 GtkFileChooserButtonPrivate *priv = button->priv;
2490 file = get_selected_file (button);
2494 if (priv->update_button_cancellable)
2496 g_cancellable_cancel (priv->update_button_cancellable);
2497 priv->update_button_cancellable = NULL;
2502 GtkFileSystemVolume *volume = NULL;
2504 volume = _gtk_file_system_get_volume_for_file (priv->fs, file);
2509 base_file = _gtk_file_system_volume_get_root (volume);
2510 if (base_file && g_file_equal (base_file, file))
2514 label_text = _gtk_file_system_volume_get_display_name (volume);
2515 pixbuf = _gtk_file_system_volume_render_icon (volume,
2516 GTK_WIDGET (button),
2519 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
2521 g_object_unref (pixbuf);
2525 g_object_unref (base_file);
2527 _gtk_file_system_volume_unref (volume);
2533 if (g_file_is_native (file))
2535 priv->update_button_cancellable =
2536 _gtk_file_system_get_info (priv->fs, file,
2537 "standard::icon,standard::display-name",
2538 update_label_get_info_cb,
2539 g_object_ref (button));
2545 label_text = _gtk_file_system_get_bookmark_label (button->priv->fs, file);
2546 pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (priv->image)),
2548 priv->icon_size, 0, NULL);
2549 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
2551 g_object_unref (pixbuf);
2557 g_object_unref (file);
2561 gtk_label_set_text (GTK_LABEL (priv->label), label_text);
2562 g_free (label_text);
2566 gtk_label_set_text (GTK_LABEL (priv->label), _(FALLBACK_DISPLAY_NAME));
2567 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), NULL);
2572 /* ************************ *
2573 * Child Object Callbacks *
2574 * ************************ */
2578 fs_volumes_changed_cb (GtkFileSystem *fs,
2581 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2582 GtkFileChooserButtonPrivate *priv = button->priv;
2585 model_remove_rows (user_data,
2586 model_get_type_position (user_data, ROW_TYPE_VOLUME),
2589 priv->n_volumes = 0;
2591 volumes = _gtk_file_system_list_volumes (fs);
2592 model_add_volumes (user_data, volumes);
2593 g_slist_free (volumes);
2595 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2597 update_label_and_image (user_data);
2598 update_combo_box (user_data);
2602 fs_bookmarks_changed_cb (GtkFileSystem *fs,
2605 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2606 GtkFileChooserButtonPrivate *priv = button->priv;
2609 bookmarks = _gtk_file_system_list_bookmarks (fs);
2610 model_remove_rows (user_data,
2611 model_get_type_position (user_data,
2612 ROW_TYPE_BOOKMARK_SEPARATOR),
2613 (priv->n_bookmarks + priv->has_bookmark_separator));
2614 priv->has_bookmark_separator = FALSE;
2615 priv->n_bookmarks = 0;
2616 model_add_bookmarks (user_data, bookmarks);
2617 g_slist_foreach (bookmarks, (GFunc) g_object_unref, NULL);
2618 g_slist_free (bookmarks);
2620 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2622 update_label_and_image (user_data);
2623 update_combo_box (user_data);
2627 save_inactive_state (GtkFileChooserButton *button)
2629 GtkFileChooserButtonPrivate *priv = button->priv;
2631 if (priv->current_folder_while_inactive)
2632 g_object_unref (priv->current_folder_while_inactive);
2634 if (priv->selection_while_inactive)
2635 g_object_unref (priv->selection_while_inactive);
2637 priv->current_folder_while_inactive = gtk_file_chooser_get_current_folder_file (GTK_FILE_CHOOSER (priv->dialog));
2638 priv->selection_while_inactive = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (priv->dialog));
2642 restore_inactive_state (GtkFileChooserButton *button)
2644 GtkFileChooserButtonPrivate *priv = button->priv;
2646 if (priv->current_folder_while_inactive)
2647 gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (priv->dialog), priv->current_folder_while_inactive, NULL);
2649 if (priv->selection_while_inactive)
2650 gtk_file_chooser_select_file (GTK_FILE_CHOOSER (priv->dialog), priv->selection_while_inactive, NULL);
2652 gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
2657 open_dialog (GtkFileChooserButton *button)
2659 GtkFileChooserButtonPrivate *priv = button->priv;
2661 /* Setup the dialog parent to be chooser button's toplevel, and be modal
2663 if (!gtk_widget_get_visible (priv->dialog))
2665 GtkWidget *toplevel;
2667 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
2669 if (gtk_widget_is_toplevel (toplevel) && GTK_IS_WINDOW (toplevel))
2671 if (GTK_WINDOW (toplevel) != gtk_window_get_transient_for (GTK_WINDOW (priv->dialog)))
2672 gtk_window_set_transient_for (GTK_WINDOW (priv->dialog),
2673 GTK_WINDOW (toplevel));
2675 gtk_window_set_modal (GTK_WINDOW (priv->dialog),
2676 gtk_window_get_modal (GTK_WINDOW (toplevel)));
2682 g_signal_handler_block (priv->dialog,
2683 priv->dialog_folder_changed_id);
2684 g_signal_handler_block (priv->dialog,
2685 priv->dialog_file_activated_id);
2686 g_signal_handler_block (priv->dialog,
2687 priv->dialog_selection_changed_id);
2689 restore_inactive_state (button);
2691 priv->active = TRUE;
2694 gtk_widget_set_sensitive (priv->combo_box, FALSE);
2695 gtk_window_present (GTK_WINDOW (priv->dialog));
2700 combo_box_changed_cb (GtkComboBox *combo_box,
2705 if (gtk_combo_box_get_active_iter (combo_box, &iter))
2707 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2708 GtkFileChooserButtonPrivate *priv = button->priv;
2712 type = ROW_TYPE_INVALID;
2715 gtk_tree_model_get (priv->filter_model, &iter,
2722 case ROW_TYPE_SPECIAL:
2723 case ROW_TYPE_SHORTCUT:
2724 case ROW_TYPE_BOOKMARK:
2725 case ROW_TYPE_CURRENT_FOLDER:
2726 gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
2728 gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (priv->dialog),
2731 case ROW_TYPE_VOLUME:
2735 gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
2736 base_file = _gtk_file_system_volume_get_root (data);
2739 gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (priv->dialog),
2741 g_object_unref (base_file);
2745 case ROW_TYPE_OTHER:
2746 open_dialog (user_data);
2756 button_clicked_cb (GtkButton *real_button,
2759 open_dialog (user_data);
2764 dialog_current_folder_changed_cb (GtkFileChooser *dialog,
2767 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2769 g_signal_emit_by_name (button, "current-folder-changed");
2773 dialog_file_activated_cb (GtkFileChooser *dialog,
2776 g_signal_emit_by_name (user_data, "file-activated");
2780 dialog_selection_changed_cb (GtkFileChooser *dialog,
2783 update_label_and_image (user_data);
2784 update_combo_box (user_data);
2785 g_signal_emit_by_name (user_data, "selection-changed");
2789 dialog_update_preview_cb (GtkFileChooser *dialog,
2792 g_signal_emit_by_name (user_data, "update-preview");
2796 dialog_notify_cb (GObject *dialog,
2802 iface = g_type_interface_peek (g_type_class_peek (G_OBJECT_TYPE (dialog)),
2803 GTK_TYPE_FILE_CHOOSER);
2804 if (g_object_interface_find_property (iface, pspec->name))
2805 g_object_notify (user_data, pspec->name);
2807 if (g_ascii_strcasecmp (pspec->name, "local-only") == 0)
2809 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2810 GtkFileChooserButtonPrivate *priv = button->priv;
2812 if (priv->has_current_folder)
2818 pos = model_get_type_position (user_data,
2819 ROW_TYPE_CURRENT_FOLDER);
2820 gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
2823 gtk_tree_model_get (priv->model, &iter, DATA_COLUMN, &data, -1);
2825 /* If the path isn't local but we're in local-only mode now, remove
2826 * the custom-folder row */
2827 if (data && _gtk_file_has_native_path (G_FILE (data)) &&
2828 gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->dialog)))
2831 model_remove_rows (user_data, pos, 2);
2835 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2836 update_combo_box (user_data);
2841 dialog_delete_event_cb (GtkWidget *dialog,
2845 g_signal_emit_by_name (dialog, "response", GTK_RESPONSE_DELETE_EVENT);
2851 dialog_response_cb (GtkDialog *dialog,
2855 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2856 GtkFileChooserButtonPrivate *priv = button->priv;
2858 if (response == GTK_RESPONSE_ACCEPT ||
2859 response == GTK_RESPONSE_OK)
2861 save_inactive_state (button);
2863 g_signal_emit_by_name (button, "current-folder-changed");
2864 g_signal_emit_by_name (button, "selection-changed");
2866 update_label_and_image (button);
2867 update_combo_box (button);
2871 restore_inactive_state (button);
2876 g_signal_handler_unblock (priv->dialog,
2877 priv->dialog_folder_changed_id);
2878 g_signal_handler_unblock (priv->dialog,
2879 priv->dialog_file_activated_id);
2880 g_signal_handler_unblock (priv->dialog,
2881 priv->dialog_selection_changed_id);
2882 priv->active = FALSE;
2885 gtk_widget_set_sensitive (priv->combo_box, TRUE);
2886 gtk_widget_hide (priv->dialog);
2888 if (response == GTK_RESPONSE_ACCEPT ||
2889 response == GTK_RESPONSE_OK)
2890 g_signal_emit (button, file_chooser_button_signals[FILE_SET], 0);
2894 /* ************************************************************************** *
2896 * ************************************************************************** */
2899 * gtk_file_chooser_button_new:
2900 * @title: the title of the browse dialog.
2901 * @action: the open mode for the widget.
2903 * Creates a new file-selecting button widget.
2905 * Returns: a new button widget.
2910 gtk_file_chooser_button_new (const gchar *title,
2911 GtkFileChooserAction action)
2913 g_return_val_if_fail (action == GTK_FILE_CHOOSER_ACTION_OPEN ||
2914 action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, NULL);
2916 return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
2918 "title", (title ? title : _(DEFAULT_TITLE)),
2923 * gtk_file_chooser_button_new_with_dialog:
2924 * @dialog: the widget to use as dialog
2926 * Creates a #GtkFileChooserButton widget which uses @dialog as its
2927 * file-picking window.
2929 * Note that @dialog must be a #GtkDialog (or subclass) which
2930 * implements the #GtkFileChooser interface and must not have
2931 * %GTK_DIALOG_DESTROY_WITH_PARENT set.
2933 * Also note that the dialog needs to have its confirmative button
2934 * added with response %GTK_RESPONSE_ACCEPT or %GTK_RESPONSE_OK in
2935 * order for the button to take over the file selected in the dialog.
2937 * Returns: a new button widget.
2942 gtk_file_chooser_button_new_with_dialog (GtkWidget *dialog)
2944 g_return_val_if_fail (GTK_IS_FILE_CHOOSER (dialog) && GTK_IS_DIALOG (dialog), NULL);
2946 return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
2952 * gtk_file_chooser_button_set_title:
2953 * @button: the button widget to modify.
2954 * @title: the new browse dialog title.
2956 * Modifies the @title of the browse dialog used by @button.
2961 gtk_file_chooser_button_set_title (GtkFileChooserButton *button,
2964 g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
2966 gtk_window_set_title (GTK_WINDOW (button->priv->dialog), title);
2967 g_object_notify (G_OBJECT (button), "title");
2971 * gtk_file_chooser_button_get_title:
2972 * @button: the button widget to examine.
2974 * Retrieves the title of the browse dialog used by @button. The returned value
2975 * should not be modified or freed.
2977 * Returns: a pointer to the browse dialog's title.
2982 gtk_file_chooser_button_get_title (GtkFileChooserButton *button)
2984 g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), NULL);
2986 return gtk_window_get_title (GTK_WINDOW (button->priv->dialog));
2990 * gtk_file_chooser_button_get_width_chars:
2991 * @button: the button widget to examine.
2993 * Retrieves the width in characters of the @button widget's entry and/or label.
2995 * Returns: an integer width (in characters) that the button will use to size itself.
3000 gtk_file_chooser_button_get_width_chars (GtkFileChooserButton *button)
3002 g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), -1);
3004 return gtk_label_get_width_chars (GTK_LABEL (button->priv->label));
3008 * gtk_file_chooser_button_set_width_chars:
3009 * @button: the button widget to examine.
3010 * @n_chars: the new width, in characters.
3012 * Sets the width (in characters) that @button will use to @n_chars.
3017 gtk_file_chooser_button_set_width_chars (GtkFileChooserButton *button,
3020 g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
3022 gtk_label_set_width_chars (GTK_LABEL (button->priv->label), n_chars);
3023 g_object_notify (G_OBJECT (button), "width-chars");
3027 * gtk_file_chooser_button_set_focus_on_click:
3028 * @button: a #GtkFileChooserButton
3029 * @focus_on_click: whether the button grabs focus when clicked with the mouse
3031 * Sets whether the button will grab focus when it is clicked with the mouse.
3032 * Making mouse clicks not grab focus is useful in places like toolbars where
3033 * you don't want the keyboard focus removed from the main area of the
3039 gtk_file_chooser_button_set_focus_on_click (GtkFileChooserButton *button,
3040 gboolean focus_on_click)
3042 GtkFileChooserButtonPrivate *priv;
3044 g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
3046 priv = button->priv;
3048 focus_on_click = focus_on_click != FALSE;
3050 if (priv->focus_on_click != focus_on_click)
3052 priv->focus_on_click = focus_on_click;
3053 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button), focus_on_click);
3054 gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (priv->combo_box), focus_on_click);
3056 g_object_notify (G_OBJECT (button), "focus-on-click");
3061 * gtk_file_chooser_button_get_focus_on_click:
3062 * @button: a #GtkFileChooserButton
3064 * Returns whether the button grabs focus when it is clicked with the mouse.
3065 * See gtk_file_chooser_button_set_focus_on_click().
3067 * Return value: %TRUE if the button grabs focus when it is clicked with
3073 gtk_file_chooser_button_get_focus_on_click (GtkFileChooserButton *button)
3075 g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), FALSE);
3077 return button->priv->focus_on_click;