~mterry/ubuntu/natty/gnome-shell/wip

« back to all changes in this revision

Viewing changes to src/st/st-widget.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2009-10-12 22:44:00 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20091012224400-k91p42yvou07i525
Tags: 2.28.0-0ubuntu1
* New upstream version
* debian/control:
  - updated build requirement
* debian/patches/80_git_change_fix_alt_tab_ressource_usage.patch:
  - git change to fix ressources not being freed on alt-tab

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 *
 
8
 * This program is free software; you can redistribute it and/or modify it
 
9
 * under the terms and conditions of the GNU Lesser General Public License,
 
10
 * version 2.1, as published by the Free Software Foundation.
 
11
 *
 
12
 * This program is distributed in the hope it will be useful, but WITHOUT ANY
 
13
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 
14
 * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
 
15
 * more details.
 
16
 *
 
17
 * You should have received a copy of the GNU Lesser General Public License
 
18
 * along with this program; if not, write to the Free Software Foundation,
 
19
 * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 
20
 * Boston, MA 02111-1307, USA.
 
21
 *
 
22
 * Written by: Emmanuele Bassi <ebassi@openedhand.com>
 
23
 *             Thomas Wood <thomas@linux.intel.com>
 
24
 *
 
25
 */
 
26
 
 
27
#ifdef HAVE_CONFIG_H
 
28
#include "config.h"
 
29
#endif
 
30
 
 
31
#include <stdlib.h>
 
32
#include <string.h>
 
33
 
 
34
#include <clutter/clutter.h>
 
35
 
 
36
#include "st-widget.h"
 
37
 
 
38
#include "st-marshal.h"
 
39
#include "st-private.h"
 
40
#include "st-texture-cache.h"
 
41
#include "st-texture-frame.h"
 
42
#include "st-theme-context.h"
 
43
#include "st-tooltip.h"
 
44
 
 
45
#include <big/rectangle.h>
 
46
 
 
47
/*
 
48
 * Forward declaration for sake of StWidgetChild
 
49
 */
 
50
struct _StWidgetPrivate
 
51
{
 
52
  StTheme      *theme;
 
53
  StThemeNode  *theme_node;
 
54
  gchar        *pseudo_class;
 
55
  gchar        *style_class;
 
56
  gchar        *inline_style;
 
57
 
 
58
  ClutterActor *border_image;
 
59
  ClutterActor *background_image;
 
60
  ClutterColor  bg_color;
 
61
 
 
62
  gboolean      is_stylable : 1;
 
63
  gboolean      has_tooltip : 1;
 
64
  gboolean      is_style_dirty : 1;
 
65
  gboolean      draw_bg_color : 1;
 
66
 
 
67
  StTooltip    *tooltip;
 
68
};
 
69
 
 
70
/**
 
71
 * SECTION:st-widget
 
72
 * @short_description: Base class for stylable actors
 
73
 *
 
74
 * #StWidget is a simple abstract class on top of #ClutterActor. It
 
75
 * provides basic themeing properties.
 
76
 *
 
77
 * Actors in the St library should subclass #StWidget if they plan
 
78
 * to obey to a certain #StStyle.
 
79
 */
 
80
 
 
81
enum
 
82
{
 
83
  PROP_0,
 
84
 
 
85
  PROP_THEME,
 
86
  PROP_PSEUDO_CLASS,
 
87
  PROP_STYLE_CLASS,
 
88
  PROP_STYLE,
 
89
 
 
90
  PROP_STYLABLE,
 
91
 
 
92
  PROP_HAS_TOOLTIP,
 
93
  PROP_TOOLTIP_TEXT
 
94
};
 
95
 
 
96
enum
 
97
{
 
98
  STYLE_CHANGED,
 
99
 
 
100
  LAST_SIGNAL
 
101
};
 
102
 
 
103
static guint signals[LAST_SIGNAL] = { 0, };
 
104
 
 
105
G_DEFINE_ABSTRACT_TYPE (StWidget, st_widget, CLUTTER_TYPE_ACTOR);
 
106
 
 
107
#define ST_WIDGET_GET_PRIVATE(obj)    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_WIDGET, StWidgetPrivate))
 
108
 
 
109
static void st_widget_recompute_style (StWidget    *widget,
 
110
                                       StThemeNode *old_theme_node);
 
111
 
 
112
static void
 
113
st_widget_set_property (GObject      *gobject,
 
114
                        guint         prop_id,
 
115
                        const GValue *value,
 
116
                        GParamSpec   *pspec)
 
117
{
 
118
  StWidget *actor = ST_WIDGET (gobject);
 
119
 
 
120
  switch (prop_id)
 
121
    {
 
122
    case PROP_THEME:
 
123
      st_widget_set_theme (actor, g_value_get_object (value));
 
124
      break;
 
125
 
 
126
    case PROP_PSEUDO_CLASS:
 
127
      st_widget_set_style_pseudo_class (actor, g_value_get_string (value));
 
128
      break;
 
129
 
 
130
    case PROP_STYLE_CLASS:
 
131
      st_widget_set_style_class_name (actor, g_value_get_string (value));
 
132
      break;
 
133
 
 
134
    case PROP_STYLE:
 
135
      st_widget_set_style (actor, g_value_get_string (value));
 
136
      break;
 
137
 
 
138
    case PROP_STYLABLE:
 
139
      if (actor->priv->is_stylable != g_value_get_boolean (value))
 
140
        {
 
141
          actor->priv->is_stylable = g_value_get_boolean (value);
 
142
          clutter_actor_queue_relayout ((ClutterActor *) gobject);
 
143
        }
 
144
      break;
 
145
 
 
146
    case PROP_HAS_TOOLTIP:
 
147
      st_widget_set_has_tooltip (actor, g_value_get_boolean (value));
 
148
      break;
 
149
 
 
150
    case PROP_TOOLTIP_TEXT:
 
151
      st_widget_set_tooltip_text (actor, g_value_get_string (value));
 
152
      break;
 
153
 
 
154
    default:
 
155
      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
 
156
      break;
 
157
    }
 
158
}
 
159
 
 
160
static void
 
161
st_widget_get_property (GObject    *gobject,
 
162
                        guint       prop_id,
 
163
                        GValue     *value,
 
164
                        GParamSpec *pspec)
 
