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