gnome-shell-3.6.3.1/src/st/st-widget.c

No issues found

   1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
   2 /*
   3  * st-widget.c: Base class for St actors
   4  *
   5  * Copyright 2007 OpenedHand
   6  * Copyright 2008, 2009 Intel Corporation.
   7  * Copyright 2009, 2010 Red Hat, Inc.
   8  * Copyright 2009 Abderrahim Kitouni
   9  * Copyright 2009, 2010 Florian Mç«Żllner
  10  * Copyright 2010 Adel Gadllah
  11  * Copyright 2012 Igalia, S.L.
  12  *
  13  * This program is free software; you can redistribute it and/or modify it
  14  * under the terms and conditions of the GNU Lesser General Public License,
  15  * version 2.1, as published by the Free Software Foundation.
  16  *
  17  * This program is distributed in the hope it will be useful, but WITHOUT ANY
  18  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  19  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
  20  * more details.
  21  *
  22  * You should have received a copy of the GNU Lesser General Public License
  23  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  24  */
  25 
  26 #ifdef HAVE_CONFIG_H
  27 #include "config.h"
  28 #endif
  29 
  30 #include <stdlib.h>
  31 #include <string.h>
  32 #include <math.h>
  33 
  34 #include <clutter/clutter.h>
  35 
  36 #include "st-widget.h"
  37 
  38 #include "st-label.h"
  39 #include "st-private.h"
  40 #include "st-texture-cache.h"
  41 #include "st-theme-context.h"
  42 #include "st-theme-node-transition.h"
  43 
  44 #include "st-widget-accessible.h"
  45 
  46 #include <gtk/gtk.h>
  47 #include <atk/atk-enum-types.h>
  48 
  49 /*
  50  * Forward declaration for sake of StWidgetChild
  51  */
  52 struct _StWidgetPrivate
  53 {
  54   StTheme      *theme;
  55   StThemeNode  *theme_node;
  56   gchar        *pseudo_class;
  57   gchar        *style_class;
  58   gchar        *inline_style;
  59 
  60   StThemeNodeTransition *transition_animation;
  61 
  62   gboolean      is_stylable : 1;
  63   gboolean      is_style_dirty : 1;
  64   gboolean      draw_bg_color : 1;
  65   gboolean      draw_border_internal : 1;
  66   gboolean      track_hover : 1;
  67   gboolean      hover : 1;
  68   gboolean      can_focus : 1;
  69 
  70   AtkObject *accessible;
  71   AtkRole accessible_role;
  72   AtkStateSet *local_state_set;
  73 
  74   ClutterActor *label_actor;
  75   gchar        *accessible_name;
  76 
  77   /* Even though Clutter has first_child/last_child properties,
  78    * we need to keep track of the old first/last children so
  79    * that we can remove the pseudo classes on them. */
  80   StWidget *prev_last_child;
  81   StWidget *prev_first_child;
  82 };
  83 
  84 /**
  85  * SECTION:st-widget
  86  * @short_description: Base class for stylable actors
  87  *
  88  * #StWidget is a simple abstract class on top of #ClutterActor. It
  89  * provides basic themeing properties.
  90  *
  91  * Actors in the St library should subclass #StWidget if they plan
  92  * to obey to a certain #StStyle.
  93  */
  94 
  95 enum
  96 {
  97   PROP_0,
  98 
  99   PROP_THEME,
 100   PROP_PSEUDO_CLASS,
 101   PROP_STYLE_CLASS,
 102   PROP_STYLE,
 103   PROP_STYLABLE,
 104   PROP_TRACK_HOVER,
 105   PROP_HOVER,
 106   PROP_CAN_FOCUS,
 107   PROP_LABEL_ACTOR,
 108   PROP_ACCESSIBLE_ROLE,
 109   PROP_ACCESSIBLE_NAME
 110 };
 111 
 112 enum
 113 {
 114   STYLE_CHANGED,
 115   POPUP_MENU,
 116 
 117   LAST_SIGNAL
 118 };
 119 
 120 static guint signals[LAST_SIGNAL] = { 0, };
 121 
 122 gfloat st_slow_down_factor = 1.0;
 123 
 124 G_DEFINE_TYPE (StWidget, st_widget, CLUTTER_TYPE_ACTOR);
 125 
 126 #define ST_WIDGET_GET_PRIVATE(obj)    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_WIDGET, StWidgetPrivate))
 127 
 128 static void st_widget_recompute_style (StWidget    *widget,
 129                                        StThemeNode *old_theme_node);
 130 static gboolean st_widget_real_navigate_focus (StWidget         *widget,
 131                                                ClutterActor     *from,
 132                                                GtkDirectionType  direction);
 133 
 134 static AtkObject * st_widget_get_accessible (ClutterActor *actor);
 135 
 136 static void
 137 st_widget_set_property (GObject      *gobject,
 138                         guint         prop_id,
 139                         const GValue *value,
 140                         GParamSpec   *pspec)
 141 {
 142   StWidget *actor = ST_WIDGET (gobject);
 143 
 144   switch (prop_id)
 145     {
 146     case PROP_THEME:
 147       st_widget_set_theme (actor, g_value_get_object (value));
 148       break;
 149 
 150     case PROP_PSEUDO_CLASS:
 151       st_widget_set_style_pseudo_class (actor, g_value_get_string (value));
 152       break;
 153 
 154     case PROP_STYLE_CLASS:
 155       st_widget_set_style_class_name (actor, g_value_get_string (value));
 156       break;
 157 
 158     case PROP_STYLE:
 159       st_widget_set_style (actor, g_value_get_string (value));
 160       break;
 161 
 162     case PROP_STYLABLE:
 163       if (actor->priv->is_stylable != g_value_get_boolean (value))
 164         {
 165           actor->priv->is_stylable = g_value_get_boolean (value);
 166           clutter_actor_queue_relayout ((ClutterActor *) gobject);
 167         }
 168       break;
 169 
 170     case PROP_TRACK_HOVER:
 171       st_widget_set_track_hover (actor, g_value_get_boolean (value));
 172       break;
 173 
 174     case PROP_HOVER:
 175       st_widget_set_hover (actor, g_value_get_boolean (value));
 176       break;
 177 
 178     case PROP_CAN_FOCUS:
 179       st_widget_set_can_focus (actor, g_value_get_boolean (value));
 180       break;
 181 
 182     case PROP_LABEL_ACTOR:
 183       st_widget_set_label_actor (actor, g_value_get_object (value));
 184       break;
 185 
 186     case PROP_ACCESSIBLE_ROLE:
 187       st_widget_set_accessible_role (actor, g_value_get_enum (value));
 188       break;
 189 
 190     case PROP_ACCESSIBLE_NAME:
 191       st_widget_set_accessible_name (actor, g_value_get_string (value));
 192       break;
 193 
 194     default:
 195       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
 196       break;
 197     }
 198 }
 199 
 200 static void
 201 st_widget_get_property (GObject    *gobject,
 202                         guint       prop_id,
 203                         GValue     *value,
 204                         GParamSpec *pspec)
 205 {
 206   StWidget *actor = ST_WIDGET (gobject);
 207   StWidgetPrivate *priv = actor->priv;
 208 
 209   switch (prop_id)
 210     {
 211     case PROP_THEME:
 212       g_value_set_object (value, priv->theme);
 213       break;
 214 
 215     case PROP_PSEUDO_CLASS:
 216       g_value_set_string (value, priv->pseudo_class);
 217       break;
 218 
 219     case PROP_STYLE_CLASS:
 220       g_value_set_string (value, priv->style_class);
 221       break;
 222 
 223     case PROP_STYLE:
 224       g_value_set_string (value, priv->inline_style);
 225       break;
 226 
 227     case PROP_STYLABLE:
 228       g_value_set_boolean (value, priv->is_stylable);
 229       break;
 230 
 231     case PROP_TRACK_HOVER:
 232       g_value_set_boolean (value, priv->track_hover);
 233       break;
 234 
 235     case PROP_HOVER:
 236       g_value_set_boolean (value, priv->hover);
 237       break;
 238 
 239     case PROP_CAN_FOCUS:
 240       g_value_set_boolean (value, priv->can_focus);
 241       break;
 242 
 243     case PROP_LABEL_ACTOR:
 244       g_value_set_object (value, priv->label_actor);
 245       break;
 246 
 247     case PROP_ACCESSIBLE_ROLE:
 248       g_value_set_enum (value, st_widget_get_accessible_role (actor));
 249       break;
 250 
 251     case PROP_ACCESSIBLE_NAME:
 252       g_value_set_string (value, priv->accessible_name);
 253       break;
 254 
 255     default:
 256       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
 257       break;
 258     }
 259 }
 260 
 261 static void
 262 st_widget_remove_transition (StWidget *widget)
 263 {
 264   if (widget->priv->transition_animation)
 265     {
 266       g_object_run_dispose (G_OBJECT (widget->priv->transition_animation));
 267       g_object_unref (widget->priv->transition_animation);
 268       widget->priv->transition_animation = NULL;
 269     }
 270 }
 271 
 272 static void
 273 st_widget_dispose (GObject *gobject)
 274 {
 275   StWidget *actor = ST_WIDGET (gobject);
 276   StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
 277 
 278   if (priv->theme)
 279     {
 280       g_object_unref (priv->theme);
 281       priv->theme = NULL;
 282     }
 283 
 284   if (priv->theme_node)
 285     {
 286       g_object_run_dispose (G_OBJECT (priv->theme_node));
 287       g_object_unref (priv->theme_node);
 288       priv->theme_node = NULL;
 289     }
 290 
 291   st_widget_remove_transition (actor);
 292 
 293   /* The real dispose of this accessible is done on
 294    * AtkGObjectAccessible weak ref callback
 295    */
 296   if (priv->accessible)
 297     priv->accessible = NULL;
 298 
 299   if (priv->label_actor)
 300     {
 301       g_object_unref (priv->label_actor);
 302       priv->label_actor = NULL;
 303     }
 304 
 305   g_clear_object (&priv->prev_first_child);
 306   g_clear_object (&priv->prev_last_child);
 307 
 308   G_OBJECT_CLASS (st_widget_parent_class)->dispose (gobject);
 309 }
 310 
 311 static void
 312 st_widget_finalize (GObject *gobject)
 313 {
 314   StWidgetPrivate *priv = ST_WIDGET (gobject)->priv;
 315 
 316   g_free (priv->style_class);
 317   g_free (priv->pseudo_class);
 318   g_object_unref (priv->local_state_set);
 319   g_free (priv->accessible_name);
 320   g_free (priv->inline_style);
 321 
 322   G_OBJECT_CLASS (st_widget_parent_class)->finalize (gobject);
 323 }
 324 
 325 
 326 static void
 327 st_widget_get_preferred_width (ClutterActor *self,
 328                                gfloat        for_height,
 329                                gfloat       *min_width_p,
 330                                gfloat       *natural_width_p)
 331 {
 332   StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
 333 
 334   st_theme_node_adjust_for_width (theme_node, &for_height);
 335 
 336   CLUTTER_ACTOR_CLASS (st_widget_parent_class)->get_preferred_width (self, for_height, min_width_p, natural_width_p);
 337 
 338   st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
 339 }
 340 
 341 static void
 342 st_widget_get_preferred_height (ClutterActor *self,
 343                                 gfloat        for_width,
 344                                 gfloat       *min_height_p,
 345                                 gfloat       *natural_height_p)
 346 {
 347   StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
 348 
 349   st_theme_node_adjust_for_width (theme_node, &for_width);
 350 
 351   CLUTTER_ACTOR_CLASS (st_widget_parent_class)->get_preferred_height (self, for_width, min_height_p, natural_height_p);
 352 
 353   st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
 354 }
 355 
 356 static void
 357 st_widget_allocate (ClutterActor          *actor,
 358                     const ClutterActorBox *box,
 359                     ClutterAllocationFlags flags)
 360 {
 361   StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
 362   ClutterActorBox content_box;
 363 
 364   /* Note that we can't just chain up to clutter_actor_real_allocate --
 365    * Clutter does some dirty tricks for backwards compatibility.
 366    * Clutter also passes the actor's allocation directly to the layout
 367    * manager, meaning that we can't modify it for children only.
 368    */
 369 
 370   clutter_actor_set_allocation (actor, box, flags);
 371 
 372   st_theme_node_get_content_box (theme_node, box, &content_box);
 373 
 374   /* If we've chained up to here, we want to allocate the children using the
 375    * currently installed layout manager */
 376   clutter_layout_manager_allocate (clutter_actor_get_layout_manager (actor),
 377                                    CLUTTER_CONTAINER (actor),
 378                                    &content_box,
 379                                    flags);
 380 }
 381 
 382 /**
 383  * st_widget_paint_background:
 384  * @widget: The #StWidget
 385  *
 386  * Paint the background of the widget. This is meant to be called by
 387  * subclasses of StWiget that need to paint the background without
 388  * painting children.
 389  */
 390 void
 391 st_widget_paint_background (StWidget *widget)
 392 {
 393   StThemeNode *theme_node;
 394   ClutterActorBox allocation;
 395   guint8 opacity;
 396 
 397   theme_node = st_widget_get_theme_node (widget);
 398 
 399   clutter_actor_get_allocation_box (CLUTTER_ACTOR (widget), &allocation);
 400 
 401   opacity = clutter_actor_get_paint_opacity (CLUTTER_ACTOR (widget));
 402 
 403   if (widget->priv->transition_animation)
 404     st_theme_node_transition_paint (widget->priv->transition_animation,
 405                                     &allocation,
 406                                     opacity);
 407   else
 408     st_theme_node_paint (theme_node, &allocation, opacity);
 409 }
 410 
 411 static void
 412 st_widget_paint (ClutterActor *actor)
 413 {
 414   st_widget_paint_background (ST_WIDGET (actor));
 415 
 416   /* Chain up so we paint children. */
 417   CLUTTER_ACTOR_CLASS (st_widget_parent_class)->paint (actor);
 418 }
 419 
 420 static void
 421 st_widget_parent_set (ClutterActor *widget,
 422                       ClutterActor *old_parent)
 423 {
 424   ClutterActorClass *parent_class;
 425   ClutterActor *new_parent;
 426 
 427   parent_class = CLUTTER_ACTOR_CLASS (st_widget_parent_class);
 428   if (parent_class->parent_set)
 429     parent_class->parent_set (widget, old_parent);
 430 
 431   new_parent = clutter_actor_get_parent (widget);
 432 
 433   /* don't send the style changed signal if we no longer have a parent actor */
 434   if (new_parent)
 435     st_widget_style_changed (ST_WIDGET (widget));
 436 }
 437 
 438 static void
 439 st_widget_map (ClutterActor *actor)
 440 {
 441   StWidget *self = ST_WIDGET (actor);
 442 
 443   CLUTTER_ACTOR_CLASS (st_widget_parent_class)->map (actor);
 444 
 445   st_widget_ensure_style (self);
 446 }
 447 
 448 static void
 449 st_widget_unmap (ClutterActor *actor)
 450 {
 451   StWidget *self = ST_WIDGET (actor);
 452   StWidgetPrivate *priv = self->priv;
 453 
 454   CLUTTER_ACTOR_CLASS (st_widget_parent_class)->unmap (actor);
 455 
 456   if (priv->track_hover && priv->hover)
 457     st_widget_set_hover (self, FALSE);
 458 }
 459 
 460 static void
 461 notify_children_of_style_change (ClutterActor *self)
 462 {
 463   ClutterActorIter iter;
 464   ClutterActor *actor;
 465 
 466   clutter_actor_iter_init (&iter, self);
 467   while (clutter_actor_iter_next (&iter, &actor))
 468     {
 469       if (ST_IS_WIDGET (actor))
 470         st_widget_style_changed (ST_WIDGET (actor));
 471       else
 472         notify_children_of_style_change (actor);
 473     }
 474 }
 475 
 476 static void
 477 st_widget_real_style_changed (StWidget *self)
 478 {
 479   StWidgetPrivate *priv = ST_WIDGET (self)->priv;
 480 
 481   /* application has request this widget is not stylable */
 482   if (!priv->is_stylable)
 483     return;
 484 
 485   clutter_actor_queue_redraw ((ClutterActor *) self);
 486   notify_children_of_style_change ((ClutterActor *) self);
 487 }
 488 
 489 void
 490 st_widget_style_changed (StWidget *widget)
 491 {
 492   StThemeNode *old_theme_node = NULL;
 493 
 494   widget->priv->is_style_dirty = TRUE;
 495   if (widget->priv->theme_node)
 496     {
 497       old_theme_node = widget->priv->theme_node;
 498       widget->priv->theme_node = NULL;
 499     }
 500 
 501   /* update the style only if we are mapped */
 502   if (CLUTTER_ACTOR_IS_MAPPED (CLUTTER_ACTOR (widget)))
 503     st_widget_recompute_style (widget, old_theme_node);
 504 
 505   if (old_theme_node)
 506     g_object_unref (old_theme_node);
 507 }
 508 
 509 static void
 510 on_theme_context_changed (StThemeContext *context,
 511                           ClutterStage   *stage)
 512 {
 513   notify_children_of_style_change (CLUTTER_ACTOR (stage));
 514 }
 515 
 516 static StThemeNode *
 517 get_root_theme_node (ClutterStage *stage)
 518 {
 519   StThemeContext *context = st_theme_context_get_for_stage (stage);
 520 
 521   if (!g_object_get_data (G_OBJECT (context), "st-theme-initialized"))
 522     {
 523       g_object_set_data (G_OBJECT (context), "st-theme-initialized", GUINT_TO_POINTER (1));
 524       g_signal_connect (G_OBJECT (context), "changed",
 525                         G_CALLBACK (on_theme_context_changed), stage);
 526     }
 527 
 528   return st_theme_context_get_root_node (context);
 529 }
 530 
 531 /**
 532  * st_widget_get_theme_node:
 533  * @widget: a #StWidget
 534  *
 535  * Gets the theme node holding style information for the widget.
 536  * The theme node is used to access standard and custom CSS
 537  * properties of the widget.
 538  *
 539  * Note: it is a fatal error to call this on a widget that is
 540  *  not been added to a stage.
 541  *
 542  * Return value: (transfer none): the theme node for the widget.
 543  *   This is owned by the widget. When attributes of the widget
 544  *   or the environment that affect the styling change (for example
 545  *   the style_class property of the widget), it will be recreated,
 546  *   and the ::style-changed signal will be emitted on the widget.
 547  */
 548 StThemeNode *
 549 st_widget_get_theme_node (StWidget *widget)
 550 {
 551   StWidgetPrivate *priv = widget->priv;
 552 
 553   if (priv->theme_node == NULL)
 554     {
 555       StThemeNode *parent_node = NULL;
 556       ClutterStage *stage = NULL;
 557       ClutterActor *parent;
 558       char *pseudo_class, *direction_pseudo_class;
 559 
 560       parent = clutter_actor_get_parent (CLUTTER_ACTOR (widget));
 561       while (parent != NULL)
 562         {
 563           if (parent_node == NULL && ST_IS_WIDGET (parent))
 564             parent_node = st_widget_get_theme_node (ST_WIDGET (parent));
 565           else if (CLUTTER_IS_STAGE (parent))
 566             stage = CLUTTER_STAGE (parent);
 567 
 568           parent = clutter_actor_get_parent (parent);
 569         }
 570 
 571       if (stage == NULL)
 572         {
 573           g_error ("st_widget_get_theme_node called on the widget %s which is not in the stage.",
 574                     st_describe_actor (CLUTTER_ACTOR (widget)));
 575         }
 576 
 577       if (parent_node == NULL)
 578         parent_node = get_root_theme_node (CLUTTER_STAGE (stage));
 579 
 580       /* Always append a "magic" pseudo class indicating the text
 581        * direction, to allow to adapt the CSS when necessary without
 582        * requiring separate style sheets.
 583        */
 584       if (clutter_actor_get_text_direction (CLUTTER_ACTOR (widget)) == CLUTTER_TEXT_DIRECTION_RTL)
 585         direction_pseudo_class = "rtl";
 586       else
 587         direction_pseudo_class = "ltr";
 588 
 589       if (priv->pseudo_class)
 590         pseudo_class = g_strconcat(priv->pseudo_class, " ",
 591                                    direction_pseudo_class, NULL);
 592       else
 593         pseudo_class = direction_pseudo_class;
 594 
 595       priv->theme_node = st_theme_node_new (st_theme_context_get_for_stage (stage),
 596                                             parent_node, priv->theme,
 597                                             G_OBJECT_TYPE (widget),
 598                                             clutter_actor_get_name (CLUTTER_ACTOR (widget)),
 599                                             priv->style_class,
 600                                             pseudo_class,
 601                                             priv->inline_style);
 602 
 603       if (pseudo_class != direction_pseudo_class)
 604         g_free (pseudo_class);
 605     }
 606 
 607   return priv->theme_node;
 608 }
 609 
 610 /**
 611  * st_widget_peek_theme_node:
 612  * @widget: a #StWidget
 613  *
 614  * Returns the theme node for the widget if it has already been
 615  * computed, %NULL if the widget hasn't been added to a  stage or the theme
 616  * node hasn't been computed. If %NULL is returned, then ::style-changed
 617  * will be reliably emitted before the widget is allocated or painted.
 618  *
 619  * Return value: (transfer none): the theme node for the widget.
 620  *   This is owned by the widget. When attributes of the widget
 621  *   or the environment that affect the styling change (for example
 622  *   the style_class property of the widget), it will be recreated,
 623  *   and the ::style-changed signal will be emitted on the widget.
 624  */
 625 StThemeNode *
 626 st_widget_peek_theme_node (StWidget *widget)
 627 {
 628   StWidgetPrivate *priv = widget->priv;
 629 
 630   return priv->theme_node;
 631 }
 632 
 633 static gboolean
 634 st_widget_enter (ClutterActor         *actor,
 635                  ClutterCrossingEvent *event)
 636 {
 637   StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
 638 
 639   if (priv->track_hover)
 640     {
 641       if (clutter_actor_contains (actor, event->source))
 642         st_widget_set_hover (ST_WIDGET (actor), TRUE);
 643       else
 644         {
 645           /* The widget has a grab and is being told about an
 646            * enter-event outside its hierarchy. Hopefully we already
 647            * got a leave-event, but if not, handle it now.
 648            */
 649           st_widget_set_hover (ST_WIDGET (actor), FALSE);
 650         }
 651     }
 652 
 653   if (CLUTTER_ACTOR_CLASS (st_widget_parent_class)->enter_event)
 654     return CLUTTER_ACTOR_CLASS (st_widget_parent_class)->enter_event (actor, event);
 655   else
 656     return FALSE;
 657 }
 658 
 659 static gboolean
 660 st_widget_leave (ClutterActor         *actor,
 661                  ClutterCrossingEvent *event)
 662 {
 663   StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
 664 
 665   if (priv->track_hover)
 666     {
 667       if (!event->related || !clutter_actor_contains (actor, event->related))
 668         st_widget_set_hover (ST_WIDGET (actor), FALSE);
 669     }
 670 
 671   if (CLUTTER_ACTOR_CLASS (st_widget_parent_class)->leave_event)
 672     return CLUTTER_ACTOR_CLASS (st_widget_parent_class)->leave_event (actor, event);
 673   else
 674     return FALSE;
 675 }
 676 
 677 static void
 678 st_widget_key_focus_in (ClutterActor *actor)
 679 {
 680   StWidget *widget = ST_WIDGET (actor);
 681 
 682   st_widget_add_style_pseudo_class (widget, "focus");
 683 }
 684 
 685 static void
 686 st_widget_key_focus_out (ClutterActor *actor)
 687 {
 688   StWidget *widget = ST_WIDGET (actor);
 689 
 690   st_widget_remove_style_pseudo_class (widget, "focus");
 691 }
 692 
 693 static gboolean
 694 st_widget_key_press_event (ClutterActor    *actor,
 695                            ClutterKeyEvent *event)
 696 {
 697   if (event->keyval == CLUTTER_KEY_Menu ||
 698       (event->keyval == CLUTTER_KEY_F10 &&
 699        (event->modifier_state & CLUTTER_SHIFT_MASK)))
 700     {
 701       g_signal_emit (actor, signals[POPUP_MENU], 0);
 702       return TRUE;
 703     }
 704 
 705   return FALSE;
 706 }
 707 
 708 static gboolean
 709 st_widget_get_paint_volume (ClutterActor *self,
 710                             ClutterPaintVolume *volume)
 711 {
 712   ClutterActorBox paint_box, alloc_box;
 713   StThemeNode *theme_node;
 714   StWidgetPrivate *priv;
 715   ClutterVertex origin;
 716 
 717   /* Setting the paint volume does not make sense when we don't have any allocation */
 718   if (!clutter_actor_has_allocation (self))
 719     return FALSE;
 720 
 721   priv = ST_WIDGET (self)->priv;
 722 
 723   theme_node = st_widget_get_theme_node (ST_WIDGET (self));
 724   clutter_actor_get_allocation_box (self, &alloc_box);
 725 
 726   if (priv->transition_animation)
 727     st_theme_node_transition_get_paint_box (priv->transition_animation,
 728                                             &alloc_box, &paint_box);
 729   else
 730     st_theme_node_get_paint_box (theme_node, &alloc_box, &paint_box);
 731 
 732   origin.x = paint_box.x1 - alloc_box.x1;
 733   origin.y = paint_box.y1 - alloc_box.y1;
 734   origin.z = 0.0f;
 735 
 736   clutter_paint_volume_set_origin (volume, &origin);
 737   clutter_paint_volume_set_width (volume, paint_box.x2 - paint_box.x1);
 738   clutter_paint_volume_set_height (volume, paint_box.y2 - paint_box.y1);
 739 
 740   if (!clutter_actor_get_clip_to_allocation (self))
 741     {
 742       ClutterActor *child;
 743       /* Based on ClutterGroup/ClutterBox; include the children's
 744        * paint volumes, since they may paint outside our allocation.
 745        */
 746       for (child = clutter_actor_get_first_child (self);
 747            child != NULL;
 748            child = clutter_actor_get_next_sibling (child))
 749         {
 750           const ClutterPaintVolume *child_volume;
 751 
 752           child_volume = clutter_actor_get_transformed_paint_volume (child, self);
 753           if (!child_volume)
 754             return FALSE;
 755 
 756           clutter_paint_volume_union (volume, child_volume);
 757         }
 758     }
 759 
 760   return TRUE;
 761 }
 762 
 763 static GList *
 764 st_widget_real_get_focus_chain (StWidget *widget)
 765 {
 766   return clutter_actor_get_children (CLUTTER_ACTOR (widget));
 767 }
 768 
 769 
 770 static void
 771 st_widget_class_init (StWidgetClass *klass)
 772 {
 773   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 774   ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
 775   GParamSpec *pspec;
 776 
 777   g_type_class_add_private (klass, sizeof (StWidgetPrivate));
 778 
 779   gobject_class->set_property = st_widget_set_property;
 780   gobject_class->get_property = st_widget_get_property;
 781   gobject_class->dispose = st_widget_dispose;
 782   gobject_class->finalize = st_widget_finalize;
 783 
 784   actor_class->get_preferred_width = st_widget_get_preferred_width;
 785   actor_class->get_preferred_height = st_widget_get_preferred_height;
 786   actor_class->allocate = st_widget_allocate;
 787   actor_class->paint = st_widget_paint;
 788   actor_class->get_paint_volume = st_widget_get_paint_volume;
 789   actor_class->parent_set = st_widget_parent_set;
 790   actor_class->map = st_widget_map;
 791   actor_class->unmap = st_widget_unmap;
 792 
 793   actor_class->enter_event = st_widget_enter;
 794   actor_class->leave_event = st_widget_leave;
 795   actor_class->key_focus_in = st_widget_key_focus_in;
 796   actor_class->key_focus_out = st_widget_key_focus_out;
 797   actor_class->key_press_event = st_widget_key_press_event;
 798 
 799   actor_class->get_accessible = st_widget_get_accessible;
 800 
 801   klass->style_changed = st_widget_real_style_changed;
 802   klass->navigate_focus = st_widget_real_navigate_focus;
 803   klass->get_accessible_type = st_widget_accessible_get_type;
 804   klass->get_focus_chain = st_widget_real_get_focus_chain;
 805 
 806   /**
 807    * StWidget:pseudo-class:
 808    *
 809    * The pseudo-class of the actor. Typical values include "hover", "active",
 810    * "focus".
 811    */
 812   g_object_class_install_property (gobject_class,
 813                                    PROP_PSEUDO_CLASS,
 814                                    g_param_spec_string ("pseudo-class",
 815                                                         "Pseudo Class",
 816                                                         "Pseudo class for styling",
 817                                                         "",
 818                                                         ST_PARAM_READWRITE));
 819   /**
 820    * StWidget:style-class:
 821    *
 822    * The style-class of the actor for use in styling.
 823    */
 824   g_object_class_install_property (gobject_class,
 825                                    PROP_STYLE_CLASS,
 826                                    g_param_spec_string ("style-class",
 827                                                         "Style Class",
 828                                                         "Style class for styling",
 829                                                         "",
 830                                                         ST_PARAM_READWRITE));
 831 
 832   /**
 833    * StWidget:style:
 834    *
 835    * Inline style information for the actor as a ';'-separated list of
 836    * CSS properties.
 837    */
 838   g_object_class_install_property (gobject_class,
 839                                    PROP_STYLE,
 840                                    g_param_spec_string ("style",
 841                                                         "Style",
 842                                                         "Inline style string",
 843                                                         "",
 844                                                         ST_PARAM_READWRITE));
 845 
 846   /**
 847    * StWidget:theme:
 848    *
 849    * A theme set on this actor overriding the global theming for this actor
 850    * and its descendants
 851    */
 852   g_object_class_install_property (gobject_class,
 853                                    PROP_THEME,
 854                                    g_param_spec_object ("theme",
 855                                                         "Theme",
 856                                                         "Theme override",
 857                                                         ST_TYPE_THEME,
 858                                                         ST_PARAM_READWRITE));
 859 
 860   /**
 861    * StWidget:stylable:
 862    *
 863    * Enable or disable styling of the widget
 864    */
 865   pspec = g_param_spec_boolean ("stylable",
 866                                 "Stylable",
 867                                 "Whether the table should be styled",
 868                                 TRUE,
 869                                 ST_PARAM_READWRITE);
 870   g_object_class_install_property (gobject_class,
 871                                    PROP_STYLABLE,
 872                                    pspec);
 873 
 874   /**
 875    * StWidget:track-hover:
 876    *
 877    * Determines whether the widget tracks pointer hover state. If
 878    * %TRUE (and the widget is visible and reactive), the
 879    * #StWidget:hover property and "hover" style pseudo class will be
 880    * adjusted automatically as the pointer moves in and out of the
 881    * widget.
 882    */
 883   pspec = g_param_spec_boolean ("track-hover",
 884                                 "Track hover",
 885                                 "Determines whether the widget tracks hover state",
 886                                 FALSE,
 887                                 ST_PARAM_READWRITE);
 888   g_object_class_install_property (gobject_class,
 889                                    PROP_TRACK_HOVER,
 890                                    pspec);
 891 
 892   /**
 893    * StWidget:hover:
 894    *
 895    * Whether or not the pointer is currently hovering over the widget. This is
 896    * only tracked automatically if #StWidget:track-hover is %TRUE, but you can
 897    * adjust it manually in any case.
 898    */
 899   pspec = g_param_spec_boolean ("hover",
 900                                 "Hover",
 901                                 "Whether the pointer is hovering over the widget",
 902                                 FALSE,
 903                                 ST_PARAM_READWRITE);
 904   g_object_class_install_property (gobject_class,
 905                                    PROP_HOVER,
 906                                    pspec);
 907 
 908   /**
 909    * StWidget:can-focus:
 910    *
 911    * Whether or not the widget can be focused via keyboard navigation.
 912    */
 913   pspec = g_param_spec_boolean ("can-focus",
 914                                 "Can focus",
 915                                 "Whether the widget can be focused via keyboard navigation",
 916                                 FALSE,
 917                                 ST_PARAM_READWRITE);
 918   g_object_class_install_property (gobject_class,
 919                                    PROP_CAN_FOCUS,
 920                                    pspec);
 921 
 922   /**
 923    * ClutterActor:label-actor:
 924    *
 925    * An actor that labels this widget.
 926    */
 927   g_object_class_install_property (gobject_class,
 928                                    PROP_LABEL_ACTOR,
 929                                    g_param_spec_object ("label-actor",
 930                                                         "Label",
 931                                                         "Label that identifies this widget",
 932                                                         CLUTTER_TYPE_ACTOR,
 933                                                         ST_PARAM_READWRITE));
 934   /**
 935    * StWidget:accessible-role:
 936    *
 937    * The accessible role of this object
 938    */
 939   g_object_class_install_property (gobject_class,
 940                                    PROP_ACCESSIBLE_ROLE,
 941                                    g_param_spec_enum ("accessible-role",
 942                                                       "Accessible Role",
 943                                                       "The accessible role of this object",
 944                                                       ATK_TYPE_ROLE,
 945                                                       ATK_ROLE_INVALID,
 946                                                       G_PARAM_READWRITE));
 947 
 948 
 949   /**
 950    * StWidget:accessible-name:
 951    *
 952    * Object instance's name for assistive technology access.
 953    */
 954   g_object_class_install_property (gobject_class,
 955                                    PROP_ACCESSIBLE_NAME,
 956                                    g_param_spec_string ("accessible-name",
 957                                                         "Accessible name",
 958                                                         "Object instance's name for assistive technology access.",
 959                                                         NULL,
 960                                                         ST_PARAM_READWRITE));
 961 
 962   /**
 963    * StWidget::style-changed:
 964    * @widget: the #StWidget
 965    *
 966    * Emitted when the style information that the widget derives from the
 967    * theme changes
 968    */
 969   signals[STYLE_CHANGED] =
 970     g_signal_new ("style-changed",
 971                   G_TYPE_FROM_CLASS (klass),
 972                   G_SIGNAL_RUN_LAST,
 973                   G_STRUCT_OFFSET (StWidgetClass, style_changed),
 974                   NULL, NULL, NULL,
 975                   G_TYPE_NONE, 0);
 976 
 977   /**
 978    * StWidget::popup-menu:
 979    * @widget: the #StWidget
 980    *
 981    * Emitted when the user has requested a context menu (eg, via a
 982    * keybinding)
 983    */
 984   signals[POPUP_MENU] =
 985     g_signal_new ("popup-menu",
 986                   G_TYPE_FROM_CLASS (klass),
 987                   G_SIGNAL_RUN_LAST,
 988                   G_STRUCT_OFFSET (StWidgetClass, popup_menu),
 989                   NULL, NULL, NULL,
 990                   G_TYPE_NONE, 0);
 991 }
 992 
 993 /**
 994  * st_widget_set_theme:
 995  * @actor: a #StWidget
 996  * @theme: a new style class string
 997  *
 998  * Overrides the theme that would be inherited from the actor's parent
 999  * or the stage with an entirely new theme (set of stylesheets).
1000  */
1001 void
1002 st_widget_set_theme (StWidget  *actor,
1003                      StTheme   *theme)
1004 {
1005   StWidgetPrivate *priv;
1006 
1007   g_return_if_fail (ST_IS_WIDGET (actor));
1008 
1009   priv = actor->priv;
1010 
1011   if (theme != priv->theme)
1012     {
1013       if (priv->theme)
1014         g_object_unref (priv->theme);
1015       priv->theme = g_object_ref (theme);
1016 
1017       st_widget_style_changed (actor);
1018 
1019       g_object_notify (G_OBJECT (actor), "theme");
1020     }
1021 }
1022 
1023 /**
1024  * st_widget_get_theme:
1025  * @actor: a #StWidget
1026  *
1027  * Gets the overriding theme set on the actor. See st_widget_set_theme()
1028  *
1029  * Return value: (transfer none): the overriding theme, or %NULL
1030  */
1031 StTheme *
1032 st_widget_get_theme (StWidget *actor)
1033 {
1034   g_return_val_if_fail (ST_IS_WIDGET (actor), NULL);
1035 
1036   return actor->priv->theme;
1037 }
1038 
1039 static const gchar *
1040 find_class_name (const gchar *class_list,
1041                  const gchar *class_name)
1042 {
1043   gint len = strlen (class_name);
1044   const gchar *match;
1045 
1046   if (!class_list)
1047     return NULL;
1048 
1049   for (match = strstr (class_list, class_name); match; match = strstr (match + 1, class_name))
1050     {
1051       if ((match == class_list || g_ascii_isspace (match[-1])) &&
1052           (match[len] == '\0' || g_ascii_isspace (match[len])))
1053         return match;
1054     }
1055 
1056   return NULL;
1057 }
1058 
1059 static gboolean
1060 set_class_list (gchar       **class_list,
1061                 const gchar  *new_class_list)
1062 {
1063   if (g_strcmp0 (*class_list, new_class_list) != 0)
1064     {
1065       g_free (*class_list);
1066       *class_list = g_strdup (new_class_list);
1067       return TRUE;
1068     }
1069   else
1070     return FALSE;
1071 }
1072 
1073 static gboolean
1074 add_class_name (gchar       **class_list,
1075                 const gchar  *class_name)
1076 {
1077   gchar *new_class_list;
1078 
1079   if (*class_list)
1080     {
1081       if (find_class_name (*class_list, class_name))
1082         return FALSE;
1083 
1084       new_class_list = g_strdup_printf ("%s %s", *class_list, class_name);
1085       g_free (*class_list);
1086       *class_list = new_class_list;
1087     }
1088   else
1089     *class_list = g_strdup (class_name);
1090 
1091   return TRUE;
1092 }
1093 
1094 static gboolean
1095 remove_class_name (gchar       **class_list,
1096                    const gchar  *class_name)
1097 {
1098   const gchar *match, *end;
1099   gchar *new_class_list;
1100 
1101   if (!*class_list)
1102     return FALSE;
1103 
1104   if (strcmp (*class_list, class_name) == 0)
1105     {
1106       g_free (*class_list);
1107       *class_list = NULL;
1108       return TRUE;
1109     }
1110 
1111   match = find_class_name (*class_list, class_name);
1112   if (!match)
1113     return FALSE;
1114   end = match + strlen (class_name);
1115 
1116   /* Adjust either match or end to include a space as well.
1117    * (One or the other must be possible at this point.)
1118    */
1119   if (match != *class_list)
1120     match--;
1121   else
1122     end++;
1123 
1124   new_class_list = g_strdup_printf ("%.*s%s", (int)(match - *class_list),
1125                                     *class_list, end);
1126   g_free (*class_list);
1127   *class_list = new_class_list;
1128 
1129   return TRUE;
1130 }
1131 
1132 /**
1133  * st_widget_set_style_class_name:
1134  * @actor: a #StWidget
1135  * @style_class_list: (allow-none): a new style class list string
1136  *
1137  * Set the style class name list. @style_class_list can either be
1138  * %NULL, for no classes, or a space-separated list of style class
1139  * names. See also st_widget_add_style_class_name() and
1140  * st_widget_remove_style_class_name().
1141  */
1142 void
1143 st_widget_set_style_class_name (StWidget    *actor,
1144                                 const gchar *style_class_list)
1145 {
1146   g_return_if_fail (ST_IS_WIDGET (actor));
1147 
1148   if (set_class_list (&actor->priv->style_class, style_class_list))
1149     {
1150       st_widget_style_changed (actor);
1151       g_object_notify (G_OBJECT (actor), "style-class");
1152     }
1153 }
1154 
1155 /**
1156  * st_widget_add_style_class_name:
1157  * @actor: a #StWidget
1158  * @style_class: a style class name string
1159  *
1160  * Adds @style_class to @actor's style class name list, if it is not
1161  * already present.
1162  */
1163 void
1164 st_widget_add_style_class_name (StWidget    *actor,
1165                                 const gchar *style_class)
1166 {
1167   g_return_if_fail (ST_IS_WIDGET (actor));
1168   g_return_if_fail (style_class != NULL);
1169 
1170   if (add_class_name (&actor->priv->style_class, style_class))
1171     {
1172       st_widget_style_changed (actor);
1173       g_object_notify (G_OBJECT (actor), "style-class");
1174     }
1175 }
1176 
1177 /**
1178  * st_widget_remove_style_class_name:
1179  * @actor: a #StWidget
1180  * @style_class: a style class name string
1181  *
1182  * Removes @style_class from @actor's style class name, if it is
1183  * present.
1184  */
1185 void
1186 st_widget_remove_style_class_name (StWidget    *actor,
1187                                    const gchar *style_class)
1188 {
1189   g_return_if_fail (ST_IS_WIDGET (actor));
1190   g_return_if_fail (style_class != NULL);
1191 
1192   if (remove_class_name (&actor->priv->style_class, style_class))
1193     {
1194       st_widget_style_changed (actor);
1195       g_object_notify (G_OBJECT (actor), "style-class");
1196     }
1197 }
1198 
1199 /**
1200  * st_widget_get_style_class_name:
1201  * @actor: a #StWidget
1202  *
1203  * Get the current style class name
1204  *
1205  * Returns: the class name string. The string is owned by the #StWidget and
1206  * should not be modified or freed.
1207  */
1208 const gchar*
1209 st_widget_get_style_class_name (StWidget *actor)
1210 {
1211   g_return_val_if_fail (ST_IS_WIDGET (actor), NULL);
1212 
1213   return actor->priv->style_class;
1214 }
1215 
1216 /**
1217  * st_widget_has_style_class_name:
1218  * @actor: a #StWidget
1219  * @style_class: a style class string
1220  *
1221  * Tests if @actor's style class list includes @style_class.
1222  *
1223  * Returns: whether or not @actor's style class list includes
1224  * @style_class.
1225  */
1226 gboolean
1227 st_widget_has_style_class_name (StWidget    *actor,
1228                                 const gchar *style_class)
1229 {
1230   g_return_val_if_fail (ST_IS_WIDGET (actor), FALSE);
1231 
1232   return find_class_name (actor->priv->style_class, style_class) != NULL;
1233 }
1234 
1235 /**
1236  * st_widget_get_style_pseudo_class:
1237  * @actor: a #StWidget
1238  *
1239  * Get the current style pseudo class list.
1240  *
1241  * Note that an actor can have multiple pseudo classes; if you just
1242  * want to test for the presence of a specific pseudo class, use
1243  * st_widget_has_style_pseudo_class().
1244  *
1245  * Returns: the pseudo class list string. The string is owned by the
1246  * #StWidget and should not be modified or freed.
1247  */
1248 const gchar*
1249 st_widget_get_style_pseudo_class (StWidget *actor)
1250 {
1251   g_return_val_if_fail (ST_IS_WIDGET (actor), NULL);
1252 
1253   return actor->priv->pseudo_class;
1254 }
1255 
1256 /**
1257  * st_widget_has_style_pseudo_class:
1258  * @actor: a #StWidget
1259  * @pseudo_class: a pseudo class string
1260  *
1261  * Tests if @actor's pseudo class list includes @pseudo_class.
1262  *
1263  * Returns: whether or not @actor's pseudo class list includes
1264  * @pseudo_class.
1265  */
1266 gboolean
1267 st_widget_has_style_pseudo_class (StWidget    *actor,
1268                                   const gchar *pseudo_class)
1269 {
1270   g_return_val_if_fail (ST_IS_WIDGET (actor), FALSE);
1271 
1272   return find_class_name (actor->priv->pseudo_class, pseudo_class) != NULL;
1273 }
1274 
1275 /**
1276  * st_widget_set_style_pseudo_class:
1277  * @actor: a #StWidget
1278  * @pseudo_class_list: (allow-none): a new pseudo class list string
1279  *
1280  * Set the style pseudo class list. @pseudo_class_list can either be
1281  * %NULL, for no classes, or a space-separated list of pseudo class
1282  * names. See also st_widget_add_style_pseudo_class() and
1283  * st_widget_remove_style_pseudo_class().
1284  */
1285 void
1286 st_widget_set_style_pseudo_class (StWidget    *actor,
1287                                   const gchar *pseudo_class_list)
1288 {
1289   g_return_if_fail (ST_IS_WIDGET (actor));
1290 
1291   if (set_class_list (&actor->priv->pseudo_class, pseudo_class_list))
1292     {
1293       st_widget_style_changed (actor);
1294       g_object_notify (G_OBJECT (actor), "pseudo-class");
1295     }
1296 }
1297 
1298 /**
1299  * st_widget_add_style_pseudo_class:
1300  * @actor: a #StWidget
1301  * @pseudo_class: a pseudo class string
1302  *
1303  * Adds @pseudo_class to @actor's pseudo class list, if it is not
1304  * already present.
1305  */
1306 void
1307 st_widget_add_style_pseudo_class (StWidget    *actor,
1308                                   const gchar *pseudo_class)
1309 {
1310   g_return_if_fail (ST_IS_WIDGET (actor));
1311   g_return_if_fail (pseudo_class != NULL);
1312 
1313   if (add_class_name (&actor->priv->pseudo_class, pseudo_class))
1314     {
1315       st_widget_style_changed (actor);
1316       g_object_notify (G_OBJECT (actor), "pseudo-class");
1317     }
1318 }
1319 
1320 /**
1321  * st_widget_remove_style_pseudo_class:
1322  * @actor: a #StWidget
1323  * @pseudo_class: a pseudo class string
1324  *
1325  * Removes @pseudo_class from @actor's pseudo class, if it is present.
1326  */
1327 void
1328 st_widget_remove_style_pseudo_class (StWidget    *actor,
1329                                      const gchar *pseudo_class)
1330 {
1331   g_return_if_fail (ST_IS_WIDGET (actor));
1332   g_return_if_fail (pseudo_class != NULL);
1333 
1334   if (remove_class_name (&actor->priv->pseudo_class, pseudo_class))
1335     {
1336       st_widget_style_changed (actor);
1337       g_object_notify (G_OBJECT (actor), "pseudo-class");
1338     }
1339 }
1340 
1341 /**
1342  * st_widget_set_style:
1343  * @actor: a #StWidget
1344  * @style: (allow-none): a inline style string, or %NULL
1345  *
1346  * Set the inline style string for this widget. The inline style string is an
1347  * optional ';'-separated list of CSS properties that override the style as
1348  * determined from the stylesheets of the current theme.
1349  */
1350 void
1351 st_widget_set_style (StWidget  *actor,
1352                      const gchar *style)
1353 {
1354   StWidgetPrivate *priv;
1355 
1356   g_return_if_fail (ST_IS_WIDGET (actor));
1357 
1358   priv = actor->priv;
1359 
1360   if (g_strcmp0 (style, priv->inline_style))
1361     {
1362       g_free (priv->inline_style);
1363       priv->inline_style = g_strdup (style);
1364 
1365       st_widget_style_changed (actor);
1366 
1367       g_object_notify (G_OBJECT (actor), "style");
1368     }
1369 }
1370 
1371 /**
1372  * st_widget_get_style:
1373  * @actor: a #StWidget
1374  *
1375  * Get the current inline style string. See st_widget_set_style().
1376  *
1377  * Returns: The inline style string, or %NULL. The string is owned by the
1378  * #StWidget and should not be modified or freed.
1379  */
1380 const gchar*
1381 st_widget_get_style (StWidget *actor)
1382 {
1383   g_return_val_if_fail (ST_IS_WIDGET (actor), NULL);
1384 
1385   return actor->priv->inline_style;
1386 }
1387 
1388 static void
1389 st_widget_name_notify (StWidget   *widget,
1390                        GParamSpec *pspec,
1391                        gpointer    data)
1392 {
1393   st_widget_style_changed (widget);
1394 }
1395 
1396 static void
1397 st_widget_reactive_notify (StWidget   *widget,
1398                            GParamSpec *pspec,
1399                            gpointer    data)
1400 {
1401   if (clutter_actor_get_reactive (CLUTTER_ACTOR (widget)))
1402     st_widget_remove_style_pseudo_class (widget, "insensitive");
1403   else
1404     st_widget_add_style_pseudo_class (widget, "insensitive");
1405 }
1406 
1407 static void
1408 st_widget_first_child_notify (StWidget   *widget,
1409                               GParamSpec *pspec,
1410                               gpointer    data)
1411 {
1412   ClutterActor *first_child;
1413 
1414   if (widget->priv->prev_first_child != NULL)
1415     {
1416       st_widget_remove_style_pseudo_class (widget->priv->prev_first_child, "first-child");
1417       g_clear_object (&widget->priv->prev_first_child);
1418     }
1419 
1420   first_child = clutter_actor_get_first_child (CLUTTER_ACTOR (widget));
1421 
1422   if (first_child == NULL)
1423     return;
1424 
1425   if (ST_IS_WIDGET (first_child))
1426     {
1427       st_widget_add_style_pseudo_class (ST_WIDGET (first_child), "first-child");
1428       widget->priv->prev_first_child = g_object_ref (ST_WIDGET (first_child));
1429     }
1430 }
1431 
1432 static void
1433 st_widget_last_child_notify (StWidget   *widget,
1434                              GParamSpec *pspec,
1435                              gpointer    data)
1436 {
1437   ClutterActor *last_child;
1438 
1439   if (widget->priv->prev_last_child != NULL)
1440     {
1441       st_widget_remove_style_pseudo_class (widget->priv->prev_last_child, "last-child");
1442       g_clear_object (&widget->priv->prev_last_child);
1443     }
1444 
1445   last_child = clutter_actor_get_last_child (CLUTTER_ACTOR (widget));
1446 
1447   if (last_child == NULL)
1448     return;
1449 
1450   if (ST_IS_WIDGET (last_child))
1451     {
1452       st_widget_add_style_pseudo_class (ST_WIDGET (last_child), "last-child");
1453       widget->priv->prev_last_child = g_object_ref (ST_WIDGET (last_child));
1454     }
1455 }
1456 
1457 static void
1458 st_widget_init (StWidget *actor)
1459 {
1460   StWidgetPrivate *priv;
1461 
1462   actor->priv = priv = ST_WIDGET_GET_PRIVATE (actor);
1463   priv->is_stylable = TRUE;
1464   priv->transition_animation = NULL;
1465   priv->local_state_set = atk_state_set_new ();
1466 
1467   /* connect style changed */
1468   g_signal_connect (actor, "notify::name", G_CALLBACK (st_widget_name_notify), NULL);
1469   g_signal_connect (actor, "notify::reactive", G_CALLBACK (st_widget_reactive_notify), NULL);
1470 
1471   g_signal_connect (actor, "notify::first-child", G_CALLBACK (st_widget_first_child_notify), NULL);
1472   g_signal_connect (actor, "notify::last-child", G_CALLBACK (st_widget_last_child_notify), NULL);
1473 }
1474 
1475 static void
1476 on_transition_completed (StThemeNodeTransition *transition,
1477                          StWidget              *widget)
1478 {
1479   st_widget_remove_transition (widget);
1480 }
1481 
1482 static void
1483 st_widget_recompute_style (StWidget    *widget,
1484                            StThemeNode *old_theme_node)
1485 {
1486   StThemeNode *new_theme_node = st_widget_get_theme_node (widget);
1487   int transition_duration;
1488   gboolean paint_equal;
1489 
1490   if (!old_theme_node ||
1491       !st_theme_node_geometry_equal (old_theme_node, new_theme_node))
1492     clutter_actor_queue_relayout ((ClutterActor *) widget);
1493 
1494   transition_duration = st_theme_node_get_transition_duration (new_theme_node);
1495 
1496   paint_equal = old_theme_node && st_theme_node_paint_equal (old_theme_node, new_theme_node);
1497 
1498   if (paint_equal)
1499     st_theme_node_copy_cached_paint_state (new_theme_node, old_theme_node);
1500 
1501   if (transition_duration > 0)
1502     {
1503       if (widget->priv->transition_animation != NULL)
1504         {
1505           st_theme_node_transition_update (widget->priv->transition_animation,
1506                                            new_theme_node);
1507         }
1508       else if (old_theme_node && !paint_equal)
1509         {
1510           /* Since our transitions are only of the painting done by StThemeNode, we
1511            * only want to start a transition when what is painted changes; if
1512            * other visual aspects like the foreground color of a label change,
1513            * we can't animate that anyways.
1514            */
1515 
1516           widget->priv->transition_animation =
1517             st_theme_node_transition_new (old_theme_node,
1518                                           new_theme_node,
1519                                           transition_duration);
1520 
1521           g_signal_connect (widget->priv->transition_animation, "completed",
1522                             G_CALLBACK (on_transition_completed), widget);
1523           g_signal_connect_swapped (widget->priv->transition_animation,
1524                                     "new-frame",
1525                                     G_CALLBACK (clutter_actor_queue_redraw),
1526                                     widget);
1527         }
1528     }
1529   else if (widget->priv->transition_animation)
1530     {
1531       st_widget_remove_transition (widget);
1532     }
1533 
1534   g_signal_emit (widget, signals[STYLE_CHANGED], 0);
1535   widget->priv->is_style_dirty = FALSE;
1536 }
1537 
1538 /**
1539  * st_widget_ensure_style:
1540  * @widget: A #StWidget
1541  *
1542  * Ensures that @widget has read its style information.
1543  *
1544  */
1545 void
1546 st_widget_ensure_style (StWidget *widget)
1547 {
1548   g_return_if_fail (ST_IS_WIDGET (widget));
1549 
1550   if (widget->priv->is_style_dirty)
1551     st_widget_recompute_style (widget, NULL);
1552 }
1553 
1554 /**
1555  * st_widget_set_track_hover:
1556  * @widget: A #StWidget
1557  * @track_hover: %TRUE if the widget should track the pointer hover state
1558  *
1559  * Enables hover tracking on the #StWidget.
1560  *
1561  * If hover tracking is enabled, and the widget is visible and
1562  * reactive, then @widget's #StWidget:hover property will be updated
1563  * automatically to reflect whether the pointer is in @widget (or one
1564  * of its children), and @widget's #StWidget:pseudo-class will have
1565  * the "hover" class added and removed from it accordingly.
1566  *
1567  * Note that currently it is not possible to correctly track the hover
1568  * state when another actor has a pointer grab. You can use
1569  * st_widget_sync_hover() to update the property manually in this
1570  * case.
1571  */
1572 void
1573 st_widget_set_track_hover (StWidget *widget,
1574                            gboolean  track_hover)
1575 {
1576   StWidgetPrivate *priv;
1577 
1578   g_return_if_fail (ST_IS_WIDGET (widget));
1579 
1580   priv = widget->priv;
1581 
1582   if (priv->track_hover != track_hover)
1583     {
1584       priv->track_hover = track_hover;
1585       g_object_notify (G_OBJECT (widget), "track-hover");
1586 
1587       if (priv->track_hover)
1588         st_widget_sync_hover (widget);
1589       else
1590         st_widget_set_hover (widget, FALSE);
1591     }
1592 }
1593 
1594 /**
1595  * st_widget_get_track_hover:
1596  * @widget: A #StWidget
1597  *
1598  * Returns the current value of the track-hover property. See
1599  * st_widget_set_track_hover() for more information.
1600  *
1601  * Returns: current value of track-hover on @widget
1602  */
1603 gboolean
1604 st_widget_get_track_hover (StWidget *widget)
1605 {
1606   g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE);
1607 
1608   return widget->priv->track_hover;
1609 }
1610 
1611 /**
1612  * st_widget_set_hover:
1613  * @widget: A #StWidget
1614  * @hover: whether the pointer is hovering over the widget
1615  *
1616  * Sets @widget's hover property and adds or removes "hover" from its
1617  * pseudo class accordingly.
1618  *
1619  * If you have set #StWidget:track-hover, you should not need to call
1620  * this directly. You can call st_widget_sync_hover() if the hover
1621  * state might be out of sync due to another actor's pointer grab.
1622  */
1623 void
1624 st_widget_set_hover (StWidget *widget,
1625                      gboolean  hover)
1626 {
1627   StWidgetPrivate *priv;
1628 
1629   g_return_if_fail (ST_IS_WIDGET (widget));
1630 
1631   priv = widget->priv;
1632 
1633   if (priv->hover != hover)
1634     {
1635       priv->hover = hover;
1636       if (priv->hover)
1637         st_widget_add_style_pseudo_class (widget, "hover");
1638       else
1639         st_widget_remove_style_pseudo_class (widget, "hover");
1640       g_object_notify (G_OBJECT (widget), "hover");
1641     }
1642 }
1643 
1644 /**
1645  * st_widget_sync_hover:
1646  * @widget: A #StWidget
1647  *
1648  * Sets @widget's hover state according to the current pointer
1649  * position. This can be used to ensure that it is correct after
1650  * (or during) a pointer grab.
1651  */
1652 void
1653 st_widget_sync_hover (StWidget *widget)
1654 {
1655   ClutterDeviceManager *device_manager;
1656   ClutterInputDevice *pointer;
1657   ClutterActor *pointer_actor;
1658 
1659   device_manager = clutter_device_manager_get_default ();
1660   pointer = clutter_device_manager_get_core_device (device_manager,
1661                                                     CLUTTER_POINTER_DEVICE);
1662   pointer_actor = clutter_input_device_get_pointer_actor (pointer);
1663   if (pointer_actor)
1664     st_widget_set_hover (widget, clutter_actor_contains (CLUTTER_ACTOR (widget), pointer_actor));
1665   else
1666     st_widget_set_hover (widget, FALSE);
1667 }
1668 
1669 /**
1670  * st_widget_get_hover:
1671  * @widget: A #StWidget
1672  *
1673  * If #StWidget:track-hover is set, this returns whether the pointer
1674  * is currently over the widget.
1675  *
1676  * Returns: current value of hover on @widget
1677  */
1678 gboolean
1679 st_widget_get_hover (StWidget *widget)
1680 {
1681   g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE);
1682 
1683   return widget->priv->hover;
1684 }
1685 
1686 /**
1687  * st_widget_set_can_focus:
1688  * @widget: A #StWidget
1689  * @can_focus: %TRUE if the widget can receive keyboard focus
1690  *   via keyboard navigation
1691  *
1692  * Marks @widget as being able to receive keyboard focus via
1693  * keyboard navigation.
1694  */
1695 void
1696 st_widget_set_can_focus (StWidget *widget,
1697                          gboolean  can_focus)
1698 {
1699   StWidgetPrivate *priv;
1700 
1701   g_return_if_fail (ST_IS_WIDGET (widget));
1702 
1703   priv = widget->priv;
1704 
1705   if (priv->can_focus != can_focus)
1706     {
1707       priv->can_focus = can_focus;
1708       g_object_notify (G_OBJECT (widget), "can-focus");
1709     }
1710 }
1711 
1712 /**
1713  * st_widget_get_can_focus:
1714  * @widget: A #StWidget
1715  *
1716  * Returns the current value of the can-focus property. See
1717  * st_widget_set_can_focus() for more information.
1718  *
1719  * Returns: current value of can-focus on @widget
1720  */
1721 gboolean
1722 st_widget_get_can_focus (StWidget *widget)
1723 {
1724   g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE);
1725 
1726   return widget->priv->can_focus;
1727 }
1728 
1729 /* filter @children to contain only only actors that overlap @rbox
1730  * when moving in @direction. (Assuming no transformations.)
1731  */
1732 static GList *
1733 filter_by_position (GList            *children,
1734                     ClutterActorBox  *rbox,
1735                     GtkDirectionType  direction)
1736 {
1737   ClutterActorBox cbox;
1738   ClutterVertex abs_vertices[4];
1739   GList *l, *ret;
1740   ClutterActor *child;
1741 
1742   for (l = children, ret = NULL; l; l = l->next)
1743     {
1744       child = l->data;
1745       clutter_actor_get_abs_allocation_vertices (child, abs_vertices);
1746       clutter_actor_box_from_vertices (&cbox, abs_vertices);
1747 
1748       /* Filter out children if they are in the wrong direction from
1749        * @rbox, or if they don't overlap it. To account for floating-
1750        * point imprecision, an actor is "down" (etc.) from an another
1751        * actor even if it overlaps it by up to 0.1 pixels.
1752        */
1753       switch (direction)
1754         {
1755         case GTK_DIR_UP:
1756           if (cbox.y2 > rbox->y1 + 0.1)
1757             continue;
1758           break;
1759 
1760         case GTK_DIR_DOWN:
1761           if (cbox.y1 < rbox->y2 - 0.1)
1762             continue;
1763           break;
1764 
1765         case GTK_DIR_LEFT:
1766           if (cbox.x2 > rbox->x1 + 0.1)
1767             continue;
1768           break;
1769 
1770         case GTK_DIR_RIGHT:
1771           if (cbox.x1 < rbox->x2 - 0.1)
1772             continue;
1773           break;
1774 
1775         default:
1776           g_return_val_if_reached (NULL);
1777         }
1778 
1779       ret = g_list_prepend (ret, child);
1780     }
1781 
1782   g_list_free (children);
1783   return ret;
1784 }
1785 
1786 
1787 typedef struct {
1788   GtkDirectionType direction;
1789   ClutterActorBox box;
1790 } StWidgetChildSortData;
1791 
1792 static int
1793 sort_by_position (gconstpointer  a,
1794                   gconstpointer  b,
1795                   gpointer       user_data)
1796 {
1797   ClutterActor *actor_a = (ClutterActor *)a;
1798   ClutterActor *actor_b = (ClutterActor *)b;
1799   StWidgetChildSortData *sort_data = user_data;
1800   GtkDirectionType direction = sort_data->direction;
1801   ClutterActorBox abox, bbox;
1802   ClutterVertex abs_vertices[4];
1803   int ax, ay, bx, by;
1804   int cmp, fmid;
1805 
1806   /* Determine the relationship, relative to motion in @direction, of
1807    * the center points of the two actors. Eg, for %GTK_DIR_UP, we
1808    * return a negative number if @actor_a's center is below @actor_b's
1809    * center, and postive if vice versa, which will result in an
1810    * overall list sorted bottom-to-top.
1811    */
1812 
1813   clutter_actor_get_abs_allocation_vertices (actor_a, abs_vertices);
1814   clutter_actor_box_from_vertices (&abox, abs_vertices);
1815   ax = (int)(abox.x1 + abox.x2) / 2;
1816   ay = (int)(abox.y1 + abox.y2) / 2;
1817   clutter_actor_get_abs_allocation_vertices (actor_b, abs_vertices);
1818   clutter_actor_box_from_vertices (&bbox, abs_vertices);
1819   bx = (int)(bbox.x1 + bbox.x2) / 2;
1820   by = (int)(bbox.y1 + bbox.y2) / 2;
1821 
1822   switch (direction)
1823     {
1824     case GTK_DIR_UP:
1825       cmp = by - ay;
1826       break;
1827     case GTK_DIR_DOWN:
1828       cmp = ay - by;
1829       break;
1830     case GTK_DIR_LEFT:
1831       cmp = bx - ax;
1832       break;
1833     case GTK_DIR_RIGHT:
1834       cmp = ax - bx;
1835       break;
1836     default:
1837       g_return_val_if_reached (0);
1838     }
1839 
1840   if (cmp)
1841     return cmp;
1842 
1843   /* If two actors have the same center on the axis being sorted,
1844    * prefer the one that is closer to the center of the current focus
1845    * actor on the other axis. Eg, for %GTK_DIR_UP, prefer whichever
1846    * of @actor_a and @actor_b has a horizontal center closest to the
1847    * current focus actor's horizontal center.
1848    *
1849    * (This matches GTK's behavior.)
1850    */
1851   switch (direction)
1852     {
1853     case GTK_DIR_UP:
1854     case GTK_DIR_DOWN:
1855       fmid = (int)(sort_data->box.x1 + sort_data->box.x2) / 2;
1856       return abs (ax - fmid) - abs (bx - fmid);
1857     case GTK_DIR_LEFT:
1858     case GTK_DIR_RIGHT:
1859       fmid = (int)(sort_data->box.y1 + sort_data->box.y2) / 2;
1860       return abs (ay - fmid) - abs (by - fmid);
1861     default:
1862       g_return_val_if_reached (0);
1863     }
1864 }
1865 
1866 static gboolean
1867 st_widget_real_navigate_focus (StWidget         *widget,
1868                                ClutterActor     *from,
1869                                GtkDirectionType  direction)
1870 {
1871   ClutterActor *widget_actor, *focus_child;
1872   GList *children, *l;
1873 
1874   widget_actor = CLUTTER_ACTOR (widget);
1875   if (from == widget_actor)
1876     return FALSE;
1877 
1878   /* Figure out if @from is a descendant of @widget, and if so,
1879    * set @focus_child to the immediate child of @widget that
1880    * contains (or *is*) @from.
1881    */
1882   focus_child = from;
1883   while (focus_child && clutter_actor_get_parent (focus_child) != widget_actor)
1884     focus_child = clutter_actor_get_parent (focus_child);
1885 
1886   if (widget->priv->can_focus)
1887     {
1888       if (!focus_child)
1889         {
1890           if (CLUTTER_ACTOR_IS_MAPPED (widget_actor))
1891             {
1892               /* Accept focus from outside */
1893               clutter_actor_grab_key_focus (widget_actor);
1894               return TRUE;
1895             }
1896           else
1897             {
1898               /* Refuse to set focus on hidden actors */
1899               return FALSE;
1900             }
1901         }
1902       else
1903         {
1904           /* Yield focus from within: since @widget itself is
1905            * focusable we don't allow the focus to be navigated
1906            * within @widget.
1907            */
1908           return FALSE;
1909         }
1910     }
1911 
1912   /* See if we can navigate within @focus_child */
1913   if (focus_child && ST_IS_WIDGET (focus_child))
1914     {
1915       if (st_widget_navigate_focus (ST_WIDGET (focus_child), from, direction, FALSE))
1916         return TRUE;
1917     }
1918 
1919   children = st_widget_get_focus_chain (widget);
1920   if (direction == GTK_DIR_TAB_FORWARD ||
1921       direction == GTK_DIR_TAB_BACKWARD)
1922     {
1923       /* At this point we know that we want to navigate focus to one of
1924        * @widget's immediate children; the next one after @focus_child, or the
1925        * first one if @focus_child is %NULL. (With "next" and "first" being
1926        * determined by @direction.)
1927        */
1928       if (direction == GTK_DIR_TAB_BACKWARD)
1929         children = g_list_reverse (children);
1930 
1931       if (focus_child)
1932         {
1933           /* Remove focus_child and any earlier children */
1934           while (children && children->data != focus_child)
1935             children = g_list_delete_link (children, children);
1936           if (children)
1937             children = g_list_delete_link (children, children);
1938         }
1939     }
1940   else /* direction is an arrow key, not tab */
1941     {
1942       StWidgetChildSortData sort_data;
1943       ClutterVertex abs_vertices[4];
1944 
1945       /* Compute the allocation box of the previous focused actor. If there
1946        * was no previous focus, use the coordinates of the appropriate edge of
1947        * @widget.
1948        *
1949        * Note that all of this code assumes the actors are not
1950        * transformed (or at most, they are all scaled by the same
1951        * amount). If @widget or any of its children is rotated, or
1952        * any child is inconsistently scaled, then the focus chain will
1953        * probably be unpredictable.
1954        */
1955       if (from)
1956         {
1957           clutter_actor_get_abs_allocation_vertices (from, abs_vertices);
1958           clutter_actor_box_from_vertices (&sort_data.box, abs_vertices);
1959         }
1960       else
1961         {
1962           clutter_actor_get_abs_allocation_vertices (widget_actor, abs_vertices);
1963           clutter_actor_box_from_vertices (&sort_data.box, abs_vertices);
1964           switch (direction)
1965             {
1966             case GTK_DIR_UP:
1967               sort_data.box.y1 = sort_data.box.y2;
1968               break;
1969             case GTK_DIR_DOWN:
1970               sort_data.box.y2 = sort_data.box.y1;
1971               break;
1972             case GTK_DIR_LEFT:
1973               sort_data.box.x1 = sort_data.box.x2;
1974               break;
1975             case GTK_DIR_RIGHT:
1976               sort_data.box.x2 = sort_data.box.x1;
1977               break;
1978             default:
1979               g_warn_if_reached ();
1980             }
1981         }
1982       sort_data.direction = direction;
1983 
1984       if (from)
1985         children = filter_by_position (children, &sort_data.box, direction);
1986       if (children)
1987         children = g_list_sort_with_data (children, sort_by_position, &sort_data);
1988     }
1989 
1990   /* Now try each child in turn */
1991   for (l = children; l; l = l->next)
1992     {
1993       if (ST_IS_WIDGET (l->data))
1994         {
1995           if (st_widget_navigate_focus (l->data, from, direction, FALSE))
1996             {
1997               g_list_free (children);
1998               return TRUE;
1999             }
2000         }
2001     }
2002 
2003   g_list_free (children);
2004   return FALSE;
2005 }
2006 
2007 
2008 /**
2009  * st_widget_navigate_focus:
2010  * @widget: the "top level" container
2011  * @from: (allow-none): the actor that the focus is coming from
2012  * @direction: the direction focus is moving in
2013  * @wrap_around: whether focus should wrap around
2014  *
2015  * Tries to update the keyboard focus within @widget in response to a
2016  * keyboard event.
2017  *
2018  * If @from is a descendant of @widget, this attempts to move the
2019  * keyboard focus to the next descendant of @widget (in the order
2020  * implied by @direction) that has the #StWidget:can-focus property
2021  * set. If @from is %NULL, this attempts to focus either @widget
2022  * itself, or its first descendant in the order implied by
2023  * @direction. If @from is outside of @widget, it behaves as if it was
2024  * a descendant if @direction is one of the directional arrows and as
2025  * if it was %NULL otherwise.
2026  *
2027  * If a container type is marked #StWidget:can-focus, the expected
2028  * behavior is that it will only take up a single slot on the focus
2029  * chain as a whole, rather than allowing navigation between its child
2030  * actors (or having a distinction between itself being focused and
2031  * one of its children being focused).
2032  *
2033  * Some widget classes might have slightly different behavior from the
2034  * above, where that would make more sense.
2035  *
2036  * If @wrap_around is %TRUE and @from is a child of @widget, but the
2037  * widget has no further children that can accept the focus in the
2038  * given direction, then st_widget_navigate_focus() will try a second
2039  * time, using a %NULL @from, which should cause it to reset the focus
2040  * to the first available widget in the given direction.
2041  *
2042  * Return value: %TRUE if clutter_actor_grab_key_focus() has been
2043  * called on an actor. %FALSE if not.
2044  */
2045 gboolean
2046 st_widget_navigate_focus (StWidget         *widget,
2047                           ClutterActor     *from,
2048                           GtkDirectionType  direction,
2049                           gboolean          wrap_around)
2050 {
2051   g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE);
2052 
2053   if (ST_WIDGET_GET_CLASS (widget)->navigate_focus (widget, from, direction))
2054     return TRUE;
2055   if (wrap_around && from && clutter_actor_contains (CLUTTER_ACTOR (widget), from))
2056     return ST_WIDGET_GET_CLASS (widget)->navigate_focus (widget, NULL, direction);
2057   return FALSE;
2058 }
2059 
2060 static gboolean
2061 append_actor_text (GString      *desc,
2062                    ClutterActor *actor)
2063 {
2064   if (CLUTTER_IS_TEXT (actor))
2065     {
2066       g_string_append_printf (desc, " (\"%s\")",
2067                               clutter_text_get_text (CLUTTER_TEXT (actor)));
2068       return TRUE;
2069     }
2070   else if (ST_IS_LABEL (actor))
2071     {
2072       g_string_append_printf (desc, " (\"%s\")",
2073                               st_label_get_text (ST_LABEL (actor)));
2074       return TRUE;
2075     }
2076   else
2077     return FALSE;
2078 }
2079 
2080 /**
2081  * st_describe_actor:
2082  * @actor: a #ClutterActor
2083  *
2084  * Creates a string describing @actor, for use in debugging. This
2085  * includes the class name and actor name (if any), plus if @actor
2086  * is an #StWidget, its style class and pseudo class names.
2087  *
2088  * Return value: the debug name.
2089  */
2090 char *
2091 st_describe_actor (ClutterActor *actor)
2092 {
2093   GString *desc;
2094   const char *name;
2095   int i;
2096 
2097   if (!actor)
2098     return g_strdup ("[null]");
2099 
2100   desc = g_string_new (NULL);
2101   g_string_append_printf (desc, "[%p %s", actor,
2102                           G_OBJECT_TYPE_NAME (actor));
2103 
2104   if (ST_IS_WIDGET (actor))
2105     {
2106       const char *style_class = st_widget_get_style_class_name (ST_WIDGET (actor));
2107       const char *pseudo_class = st_widget_get_style_pseudo_class (ST_WIDGET (actor));
2108       char **classes;
2109 
2110       if (style_class)
2111         {
2112           classes = g_strsplit (style_class, ",", -1);
2113           for (i = 0; classes[i]; i++)
2114             {
2115               g_strchug (classes[i]);
2116               g_string_append_printf (desc, ".%s", classes[i]);
2117             }
2118           g_strfreev (classes);
2119         }
2120 
2121       if (pseudo_class)
2122         {
2123           classes = g_strsplit (pseudo_class, ",", -1);
2124           for (i = 0; classes[i]; i++)
2125             {
2126               g_strchug (classes[i]);
2127               g_string_append_printf (desc, ":%s", classes[i]);
2128             }
2129           g_strfreev (classes);
2130         }
2131     }
2132 
2133   name = clutter_actor_get_name (actor);
2134   if (name)
2135     g_string_append_printf (desc, " \"%s\"", name);
2136 
2137   if (!append_actor_text (desc, actor))
2138     {
2139       GList *children, *l;
2140 
2141       /* Do a limited search of @actor's children looking for a label */
2142       children = clutter_actor_get_children (actor);
2143       for (l = children, i = 0; l && i < 20; l = l->next, i++)
2144         {
2145           if (append_actor_text (desc, l->data))
2146             break;
2147           children = g_list_concat (children, clutter_actor_get_children (l->data));
2148         }
2149       g_list_free (children);
2150     }
2151 
2152   g_string_append_c (desc, ']');
2153 
2154   return g_string_free (desc, FALSE);
2155 }
2156 
2157 /**
2158  * st_set_slow_down_factor:
2159  * @factor: new slow-down factor
2160  *
2161  * Set a global factor applied to all animation durations
2162  */
2163 void
2164 st_set_slow_down_factor (gfloat factor)
2165 {
2166   st_slow_down_factor = factor;
2167 }
2168 
2169 /**
2170  * st_get_slow_down_factor:
2171  *
2172  * Returns: the global factor applied to all animation durations
2173  */
2174 gfloat
2175 st_get_slow_down_factor ()
2176 {
2177   return st_slow_down_factor;
2178 }
2179 
2180 
2181 /**
2182  * st_widget_get_label_actor:
2183  * @widget: a #StWidget
2184  *
2185  * Gets the label that identifies @widget if it is defined
2186  *
2187  * Return value: (transfer none): the label that identifies the widget
2188  */
2189 ClutterActor *
2190 st_widget_get_label_actor (StWidget *widget)
2191 {
2192   g_return_val_if_fail (ST_IS_WIDGET (widget), NULL);
2193 
2194   return widget->priv->label_actor;
2195 }
2196 
2197 /**
2198  * st_widget_set_label_actor:
2199  * @widget: a #StWidget
2200  * @label: a #ClutterActor
2201  *
2202  * Sets @label as the #ClutterActor that identifies (labels)
2203  * @widget. @label can be %NULL to indicate that @widget is not
2204  * labelled any more
2205  */
2206 
2207 void
2208 st_widget_set_label_actor (StWidget     *widget,
2209                            ClutterActor *label)
2210 {
2211   g_return_if_fail (ST_IS_WIDGET (widget));
2212 
2213   if (widget->priv->label_actor != label)
2214     {
2215       if (widget->priv->label_actor)
2216         g_object_unref (widget->priv->label_actor);
2217 
2218       if (label != NULL)
2219         widget->priv->label_actor = g_object_ref (label);
2220       else
2221         widget->priv->label_actor = NULL;
2222 
2223       g_object_notify (G_OBJECT (widget), "label-actor");
2224     }
2225 }
2226 
2227 /**
2228  * st_widget_set_accessible_name:
2229  * @widget: widget to set the accessible name for
2230  * @name: (allow-none): a character string to be set as the accessible name
2231  *
2232  * This method sets @name as the accessible name for @widget.
2233  *
2234  * Usually you will have no need to set the accessible name for an
2235  * object, as usually there is a label for most of the interface
2236  * elements. So in general it is better to just use
2237  * @st_widget_set_label_actor. This method is only required when you
2238  * need to set an accessible name and there is no available label
2239  * object.
2240  *
2241  */
2242 void
2243 st_widget_set_accessible_name (StWidget    *widget,
2244                                const gchar *name)
2245 {
2246   g_return_if_fail (ST_IS_WIDGET (widget));
2247 
2248   if (widget->priv->accessible_name != NULL)
2249     g_free (widget->priv->accessible_name);
2250 
2251   widget->priv->accessible_name = g_strdup (name);
2252   g_object_notify (G_OBJECT (widget), "accessible-name");
2253 }
2254 
2255 /**
2256  * st_widget_get_accessible_name:
2257  * @widget: widget to get the accessible name for
2258  *
2259  * Gets the accessible name for this widget. See
2260  * st_widget_set_accessible_name() for more information.
2261  *
2262  * Return value: a character string representing the accessible name
2263  * of the widget.
2264  */
2265 const gchar *
2266 st_widget_get_accessible_name (StWidget    *widget)
2267 {
2268   g_return_val_if_fail (ST_IS_WIDGET (widget), NULL);
2269 
2270   return widget->priv->accessible_name;
2271 }
2272 
2273 /**
2274  * st_widget_set_accessible_role:
2275  * @widget: widget to set the accessible role for
2276  * @role: The role to use
2277  *
2278  * This method sets @role as the accessible role for @widget. This
2279  * role describes what kind of user interface element @widget is and
2280  * is provided so that assistive technologies know how to present
2281  * @widget to the user.
2282  *
2283  * Usually you will have no need to set the accessible role for an
2284  * object, as this information is extracted from the context of the
2285  * object (ie: a #StButton has by default a push button role). This
2286  * method is only required when you need to redefine the role
2287  * currently associated with the widget, for instance if it is being
2288  * used in an unusual way (ie: a #StButton used as a togglebutton), or
2289  * if a generic object is used directly (ie: a container as a menu
2290  * item).
2291  *
2292  * If @role is #ATK_ROLE_INVALID, the role will not be changed
2293  * and the accessible's default role will be used instead.
2294  */
2295 void
2296 st_widget_set_accessible_role (StWidget *widget,
2297                                AtkRole   role)
2298 {
2299   g_return_if_fail (ST_IS_WIDGET (widget));
2300 
2301   widget->priv->accessible_role = role;
2302 
2303   g_object_notify (G_OBJECT (widget), "accessible-role");
2304 }
2305 
2306 
2307 /**
2308  * st_widget_get_accessible_role:
2309  * @widget: widget to get the accessible role for
2310  *
2311  * Gets the #AtkRole for this widget. See
2312  * st_widget_set_accessible_role() for more information.
2313  *
2314  * Return value: accessible #AtkRole for this widget
2315  */
2316 AtkRole
2317 st_widget_get_accessible_role (StWidget *widget)
2318 {
2319   AtkObject *accessible = NULL;
2320   AtkRole role = ATK_ROLE_INVALID;
2321 
2322   g_return_val_if_fail (ST_IS_WIDGET (widget), ATK_ROLE_INVALID);
2323 
2324   if (widget->priv->accessible_role != ATK_ROLE_INVALID)
2325     role = widget->priv->accessible_role;
2326   else if (widget->priv->accessible != NULL)
2327     role = atk_object_get_role (accessible);
2328 
2329   return role;
2330 }
2331 
2332 static void
2333 notify_accessible_state_change (StWidget     *widget,
2334                                 AtkStateType  state,
2335                                 gboolean      value)
2336 {
2337   if (widget->priv->accessible != NULL)
2338     atk_object_notify_state_change (widget->priv->accessible, state, value);
2339 }
2340 
2341 /**
2342  * st_widget_add_accessible_state:
2343  * @widget: A #StWidget
2344  * @state: #AtkStateType state to add
2345  *
2346  * This method adds @state as one of the accessible states for
2347  * @widget. The list of states of a widget describes the current state
2348  * of user interface element @widget and is provided so that assistive
2349  * technologies know how to present @widget to the user.
2350  *
2351  * Usually you will have no need to add accessible states for an
2352  * object, as the accessible object can extract most of the states
2353  * from the object itself (ie: a #StButton knows when it is pressed).
2354  * This method is only required when one cannot extract the
2355  * information automatically from the object itself (i.e.: a generic
2356  * container used as a toggle menu item will not automatically include
2357  * the toggled state).
2358  *
2359  */
2360 void
2361 st_widget_add_accessible_state (StWidget    *widget,
2362                                 AtkStateType state)
2363 {
2364   g_return_if_fail (ST_IS_WIDGET (widget));
2365 
2366   if (atk_state_set_add_state (widget->priv->local_state_set, state))
2367     notify_accessible_state_change (widget, state, TRUE);
2368 }
2369 
2370 /**
2371  * st_widget_remove_accessible_state:
2372  * @widget: A #StWidget
2373  * @state: #AtkState state to remove
2374  *
2375  * This method removes @state as on of the accessible states for
2376  * @widget. See st_widget_add_accessible_state() for more information.
2377  *
2378  */
2379 void
2380 st_widget_remove_accessible_state (StWidget    *widget,
2381                                    AtkStateType state)
2382 {
2383   g_return_if_fail (ST_IS_WIDGET (widget));
2384 
2385   if (atk_state_set_remove_state (widget->priv->local_state_set, state))
2386     notify_accessible_state_change (widget, state, FALSE);
2387 }
2388 
2389 /******************************************************************************/
2390 /*************************** ACCESSIBILITY SUPPORT ****************************/
2391 /******************************************************************************/
2392 
2393 /* GObject */
2394 
2395 static void st_widget_accessible_class_init (StWidgetAccessibleClass *klass);
2396 static void st_widget_accessible_init       (StWidgetAccessible *widget);
2397 static void st_widget_accessible_dispose    (GObject *gobject);
2398 
2399 /* AtkObject */
2400 static AtkStateSet *st_widget_accessible_ref_state_set (AtkObject *obj);
2401 static void         st_widget_accessible_initialize    (AtkObject *obj,
2402                                                         gpointer   data);
2403 static AtkRole      st_widget_accessible_get_role      (AtkObject *obj);
2404 
2405 /* Private methods */
2406 static void on_pseudo_class_notify (GObject    *gobject,
2407                                     GParamSpec *pspec,
2408                                     gpointer    data);
2409 static void on_can_focus_notify    (GObject    *gobject,
2410                                     GParamSpec *pspec,
2411                                     gpointer    data);
2412 static void on_label_notify        (GObject    *gobject,
2413                                     GParamSpec *pspec,
2414                                     gpointer    data);
2415 static void check_pseudo_class     (StWidgetAccessible *self,
2416                                     StWidget *widget);
2417 static void check_labels           (StWidgetAccessible *self,
2418                                     StWidget *widget);
2419 
2420 G_DEFINE_TYPE (StWidgetAccessible, st_widget_accessible, CALLY_TYPE_ACTOR)
2421 
2422 #define ST_WIDGET_ACCESSIBLE_GET_PRIVATE(obj) \
2423   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_WIDGET_ACCESSIBLE, \
2424                                 StWidgetAccessiblePrivate))
2425 
2426 struct _StWidgetAccessiblePrivate
2427 {
2428   /* Cached values (used to avoid extra notifications) */
2429   gboolean selected;
2430   gboolean checked;
2431 
2432   /* The current_label. Right now there are the proper atk
2433    * relationships between this object and the label
2434    */
2435   AtkObject *current_label;
2436 };
2437 
2438 
2439 static AtkObject *
2440 st_widget_get_accessible (ClutterActor *actor)
2441 {
2442   StWidget *widget = NULL;
2443 
2444   g_return_val_if_fail (ST_IS_WIDGET (actor), NULL);
2445 
2446   widget = ST_WIDGET (actor);
2447 
2448   if (widget->priv->accessible == NULL)
2449     {
2450       widget->priv->accessible =
2451         g_object_new (ST_WIDGET_GET_CLASS (widget)->get_accessible_type (),
2452                       NULL);
2453 
2454       atk_object_initialize (widget->priv->accessible, actor);
2455     }
2456 
2457   return widget->priv->accessible;
2458 }
2459 
2460 static const gchar *
2461 st_widget_accessible_get_name (AtkObject *obj)
2462 {
2463   const gchar* name = NULL;
2464 
2465   g_return_val_if_fail (ST_IS_WIDGET_ACCESSIBLE (obj), NULL);
2466 
2467   name = ATK_OBJECT_CLASS (st_widget_accessible_parent_class)->get_name (obj);
2468   if (name == NULL)
2469     {
2470       StWidget *widget = NULL;
2471 
2472       widget = ST_WIDGET (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj)));
2473 
2474       if (widget == NULL)
2475         name = NULL;
2476       else
2477         name = widget->priv->accessible_name;
2478     }
2479 
2480   return name;
2481 }
2482 
2483 static void
2484 st_widget_accessible_class_init (StWidgetAccessibleClass *klass)
2485 {
2486   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
2487   AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
2488 
2489   gobject_class->dispose = st_widget_accessible_dispose;
2490 
2491   atk_class->ref_state_set = st_widget_accessible_ref_state_set;
2492   atk_class->initialize = st_widget_accessible_initialize;
2493   atk_class->get_role = st_widget_accessible_get_role;
2494   atk_class->get_name = st_widget_accessible_get_name;
2495 
2496   g_type_class_add_private (gobject_class, sizeof (StWidgetAccessiblePrivate));
2497 }
2498 
2499 static void
2500 st_widget_accessible_init (StWidgetAccessible *self)
2501 {
2502   StWidgetAccessiblePrivate *priv = ST_WIDGET_ACCESSIBLE_GET_PRIVATE (self);
2503 
2504   self->priv = priv;
2505 }
2506 
2507 static void
2508 st_widget_accessible_dispose (GObject *gobject)
2509 {
2510   StWidgetAccessible *self = ST_WIDGET_ACCESSIBLE (gobject);
2511 
2512   if (self->priv->current_label)
2513     {
2514       g_object_unref (self->priv->current_label);
2515       self->priv->current_label = NULL;
2516     }
2517 
2518   G_OBJECT_CLASS (st_widget_accessible_parent_class)->dispose (gobject);
2519 }
2520 
2521 static void
2522 on_accessible_name_notify (GObject    *gobject,
2523                            GParamSpec *pspec,
2524                            AtkObject  *accessible)
2525 {
2526   g_object_notify (G_OBJECT (accessible), "accessible-name");
2527 }
2528 
2529 static void
2530 st_widget_accessible_initialize (AtkObject *obj,
2531                                  gpointer   data)
2532 {
2533   ATK_OBJECT_CLASS (st_widget_accessible_parent_class)->initialize (obj, data);
2534 
2535   g_signal_connect (data, "notify::pseudo-class",
2536                     G_CALLBACK (on_pseudo_class_notify),
2537                     obj);
2538 
2539   g_signal_connect (data, "notify::can-focus",
2540                     G_CALLBACK (on_can_focus_notify),
2541                     obj);
2542 
2543   g_signal_connect (data, "notify::label-actor",
2544                     G_CALLBACK (on_label_notify),
2545                     obj);
2546 
2547   g_signal_connect (data, "notify::accessible-name",
2548                     G_CALLBACK (on_accessible_name_notify),
2549                     obj);
2550 
2551   /* Check the cached selected state and notify the first selection.
2552    * Ie: it is required to ensure a first notification when Alt+Tab
2553    * popup appears
2554    */
2555   check_pseudo_class (ST_WIDGET_ACCESSIBLE (obj), ST_WIDGET (data));
2556   check_labels (ST_WIDGET_ACCESSIBLE (obj), ST_WIDGET (data));
2557 }
2558 
2559 static AtkStateSet *
2560 st_widget_accessible_ref_state_set (AtkObject *obj)
2561 {
2562   AtkStateSet *result = NULL;
2563   AtkStateSet *aux_set = NULL;
2564   ClutterActor *actor = NULL;
2565   StWidget *widget = NULL;
2566   StWidgetAccessible *self = NULL;
2567 
2568   result = ATK_OBJECT_CLASS (st_widget_accessible_parent_class)->ref_state_set (obj);
2569 
2570   actor = CLUTTER_ACTOR (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj)));
2571 
2572   if (actor == NULL) /* State is defunct */
2573     return result;
2574 
2575   widget = ST_WIDGET (actor);
2576   self = ST_WIDGET_ACCESSIBLE (obj);
2577 
2578   /* priv->selected should be properly updated on the
2579    * ATK_STATE_SELECTED notification callbacks
2580    */
2581   if (self->priv->selected)
2582     atk_state_set_add_state (result, ATK_STATE_SELECTED);
2583 
2584   if (self->priv->checked)
2585     atk_state_set_add_state (result, ATK_STATE_CHECKED);
2586 
2587   /* On clutter there isn't any tip to know if a actor is focusable or
2588    * not, anyone can receive the key_focus. For this reason
2589    * cally_actor sets any actor as FOCUSABLE. This is not the case on
2590    * St, where we have can_focus. But this means that we need to
2591    * remove the state FOCUSABLE if it is not focusable
2592    */
2593   if (st_widget_get_can_focus (widget))
2594     atk_state_set_add_state (result, ATK_STATE_FOCUSABLE);
2595   else
2596     atk_state_set_remove_state (result, ATK_STATE_FOCUSABLE);
2597 
2598   /* We add the states added externally if required */
2599   if (!atk_state_set_is_empty (widget->priv->local_state_set))
2600     {
2601       aux_set = atk_state_set_or_sets (result, widget->priv->local_state_set);
2602 
2603       g_object_unref (result); /* previous result will not be used */
2604       result = aux_set;
2605     }
2606 
2607   return result;
2608 }
2609 
2610 static AtkRole
2611 st_widget_accessible_get_role (AtkObject *obj)
2612 {
2613   StWidget *widget = NULL;
2614 
2615   g_return_val_if_fail (ST_IS_WIDGET_ACCESSIBLE (obj), ATK_ROLE_INVALID);
2616 
2617   widget = ST_WIDGET (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj)));
2618 
2619   if (widget == NULL)
2620     return ATK_ROLE_INVALID;
2621 
2622   if (widget->priv->accessible_role != ATK_ROLE_INVALID)
2623     return widget->priv->accessible_role;
2624 
2625   return ATK_OBJECT_CLASS (st_widget_accessible_parent_class)->get_role (obj);
2626 }
2627 
2628 static void
2629 on_pseudo_class_notify (GObject    *gobject,
2630                         GParamSpec *pspec,
2631                         gpointer    data)
2632 {
2633   check_pseudo_class (ST_WIDGET_ACCESSIBLE (data),
2634                       ST_WIDGET (gobject));
2635 }
2636 
2637 /*
2638  * In some cases the only way to check some states are checking the
2639  * pseudo-class. Like if the object is selected (see bug 637830) or if
2640  * the object is toggled. This method also notifies a state change if
2641  * the value is different to the one cached.
2642  *
2643  * We also assume that if the object uses that pseudo-class, it makes
2644  * sense to notify that state change. It would be possible to refine
2645  * that behaviour checking the role (ie: notify CHECKED changes only
2646  * for CHECK_BUTTON roles).
2647  *
2648  * In a ideal world we would have a more standard way to get the
2649  * state, like the widget-context (as in the case of
2650  * gtktreeview-cells), or something like the property "can-focus". But
2651  * for the moment this is enough, and we can update that in the future
2652  * if required.
2653  */
2654 static void
2655 check_pseudo_class (StWidgetAccessible *self,
2656                     StWidget *widget)
2657 {
2658   gboolean found = FALSE;
2659 
2660   found = st_widget_has_style_pseudo_class (widget,
2661                                             "selected");
2662 
2663   if (found != self->priv->selected)
2664     {
2665       self->priv->selected = found;
2666       atk_object_notify_state_change (ATK_OBJECT (self),
2667                                       ATK_STATE_SELECTED,
2668                                       found);
2669     }
2670 
2671   found = st_widget_has_style_pseudo_class (widget,
2672                                             "checked");
2673   if (found != self->priv->checked)
2674     {
2675       self->priv->checked = found;
2676       atk_object_notify_state_change (ATK_OBJECT (self),
2677                                       ATK_STATE_CHECKED,
2678                                       found);
2679     }
2680 }
2681 
2682 static void
2683 on_can_focus_notify (GObject    *gobject,
2684                      GParamSpec *pspec,
2685                      gpointer    data)
2686 {
2687   gboolean can_focus = st_widget_get_can_focus (ST_WIDGET (gobject));
2688 
2689   atk_object_notify_state_change (ATK_OBJECT (data),
2690                                   ATK_STATE_FOCUSABLE, can_focus);
2691 }
2692 
2693 static void
2694 on_label_notify (GObject    *gobject,
2695                  GParamSpec *pspec,
2696                  gpointer    data)
2697 {
2698   check_labels (ST_WIDGET_ACCESSIBLE (data), ST_WIDGET (gobject));
2699 }
2700 
2701 static void
2702 check_labels (StWidgetAccessible *widget_accessible,
2703               StWidget           *widget)
2704 {
2705   ClutterActor *label = NULL;
2706   AtkObject *label_accessible = NULL;
2707 
2708   /* We only call this method at startup, and when the label changes,
2709    * so it is fine to remove the previous relationships if we have the
2710    * current_label by default
2711    */
2712   if (widget_accessible->priv->current_label != NULL)
2713     {
2714       AtkObject *previous_label = widget_accessible->priv->current_label;
2715 
2716       atk_object_remove_relationship (ATK_OBJECT (widget_accessible),
2717                                       ATK_RELATION_LABELLED_BY,
2718                                       previous_label);
2719 
2720       atk_object_remove_relationship (previous_label,
2721                                       ATK_RELATION_LABEL_FOR,
2722                                       ATK_OBJECT (widget_accessible));
2723 
2724       g_object_unref (previous_label);
2725     }
2726 
2727   label = st_widget_get_label_actor (widget);
2728   if (label == NULL)
2729     {
2730       widget_accessible->priv->current_label = NULL;
2731     }
2732   else
2733     {
2734       label_accessible = clutter_actor_get_accessible (label);
2735       widget_accessible->priv->current_label = g_object_ref (label_accessible);
2736 
2737       atk_object_add_relationship (ATK_OBJECT (widget_accessible),
2738                                    ATK_RELATION_LABELLED_BY,
2739                                    label_accessible);
2740 
2741       atk_object_add_relationship (label_accessible,
2742                                    ATK_RELATION_LABEL_FOR,
2743                                    ATK_OBJECT (widget_accessible));
2744     }
2745 }
2746 
2747 /**
2748  * st_widget_get_focus_chain:
2749  * @widget: An #StWidget
2750  *
2751  * Gets a list of the focusable children of @widget, in "Tab"
2752  * order. By default, this returns all visible
2753  * (as in CLUTTER_ACTOR_IS_VISIBLE()) children of @widget.
2754  *
2755  * Returns: (element-type Clutter.Actor) (transfer container):
2756  *   @widget's focusable children
2757  */
2758 GList *
2759 st_widget_get_focus_chain (StWidget *widget)
2760 {
2761   return ST_WIDGET_GET_CLASS (widget)->get_focus_chain (widget);
2762 }
2763 
2764 /**
2765  * st_widget_clear_background_image:
2766  * @widget: An #StWidget
2767  *
2768  * Force a reload of the background-image property. Usually properties
2769  * are cached heavily to avoid unnecessary work on paint, this method
2770  * will force the cache to be recreated.
2771  */
2772 void
2773 st_widget_clear_background_image (StWidget *actor)
2774 {
2775   GFile *file;
2776   const char *path;
2777   char *uri;
2778 
2779   if (actor->priv->theme_node == NULL)
2780     return;
2781 
2782   path = st_theme_node_get_background_image (actor->priv->theme_node);
2783   if (path == NULL)
2784     return;
2785 
2786   file = g_file_new_for_path (path);
2787   uri = g_file_get_uri (file);
2788 
2789   st_texture_cache_clear_uri (st_texture_cache_get_default (), uri);
2790   st_theme_node_invalidate_paint_state (actor->priv->theme_node);
2791 
2792   if (CLUTTER_ACTOR_IS_MAPPED (CLUTTER_ACTOR (actor)))
2793     clutter_actor_queue_redraw (CLUTTER_ACTOR (actor));
2794 
2795   g_object_unref (file);
2796   g_free (uri);
2797 }