165
{
 
166
  StWidget *actor = ST_WIDGET (gobject);
 
167
  StWidgetPrivate *priv = actor->priv;
 
168
 
 
169
  switch (prop_id)
 
170
    {
 
171
    case PROP_THEME:
 
172
      g_value_set_object (value, priv->theme);
 
173
      break;
 
174
 
 
175
    case PROP_PSEUDO_CLASS:
 
176
      g_value_set_string (value, priv->pseudo_class);
 
177
      break;
 
178
 
 
179
    case PROP_STYLE_CLASS:
 
180
      g_value_set_string (value, priv->style_class);
 
181
      break;
 
182
 
 
183
    case PROP_STYLE:
 
184
      g_value_set_string (value, priv->inline_style);
 
185
      break;
 
186
 
 
187
    case PROP_STYLABLE:
 
188
      g_value_set_boolean (value, priv->is_stylable);
 
189
      break;
 
190
 
 
191
    case PROP_HAS_TOOLTIP:
 
192
      g_value_set_boolean (value, priv->has_tooltip);
 
193
      break;
 
194
 
 
195
    case PROP_TOOLTIP_TEXT:
 
196
      g_value_set_string (value, st_widget_get_tooltip_text (actor));
 
197
      break;
 
198
 
 
199
    default:
 
200
      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
 
201
      break;
 
202
    }
 
203
}
 
204
 
 
205
static void
 
206
st_widget_dispose (GObject *gobject)
 
207
{
 
208
  StWidget *actor = ST_WIDGET (gobject);
 
209
  StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
 
210
 
 
211
  if (priv->theme)
 
212
    {
 
213
      g_object_unref (priv->theme);
 
214
      priv->theme = NULL;
 
215
    }
 
216
 
 
217
  if (priv->border_image)
 
218
    {
 
219
      clutter_actor_unparent (priv->border_image);
 
220
      priv->border_image = NULL;
 
221
    }
 
222
 
 
223
  if (priv->tooltip)
 
224
    {
 
225
      ClutterContainer *parent;
 
226
      ClutterActor *tooltip = CLUTTER_ACTOR (priv->tooltip);
 
227
 
 
228
      /* this is just a little bit awkward because the tooltip is parented
 
229
       * on the stage, but we still want to "own" it */
 
230
      parent = CLUTTER_CONTAINER (clutter_actor_get_parent (tooltip));
 
231
 
 
232
      if (parent)
 
233
        clutter_container_remove_actor (parent, tooltip);
 
234
 
 
235
      priv->tooltip = NULL;
 
236
    }
 
237
 
 
238
  G_OBJECT_CLASS (st_widget_parent_class)->dispose (gobject);
 
239
}
 
240
 
 
241
static void
 
242
st_widget_finalize (GObject *gobject)
 
243
{
 
244
  StWidgetPrivate *priv = ST_WIDGET (gobject)->priv;
 
245
 
 
246
  g_free (priv->style_class);
 
247
  g_free (priv->pseudo_class);
 
248
 
 
249
  G_OBJECT_CLASS (st_widget_parent_class)->finalize (gobject);
 
250
}
 
251
 
 
252
static void
 
253
st_widget_allocate (ClutterActor          *actor,
 
254
                    const ClutterActorBox *box,
 
255
                    ClutterAllocationFlags flags)
 
256
{
 
257
  StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
 
258
  ClutterActorClass *klass;
 
259
  ClutterGeometry area;
 
260
  ClutterVertex in_v, out_v;
 
261
 
 
262
  klass = CLUTTER_ACTOR_CLASS (st_widget_parent_class);
 
263
  klass->allocate (actor, box, flags);
 
264
 
 
265
  /* update tooltip position */
 
266
  if (priv->tooltip)
 
267
    {
 
268
      in_v.x = in_v.y = in_v.z = 0;
 
269
      clutter_actor_apply_transform_to_point (actor, &in_v, &out_v);
 
270
      area.x = out_v.x;
 
271
      area.y = out_v.y;
 
272
 
 
273
      in_v.x = box->x2 - box->x1;
 
274
      in_v.y = box->y2 - box->y1;
 
275
      clutter_actor_apply_transform_to_point (actor, &in_v, &out_v);
 
276
      area.width = out_v.x - area.x;
 
277
      area.height = out_v.y - area.y;
 
278
 
 
279
      st_tooltip_set_tip_area (priv->tooltip, &area);
 
280
    }
 
281
 
 
282
 
 
283
 
 
284
  if (priv->border_image)
 
285
    {
 
286
      ClutterActorBox frame_box = {
 
287
        0,
 
288
        0,
 
289
        box->x2 - box->x1,
 
290
        box->y2 - box->y1
 
291
      };
 
292
 
 
293
      clutter_actor_allocate (CLUTTER_ACTOR (priv->border_image),
 
294
                              &frame_box,
 
295
                              flags);
 
296
    }
 
297
 
 
298
  if (priv->background_image)
 
299
    {
 
300
      ClutterActorBox frame_box = {
 
301
        0, 0, box->x2 - box->x1, box->y2 - box->y1
 
302
      };
 
303
      gfloat w, h;
 
304
 
 
305
      clutter_actor_get_size (CLUTTER_ACTOR (priv->background_image), &w, &h);
 
306
 
 
307
      /* scale the background into the allocated bounds */
 
308
      if (w > frame_box.x2 || h > frame_box.y2)
 
309
        {
 
310
          gint new_h, new_w, offset;
 
311
          gint box_w, box_h;
 
312
 
 
313
          box_w = (int) frame_box.x2;
 
314
          box_h = (int) frame_box.y2;
 
315
 
 
316
          /* scale to fit */
 
317
          new_h = (int)((h / w) * ((gfloat) box_w));
 
318
          new_w = (int)((w / h) * ((gfloat) box_h));
 
319
 
 
320
          if (new_h > box_h)
 
321
            {
 
322
              /* center for new width */
 
323
              offset = ((box_w) - new_w) * 0.5;
 
324
              frame_box.x1 = offset;
 
325
              frame_box.x2 = offset + new_w;
 
326
 
 
327
              frame_box.y2 = box_h;
 
328
            }
 
329
          else
 
330
            {
 
331
              /* center for new height */
 
332
              offset = ((box_h) - new_h) * 0.5;
 
333
              frame_box.y1 = offset;
 
334
              frame_box.y2 = offset + new_h;
 
335
 
 
336
              frame_box.x2 = box_w;
 
337
            }
 
338
 
 
339
        }
 
340
      else
 
341
        {
 
342
          /* center the background on the widget */
 
343
          frame_box.x1 = (int)(((box->x2 - box->x1) / 2) - (w / 2));
 
344
          frame_box.y1 = (int)(((box->y2 - box->y1) / 2) - (h / 2));
 
345
          frame_box.x2 = frame_box.x1 + w;
 
346
          frame_box.y2 = frame_box.y1 + h;
 
347
        }
 
348
 
 
349
      clutter_actor_allocate (CLUTTER_ACTOR (priv->background_image),
 
350
                              &frame_box,
 
351
                              flags);
 
352
    }
 
353
}
 
354
 
 
355
static void
 
356
st_widget_real_draw_background (StWidget *self)
 
