X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkbutton.c;h=d749344a0527245961a20e0910cd65eb6a45289b;hb=1a312bfa3e32414065a87d7376a9bff8c61f0560;hp=cc49f886efaa45cb511af7afb2033408ac8a3e2b;hpb=1773d815583c0d4bded05c1d89d6fce7b6032e0c;p=~andy%2Fgtk diff --git a/gtk/gtkbutton.c b/gtk/gtkbutton.c index cc49f886e..d749344a0 100644 --- a/gtk/gtkbutton.c +++ b/gtk/gtkbutton.c @@ -24,24 +24,28 @@ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ +#include "config.h" #include #include "gtkalignment.h" #include "gtkbutton.h" #include "gtklabel.h" #include "gtkmain.h" -#include "gtksignal.h" +#include "gtkmarshalers.h" #include "gtkimage.h" #include "gtkhbox.h" +#include "gtkvbox.h" #include "gtkstock.h" #include "gtkiconfactory.h" +#include "gtkactivatable.h" +#include "gtkprivate.h" #include "gtkintl.h" +#include "gtkalias.h" -#define CHILD_SPACING 1 +static const GtkBorder default_default_border = { 1, 1, 1, 1 }; +static const GtkBorder default_default_outside_border = { 0, 0, 0, 0 }; +static const GtkBorder default_inner_border = { 1, 1, 1, 1 }; -static GtkBorder default_default_border = { 1, 1, 1, 1 }; -static GtkBorder default_default_outside_border = { 0, 0, 0, 0 }; - -/* Time out before giving up on getting a key release when animatng +/* Time out before giving up on getting a key release when animating * the close button. */ #define ACTIVATE_TIMEOUT 250 @@ -59,116 +63,148 @@ enum { enum { PROP_0, PROP_LABEL, + PROP_IMAGE, PROP_RELIEF, PROP_USE_UNDERLINE, - PROP_USE_STOCK + PROP_USE_STOCK, + PROP_FOCUS_ON_CLICK, + PROP_XALIGN, + PROP_YALIGN, + PROP_IMAGE_POSITION, + + /* activatable properties */ + PROP_ACTIVATABLE_RELATED_ACTION, + PROP_ACTIVATABLE_USE_ACTION_APPEARANCE }; -static void gtk_button_class_init (GtkButtonClass *klass); -static void gtk_button_init (GtkButton *button); -static void gtk_button_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_button_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); -static void gtk_button_realize (GtkWidget *widget); -static void gtk_button_unrealize (GtkWidget *widget); -static void gtk_button_size_request (GtkWidget *widget, - GtkRequisition *requisition); -static void gtk_button_size_allocate (GtkWidget *widget, - GtkAllocation *allocation); -static void gtk_button_paint (GtkWidget *widget, - GdkRectangle *area); -static gint gtk_button_expose (GtkWidget *widget, - GdkEventExpose *event); -static gint gtk_button_button_press (GtkWidget *widget, - GdkEventButton *event); -static gint gtk_button_button_release (GtkWidget *widget, - GdkEventButton *event); -static gint gtk_button_key_release (GtkWidget *widget, - GdkEventKey *event); -static gint gtk_button_enter_notify (GtkWidget *widget, - GdkEventCrossing *event); -static gint gtk_button_leave_notify (GtkWidget *widget, - GdkEventCrossing *event); -static void gtk_real_button_pressed (GtkButton *button); -static void gtk_real_button_released (GtkButton *button); -static void gtk_real_button_activate (GtkButton *button); -static void gtk_button_update_state (GtkButton *button); -static GtkType gtk_button_child_type (GtkContainer *container); -static void gtk_button_finish_activate (GtkButton *button, - gboolean do_it); - -static GObject* gtk_button_constructor (GType type, - guint n_construct_properties, - GObjectConstructParam *construct_params); -static void gtk_button_construct_child (GtkButton *button); - - -static GtkBinClass *parent_class = NULL; -static guint button_signals[LAST_SIGNAL] = { 0 }; - +#define GTK_BUTTON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_BUTTON, GtkButtonPrivate)) +typedef struct _GtkButtonPrivate GtkButtonPrivate; -GtkType -gtk_button_get_type (void) +struct _GtkButtonPrivate { - static GtkType button_type = 0; + gfloat xalign; + gfloat yalign; + GtkWidget *image; + guint align_set : 1; + guint image_is_stock : 1; + guint has_grab : 1; + guint use_action_appearance : 1; + guint32 grab_time; + GtkPositionType image_position; + GtkAction *action; +}; - if (!button_type) - { - static const GTypeInfo button_info = - { - sizeof (GtkButtonClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) gtk_button_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (GtkButton), - 16, /* n_preallocs */ - (GInstanceInitFunc) gtk_button_init, - }; - - button_type = g_type_register_static (GTK_TYPE_BIN, "GtkButton", &button_info, 0); - } +static void gtk_button_destroy (GtkObject *object); +static void gtk_button_dispose (GObject *object); +static void gtk_button_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_button_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void gtk_button_screen_changed (GtkWidget *widget, + GdkScreen *previous_screen); +static void gtk_button_realize (GtkWidget * widget); +static void gtk_button_unrealize (GtkWidget * widget); +static void gtk_button_map (GtkWidget * widget); +static void gtk_button_unmap (GtkWidget * widget); +static void gtk_button_style_set (GtkWidget * widget, GtkStyle * prev_style); +static void gtk_button_size_request (GtkWidget * widget, + GtkRequisition * requisition); +static void gtk_button_size_allocate (GtkWidget * widget, + GtkAllocation * allocation); +static gint gtk_button_expose (GtkWidget * widget, GdkEventExpose * event); +static gint gtk_button_button_press (GtkWidget * widget, + GdkEventButton * event); +static gint gtk_button_button_release (GtkWidget * widget, + GdkEventButton * event); +static gint gtk_button_grab_broken (GtkWidget * widget, + GdkEventGrabBroken * event); +static gint gtk_button_key_release (GtkWidget * widget, GdkEventKey * event); +static gint gtk_button_enter_notify (GtkWidget * widget, + GdkEventCrossing * event); +static gint gtk_button_leave_notify (GtkWidget * widget, + GdkEventCrossing * event); +static void gtk_real_button_pressed (GtkButton * button); +static void gtk_real_button_released (GtkButton * button); +static void gtk_real_button_clicked (GtkButton * button); +static void gtk_real_button_activate (GtkButton *button); +static void gtk_button_update_state (GtkButton *button); +static void gtk_button_add (GtkContainer *container, + GtkWidget *widget); +static GType gtk_button_child_type (GtkContainer *container); +static void gtk_button_finish_activate (GtkButton *button, + gboolean do_it); + +static GObject* gtk_button_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_params); +static void gtk_button_construct_child (GtkButton *button); +static void gtk_button_state_changed (GtkWidget *widget, + GtkStateType previous_state); +static void gtk_button_grab_notify (GtkWidget *widget, + gboolean was_grabbed); + + +static void gtk_button_activatable_interface_init (GtkActivatableIface *iface); +static void gtk_button_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name); +static void gtk_button_sync_action_properties (GtkActivatable *activatable, + GtkAction *action); +static void gtk_button_set_related_action (GtkButton *button, + GtkAction *action); +static void gtk_button_set_use_action_appearance (GtkButton *button, + gboolean use_appearance); - return button_type; -} +static guint button_signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE_WITH_CODE (GtkButton, gtk_button, GTK_TYPE_BIN, + G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE, + gtk_button_activatable_interface_init)) static void gtk_button_class_init (GtkButtonClass *klass) { - GObjectClass *g_object_class; + GObjectClass *gobject_class; GtkObjectClass *object_class; GtkWidgetClass *widget_class; GtkContainerClass *container_class; - g_object_class = G_OBJECT_CLASS (klass); + gobject_class = G_OBJECT_CLASS (klass); object_class = (GtkObjectClass*) klass; widget_class = (GtkWidgetClass*) klass; container_class = (GtkContainerClass*) klass; - parent_class = g_type_class_peek_parent (klass); + gobject_class->constructor = gtk_button_constructor; + gobject_class->dispose = gtk_button_dispose; + gobject_class->set_property = gtk_button_set_property; + gobject_class->get_property = gtk_button_get_property; - g_object_class->constructor = gtk_button_constructor; - g_object_class->set_property = gtk_button_set_property; - g_object_class->get_property = gtk_button_get_property; + object_class->destroy = gtk_button_destroy; + widget_class->screen_changed = gtk_button_screen_changed; widget_class->realize = gtk_button_realize; widget_class->unrealize = gtk_button_unrealize; + widget_class->map = gtk_button_map; + widget_class->unmap = gtk_button_unmap; + widget_class->style_set = gtk_button_style_set; widget_class->size_request = gtk_button_size_request; widget_class->size_allocate = gtk_button_size_allocate; widget_class->expose_event = gtk_button_expose; widget_class->button_press_event = gtk_button_button_press; widget_class->button_release_event = gtk_button_button_release; + widget_class->grab_broken_event = gtk_button_grab_broken; widget_class->key_release_event = gtk_button_key_release; widget_class->enter_notify_event = gtk_button_enter_notify; widget_class->leave_notify_event = gtk_button_leave_notify; + widget_class->state_changed = gtk_button_state_changed; + widget_class->grab_notify = gtk_button_grab_notify; container_class->child_type = gtk_button_child_type; + container_class->add = gtk_button_add; klass->pressed = gtk_real_button_pressed; klass->released = gtk_real_button_released; @@ -177,119 +213,319 @@ gtk_button_class_init (GtkButtonClass *klass) klass->leave = gtk_button_update_state; klass->activate = gtk_real_button_activate; - g_object_class_install_property (G_OBJECT_CLASS(object_class), + g_object_class_install_property (gobject_class, PROP_LABEL, g_param_spec_string ("label", - _("Label"), - _("Text of the label widget inside the button, if the button contains a label widget."), + P_("Label"), + P_("Text of the label widget inside the button, if the button contains a label widget"), NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT)); - g_object_class_install_property (G_OBJECT_CLASS(object_class), + g_object_class_install_property (gobject_class, PROP_USE_UNDERLINE, - g_param_spec_boolean ("use_underline", - _("Use underline"), - _("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"), + g_param_spec_boolean ("use-underline", + P_("Use underline"), + P_("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"), FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT)); - g_object_class_install_property (G_OBJECT_CLASS(object_class), + g_object_class_install_property (gobject_class, PROP_USE_STOCK, - g_param_spec_boolean ("use_stock", - _("Use stock"), - _("If set, the label is used to pick a stock item instead of being displayed"), + g_param_spec_boolean ("use-stock", + P_("Use stock"), + P_("If set, the label is used to pick a stock item instead of being displayed"), FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_object_class_install_property (gobject_class, + PROP_FOCUS_ON_CLICK, + g_param_spec_boolean ("focus-on-click", + P_("Focus on click"), + P_("Whether the button grabs focus when it is clicked with the mouse"), + TRUE, + GTK_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS(object_class), + g_object_class_install_property (gobject_class, PROP_RELIEF, g_param_spec_enum ("relief", - _("Border relief"), - _("The border relief style."), + P_("Border relief"), + P_("The border relief style"), GTK_TYPE_RELIEF_STYLE, GTK_RELIEF_NORMAL, - G_PARAM_READABLE | G_PARAM_WRITABLE)); - + GTK_PARAM_READWRITE)); + + /** + * GtkButton:xalign: + * + * If the child of the button is a #GtkMisc or #GtkAlignment, this property + * can be used to control it's horizontal alignment. 0.0 is left aligned, + * 1.0 is right aligned. + * + * Since: 2.4 + */ + g_object_class_install_property (gobject_class, + PROP_XALIGN, + g_param_spec_float("xalign", + P_("Horizontal alignment for child"), + P_("Horizontal position of child in available space. 0.0 is left aligned, 1.0 is right aligned"), + 0.0, + 1.0, + 0.5, + GTK_PARAM_READWRITE)); + + /** + * GtkButton:yalign: + * + * If the child of the button is a #GtkMisc or #GtkAlignment, this property + * can be used to control it's vertical alignment. 0.0 is top aligned, + * 1.0 is bottom aligned. + * + * Since: 2.4 + */ + g_object_class_install_property (gobject_class, + PROP_YALIGN, + g_param_spec_float("yalign", + P_("Vertical alignment for child"), + P_("Vertical position of child in available space. 0.0 is top aligned, 1.0 is bottom aligned"), + 0.0, + 1.0, + 0.5, + GTK_PARAM_READWRITE)); + + /** + * GtkButton::image: + * + * The child widget to appear next to the button text. + * + * Since: 2.6 + */ + g_object_class_install_property (gobject_class, + PROP_IMAGE, + g_param_spec_object ("image", + P_("Image widget"), + P_("Child widget to appear next to the button text"), + GTK_TYPE_WIDGET, + GTK_PARAM_READWRITE)); + + /** + * GtkButton:image-position: + * + * The position of the image relative to the text inside the button. + * + * Since: 2.10 + */ + g_object_class_install_property (gobject_class, + PROP_IMAGE_POSITION, + g_param_spec_enum ("image-position", + P_("Image position"), + P_("The position of the image relative to the text"), + GTK_TYPE_POSITION_TYPE, + GTK_POS_LEFT, + GTK_PARAM_READWRITE)); + + g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_RELATED_ACTION, "related-action"); + g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_USE_ACTION_APPEARANCE, "use-action-appearance"); + + /** + * GtkButton::pressed: + * @button: the object that received the signal + * + * Emitted when the button is pressed. + * + * Deprecated: 2.8: Use the #GtkWidget::button-press-event signal. + */ button_signals[PRESSED] = - gtk_signal_new ("pressed", - GTK_RUN_FIRST, - GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkButtonClass, pressed), - gtk_marshal_VOID__VOID, - GTK_TYPE_NONE, 0); + g_signal_new (I_("pressed"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkButtonClass, pressed), + NULL, NULL, + _gtk_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /** + * GtkButton::released: + * @button: the object that received the signal + * + * Emitted when the button is released. + * + * Deprecated: 2.8: Use the #GtkWidget::button-release-event signal. + */ button_signals[RELEASED] = - gtk_signal_new ("released", - GTK_RUN_FIRST, - GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkButtonClass, released), - gtk_marshal_VOID__VOID, - GTK_TYPE_NONE, 0); + g_signal_new (I_("released"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkButtonClass, released), + NULL, NULL, + _gtk_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /** + * GtkButton::clicked: + * @button: the object that received the signal + * + * Emitted when the button has been activated (pressed and released). + */ button_signals[CLICKED] = - gtk_signal_new ("clicked", - GTK_RUN_FIRST | GTK_RUN_ACTION, - GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkButtonClass, clicked), - gtk_marshal_VOID__VOID, - GTK_TYPE_NONE, 0); + g_signal_new (I_("clicked"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkButtonClass, clicked), + NULL, NULL, + _gtk_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /** + * GtkButton::enter: + * @button: the object that received the signal + * + * Emitted when the pointer enters the button. + * + * Deprecated: 2.8: Use the #GtkWidget::enter-notify-event signal. + */ button_signals[ENTER] = - gtk_signal_new ("enter", - GTK_RUN_FIRST, - GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkButtonClass, enter), - gtk_marshal_VOID__VOID, - GTK_TYPE_NONE, 0); + g_signal_new (I_("enter"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkButtonClass, enter), + NULL, NULL, + _gtk_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /** + * GtkButton::leave: + * @button: the object that received the signal + * + * Emitted when the pointer leaves the button. + * + * Deprecated: 2.8: Use the #GtkWidget::leave-notify-event signal. + */ button_signals[LEAVE] = - gtk_signal_new ("leave", - GTK_RUN_FIRST, - GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkButtonClass, leave), - gtk_marshal_VOID__VOID, - GTK_TYPE_NONE, 0); + g_signal_new (I_("leave"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkButtonClass, leave), + NULL, NULL, + _gtk_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /** + * GtkButton::activate: + * @widget: the object which received the signal. + * + * The ::activate signal on GtkButton is an action signal and + * emitting it causes the button to animate press then release. + * Applications should never connect to this signal, but use the + * #GtkButton::clicked signal. + */ button_signals[ACTIVATE] = - gtk_signal_new ("activate", - GTK_RUN_FIRST, - GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkButtonClass, activate), - gtk_marshal_VOID__VOID, - GTK_TYPE_NONE, 0); + g_signal_new (I_("activate"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkButtonClass, activate), + NULL, NULL, + _gtk_marshal_VOID__VOID, + G_TYPE_NONE, 0); widget_class->activate_signal = button_signals[ACTIVATE]; gtk_widget_class_install_style_property (widget_class, - g_param_spec_boxed ("default_border", - _("Default Spacing"), - _("Extra space to add for CAN_DEFAULT buttons"), + g_param_spec_boxed ("default-border", + P_("Default Spacing"), + P_("Extra space to add for CAN_DEFAULT buttons"), GTK_TYPE_BORDER, - G_PARAM_READABLE)); + GTK_PARAM_READABLE)); gtk_widget_class_install_style_property (widget_class, - g_param_spec_boxed ("default_outside_border", - _("Default Outside Spacing"), - _("Extra space to add for CAN_DEFAULT buttons that is always drawn outside the border"), + g_param_spec_boxed ("default-outside-border", + P_("Default Outside Spacing"), + P_("Extra space to add for CAN_DEFAULT buttons that is always drawn outside the border"), GTK_TYPE_BORDER, - G_PARAM_READABLE)); + GTK_PARAM_READABLE)); gtk_widget_class_install_style_property (widget_class, - g_param_spec_int ("child_displacement_x", - _("Child X Displacement"), - _("How far in the x direction to move the child when the button is depressed"), + g_param_spec_int ("child-displacement-x", + P_("Child X Displacement"), + P_("How far in the x direction to move the child when the button is depressed"), G_MININT, G_MAXINT, 0, - G_PARAM_READABLE)); + GTK_PARAM_READABLE)); gtk_widget_class_install_style_property (widget_class, - g_param_spec_int ("child_displacement_y", - _("Child Y Displacement"), - _("How far in the y direction to move the child when the button is depressed"), + g_param_spec_int ("child-displacement-y", + P_("Child Y Displacement"), + P_("How far in the y direction to move the child when the button is depressed"), G_MININT, G_MAXINT, 0, - G_PARAM_READABLE)); + GTK_PARAM_READABLE)); + + /** + * GtkButton:displace-focus: + * + * Whether the child_displacement_x/child_displacement_y properties + * should also affect the focus rectangle. + * + * Since: 2.6 + */ + gtk_widget_class_install_style_property (widget_class, + g_param_spec_boolean ("displace-focus", + P_("Displace focus"), + P_("Whether the child_displacement_x/_y properties should also affect the focus rectangle"), + FALSE, + GTK_PARAM_READABLE)); + + /** + * GtkButton:inner-border: + * + * Sets the border between the button edges and child. + * + * Since: 2.10 + */ + gtk_widget_class_install_style_property (widget_class, + g_param_spec_boxed ("inner-border", + P_("Inner Border"), + P_("Border between button edges and child."), + GTK_TYPE_BORDER, + GTK_PARAM_READABLE)); + + /** + * GtkButton::image-spacing: + * + * Spacing in pixels between the image and label. + * + * Since: 2.10 + */ + gtk_widget_class_install_style_property (widget_class, + g_param_spec_int ("image-spacing", + P_("Image spacing"), + P_("Spacing in pixels between the image and label"), + 0, + G_MAXINT, + 2, + GTK_PARAM_READABLE)); + + /** + * GtkSettings::gtk-button-images: + * + * Whether images should be shown on buttons + * + * Since: 2.4 + */ + gtk_settings_install_property (g_param_spec_boolean ("gtk-button-images", + P_("Show button images"), + P_("Whether images should be shown on buttons"), + TRUE, + GTK_PARAM_READWRITE)); + + g_type_class_add_private (gobject_class, sizeof (GtkButtonPrivate)); } static void gtk_button_init (GtkButton *button) { + GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_FOCUS | GTK_RECEIVES_DEFAULT); - GTK_WIDGET_UNSET_FLAGS (button, GTK_NO_WINDOW); + GTK_WIDGET_SET_FLAGS (button, GTK_NO_WINDOW); button->label_text = NULL; @@ -300,6 +536,29 @@ gtk_button_init (GtkButton *button) button->use_stock = FALSE; button->use_underline = FALSE; button->depressed = FALSE; + button->depress_on_activate = TRUE; + button->focus_on_click = TRUE; + + priv->xalign = 0.5; + priv->yalign = 0.5; + priv->align_set = 0; + priv->image_is_stock = TRUE; + priv->image_position = GTK_POS_LEFT; + priv->use_action_appearance = TRUE; +} + +static void +gtk_button_destroy (GtkObject *object) +{ + GtkButton *button = GTK_BUTTON (object); + + if (button->label_text) + { + g_free (button->label_text); + button->label_text = NULL; + } + + GTK_OBJECT_CLASS (gtk_button_parent_class)->destroy (object); } static GObject* @@ -310,9 +569,9 @@ gtk_button_constructor (GType type, GObject *object; GtkButton *button; - object = (* G_OBJECT_CLASS (parent_class)->constructor) (type, - n_construct_properties, - construct_params); + object = G_OBJECT_CLASS (gtk_button_parent_class)->constructor (type, + n_construct_properties, + construct_params); button = GTK_BUTTON (object); button->constructed = TRUE; @@ -324,13 +583,59 @@ gtk_button_constructor (GType type, } -static GtkType +static GType gtk_button_child_type (GtkContainer *container) { if (!GTK_BIN (container)->child) return GTK_TYPE_WIDGET; else - return GTK_TYPE_NONE; + return G_TYPE_NONE; +} + +static void +maybe_set_alignment (GtkButton *button, + GtkWidget *widget) +{ + GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + + if (GTK_IS_MISC (widget)) + { + GtkMisc *misc = GTK_MISC (widget); + + if (priv->align_set) + gtk_misc_set_alignment (misc, priv->xalign, priv->yalign); + } + else if (GTK_IS_ALIGNMENT (widget)) + { + GtkAlignment *alignment = GTK_ALIGNMENT (widget); + + if (priv->align_set) + gtk_alignment_set (alignment, priv->xalign, priv->yalign, + alignment->xscale, alignment->yscale); + } +} + +static void +gtk_button_add (GtkContainer *container, + GtkWidget *widget) +{ + maybe_set_alignment (GTK_BUTTON (container), widget); + + GTK_CONTAINER_CLASS (gtk_button_parent_class)->add (container, widget); +} + +static void +gtk_button_dispose (GObject *object) +{ + GtkButton *button = GTK_BUTTON (object); + GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + + if (priv->action) + { + gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (button), NULL); + priv->action = NULL; + } + G_OBJECT_CLASS (gtk_button_parent_class)->dispose (object); } static void @@ -339,15 +644,17 @@ gtk_button_set_property (GObject *object, const GValue *value, GParamSpec *pspec) { - GtkButton *button; - - button = GTK_BUTTON (object); + GtkButton *button = GTK_BUTTON (object); + GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); switch (prop_id) { case PROP_LABEL: gtk_button_set_label (button, g_value_get_string (value)); break; + case PROP_IMAGE: + gtk_button_set_image (button, (GtkWidget *) g_value_get_object (value)); + break; case PROP_RELIEF: gtk_button_set_relief (button, g_value_get_enum (value)); break; @@ -357,6 +664,24 @@ gtk_button_set_property (GObject *object, case PROP_USE_STOCK: gtk_button_set_use_stock (button, g_value_get_boolean (value)); break; + case PROP_FOCUS_ON_CLICK: + gtk_button_set_focus_on_click (button, g_value_get_boolean (value)); + break; + case PROP_XALIGN: + gtk_button_set_alignment (button, g_value_get_float (value), priv->yalign); + break; + case PROP_YALIGN: + gtk_button_set_alignment (button, priv->xalign, g_value_get_float (value)); + break; + case PROP_IMAGE_POSITION: + gtk_button_set_image_position (button, g_value_get_enum (value)); + break; + case PROP_ACTIVATABLE_RELATED_ACTION: + gtk_button_set_related_action (button, g_value_get_object (value)); + break; + case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE: + gtk_button_set_use_action_appearance (button, g_value_get_boolean (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -369,15 +694,17 @@ gtk_button_get_property (GObject *object, GValue *value, GParamSpec *pspec) { - GtkButton *button; - - button = GTK_BUTTON (object); + GtkButton *button = GTK_BUTTON (object); + GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); switch (prop_id) { case PROP_LABEL: g_value_set_string (value, button->label_text); break; + case PROP_IMAGE: + g_value_set_object (value, (GObject *)priv->image); + break; case PROP_RELIEF: g_value_set_enum (value, gtk_button_get_relief (button)); break; @@ -387,60 +714,309 @@ gtk_button_get_property (GObject *object, case PROP_USE_STOCK: g_value_set_boolean (value, button->use_stock); break; + case PROP_FOCUS_ON_CLICK: + g_value_set_boolean (value, button->focus_on_click); + break; + case PROP_XALIGN: + g_value_set_float (value, priv->xalign); + break; + case PROP_YALIGN: + g_value_set_float (value, priv->yalign); + break; + case PROP_IMAGE_POSITION: + g_value_set_enum (value, priv->image_position); + break; + case PROP_ACTIVATABLE_RELATED_ACTION: + g_value_set_object (value, priv->action); + break; + case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE: + g_value_set_boolean (value, priv->use_action_appearance); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } +static void +gtk_button_activatable_interface_init (GtkActivatableIface *iface) +{ + iface->update = gtk_button_update; + iface->sync_action_properties = gtk_button_sync_action_properties; +} + +static void +activatable_update_stock_id (GtkButton *button, + GtkAction *action) +{ + if (!gtk_button_get_use_stock (button)) + return; + + gtk_button_set_label (button, gtk_action_get_stock_id (action)); +} + +static void +activatable_update_short_label (GtkButton *button, + GtkAction *action) +{ + GtkWidget *image; + + if (gtk_button_get_use_stock (button)) + return; + + image = gtk_button_get_image (button); + + /* Dont touch custom child... */ + if (GTK_IS_IMAGE (image) || + GTK_BIN (button)->child == NULL || + GTK_IS_LABEL (GTK_BIN (button)->child)) + { + gtk_button_set_label (button, gtk_action_get_short_label (action)); + gtk_button_set_use_underline (button, TRUE); + } +} + +static void +activatable_update_icon_name (GtkButton *button, + GtkAction *action) +{ + GtkWidget *image; + + if (gtk_button_get_use_stock (button)) + return; + + image = gtk_button_get_image (button); + + if (GTK_IS_IMAGE (image) && + (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_EMPTY || + gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_ICON_NAME)) + gtk_image_set_from_icon_name (GTK_IMAGE (image), + gtk_action_get_icon_name (action), GTK_ICON_SIZE_MENU); +} + +static void +activatable_update_gicon (GtkButton *button, + GtkAction *action) +{ + GtkWidget *image = gtk_button_get_image (button); + GIcon *icon = gtk_action_get_gicon (action); + + if (GTK_IS_IMAGE (image) && + (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_EMPTY || + gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_GICON)) + gtk_image_set_from_gicon (GTK_IMAGE (image), icon, GTK_ICON_SIZE_BUTTON); +} + +static void +gtk_button_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name) +{ + GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (activatable); + + if (strcmp (property_name, "visible") == 0) + { + if (gtk_action_is_visible (action)) + gtk_widget_show (GTK_WIDGET (activatable)); + else + gtk_widget_hide (GTK_WIDGET (activatable)); + } + else if (strcmp (property_name, "sensitive") == 0) + gtk_widget_set_sensitive (GTK_WIDGET (activatable), gtk_action_is_sensitive (action)); + + if (!priv->use_action_appearance) + return; + + if (strcmp (property_name, "stock-id") == 0) + activatable_update_stock_id (GTK_BUTTON (activatable), action); + else if (strcmp (property_name, "gicon") == 0) + activatable_update_gicon (GTK_BUTTON (activatable), action); + else if (strcmp (property_name, "short-label") == 0) + activatable_update_short_label (GTK_BUTTON (activatable), action); + else if (strcmp (property_name, "icon-name") == 0) + activatable_update_icon_name (GTK_BUTTON (activatable), action); +} + +static void +gtk_button_sync_action_properties (GtkActivatable *activatable, + GtkAction *action) +{ + GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (activatable); + + if (!action) + return; + + if (gtk_action_is_visible (action)) + gtk_widget_show (GTK_WIDGET (activatable)); + else + gtk_widget_hide (GTK_WIDGET (activatable)); + + gtk_widget_set_sensitive (GTK_WIDGET (activatable), gtk_action_is_sensitive (action)); + + if (priv->use_action_appearance) + { + activatable_update_stock_id (GTK_BUTTON (activatable), action); + activatable_update_short_label (GTK_BUTTON (activatable), action); + activatable_update_gicon (GTK_BUTTON (activatable), action); + activatable_update_icon_name (GTK_BUTTON (activatable), action); + } +} + +static void +gtk_button_set_related_action (GtkButton *button, + GtkAction *action) +{ + GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + + if (priv->action == action) + return; + + /* This should be a default handler, but for compatibility reasons + * we need to support derived classes that don't chain up their + * clicked handler. + */ + g_signal_handlers_disconnect_by_func (button, gtk_real_button_clicked, NULL); + if (action) + g_signal_connect_after (button, "clicked", + G_CALLBACK (gtk_real_button_clicked), NULL); + + gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (button), action); + + priv->action = action; +} + +static void +gtk_button_set_use_action_appearance (GtkButton *button, + gboolean use_appearance) +{ + GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + + if (priv->use_action_appearance != use_appearance) + { + priv->use_action_appearance = use_appearance; + + gtk_activatable_sync_action_properties (GTK_ACTIVATABLE (button), priv->action); + } +} + GtkWidget* gtk_button_new (void) { - return GTK_WIDGET (gtk_type_new (gtk_button_get_type ())); + return g_object_new (GTK_TYPE_BUTTON, NULL); +} + +static gboolean +show_image (GtkButton *button) +{ + gboolean show; + + if (button->label_text) + { + GtkSettings *settings; + + settings = gtk_widget_get_settings (GTK_WIDGET (button)); + g_object_get (settings, "gtk-button-images", &show, NULL); + } + else + show = TRUE; + + return show; } static void gtk_button_construct_child (GtkButton *button) { + GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); GtkStockItem item; GtkWidget *label; - GtkWidget *image; - GtkWidget *hbox; + GtkWidget *box; GtkWidget *align; - + GtkWidget *image = NULL; + gchar *label_text = NULL; + gint image_spacing; + if (!button->constructed) return; - - if (button->label_text == NULL) + + if (!button->label_text && !priv->image) return; + + gtk_widget_style_get (GTK_WIDGET (button), + "image-spacing", &image_spacing, + NULL); + + if (priv->image && !priv->image_is_stock) + { + image = g_object_ref (priv->image); + if (image->parent) + gtk_container_remove (GTK_CONTAINER (image->parent), image); + } + + priv->image = NULL; if (GTK_BIN (button)->child) gtk_container_remove (GTK_CONTAINER (button), GTK_BIN (button)->child); - if (button->use_stock && + button->label_text && gtk_stock_lookup (button->label_text, &item)) { - label = gtk_label_new_with_mnemonic (item.label); + if (!image) + image = g_object_ref (gtk_image_new_from_stock (button->label_text, GTK_ICON_SIZE_BUTTON)); - gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button)); - - image = gtk_image_new_from_stock (button->label_text, GTK_ICON_SIZE_BUTTON); - hbox = gtk_hbox_new (FALSE, 2); + label_text = item.label; + } + else + label_text = button->label_text; - align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); - - gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); - gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0); + if (image) + { + priv->image = image; + g_object_set (priv->image, + "visible", show_image (button), + "no-show-all", TRUE, + NULL); + + if (priv->image_position == GTK_POS_LEFT || + priv->image_position == GTK_POS_RIGHT) + box = gtk_hbox_new (FALSE, image_spacing); + else + box = gtk_vbox_new (FALSE, image_spacing); + + if (priv->align_set) + align = gtk_alignment_new (priv->xalign, priv->yalign, 0.0, 0.0); + else + align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); + + if (priv->image_position == GTK_POS_LEFT || + priv->image_position == GTK_POS_TOP) + gtk_box_pack_start (GTK_BOX (box), priv->image, FALSE, FALSE, 0); + else + gtk_box_pack_end (GTK_BOX (box), priv->image, FALSE, FALSE, 0); + + if (label_text) + { + label = gtk_label_new_with_mnemonic (label_text); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), + GTK_WIDGET (button)); + + if (priv->image_position == GTK_POS_RIGHT || + priv->image_position == GTK_POS_BOTTOM) + gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0); + else + gtk_box_pack_end (GTK_BOX (box), label, FALSE, FALSE, 0); + } gtk_container_add (GTK_CONTAINER (button), align); - gtk_container_add (GTK_CONTAINER (align), hbox); + gtk_container_add (GTK_CONTAINER (align), box); gtk_widget_show_all (align); + g_object_unref (image); + return; } - + if (button->use_underline) { label = gtk_label_new_with_mnemonic (button->label_text); @@ -449,8 +1025,9 @@ gtk_button_construct_child (GtkButton *button) else label = gtk_label_new (button->label_text); - gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5); - + if (priv->align_set) + gtk_misc_set_alignment (GTK_MISC (label), priv->xalign, priv->yalign); + gtk_widget_show (label); gtk_container_add (GTK_CONTAINER (button), label); } @@ -480,8 +1057,8 @@ gtk_button_new_from_stock (const gchar *stock_id) { return g_object_new (GTK_TYPE_BUTTON, "label", stock_id, - "use_stock", TRUE, - "use_underline", TRUE, + "use-stock", TRUE, + "use-underline", TRUE, NULL); } @@ -492,14 +1069,16 @@ gtk_button_new_from_stock (const gchar *stock_id) * @returns: a new #GtkButton * * Creates a new #GtkButton containing a label. - * If characters in @label are preceded by an underscore, they are underlined - * indicating that they represent a keyboard accelerator called a mnemonic. + * If characters in @label are preceded by an underscore, they are underlined. + * If you need a literal underscore character in a label, use '__' (two + * underscores). The first underlined character represents a keyboard + * accelerator called a mnemonic. * Pressing Alt and that key activates the button. **/ GtkWidget* gtk_button_new_with_mnemonic (const gchar *label) { - return g_object_new (GTK_TYPE_BUTTON, "label", label, "use_underline", TRUE, NULL); + return g_object_new (GTK_TYPE_BUTTON, "label", label, "use-underline", TRUE, NULL); } void @@ -507,7 +1086,8 @@ gtk_button_pressed (GtkButton *button) { g_return_if_fail (GTK_IS_BUTTON (button)); - gtk_signal_emit (GTK_OBJECT (button), button_signals[PRESSED]); + + g_signal_emit (button, button_signals[PRESSED], 0); } void @@ -515,7 +1095,7 @@ gtk_button_released (GtkButton *button) { g_return_if_fail (GTK_IS_BUTTON (button)); - gtk_signal_emit (GTK_OBJECT (button), button_signals[RELEASED]); + g_signal_emit (button, button_signals[RELEASED], 0); } void @@ -523,7 +1103,7 @@ gtk_button_clicked (GtkButton *button) { g_return_if_fail (GTK_IS_BUTTON (button)); - gtk_signal_emit (GTK_OBJECT (button), button_signals[CLICKED]); + g_signal_emit (button, button_signals[CLICKED], 0); } void @@ -531,7 +1111,7 @@ gtk_button_enter (GtkButton *button) { g_return_if_fail (GTK_IS_BUTTON (button)); - gtk_signal_emit (GTK_OBJECT (button), button_signals[ENTER]); + g_signal_emit (button, button_signals[ENTER], 0); } void @@ -539,7 +1119,7 @@ gtk_button_leave (GtkButton *button) { g_return_if_fail (GTK_IS_BUTTON (button)); - gtk_signal_emit (GTK_OBJECT (button), button_signals[LEAVE]); + g_signal_emit (button, button_signals[LEAVE], 0); } void @@ -548,15 +1128,17 @@ gtk_button_set_relief (GtkButton *button, { g_return_if_fail (GTK_IS_BUTTON (button)); - button->relief = newrelief; - g_object_notify(G_OBJECT(button), "relief"); - gtk_widget_queue_draw (GTK_WIDGET (button)); + if (newrelief != button->relief) + { + button->relief = newrelief; + g_object_notify (G_OBJECT (button), "relief"); + gtk_widget_queue_draw (GTK_WIDGET (button)); + } } GtkReliefStyle gtk_button_get_relief (GtkButton *button) { - g_return_val_if_fail (button != NULL, GTK_RELIEF_NORMAL); g_return_val_if_fail (GTK_IS_BUTTON (button), GTK_RELIEF_NORMAL); return button->relief; @@ -570,8 +1152,6 @@ gtk_button_realize (GtkWidget *widget) gint attributes_mask; gint border_width; - g_return_if_fail (GTK_IS_BUTTON (widget)); - button = GTK_BUTTON (widget); GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); @@ -582,23 +1162,23 @@ gtk_button_realize (GtkWidget *widget) attributes.y = widget->allocation.y + border_width; attributes.width = widget->allocation.width - border_width * 2; attributes.height = widget->allocation.height - border_width * 2; - attributes.wclass = GDK_INPUT_OUTPUT; - attributes.visual = gtk_widget_get_visual (widget); - attributes.colormap = gtk_widget_get_colormap (widget); + attributes.wclass = GDK_INPUT_ONLY; attributes.event_mask = gtk_widget_get_events (widget); - attributes.event_mask |= (GDK_EXPOSURE_MASK | - GDK_BUTTON_PRESS_MASK | + attributes.event_mask |= (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); - attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; + attributes_mask = GDK_WA_X | GDK_WA_Y; - widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); - gdk_window_set_user_data (widget->window, button); + widget->window = gtk_widget_get_parent_window (widget); + g_object_ref (widget->window); + + button->event_window = gdk_window_new (gtk_widget_get_parent_window (widget), + &attributes, attributes_mask); + gdk_window_set_user_data (button->event_window, button); widget->style = gtk_style_attach (widget->style, widget->window); - gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); } static void @@ -608,14 +1188,80 @@ gtk_button_unrealize (GtkWidget *widget) if (button->activate_timeout) gtk_button_finish_activate (button, FALSE); + + if (button->event_window) + { + gdk_window_set_user_data (button->event_window, NULL); + gdk_window_destroy (button->event_window); + button->event_window = NULL; + } + + GTK_WIDGET_CLASS (gtk_button_parent_class)->unrealize (widget); +} + +static void +gtk_button_map (GtkWidget *widget) +{ + GtkButton *button = GTK_BUTTON (widget); + + GTK_WIDGET_CLASS (gtk_button_parent_class)->map (widget); + + if (button->event_window) + gdk_window_show (button->event_window); +} + +static void +gtk_button_unmap (GtkWidget *widget) +{ + GtkButton *button = GTK_BUTTON (widget); - GTK_WIDGET_CLASS (parent_class)->unrealize (widget); + if (button->event_window) + gdk_window_hide (button->event_window); + + GTK_WIDGET_CLASS (gtk_button_parent_class)->unmap (widget); +} + +static void +gtk_button_update_image_spacing (GtkButton *button) +{ + GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + GtkWidget *child; + gint spacing; + + /* Keep in sync with gtk_button_construct_child, + * we only want to update the spacing if the box + * was constructed there. + */ + if (!button->constructed || !priv->image) + return; + + child = GTK_BIN (button)->child; + if (GTK_IS_ALIGNMENT (child)) + { + child = GTK_BIN (child)->child; + if (GTK_IS_BOX (child)) + { + gtk_widget_style_get (GTK_WIDGET (button), + "image-spacing", &spacing, + NULL); + + gtk_box_set_spacing (GTK_BOX (child), spacing); + } + } +} + +static void +gtk_button_style_set (GtkWidget *widget, + GtkStyle *prev_style) +{ + gtk_button_update_image_spacing (GTK_BUTTON (widget)); } static void gtk_button_get_props (GtkButton *button, GtkBorder *default_border, GtkBorder *default_outside_border, + GtkBorder *inner_border, gboolean *interior_focus) { GtkWidget *widget = GTK_WIDGET (button); @@ -623,12 +1269,12 @@ gtk_button_get_props (GtkButton *button, if (default_border) { - gtk_widget_style_get (widget, "default_border", &tmp_border, NULL); + gtk_widget_style_get (widget, "default-border", &tmp_border, NULL); if (tmp_border) { *default_border = *tmp_border; - g_free (tmp_border); + gtk_border_free (tmp_border); } else *default_border = default_default_border; @@ -636,19 +1282,32 @@ gtk_button_get_props (GtkButton *button, if (default_outside_border) { - gtk_widget_style_get (widget, "default_outside_border", &tmp_border, NULL); + gtk_widget_style_get (widget, "default-outside-border", &tmp_border, NULL); if (tmp_border) { *default_outside_border = *tmp_border; - g_free (tmp_border); + gtk_border_free (tmp_border); } else *default_outside_border = default_default_outside_border; } + if (inner_border) + { + gtk_widget_style_get (widget, "inner-border", &tmp_border, NULL); + + if (tmp_border) + { + *inner_border = *tmp_border; + gtk_border_free (tmp_border); + } + else + *inner_border = default_inner_border; + } + if (interior_focus) - gtk_widget_style_get (widget, "interior_focus", interior_focus, NULL); + gtk_widget_style_get (widget, "interior-focus", interior_focus, NULL); } static void @@ -657,14 +1316,22 @@ gtk_button_size_request (GtkWidget *widget, { GtkButton *button = GTK_BUTTON (widget); GtkBorder default_border; - gboolean interior_focus; - - gtk_button_get_props (button, &default_border, NULL, &interior_focus); - - requisition->width = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING + - GTK_WIDGET (widget)->style->xthickness) * 2; - requisition->height = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING + - GTK_WIDGET (widget)->style->ythickness) * 2; + GtkBorder inner_border; + gint focus_width; + gint focus_pad; + + gtk_button_get_props (button, &default_border, NULL, &inner_border, NULL); + gtk_widget_style_get (GTK_WIDGET (widget), + "focus-line-width", &focus_width, + "focus-padding", &focus_pad, + NULL); + + requisition->width = ((GTK_CONTAINER (widget)->border_width + + GTK_WIDGET (widget)->style->xthickness) * 2 + + inner_border.left + inner_border.right); + requisition->height = ((GTK_CONTAINER (widget)->border_width + + GTK_WIDGET (widget)->style->ythickness) * 2 + + inner_border.top + inner_border.bottom); if (GTK_WIDGET_CAN_DEFAULT (widget)) { @@ -681,12 +1348,9 @@ gtk_button_size_request (GtkWidget *widget, requisition->width += child_requisition.width; requisition->height += child_requisition.height; } - - if (interior_focus) - { - requisition->width += 2; - requisition->height += 2; - } + + requisition->width += 2 * (focus_width + focus_pad); + requisition->height += 2 * (focus_width + focus_pad); } static void @@ -700,13 +1364,21 @@ gtk_button_size_allocate (GtkWidget *widget, gint xthickness = GTK_WIDGET (widget)->style->xthickness; gint ythickness = GTK_WIDGET (widget)->style->ythickness; GtkBorder default_border; - - gtk_button_get_props (button, &default_border, NULL, NULL); - + GtkBorder inner_border; + gint focus_width; + gint focus_pad; + + gtk_button_get_props (button, &default_border, NULL, &inner_border, NULL); + gtk_widget_style_get (GTK_WIDGET (widget), + "focus-line-width", &focus_width, + "focus-padding", &focus_pad, + NULL); + + widget->allocation = *allocation; if (GTK_WIDGET_REALIZED (widget)) - gdk_window_move_resize (widget->window, + gdk_window_move_resize (button->event_window, widget->allocation.x + border_width, widget->allocation.y + border_width, widget->allocation.width - border_width * 2, @@ -714,13 +1386,19 @@ gtk_button_size_allocate (GtkWidget *widget, if (GTK_BIN (button)->child && GTK_WIDGET_VISIBLE (GTK_BIN (button)->child)) { - child_allocation.x = (CHILD_SPACING + xthickness); - child_allocation.y = (CHILD_SPACING + ythickness); - - child_allocation.width = MAX (1, (gint)widget->allocation.width - child_allocation.x * 2 - - border_width * 2); - child_allocation.height = MAX (1, (gint)widget->allocation.height - child_allocation.y * 2 - - border_width * 2); + child_allocation.x = widget->allocation.x + border_width + inner_border.left + xthickness; + child_allocation.y = widget->allocation.y + border_width + inner_border.top + ythickness; + + child_allocation.width = MAX (1, widget->allocation.width - + xthickness * 2 - + inner_border.left - + inner_border.right - + border_width * 2); + child_allocation.height = MAX (1, widget->allocation.height - + ythickness * 2 - + inner_border.top - + inner_border.bottom - + border_width * 2); if (GTK_WIDGET_CAN_DEFAULT (button)) { @@ -730,14 +1408,22 @@ gtk_button_size_allocate (GtkWidget *widget, child_allocation.height = MAX (1, child_allocation.height - default_border.top - default_border.bottom); } + if (GTK_WIDGET_CAN_FOCUS (button)) + { + child_allocation.x += focus_width + focus_pad; + child_allocation.y += focus_width + focus_pad; + child_allocation.width = MAX (1, child_allocation.width - (focus_width + focus_pad) * 2); + child_allocation.height = MAX (1, child_allocation.height - (focus_width + focus_pad) * 2); + } + if (button->depressed) { gint child_displacement_x; gint child_displacement_y; gtk_widget_style_get (widget, - "child_displacement_x", &child_displacement_x, - "child_displacement_y", &child_displacement_y, + "child-displacement-x", &child_displacement_x, + "child-displacement-y", &child_displacement_y, NULL); child_allocation.x += child_displacement_x; child_allocation.y += child_displacement_y; @@ -747,57 +1433,39 @@ gtk_button_size_allocate (GtkWidget *widget, } } -/* - * +------------------------------------------------+ - * | BORDER | - * | +------------------------------------------+ | - * | |\\\\\\\\\\\\\\\\DEFAULT\\\\\\\\\\\\\\\\\ | | - * | |\\+------------------------------------+ | | - * | |\\| | SPACING 3 | | | | - * | |\\| +--------------------------------+ | | | - * | |\\| |########## FOCUS ###############| | | | - * | |\\| |#+----------------------------+#| | | | - * | |\\| |#| RELIEF \|#| | | | - * | |\\| |#| +-----------------------+\|#| | | | - * | |\\|1|#| + THE TEXT +\|#|2| | | - * | |\\| |#| +-----------------------+\|#| | | | - * | |\\| |#| \\\\\ ythickness \\\\\\\\\\|#| | | | - * | |\\| |#+----------------------------+#| | | | - * | |\\| |########### 1 ##################| | | | - * | |\\| +--------------------------------+ | | | - * | |\\| | default spacing 4 | | | | - * | |\\+------------------------------------+ | | - * | |\ ythickness | | - * | +------------------------------------------+ | - * | border_width | - * +------------------------------------------------+ - */ - -static void -gtk_button_paint (GtkWidget *widget, - GdkRectangle *area) +void +_gtk_button_paint (GtkButton *button, + const GdkRectangle *area, + GtkStateType state_type, + GtkShadowType shadow_type, + const gchar *main_detail, + const gchar *default_detail) { - GtkButton *button; - GtkShadowType shadow_type; + GtkWidget *widget; gint width, height; gint x, y; + gint border_width; GtkBorder default_border; GtkBorder default_outside_border; gboolean interior_focus; + gint focus_width; + gint focus_pad; - if (GTK_WIDGET_DRAWABLE (widget)) + if (GTK_WIDGET_DRAWABLE (button)) { - button = GTK_BUTTON (widget); - - gtk_button_get_props (button, &default_border, &default_outside_border, &interior_focus); + widget = GTK_WIDGET (button); + border_width = GTK_CONTAINER (widget)->border_width; + + gtk_button_get_props (button, &default_border, &default_outside_border, NULL, &interior_focus); + gtk_widget_style_get (GTK_WIDGET (widget), + "focus-line-width", &focus_width, + "focus-padding", &focus_pad, + NULL); - x = 0; - y = 0; - width = widget->allocation.width - GTK_CONTAINER (widget)->border_width * 2; - height = widget->allocation.height - GTK_CONTAINER (widget)->border_width * 2; - - gdk_window_set_back_pixmap (widget->window, NULL, TRUE); - gdk_window_clear_area (widget->window, area->x, area->y, area->width, area->height); + x = widget->allocation.x + border_width; + y = widget->allocation.y + border_width; + width = widget->allocation.width - border_width * 2; + height = widget->allocation.height - border_width * 2; if (GTK_WIDGET_HAS_DEFAULT (widget) && GTK_BUTTON (widget)->relief == GTK_RELIEF_NORMAL) @@ -822,42 +1490,55 @@ gtk_button_paint (GtkWidget *widget, if (!interior_focus && GTK_WIDGET_HAS_FOCUS (widget)) { - x += 1; - y += 1; - width -= 2; - height -= 2; + x += focus_width + focus_pad; + y += focus_width + focus_pad; + width -= 2 * (focus_width + focus_pad); + height -= 2 * (focus_width + focus_pad); } - shadow_type = button->depressed ? GTK_SHADOW_IN : GTK_SHADOW_OUT; - - if ((button->relief != GTK_RELIEF_NONE) || - ((GTK_WIDGET_STATE(widget) != GTK_STATE_NORMAL) && - (GTK_WIDGET_STATE(widget) != GTK_STATE_INSENSITIVE))) + if (button->relief != GTK_RELIEF_NONE || button->depressed || + GTK_WIDGET_STATE(widget) == GTK_STATE_PRELIGHT) gtk_paint_box (widget->style, widget->window, - GTK_WIDGET_STATE (widget), + state_type, shadow_type, area, widget, "button", x, y, width, height); if (GTK_WIDGET_HAS_FOCUS (widget)) { + gint child_displacement_x; + gint child_displacement_y; + gboolean displace_focus; + + gtk_widget_style_get (GTK_WIDGET (widget), + "child-displacement-y", &child_displacement_y, + "child-displacement-x", &child_displacement_x, + "displace-focus", &displace_focus, + NULL); + if (interior_focus) { - x += widget->style->xthickness + 1; - y += widget->style->ythickness + 1; - width -= 2 * (widget->style->xthickness + 1); - height -= 2 * (widget->style->xthickness + 1); + x += widget->style->xthickness + focus_pad; + y += widget->style->ythickness + focus_pad; + width -= 2 * (widget->style->xthickness + focus_pad); + height -= 2 * (widget->style->ythickness + focus_pad); } else { - x -= 1; - y -= 1; - width += 2; - height += 2; + x -= focus_width + focus_pad; + y -= focus_width + focus_pad; + width += 2 * (focus_width + focus_pad); + height += 2 * (focus_width + focus_pad); + } + + if (button->depressed && displace_focus) + { + x += child_displacement_x; + y += child_displacement_y; } - gtk_paint_focus (widget->style, widget->window, + gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget), area, widget, "button", - x, y, width - 1, height - 1); + x, y, width, height); } } } @@ -866,21 +1547,18 @@ static gboolean gtk_button_expose (GtkWidget *widget, GdkEventExpose *event) { - GtkBin *bin; - - g_return_val_if_fail (widget != NULL, FALSE); - g_return_val_if_fail (GTK_IS_BUTTON (widget), FALSE); - g_return_val_if_fail (event != NULL, FALSE); - if (GTK_WIDGET_DRAWABLE (widget)) { - bin = GTK_BIN (widget); + GtkButton *button = GTK_BUTTON (widget); - gtk_button_paint (widget, &event->area); - - (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event); + _gtk_button_paint (button, &event->area, + GTK_WIDGET_STATE (widget), + button->depressed ? GTK_SHADOW_IN : GTK_SHADOW_OUT, + "button", "buttondefault"); + + GTK_WIDGET_CLASS (gtk_button_parent_class)->expose_event (widget, event); } - + return FALSE; } @@ -890,15 +1568,11 @@ gtk_button_button_press (GtkWidget *widget, { GtkButton *button; - g_return_val_if_fail (widget != NULL, FALSE); - g_return_val_if_fail (GTK_IS_BUTTON (widget), FALSE); - g_return_val_if_fail (event != NULL, FALSE); - if (event->type == GDK_BUTTON_PRESS) { button = GTK_BUTTON (widget); - if (!GTK_WIDGET_HAS_FOCUS (widget)) + if (button->focus_on_click && !GTK_WIDGET_HAS_FOCUS (widget)) gtk_widget_grab_focus (widget); if (event->button == 1) @@ -914,10 +1588,6 @@ gtk_button_button_release (GtkWidget *widget, { GtkButton *button; - g_return_val_if_fail (widget != NULL, FALSE); - g_return_val_if_fail (GTK_IS_BUTTON (widget), FALSE); - g_return_val_if_fail (event != NULL, FALSE); - if (event->button == 1) { button = GTK_BUTTON (widget); @@ -927,6 +1597,29 @@ gtk_button_button_release (GtkWidget *widget, return TRUE; } +static gboolean +gtk_button_grab_broken (GtkWidget *widget, + GdkEventGrabBroken *event) +{ + GtkButton *button = GTK_BUTTON (widget); + gboolean save_in; + + /* Simulate a button release without the pointer in the button */ + if (button->button_down) + { + save_in = button->in_button; + button->in_button = FALSE; + gtk_button_released (button); + if (save_in != button->in_button) + { + button->in_button = save_in; + gtk_button_update_state (button); + } + } + + return TRUE; +} + static gboolean gtk_button_key_release (GtkWidget *widget, GdkEventKey *event) @@ -938,8 +1631,8 @@ gtk_button_key_release (GtkWidget *widget, gtk_button_finish_activate (button, TRUE); return TRUE; } - else if (GTK_WIDGET_CLASS (parent_class)->key_release_event) - return GTK_WIDGET_CLASS (parent_class)->key_release_event (widget, event); + else if (GTK_WIDGET_CLASS (gtk_button_parent_class)->key_release_event) + return GTK_WIDGET_CLASS (gtk_button_parent_class)->key_release_event (widget, event); else return FALSE; } @@ -951,10 +1644,6 @@ gtk_button_enter_notify (GtkWidget *widget, GtkButton *button; GtkWidget *event_widget; - g_return_val_if_fail (widget != NULL, FALSE); - g_return_val_if_fail (GTK_IS_BUTTON (widget), FALSE); - g_return_val_if_fail (event != NULL, FALSE); - button = GTK_BUTTON (widget); event_widget = gtk_get_event_widget ((GdkEvent*) event); @@ -975,15 +1664,12 @@ gtk_button_leave_notify (GtkWidget *widget, GtkButton *button; GtkWidget *event_widget; - g_return_val_if_fail (widget != NULL, FALSE); - g_return_val_if_fail (GTK_IS_BUTTON (widget), FALSE); - g_return_val_if_fail (event != NULL, FALSE); - button = GTK_BUTTON (widget); event_widget = gtk_get_event_widget ((GdkEvent*) event); if ((event_widget == widget) && - (event->detail != GDK_NOTIFY_INFERIOR)) + (event->detail != GDK_NOTIFY_INFERIOR) && + (GTK_WIDGET_SENSITIVE (event_widget))) { button->in_button = FALSE; gtk_button_leave (button); @@ -1019,15 +1705,20 @@ gtk_real_button_released (GtkButton *button) } } +static void +gtk_real_button_clicked (GtkButton *button) +{ + GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + + if (priv->action) + gtk_action_activate (priv->action); +} + static gboolean button_activate_timeout (gpointer data) { - GDK_THREADS_ENTER (); - gtk_button_finish_activate (data, TRUE); - GDK_THREADS_LEAVE (); - return FALSE; } @@ -1035,22 +1726,29 @@ static void gtk_real_button_activate (GtkButton *button) { GtkWidget *widget = GTK_WIDGET (button); - - g_return_if_fail (GTK_IS_BUTTON (button)); + GtkButtonPrivate *priv; + guint32 time; + + priv = GTK_BUTTON_GET_PRIVATE (button); if (GTK_WIDGET_REALIZED (button) && !button->activate_timeout) { - if (gdk_keyboard_grab (widget->window, TRUE, - gtk_get_current_event_time ()) == 0) + time = gtk_get_current_event_time (); + if (gdk_keyboard_grab (button->event_window, TRUE, time) == + GDK_GRAB_SUCCESS) { - gtk_grab_add (widget); - - button->activate_timeout = g_timeout_add (ACTIVATE_TIMEOUT, - button_activate_timeout, - button); - button->button_down = TRUE; - gtk_button_update_state (button); + priv->has_grab = TRUE; + priv->grab_time = time; } + + gtk_grab_add (widget); + + button->activate_timeout = gdk_threads_add_timeout (ACTIVATE_TIMEOUT, + button_activate_timeout, + button); + button->button_down = TRUE; + gtk_button_update_state (button); + gtk_widget_queue_draw (GTK_WIDGET (button)); } } @@ -1059,16 +1757,24 @@ gtk_button_finish_activate (GtkButton *button, gboolean do_it) { GtkWidget *widget = GTK_WIDGET (button); + GtkButtonPrivate *priv; + priv = GTK_BUTTON_GET_PRIVATE (button); + g_source_remove (button->activate_timeout); button->activate_timeout = 0; - gdk_keyboard_ungrab (gtk_get_current_event_time ()); + if (priv->has_grab) + { + gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), + priv->grab_time); + } gtk_grab_remove (widget); button->button_down = FALSE; gtk_button_update_state (button); + gtk_widget_queue_draw (GTK_WIDGET (button)); if (do_it) gtk_button_clicked (button); @@ -1089,10 +1795,13 @@ void gtk_button_set_label (GtkButton *button, const gchar *label) { - g_return_if_fail (GTK_IS_BUTTON (button)); + gchar *new_label; + g_return_if_fail (GTK_IS_BUTTON (button)); + + new_label = g_strdup (label); g_free (button->label_text); - button->label_text = g_strdup (label); + button->label_text = new_label; gtk_button_construct_child (button); @@ -1104,13 +1813,13 @@ gtk_button_set_label (GtkButton *button, * @button: a #GtkButton * * Fetches the text from the label of the button, as set by - * gtk_button_set_label(). + * gtk_button_set_label(). If the label text has not + * been set the return value will be %NULL. This will be the + * case if you create an empty button with gtk_button_new() to + * use as a container. * - * Return value: the text of the label widget. This string is - * owned by the widget and must not be modified or freed. - * If the label text has not been set the return value - * will be NULL. This will be the case if you create an - * empty button with gtk_button_new() to use as a container. + * Return value: The text of the label widget. This string is owned + * by the widget and must not be modified or freed. **/ G_CONST_RETURN gchar * gtk_button_get_label (GtkButton *button) @@ -1142,13 +1851,13 @@ gtk_button_set_use_underline (GtkButton *button, gtk_button_construct_child (button); - g_object_notify (G_OBJECT (button), "use_underline"); + g_object_notify (G_OBJECT (button), "use-underline"); } } /** * gtk_button_get_use_underline: - * @label: a #GtkButton + * @button: a #GtkButton * * Returns whether an embedded underline in the button label indicates a * mnemonic. See gtk_button_set_use_underline (). @@ -1169,7 +1878,7 @@ gtk_button_get_use_underline (GtkButton *button) * @button: a #GtkButton * @use_stock: %TRUE if the button should use a stock item * - * If true, the label set on the button is used as a + * If %TRUE, the label set on the button is used as a * stock id to select the stock item for the button. */ void @@ -1186,7 +1895,7 @@ gtk_button_set_use_stock (GtkButton *button, gtk_button_construct_child (button); - g_object_notify (G_OBJECT (button), "use_stock"); + g_object_notify (G_OBJECT (button), "use-stock"); } } @@ -1208,6 +1917,118 @@ gtk_button_get_use_stock (GtkButton *button) return button->use_stock; } +/** + * gtk_button_set_focus_on_click: + * @button: a #GtkButton + * @focus_on_click: whether the button grabs focus when clicked with the mouse + * + * Sets whether the button will grab focus when it is clicked with the mouse. + * Making mouse clicks not grab focus is useful in places like toolbars where + * you don't want the keyboard focus removed from the main area of the + * application. + * + * Since: 2.4 + **/ +void +gtk_button_set_focus_on_click (GtkButton *button, + gboolean focus_on_click) +{ + g_return_if_fail (GTK_IS_BUTTON (button)); + + focus_on_click = focus_on_click != FALSE; + + if (button->focus_on_click != focus_on_click) + { + button->focus_on_click = focus_on_click; + + g_object_notify (G_OBJECT (button), "focus-on-click"); + } +} + +/** + * gtk_button_get_focus_on_click: + * @button: a #GtkButton + * + * Returns whether the button grabs focus when it is clicked with the mouse. + * See gtk_button_set_focus_on_click(). + * + * Return value: %TRUE if the button grabs focus when it is clicked with + * the mouse. + * + * Since: 2.4 + **/ +gboolean +gtk_button_get_focus_on_click (GtkButton *button) +{ + g_return_val_if_fail (GTK_IS_BUTTON (button), FALSE); + + return button->focus_on_click; +} + +/** + * gtk_button_set_alignment: + * @button: a #GtkButton + * @xalign: the horizontal position of the child, 0.0 is left aligned, + * 1.0 is right aligned + * @yalign: the vertical position of the child, 0.0 is top aligned, + * 1.0 is bottom aligned + * + * Sets the alignment of the child. This property has no effect unless + * the child is a #GtkMisc or a #GtkAligment. + * + * Since: 2.4 + */ +void +gtk_button_set_alignment (GtkButton *button, + gfloat xalign, + gfloat yalign) +{ + GtkButtonPrivate *priv; + + g_return_if_fail (GTK_IS_BUTTON (button)); + + priv = GTK_BUTTON_GET_PRIVATE (button); + + priv->xalign = xalign; + priv->yalign = yalign; + priv->align_set = 1; + + maybe_set_alignment (button, GTK_BIN (button)->child); + + g_object_freeze_notify (G_OBJECT (button)); + g_object_notify (G_OBJECT (button), "xalign"); + g_object_notify (G_OBJECT (button), "yalign"); + g_object_thaw_notify (G_OBJECT (button)); +} + +/** + * gtk_button_get_alignment: + * @button: a #GtkButton + * @xalign: return location for horizontal alignment + * @yalign: return location for vertical alignment + * + * Gets the alignment of the child in the button. + * + * Since: 2.4 + */ +void +gtk_button_get_alignment (GtkButton *button, + gfloat *xalign, + gfloat *yalign) +{ + GtkButtonPrivate *priv; + + g_return_if_fail (GTK_IS_BUTTON (button)); + + priv = GTK_BUTTON_GET_PRIVATE (button); + + if (xalign) + *xalign = priv->xalign; + + if (yalign) + *yalign = priv->yalign; +} + /** * _gtk_button_set_depressed: * @button: a #GtkButton @@ -1238,13 +2059,234 @@ gtk_button_update_state (GtkButton *button) gboolean depressed; GtkStateType new_state; - depressed = button->button_down && (button->in_button || button->activate_timeout); + if (button->activate_timeout) + depressed = button->depress_on_activate; + else + depressed = button->in_button && button->button_down; - if (!button->button_down && button->in_button) + if (button->in_button && (!button->button_down || !depressed)) new_state = GTK_STATE_PRELIGHT; else - new_state = depressed ? GTK_STATE_ACTIVE: GTK_STATE_NORMAL; + new_state = depressed ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL; _gtk_button_set_depressed (button, depressed); gtk_widget_set_state (GTK_WIDGET (button), new_state); } + +static void +show_image_change_notify (GtkButton *button) +{ + GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + + if (priv->image) + { + if (show_image (button)) + gtk_widget_show (priv->image); + else + gtk_widget_hide (priv->image); + } +} + +static void +traverse_container (GtkWidget *widget, + gpointer data) +{ + if (GTK_IS_BUTTON (widget)) + show_image_change_notify (GTK_BUTTON (widget)); + else if (GTK_IS_CONTAINER (widget)) + gtk_container_forall (GTK_CONTAINER (widget), traverse_container, NULL); +} + +static void +gtk_button_setting_changed (GtkSettings *settings) +{ + GList *list, *l; + + list = gtk_window_list_toplevels (); + + for (l = list; l; l = l->next) + gtk_container_forall (GTK_CONTAINER (l->data), + traverse_container, NULL); + + g_list_free (list); +} + + +static void +gtk_button_screen_changed (GtkWidget *widget, + GdkScreen *previous_screen) +{ + GtkSettings *settings; + guint show_image_connection; + + if (!gtk_widget_has_screen (widget)) + return; + + settings = gtk_widget_get_settings (widget); + + show_image_connection = + GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (settings), + "gtk-button-connection")); + + if (show_image_connection) + return; + + show_image_connection = + g_signal_connect (settings, "notify::gtk-button-images", + G_CALLBACK (gtk_button_setting_changed), NULL); + g_object_set_data (G_OBJECT (settings), + I_("gtk-button-connection"), + GUINT_TO_POINTER (show_image_connection)); + + show_image_change_notify (GTK_BUTTON (widget)); +} + +static void +gtk_button_state_changed (GtkWidget *widget, + GtkStateType previous_state) +{ + GtkButton *button = GTK_BUTTON (widget); + + if (!GTK_WIDGET_IS_SENSITIVE (widget)) + { + button->in_button = FALSE; + gtk_real_button_released (button); + } +} + +static void +gtk_button_grab_notify (GtkWidget *widget, + gboolean was_grabbed) +{ + GtkButton *button = GTK_BUTTON (widget); + gboolean save_in; + + if (!was_grabbed) + { + save_in = button->in_button; + button->in_button = FALSE; + gtk_real_button_released (button); + if (save_in != button->in_button) + { + button->in_button = save_in; + gtk_button_update_state (button); + } + } +} + +/** + * gtk_button_set_image: + * @button: a #GtkButton + * @image: a widget to set as the image for the button + * + * Set the image of @button to the given widget. Note that + * it depends on the #GtkSettings:gtk-button-images setting whether the + * image will be displayed or not, you don't have to call + * gtk_widget_show() on @image yourself. + * + * Since: 2.6 + */ +void +gtk_button_set_image (GtkButton *button, + GtkWidget *image) +{ + GtkButtonPrivate *priv; + + g_return_if_fail (GTK_IS_BUTTON (button)); + g_return_if_fail (image == NULL || GTK_IS_WIDGET (image)); + + priv = GTK_BUTTON_GET_PRIVATE (button); + + if (priv->image && priv->image->parent) + gtk_container_remove (GTK_CONTAINER (priv->image->parent), priv->image); + + priv->image = image; + priv->image_is_stock = (image == NULL); + + gtk_button_construct_child (button); + + g_object_notify (G_OBJECT (button), "image"); +} + +/** + * gtk_button_get_image: + * @button: a #GtkButton + * + * Gets the widget that is currenty set as the image of @button. + * This may have been explicitly set by gtk_button_set_image() + * or constructed by gtk_button_new_from_stock(). + * + * Return value: a #GtkWidget or %NULL in case there is no image + * + * Since: 2.6 + */ +GtkWidget * +gtk_button_get_image (GtkButton *button) +{ + GtkButtonPrivate *priv; + + g_return_val_if_fail (GTK_IS_BUTTON (button), NULL); + + priv = GTK_BUTTON_GET_PRIVATE (button); + + return priv->image; +} + +/** + * gtk_button_set_image_position: + * @button: a #GtkButton + * @position: the position + * + * Sets the position of the image relative to the text + * inside the button. + * + * Since: 2.10 + */ +void +gtk_button_set_image_position (GtkButton *button, + GtkPositionType position) +{ + + GtkButtonPrivate *priv; + + g_return_if_fail (GTK_IS_BUTTON (button)); + g_return_if_fail (position >= GTK_POS_LEFT && position <= GTK_POS_BOTTOM); + + priv = GTK_BUTTON_GET_PRIVATE (button); + + if (priv->image_position != position) + { + priv->image_position = position; + + gtk_button_construct_child (button); + + g_object_notify (G_OBJECT (button), "image-position"); + } +} + +/** + * gtk_button_get_image_position: + * @button: a #GtkButton + * + * Gets the position of the image relative to the text + * inside the button. + * + * Return value: the position + * + * Since: 2.10 + */ +GtkPositionType +gtk_button_get_image_position (GtkButton *button) +{ + GtkButtonPrivate *priv; + + g_return_val_if_fail (GTK_IS_BUTTON (button), GTK_POS_LEFT); + + priv = GTK_BUTTON_GET_PRIVATE (button); + + return priv->image_position; +} + + +#define __GTK_BUTTON_C__ +#include "gtkaliasdef.c"