1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 2 -*- */
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, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
25 #include <sys/types.h>
38 #include "gtkicontheme.h"
39 #include "gtkiconfactory.h"
43 #include "gtktogglebutton.h"
44 #include "gtkvseparator.h"
45 #include "gtkfilechooserdialog.h"
46 #include "gtkfilechooserentry.h"
47 #include "gtkfilechooserprivate.h"
48 #include "gtkfilechooserutils.h"
50 #include "gtkfilechooserbutton.h"
57 #define GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE(object) (GTK_FILE_CHOOSER_BUTTON ((object))->priv)
59 #define DEFAULT_FILENAME N_("(None)")
60 #define MIN_LABEL_WIDTH 100
61 #define ENTRY_BUTTON_SPACING 0
62 #define FALLBACK_ICON_SIZE 20
63 #define FALLBACK_ICON_NAME "stock_unknown"
64 #define NEW_FILE_ICON_NAME "stock_new"
65 #define NEW_DIR_ICON_NAME "stock_new-dir"
67 /* ********************** *
68 * Private Enumerations *
69 * ********************** */
83 /* ******************** *
84 * Private Structures *
85 * ******************** */
87 struct _GtkFileChooserButtonPrivate
90 GtkWidget *accept_button;
92 GtkWidget *entry_image;
95 GtkWidget *label_image;
100 gulong entry_changed_id;
101 gulong dialog_file_activated_id;
102 gulong dialog_folder_changed_id;
103 gulong dialog_selection_changed_id;
104 gulong dialog_selection_changed_proxy_id;
105 gulong settings_signal_id;
121 /* ********************* *
122 * Function Prototypes *
123 * ********************* */
125 /* GObject Functions */
126 static GObject *gtk_file_chooser_button_constructor (GType type,
128 GObjectConstructParam *params);
129 static void gtk_file_chooser_button_set_property (GObject *object,
133 static void gtk_file_chooser_button_get_property (GObject *object,
138 /* GtkObject Functions */
139 static void gtk_file_chooser_button_destroy (GtkObject *object);
141 /* GtkWidget Functions */
142 static void gtk_file_chooser_button_drag_data_received (GtkWidget *widget,
143 GdkDragContext *context,
146 GtkSelectionData *data,
149 static void gtk_file_chooser_button_show_all (GtkWidget *widget);
150 static void gtk_file_chooser_button_hide_all (GtkWidget *widget);
151 static void gtk_file_chooser_button_show (GtkWidget *widget);
152 static void gtk_file_chooser_button_hide (GtkWidget *widget);
153 static gboolean gtk_file_chooser_button_mnemonic_activate (GtkWidget *widget,
154 gboolean group_cycling);
155 static void gtk_file_chooser_button_style_set (GtkWidget *widget,
156 GtkStyle *old_style);
157 static void gtk_file_chooser_button_screen_changed (GtkWidget *widget,
158 GdkScreen *old_screen);
160 /* Child Widget Callbacks */
161 static void dialog_update_preview_cb (GtkFileChooser *dialog,
163 static void dialog_selection_changed_cb (GtkFileChooser *dialog,
165 static void dialog_selection_changed_proxy_cb (GtkFileChooser *dialog,
167 static void dialog_file_activated_cb (GtkFileChooser *dialog,
169 static void dialog_current_folder_changed_cb (GtkFileChooser *dialog,
171 static void dialog_notify_cb (GObject *dialog,
174 static gboolean dialog_delete_event_cb (GtkWidget *dialog,
177 static void dialog_response_cb (GtkFileChooser *dialog,
181 static void button_toggled_cb (GtkToggleButton *real_button,
183 static void button_notify_active_cb (GObject *real_button,
187 static void entry_size_allocate_cb (GtkWidget *entry,
188 GtkAllocation *allocation,
190 static void entry_changed_cb (GtkEditable *editable,
193 /* Utility Functions */
194 static void remove_settings_signal (GtkFileChooserButton *button,
197 static void update_dialog (GtkFileChooserButton *button);
198 static void update_entry (GtkFileChooserButton *button);
199 static void update_label (GtkFileChooserButton *button);
200 static void update_icons (GtkFileChooserButton *button);
203 /* ******************* *
204 * GType Declaration *
205 * ******************* */
207 G_DEFINE_TYPE_WITH_CODE (GtkFileChooserButton, gtk_file_chooser_button, GTK_TYPE_HBOX, { \
208 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER, _gtk_file_chooser_delegate_iface_init) \
212 /* ***************** *
214 * ***************** */
217 gtk_file_chooser_button_class_init (GtkFileChooserButtonClass * class)
219 GObjectClass *gobject_class;
220 GtkObjectClass *gtkobject_class;
221 GtkWidgetClass *widget_class;
223 gobject_class = G_OBJECT_CLASS (class);
224 gtkobject_class = GTK_OBJECT_CLASS (class);
225 widget_class = GTK_WIDGET_CLASS (class);
227 gobject_class->constructor = gtk_file_chooser_button_constructor;
228 gobject_class->set_property = gtk_file_chooser_button_set_property;
229 gobject_class->get_property = gtk_file_chooser_button_get_property;
231 gtkobject_class->destroy = gtk_file_chooser_button_destroy;
233 widget_class->drag_data_received = gtk_file_chooser_button_drag_data_received;
234 widget_class->show_all = gtk_file_chooser_button_show_all;
235 widget_class->hide_all = gtk_file_chooser_button_hide_all;
236 widget_class->show = gtk_file_chooser_button_show;
237 widget_class->hide = gtk_file_chooser_button_hide;
238 widget_class->style_set = gtk_file_chooser_button_style_set;
239 widget_class->screen_changed = gtk_file_chooser_button_screen_changed;
240 widget_class->mnemonic_activate = gtk_file_chooser_button_mnemonic_activate;
243 * GtkFileChooserButton:dialog:
245 * Instance of the #GtkFileChooserDialog associated with the button.
249 g_object_class_install_property (gobject_class, PROP_DIALOG,
250 g_param_spec_object ("dialog",
252 P_("The file chooser dialog to use."),
253 GTK_TYPE_FILE_CHOOSER_DIALOG,
255 G_PARAM_CONSTRUCT_ONLY)));
258 * GtkFileChooserButton:title:
260 * Title to put on the #GtkFileChooserDialog associated with the button.
264 g_object_class_install_property (gobject_class, PROP_TITLE,
265 g_param_spec_string ("title",
267 P_("The title of the file chooser dialog."),
272 * GtkFileChooserButton:active:
274 * %TRUE, if the #GtkFileChooserDialog associated with the button has been
275 * made visible. This can also be set by the application, though it is
276 * rarely useful to do so.
280 g_object_class_install_property (gobject_class, PROP_ACTIVE,
281 g_param_spec_boolean ("active",
283 P_("Whether the browse dialog is visible or not."),
284 FALSE, G_PARAM_READWRITE));
287 * GtkFileChooserButton:width-chars:
289 * The width of the entry and label inside the button, in characters.
293 g_object_class_install_property (gobject_class, PROP_WIDTH_CHARS,
294 g_param_spec_int ("width-chars",
295 P_("Width In Characters"),
296 P_("The desired width of the button widget, in characters."),
300 _gtk_file_chooser_install_properties (gobject_class);
302 g_type_class_add_private (class, sizeof (GtkFileChooserButtonPrivate));
307 gtk_file_chooser_button_init (GtkFileChooserButton *button)
309 GtkFileChooserButtonPrivate *priv;
310 GtkWidget *box, *image, *sep;
311 GtkTargetList *target_list;
313 gtk_box_set_spacing (GTK_BOX (button), ENTRY_BUTTON_SPACING);
315 priv = G_TYPE_INSTANCE_GET_PRIVATE (button, GTK_TYPE_FILE_CHOOSER_BUTTON,
316 GtkFileChooserButtonPrivate);
319 priv->icon_size = FALLBACK_ICON_SIZE;
321 gtk_widget_push_composite_child ();
323 priv->entry_box = gtk_hbox_new (FALSE, 4);
324 gtk_container_add (GTK_CONTAINER (button), priv->entry_box);
326 priv->entry_image = gtk_image_new ();
327 gtk_box_pack_start (GTK_BOX (priv->entry_box), priv->entry_image,
329 gtk_widget_show (priv->entry_image);
331 priv->entry = _gtk_file_chooser_entry_new (FALSE);
332 gtk_container_add (GTK_CONTAINER (priv->entry_box), priv->entry);
333 gtk_widget_show (priv->entry);
335 priv->button = gtk_toggle_button_new ();
336 g_signal_connect (priv->button, "toggled",
337 G_CALLBACK (button_toggled_cb), button);
338 g_signal_connect (priv->button, "notify::active",
339 G_CALLBACK (button_notify_active_cb), button);
340 g_signal_connect (priv->entry, "size-allocate",
341 G_CALLBACK (entry_size_allocate_cb), priv->button);
342 gtk_box_pack_start (GTK_BOX (button), priv->button, TRUE, TRUE, 0);
343 gtk_widget_show (priv->button);
345 box = gtk_hbox_new (FALSE, 4);
346 gtk_container_add (GTK_CONTAINER (priv->button), box);
347 gtk_widget_show (box);
349 priv->label_box = gtk_hbox_new (FALSE, 6);
350 gtk_container_add (GTK_CONTAINER (box), priv->label_box);
351 gtk_widget_show (priv->label_box);
353 priv->label_image = gtk_image_new ();
354 gtk_box_pack_start (GTK_BOX (priv->label_box), priv->label_image,
356 gtk_widget_show (priv->label_image);
358 priv->label = gtk_label_new (_(DEFAULT_FILENAME));
359 gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_START);
360 gtk_misc_set_alignment (GTK_MISC (priv->label), 0.0, 0.5);
361 gtk_container_add (GTK_CONTAINER (priv->label_box), priv->label);
362 gtk_widget_show (priv->label);
364 sep = gtk_vseparator_new ();
365 gtk_box_pack_end (GTK_BOX (priv->label_box), sep, FALSE, FALSE, 0);
366 gtk_widget_show (sep);
368 image = gtk_image_new_from_stock (GTK_STOCK_OPEN,
369 GTK_ICON_SIZE_SMALL_TOOLBAR);
370 gtk_box_pack_end (GTK_BOX (box), image, FALSE, FALSE, 0);
371 gtk_widget_show (image);
373 gtk_widget_pop_composite_child ();
376 gtk_drag_dest_unset (priv->entry);
377 gtk_drag_dest_set (GTK_WIDGET (button),
378 (GTK_DEST_DEFAULT_ALL),
381 target_list = gtk_target_list_new (NULL, 0);
382 gtk_target_list_add_uri_targets (target_list, TEXT_URI_LIST);
383 gtk_target_list_add_text_targets (target_list, TEXT_PLAIN);
384 gtk_drag_dest_set_target_list (GTK_WIDGET (button), target_list);
385 gtk_target_list_unref (target_list);
389 /* ******************* *
390 * GObject Functions *
391 * ******************* */
394 gtk_file_chooser_button_constructor (GType type,
396 GObjectConstructParam *params)
399 GtkFileChooserButtonPrivate *priv;
402 object = (*G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->constructor) (type,
405 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (object);
410 priv->dialog = gtk_file_chooser_dialog_new_with_backend (NULL, NULL,
411 GTK_FILE_CHOOSER_ACTION_OPEN,
412 priv->backend, NULL);
414 priv->dialog = gtk_file_chooser_dialog_new (NULL, NULL,
415 GTK_FILE_CHOOSER_ACTION_OPEN,
418 gtk_dialog_add_button (GTK_DIALOG (priv->dialog),
419 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
420 priv->accept_button = gtk_dialog_add_button (GTK_DIALOG (priv->dialog),
422 GTK_RESPONSE_ACCEPT);
423 gtk_dialog_set_default_response (GTK_DIALOG (priv->dialog),
424 GTK_RESPONSE_ACCEPT);
426 gtk_dialog_set_alternative_button_order (GTK_DIALOG (priv->dialog),
432 g_free (priv->backend);
433 priv->backend = NULL;
435 g_signal_connect (priv->dialog, "delete-event",
436 G_CALLBACK (dialog_delete_event_cb), object);
437 g_signal_connect (priv->dialog, "response",
438 G_CALLBACK (dialog_response_cb), object);
440 /* This is used, instead of the standard delegate, to ensure that signals are only
441 * delegated when the OK button is pressed. */
442 g_object_set_qdata (object, GTK_FILE_CHOOSER_DELEGATE_QUARK, priv->dialog);
443 priv->dialog_folder_changed_id =
444 g_signal_connect (priv->dialog, "current-folder-changed",
445 G_CALLBACK (dialog_current_folder_changed_cb), object);
446 priv->dialog_file_activated_id =
447 g_signal_connect (priv->dialog, "file-activated",
448 G_CALLBACK (dialog_file_activated_cb), object);
449 priv->dialog_selection_changed_id =
450 g_signal_connect (priv->dialog, "selection-changed",
451 G_CALLBACK (dialog_selection_changed_cb), object);
452 priv->dialog_selection_changed_proxy_id =
453 g_signal_connect (priv->dialog, "selection-changed",
454 G_CALLBACK (dialog_selection_changed_proxy_cb), object);
455 g_signal_connect (priv->dialog, "update-preview",
456 G_CALLBACK (dialog_update_preview_cb), object);
457 g_signal_connect (priv->dialog, "notify",
458 G_CALLBACK (dialog_notify_cb), object);
459 g_object_add_weak_pointer (G_OBJECT (priv->dialog),
460 (gpointer *) (&priv->dialog));
462 _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (priv->entry),
463 _gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog)));
464 path = gtk_file_path_new_steal ("/");
465 _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (priv->entry),
467 priv->entry_changed_id = g_signal_connect (priv->entry, "changed",
468 G_CALLBACK (entry_changed_cb),
471 update_label (GTK_FILE_CHOOSER_BUTTON (object));
477 gtk_file_chooser_button_set_property (GObject *object,
482 GtkFileChooserButtonPrivate *priv;
484 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (object);
490 priv->dialog = g_value_get_object (value);
493 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
494 g_value_get_boolean (value));
496 case PROP_WIDTH_CHARS:
497 gtk_file_chooser_button_set_width_chars (GTK_FILE_CHOOSER_BUTTON (object),
498 g_value_get_int (value));
501 case GTK_FILE_CHOOSER_PROP_ACTION:
502 switch (g_value_get_enum (value))
504 case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
505 case GTK_FILE_CHOOSER_ACTION_SAVE:
510 eclass = g_type_class_peek (GTK_TYPE_FILE_CHOOSER_ACTION);
511 eval = g_enum_get_value (eclass, g_value_get_enum (value));
512 g_warning ("%s: Choosers of type `%s` do not support `%s'.",
513 G_STRFUNC, G_OBJECT_TYPE_NAME (object), eval->value_name);
515 g_value_set_enum (value, GTK_FILE_CHOOSER_ACTION_OPEN);
520 g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
521 _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (priv->entry),
522 (GtkFileChooserAction) g_value_get_enum (value));
523 update_icons (GTK_FILE_CHOOSER_BUTTON (object));
525 switch (g_value_get_enum (value))
527 case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
528 gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
529 /* Fall through to set the widget states */
530 case GTK_FILE_CHOOSER_ACTION_OPEN:
531 gtk_widget_hide (priv->entry_box);
532 gtk_widget_show (priv->label_box);
533 gtk_box_set_child_packing (GTK_BOX (object), priv->button,
534 TRUE, TRUE, 0, GTK_PACK_START);
535 gtk_button_set_label (GTK_BUTTON (priv->accept_button),
539 case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
540 gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
541 /* Fall through to set the widget states */
542 case GTK_FILE_CHOOSER_ACTION_SAVE:
543 gtk_widget_show (priv->entry_box);
544 gtk_widget_hide (priv->label_box);
545 gtk_box_set_child_packing (GTK_BOX (object), priv->button,
546 FALSE, FALSE, 0, GTK_PACK_START);
547 gtk_button_set_label (GTK_BUTTON (priv->accept_button),
554 case GTK_FILE_CHOOSER_PROP_FILTER:
555 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
556 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
557 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
558 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
559 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
560 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
561 g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
564 case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
566 priv->backend = g_value_dup_string (value);
569 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
570 g_warning ("%s: Choosers of type `%s` do not support selecting multiple files.",
571 G_STRFUNC, G_OBJECT_TYPE_NAME (object));
574 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
580 gtk_file_chooser_button_get_property (GObject *object,
585 GtkFileChooserButtonPrivate *priv;
587 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (object);
592 g_value_set_boolean (value,
593 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)));
595 case PROP_WIDTH_CHARS:
596 g_value_set_int (value,
597 gtk_entry_get_width_chars (GTK_ENTRY (priv->entry)));
601 case GTK_FILE_CHOOSER_PROP_ACTION:
602 case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
603 case GTK_FILE_CHOOSER_PROP_FILTER:
604 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
605 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
606 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
607 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
608 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
609 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
610 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
611 g_object_get_property (G_OBJECT (priv->dialog), pspec->name, value);
615 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
621 /* ********************* *
622 * GtkObject Functions *
623 * ********************* */
626 gtk_file_chooser_button_destroy (GtkObject * object)
628 GtkFileChooserButtonPrivate *priv;
630 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (object);
633 g_source_remove (priv->update_id);
635 if (priv->dialog != NULL)
636 gtk_widget_destroy (priv->dialog);
638 remove_settings_signal (GTK_FILE_CHOOSER_BUTTON (object),
639 gtk_widget_get_screen (GTK_WIDGET (object)));
641 if (GTK_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->destroy != NULL)
642 (*GTK_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->destroy) (object);
646 /* ********************* *
647 * GtkWidget Functions *
648 * ********************* */
651 gtk_file_chooser_button_drag_data_received (GtkWidget *widget,
652 GdkDragContext *context,
655 GtkSelectionData *data,
659 GtkFileChooserButtonPrivate *priv;
662 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->drag_data_received != NULL)
663 (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->drag_data_received) (widget,
669 if (widget == NULL || context == NULL || data == NULL || data->length < 0)
672 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (widget);
682 uris = gtk_selection_data_get_uris (data);
688 for (i = 0; !selected && uris[i] != NULL; i++)
691 GtkFilePath *path, *base_path;
693 fs = _gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog));
694 path = gtk_file_system_uri_to_path (fs, uris[i]);
698 gtk_file_system_get_parent (fs, path, &base_path, NULL))
700 GtkFileFolder *folder;
703 folder = gtk_file_system_get_folder (fs, base_path,
704 GTK_FILE_INFO_IS_FOLDER,
707 info = gtk_file_folder_get_info (folder, path, NULL);
711 GtkFileChooserAction action;
713 g_object_get (priv->dialog, "action", &action, NULL);
716 ((((action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER ||
717 action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) &&
718 gtk_file_info_get_is_folder (info)) ||
719 ((action == GTK_FILE_CHOOSER_ACTION_OPEN ||
720 action == GTK_FILE_CHOOSER_ACTION_SAVE) &&
721 !gtk_file_info_get_is_folder (info))) &&
722 _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (priv->dialog),
725 gtk_file_info_free (info);
730 gtk_file_path_free (base_path);
733 gtk_file_path_free (path);
741 text = gtk_selection_data_get_text (data);
742 gtk_entry_set_text (GTK_ENTRY (priv->entry), text);
747 gtk_drag_finish (context, TRUE, FALSE, drag_time);
751 gtk_file_chooser_button_show_all (GtkWidget *widget)
753 gtk_widget_show (widget);
757 gtk_file_chooser_button_hide_all (GtkWidget *widget)
759 gtk_widget_hide (widget);
763 gtk_file_chooser_button_show (GtkWidget *widget)
765 GtkFileChooserButtonPrivate *priv;
767 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (widget);
769 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->show)
770 (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->show) (widget);
772 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
773 gtk_widget_show (priv->dialog);
777 gtk_file_chooser_button_hide (GtkWidget *widget)
779 GtkFileChooserButtonPrivate *priv;
781 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (widget);
783 gtk_widget_hide (priv->dialog);
785 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide)
786 (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide) (widget);
790 gtk_file_chooser_button_mnemonic_activate (GtkWidget *widget,
791 gboolean group_cycling)
793 GtkFileChooserButtonPrivate *priv;
795 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (widget);
797 switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)))
799 case GTK_FILE_CHOOSER_ACTION_OPEN:
800 case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
801 gtk_widget_grab_focus (priv->button);
803 case GTK_FILE_CHOOSER_ACTION_SAVE:
804 case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
805 gtk_widget_grab_focus (priv->entry);
812 /* Changes the icons wherever it is needed */
814 change_icon_theme (GtkFileChooserButton *button)
816 GtkSettings *settings;
819 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
821 if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_SMALL_TOOLBAR,
823 button->priv->icon_size = MAX (width, height);
825 button->priv->icon_size = FALLBACK_ICON_SIZE;
827 update_icons (button);
830 /* Callback used when a GtkSettings value changes */
832 settings_notify_cb (GObject *object,
838 name = g_param_spec_get_name (pspec);
840 if (strcmp (name, "gtk-icon-theme-name") == 0
841 || strcmp (name, "gtk-icon-sizes") == 0)
842 change_icon_theme (user_data);
845 /* Installs a signal handler for GtkSettings so that we can monitor changes in
849 check_icon_theme (GtkFileChooserButton *button)
851 GtkSettings *settings;
853 if (button->priv->settings_signal_id)
856 if (gtk_widget_has_screen (GTK_WIDGET (button)))
858 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
859 button->priv->settings_signal_id = g_signal_connect (settings, "notify",
860 G_CALLBACK (settings_notify_cb),
863 change_icon_theme (button);
868 gtk_file_chooser_button_style_set (GtkWidget *widget,
871 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->style_set)
872 (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->style_set) (widget,
875 if (gtk_widget_has_screen (widget))
876 change_icon_theme (GTK_FILE_CHOOSER_BUTTON (widget));
880 gtk_file_chooser_button_screen_changed (GtkWidget *widget,
881 GdkScreen *old_screen)
883 GtkFileChooserButton *button;
885 button = GTK_FILE_CHOOSER_BUTTON (widget);
887 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->screen_changed)
888 (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->screen_changed) (widget,
891 remove_settings_signal (button, old_screen);
892 check_icon_theme (button);
896 /* ************************************************************************** *
898 * ************************************************************************** */
901 * gtk_file_chooser_button_new:
902 * @title: the title of the browse dialog.
904 * Creates a new file-selecting button widget.
906 * Returns: a new button widget.
911 gtk_file_chooser_button_new (const gchar *title)
913 return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
919 * gtk_file_chooser_button_new_with_backend:
920 * @title: the title of the browse dialog.
921 * @backend: the name of the #GtkFileSystem backend to use.
923 * Creates a new file-selecting button widget using @backend.
925 * Returns: a new button widget.
930 gtk_file_chooser_button_new_with_backend (const gchar *title,
931 const gchar *backend)
933 return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
935 "file-system-backend", backend,
940 * gtk_file_chooser_button_new_with_dialog:
941 * @dialog: the #GtkDialog widget to use.
943 * Creates a #GtkFileChooserButton widget which uses @dialog as it's
944 * file-picking window. Note that @dialog must be a #GtkFileChooserDialog (or
947 * Returns: a new button widget.
952 gtk_file_chooser_button_new_with_dialog (GtkWidget *dialog)
954 g_return_val_if_fail (GTK_IS_FILE_CHOOSER_DIALOG (dialog), NULL);
956 return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
962 * gtk_file_chooser_button_set_title:
963 * @button: the button widget to modify.
964 * @title: the new browse dialog title.
966 * Modifies the @title of the browse dialog used by @button.
971 gtk_file_chooser_button_set_title (GtkFileChooserButton *button,
974 g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
976 gtk_window_set_title (GTK_WINDOW (button->priv->dialog), title);
977 g_object_notify (G_OBJECT (button), "title");
981 * gtk_file_chooser_button_get_title:
982 * @button: the button widget to examine.
984 * Retrieves the title of the browse dialog used by @button. The returned value
985 * should not be modified or freed.
987 * Returns: a pointer to the browse dialog's title.
991 G_CONST_RETURN gchar *
992 gtk_file_chooser_button_get_title (GtkFileChooserButton *button)
994 g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), NULL);
996 return gtk_window_get_title (GTK_WINDOW (button->priv->dialog));
1000 * gtk_file_chooser_button_set_active:
1001 * @button: the button widget to modify.
1002 * @is_active: whether or not the dialog is visible.
1004 * Modifies whether or not the dialog attached to @button is visible or not.
1009 gtk_file_chooser_button_set_active (GtkFileChooserButton *button,
1012 g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
1014 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button->priv->button), is_active);
1018 * gtk_file_chooser_button_get_active:
1019 * @button: the button widget to examine.
1021 * Retrieves whether or not the dialog attached to @button is visible.
1023 * Returns: a boolean whether the dialog is visible or not.
1028 gtk_file_chooser_button_get_active (GtkFileChooserButton *button)
1030 g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), FALSE);
1032 return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button->priv->button));
1036 * gtk_file_chooser_button_get_width_chars:
1037 * @button: the button widget to examine.
1039 * Retrieves the width in characters of the @button widget's entry and/or label.
1041 * Returns: an integer width (in characters) that the button will use to size itself.
1046 gtk_file_chooser_button_get_width_chars (GtkFileChooserButton *button)
1048 g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), -1);
1050 return gtk_entry_get_width_chars (GTK_ENTRY (button->priv->entry));
1054 * gtk_file_chooser_button_set_width_chars:
1055 * @button: the button widget to examine.
1056 * @n_chars: the new width, in chracters.
1058 * Sets the width (in characters) that @button will use to @n_chars.
1063 gtk_file_chooser_button_set_width_chars (GtkFileChooserButton *button,
1066 g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
1068 gtk_entry_set_width_chars (GTK_ENTRY (button->priv->entry), n_chars);
1069 gtk_label_set_width_chars (GTK_LABEL (button->priv->label), n_chars);
1070 g_object_notify (G_OBJECT (button), "width-chars");
1074 /* ******************* *
1075 * Utility Functions *
1076 * ******************* */
1078 /* Removes the settings signal handler. It's safe to call multiple times */
1080 remove_settings_signal (GtkFileChooserButton *button,
1083 if (button->priv->settings_signal_id)
1085 GtkSettings *settings;
1087 settings = gtk_settings_get_for_screen (screen);
1088 g_signal_handler_disconnect (settings,
1089 button->priv->settings_signal_id);
1090 button->priv->settings_signal_id = 0;
1094 static GtkIconTheme *
1095 get_icon_theme (GtkWidget *widget)
1097 if (gtk_widget_has_screen (widget))
1098 return gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
1100 return gtk_icon_theme_get_default ();
1104 check_if_path_exists (GtkFileSystem *fs,
1105 const GtkFilePath *path)
1107 gboolean path_exists;
1108 GtkFilePath *parent_path;
1110 path_exists = FALSE;
1113 if (gtk_file_system_get_parent (fs, path, &parent_path, NULL))
1115 GtkFileFolder *folder;
1117 folder = gtk_file_system_get_folder (fs, parent_path, 0, NULL);
1122 info = gtk_file_folder_get_info (folder, path, NULL);
1126 gtk_file_info_free (info);
1129 g_object_unref (folder);
1132 gtk_file_path_free (parent_path);
1139 update_icons (GtkFileChooserButton *button)
1141 GtkFileChooserButtonPrivate *priv;
1145 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (button);
1147 paths = _gtk_file_chooser_get_paths (GTK_FILE_CHOOSER (priv->dialog));
1155 fs = _gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog));
1157 switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)))
1159 case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
1161 GtkFileSystemVolume *volume;
1163 volume = gtk_file_system_get_volume_for_path (fs, path);
1166 GtkFilePath *base_path;
1168 base_path = gtk_file_system_volume_get_base_path (fs, volume);
1170 if (base_path && gtk_file_path_compare (base_path, path) == 0)
1171 pixbuf = gtk_file_system_volume_render_icon (fs, volume,
1172 GTK_WIDGET (button),
1177 gtk_file_path_free (base_path);
1179 gtk_file_system_volume_free (fs, volume);
1183 case GTK_FILE_CHOOSER_ACTION_OPEN:
1185 pixbuf = gtk_file_system_render_icon (fs, path, GTK_WIDGET (button),
1186 priv->icon_size, NULL);
1189 case GTK_FILE_CHOOSER_ACTION_SAVE:
1190 if (check_if_path_exists (fs, path))
1191 pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (button)),
1192 GTK_STOCK_DIALOG_WARNING,
1193 priv->icon_size, 0, NULL);
1195 pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (button)),
1197 priv->icon_size, 0, NULL);
1199 case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
1200 if (check_if_path_exists (fs, path))
1201 pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (button)),
1202 GTK_STOCK_DIALOG_WARNING,
1203 priv->icon_size, 0, NULL);
1205 pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (button)),
1207 priv->icon_size, 0, NULL);
1211 gtk_file_paths_free (paths);
1215 pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (button)),
1217 priv->icon_size, 0, NULL);
1219 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->entry_image), pixbuf);
1220 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->label_image), pixbuf);
1223 g_object_unref (pixbuf);
1228 update_label (GtkFileChooserButton *button)
1230 GtkFileChooserButtonPrivate *priv;
1234 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (button);
1235 paths = _gtk_file_chooser_get_paths (GTK_FILE_CHOOSER (button->priv->dialog));
1242 GtkFileSystemVolume *volume;
1246 fs = _gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog));
1248 volume = gtk_file_system_get_volume_for_path (fs, path);
1251 GtkFilePath *base_path;
1253 base_path = gtk_file_system_volume_get_base_path (fs, volume);
1254 if (base_path && gtk_file_path_compare (base_path, path) == 0)
1255 label_text = gtk_file_system_volume_get_display_name (fs, volume);
1258 gtk_file_path_free (base_path);
1260 gtk_file_system_volume_free (fs, volume);
1266 if (gtk_file_system_path_is_local (fs, path))
1272 filename = gtk_file_system_path_to_filename (fs, path);
1277 home = g_get_home_dir ();
1279 /* Munging for psuedo-volumes and files in the user's home tree */
1282 if (strcmp (filename, home) == 0)
1284 label_text = g_strdup (_("Home"));
1288 tmp = g_build_filename (home, "Desktop", NULL);
1290 if (strcmp (filename, tmp) == 0)
1291 label_text = g_strdup (_("Desktop"));
1298 if (g_str_has_prefix (filename, home))
1300 label_text = g_strconcat ("~", filename + strlen (home), NULL);
1306 label_text = g_strdup (filename);
1315 uri = gtk_file_system_path_to_uri (fs, path);
1322 gtk_file_paths_free (paths);
1327 gtk_label_set_text (GTK_LABEL (priv->label), label_text);
1328 g_free (label_text);
1331 gtk_label_set_text (GTK_LABEL (priv->label), _(DEFAULT_FILENAME));
1335 update_entry (GtkFileChooserButton *button)
1337 GtkFileChooserButtonPrivate *priv;
1341 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (button);
1343 paths = _gtk_file_chooser_get_paths (GTK_FILE_CHOOSER (priv->dialog));
1351 fs = _gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog));
1353 if (gtk_file_system_path_is_local (fs, path))
1355 filename = gtk_file_system_path_to_filename (fs, path);
1362 if (g_file_test (filename, G_FILE_TEST_IS_DIR))
1364 tmp = g_strconcat (filename, "/", NULL);
1369 home = g_get_home_dir ();
1371 if (home && g_str_has_prefix (filename, home))
1373 tmp = g_strconcat ("~", filename + strlen (home), NULL);
1380 filename = gtk_file_system_path_to_uri (fs, path);
1389 entry_text = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
1392 gtk_entry_set_text (GTK_ENTRY (priv->entry), entry_text);
1393 g_free (entry_text);
1396 gtk_entry_set_text (GTK_ENTRY (priv->entry), "");
1400 update_dialog (GtkFileChooserButton *button)
1402 GtkFileChooserButtonPrivate *priv;
1403 GtkFilePath *current_folder;
1405 GtkFilePath *folder_part, *full_path;
1408 GtkFilePath *base_path;
1410 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (button);
1413 fs = _gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog));
1415 text = gtk_entry_get_text (GTK_ENTRY (priv->entry));
1417 base_path = gtk_file_path_new_dup ("/");
1418 gtk_file_system_parse (fs, base_path, text, &folder_part, &file_part, NULL);
1419 gtk_file_path_free (base_path);
1421 switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)))
1423 case GTK_FILE_CHOOSER_ACTION_OPEN:
1424 gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
1427 GtkFileFolder *folder;
1430 folder = gtk_file_system_get_folder (fs, folder_part,
1431 GTK_FILE_INFO_IS_FOLDER, NULL);
1433 full_path = gtk_file_system_make_path (fs, folder_part, file_part, NULL);
1434 info = gtk_file_folder_get_info (folder, full_path, NULL);
1436 /* Entry contents don't exist. */
1438 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (priv->dialog),
1440 /* Entry contents are a folder */
1441 else if (gtk_file_info_get_is_folder (info))
1442 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (priv->dialog),
1444 /* Entry contents must be a file. */
1446 _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (priv->dialog),
1450 gtk_file_info_free (info);
1452 gtk_file_path_free (full_path);
1455 case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
1456 gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
1459 /* Entry contents don't exist. */
1460 if (file_part && file_part[0] != '\0')
1462 full_path = gtk_file_system_make_path (fs, folder_part, file_part,
1466 _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (priv->dialog),
1468 gtk_file_path_free (full_path);
1471 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (priv->dialog),
1476 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (priv->dialog),
1482 case GTK_FILE_CHOOSER_ACTION_SAVE:
1483 case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
1484 gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
1487 current_folder = _gtk_file_chooser_get_current_folder_path (GTK_FILE_CHOOSER (priv->dialog));
1489 if (!current_folder ||
1490 gtk_file_path_compare (current_folder, folder_part) != 0)
1492 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (priv->dialog),
1494 g_signal_emit_by_name (button, "current-folder-changed");
1498 gtk_file_path_free (current_folder);
1501 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (priv->dialog),
1503 g_signal_emit_by_name (button, "selection-changed");
1508 /* ************************ *
1509 * Child-Widget Callbacks *
1510 * ************************ */
1513 dialog_current_folder_changed_cb (GtkFileChooser *dialog,
1516 g_signal_emit_by_name (user_data, "current-folder-changed");
1520 dialog_file_activated_cb (GtkFileChooser *dialog,
1523 g_signal_emit_by_name (user_data, "file-activated");
1527 dialog_selection_changed_cb (GtkFileChooser *dialog,
1530 GtkFileChooserButtonPrivate *priv;
1532 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (user_data);
1534 g_signal_handler_block (priv->entry, priv->entry_changed_id);
1535 update_entry (user_data);
1536 g_signal_handler_unblock (priv->entry, priv->entry_changed_id);
1537 update_icons (user_data);
1538 update_label (user_data);
1542 dialog_selection_changed_proxy_cb (GtkFileChooser *dialog,
1545 GtkFileChooserButtonPrivate *priv;
1547 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (user_data);
1549 g_signal_emit_by_name (user_data, "selection-changed");
1553 dialog_update_preview_cb (GtkFileChooser *dialog,
1556 g_signal_emit_by_name (user_data, "update-preview");
1560 dialog_notify_cb (GObject *dialog,
1566 iface = g_type_interface_peek (g_type_class_peek (G_OBJECT_TYPE (dialog)),
1567 GTK_TYPE_FILE_CHOOSER);
1568 if (g_object_interface_find_property (iface, pspec->name))
1569 g_object_notify (user_data, pspec->name);
1573 dialog_delete_event_cb (GtkWidget *dialog,
1577 g_signal_emit_by_name (dialog, "response", GTK_RESPONSE_DELETE_EVENT);
1583 dialog_response_cb (GtkFileChooser *dialog,
1587 GtkFileChooserButtonPrivate *priv;
1589 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (user_data);
1591 if (response == GTK_RESPONSE_ACCEPT)
1593 g_signal_handler_block (priv->entry, priv->entry_changed_id);
1594 update_entry (user_data);
1595 g_signal_handler_unblock (priv->entry, priv->entry_changed_id);
1596 update_label (user_data);
1597 update_icons (user_data);
1599 g_signal_emit_by_name (user_data, "current-folder-changed");
1600 g_signal_emit_by_name (user_data, "selection-changed");
1604 g_signal_handler_block (priv->dialog, priv->dialog_selection_changed_id);
1605 update_dialog (user_data);
1606 g_signal_handler_unblock (priv->dialog, priv->dialog_selection_changed_id);
1609 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), FALSE);
1614 button_toggled_cb (GtkToggleButton *real_button,
1617 GtkFileChooserButtonPrivate *priv;
1619 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (user_data);
1621 if (gtk_toggle_button_get_active (real_button))
1623 /* Setup the dialog parent to be chooser button's toplevel, and be modal
1625 if (!GTK_WIDGET_VISIBLE (priv->dialog))
1627 GtkWidget *toplevel;
1629 toplevel = gtk_widget_get_toplevel (user_data);
1631 if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_IS_WINDOW (toplevel))
1633 if (GTK_WINDOW (toplevel) != gtk_window_get_transient_for (GTK_WINDOW (priv->dialog)))
1634 gtk_window_set_transient_for (GTK_WINDOW (priv->dialog), GTK_WINDOW (toplevel));
1636 gtk_window_set_modal (GTK_WINDOW (priv->dialog),
1637 gtk_window_get_modal (GTK_WINDOW (toplevel)));
1641 g_signal_handler_block (priv->dialog,
1642 priv->dialog_folder_changed_id);
1643 g_signal_handler_block (priv->dialog,
1644 priv->dialog_file_activated_id);
1645 g_signal_handler_block (priv->dialog,
1646 priv->dialog_selection_changed_proxy_id);
1647 gtk_widget_set_sensitive (priv->entry, FALSE);
1648 gtk_window_present (GTK_WINDOW (priv->dialog));
1652 g_signal_handler_unblock (priv->dialog,
1653 priv->dialog_folder_changed_id);
1654 g_signal_handler_unblock (priv->dialog,
1655 priv->dialog_file_activated_id);
1656 g_signal_handler_unblock (priv->dialog,
1657 priv->dialog_selection_changed_proxy_id);
1658 gtk_widget_set_sensitive (priv->entry, TRUE);
1659 gtk_widget_hide (priv->dialog);
1664 button_notify_active_cb (GObject *real_button,
1668 g_object_notify (user_data, "active");
1673 update_idler (gpointer user_data)
1675 GtkFileChooserButtonPrivate *priv;
1679 GDK_THREADS_ENTER ();
1681 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (user_data);
1683 if (!gtk_editable_get_selection_bounds (GTK_EDITABLE (priv->entry),
1686 g_signal_handler_block (priv->dialog,
1687 priv->dialog_selection_changed_id);
1688 update_dialog (user_data);
1689 g_signal_handler_unblock (priv->dialog,
1690 priv->dialog_selection_changed_id);
1691 update_icons (user_data);
1692 update_label (user_data);
1693 priv->update_id = 0;
1699 GDK_THREADS_LEAVE ();
1705 entry_changed_cb (GtkEditable *editable,
1708 GtkFileChooserButtonPrivate *priv;
1710 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (user_data);
1712 if (priv->update_id)
1713 g_source_remove (priv->update_id);
1715 priv->update_id = g_idle_add_full (G_PRIORITY_LOW, update_idler,
1719 /* Ensure the button height == entry height */
1721 entry_size_allocate_cb (GtkWidget *entry,
1722 GtkAllocation *allocation,
1725 gtk_widget_set_size_request (user_data, -1, allocation->height);