357
{
 
358
  StWidgetPrivate *priv = self->priv;
 
359
 
 
360
  /* Default implementation just draws the background
 
361
   * colour and the image on top
 
362
   */
 
363
  if (priv->draw_bg_color)
 
364
    {
 
365
      ClutterActor *actor = CLUTTER_ACTOR (self);
 
366
      ClutterActorBox allocation = { 0, };
 
367
      ClutterColor bg_color = priv->bg_color;
 
368
      gfloat w, h;
 
369
 
 
370
      bg_color.alpha = clutter_actor_get_paint_opacity (actor)
 
371
                       * bg_color.alpha
 
372
                       / 255;
 
373
 
 
374
      clutter_actor_get_allocation_box (actor, &allocation);
 
375
 
 
376
      w = allocation.x2 - allocation.x1;
 
377
      h = allocation.y2 - allocation.y1;
 
378
 
 
379
      cogl_set_source_color4ub (bg_color.red,
 
380
                                bg_color.green,
 
381
                                bg_color.blue,
 
382
                                bg_color.alpha);
 
383
      cogl_rectangle (0, 0, w, h);
 
384
    }
 
385
 
 
386
  if (priv->border_image)
 
387
    clutter_actor_paint (priv->border_image);
 
388
}
 
389
 
 
390
static void
 
391
st_widget_paint (ClutterActor *self)
 
392
{
 
393
  StWidgetPrivate *priv = ST_WIDGET (self)->priv;
 
394
  StWidgetClass *klass = ST_WIDGET_GET_CLASS (self);
 
395
 
 
396
  klass->draw_background (ST_WIDGET (self));
 
397
 
 
398
  if (priv->background_image != NULL)
 
399
    clutter_actor_paint (priv->background_image);
 
400
}
 
401
 
 
402
static void
 
403
st_widget_parent_set (ClutterActor *widget,
 
404
                      ClutterActor *old_parent)
 
405
{
 
406
  ClutterActorClass *parent_class;
 
407
  ClutterActor *new_parent;
 
408
 
 
409
  parent_class = CLUTTER_ACTOR_CLASS (st_widget_parent_class);
 
410
  if (parent_class->parent_set)
 
411
    parent_class->parent_set (widget, old_parent);
 
412
 
 
413
  new_parent = clutter_actor_get_parent (widget);
 
414
 
 
415
  /* don't send the style changed signal if we no longer have a parent actor */
 
416
  if (new_parent)
 
417
    st_widget_style_changed (ST_WIDGET (widget));
 
418
}
 
419
 
 
420
static void
 
421
st_widget_map (ClutterActor *actor)
 
422
{
 
423
  StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
 
424
 
 
425
  CLUTTER_ACTOR_CLASS (st_widget_parent_class)->map (actor);
 
426
 
 
427
  st_widget_ensure_style ((StWidget*) actor);
 
428
 
 
429
  if (priv->border_image)
 
430
    clutter_actor_map (priv->border_image);
 
431
 
 
432
  if (priv->background_image)
 
433
    clutter_actor_map (priv->background_image);
 
434
 
 
435
  if (priv->tooltip)
 
436
    clutter_actor_map ((ClutterActor *) priv->tooltip);
 
437
}
 
438
 
 
439
static void
 
440
st_widget_unmap (ClutterActor *actor)
 
441
{
 
442
  StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
 
443
 
 
444
  CLUTTER_ACTOR_CLASS (st_widget_parent_class)->unmap (actor);
 
445
 
 
446
  if (priv->border_image)
 
447
    clutter_actor_unmap (priv->border_image);
 
448
 
 
449
  if (priv->background_image)
 
450
    clutter_actor_unmap (priv->background_image);
 
451
 
 
452
  if (priv->tooltip)
 
453
    clutter_actor_unmap ((ClutterActor *) priv->tooltip);
 
454
}
 
455
 
 
456
static void notify_children_of_style_change (ClutterContainer *container);
 
457
 
 
458
static void
 
459
notify_children_of_style_change_foreach (ClutterActor *actor,
 
460
                                         gpointer      user_data)
 
461
{
 
462
  if (ST_IS_WIDGET (actor))
 
463
    st_widget_style_changed (ST_WIDGET (actor));
 
464
  else if (CLUTTER_IS_CONTAINER (actor))
 
465
    notify_children_of_style_change ((ClutterContainer *)actor);
 
466
}
 
467
 
 
468
static void
 
469
notify_children_of_style_change (ClutterContainer *container)
 
470
{
 
471
  /* notify our children that their parent stylable has changed */
 
472
  clutter_container_foreach (container,
 
473
                             notify_children_of_style_change_foreach,
 
474
                             NULL);
 
475
}
 
476
 
 
477
static void
 
478
st_widget_real_style_changed (StWidget *self)
 
