No issues found
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /*
3 * st-button.c: Plain button actor
4 *
5 * Copyright 2007 OpenedHand
6 * Copyright 2008, 2009 Intel Corporation.
7 * Copyright 2009, 2010 Red Hat, Inc.
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms and conditions of the GNU Lesser General Public License,
11 * version 2.1, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope it will be useful, but WITHOUT ANY
14 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 /**
23 * SECTION:st-button
24 * @short_description: Button widget
25 *
26 * A button widget with support for either a text label or icon, toggle mode
27 * and transitions effects between states.
28 */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include <glib.h>
38
39 #include <clutter/clutter.h>
40
41 #include "st-button.h"
42
43 #include "st-enum-types.h"
44 #include "st-texture-cache.h"
45 #include "st-private.h"
46
47 #include <st/st-widget-accessible.h>
48
49 enum
50 {
51 PROP_0,
52
53 PROP_LABEL,
54 PROP_BUTTON_MASK,
55 PROP_TOGGLE_MODE,
56 PROP_CHECKED,
57 PROP_PRESSED
58 };
59
60 enum
61 {
62 CLICKED,
63
64 LAST_SIGNAL
65 };
66
67 #define ST_BUTTON_GET_PRIVATE(obj) \
68 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_BUTTON, StButtonPrivate))
69
70 struct _StButtonPrivate
71 {
72 gchar *text;
73
74 guint button_mask : 3;
75 guint is_toggle : 1;
76
77 guint pressed : 3;
78 guint grabbed : 3;
79 guint is_checked : 1;
80
81 gint spacing;
82 };
83
84 static guint button_signals[LAST_SIGNAL] = { 0, };
85
86 G_DEFINE_TYPE (StButton, st_button, ST_TYPE_BIN);
87
88 static GType st_button_accessible_get_type (void) G_GNUC_CONST;
89
90 static void
91 st_button_update_label_style (StButton *button)
92 {
93 ClutterActor *label;
94
95 label = st_bin_get_child (ST_BIN (button));
96
97 /* check the child is really a label */
98 if (!CLUTTER_IS_TEXT (label))
99 return;
100
101 _st_set_text_from_style (CLUTTER_TEXT (label), st_widget_get_theme_node (ST_WIDGET (button)));
102 }
103
104 static void
105 st_button_style_changed (StWidget *widget)
106 {
107 StButton *button = ST_BUTTON (widget);
108 StButtonPrivate *priv = button->priv;
109 StButtonClass *button_class = ST_BUTTON_GET_CLASS (button);
110 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (button));
111 double spacing;
112
113 ST_WIDGET_CLASS (st_button_parent_class)->style_changed (widget);
114
115 spacing = 6;
116 st_theme_node_lookup_length (theme_node, "border-spacing", FALSE, &spacing);
117 priv->spacing = (int)(0.5 + spacing);
118
119 /* update the label styling */
120 st_button_update_label_style (button);
121
122 /* run a transition if applicable */
123 if (button_class->transition)
124 {
125 button_class->transition (button);
126 }
127 }
128
129 static void
130 st_button_press (StButton *button,
131 StButtonMask mask)
132 {
133 if (button->priv->pressed == 0)
134 st_widget_add_style_pseudo_class (ST_WIDGET (button), "active");
135
136 button->priv->pressed |= mask;
137 }
138
139 static void
140 st_button_release (StButton *button,
141 StButtonMask mask,
142 int clicked_button)
143 {
144 button->priv->pressed &= ~mask;
145 if (button->priv->pressed != 0)
146 return;
147
148 st_widget_remove_style_pseudo_class (ST_WIDGET (button), "active");
149
150 if (clicked_button)
151 {
152 if (button->priv->is_toggle)
153 st_button_set_checked (button, !button->priv->is_checked);
154
155 g_signal_emit (button, button_signals[CLICKED], 0, clicked_button);
156 }
157 }
158
159 static gboolean
160 st_button_button_press (ClutterActor *actor,
161 ClutterButtonEvent *event)
162 {
163 StButton *button = ST_BUTTON (actor);
164 StButtonMask mask = ST_BUTTON_MASK_FROM_BUTTON (event->button);
165
166 if (button->priv->button_mask & mask)
167 {
168 if (button->priv->grabbed == 0)
169 clutter_grab_pointer (actor);
170
171 button->priv->grabbed |= mask;
172 st_button_press (button, mask);
173
174 return TRUE;
175 }
176
177 return FALSE;
178 }
179
180 static gboolean
181 st_button_button_release (ClutterActor *actor,
182 ClutterButtonEvent *event)
183 {
184 StButton *button = ST_BUTTON (actor);
185 StButtonMask mask = ST_BUTTON_MASK_FROM_BUTTON (event->button);
186
187 if (button->priv->button_mask & mask)
188 {
189 gboolean is_click;
190
191 is_click = button->priv->grabbed && st_widget_get_hover (ST_WIDGET (button));
192 st_button_release (button, mask, is_click ? event->button : 0);
193
194 button->priv->grabbed &= ~mask;
195 if (button->priv->grabbed == 0)
196 clutter_ungrab_pointer ();
197
198 return TRUE;
199 }
200
201 return FALSE;
202 }
203
204 static gboolean
205 st_button_key_press (ClutterActor *actor,
206 ClutterKeyEvent *event)
207 {
208 StButton *button = ST_BUTTON (actor);
209
210 if (button->priv->button_mask & ST_BUTTON_ONE)
211 {
212 if (event->keyval == CLUTTER_KEY_space ||
213 event->keyval == CLUTTER_KEY_Return ||
214 event->keyval == CLUTTER_KEY_KP_Enter)
215 {
216 st_button_press (button, ST_BUTTON_ONE);
217 return TRUE;
218 }
219 }
220
221 return CLUTTER_ACTOR_CLASS (st_button_parent_class)->key_press_event (actor, event);
222 }
223
224 static gboolean
225 st_button_key_release (ClutterActor *actor,
226 ClutterKeyEvent *event)
227 {
228 StButton *button = ST_BUTTON (actor);
229
230 if (button->priv->button_mask & ST_BUTTON_ONE)
231 {
232 if (event->keyval == CLUTTER_KEY_space ||
233 event->keyval == CLUTTER_KEY_Return ||
234 event->keyval == CLUTTER_KEY_KP_Enter)
235 {
236 gboolean is_click;
237
238 is_click = (button->priv->pressed & ST_BUTTON_ONE);
239 st_button_release (button, ST_BUTTON_ONE, is_click ? 1 : 0);
240 return TRUE;
241 }
242 }
243
244 return FALSE;
245 }
246
247 static void
248 st_button_key_focus_out (ClutterActor *actor)
249 {
250 StButton *button = ST_BUTTON (actor);
251
252 /* If we lose focus between a key press and release, undo the press */
253 if ((button->priv->pressed & ST_BUTTON_ONE) &&
254 !(button->priv->grabbed & ST_BUTTON_ONE))
255 st_button_release (button, ST_BUTTON_ONE, 0);
256
257 CLUTTER_ACTOR_CLASS (st_button_parent_class)->key_focus_out (actor);
258 }
259
260 static gboolean
261 st_button_enter (ClutterActor *actor,
262 ClutterCrossingEvent *event)
263 {
264 StButton *button = ST_BUTTON (actor);
265 gboolean ret;
266
267 ret = CLUTTER_ACTOR_CLASS (st_button_parent_class)->enter_event (actor, event);
268
269 if (button->priv->grabbed)
270 {
271 if (st_widget_get_hover (ST_WIDGET (button)))
272 st_button_press (button, button->priv->grabbed);
273 else
274 st_button_release (button, button->priv->grabbed, 0);
275 }
276
277 return ret;
278 }
279
280 static gboolean
281 st_button_leave (ClutterActor *actor,
282 ClutterCrossingEvent *event)
283 {
284 StButton *button = ST_BUTTON (actor);
285 gboolean ret;
286
287 ret = CLUTTER_ACTOR_CLASS (st_button_parent_class)->leave_event (actor, event);
288
289 if (button->priv->grabbed)
290 {
291 if (st_widget_get_hover (ST_WIDGET (button)))
292 st_button_press (button, button->priv->grabbed);
293 else
294 st_button_release (button, button->priv->grabbed, 0);
295 }
296
297 return ret;
298 }
299
300 static void
301 st_button_set_property (GObject *gobject,
302 guint prop_id,
303 const GValue *value,
304 GParamSpec *pspec)
305 {
306 StButton *button = ST_BUTTON (gobject);
307
308 switch (prop_id)
309 {
310 case PROP_LABEL:
311 st_button_set_label (button, g_value_get_string (value));
312 break;
313 case PROP_BUTTON_MASK:
314 st_button_set_button_mask (button, g_value_get_flags (value));
315 break;
316 case PROP_TOGGLE_MODE:
317 st_button_set_toggle_mode (button, g_value_get_boolean (value));
318 break;
319 case PROP_CHECKED:
320 st_button_set_checked (button, g_value_get_boolean (value));
321 break;
322
323
324 default:
325 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
326 break;
327 }
328 }
329
330 static void
331 st_button_get_property (GObject *gobject,
332 guint prop_id,
333 GValue *value,
334 GParamSpec *pspec)
335 {
336 StButtonPrivate *priv = ST_BUTTON (gobject)->priv;
337
338 switch (prop_id)
339 {
340 case PROP_LABEL:
341 g_value_set_string (value, priv->text);
342 break;
343 case PROP_BUTTON_MASK:
344 g_value_set_flags (value, priv->button_mask);
345 break;
346 case PROP_TOGGLE_MODE:
347 g_value_set_boolean (value, priv->is_toggle);
348 break;
349 case PROP_CHECKED:
350 g_value_set_boolean (value, priv->is_checked);
351 break;
352 case PROP_PRESSED:
353 g_value_set_boolean (value, priv->pressed != 0);
354 break;
355
356
357 default:
358 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
359 break;
360 }
361 }
362
363 static void
364 st_button_finalize (GObject *gobject)
365 {
366 StButtonPrivate *priv = ST_BUTTON (gobject)->priv;
367
368 g_free (priv->text);
369
370 G_OBJECT_CLASS (st_button_parent_class)->finalize (gobject);
371 }
372
373 static void
374 st_button_class_init (StButtonClass *klass)
375 {
376 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
377 ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
378 StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
379 GParamSpec *pspec;
380
381 g_type_class_add_private (klass, sizeof (StButtonPrivate));
382
383 gobject_class->set_property = st_button_set_property;
384 gobject_class->get_property = st_button_get_property;
385 gobject_class->finalize = st_button_finalize;
386
387 actor_class->button_press_event = st_button_button_press;
388 actor_class->button_release_event = st_button_button_release;
389 actor_class->key_press_event = st_button_key_press;
390 actor_class->key_release_event = st_button_key_release;
391 actor_class->key_focus_out = st_button_key_focus_out;
392 actor_class->enter_event = st_button_enter;
393 actor_class->leave_event = st_button_leave;
394
395 widget_class->style_changed = st_button_style_changed;
396 widget_class->get_accessible_type = st_button_accessible_get_type;
397
398 pspec = g_param_spec_string ("label",
399 "Label",
400 "Label of the button",
401 NULL, G_PARAM_READWRITE);
402 g_object_class_install_property (gobject_class, PROP_LABEL, pspec);
403
404 pspec = g_param_spec_flags ("button-mask",
405 "Button mask",
406 "Which buttons trigger the 'clicked' signal",
407 ST_TYPE_BUTTON_MASK, ST_BUTTON_ONE,
408 G_PARAM_READWRITE);
409 g_object_class_install_property (gobject_class, PROP_BUTTON_MASK, pspec);
410
411 pspec = g_param_spec_boolean ("toggle-mode",
412 "Toggle Mode",
413 "Enable or disable toggling",
414 FALSE, G_PARAM_READWRITE);
415 g_object_class_install_property (gobject_class, PROP_TOGGLE_MODE, pspec);
416
417 pspec = g_param_spec_boolean ("checked",
418 "Checked",
419 "Indicates if a toggle button is \"on\""
420 " or \"off\"",
421 FALSE, G_PARAM_READWRITE);
422 g_object_class_install_property (gobject_class, PROP_CHECKED, pspec);
423
424 pspec = g_param_spec_boolean ("pressed",
425 "Pressed",
426 "Indicates if the button is pressed in",
427 FALSE, G_PARAM_READABLE);
428 g_object_class_install_property (gobject_class, PROP_PRESSED, pspec);
429
430
431 /**
432 * StButton::clicked:
433 * @button: the object that received the signal
434 * @clicked_button: the mouse button that was used
435 *
436 * Emitted when the user activates the button, either with a mouse press and
437 * release or with the keyboard.
438 */
439 button_signals[CLICKED] =
440 g_signal_new ("clicked",
441 G_TYPE_FROM_CLASS (klass),
442 G_SIGNAL_RUN_LAST,
443 G_STRUCT_OFFSET (StButtonClass, clicked),
444 NULL, NULL, NULL,
445 G_TYPE_NONE, 1,
446 G_TYPE_INT);
447 }
448
449 static void
450 st_button_init (StButton *button)
451 {
452 button->priv = ST_BUTTON_GET_PRIVATE (button);
453 button->priv->spacing = 6;
454 button->priv->button_mask = ST_BUTTON_ONE;
455
456 clutter_actor_set_reactive (CLUTTER_ACTOR (button), TRUE);
457 st_widget_set_track_hover (ST_WIDGET (button), TRUE);
458 }
459
460 /**
461 * st_button_new:
462 *
463 * Create a new button
464 *
465 * Returns: a new #StButton
466 */
467 StWidget *
468 st_button_new (void)
469 {
470 return g_object_new (ST_TYPE_BUTTON, NULL);
471 }
472
473 /**
474 * st_button_new_with_label:
475 * @text: text to set the label to
476 *
477 * Create a new #StButton with the specified label
478 *
479 * Returns: a new #StButton
480 */
481 StWidget *
482 st_button_new_with_label (const gchar *text)
483 {
484 return g_object_new (ST_TYPE_BUTTON, "label", text, NULL);
485 }
486
487 /**
488 * st_button_get_label:
489 * @button: a #StButton
490 *
491 * Get the text displayed on the button
492 *
493 * Returns: the text for the button. This must not be freed by the application
494 */
495 const gchar *
496 st_button_get_label (StButton *button)
497 {
498 g_return_val_if_fail (ST_IS_BUTTON (button), NULL);
499
500 return button->priv->text;
501 }
502
503 /**
504 * st_button_set_label:
505 * @button: a #Stbutton
506 * @text: text to set the label to
507 *
508 * Sets the text displayed on the button
509 */
510 void
511 st_button_set_label (StButton *button,
512 const gchar *text)
513 {
514 StButtonPrivate *priv;
515 ClutterActor *label;
516
517 g_return_if_fail (ST_IS_BUTTON (button));
518
519 priv = button->priv;
520
521 g_free (priv->text);
522
523 if (text)
524 priv->text = g_strdup (text);
525 else
526 priv->text = g_strdup ("");
527
528 label = st_bin_get_child (ST_BIN (button));
529
530 if (label && CLUTTER_IS_TEXT (label))
531 {
532 clutter_text_set_text (CLUTTER_TEXT (label), priv->text);
533 }
534 else
535 {
536 label = g_object_new (CLUTTER_TYPE_TEXT,
537 "text", priv->text,
538 "line-alignment", PANGO_ALIGN_CENTER,
539 "ellipsize", PANGO_ELLIPSIZE_END,
540 "use-markup", TRUE,
541 NULL);
542 st_bin_set_child (ST_BIN (button), label);
543 }
544
545 /* Fake a style change so that we reset the style properties on the label */
546 st_widget_style_changed (ST_WIDGET (button));
547
548 g_object_notify (G_OBJECT (button), "label");
549 }
550
551 /**
552 * st_button_get_button_mask:
553 * @button: a #StButton
554 *
555 * Gets the mask of mouse buttons that @button emits the
556 * #StButton::clicked signal for.
557 *
558 * Returns: the mask of mouse buttons that @button emits the
559 * #StButton::clicked signal for.
560 */
561 StButtonMask
562 st_button_get_button_mask (StButton *button)
563 {
564 g_return_val_if_fail (ST_IS_BUTTON (button), 0);
565
566 return button->priv->button_mask;
567 }
568
569 /**
570 * st_button_set_button_mask:
571 * @button: a #Stbutton
572 * @mask: the mask of mouse buttons that @button responds to
573 *
574 * Sets which mouse buttons @button emits #StButton::clicked for.
575 */
576 void
577 st_button_set_button_mask (StButton *button,
578 StButtonMask mask)
579 {
580 g_return_if_fail (ST_IS_BUTTON (button));
581
582 button->priv->button_mask = mask;
583
584 g_object_notify (G_OBJECT (button), "button-mask");
585 }
586
587 /**
588 * st_button_get_toggle_mode:
589 * @button: a #StButton
590 *
591 * Get the toggle mode status of the button.
592 *
593 * Returns: %TRUE if toggle mode is set, otherwise %FALSE
594 */
595 gboolean
596 st_button_get_toggle_mode (StButton *button)
597 {
598 g_return_val_if_fail (ST_IS_BUTTON (button), FALSE);
599
600 return button->priv->is_toggle;
601 }
602
603 /**
604 * st_button_set_toggle_mode:
605 * @button: a #Stbutton
606 * @toggle: %TRUE or %FALSE
607 *
608 * Enables or disables toggle mode for the button. In toggle mode, the active
609 * state will be "toggled" when the user clicks the button.
610 */
611 void
612 st_button_set_toggle_mode (StButton *button,
613 gboolean toggle)
614 {
615 g_return_if_fail (ST_IS_BUTTON (button));
616
617 button->priv->is_toggle = toggle;
618
619 g_object_notify (G_OBJECT (button), "toggle-mode");
620 }
621
622 /**
623 * st_button_get_checked:
624 * @button: a #StButton
625 *
626 * Get the state of the button that is in toggle mode.
627 *
628 * Returns: %TRUE if the button is checked, or %FALSE if not
629 */
630 gboolean
631 st_button_get_checked (StButton *button)
632 {
633 g_return_val_if_fail (ST_IS_BUTTON (button), FALSE);
634
635 return button->priv->is_checked;
636 }
637
638 /**
639 * st_button_set_checked:
640 * @button: a #Stbutton
641 * @checked: %TRUE or %FALSE
642 *
643 * Sets the pressed state of the button. This is only really useful if the
644 * button has #toggle-mode mode set to %TRUE.
645 */
646 void
647 st_button_set_checked (StButton *button,
648 gboolean checked)
649 {
650 g_return_if_fail (ST_IS_BUTTON (button));
651
652 if (button->priv->is_checked != checked)
653 {
654 button->priv->is_checked = checked;
655
656 if (checked)
657 st_widget_add_style_pseudo_class (ST_WIDGET (button), "checked");
658 else
659 st_widget_remove_style_pseudo_class (ST_WIDGET (button), "checked");
660 }
661
662 g_object_notify (G_OBJECT (button), "checked");
663 }
664
665 /**
666 * st_button_fake_release:
667 * @button: an #StButton
668 *
669 * If this widget is holding a pointer grab, this function will
670 * will ungrab it, and reset the pressed state. The effect is
671 * similar to if the user had released the mouse button, but without
672 * emitting the clicked signal.
673 *
674 * This function is useful if for example you want to do something
675 * after the user is holding the mouse button for a given period of
676 * time, breaking the grab.
677 */
678 void
679 st_button_fake_release (StButton *button)
680 {
681 if (button->priv->pressed)
682 st_button_release (button, button->priv->pressed, 0);
683
684 if (button->priv->grabbed)
685 {
686 button->priv->grabbed = 0;
687 clutter_ungrab_pointer ();
688 }
689 }
690
691 /******************************************************************************/
692 /*************************** ACCESSIBILITY SUPPORT ****************************/
693 /******************************************************************************/
694
695 #define ST_TYPE_BUTTON_ACCESSIBLE st_button_accessible_get_type ()
696
697 #define ST_BUTTON_ACCESSIBLE(obj) \
698 (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
699 ST_TYPE_BUTTON_ACCESSIBLE, StButtonAccessible))
700
701 #define ST_IS_BUTTON_ACCESSIBLE(obj) \
702 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
703 ST_TYPE_BUTTON_ACCESSIBLE))
704
705 #define ST_BUTTON_ACCESSIBLE_CLASS(klass) \
706 (G_TYPE_CHECK_CLASS_CAST ((klass), \
707 ST_TYPE_BUTTON_ACCESSIBLE, StButtonAccessibleClass))
708
709 #define ST_IS_BUTTON_ACCESSIBLE_CLASS(klass) \
710 (G_TYPE_CHECK_CLASS_TYPE ((klass), \
711 ST_TYPE_BUTTON_ACCESSIBLE))
712
713 #define ST_BUTTON_ACCESSIBLE_GET_CLASS(obj) \
714 (G_TYPE_INSTANCE_GET_CLASS ((obj), \
715 ST_TYPE_BUTTON_ACCESSIBLE, StButtonAccessibleClass))
716
717 typedef struct _StButtonAccessible StButtonAccessible;
718 typedef struct _StButtonAccessibleClass StButtonAccessibleClass;
719
720 struct _StButtonAccessible
721 {
722 StWidgetAccessible parent;
723 };
724
725 struct _StButtonAccessibleClass
726 {
727 StWidgetAccessibleClass parent_class;
728 };
729
730 static void st_button_accessible_class_init (StButtonAccessibleClass *klass);
731 static void st_button_accessible_init (StButtonAccessible *button);
732
733 /* AtkObject */
734 static void st_button_accessible_initialize (AtkObject *obj,
735 gpointer data);
736
737 G_DEFINE_TYPE (StButtonAccessible, st_button_accessible, ST_TYPE_WIDGET_ACCESSIBLE)
738
739 static const gchar *
740 st_button_accessible_get_name (AtkObject *obj)
741 {
742 StButton *button = NULL;
743 const gchar *name = NULL;
744
745 button = ST_BUTTON (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj)));
746
747 if (button == NULL)
748 return NULL;
749
750 name = ATK_OBJECT_CLASS (st_button_accessible_parent_class)->get_name (obj);
751 if (name != NULL)
752 return name;
753
754 return button->priv->text;
755 }
756
757 static void
758 st_button_accessible_class_init (StButtonAccessibleClass *klass)
759 {
760 AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
761
762 atk_class->initialize = st_button_accessible_initialize;
763 atk_class->get_name = st_button_accessible_get_name;
764 }
765
766 static void
767 st_button_accessible_init (StButtonAccessible *self)
768 {
769 /* initialization done on AtkObject->initialize */
770 }
771
772 static void
773 st_button_accessible_notify_label_cb (StButton *button,
774 GParamSpec *psec,
775 AtkObject *accessible)
776 {
777 g_object_notify (G_OBJECT (accessible), "accessible-name");
778 }
779
780 static void
781 st_button_accessible_compute_role (AtkObject *accessible,
782 StButton *button)
783 {
784 atk_object_set_role (accessible, button->priv->is_toggle
785 ? ATK_ROLE_TOGGLE_BUTTON : ATK_ROLE_PUSH_BUTTON);
786 }
787
788 static void
789 st_button_accessible_notify_toggle_mode_cb (StButton *button,
790 GParamSpec *psec,
791 AtkObject *accessible)
792 {
793 st_button_accessible_compute_role (accessible, button);
794 }
795
796 static void
797 st_button_accessible_initialize (AtkObject *obj,
798 gpointer data)
799 {
800 ATK_OBJECT_CLASS (st_button_accessible_parent_class)->initialize (obj, data);
801
802 st_button_accessible_compute_role (obj, ST_BUTTON (data));
803
804 g_signal_connect (data, "notify::label",
805 G_CALLBACK (st_button_accessible_notify_label_cb), obj);
806 g_signal_connect (data, "notify::toggle-mode",
807 G_CALLBACK (st_button_accessible_notify_toggle_mode_cb), obj);
808 }