X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkbutton.c;h=3ac36a71b8d37ddeccc5ee2b95a641466345d1eb;hb=a89d420270d1a856e072ed87c365b0176f102e6c;hp=ab3fe77f023b75ee6862c016348eb48c61a2a7e9;hpb=8885320d211aa55bcfa63e43ee4583b5533364ce;p=~andy%2Fgtk diff --git a/gtk/gtkbutton.c b/gtk/gtkbutton.c index ab3fe77f0..3ac36a71b 100644 --- a/gtk/gtkbutton.c +++ b/gtk/gtkbutton.c @@ -12,9 +12,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ /* @@ -26,45 +24,50 @@ /** * SECTION:gtkbutton - * @Short_description: A widget that creates a signal when clicked on + * @Short_description: A widget that emits a signal when clicked on * @Title: GtkButton * - * The #GtkButton widget is generally used to attach a function to that is + * The #GtkButton widget is generally used to trigger a callback function that is * called when the button is pressed. The various signals and how to use them * are outlined below. * - * The #GtkButton widget can hold any valid child widget. That is it can hold - * most any other standard #GtkWidget. The most commonly used child is the + * The #GtkButton widget can hold any valid child widget. That is, it can hold + * almost any other standard #GtkWidget. The most commonly used child is the * #GtkLabel. */ #include "config.h" + +#include "gtkbutton.h" +#include "gtkbuttonprivate.h" + #include #include "gtkalignment.h" -#include "gtkbutton.h" #include "gtklabel.h" #include "gtkmain.h" #include "gtkmarshalers.h" #include "gtkimage.h" -#include "gtkhbox.h" -#include "gtkvbox.h" +#include "gtkbox.h" #include "gtkstock.h" #include "gtkiconfactory.h" #include "gtkactivatable.h" #include "gtksizerequest.h" +#include "gtktypebuiltins.h" #include "gtkprivate.h" #include "gtkintl.h" - +#include "a11y/gtkbuttonaccessible.h" +#include "gtkapplicationprivate.h" +#include "gtkactionhelper.h" 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 }; /* Time out before giving up on getting a key release when animating * the close button. */ #define ACTIVATE_TIMEOUT 250 + enum { PRESSED, RELEASED, @@ -86,28 +89,15 @@ enum { PROP_XALIGN, PROP_YALIGN, PROP_IMAGE_POSITION, + PROP_ACTION_NAME, + PROP_ACTION_TARGET, + PROP_ALWAYS_SHOW_IMAGE, /* activatable properties */ PROP_ACTIVATABLE_RELATED_ACTION, PROP_ACTIVATABLE_USE_ACTION_APPEARANCE }; -#define GTK_BUTTON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_BUTTON, GtkButtonPrivate)) -typedef struct _GtkButtonPrivate GtkButtonPrivate; - -struct _GtkButtonPrivate -{ - gfloat xalign; - gfloat yalign; - GtkWidget *image; - guint align_set : 1; - guint image_is_stock : 1; - guint use_action_appearance : 1; - guint32 grab_time; - GdkDevice *grab_keyboard; - GtkPositionType image_position; - GtkAction *action; -}; static void gtk_button_destroy (GtkWidget *widget); static void gtk_button_dispose (GObject *object); @@ -125,7 +115,7 @@ 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_style_updated (GtkWidget * widget); static void gtk_button_size_allocate (GtkWidget * widget, GtkAllocation * allocation); static gint gtk_button_draw (GtkWidget * widget, cairo_t *cr); @@ -133,6 +123,8 @@ static gint gtk_button_button_press (GtkWidget * widget, GdkEventButton * event); static gint gtk_button_button_release (GtkWidget * widget, GdkEventButton * event); +static gboolean gtk_button_touch (GtkWidget *widget, + GdkEventTouch *event); static gint gtk_button_grab_broken (GtkWidget * widget, GdkEventGrabBroken * event); static gint gtk_button_key_release (GtkWidget * widget, GdkEventKey * event); @@ -160,7 +152,7 @@ static void gtk_button_state_changed (GtkWidget *widget, static void gtk_button_grab_notify (GtkWidget *widget, gboolean was_grabbed); - +static void gtk_button_actionable_iface_init (GtkActionableInterface *iface); static void gtk_button_activatable_interface_init(GtkActivatableIface *iface); static void gtk_button_update (GtkActivatable *activatable, GtkAction *action, @@ -182,6 +174,7 @@ static void gtk_button_get_preferred_height (GtkWidget *widget, static guint button_signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE_WITH_CODE (GtkButton, gtk_button, GTK_TYPE_BIN, + G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIONABLE, gtk_button_actionable_iface_init) G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE, gtk_button_activatable_interface_init)) @@ -209,11 +202,12 @@ gtk_button_class_init (GtkButtonClass *klass) 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->style_updated = gtk_button_style_updated; widget_class->size_allocate = gtk_button_size_allocate; widget_class->draw = gtk_button_draw; widget_class->button_press_event = gtk_button_button_press; widget_class->button_release_event = gtk_button_button_release; + widget_class->touch_event = gtk_button_touch; 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; @@ -277,7 +271,7 @@ gtk_button_class_init (GtkButtonClass *klass) * 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, + * can be used to control its horizontal alignment. 0.0 is left aligned, * 1.0 is right aligned. * * Since: 2.4 @@ -296,7 +290,7 @@ gtk_button_class_init (GtkButtonClass *klass) * 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, + * can be used to control its vertical alignment. 0.0 is top aligned, * 1.0 is bottom aligned. * * Since: 2.4 @@ -312,7 +306,7 @@ gtk_button_class_init (GtkButtonClass *klass) GTK_PARAM_READWRITE)); /** - * GtkButton::image: + * GtkButton:image: * * The child widget to appear next to the button text. * @@ -342,6 +336,28 @@ gtk_button_class_init (GtkButtonClass *klass) GTK_POS_LEFT, GTK_PARAM_READWRITE)); + /** + * GtkButton:always-show-image: + * + * If %TRUE, the button will ignore the #GtkSettings:gtk-button-images + * setting and always show the image, if available. + * + * Use this property if the button would be useless or hard to use + * without the image. + * + * Since: 3.6 + */ + g_object_class_install_property (gobject_class, + PROP_ALWAYS_SHOW_IMAGE, + g_param_spec_boolean ("always-show-image", + P_("Always show image"), + P_("Whether the image will always be shown"), + FALSE, + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_object_class_override_property (gobject_class, PROP_ACTION_NAME, "action-name"); + g_object_class_override_property (gobject_class, PROP_ACTION_TARGET, "action-target"); + 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"); @@ -514,6 +530,9 @@ gtk_button_class_init (GtkButtonClass *klass) * Sets the border between the button edges and child. * * Since: 2.10 + * + * Deprecated: 3.4: Use the standard border and padding CSS properties; + * the value of this style property is ignored. */ gtk_widget_class_install_style_property (widget_class, g_param_spec_boxed ("inner-border", @@ -539,28 +558,36 @@ gtk_button_class_init (GtkButtonClass *klass) GTK_PARAM_READABLE)); g_type_class_add_private (gobject_class, sizeof (GtkButtonPrivate)); + + gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_BUTTON_ACCESSIBLE); } static void gtk_button_init (GtkButton *button) { - GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + GtkButtonPrivate *priv; + GtkStyleContext *context; + + button->priv = G_TYPE_INSTANCE_GET_PRIVATE (button, + GTK_TYPE_BUTTON, + GtkButtonPrivate); + priv = button->priv; gtk_widget_set_can_focus (GTK_WIDGET (button), TRUE); gtk_widget_set_receives_default (GTK_WIDGET (button), TRUE); gtk_widget_set_has_window (GTK_WIDGET (button), FALSE); - button->label_text = NULL; - - button->constructed = FALSE; - button->in_button = FALSE; - button->button_down = FALSE; - button->relief = GTK_RELIEF_NORMAL; - button->use_stock = FALSE; - button->use_underline = FALSE; - button->depressed = FALSE; - button->depress_on_activate = TRUE; - button->focus_on_click = TRUE; + priv->label_text = NULL; + + priv->constructed = FALSE; + priv->in_button = FALSE; + priv->button_down = FALSE; + priv->relief = GTK_RELIEF_NORMAL; + priv->use_stock = FALSE; + priv->use_underline = FALSE; + priv->depressed = FALSE; + priv->depress_on_activate = TRUE; + priv->focus_on_click = TRUE; priv->xalign = 0.5; priv->yalign = 0.5; @@ -568,17 +595,21 @@ gtk_button_init (GtkButton *button) priv->image_is_stock = TRUE; priv->image_position = GTK_POS_LEFT; priv->use_action_appearance = TRUE; + + context = gtk_widget_get_style_context (GTK_WIDGET (button)); + gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON); } static void gtk_button_destroy (GtkWidget *widget) { GtkButton *button = GTK_BUTTON (widget); + GtkButtonPrivate *priv = button->priv; - if (button->label_text) + if (priv->label_text) { - g_free (button->label_text); - button->label_text = NULL; + g_free (priv->label_text); + priv->label_text = NULL; } GTK_WIDGET_CLASS (gtk_button_parent_class)->destroy (widget); @@ -591,15 +622,18 @@ gtk_button_constructor (GType type, { GObject *object; GtkButton *button; + GtkButtonPrivate *priv; object = G_OBJECT_CLASS (gtk_button_parent_class)->constructor (type, n_construct_properties, construct_params); button = GTK_BUTTON (object); - button->constructed = TRUE; + priv = button->priv; + + priv->constructed = TRUE; - if (button->label_text != NULL) + if (priv->label_text != NULL) gtk_button_construct_child (button); return object; @@ -619,7 +653,7 @@ static void maybe_set_alignment (GtkButton *button, GtkWidget *widget) { - GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + GtkButtonPrivate *priv = button->priv; if (GTK_IS_MISC (widget)) { @@ -658,7 +692,9 @@ static void gtk_button_dispose (GObject *object) { GtkButton *button = GTK_BUTTON (object); - GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + GtkButtonPrivate *priv = button->priv; + + g_clear_object (&priv->action_helper); if (priv->action) { @@ -668,6 +704,36 @@ gtk_button_dispose (GObject *object) G_OBJECT_CLASS (gtk_button_parent_class)->dispose (object); } +static void +gtk_button_set_action_name (GtkActionable *actionable, + const gchar *action_name) +{ + GtkButton *button = GTK_BUTTON (actionable); + + g_return_if_fail (button->priv->action == NULL); + + if (!button->priv->action_helper) + button->priv->action_helper = gtk_action_helper_new (actionable); + + g_signal_handlers_disconnect_by_func (button, gtk_real_button_clicked, NULL); + if (action_name) + g_signal_connect_after (button, "clicked", G_CALLBACK (gtk_real_button_clicked), NULL); + + gtk_action_helper_set_action_name (button->priv->action_helper, action_name); +} + +static void +gtk_button_set_action_target_value (GtkActionable *actionable, + GVariant *action_target) +{ + GtkButton *button = GTK_BUTTON (actionable); + + if (!button->priv->action_helper) + button->priv->action_helper = gtk_action_helper_new (actionable); + + gtk_action_helper_set_action_target_value (button->priv->action_helper, action_target); +} + static void gtk_button_set_property (GObject *object, guint prop_id, @@ -675,7 +741,7 @@ gtk_button_set_property (GObject *object, GParamSpec *pspec) { GtkButton *button = GTK_BUTTON (object); - GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + GtkButtonPrivate *priv = button->priv; switch (prop_id) { @@ -685,6 +751,9 @@ gtk_button_set_property (GObject *object, case PROP_IMAGE: gtk_button_set_image (button, (GtkWidget *) g_value_get_object (value)); break; + case PROP_ALWAYS_SHOW_IMAGE: + gtk_button_set_always_show_image (button, g_value_get_boolean (value)); + break; case PROP_RELIEF: gtk_button_set_relief (button, g_value_get_enum (value)); break; @@ -712,6 +781,12 @@ gtk_button_set_property (GObject *object, case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE: gtk_button_set_use_action_appearance (button, g_value_get_boolean (value)); break; + case PROP_ACTION_NAME: + gtk_button_set_action_name (GTK_ACTIONABLE (button), g_value_get_string (value)); + break; + case PROP_ACTION_TARGET: + gtk_button_set_action_target_value (GTK_ACTIONABLE (button), g_value_get_variant (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -725,27 +800,30 @@ gtk_button_get_property (GObject *object, GParamSpec *pspec) { GtkButton *button = GTK_BUTTON (object); - GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + GtkButtonPrivate *priv = button->priv; switch (prop_id) { case PROP_LABEL: - g_value_set_string (value, button->label_text); + g_value_set_string (value, priv->label_text); break; case PROP_IMAGE: g_value_set_object (value, (GObject *)priv->image); break; + case PROP_ALWAYS_SHOW_IMAGE: + g_value_set_boolean (value, gtk_button_get_always_show_image (button)); + break; case PROP_RELIEF: - g_value_set_enum (value, gtk_button_get_relief (button)); + g_value_set_enum (value, priv->relief); break; case PROP_USE_UNDERLINE: - g_value_set_boolean (value, button->use_underline); + g_value_set_boolean (value, priv->use_underline); break; case PROP_USE_STOCK: - g_value_set_boolean (value, button->use_stock); + g_value_set_boolean (value, priv->use_stock); break; case PROP_FOCUS_ON_CLICK: - g_value_set_boolean (value, button->focus_on_click); + g_value_set_boolean (value, priv->focus_on_click); break; case PROP_XALIGN: g_value_set_float (value, priv->xalign); @@ -762,12 +840,43 @@ gtk_button_get_property (GObject *object, case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE: g_value_set_boolean (value, priv->use_action_appearance); break; + case PROP_ACTION_NAME: + g_value_set_string (value, gtk_action_helper_get_action_name (priv->action_helper)); + break; + case PROP_ACTION_TARGET: + g_value_set_variant (value, gtk_action_helper_get_action_target_value (priv->action_helper)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } +static const gchar * +gtk_button_get_action_name (GtkActionable *actionable) +{ + GtkButton *button = GTK_BUTTON (actionable); + + return gtk_action_helper_get_action_name (button->priv->action_helper); +} + +static GVariant * +gtk_button_get_action_target_value (GtkActionable *actionable) +{ + GtkButton *button = GTK_BUTTON (actionable); + + return gtk_action_helper_get_action_target_value (button->priv->action_helper); +} + +static void +gtk_button_actionable_iface_init (GtkActionableInterface *iface) +{ + iface->get_action_name = gtk_button_get_action_name; + iface->set_action_name = gtk_button_set_action_name; + iface->get_action_target_value = gtk_button_get_action_target_value; + iface->set_action_target_value = gtk_button_set_action_target_value; +} + static void gtk_button_activatable_interface_init (GtkActivatableIface *iface) { @@ -844,7 +953,8 @@ gtk_button_update (GtkActivatable *activatable, GtkAction *action, const gchar *property_name) { - GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (activatable); + GtkButton *button = GTK_BUTTON (activatable); + GtkButtonPrivate *priv = button->priv; if (strcmp (property_name, "visible") == 0) { @@ -873,7 +983,8 @@ static void gtk_button_sync_action_properties (GtkActivatable *activatable, GtkAction *action) { - GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (activatable); + GtkButton *button = GTK_BUTTON (activatable); + GtkButtonPrivate *priv = button->priv; if (!action) return; @@ -892,13 +1003,18 @@ gtk_button_sync_action_properties (GtkActivatable *activatable, activatable_update_gicon (GTK_BUTTON (activatable), action); activatable_update_icon_name (GTK_BUTTON (activatable), action); } + + gtk_button_set_always_show_image (button, + gtk_action_get_always_show_image (action)); } static void gtk_button_set_related_action (GtkButton *button, GtkAction *action) { - GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + GtkButtonPrivate *priv = button->priv; + + g_return_if_fail (gtk_action_helper_get_action_name (button->priv->action_helper) == NULL); if (priv->action == action) return; @@ -921,7 +1037,7 @@ static void gtk_button_set_use_action_appearance (GtkButton *button, gboolean use_appearance) { - GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + GtkButtonPrivate *priv = button->priv; if (priv->use_action_appearance != use_appearance) { @@ -948,9 +1064,10 @@ gtk_button_new (void) static gboolean show_image (GtkButton *button) { + GtkButtonPrivate *priv = button->priv; gboolean show; - - if (button->label_text) + + if (priv->label_text && !priv->always_show_image) { GtkSettings *settings; @@ -963,10 +1080,12 @@ show_image (GtkButton *button) return show; } + static void gtk_button_construct_child (GtkButton *button) { - GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + GtkButtonPrivate *priv = button->priv; + GtkStyleContext *context; GtkStockItem item; GtkWidget *child; GtkWidget *label; @@ -976,15 +1095,17 @@ gtk_button_construct_child (GtkButton *button) gchar *label_text = NULL; gint image_spacing; - if (!button->constructed) + if (!priv->constructed) return; - if (!button->label_text && !priv->image) + if (!priv->label_text && !priv->image) return; - gtk_widget_style_get (GTK_WIDGET (button), - "image-spacing", &image_spacing, - NULL); + context = gtk_widget_get_style_context (GTK_WIDGET (button)); + + gtk_style_context_get_style (context, + "image-spacing", &image_spacing, + NULL); if (priv->image && !priv->image_is_stock) { @@ -1003,17 +1124,17 @@ gtk_button_construct_child (GtkButton *button) if (child) gtk_container_remove (GTK_CONTAINER (button), child); - if (button->use_stock && - button->label_text && - gtk_stock_lookup (button->label_text, &item)) + if (priv->use_stock && + priv->label_text && + gtk_stock_lookup (priv->label_text, &item)) { if (!image) - image = g_object_ref (gtk_image_new_from_stock (button->label_text, GTK_ICON_SIZE_BUTTON)); + image = g_object_ref (gtk_image_new_from_stock (priv->label_text, GTK_ICON_SIZE_BUTTON)); label_text = item.label; } else - label_text = button->label_text; + label_text = priv->label_text; if (image) { @@ -1025,9 +1146,9 @@ gtk_button_construct_child (GtkButton *button) if (priv->image_position == GTK_POS_LEFT || priv->image_position == GTK_POS_RIGHT) - box = gtk_hbox_new (FALSE, image_spacing); + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, image_spacing); else - box = gtk_vbox_new (FALSE, image_spacing); + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, image_spacing); if (priv->align_set) align = gtk_alignment_new (priv->xalign, priv->yalign, 0.0, 0.0); @@ -1042,7 +1163,7 @@ gtk_button_construct_child (GtkButton *button) if (label_text) { - if (button->use_underline || button->use_stock) + if (priv->use_underline || priv->use_stock) { label = gtk_label_new_with_mnemonic (label_text); gtk_label_set_mnemonic_widget (GTK_LABEL (label), @@ -1067,13 +1188,13 @@ gtk_button_construct_child (GtkButton *button) return; } - if (button->use_underline || button->use_stock) + if (priv->use_underline || priv->use_stock) { - label = gtk_label_new_with_mnemonic (button->label_text); + label = gtk_label_new_with_mnemonic (priv->label_text); gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button)); } else - label = gtk_label_new (button->label_text); + label = gtk_label_new (priv->label_text); if (priv->align_set) gtk_misc_set_alignment (GTK_MISC (label), priv->xalign, priv->yalign); @@ -1125,14 +1246,15 @@ gtk_button_new_from_stock (const gchar *stock_id) * gtk_button_new_with_mnemonic: * @label: The text of the button, with an underscore in front of the * mnemonic character - * @returns: a new #GtkButton * * Creates a new #GtkButton containing a label. * 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 + * 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. + * + * Returns: a new #GtkButton **/ GtkWidget* gtk_button_new_with_mnemonic (const gchar *label) @@ -1153,7 +1275,6 @@ gtk_button_pressed (GtkButton *button) { g_return_if_fail (GTK_IS_BUTTON (button)); - g_signal_emit (button, button_signals[PRESSED], 0); } @@ -1234,11 +1355,15 @@ void gtk_button_set_relief (GtkButton *button, GtkReliefStyle newrelief) { + GtkButtonPrivate *priv; + g_return_if_fail (GTK_IS_BUTTON (button)); - if (newrelief != button->relief) + priv = button->priv; + + if (newrelief != priv->relief) { - button->relief = newrelief; + priv->relief = newrelief; g_object_notify (G_OBJECT (button), "relief"); gtk_widget_queue_draw (GTK_WIDGET (button)); } @@ -1257,13 +1382,14 @@ gtk_button_get_relief (GtkButton *button) { g_return_val_if_fail (GTK_IS_BUTTON (button), GTK_RELIEF_NORMAL); - return button->relief; + return button->priv->relief; } static void gtk_button_realize (GtkWidget *widget) { GtkButton *button = GTK_BUTTON (widget); + GtkButtonPrivate *priv = button->priv; GtkAllocation allocation; GdkWindow *window; GdkWindowAttr attributes; @@ -1281,9 +1407,10 @@ gtk_button_realize (GtkWidget *widget) attributes.wclass = GDK_INPUT_ONLY; attributes.event_mask = gtk_widget_get_events (widget); attributes.event_mask |= (GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_ENTER_NOTIFY_MASK | - GDK_LEAVE_NOTIFY_MASK); + GDK_BUTTON_RELEASE_MASK | + GDK_TOUCH_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK); attributes_mask = GDK_WA_X | GDK_WA_Y; @@ -1291,28 +1418,27 @@ gtk_button_realize (GtkWidget *widget) gtk_widget_set_window (widget, window); g_object_ref (window); - button->event_window = gdk_window_new (window, - &attributes, attributes_mask); - gdk_window_set_user_data (button->event_window, button); - - gtk_widget_style_attach (widget); + priv->event_window = gdk_window_new (window, + &attributes, attributes_mask); + gtk_widget_register_window (widget, priv->event_window); } static void gtk_button_unrealize (GtkWidget *widget) { GtkButton *button = GTK_BUTTON (widget); + GtkButtonPrivate *priv = button->priv; - if (button->activate_timeout) + if (priv->activate_timeout) gtk_button_finish_activate (button, FALSE); - if (button->event_window) + if (priv->event_window) { - gdk_window_set_user_data (button->event_window, NULL); - gdk_window_destroy (button->event_window); - button->event_window = NULL; + gtk_widget_unregister_window (widget, priv->event_window); + gdk_window_destroy (priv->event_window); + priv->event_window = NULL; } - + GTK_WIDGET_CLASS (gtk_button_parent_class)->unrealize (widget); } @@ -1320,28 +1446,34 @@ static void gtk_button_map (GtkWidget *widget) { GtkButton *button = GTK_BUTTON (widget); - + GtkButtonPrivate *priv = button->priv; + GTK_WIDGET_CLASS (gtk_button_parent_class)->map (widget); - if (button->event_window) - gdk_window_show (button->event_window); + if (priv->event_window) + gdk_window_show (priv->event_window); } static void gtk_button_unmap (GtkWidget *widget) { GtkButton *button = GTK_BUTTON (widget); - - if (button->event_window) - gdk_window_hide (button->event_window); + GtkButtonPrivate *priv = button->priv; + + if (priv->event_window) + { + gdk_window_hide (priv->event_window); + priv->in_button = FALSE; + } GTK_WIDGET_CLASS (gtk_button_parent_class)->unmap (widget); } static void -gtk_button_update_image_spacing (GtkButton *button) +gtk_button_update_image_spacing (GtkButton *button, + GtkStyleContext *context) { - GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + GtkButtonPrivate *priv = button->priv; GtkWidget *child; gint spacing; @@ -1349,7 +1481,7 @@ gtk_button_update_image_spacing (GtkButton *button) * we only want to update the spacing if the box * was constructed there. */ - if (!button->constructed || !priv->image) + if (!priv->constructed || !priv->image) return; child = gtk_bin_get_child (GTK_BIN (button)); @@ -1358,35 +1490,47 @@ gtk_button_update_image_spacing (GtkButton *button) child = gtk_bin_get_child (GTK_BIN (child)); if (GTK_IS_BOX (child)) { - gtk_widget_style_get (GTK_WIDGET (button), - "image-spacing", &spacing, - NULL); + gtk_style_context_get_style (context, + "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_style_updated (GtkWidget *widget) { - gtk_button_update_image_spacing (GTK_BUTTON (widget)); + GtkStyleContext *context; + + GTK_WIDGET_CLASS (gtk_button_parent_class)->style_updated (widget); + + context = gtk_widget_get_style_context (widget); + + gtk_button_update_image_spacing (GTK_BUTTON (widget), context); } static void gtk_button_get_props (GtkButton *button, GtkBorder *default_border, GtkBorder *default_outside_border, - GtkBorder *inner_border, + GtkBorder *padding, + GtkBorder *border, gboolean *interior_focus) { - GtkWidget *widget = GTK_WIDGET (button); + GtkStyleContext *context; + GtkStateFlags state; GtkBorder *tmp_border; + context = gtk_widget_get_style_context (GTK_WIDGET (button)); + state = gtk_style_context_get_state (context); + if (default_border) { - gtk_widget_style_get (widget, "default-border", &tmp_border, NULL); + gtk_style_context_get_style (context, + "default-border", &tmp_border, + NULL); if (tmp_border) { @@ -1399,7 +1543,9 @@ gtk_button_get_props (GtkButton *button, if (default_outside_border) { - gtk_widget_style_get (widget, "default-outside-border", &tmp_border, NULL); + gtk_style_context_get_style (context, + "default-outside-border", &tmp_border, + NULL); if (tmp_border) { @@ -1410,21 +1556,18 @@ gtk_button_get_props (GtkButton *button, *default_outside_border = default_default_outside_border; } - if (inner_border) + if (interior_focus) { - 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; + gtk_style_context_get_style (context, + "interior-focus", interior_focus, + NULL); } - if (interior_focus) - gtk_widget_style_get (widget, "interior-focus", interior_focus, NULL); + if (padding) + gtk_style_context_get_padding (context, state, padding); + + if (border) + gtk_style_context_get_border (context, state, border); } static void @@ -1432,30 +1575,29 @@ gtk_button_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { GtkButton *button = GTK_BUTTON (widget); + GtkButtonPrivate *priv = button->priv; GtkAllocation child_allocation; - GtkStyle *style; + GtkStyleContext *context; GtkWidget *child; - - gint xthickness, ythickness; GtkBorder default_border; - GtkBorder inner_border; + GtkBorder padding; + GtkBorder border; gint focus_width; gint focus_pad; - style = gtk_widget_get_style (widget); - xthickness = style->xthickness; - ythickness = style->ythickness; + context = gtk_widget_get_style_context (widget); - 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); + gtk_button_get_props (button, &default_border, NULL, + &padding, &border, NULL); + gtk_style_context_get_style (context, + "focus-line-width", &focus_width, + "focus-padding", &focus_pad, + NULL); gtk_widget_set_allocation (widget, allocation); if (gtk_widget_get_realized (widget)) - gdk_window_move_resize (button->event_window, + gdk_window_move_resize (priv->event_window, allocation->x, allocation->y, allocation->width, @@ -1464,20 +1606,18 @@ gtk_button_size_allocate (GtkWidget *widget, child = gtk_bin_get_child (GTK_BIN (button)); if (child && gtk_widget_get_visible (child)) { - child_allocation.x = allocation->x + inner_border.left + xthickness; - child_allocation.y = allocation->y + inner_border.top + ythickness; + child_allocation.x = allocation->x + padding.left + border.left; + child_allocation.y = allocation->y + padding.top + border.top; child_allocation.width = allocation->width - - xthickness * 2 - - inner_border.left - - inner_border.right; + (padding.left + padding.right) - + (border.left + border.right); child_allocation.height = allocation->height - - ythickness * 2 - - inner_border.top - - inner_border.bottom; + (padding.top + padding.bottom) - + (border.top + border.bottom); if (gtk_widget_get_can_default (GTK_WIDGET (button))) { @@ -1495,15 +1635,15 @@ gtk_button_size_allocate (GtkWidget *widget, child_allocation.height = child_allocation.height - (focus_width + focus_pad) * 2; } - if (button->depressed) + if (priv->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, - NULL); + + gtk_style_context_get_style (context, + "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; } @@ -1515,17 +1655,12 @@ gtk_button_size_allocate (GtkWidget *widget, } } -void -_gtk_button_paint (GtkButton *button, - cairo_t *cr, - int width, - int height, - GtkStateType state_type, - GtkShadowType shadow_type, - const gchar *main_detail, - const gchar *default_detail) -{ - GtkWidget *widget; +static gboolean +gtk_button_draw (GtkWidget *widget, + cairo_t *cr) +{ + GtkButton *button = GTK_BUTTON (widget); + GtkButtonPrivate *priv = button->priv; gint x, y; GtkBorder default_border; GtkBorder default_outside_border; @@ -1533,32 +1668,30 @@ _gtk_button_paint (GtkButton *button, gint focus_width; gint focus_pad; GtkAllocation allocation; - GdkWindow *window; - GtkStyle *style; + GtkStyleContext *context; + GtkStateFlags state; + gboolean draw_focus; + gint width, height; - widget = GTK_WIDGET (button); + context = gtk_widget_get_style_context (widget); + state = gtk_style_context_get_state (context); - gtk_button_get_props (button, &default_border, &default_outside_border, NULL, &interior_focus); - gtk_widget_style_get (widget, - "focus-line-width", &focus_width, - "focus-padding", &focus_pad, - NULL); + gtk_button_get_props (button, &default_border, &default_outside_border, NULL, NULL, &interior_focus); + gtk_style_context_get_style (context, + "focus-line-width", &focus_width, + "focus-padding", &focus_pad, + NULL); gtk_widget_get_allocation (widget, &allocation); - style = gtk_widget_get_style (widget); - window = gtk_widget_get_window (widget); x = 0; y = 0; + width = allocation.width; + height = allocation.height; if (gtk_widget_has_default (widget) && - GTK_BUTTON (widget)->relief == GTK_RELIEF_NORMAL) + priv->relief == GTK_RELIEF_NORMAL) { - gtk_paint_box (style, cr, - GTK_STATE_NORMAL, GTK_SHADOW_IN, - widget, "buttondefault", - x, y, width, height); - x += default_border.left; y += default_border.top; width -= default_border.left + default_border.right; @@ -1571,8 +1704,11 @@ _gtk_button_paint (GtkButton *button, width -= default_outside_border.left + default_outside_border.right; height -= default_outside_border.top + default_outside_border.bottom; } - - if (!interior_focus && gtk_widget_has_focus (widget)) + + draw_focus = gtk_widget_has_visible_focus (widget); + + + if (!interior_focus && draw_focus) { x += focus_width + focus_pad; y += focus_width + focus_pad; @@ -1580,31 +1716,35 @@ _gtk_button_paint (GtkButton *button, height -= 2 * (focus_width + focus_pad); } - if (button->relief != GTK_RELIEF_NONE || button->depressed || - gtk_widget_get_state(widget) == GTK_STATE_PRELIGHT) - gtk_paint_box (style, cr, - state_type, - shadow_type, widget, "button", - x, y, width, height); - - if (gtk_widget_has_focus (widget)) + if (priv->relief != GTK_RELIEF_NONE || priv->depressed || + state & GTK_STATE_FLAG_PRELIGHT) + { + gtk_render_background (context, cr, + x, y, width, height); + gtk_render_frame (context, cr, + x, y, width, height); + } + + if (draw_focus) { gint child_displacement_x; gint child_displacement_y; gboolean displace_focus; - - gtk_widget_style_get (widget, - "child-displacement-y", &child_displacement_y, - "child-displacement-x", &child_displacement_x, - "displace-focus", &displace_focus, - NULL); + GtkBorder border; + + gtk_style_context_get_style (context, + "child-displacement-y", &child_displacement_y, + "child-displacement-x", &child_displacement_x, + "displace-focus", &displace_focus, + NULL); + gtk_style_context_get_border (context, state, &border); if (interior_focus) { - x += style->xthickness + focus_pad; - y += style->ythickness + focus_pad; - width -= 2 * (style->xthickness + focus_pad); - height -= 2 * (style->ythickness + focus_pad); + x += border.left + focus_pad; + y += border.top + focus_pad; + width -= (2 * focus_pad) + border.left + border.right; + height -= (2 * focus_pad) + border.top + border.bottom; } else { @@ -1614,31 +1754,14 @@ _gtk_button_paint (GtkButton *button, height += 2 * (focus_width + focus_pad); } - if (button->depressed && displace_focus) + if (priv->depressed && displace_focus) { x += child_displacement_x; y += child_displacement_y; } - gtk_paint_focus (style, cr, - gtk_widget_get_state (widget), - widget, "button", - x, y, width, height); + gtk_render_focus (context, cr, x, y, width, height); } -} - -static gboolean -gtk_button_draw (GtkWidget *widget, - cairo_t *cr) -{ - GtkButton *button = GTK_BUTTON (widget); - - _gtk_button_paint (button, cr, - gtk_widget_get_allocated_width (widget), - gtk_widget_get_allocated_height (widget), - gtk_widget_get_state (widget), - button->depressed ? GTK_SHADOW_IN : GTK_SHADOW_OUT, - "button", "buttondefault"); GTK_WIDGET_CLASS (gtk_button_parent_class)->draw (widget, cr); @@ -1650,16 +1773,18 @@ gtk_button_button_press (GtkWidget *widget, GdkEventButton *event) { GtkButton *button; + GtkButtonPrivate *priv; if (event->type == GDK_BUTTON_PRESS) { button = GTK_BUTTON (widget); + priv = button->priv; - if (button->focus_on_click && !gtk_widget_has_focus (widget)) + if (priv->focus_on_click && !gtk_widget_has_focus (widget)) gtk_widget_grab_focus (widget); - if (event->button == 1) - gtk_button_pressed (button); + if (event->button == GDK_BUTTON_PRIMARY) + g_signal_emit (button, button_signals[PRESSED], 0); } return TRUE; @@ -1671,10 +1796,32 @@ gtk_button_button_release (GtkWidget *widget, { GtkButton *button; - if (event->button == 1) + if (event->button == GDK_BUTTON_PRIMARY) { button = GTK_BUTTON (widget); - gtk_button_released (button); + g_signal_emit (button, button_signals[RELEASED], 0); + } + + return TRUE; +} + +static gboolean +gtk_button_touch (GtkWidget *widget, + GdkEventTouch *event) +{ + GtkButton *button = GTK_BUTTON (widget); + GtkButtonPrivate *priv = button->priv; + + if (event->type == GDK_TOUCH_BEGIN) + { + if (priv->focus_on_click && !gtk_widget_has_focus (widget)) + gtk_widget_grab_focus (widget); + + g_signal_emit (button, button_signals[PRESSED], 0); + } + else if (event->type == GDK_TOUCH_END) + { + g_signal_emit (button, button_signals[RELEASED], 0); } return TRUE; @@ -1685,17 +1832,18 @@ gtk_button_grab_broken (GtkWidget *widget, GdkEventGrabBroken *event) { GtkButton *button = GTK_BUTTON (widget); + GtkButtonPrivate *priv = button->priv; gboolean save_in; /* Simulate a button release without the pointer in the button */ - if (button->button_down) + if (priv->button_down) { - save_in = button->in_button; - button->in_button = FALSE; - gtk_button_released (button); - if (save_in != button->in_button) + save_in = priv->in_button; + priv->in_button = FALSE; + g_signal_emit (button, button_signals[RELEASED], 0); + if (save_in != priv->in_button) { - button->in_button = save_in; + priv->in_button = save_in; gtk_button_update_state (button); } } @@ -1708,8 +1856,9 @@ gtk_button_key_release (GtkWidget *widget, GdkEventKey *event) { GtkButton *button = GTK_BUTTON (widget); + GtkButtonPrivate *priv = button->priv; - if (button->activate_timeout) + if (priv->activate_timeout) { gtk_button_finish_activate (button, TRUE); return TRUE; @@ -1724,15 +1873,14 @@ static gboolean gtk_button_enter_notify (GtkWidget *widget, GdkEventCrossing *event) { - GtkButton *button; - - button = GTK_BUTTON (widget); + GtkButton *button = GTK_BUTTON (widget); + GtkButtonPrivate *priv = button->priv; - if ((event->window == button->event_window) && + if ((event->window == button->priv->event_window) && (event->detail != GDK_NOTIFY_INFERIOR)) { - button->in_button = TRUE; - gtk_button_enter (button); + priv->in_button = TRUE; + g_signal_emit (button, button_signals[ENTER], 0); } return FALSE; @@ -1742,16 +1890,15 @@ static gboolean gtk_button_leave_notify (GtkWidget *widget, GdkEventCrossing *event) { - GtkButton *button; - - button = GTK_BUTTON (widget); + GtkButton *button = GTK_BUTTON (widget); + GtkButtonPrivate *priv = button->priv; - if ((event->window == button->event_window) && + if ((event->window == button->priv->event_window) && (event->detail != GDK_NOTIFY_INFERIOR) && (gtk_widget_get_sensitive (widget))) { - button->in_button = FALSE; - gtk_button_leave (button); + priv->in_button = FALSE; + g_signal_emit (button, button_signals[LEAVE], 0); } return FALSE; @@ -1760,24 +1907,63 @@ gtk_button_leave_notify (GtkWidget *widget, static void gtk_real_button_pressed (GtkButton *button) { - if (button->activate_timeout) + GtkButtonPrivate *priv = button->priv; + + if (priv->activate_timeout) return; - - button->button_down = TRUE; + + priv->button_down = TRUE; gtk_button_update_state (button); } +static gboolean +touch_release_in_button (GtkButton *button) +{ + GtkButtonPrivate *priv; + gint width, height; + GdkEvent *event; + gdouble x, y; + + priv = button->priv; + event = gtk_get_current_event (); + + if (!event) + return FALSE; + + if (event->type != GDK_TOUCH_END || + event->touch.window != priv->event_window) + { + gdk_event_free (event); + return FALSE; + } + + gdk_event_get_coords (event, &x, &y); + width = gdk_window_get_width (priv->event_window); + height = gdk_window_get_height (priv->event_window); + + gdk_event_free (event); + + if (x >= 0 && x <= width && + y >= 0 && y <= height) + return TRUE; + + return FALSE; +} + static void gtk_real_button_released (GtkButton *button) { - if (button->button_down) + GtkButtonPrivate *priv = button->priv; + + if (priv->button_down) { - button->button_down = FALSE; + priv->button_down = FALSE; - if (button->activate_timeout) + if (priv->activate_timeout) return; - - if (button->in_button) + + if (priv->in_button || + touch_release_in_button (button)) gtk_button_clicked (button); gtk_button_update_state (button); @@ -1787,7 +1973,10 @@ gtk_real_button_released (GtkButton *button) static void gtk_real_button_clicked (GtkButton *button) { - GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + GtkButtonPrivate *priv = button->priv; + + if (priv->action_helper) + gtk_action_helper_activate (priv->action_helper); if (priv->action) gtk_action_activate (priv->action); @@ -1805,36 +1994,39 @@ static void gtk_real_button_activate (GtkButton *button) { GtkWidget *widget = GTK_WIDGET (button); - GtkButtonPrivate *priv; + GtkButtonPrivate *priv = button->priv; GdkDevice *device; guint32 time; - priv = GTK_BUTTON_GET_PRIVATE (button); device = gtk_get_current_event_device (); - if (device && device->source != GDK_SOURCE_KEYBOARD) + if (device && gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD) device = gdk_device_get_associated_device (device); - g_return_if_fail (device && device->source == GDK_SOURCE_KEYBOARD); - - if (gtk_widget_get_realized (widget) && !button->activate_timeout) + if (gtk_widget_get_realized (widget) && !priv->activate_timeout) { time = gtk_get_current_event_time (); - if (gdk_device_grab (device, button->event_window, - GDK_OWNERSHIP_WINDOW, TRUE, - GDK_KEY_PRESS | GDK_KEY_RELEASE, - NULL, time) == GDK_GRAB_SUCCESS) - { - gtk_device_grab_add (widget, device, TRUE); - priv->grab_keyboard = device; - priv->grab_time = time; + /* bgo#626336 - Only grab if we have a device (from an event), not if we + * were activated programmatically when no event is available. + */ + if (device && gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD) + { + if (gdk_device_grab (device, priv->event_window, + GDK_OWNERSHIP_WINDOW, TRUE, + GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK, + NULL, time) == GDK_GRAB_SUCCESS) + { + gtk_device_grab_add (widget, device, TRUE); + priv->grab_keyboard = device; + priv->grab_time = time; + } } - button->activate_timeout = gdk_threads_add_timeout (ACTIVATE_TIMEOUT, + priv->activate_timeout = gdk_threads_add_timeout (ACTIVATE_TIMEOUT, button_activate_timeout, button); - button->button_down = TRUE; + priv->button_down = TRUE; gtk_button_update_state (button); gtk_widget_queue_draw (GTK_WIDGET (button)); } @@ -1845,12 +2037,10 @@ gtk_button_finish_activate (GtkButton *button, gboolean do_it) { GtkWidget *widget = GTK_WIDGET (button); - GtkButtonPrivate *priv; - - priv = GTK_BUTTON_GET_PRIVATE (button); + GtkButtonPrivate *priv = button->priv; - g_source_remove (button->activate_timeout); - button->activate_timeout = 0; + g_source_remove (priv->activate_timeout); + priv->activate_timeout = 0; if (priv->grab_keyboard) { @@ -1859,7 +2049,7 @@ gtk_button_finish_activate (GtkButton *button, priv->grab_keyboard = NULL; } - button->button_down = FALSE; + priv->button_down = FALSE; gtk_button_update_state (button); gtk_widget_queue_draw (GTK_WIDGET (button)); @@ -1876,34 +2066,36 @@ gtk_button_get_size (GtkWidget *widget, gint *natural_size) { GtkButton *button = GTK_BUTTON (widget); - GtkStyle *style; + GtkStyleContext *context; GtkWidget *child; GtkBorder default_border; - GtkBorder inner_border; + GtkBorder padding; + GtkBorder border; gint focus_width; gint focus_pad; gint minimum, natural; - 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); + context = gtk_widget_get_style_context (widget); - style = gtk_widget_get_style (GTK_WIDGET (widget)); + gtk_button_get_props (button, &default_border, NULL, + &padding, &border, NULL); + gtk_style_context_get_style (context, + "focus-line-width", &focus_width, + "focus-padding", &focus_pad, + NULL); if (orientation == GTK_ORIENTATION_HORIZONTAL) { - minimum = (style->xthickness * 2 + - inner_border.left + inner_border.right); + minimum = padding.left + padding.right + + border.left + border.right; if (gtk_widget_get_can_default (GTK_WIDGET (widget))) minimum += default_border.left + default_border.right; } else { - minimum = (style->ythickness * 2 + - inner_border.top + inner_border.bottom); + minimum = padding.top + padding.bottom + + border.top + border.bottom; if (gtk_widget_get_can_default (GTK_WIDGET (widget))) minimum += default_border.top + default_border.bottom; @@ -1964,14 +2156,17 @@ void gtk_button_set_label (GtkButton *button, const gchar *label) { + GtkButtonPrivate *priv; gchar *new_label; - + g_return_if_fail (GTK_IS_BUTTON (button)); + priv = button->priv; + new_label = g_strdup (label); - g_free (button->label_text); - button->label_text = new_label; - + g_free (priv->label_text); + priv->label_text = new_label; + gtk_button_construct_child (button); g_object_notify (G_OBJECT (button), "label"); @@ -1990,12 +2185,12 @@ gtk_button_set_label (GtkButton *button, * 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 * +const gchar * gtk_button_get_label (GtkButton *button) { g_return_val_if_fail (GTK_IS_BUTTON (button), NULL); - - return button->label_text; + + return button->priv->label_text; } /** @@ -2010,14 +2205,18 @@ void gtk_button_set_use_underline (GtkButton *button, gboolean use_underline) { + GtkButtonPrivate *priv; + g_return_if_fail (GTK_IS_BUTTON (button)); + priv = button->priv; + use_underline = use_underline != FALSE; - if (use_underline != button->use_underline) + if (use_underline != priv->use_underline) { - button->use_underline = use_underline; - + priv->use_underline = use_underline; + gtk_button_construct_child (button); g_object_notify (G_OBJECT (button), "use-underline"); @@ -2038,8 +2237,8 @@ gboolean gtk_button_get_use_underline (GtkButton *button) { g_return_val_if_fail (GTK_IS_BUTTON (button), FALSE); - - return button->use_underline; + + return button->priv->use_underline; } /** @@ -2054,14 +2253,18 @@ void gtk_button_set_use_stock (GtkButton *button, gboolean use_stock) { + GtkButtonPrivate *priv; + g_return_if_fail (GTK_IS_BUTTON (button)); + priv = button->priv; + use_stock = use_stock != FALSE; - if (use_stock != button->use_stock) + if (use_stock != priv->use_stock) { - button->use_stock = use_stock; - + priv->use_stock = use_stock; + gtk_button_construct_child (button); g_object_notify (G_OBJECT (button), "use-stock"); @@ -2082,8 +2285,8 @@ gboolean gtk_button_get_use_stock (GtkButton *button) { g_return_val_if_fail (GTK_IS_BUTTON (button), FALSE); - - return button->use_stock; + + return button->priv->use_stock; } /** @@ -2102,13 +2305,17 @@ void gtk_button_set_focus_on_click (GtkButton *button, gboolean focus_on_click) { + GtkButtonPrivate *priv; + g_return_if_fail (GTK_IS_BUTTON (button)); - + + priv = button->priv; + focus_on_click = focus_on_click != FALSE; - if (button->focus_on_click != focus_on_click) + if (priv->focus_on_click != focus_on_click) { - button->focus_on_click = focus_on_click; + priv->focus_on_click = focus_on_click; g_object_notify (G_OBJECT (button), "focus-on-click"); } @@ -2131,7 +2338,7 @@ gtk_button_get_focus_on_click (GtkButton *button) { g_return_val_if_fail (GTK_IS_BUTTON (button), FALSE); - return button->focus_on_click; + return button->priv->focus_on_click; } /** @@ -2143,7 +2350,7 @@ gtk_button_get_focus_on_click (GtkButton *button) * 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. + * the child is a #GtkMisc or a #GtkAlignment. * * Since: 2.4 */ @@ -2156,7 +2363,7 @@ gtk_button_set_alignment (GtkButton *button, g_return_if_fail (GTK_IS_BUTTON (button)); - priv = GTK_BUTTON_GET_PRIVATE (button); + priv = button->priv; priv->xalign = xalign; priv->yalign = yalign; @@ -2173,8 +2380,8 @@ gtk_button_set_alignment (GtkButton *button, /** * gtk_button_get_alignment: * @button: a #GtkButton - * @xalign: return location for horizontal alignment - * @yalign: return location for vertical alignment + * @xalign: (out): return location for horizontal alignment + * @yalign: (out): return location for vertical alignment * * Gets the alignment of the child in the button. * @@ -2189,7 +2396,7 @@ gtk_button_get_alignment (GtkButton *button, g_return_if_fail (GTK_IS_BUTTON (button)); - priv = GTK_BUTTON_GET_PRIVATE (button); + priv = button->priv; if (xalign) *xalign = priv->xalign; @@ -2212,12 +2419,13 @@ _gtk_button_set_depressed (GtkButton *button, gboolean depressed) { GtkWidget *widget = GTK_WIDGET (button); + GtkButtonPrivate *priv = button->priv; depressed = depressed != FALSE; - if (depressed != button->depressed) + if (depressed != priv->depressed) { - button->depressed = depressed; + priv->depressed = depressed; gtk_widget_queue_resize (widget); } } @@ -2225,27 +2433,32 @@ _gtk_button_set_depressed (GtkButton *button, static void gtk_button_update_state (GtkButton *button) { + GtkButtonPrivate *priv = button->priv; + GtkStateFlags new_state; gboolean depressed; - GtkStateType new_state; - if (button->activate_timeout) - depressed = button->depress_on_activate; + if (priv->activate_timeout) + depressed = priv->depress_on_activate; else - depressed = button->in_button && button->button_down; + depressed = priv->in_button && priv->button_down; - 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 = gtk_widget_get_state_flags (GTK_WIDGET (button)) & + ~(GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_ACTIVE); + + if (priv->in_button) + new_state |= GTK_STATE_FLAG_PRELIGHT; - _gtk_button_set_depressed (button, depressed); - gtk_widget_set_state (GTK_WIDGET (button), new_state); + if (depressed) + new_state |= GTK_STATE_FLAG_ACTIVE; + + _gtk_button_set_depressed (button, depressed); + gtk_widget_set_state_flags (GTK_WIDGET (button), new_state, TRUE); } static void show_image_change_notify (GtkButton *button) { - GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + GtkButtonPrivate *priv = button->priv; if (priv->image) { @@ -2286,6 +2499,7 @@ gtk_button_screen_changed (GtkWidget *widget, GdkScreen *previous_screen) { GtkButton *button; + GtkButtonPrivate *priv; GtkSettings *settings; gulong show_image_connection; @@ -2293,12 +2507,13 @@ gtk_button_screen_changed (GtkWidget *widget, return; button = GTK_BUTTON (widget); + priv = button->priv; /* If the button is being pressed while the screen changes the release might never occur, so we reset the state. */ - if (button->button_down) + if (priv->button_down) { - button->button_down = FALSE; + priv->button_down = FALSE; gtk_button_update_state (button); } @@ -2322,10 +2537,11 @@ gtk_button_state_changed (GtkWidget *widget, GtkStateType previous_state) { GtkButton *button = GTK_BUTTON (widget); + GtkButtonPrivate *priv = button->priv; if (!gtk_widget_is_sensitive (widget)) { - button->in_button = FALSE; + priv->in_button = FALSE; gtk_real_button_released (button); } } @@ -2335,22 +2551,22 @@ gtk_button_grab_notify (GtkWidget *widget, gboolean was_grabbed) { GtkButton *button = GTK_BUTTON (widget); - GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button); + GtkButtonPrivate *priv = button->priv; gboolean save_in; - if (button->activate_timeout && + if (priv->activate_timeout && priv->grab_keyboard && gtk_widget_device_is_shadowed (widget, priv->grab_keyboard)) gtk_button_finish_activate (button, FALSE); if (!was_grabbed) { - save_in = button->in_button; - button->in_button = FALSE; + save_in = priv->in_button; + priv->in_button = FALSE; gtk_real_button_released (button); - if (save_in != button->in_button) + if (save_in != priv->in_button) { - button->in_button = save_in; + priv->in_button = save_in; gtk_button_update_state (button); } } @@ -2378,7 +2594,7 @@ gtk_button_set_image (GtkButton *button, g_return_if_fail (GTK_IS_BUTTON (button)); g_return_if_fail (image == NULL || GTK_IS_WIDGET (image)); - priv = GTK_BUTTON_GET_PRIVATE (button); + priv = button->priv; if (priv->image) { @@ -2410,13 +2626,9 @@ gtk_button_set_image (GtkButton *button, 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; + return button->priv->image; } /** @@ -2433,13 +2645,12 @@ 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); + priv = button->priv; if (priv->image_position != position) { @@ -2464,16 +2675,69 @@ gtk_button_set_image_position (GtkButton *button, */ GtkPositionType gtk_button_get_image_position (GtkButton *button) +{ + g_return_val_if_fail (GTK_IS_BUTTON (button), GTK_POS_LEFT); + + return button->priv->image_position; +} + +/** + * gtk_button_set_always_show_image: + * @button: a #GtkButton + * @always_show: %TRUE if the menuitem should always show the image + * + * If %TRUE, the button will ignore the #GtkSettings:gtk-button-images + * setting and always show the image, if available. + * + * Use this property if the button would be useless or hard to use + * without the image. + * + * Since: 3.6 + */ +void +gtk_button_set_always_show_image (GtkButton *button, + gboolean always_show) { GtkButtonPrivate *priv; - g_return_val_if_fail (GTK_IS_BUTTON (button), GTK_POS_LEFT); + g_return_if_fail (GTK_IS_BUTTON (button)); - priv = GTK_BUTTON_GET_PRIVATE (button); - - return priv->image_position; + priv = button->priv; + + if (priv->always_show_image != always_show) + { + priv->always_show_image = always_show; + + if (priv->image) + { + if (show_image (button)) + gtk_widget_show (priv->image); + else + gtk_widget_hide (priv->image); + } + + g_object_notify (G_OBJECT (button), "always-show-image"); + } } +/** + * gtk_button_get_always_show_image: + * @button: a #GtkButton + * + * Returns whether the button will ignore the #GtkSettings:gtk-button-images + * setting and always show the image, if available. + * + * Returns: %TRUE if the button will always show the image + * + * Since: 3.6 + */ +gboolean +gtk_button_get_always_show_image (GtkButton *button) +{ + g_return_val_if_fail (GTK_IS_BUTTON (button), FALSE); + + return button->priv->always_show_image; +} /** * gtk_button_get_event_window: @@ -2491,5 +2755,5 @@ gtk_button_get_event_window (GtkButton *button) { g_return_val_if_fail (GTK_IS_BUTTON (button), NULL); - return button->event_window; + return button->priv->event_window; }