479
{
 
480
  StWidgetPrivate *priv = ST_WIDGET (self)->priv;
 
481
  StThemeNode *theme_node;
 
482
  StBorderImage *border_image;
 
483
  StTextureCache *texture_cache;
 
484
  ClutterTexture *texture;
 
485
  const char *bg_file = NULL;
 
486
  gboolean relayout_needed = FALSE;
 
487
  gboolean has_changed = FALSE;
 
488
  ClutterColor color;
 
489
  guint border_width = 0;
 
490
  guint border_radius = 0;
 
491
  ClutterColor border_color = { 0, };
 
492
  StSide side;
 
493
  StCorner corner;
 
494
 
 
495
  /* application has request this widget is not stylable */
 
496
  if (!priv->is_stylable)
 
497
    return;
 
498
 
 
499
  theme_node = st_widget_get_theme_node (self);
 
500
 
 
501
  st_theme_node_get_background_color (theme_node, &color);
 
502
  if (!clutter_color_equal (&color, &priv->bg_color))
 
503
    {
 
504
      priv->bg_color = color;
 
505
      priv->draw_bg_color = color.alpha != 0;
 
506
      has_changed = TRUE;
 
507
    }
 
508
 
 
509
  if (priv->border_image)
 
510
    {
 
511
      clutter_actor_unparent (priv->border_image);
 
512
      priv->border_image = NULL;
 
513
    }
 
514
 
 
515
  if (priv->background_image)
 
516
    {
 
517
      clutter_actor_unparent (priv->background_image);
 
518
      priv->background_image = NULL;
 
519
    }
 
520
 
 
521
  texture_cache = st_texture_cache_get_default ();
 
522
 
 
523
  /* StThemeNode supports different widths and colors for different sides
 
524
   * of the border, and different radii for the different corners. We take
 
525
   * the different border widths into account when positioning, but our current
 
526
   * drawing code (using BigRectangle) can only handle a single width, color,
 
527
   * and radius, so we arbitrarily pick the first non-zero width and radius,
 
528
   * and use that.
 
529
   */
 
530
  for (side = ST_SIDE_TOP; side <= ST_SIDE_LEFT; side++)
 
531
    {
 
532
      double width = st_theme_node_get_border_width (theme_node, side);
 
533
      if (width > 0.5)
 
534
        {
 
535
          border_width = (int)(0.5 + width);
 
536
          st_theme_node_get_border_color (theme_node, side, &border_color);
 
537
          break;
 
538
        }
 
539
    }
 
540
 
 
541
  for (corner = ST_CORNER_TOPLEFT; corner <= ST_CORNER_BOTTOMLEFT; corner++)
 
542
    {
 
543
      double radius = st_theme_node_get_border_radius (theme_node, corner);
 
544
      if (radius > 0.5)
 
545
        {
 
546
          border_radius = (int)(0.5 + radius);
 
547
          break;
 
548
        }
 
549
    }
 
550
 
 
551
  /* Rough notes about the relationship of borders and backgrounds in CSS3;
 
552
   * see http://www.w3.org/TR/css3-background/ for more accurate details.
 
553
   *
 
554
   * - Things are drawn in 4 layers, from the bottom:
 
555
   *     Background color
 
556
   *     Background image
 
557
   *     Border color or border image
 
558
   *     Content
 
559
   * - The background color and image extend to and are clipped by the
 
560
   *   edge of the border area, so will be rounded if the border is rounded.
 
561
   *   (CSS3 background-clip property modifies this)
 
562
   * - The border image replaces what would normally be drawn by the border
 
563
   * - The border image is not clipped by a rounded border-radius
 
564
   * - The border radius rounds the background even if the border is
 
565
   *   zero width or a border image is being used.
 
566
   *
 
567
   * Deviations from the above as implemented here:
 
568
   *  - The combination of border image and a non-zero border radius is
 
569
   *    not supported; the background color will be drawn with square
 
570
   *    corners.
 
571
   *  - The background image is drawn above the border color or image,
 
572
   *    not below it.
 
573
   *  - We don't clip the background image to the (rounded) border area.
 
574
   *
 
575
   * The first two allow us always draw with no more than single border_image
 
576
   * and a single background image above it.
 
577
   */
 
578
 
 
579
  border_image = st_theme_node_get_border_image (theme_node);
 
580
  if (border_image)
 
581
    {
 
582
      const char *filename;
 
583
      gint border_left, border_right, border_top, border_bottom;
 
584
      gint width, height;
 
585
 
 
586
      filename = st_border_image_get_filename (border_image);
 
587
 
 
588
      /* `border-image' takes precedence over `background-image'.
 
589
       * Firefox lets the background-image shine thru when border-image has
 
590
       * alpha an channel, maybe that would be an option for the future. */
 
591
      texture = st_texture_cache_get_texture (texture_cache,
 
592
                                                filename);
 
593
 
 
594
      clutter_texture_get_base_size (CLUTTER_TEXTURE (texture),
 
595
                                     &width, &height);
 
596
 
 
597
      st_border_image_get_borders (border_image,
 
598
                                   &border_left, &border_right, &border_top, &border_bottom);
 
599
 
 
600
      priv->border_image = st_texture_frame_new (texture,
 
601
                                                 border_top,
 
602
                                                 border_right,
 
603
                                                 border_bottom,
 
604
                                                 border_left);
 
605
      clutter_actor_set_parent (priv->border_image, CLUTTER_ACTOR (self));
 
606
 
 
607
      has_changed = TRUE;
 
608
      relayout_needed = TRUE;
 
609
    }
 
610
  else if ((border_width > 0 && border_color.alpha != 0) ||
 
611
           (border_radius > 0 && priv->bg_color.alpha != 0))
 
612
    {
 
613
      priv->draw_bg_color = FALSE;
 
614
      priv->border_image = g_object_new (BIG_TYPE_RECTANGLE,
 
615
                                         "color", &priv->bg_color,
 
616
                                         "border-width", border_width,
 
617
                                         "border-color", &border_color,
 
618
                                         "corner-radius", border_radius,
 
619
                                         NULL);
 
620
 
 
621
      clutter_actor_set_parent (priv->border_image, CLUTTER_ACTOR (self));
 
622
 
 
623
      has_changed = TRUE;
 
624
      relayout_needed = TRUE;
 
625
    }
 
626
 
 
627
  bg_file = st_theme_node_get_background_image (theme_node);
 
628
  if (bg_file != NULL)
 
629
    {
 
630
      texture = st_texture_cache_get_texture (texture_cache, bg_file);
 
631
      priv->background_image = (ClutterActor*) texture;
 
632
 
 
633
      if (priv->background_image != NULL)
 
634
        {
 
635
          clutter_actor_set_parent (priv->background_image,
 
636
                                    CLUTTER_ACTOR (self));
 
637
        }
 
638
      else
 
639
        g_warning ("Could not load %s", bg_file);
 
640
 
 
641
      has_changed = TRUE;
 
642
      relayout_needed = TRUE;
 
643
    }
 
644
 
 
645
  /* If there are any properties above that need to cause a relayout thay
 
646
   * should set this flag.
 
647
   */
 
648
  if (has_changed)
 
649
    {
 
650
      if (relayout_needed)
 
651
        clutter_actor_queue_relayout ((ClutterActor *) self);
 
652
      else
 
653
        clutter_actor_queue_redraw ((ClutterActor *) self);
 
654
    }
 
655
 
 
656
  if (CLUTTER_IS_CONTAINER (self))
 
657
    notify_children_of_style_change ((ClutterContainer *)self);
 
658
}
 
659
 
 
660
void
 
661
st_widget_style_changed (StWidget *widget)
 
662
{
 
663
  StThemeNode *old_theme_node = NULL;
 
664
 
 
665
  widget->priv->is_style_dirty = TRUE;
 
666
  if (widget->priv->theme_node)
 
667
    {
 
668
      old_theme_node = widget->priv->theme_node;
 
669
      widget->priv->theme_node = NULL;
 
670
    }
 
671
 
 
672
  /* update the style only if we are mapped */
 
673
  if (CLUTTER_ACTOR_IS_MAPPED (CLUTTER_ACTOR (widget)))
 
674
    st_widget_recompute_style (widget, old_theme_node);
 
675
 
 
676
  if (old_theme_node)
 
677
    g_object_unref (old_theme_node);
 
678
}
 
679
 
 
680
static void
 
681
on_theme_context_changed (StThemeContext *context,
 
682
                          ClutterStage      *stage)
 
683
{
 
684
  notify_children_of_style_change (CLUTTER_CONTAINER (stage));
 
685
}
 
686
 
 
687
static StThemeNode *
 
688
get_root_theme_node (ClutterStage *stage)
 
689
{
 
690
  StThemeContext *context = st_theme_context_get_for_stage (stage);
 
691
 
 
692
  if (!g_object_get_data (G_OBJECT (context), "st-theme-initialized"))
 
693
    {
 
694
      g_object_set_data (G_OBJECT (context), "st-theme-initialized", GUINT_TO_POINTER (1));
 
695
      g_signal_connect (G_OBJECT (context), "changed",
 
696
                        G_CALLBACK (on_theme_context_changed), stage);
 
697
    }
 
698
 
 
699
  return st_theme_context_get_root_node (context);
 
700
}
 
701
 
 
702
/**
 
703
 * st_widget_get_theme_node:
 
704
 * @widget: a #StWidget
 
705
 *
 
706
 * Gets the theme node holding style information for the widget.
 
707
 * The theme node is used to access standard and custom CSS
 
708
 * properties of the widget.
 
709
 *
 
710
 * Return value: (transfer none): the theme node for the widget.
 
711
 *   This is owned by the widget. When attributes of the widget
 
712
 *   or the environment that affect the styling change (for example
 
713
 *   the style_class property of the widget), it will be recreated,
 
714
 *   and the ::style-changed signal will be emitted on the widget.
 
715
 */
 
716
StThemeNode *
 
717
st_widget_get_theme_node (StWidget *widget)
 
718
{
 
719
  StWidgetPrivate *priv = widget->priv;
 
720
 
 
721
  if (priv->theme_node == NULL)
 
722
    {
 
723
      StThemeNode *parent_node = NULL;
 
724
      ClutterStage *stage = NULL;
 
725
      ClutterActor *parent;
 
726
 
 
727
      parent = clutter_actor_get_parent (CLUTTER_ACTOR (widget));
 
728
      while (parent != NULL)
 
729
        {
 
730
          if (parent_node == NULL && ST_IS_WIDGET (parent))
 
731
            parent_node = st_widget_get_theme_node (ST_WIDGET (parent));
 
732
          else if (CLUTTER_IS_STAGE (parent))
 
733
            stage = CLUTTER_STAGE (parent);
 
734
 
 
735
          parent = clutter_actor_get_parent (parent);
 
736
        }
 
737
 
 
738
      if (stage == NULL)
 
739
        {
 
740
          g_warning ("st_widget_get_theme_node called on a widget not in a stage");
 
741
          stage = CLUTTER_STAGE (clutter_stage_get_default ());
 
742
        }
 
743
 
 
744
      if (parent_node == NULL)
 
745
        parent_node = get_root_theme_node (CLUTTER_STAGE (stage));
 
746
 
 
747
      priv->theme_node = st_theme_node_new (st_theme_context_get_for_stage (stage),
 
748
                                            parent_node, priv->theme,
 
749
                                            G_OBJECT_TYPE (widget),
 
750
                                            clutter_actor_get_name (CLUTTER_ACTOR (widget)),
 
751
                                            priv->style_class,
 
752
                                            priv->pseudo_class,
 
753
                                            priv->inline_style);
 
754
    }
 
755
 
 
756
  return priv->theme_node;
 
757
}
 
758
 
 
759
static gboolean
 
760
st_widget_enter (ClutterActor         *actor,
 
761
                 ClutterCrossingEvent *event)
 
762
{
 
763
  StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
 
764
 
 
765
 
 
766
  if (priv->has_tooltip)
 
767
    st_widget_show_tooltip ((StWidget*) actor);
 
768
 
 
769
  if (CLUTTER_ACTOR_CLASS (st_widget_parent_class)->enter_event)
 
770
    return CLUTTER_ACTOR_CLASS (st_widget_parent_class)->enter_event (actor, event);
 
771
  else
 
772
    return FALSE;
 
773
}
 
774
 
 
775
static gboolean
 
776
st_widget_leave (ClutterActor         *actor,
 
777
                 ClutterCrossingEvent *event)
 
778
{
 
779
  StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
 
780
 
 
781
  if (priv->has_tooltip)
 
782
    st_tooltip_hide (priv->tooltip);
 
783
 
 
784
  if (CLUTTER_ACTOR_CLASS (st_widget_parent_class)->leave_event)
 
785
    return CLUTTER_ACTOR_CLASS (st_widget_parent_class)->leave_event (actor, event);
 
786
  else
 
787
    return FALSE;
 
788
}
 
789
 
 
790
static void
 
791
st_widget_hide (ClutterActor *actor)
 
792
{
 
793
  StWidget *widget = (StWidget *) actor;
 
794
 
 
795
  /* hide the tooltip, if there is one */
 
796
  if (widget->priv->tooltip)
 
797
    st_tooltip_hide (ST_TOOLTIP (widget->priv->tooltip));
 
798
 
 
799
  CLUTTER_ACTOR_CLASS (st_widget_parent_class)->hide (actor);
 
800
}
 
801
 
 
802
 
 
803
 
 
804
static void
 
805
st_widget_class_init (StWidgetClass *klass)
 
806
{
 
807
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
808
  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
 
809
  GParamSpec *pspec;
 
810
 
 
811
  g_type_class_add_private (klass, sizeof (StWidgetPrivate));
 
812
 
 
813
  gobject_class->set_property = st_widget_set_property;
 
814
  gobject_class->get_property = st_widget_get_property;
 
815
  gobject_class->dispose = st_widget_dispose;
 
816
  gobject_class->finalize = st_widget_finalize;
 
817
 
 
818
  actor_class->allocate = st_widget_allocate;
 
819
  actor_class->paint = st_widget_paint;
 
820
  actor_class->parent_set = st_widget_parent_set;
 
821
  actor_class->map = st_widget_map;
 
822
  actor_class->unmap = st_widget_unmap;
 
823
 
 
824
  actor_class->enter_event = st_widget_enter;
 
825
  actor_class->leave_event = st_widget_leave;
 
826
  actor_class->hide = st_widget_hide;
 
827
 
 
828
  klass->draw_background = st_widget_real_draw_background;
 
829
  klass->style_changed = st_widget_real_style_changed;
 
830
 
 
831
  /**
 
832
   * StWidget:pseudo-class:
 
833
   *
 
834
   * The pseudo-class of the actor. Typical values include "hover", "active",
 
835
   * "focus".
 
836
   */
 
837
  g_object_class_install_property (gobject_class,
 
838
                                   PROP_PSEUDO_CLASS,
 
839
                                   g_param_spec_string ("pseudo-class",
 
840
                                                        "Pseudo Class",
 
841
                                                        "Pseudo class for styling",
 
842
                                                        "",
 
843
                                                        ST_PARAM_READWRITE));
 
844
  /**
 
845
   * StWidget:style-class:
 
846
   *
 
847
   * The style-class of the actor for use in styling.
 
848
   */
 
849
  g_object_class_install_property (gobject_class,
 
850
                                   PROP_STYLE_CLASS,
 
851
                                   g_param_spec_string ("style-class",
 
852
                                                        "Style Class",
 
853
                                                        "Style class for styling",
 
854
                                                        "",
 
855
                                                        ST_PARAM_READWRITE));
 
856
 
 
857
  /**
 
858
   * StWidget:style:
 
859
   *
 
860
   * Inline style information for the actor as a ';'-separated list of
 
861
   * CSS properties.
 
862
   */
 
863
  g_object_class_install_property (gobject_class,
 
864
                                   PROP_STYLE,
 
865
                                   g_param_spec_string ("style",
 
866
                                                        "Style",
 
867
                                                        "Inline style string",
 
868
                                                        "",
 
869
                                                        ST_PARAM_READWRITE));
 
870
 
 
871
  /**
 
872
   * StWidget:theme
 
873
   *
 
874
   * A theme set on this actor overriding the global theming for this actor
 
875
   * and its descendants
 
876
   */
 
877
  g_object_class_install_property (gobject_class,
 
878
                                   PROP_THEME,
 
879
                                   g_param_spec_object ("theme",
 
880
                                                        "Theme",
 
881
                                                        "Theme override",
 
882
                                                        ST_TYPE_THEME,
 
883
                                                        ST_PARAM_READWRITE));
 
884
 
 
885
  /**
 
886
   * StWidget:stylable:
 
887
   *
 
888
   * Enable or disable styling of the widget
 
889
   */
 
890
  pspec = g_param_spec_boolean ("stylable",
 
891
                                "Stylable",
 
892
                                "Whether the table should be styled",
 
893
                                TRUE,
 
894
                                ST_PARAM_READWRITE);
 
895
  g_object_class_install_property (gobject_class,
 
896
                                   PROP_STYLABLE,
 
897
                                   pspec);
 
898
 
 
899
  /**
 
900
   * StWidget:has-tooltip:
 
901
   *
 
902
   * Determines whether the widget has a tooltip. If set to TRUE, causes the
 
903
   * widget to monitor enter and leave events (i.e. sets the widget reactive).
 
904
   */
 
905
  pspec = g_param_spec_boolean ("has-tooltip",
 
906
                                "Has Tooltip",
 
907
                                "Determines whether the widget has a tooltip",
 
908
                                FALSE,
 
909
                                ST_PARAM_READWRITE);
 
910
  g_object_class_install_property (gobject_class,
 
911
                                   PROP_HAS_TOOLTIP,
 
912
                                   pspec);
 
913
 
 
914
 
 
915
  /**
 
916
   * StWidget:tooltip-text:
 
917
   *
 
918
   * text displayed on the tooltip
 
919
   */
 
920
  pspec = g_param_spec_string ("tooltip-text",
 
921
                               "Tooltip Text",
 
922
                               "Text displayed on the tooltip",
 
923
                               "",
 
924
                               ST_PARAM_READWRITE);
 
925
  g_object_class_install_property (gobject_class, PROP_TOOLTIP_TEXT, pspec);
 
926
 
 
927
  /**
 
928
   * StWidget::style-changed:
 
929
   *
 
930
   * Emitted when the style information that the widget derives from the
 
931
   * theme changes
 
932
   */
 
933
  signals[STYLE_CHANGED] =
 
934
    g_signal_new ("style-changed",
 
935
                  G_TYPE_FROM_CLASS (klass),
 
936
                  G_SIGNAL_RUN_LAST,
 
937
                  G_STRUCT_OFFSET (StWidgetClass, style_changed),
 
938
                  NULL, NULL,
 
939
                  _st_marshal_VOID__VOID,
 
940
                  G_TYPE_NONE, 0);
 
941
}
 
942
 
 
943
/**
 
944
 * st_widget_set_theme:
 
945
 * @actor: a #StWidget
 
946
 * @theme: a new style class string
 
947
 *
 
948
 * Overrides the theme that would be inherited from the actor's parent
 
949
 * or the stage with an entirely new theme (set of stylesheets).
 
950
 */
 
951
void
 
952
st_widget_set_theme (StWidget  *actor,
 
953
                      StTheme  *theme)
 
954
{
 
955
  StWidgetPrivate *priv = actor->priv;
 
956
 
 
957
  g_return_if_fail (ST_IS_WIDGET (actor));
 
958
 
 
959
  priv = actor->priv;
 
960
 
 
961
  if (theme !=priv->theme)
 
962
    {
 
963
      if (priv->theme)
 
964
        g_object_unref (priv->theme);
 
965
      priv->theme = g_object_ref (priv->theme);
 
966
 
 
967
      st_widget_style_changed (actor);
 
968
 
 
969
      g_object_notify (G_OBJECT (actor), "theme");
 
970
    }
 
971
}
 
972
 
 
973
/**
 
974
 * st_widget_get_theme:
 
975
 * @actor: a #StWidget
 
976
 *
 
977
 * Gets the overriding theme set on the actor. See st_widget_set_theme()
 
978
 *
 
979
 * Return value: (transfer none): the overriding theme, or %NULL
 
980
 */
 
981
StTheme *
 
982
st_widget_get_theme (StWidget *actor)
 
983
{
 
984
  g_return_val_if_fail (ST_IS_WIDGET (actor), NULL);
 
985
 
 
986
  return actor->priv->theme;
 
987
}
 
988
 
 
989
/**
 
990
 * st_widget_set_style_class_name:
 
991
 * @actor: a #StWidget
 
992
 * @style_class: a new style class string
 
993
 *
 
994
 * Set the style class name
 
995
 */
 
996
void
 
997
st_widget_set_style_class_name (StWidget    *actor,
 
998
                                const gchar *style_class)
 
999
{
 
1000
  StWidgetPrivate *priv = actor->priv;
 
1001
 
 
1002
  g_return_if_fail (ST_IS_WIDGET (actor));
 
1003
 
 
1004
  priv = actor->priv;
 
1005
 
 
1006
  if (g_strcmp0 (style_class, priv->style_class))
 
1007
    {
 
1008
      g_free (priv->style_class);
 
1009
      priv->style_class = g_strdup (style_class);
 
1010
 
 
1011
      st_widget_style_changed (actor);
 
1012
 
 
1013
      g_object_notify (G_OBJECT (actor), "style-class");
 
1014
    }
 
1015
}
 
1016
 
 
1017
 
 
1018
/**
 
1019
 * st_widget_get_style_class_name:
 
1020
 * @actor: a #StWidget
 
1021
 *
 
1022
 * Get the current style class name
 
1023
 *
 
1024
 * Returns: the class name string. The string is owned by the #StWidget and
 
1025
 * should not be modified or freed.
 
1026
 */
 
1027
const gchar*
 
1028
st_widget_get_style_class_name (StWidget *actor)
 
1029
{
 
1030
  g_return_val_if_fail (ST_IS_WIDGET (actor), NULL);
 
1031
 
 
1032
  return actor->priv->style_class;
 
1033
}
 
1034
 
 
1035
/**
 
1036
 * st_widget_get_style_pseudo_class:
 
1037
 * @actor: a #StWidget
 
1038
 *
 
1039
 * Get the current style pseudo class
 
1040
 *
 
1041
 * Returns: the pseudo class string. The string is owned by the #StWidget and
 
1042
 * should not be modified or freed.
 
1043
 */
 
1044
const gchar*
 
1045
st_widget_get_style_pseudo_class (StWidget *actor)
 
1046
{
 
1047
  g_return_val_if_fail (ST_IS_WIDGET (actor), NULL);
 
1048
 
 
1049
  return actor->priv->pseudo_class;
 
1050
}
 
1051
 
 
1052
/**
 
1053
 * st_widget_set_style_pseudo_class:
 
1054
 * @actor: a #StWidget
 
1055
 * @pseudo_class: a new pseudo class string
 
1056
 *
 
1057
 * Set the style pseudo class
 
1058
 */
 
1059
void
 
1060
st_widget_set_style_pseudo_class (StWidget    *actor,
 
1061
                                  const gchar *pseudo_class)
 
1062
{
 
1063
  StWidgetPrivate *priv;
 
1064
 
 
1065
  g_return_if_fail (ST_IS_WIDGET (actor));
 
1066
 
 
1067
  priv = actor->priv;
 
1068
 
 
1069
  if (g_strcmp0 (pseudo_class, priv->pseudo_class))
 
1070
    {
 
1071
      g_free (priv->pseudo_class);
 
1072
      priv->pseudo_class = g_strdup (pseudo_class);
 
1073
 
 
1074
      st_widget_style_changed (actor);
 
1075
 
 
1076
      g_object_notify (G_OBJECT (actor), "pseudo-class");
 
1077
    }
 
1078
}
 
1079
 
 
1080
/**
 
1081
 * st_widget_set_style:
 
1082
 * @actor: a #StWidget
 
1083
 * @style_class: (allow-none): a inline style string, or %NULL
 
1084
 *
 
1085
 * Set the inline style string for this widget. The inline style string is an
 
1086
 * optional ';'-separated list of CSS properties that override the style as
 
1087
 * determined from the stylesheets of the current theme.
 
1088
 */
 
1089
void
 
1090
st_widget_set_style (StWidget  *actor,
 
1091
                       const gchar *style)
 
1092
{
 
1093
  StWidgetPrivate *priv = actor->priv;
 
1094
 
 
1095
  g_return_if_fail (ST_IS_WIDGET (actor));
 
1096
 
 
1097
  priv = actor->priv;
 
1098
 
 
1099
  if (g_strcmp0 (style, priv->inline_style))
 
1100
    {
 
1101
      g_free (priv->inline_style);
 
1102
      priv->inline_style = g_strdup (style);
 
1103
 
 
1104
      st_widget_style_changed (actor);
 
1105
 
 
1106
      g_object_notify (G_OBJECT (actor), "style");
 
1107
    }
 
1108
}
 
1109
 
 
1110
/**
 
1111
 * st_widget_get_style:
 
1112
 * @actor: a #StWidget
 
1113
 *
 
1114
 * Get the current inline style string. See st_widget_set_style().
 
1115
 *
 
1116
 * Returns: The inline style string, or %NULL. The string is owned by the
 
1117
 * #StWidget and should not be modified or freed.
 
1118
 */
 
1119
const gchar*
 
1120
st_widget_get_style (StWidget *actor)
 
1121
{
 
1122
  g_return_val_if_fail (ST_IS_WIDGET (actor), NULL);
 
1123
 
 
1124
  return actor->priv->inline_style;
 
1125
}
 
1126
 
 
1127
static void
 
1128
st_widget_name_notify (StWidget   *widget,
 
1129
                       GParamSpec *pspec,
 
1130
                       gpointer    data)
 
1131
{
 
1132
  st_widget_style_changed (widget);
 
1133
}
 
1134
 
 
1135
static void
 
1136
st_widget_init (StWidget *actor)
 
1137
{
 
1138
  StWidgetPrivate *priv;
 
1139
 
 
1140
  actor->priv = priv = ST_WIDGET_GET_PRIVATE (actor);
 
1141
  priv->is_stylable = TRUE;
 
1142
 
 
1143
  /* connect style changed */
 
1144
  g_signal_connect (actor, "notify::name", G_CALLBACK (st_widget_name_notify), NULL);
 
1145
}
 
1146
 
 
1147
static void
 
1148
st_widget_recompute_style (StWidget    *widget,
 
1149
                           StThemeNode *old_theme_node)
 
1150
{
 
1151
  StThemeNode *new_theme_node = st_widget_get_theme_node (widget);
 
1152
 
 
1153
  if (!old_theme_node ||
 
1154
      !st_theme_node_geometry_equal (old_theme_node, new_theme_node))
 
1155
    clutter_actor_queue_relayout ((ClutterActor *) widget);
 
1156
 
 
1157
  g_signal_emit (widget, signals[STYLE_CHANGED], 0);
 
1158
  widget->priv->is_style_dirty = FALSE;
 
1159
}
 
1160
 
 
1161
/**
 
1162
 * st_widget_ensure_style:
 
1163
 * @widget: A #StWidget
 
1164
 *
 
1165
 * Ensures that @widget has read its style information.
 
1166
 *
 
1167
 */
 
1168
void
 
1169
st_widget_ensure_style (StWidget *widget)
 
1170
{
 
1171
  g_return_if_fail (ST_IS_WIDGET (widget));
 
1172
 
 
1173
  if (widget->priv->is_style_dirty)
 
1174
    st_widget_recompute_style (widget, NULL);
 
1175
}
 
1176
 
 
1177
/**
 
1178
 * st_widget_get_border_image:
 
1179
 * @actor: A #StWidget
 
1180
 *
 
1181
 * Get the texture used as the border image. This is set using the
 
1182
 * "border-image" CSS property. This function should normally only be used
 
1183
 * by subclasses.
 
1184
 *
 
1185
 * Returns: (transfer none): #ClutterActor
 
1186
 */
 
1187
ClutterActor *
 
1188
st_widget_get_border_image (StWidget *actor)
 
1189
{
 
1190
  StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
 
1191
  return priv->border_image;
 
1192
}
 
1193
 
 
1194
/**
 
1195
 * st_widget_get_background_image:
 
1196
 * @actor: A #StWidget
 
1197
 *
 
1198
 * Get the texture used as the background image. This is set using the
 
1199
 * "background-image" CSS property. This function should normally only be used
 
1200
 * by subclasses.
 
1201
 *
 
1202
 * Returns: (transfer none): a #ClutterActor
 
1203
 */
 
1204
ClutterActor *
 
1205
st_widget_get_background_image (StWidget *actor)
 
1206
{
 
1207
  StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
 
1208
  return priv->background_image;
 
1209
}
 
1210
 
 
1211
/**
 
1212
 * st_widget_set_has_tooltip:
 
1213
 * @widget: A #StWidget
 
1214
 * @has_tooltip: #TRUE if the widget should display a tooltip
 
1215
 *
 
1216
 * Enables tooltip support on the #StWidget.
 
1217
 *
 
1218
 * Note that setting has-tooltip to #TRUE will cause the widget to be set
 
1219
 * reactive. If you no longer need tooltip support and do not need the widget
 
1220
 * to be reactive, you need to set ClutterActor::reactive to FALSE.
 
1221
 *
 
1222
 */
 
1223
void
 
1224
st_widget_set_has_tooltip (StWidget *widget,
 
1225
                           gboolean  has_tooltip)
 
1226
{
 
1227
  StWidgetPrivate *priv;
 
1228
 
 
1229
  g_return_if_fail (ST_IS_WIDGET (widget));
 
1230
 
 
1231
  priv = widget->priv;
 
1232
 
 
1233
  priv->has_tooltip = has_tooltip;
 
1234
 
 
1235
  if (has_tooltip)
 
1236
    {
 
1237
      clutter_actor_set_reactive ((ClutterActor*) widget, TRUE);
 
1238
 
 
1239
      if (!priv->tooltip)
 
1240
        {
 
1241
          priv->tooltip = g_object_new (ST_TYPE_TOOLTIP, NULL);
 
1242
          clutter_actor_set_parent ((ClutterActor *) priv->tooltip,
 
1243
                                    (ClutterActor *) widget);
 
1244
        }
 
1245
    }
 
1246
  else
 
1247
    {
 
1248
      if (priv->tooltip)
 
1249
        {
 
1250
          clutter_actor_unparent (CLUTTER_ACTOR (priv->tooltip));
 
1251
          priv->tooltip = NULL;
 
1252
        }
 
1253
    }
 
1254
}
 
1255
 
 
1256
/**
 
1257
 * st_widget_get_has_tooltip:
 
1258
 * @widget: A #StWidget
 
1259
 *
 
1260
 * Returns the current value of the has-tooltip property. See
 
1261
 * st_tooltip_set_has_tooltip() for more information.
 
1262
 *
 
1263
 * Returns: current value of has-tooltip on @widget
 
1264
 */
 
1265
gboolean
 
1266
st_widget_get_has_tooltip (StWidget *widget)
 
1267
{
 
1268
  g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE);
 
1269
 
 
1270
  return widget->priv->has_tooltip;
 
1271
}
 
1272
 
 
1273
/**
 
1274
 * st_widget_set_tooltip_text:
 
1275
 * @widget: A #StWidget
 
1276
 * @text: text to set as the tooltip
 
1277
 *
 
1278
 * Set the tooltip text of the widget. This will set StWidget::has-tooltip to
 
1279
 * #TRUE. A value of #NULL will unset the tooltip and set has-tooltip to #FALSE.
 
1280
 *
 
1281
 */
 
1282
void
 
1283
st_widget_set_tooltip_text (StWidget    *widget,
 
1284
                            const gchar *text)
 
1285
{
 
1286
  StWidgetPrivate *priv;
 
1287
 
 
1288
  g_return_if_fail (ST_IS_WIDGET (widget));
 
1289
 
 
1290
  priv = widget->priv;
 
1291
 
 
1292
  if (text == NULL)
 
1293
    st_widget_set_has_tooltip (widget, FALSE);
 
1294
  else
 
1295
    st_widget_set_has_tooltip (widget, TRUE);
 
1296
 
 
1297
  st_tooltip_set_label (priv->tooltip, text);
 
1298
}
 
1299
 
 
1300
/**
 
1301
 * st_widget_get_tooltip_text:
 
1302
 * @widget: A #StWidget
 
1303
 *
 
1304
 * Get the current tooltip string
 
1305
 *
 
1306
 * Returns: The current tooltip string, owned by the #StWidget
 
1307
 */
 
1308
const gchar*
 
1309
st_widget_get_tooltip_text (StWidget *widget)
 
1310
{
 
1311
  StWidgetPrivate *priv;
 
1312
 
 
1313
  g_return_val_if_fail (ST_IS_WIDGET (widget), NULL);
 
1314
  priv = widget->priv;
 
1315
 
 
1316
  if (!priv->has_tooltip)
 
1317
    return NULL;
 
1318
 
 
1319
  return st_tooltip_get_label (widget->priv->tooltip);
 
1320
}
 
1321
 
 
1322
/**
 
1323
 * st_widget_show_tooltip:
 
1324
 * @widget: A #StWidget
 
1325
 *
 
1326
 * Show the tooltip for @widget
 
1327
 *
 
1328
 */
 
1329
void
 
1330
st_widget_show_tooltip (StWidget *widget)
 
1331
{
 
1332
  gfloat x, y, width, height;
 
1333
  ClutterGeometry area;
 
1334
 
 
1335
  g_return_if_fail (ST_IS_WIDGET (widget));
 
1336
 
 
1337
  /* XXX not necceary, but first allocate transform is wrong */
 
1338
 
 
1339
  clutter_actor_get_transformed_position ((ClutterActor*) widget,
 
1340
                                          &x, &y);
 
1341
 
 
1342
  clutter_actor_get_size ((ClutterActor*) widget, &width, &height);
 
1343
 
 
1344
  area.x = x;
 
1345
  area.y = y;
 
1346
  area.width = width;
 
1347
  area.height = height;
 
1348
 
 
1349
 
 
1350
  if (widget->priv->tooltip)
 
1351
    {
 
1352
      st_tooltip_set_tip_area (widget->priv->tooltip, &area);
 
1353
      st_tooltip_show (widget->priv->tooltip);
 
1354
    }
 
1355
}
 
1356
 
 
1357
/**
 
1358
 * st_widget_hide_tooltip:
 
1359
 * @widget: A #StWidget
 
1360
 *
 
1361
 * Hide the tooltip for @widget
 
1362
 *
 
1363
 */
 
1364
void
 
1365
st_widget_hide_tooltip (StWidget *widget)
 
1366
{
 
1367
  g_return_if_fail (ST_IS_WIDGET (widget));
 
1368
 
 
1369
  if (widget->priv->tooltip)
 
1370
    st_tooltip_hide (widget->priv->tooltip);
 
1371
}
 
1372
 
 
1373
/**
 
1374
 * st_widget_draw_background:
 
1375
 * @widget: a #StWidget
 
1376
 *
 
1377
 * Invokes #StWidget::draw_background() using the default background
 
1378
 * image and/or color from the @widget style
 
1379
 *
 
1380
 * This function should be used by subclasses of #StWidget that override
 
1381
 * the paint() virtual function and cannot chain up
 
1382
 */
 
1383
void
 
1384
st_widget_draw_background (StWidget *self)
 
1385
{
 
1386
  StWidgetPrivate *priv;
 
1387
  StWidgetClass *klass;
 
1388
 
 
1389
  g_return_if_fail (ST_IS_WIDGET (self));
 
1390
 
 
1391
  priv = self->priv;
 
1392
 
 
1393
  klass = ST_WIDGET_GET_CLASS (self);
 
1394
  klass->draw_background (ST_WIDGET (self));
 
1395
}