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

« back to all changes in this revision

Viewing changes to src/st/st-theme-node.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
#include <stdlib.h>
 
4
#include <string.h>
 
5
 
 
6
#include "st-theme-private.h"
 
7
#include "st-theme-context.h"
 
8
#include "st-theme-node.h"
 
9
 
 
10
static void st_theme_node_init               (StThemeNode          *node);
 
11
static void st_theme_node_class_init         (StThemeNodeClass     *klass);
 
12
static void st_theme_node_finalize           (GObject                 *object);
 
13
 
 
14
struct _StThemeNode {
 
15
  GObject parent;
 
16
 
 
17
  StThemeContext *context;
 
18
  StThemeNode *parent_node;
 
19
  StTheme *theme;
 
20
 
 
21
  PangoFontDescription *font_desc;
 
22
 
 
23
  ClutterColor background_color;
 
24
  ClutterColor foreground_color;
 
25
  ClutterColor border_color[4];
 
26
  double border_width[4];
 
27
  double border_radius[4];
 
28
  guint padding[4];
 
29
 
 
30
  char *background_image;
 
31
  StBorderImage *border_image;
 
32
 
 
33
  GType element_type;
 
34
  char *element_id;
 
35
  char *element_class;
 
36
  char *pseudo_class;
 
37
  char *inline_style;
 
38
 
 
39
  CRDeclaration **properties;
 
40
  int n_properties;
 
41
 
 
42
  /* We hold onto these separately so we can destroy them on finalize */
 
43
  CRDeclaration *inline_properties;
 
44
 
 
45
  guint properties_computed : 1;
 
46
  guint borders_computed : 1;
 
47
  guint background_computed : 1;
 
48
  guint foreground_computed : 1;
 
49
  guint border_image_computed : 1;
 
50
  guint link_type : 2;
 
51
};
 
52
 
 
53
struct _StThemeNodeClass {
 
54
  GObjectClass parent_class;
 
55
 
 
56
};
 
57
 
 
58
static const ClutterColor BLACK_COLOR = { 0, 0, 0, 0xff };
 
59
static const ClutterColor TRANSPARENT_COLOR = { 0, 0, 0, 0 };
 
60
 
 
61
G_DEFINE_TYPE (StThemeNode, st_theme_node, G_TYPE_OBJECT)
 
62
 
 
63
static void
 
64
st_theme_node_init (StThemeNode *node)
 
65
{
 
66
}
 
67
 
 
68
static void
 
69
st_theme_node_class_init (StThemeNodeClass *klass)
 
70
{
 
71
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
72
 
 
73
  object_class->finalize = st_theme_node_finalize;
 
74
}
 
75
 
 
76
static void
 
77
st_theme_node_finalize (GObject *object)
 
78
{
 
79
  StThemeNode *node = ST_THEME_NODE (object);
 
80
 
 
81
  g_free (node->element_id);
 
82
  g_free (node->element_class);
 
83
  g_free (node->pseudo_class);
 
84
  g_free (node->inline_style);
 
85
 
 
86
  if (node->properties)
 
87
    {
 
88
      g_free (node->properties);
 
89
      node->properties = NULL;
 
90
      node->n_properties = 0;
 
91
    }
 
92
 
 
93
  if (node->inline_properties)
 
94
    {
 
95
      /* This destroys the list, not just the head of the list */
 
96
      cr_declaration_destroy (node->inline_properties);
 
97
    }
 
98
 
 
99
  if (node->font_desc)
 
100
    {
 
101
      pango_font_description_free (node->font_desc);
 
102
      node->font_desc = NULL;
 
103
    }
 
104
 
 
105
  if (node->border_image)
 
106
    {
 
107
      g_object_unref (node->border_image);
 
108
      node->border_image = NULL;
 
109
    }
 
110
 
 
111
  if (node->background_image)
 
112
    g_free (node->background_image);
 
113
 
 
114
  G_OBJECT_CLASS (st_theme_node_parent_class)->finalize (object);
 
115
}
 
116
 
 
117
/**
 
118
 * st_theme_node_new:
 
119
 * @context: the context representing global state for this themed tree
 
120
 * @parent_node: (allow-none): the parent node of this node
 
121
 * @theme: (allow-none): a theme (stylesheet set) that overrides the
 
122
 *   theme inherited from the parent node
 
123
 * @element_type: the type of the GObject represented by this node
 
124
 *  in the tree (corresponding to an element if we were theming an XML
 
125
 *  document. %G_TYPE_NONE means this style was created for the stage
 
126
 * actor and matches a selector element name of 'stage'.
 
127
 * @element_id: (allow-none): the ID to match CSS rules against
 
128
 * @element_class: (allow-none): a whitespace-separated list of classes
 
129
 *   to match CSS rules against
 
130
 * @pseudo_class: (allow-none): a whitespace-separated list of pseudo-classes
 
131
 *   (like 'hover' or 'visited') to match CSS rules against
 
132
 *
 
133
 * Creates a new #StThemeNode. Once created, a node is immutable. Of any
 
134
 * of the attributes of the node (like the @element_class) change the node
 
135
 * and its child nodes must be destroyed and recreated.
 
136
 *
 
137
 * Return value: (transfer full): the theme node
 
138
 */
 
139
StThemeNode *
 
140
st_theme_node_new (StThemeContext    *context,
 
141
                   StThemeNode       *parent_node,
 
142
                   StTheme           *theme,
 
143
                   GType              element_type,
 
144
                   const char        *element_id,
 
145
                   const char        *element_class,
 
146
                   const char        *pseudo_class,
 
147
                   const char        *inline_style)
 
148
{
 
149
  StThemeNode *node;
 
150
 
 
151
  g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), NULL);
 
152
  g_return_val_if_fail (parent_node == NULL || ST_IS_THEME_NODE (parent_node), NULL);
 
153
 
 
154
  node = g_object_new (ST_TYPE_THEME_NODE, NULL);
 
155
 
 
156
  node->context = g_object_ref (context);
 
157
  if (parent_node != NULL)
 
158
    node->parent_node = g_object_ref (parent_node);
 
159
  else
 
160
    node->parent_node = NULL;
 
161
 
 
162
  if (theme == NULL && parent_node != NULL)
 
163
    theme = parent_node->theme;
 
164
 
 
165
  if (theme != NULL)
 
166
    node->theme = g_object_ref (theme);
 
167
 
 
168
  node->element_type = element_type;
 
169
  node->element_id = g_strdup (element_id);
 
170
  node->element_class = g_strdup (element_class);
 
171
  node->pseudo_class = g_strdup (pseudo_class);
 
172
  node->inline_style = g_strdup (inline_style);
 
173
 
 
174
  return node;
 
175
}
 
176
 
 
177
/**
 
178
 * st_theme_node_get_parent:
 
179
 * @node: a #StThemeNode
 
180
 *
 
181
 * Gets the parent themed element node.
 
182
 *
 
183
 * Return value: (transfer none): the parent #StThemeNode, or %NULL if this
 
184
 *  is the root node of the tree of theme elements.
 
185
 */
 
186
StThemeNode *
 
187
st_theme_node_get_parent (StThemeNode *node)
 
188
{
 
189
  g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
 
190
 
 
191
  return node->parent_node;
 
192
}
 
193
 
 
194
/**
 
195
 * st_theme_node_get_theme:
 
196
 * @node: a #StThemeNode
 
197
 *
 
198
 * Gets the theme stylesheet set that styles this node
 
199
 *
 
200
 * Return value: (transfer none): the theme stylesheet set
 
201
 */
 
202
StTheme *
 
203
st_theme_node_get_theme (StThemeNode *node)
 
204
{
 
205
  g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
 
206
 
 
207
  return node->theme;
 
208
}
 
209
 
 
210
GType
 
211
st_theme_node_get_element_type (StThemeNode *node)
 
212
{
 
213
  g_return_val_if_fail (ST_IS_THEME_NODE (node), G_TYPE_NONE);
 
214
 
 
215
  return node->element_type;
 
216
}
 
217
 
 
218
const char *
 
219
st_theme_node_get_element_id (StThemeNode *node)
 
220
{
 
221
  g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
 
222
 
 
223
  return node->element_id;
 
224
}
 
225
 
 
226
const char *
 
227
st_theme_node_get_element_class (StThemeNode *node)
 
228
{
 
229
  g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
 
230
 
 
231
  return node->element_class;
 
232
}
 
233
 
 
234
const char *
 
235
st_theme_node_get_pseudo_class (StThemeNode *node)
 
236
{
 
237
  g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
 
238
 
 
239
  return node->pseudo_class;
 
240
}
 
241
 
 
242
static void
 
243
ensure_properties (StThemeNode *node)
 
244
{
 
245
  if (!node->properties_computed)
 
246
    {
 
247
      GPtrArray *properties = NULL;
 
248
 
 
249
      node->properties_computed = TRUE;
 
250
 
 
251
      if (node->theme)
 
252
        properties = _st_theme_get_matched_properties (node->theme, node);
 
253
 
 
254
      if (node->inline_style)
 
255
        {
 
256
          CRDeclaration *cur_decl;
 
257
 
 
258
          if (!properties)
 
259
            properties = g_ptr_array_new ();
 
260
 
 
261
          node->inline_properties = _st_theme_parse_declaration_list (node->inline_style);
 
262
          for (cur_decl = node->inline_properties; cur_decl; cur_decl = cur_decl->next)
 
263
            g_ptr_array_add (properties, cur_decl);
 
264
        }
 
265
 
 
266
      if (properties)
 
267
        {
 
268
          node->n_properties = properties->len;
 
269
          node->properties = (CRDeclaration **)g_ptr_array_free (properties, FALSE);
 
270
        }
 
271
    }
 
272
}
 
273
 
 
274
typedef enum {
 
275
  VALUE_FOUND,
 
276
  VALUE_NOT_FOUND,
 
277
  VALUE_INHERIT
 
278
} GetFromTermResult;
 
279
 
 
280
static gboolean
 
281
term_is_inherit (CRTerm *term)
 
282
{
 
283
  return (term->type == TERM_IDENT &&
 
284
          strcmp (term->content.str->stryng->str, "inherit") == 0);
 
285
}
 
286
 
 
287
static gboolean
 
288
term_is_none (CRTerm *term)
 
289
{
 
290
  return (term->type == TERM_IDENT &&
 
291
          strcmp (term->content.str->stryng->str, "none") == 0);
 
292
}
 
293
 
 
294
static gboolean
 
295
term_is_transparent (CRTerm *term)
 
296
{
 
297
  return (term->type == TERM_IDENT &&
 
298
          strcmp (term->content.str->stryng->str, "transparent") == 0);
 
299
}
 
300
 
 
301
static int
 
302
color_component_from_double (double component)
 
303
{
 
304
  /* We want to spread the range 0-1 equally over 0..255, but
 
305
   * 1.0 should map to 255 not 256, so we need to special-case it.
 
306
   * See http://people.redhat.com/otaylor/pixel-converting.html
 
307
   * for (very) detailed discussion of related issues. */
 
308
  if (component >= 1.0)
 
309
    return 255;
 
310
  else
 
311
    return (int)(component * 256);
 
312
}
 
313
 
 
314
static GetFromTermResult
 
315
get_color_from_rgba_term (CRTerm       *term,
 
316
                          ClutterColor *color)
 
317
{
 
318
  CRTerm *arg = term->ext_content.func_param;
 
319
  CRNum *num;
 
320
  double r = 0, g = 0, b = 0, a = 0;
 
321
  int i;
 
322
 
 
323
  for (i = 0; i < 4; i++)
 
324
    {
 
325
      double value;
 
326
 
 
327
      if (arg == NULL)
 
328
        return VALUE_NOT_FOUND;
 
329
 
 
330
      if ((i == 0 && arg->the_operator != NO_OP) ||
 
331
          (i > 0 && arg->the_operator != COMMA))
 
332
        return VALUE_NOT_FOUND;
 
333
 
 
334
      if (arg->type != TERM_NUMBER)
 
335
        return VALUE_NOT_FOUND;
 
336
 
 
337
      num = arg->content.num;
 
338
 
 
339
      /* For simplicity, we convert a,r,g,b to [0,1.0] floats and then
 
340
       * convert them back below. Then when we set them on a cairo content
 
341
       * we convert them back to floats, and then cairo converts them
 
342
       * back to integers to pass them to X, and so forth...
 
343
       */
 
344
      if (i < 3)
 
345
        {
 
346
          if (num->type == NUM_PERCENTAGE)
 
347
            value = num->val / 100;
 
348
          else if (num->type == NUM_GENERIC)
 
349
            value = num->val / 255;
 
350
          else
 
351
            return VALUE_NOT_FOUND;
 
352
        }
 
353
      else
 
354
        {
 
355
          if (num->type != NUM_GENERIC)
 
356
            return VALUE_NOT_FOUND;
 
357
 
 
358
          value = num->val;
 
359
        }
 
360
 
 
361
      value = CLAMP (value, 0, 1);
 
362
 
 
363
      switch (i)
 
364
        {
 
365
        case 0:
 
366
          r = value;
 
367
          break;
 
368
        case 1:
 
369
          g = value;
 
370
          break;
 
371
        case 2:
 
372
          b = value;
 
373
          break;
 
374
        case 3:
 
375
          a = value;
 
376
          break;
 
377
        }
 
378
 
 
379
      arg = arg->next;
 
380
    }
 
381
 
 
382
  color->red = color_component_from_double (r);
 
383
  color->green = color_component_from_double (g);
 
384
  color->blue = color_component_from_double (b);
 
385
  color->alpha = color_component_from_double (a);
 
386
 
 
387
  return VALUE_FOUND;
 
388
}
 
389
 
 
390
static GetFromTermResult
 
391
get_color_from_term (StThemeNode  *node,
 
392
                     CRTerm       *term,
 
393
                     ClutterColor *color)
 
394
{
 
395
  CRRgb rgb;
 
396
  enum CRStatus status;
 
397
 
 
398
  /* Since libcroco doesn't know about rgba colors, it can't handle
 
399
   * the transparent keyword
 
400
   */
 
401
  if (term_is_transparent (term))
 
402
    {
 
403
      *color = TRANSPARENT_COLOR;
 
404
      return VALUE_FOUND;
 
405
    }
 
406
  /* rgba () colors - a CSS3 addition, are not supported by libcroco,
 
407
   * but they are parsed as a "function", so we can emulate the
 
408
   * functionality.
 
409
   *
 
410
   * libcroco < 0.6.2 has a bug where functions starting with 'r' are
 
411
   * misparsed. We workaround this by pre-converting 'rgba' to 'RGBA'
 
412
   * before parsing the stylesheet. Since libcroco isn't
 
413
   * case-insensitive (a bug), it's fine with functions starting with
 
414
   * 'R'. (In theory, we should be doing a case-insensitive compare
 
415
   * everywhere, not just here, but that doesn't make much sense when
 
416
   * the built-in parsing of libcroco is case-sensitive and things
 
417
   * like 10PX don't work.)
 
418
   */
 
419
  else if (term->type == TERM_FUNCTION &&
 
420
           term->content.str &&
 
421
           term->content.str->stryng &&
 
422
           term->content.str->stryng->str &&
 
423
           g_ascii_strcasecmp (term->content.str->stryng->str, "rgba") == 0)
 
424
    {
 
425
      return get_color_from_rgba_term (term, color);
 
426
    }
 
427
 
 
428
  status = cr_rgb_set_from_term (&rgb, term);
 
429
  if (status != CR_OK)
 
430
    return VALUE_NOT_FOUND;
 
431
 
 
432
  if (rgb.inherit)
 
433
    return VALUE_INHERIT;
 
434
 
 
435
  if (rgb.is_percentage)
 
436
    cr_rgb_compute_from_percentage (&rgb);
 
437
 
 
438
  color->red = rgb.red;
 
439
  color->green = rgb.green;
 
440
  color->blue = rgb.blue;
 
441
  color->alpha = 0xff;
 
442
 
 
443
  return VALUE_FOUND;
 
444
}
 
445
 
 
446
/**
 
447
 * st_theme_node_get_color:
 
448
 * @node: a #StThemeNode
 
449
 * @property_name: The name of the color property
 
450
 * @inherit: if %TRUE, if a value is not found for the property on the
 
451
 *   node, then it will be looked up on the parent node, and then on the
 
452
 *   parent's parent, and so forth. Note that if the property has a
 
453
 *   value of 'inherit' it will be inherited even if %FALSE is passed
 
454
 *   in for @inherit; this only affects the default behavior for inheritance.
 
455
 * @color: (out): location to store the color that was determined.
 
456
 *   If the property is not found, the value in this location
 
457
 *   will not be changed.
 
458
 *
 
459
 * Generically looks up a property containing a single color value. When
 
460
 * specific getters (like st_theme_node_get_background_color()) exist, they
 
461
 * should be used instead. They are cached, so more efficient, and have
 
462
 * handling for shortcut properties and other details of CSS.
 
463
 *
 
464
 * Return value: %TRUE if the property was found in the properties for this
 
465
 *  theme node (or in the properties of parent nodes when inheriting.)
 
466
 */
 
467
gboolean
 
468
st_theme_node_get_color (StThemeNode  *node,
 
469
                         const char   *property_name,
 
470
                         gboolean      inherit,
 
471
                         ClutterColor *color)
 
472
{
 
473
 
 
474
  int i;
 
475
 
 
476
  ensure_properties (node);
 
477
 
 
478
  for (i = node->n_properties - 1; i >= 0; i--)
 
479
    {
 
480
      CRDeclaration *decl = node->properties[i];
 
481
 
 
482
      if (strcmp (decl->property->stryng->str, property_name) == 0)
 
483
        {
 
484
          GetFromTermResult result = get_color_from_term (node, decl->value, color);
 
485
          if (result == VALUE_FOUND)
 
486
            {
 
487
              return TRUE;
 
488
            }
 
489
          else if (result == VALUE_INHERIT)
 
490
            {
 
491
              if (node->parent_node)
 
492
                return st_theme_node_get_color (node->parent_node, property_name, inherit, color);
 
493
              else
 
494
                break;
 
495
            }
 
496
        }
 
497
    }
 
498
 
 
499
  return FALSE;
 
500
}
 
501
 
 
502
/**
 
503
 * st_theme_node_get_double:
 
504
 * @node: a #StThemeNode
 
505
 * @property_name: The name of the numeric property
 
506
 * @inherit: if %TRUE, if a value is not found for the property on the
 
507
 *   node, then it will be looked up on the parent node, and then on the
 
508
 *   parent's parent, and so forth. Note that if the property has a
 
509
 *   value of 'inherit' it will be inherited even if %FALSE is passed
 
510
 *   in for @inherit; this only affects the default behavior for inheritance.
 
511
 * @value: (out): location to store the value that was determined.
 
512
 *   If the property is not found, the value in this location
 
513
 *   will not be changed.
 
514
 *
 
515
 * Generically looks up a property containing a single numeric value
 
516
 *  without units.
 
517
 *
 
518
 * Return value: %TRUE if the property was found in the properties for this
 
519
 *  theme node (or in the properties of parent nodes when inheriting.)
 
520
 */
 
521
gboolean
 
522
st_theme_node_get_double (StThemeNode *node,
 
523
                          const char  *property_name,
 
524
                          gboolean     inherit,
 
525
                          double      *value)
 
526
{
 
527
  gboolean result = FALSE;
 
528
  int i;
 
529
 
 
530
  ensure_properties (node);
 
531
 
 
532
  for (i = node->n_properties - 1; i >= 0; i--)
 
533
    {
 
534
      CRDeclaration *decl = node->properties[i];
 
535
 
 
536
      if (strcmp (decl->property->stryng->str, property_name) == 0)
 
537
        {
 
538
          CRTerm *term = decl->value;
 
539
 
 
540
          if (term->type != TERM_NUMBER || term->content.num->type != NUM_GENERIC)
 
541
            continue;
 
542
 
 
543
          *value = term->content.num->val;
 
544
          result = TRUE;
 
545
          break;
 
546
        }
 
547
    }
 
548
 
 
549
  if (!result && inherit && node->parent_node)
 
550
    result = st_theme_node_get_double (node->parent_node, property_name, inherit, value);
 
551
 
 
552
  return result;
 
553
}
 
554
 
 
555
static const PangoFontDescription *
 
556
get_parent_font (StThemeNode *node)
 
557
{
 
558
  if (node->parent_node)
 
559
    return st_theme_node_get_font (node->parent_node);
 
560
  else
 
561
    return st_theme_context_get_font (node->context);
 
562
}
 
563
 
 
564
static GetFromTermResult
 
565
get_length_from_term (StThemeNode *node,
 
566
                      CRTerm      *term,
 
567
                      gboolean     use_parent_font,
 
568
                      gdouble     *length)
 
569
{
 
570
  CRNum *num;
 
571
 
 
572
  enum {
 
573
    ABSOLUTE,
 
574
    POINTS,
 
575
    FONT_RELATIVE,
 
576
  } type = ABSOLUTE;
 
577
 
 
578
  double multiplier = 1.0;
 
579
 
 
580
  if (term->type != TERM_NUMBER)
 
581
    {
 
582
      g_warning ("Ignoring length property that isn't a number");
 
583
      return FALSE;
 
584
    }
 
585
 
 
586
  num = term->content.num;
 
587
 
 
588
  switch (num->type)
 
589
    {
 
590
    case NUM_LENGTH_PX:
 
591
      type = ABSOLUTE;
 
592
      multiplier = 1;
 
593
      break;
 
594
    case NUM_LENGTH_PT:
 
595
      type = POINTS;
 
596
      multiplier = 1;
 
597
      break;
 
598
    case NUM_LENGTH_IN:
 
599
      type = POINTS;
 
600
      multiplier = 72;
 
601
      break;
 
602
    case NUM_LENGTH_CM:
 
603
      type = POINTS;
 
604
      multiplier = 72. / 2.54;
 
605
      break;
 
606
    case NUM_LENGTH_MM:
 
607
      type = POINTS;
 
608
      multiplier = 72. / 25.4;
 
609
      break;
 
610
    case NUM_LENGTH_PC:
 
611
      type = POINTS;
 
612
      multiplier = 12. / 25.4;
 
613
      break;
 
614
    case NUM_LENGTH_EM:
 
615
      {
 
616
        type = FONT_RELATIVE;
 
617
        multiplier = 1;
 
618
        break;
 
619
      }
 
620
    case NUM_LENGTH_EX:
 
621
      {
 
622
        /* Doing better would require actually resolving the font description
 
623
         * to a specific font, and Pango doesn't have an ex metric anyways,
 
624
         * so we'd have to try and synthesize it by complicated means.
 
625
         *
 
626
         * The 0.5em is the CSS spec suggested thing to use when nothing
 
627
         * better is available.
 
628
         */
 
629
        type = FONT_RELATIVE;
 
630
        multiplier = 0.5;
 
631
        break;
 
632
      }
 
633
 
 
634
    case NUM_INHERIT:
 
635
      return VALUE_INHERIT;
 
636
 
 
637
    case NUM_AUTO:
 
638
      g_warning ("'auto' not supported for lengths");
 
639
      return VALUE_NOT_FOUND;
 
640
 
 
641
    case NUM_GENERIC:
 
642
      g_warning ("length values must specify a unit");
 
643
      return VALUE_NOT_FOUND;
 
644
 
 
645
    case NUM_PERCENTAGE:
 
646
      g_warning ("percentage lengths not currently supported");
 
647
      return VALUE_NOT_FOUND;
 
648
 
 
649
    case NUM_ANGLE_DEG:
 
650
    case NUM_ANGLE_RAD:
 
651
    case NUM_ANGLE_GRAD:
 
652
    case NUM_TIME_MS:
 
653
    case NUM_TIME_S:
 
654
    case NUM_FREQ_HZ:
 
655
    case NUM_FREQ_KHZ:
 
656
    case NUM_UNKNOWN_TYPE:
 
657
    case NB_NUM_TYPE:
 
658
      g_warning ("Ignoring invalid type of number of length property");
 
659
      return VALUE_NOT_FOUND;
 
660
    }
 
661
 
 
662
  switch (type)
 
663
    {
 
664
    case ABSOLUTE:
 
665
      *length = num->val * multiplier;
 
666
      break;
 
667
    case POINTS:
 
668
      {
 
669
        double resolution = st_theme_context_get_resolution (node->context);
 
670
        *length = num->val * multiplier * (resolution / 72.);
 
671
      }
 
672
      break;
 
673
    case FONT_RELATIVE:
 
674
      {
 
675
        const PangoFontDescription *desc;
 
676
        double font_size;
 
677
 
 
678
        if (use_parent_font)
 
679
          desc = get_parent_font (node);
 
680
        else
 
681
          desc = st_theme_node_get_font (node);
 
682
 
 
683
        font_size = (double)pango_font_description_get_size (desc) / PANGO_SCALE;
 
684
 
 
685
        if (pango_font_description_get_size_is_absolute (desc))
 
686
          {
 
687
            *length = num->val * multiplier * font_size;
 
688
          }
 
689
        else
 
690
          {
 
691
            double resolution = st_theme_context_get_resolution (node->context);
 
692
            *length = num->val * multiplier * (resolution / 72.) * font_size;
 
693
          }
 
694
      }
 
695
      break;
 
696
    default:
 
697
      g_assert_not_reached ();
 
698
    }
 
699
 
 
700
  return VALUE_FOUND;
 
701
}
 
702
 
 
703
static GetFromTermResult
 
704
get_length_internal (StThemeNode *node,
 
705
                     const char  *property_name,
 
706
                     const char  *suffixed,
 
707
                     gdouble     *length)
 
708
{
 
709
  int i;
 
710
 
 
711
  ensure_properties (node);
 
712
 
 
713
  for (i = node->n_properties - 1; i >= 0; i--)
 
714
    {
 
715
      CRDeclaration *decl = node->properties[i];
 
716
 
 
717
      if (strcmp (decl->property->stryng->str, property_name) == 0 ||
 
718
          (suffixed != NULL && strcmp (decl->property->stryng->str, suffixed) == 0))
 
719
        {
 
720
          GetFromTermResult result = get_length_from_term (node, decl->value, FALSE, length);
 
721
          if (result != VALUE_NOT_FOUND)
 
722
            return result;
 
723
        }
 
724
    }
 
725
 
 
726
  return VALUE_NOT_FOUND;
 
727
}
 
728
 
 
729
/**
 
730
 * st_theme_node_get_length:
 
731
 * @node: a #StThemeNode
 
732
 * @property_name: The name of the length property
 
733
 * @inherit: if %TRUE, if a value is not found for the property on the
 
734
 *   node, then it will be looked up on the parent node, and then on the
 
735
 *   parent's parent, and so forth. Note that if the property has a
 
736
 *   value of 'inherit' it will be inherited even if %FALSE is passed
 
737
 *   in for @inherit; this only affects the default behavior for inheritance.
 
738
 * @length: (out): location to store the length that was determined.
 
739
 *   If the property is not found, the value in this location
 
740
 *   will not be changed. The returned length is resolved
 
741
 *   to pixels.
 
742
 *
 
743
 * Generically looks up a property containing a single length value. When
 
744
 * specific getters (like st_theme_node_get_border_width()) exist, they
 
745
 * should be used instead. They are cached, so more efficient, and have
 
746
 * handling for shortcut properties and other details of CSS.
 
747
 *
 
748
 * Return value: %TRUE if the property was found in the properties for this
 
749
 *  theme node (or in the properties of parent nodes when inheriting.)
 
750
 */
 
751
gboolean
 
752
st_theme_node_get_length (StThemeNode *node,
 
753
                          const char  *property_name,
 
754
                          gboolean     inherit,
 
755
                          gdouble     *length)
 
756
{
 
757
  GetFromTermResult result = get_length_internal (node, property_name, NULL, length);
 
758
  if (result == VALUE_FOUND)
 
759
    return TRUE;
 
760
  else if (result == VALUE_INHERIT)
 
761
    inherit = TRUE;
 
762
 
 
763
  if (inherit && node->parent_node &&
 
764
      st_theme_node_get_length (node->parent_node, property_name, inherit, length))
 
765
    return TRUE;
 
766
  else
 
767
    return FALSE;
 
768
}
 
769
 
 
770
static void
 
771
do_border_radius_term (StThemeNode *node,
 
772
                       CRTerm      *term,
 
773
                       gboolean     topleft,
 
774
                       gboolean     topright,
 
775
                       gboolean     bottomright,
 
776
                       gboolean     bottomleft)
 
777
{
 
778
  gdouble value;
 
779
 
 
780
  if (get_length_from_term (node, term, FALSE, &value) != VALUE_FOUND)
 
781
    return;
 
782
 
 
783
  if (topleft)
 
784
    node->border_radius[ST_CORNER_TOPLEFT] = value;
 
785
  if (topright)
 
786
    node->border_radius[ST_CORNER_TOPRIGHT] = value;
 
787
  if (bottomright)
 
788
    node->border_radius[ST_CORNER_BOTTOMRIGHT] = value;
 
789
  if (bottomleft)
 
790
    node->border_radius[ST_CORNER_BOTTOMLEFT] = value;
 
791
}
 
792
 
 
793
static void
 
794
do_border_radius (StThemeNode   *node,
 
795
                  CRDeclaration *decl)
 
796
{
 
797
  const char *property_name = decl->property->stryng->str + 13; /* Skip 'border-radius' */
 
798
 
 
799
  if (strcmp (property_name, "") == 0)
 
800
    {
 
801
      /* Slight deviation ... if we don't understand some of the terms and understand others,
 
802
       * then we set the ones we understand and ignore the others instead of ignoring the
 
803
       * whole thing
 
804
       */
 
805
      if (decl->value == NULL) /* 0 values */
 
806
        return;
 
807
      else if (decl->value->next == NULL) /* 1 value */
 
808
        {
 
809
          do_border_radius_term (node, decl->value,       TRUE, TRUE, TRUE, TRUE); /* all corners */
 
810
          return;
 
811
        }
 
812
      else if (decl->value->next->next == NULL) /* 2 values */
 
813
        {
 
814
          do_border_radius_term (node, decl->value,       TRUE,  FALSE,  TRUE,  FALSE);  /* topleft/bottomright */
 
815
          do_border_radius_term (node, decl->value->next, FALSE,  TRUE,   FALSE, TRUE);  /* topright/bottomleft */
 
816
        }
 
817
      else if (decl->value->next->next->next == NULL) /* 3 values */
 
818
        {
 
819
          do_border_radius_term (node, decl->value,             TRUE,  FALSE, FALSE, FALSE); /* topleft */
 
820
          do_border_radius_term (node, decl->value->next,       FALSE, TRUE,  FALSE, TRUE);  /* topright/bottomleft */
 
821
          do_border_radius_term (node, decl->value->next->next, FALSE, FALSE, TRUE,  FALSE);  /* bottomright */
 
822
        }
 
823
      else if (decl->value->next->next->next->next == NULL) /* 4 values */
 
824
        {
 
825
          do_border_radius_term (node, decl->value,                   TRUE,  FALSE, FALSE, FALSE); /* topleft */
 
826
          do_border_radius_term (node, decl->value->next,             FALSE, TRUE,  FALSE, FALSE); /* topright */
 
827
          do_border_radius_term (node, decl->value->next->next,       FALSE, FALSE, TRUE,  FALSE); /* bottomright */
 
828
          do_border_radius_term (node, decl->value->next->next->next, FALSE, FALSE, FALSE, TRUE);  /* bottomleft */
 
829
        }
 
830
      else
 
831
        {
 
832
          g_warning ("Too many values for border-radius property");
 
833
          return;
 
834
        }
 
835
    }
 
836
  else
 
837
    {
 
838
      if (decl->value == NULL || decl->value->next != NULL)
 
839
        return;
 
840
 
 
841
      if (strcmp (property_name, "-topleft") == 0)
 
842
        do_border_radius_term (node, decl->value, TRUE,  FALSE, FALSE, FALSE);
 
843
      else if (strcmp (property_name, "-topright") == 0)
 
844
        do_border_radius_term (node, decl->value, FALSE, TRUE,  FALSE, FALSE);
 
845
      else if (strcmp (property_name, "-bottomright") == 0)
 
846
        do_border_radius_term (node, decl->value, FALSE, FALSE, TRUE,  FALSE);
 
847
      else if (strcmp (property_name, "-bottomleft") == 0)
 
848
        do_border_radius_term (node, decl->value, FALSE, FALSE, FALSE, TRUE);
 
849
    }
 
850
}
 
851
 
 
852
static void
 
853
do_border_property (StThemeNode   *node,
 
854
                    CRDeclaration *decl)
 
855
{
 
856
  const char *property_name = decl->property->stryng->str + 6; /* Skip 'border' */
 
857
  StSide side = (StSide)-1;
 
858
  ClutterColor color;
 
859
  gboolean color_set = FALSE;
 
860
  double width;
 
861
  gboolean width_set = FALSE;
 
862
  int j;
 
863
 
 
864
  if (g_str_has_prefix (property_name, "-radius"))
 
865
    {
 
866
      do_border_radius (node, decl);
 
867
      return;
 
868
    }
 
869
 
 
870
  if (g_str_has_prefix (property_name, "-left"))
 
871
    {
 
872
      side = ST_SIDE_LEFT;
 
873
      property_name += 5;
 
874
    }
 
875
  else if (g_str_has_prefix (property_name, "-right"))
 
876
    {
 
877
      side = ST_SIDE_RIGHT;
 
878
      property_name += 6;
 
879
    }
 
880
  else if (g_str_has_prefix (property_name, "-top"))
 
881
    {
 
882
      side = ST_SIDE_TOP;
 
883
      property_name += 4;
 
884
    }
 
885
  else if (g_str_has_prefix (property_name, "-bottom"))
 
886
    {
 
887
      side = ST_SIDE_BOTTOM;
 
888
      property_name += 7;
 
889
    }
 
890
 
 
891
  if (strcmp (property_name, "") == 0)
 
892
    {
 
893
      /* Set value for width/color/node in any order */
 
894
      CRTerm *term;
 
895
 
 
896
      for (term = decl->value; term; term = term->next)
 
897
        {
 
898
          GetFromTermResult result;
 
899
 
 
900
          if (term->type == TERM_IDENT)
 
901
            {
 
902
              const char *ident = term->content.str->stryng->str;
 
903
              if (strcmp (ident, "none") == 0 || strcmp (ident, "hidden") == 0)
 
904
                {
 
905
                  width = 0.;
 
906
                  continue;
 
907
                }
 
908
              else if (strcmp (ident, "solid") == 0)
 
909
                {
 
910
                  /* The only thing we support */
 
911
                  continue;
 
912
                }
 
913
              else if (strcmp (ident, "dotted") == 0 ||
 
914
                       strcmp (ident, "dashed") == 0 ||
 
915
                       strcmp (ident, "solid") == 0 ||
 
916
                       strcmp (ident, "double") == 0 ||
 
917
                       strcmp (ident, "groove") == 0 ||
 
918
                       strcmp (ident, "ridge") == 0 ||
 
919
                       strcmp (ident, "inset") == 0 ||
 
920
                       strcmp (ident, "outset") == 0)
 
921
                {
 
922
                  /* Treat the same as solid */
 
923
                  continue;
 
924
                }
 
925
 
 
926
              /* Presumably a color, fall through */
 
927
            }
 
928
 
 
929
          if (term->type == TERM_NUMBER)
 
930
            {
 
931
              result = get_length_from_term (node, term, FALSE, &width);
 
932
              if (result != VALUE_NOT_FOUND)
 
933
                {
 
934
                  width_set = result == VALUE_FOUND;
 
935
                  continue;
 
936
                }
 
937
            }
 
938
 
 
939
          result = get_color_from_term (node, term, &color);
 
940
          if (result != VALUE_NOT_FOUND)
 
941
            {
 
942
              color_set = result == VALUE_FOUND;
 
943
              continue;
 
944
            }
 
945
        }
 
946
 
 
947
    }
 
948
  else if (strcmp (property_name, "-color") == 0)
 
949
    {
 
950
      if (decl->value == NULL || decl->value->next != NULL)
 
951
        return;
 
952
 
 
953
      if (get_color_from_term (node, decl->value, &color) == VALUE_FOUND)
 
954
        /* Ignore inherit */
 
955
        color_set = TRUE;
 
956
    }
 
957
  else if (strcmp (property_name, "-width") == 0)
 
958
    {
 
959
      if (decl->value == NULL || decl->value->next != NULL)
 
960
        return;
 
961
 
 
962
      if (get_length_from_term (node, decl->value, FALSE, &width) == VALUE_FOUND)
 
963
        /* Ignore inherit */
 
964
        width_set = TRUE;
 
965
    }
 
966
 
 
967
  if (side == (StSide)-1)
 
968
    {
 
969
      for (j = 0; j < 4; j++)
 
970
        {
 
971
          if (color_set)
 
972
            node->border_color[j] = color;
 
973
          if (width_set)
 
974
            node->border_width[j] = width;
 
975
        }
 
976
    }
 
977
  else
 
978
    {
 
979
      if (color_set)
 
980
        node->border_color[side] = color;
 
981
      if (width_set)
 
982
        node->border_width[side] = width;
 
983
    }
 
984
}
 
985
 
 
986
static void
 
987
do_padding_property_term (StThemeNode *node,
 
988
                          CRTerm      *term,
 
989
                          gboolean     left,
 
990
                          gboolean     right,
 
991
                          gboolean     top,
 
992
                          gboolean     bottom)
 
993
{
 
994
  gdouble value;
 
995
 
 
996
  if (get_length_from_term (node, term, FALSE, &value) != VALUE_FOUND)
 
997
    return;
 
998
 
 
999
  if (left)
 
1000
    node->padding[ST_SIDE_LEFT] = value;
 
1001
  if (right)
 
1002
    node->padding[ST_SIDE_RIGHT] = value;
 
1003
  if (top)
 
1004
    node->padding[ST_SIDE_TOP] = value;
 
1005
  if (bottom)
 
1006
    node->padding[ST_SIDE_BOTTOM] = value;
 
1007
}
 
1008
 
 
1009
static void
 
1010
do_padding_property (StThemeNode   *node,
 
1011
                     CRDeclaration *decl)
 
1012
{
 
1013
  const char *property_name = decl->property->stryng->str + 7; /* Skip 'padding' */
 
1014
 
 
1015
  if (strcmp (property_name, "") == 0)
 
1016
    {
 
1017
      /* Slight deviation ... if we don't understand some of the terms and understand others,
 
1018
       * then we set the ones we understand and ignore the others instead of ignoring the
 
1019
       * whole thing
 
1020
       */
 
1021
      if (decl->value == NULL) /* 0 values */
 
1022
        return;
 
1023
      else if (decl->value->next == NULL) /* 1 value */
 
1024
        {
 
1025
          do_padding_property_term (node, decl->value, TRUE, TRUE, TRUE, TRUE); /* left/right/top/bottom */
 
1026
          return;
 
1027
        }
 
1028
      else if (decl->value->next->next == NULL) /* 2 values */
 
1029
        {
 
1030
          do_padding_property_term (node, decl->value,       FALSE, FALSE, TRUE,  TRUE);  /* top/bottom */
 
1031
          do_padding_property_term (node, decl->value->next, TRUE, TRUE,   FALSE, FALSE); /* left/right */
 
1032
        }
 
1033
      else if (decl->value->next->next->next == NULL) /* 3 values */
 
1034
        {
 
1035
          do_padding_property_term (node, decl->value,             FALSE, FALSE, TRUE,  FALSE); /* top */
 
1036
          do_padding_property_term (node, decl->value->next,       TRUE,  TRUE,  FALSE, FALSE); /* left/right */
 
1037
          do_padding_property_term (node, decl->value->next->next, FALSE, FALSE, FALSE, TRUE);  /* bottom */
 
1038
        }
 
1039
      else if (decl->value->next->next->next->next == NULL) /* 4 values */
 
1040
        {
 
1041
          do_padding_property_term (node, decl->value,                   FALSE, FALSE, TRUE,  FALSE); /* top */
 
1042
          do_padding_property_term (node, decl->value->next,             FALSE, TRUE,  FALSE, FALSE); /* right */
 
1043
          do_padding_property_term (node, decl->value->next->next,       FALSE, FALSE, FALSE, TRUE);  /* bottom */
 
1044
          do_padding_property_term (node, decl->value->next->next->next, TRUE,  FALSE, FALSE, FALSE); /* left */
 
1045
        }
 
1046
      else
 
1047
        {
 
1048
          g_warning ("Too many values for padding property");
 
1049
          return;
 
1050
        }
 
1051
    }
 
1052
  else
 
1053
    {
 
1054
      if (decl->value == NULL || decl->value->next != NULL)
 
1055
        return;
 
1056
 
 
1057
      if (strcmp (property_name, "-left") == 0)
 
1058
        do_padding_property_term (node, decl->value, TRUE,  FALSE, FALSE, FALSE);
 
1059
      else if (strcmp (property_name, "-right") == 0)
 
1060
        do_padding_property_term (node, decl->value, FALSE, TRUE,  FALSE, FALSE);
 
1061
      else if (strcmp (property_name, "-top") == 0)
 
1062
        do_padding_property_term (node, decl->value, FALSE, FALSE, TRUE,  FALSE);
 
1063
      else if (strcmp (property_name, "-bottom") == 0)
 
1064
        do_padding_property_term (node, decl->value, FALSE, FALSE, FALSE, TRUE);
 
1065
    }
 
1066
}
 
1067
 
 
1068
static void
 
1069
ensure_borders (StThemeNode *node)
 
1070
{
 
1071
  int i, j;
 
1072
 
 
1073
  if (node->borders_computed)
 
1074
    return;
 
1075
 
 
1076
  node->borders_computed = TRUE;
 
1077
 
 
1078
  ensure_properties (node);
 
1079
 
 
1080
  for (j = 0; j < 4; j++)
 
1081
    {
 
1082
      node->border_width[j] = 0;
 
1083
      node->border_color[j] = TRANSPARENT_COLOR;
 
1084
    }
 
1085
 
 
1086
  for (i = 0; i < node->n_properties; i++)
 
1087
    {
 
1088
      CRDeclaration *decl = node->properties[i];
 
1089
      const char *property_name = decl->property->stryng->str;
 
1090
 
 
1091
      if (g_str_has_prefix (property_name, "border"))
 
1092
        do_border_property (node, decl);
 
1093
      else if (g_str_has_prefix (property_name, "padding"))
 
1094
        do_padding_property (node, decl);
 
1095
    }
 
1096
}
 
1097
 
 
1098
double
 
1099
st_theme_node_get_border_width (StThemeNode *node,
 
1100
                                StSide       side)
 
1101
{
 
1102
  g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.);
 
1103
  g_return_val_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT, 0.);
 
1104
 
 
1105
  ensure_borders (node);
 
1106
 
 
1107
  return node->border_width[side];
 
1108
}
 
1109
 
 
1110
double
 
1111
st_theme_node_get_border_radius (StThemeNode *node,
 
1112
                                 StCorner     corner)
 
1113
{
 
1114
  g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.);
 
1115
  g_return_val_if_fail (corner >= ST_CORNER_TOPLEFT && corner <= ST_CORNER_BOTTOMLEFT, 0.);
 
1116
 
 
1117
  ensure_borders (node);
 
1118
 
 
1119
  return node->border_radius[corner];
 
1120
}
 
1121
 
 
1122
static GetFromTermResult
 
1123
get_background_color_from_term (StThemeNode  *node,
 
1124
                                CRTerm       *term,
 
1125
                                ClutterColor *color)
 
1126
{
 
1127
  GetFromTermResult result = get_color_from_term (node, term, color);
 
1128
  if (result == VALUE_NOT_FOUND)
 
1129
    {
 
1130
      if (term_is_transparent (term))
 
1131
        {
 
1132
          *color = TRANSPARENT_COLOR;
 
1133
          return VALUE_FOUND;
 
1134
        }
 
1135
    }
 
1136
 
 
1137
  return result;
 
1138
}
 
1139
 
 
1140
static void
 
1141
ensure_background (StThemeNode *node)
 
1142
{
 
1143
  int i;
 
1144
 
 
1145
  if (node->background_computed)
 
1146
    return;
 
1147
 
 
1148
  node->background_computed = TRUE;
 
1149
  node->background_color = TRANSPARENT_COLOR;
 
1150
 
 
1151
  ensure_properties (node);
 
1152
 
 
1153
  for (i = 0; i < node->n_properties; i++)
 
1154
    {
 
1155
      CRDeclaration *decl = node->properties[i];
 
1156
      const char *property_name = decl->property->stryng->str;
 
1157
 
 
1158
      if (g_str_has_prefix (property_name, "background"))
 
1159
        property_name += 10;
 
1160
      else
 
1161
        continue;
 
1162
 
 
1163
      if (strcmp (property_name, "") == 0)
 
1164
        {
 
1165
          /* We're very liberal here ... if we recognize any term in the expression we take it, and
 
1166
           * we ignore the rest. The actual specification is:
 
1167
           *
 
1168
           * background: [<'background-color'> || <'background-image'> || <'background-repeat'> || <'background-attachment'> || <'background-position'>] | inherit
 
1169
           */
 
1170
 
 
1171
          CRTerm *term;
 
1172
          /* background: property sets all terms to specified or default values */
 
1173
          node->background_color = TRANSPARENT_COLOR;
 
1174
          g_free (node->background_image);
 
1175
          node->background_image = NULL;
 
1176
 
 
1177
          for (term = decl->value; term; term = term->next)
 
1178
            {
 
1179
              GetFromTermResult result = get_background_color_from_term (node, term, &node->background_color);
 
1180
              if (result == VALUE_FOUND)
 
1181
                {
 
1182
                  /* color stored in node->background_color */
 
1183
                }
 
1184
              else if (result == VALUE_INHERIT)
 
1185
                {
 
1186
                  if (node->parent_node)
 
1187
                    {
 
1188
                      st_theme_node_get_background_color (node->parent_node, &node->background_color);
 
1189
                      node->background_image = g_strdup (st_theme_node_get_background_image (node->parent_node));
 
1190
                    }
 
1191
                }
 
1192
              else if (term_is_none (term))
 
1193
                {
 
1194
                  /* leave node->background_color as transparent */
 
1195
                }
 
1196
              else if (term->type == TERM_URI)
 
1197
                {
 
1198
                  node->background_image = _st_theme_resolve_url (node->theme,
 
1199
                                                                     decl->parent_statement->parent_sheet,
 
1200
                                                                     term->content.str->stryng->str);
 
1201
                }
 
1202
            }
 
1203
        }
 
1204
      else if (strcmp (property_name, "-color") == 0)
 
1205
        {
 
1206
          GetFromTermResult result;
 
1207
 
 
1208
          if (decl->value == NULL || decl->value->next != NULL)
 
1209
            continue;
 
1210
 
 
1211
          result = get_background_color_from_term (node, decl->value, &node->background_color);
 
1212
          if (result == VALUE_FOUND)
 
1213
            {
 
1214
              /* color stored in node->background_color */
 
1215
            }
 
1216
          else if (result == VALUE_INHERIT)
 
1217
            {
 
1218
              if (node->parent_node)
 
1219
                st_theme_node_get_background_color (node->parent_node, &node->background_color);
 
1220
            }
 
1221
        }
 
1222
      else if (strcmp (property_name, "-image") == 0)
 
1223
        {
 
1224
          if (decl->value == NULL || decl->value->next != NULL)
 
1225
            continue;
 
1226
 
 
1227
          if (decl->value->type == TERM_URI)
 
1228
            {
 
1229
              g_free (node->background_image);
 
1230
              node->background_image = _st_theme_resolve_url (node->theme,
 
1231
                                                              decl->parent_statement->parent_sheet,
 
1232
                                                              decl->value->content.str->stryng->str);
 
1233
            }
 
1234
          else if (term_is_inherit (decl->value))
 
1235
            {
 
1236
              g_free (node->background_image);
 
1237
              node->background_image = g_strdup (st_theme_node_get_background_image (node->parent_node));
 
1238
            }
 
1239
          else if (term_is_none (decl->value))
 
1240
            {
 
1241
              g_free (node->background_image);
 
1242
              node->background_image = NULL;
 
1243
            }
 
1244
        }
 
1245
    }
 
1246
}
 
1247
 
 
1248
void
 
1249
st_theme_node_get_background_color (StThemeNode  *node,
 
1250
                                    ClutterColor *color)
 
1251
{
 
1252
  g_return_if_fail (ST_IS_THEME_NODE (node));
 
1253
 
 
1254
  ensure_background (node);
 
1255
 
 
1256
  *color = node->background_color;
 
1257
}
 
1258
 
 
1259
const char *
 
1260
st_theme_node_get_background_image (StThemeNode *node)
 
1261
{
 
1262
  g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
 
1263
 
 
1264
  ensure_background (node);
 
1265
 
 
1266
  return node->background_image;
 
1267
}
 
1268
 
 
1269
void
 
1270
st_theme_node_get_foreground_color (StThemeNode  *node,
 
1271
                                    ClutterColor *color)
 
1272
{
 
1273
  g_return_if_fail (ST_IS_THEME_NODE (node));
 
1274
 
 
1275
  if (!node->foreground_computed)
 
1276
    {
 
1277
      int i;
 
1278
 
 
1279
      node->foreground_computed = TRUE;
 
1280
 
 
1281
      ensure_properties (node);
 
1282
 
 
1283
      for (i = node->n_properties - 1; i >= 0; i--)
 
1284
        {
 
1285
          CRDeclaration *decl = node->properties[i];
 
1286
 
 
1287
          if (strcmp (decl->property->stryng->str, "color") == 0)
 
1288
            {
 
1289
              GetFromTermResult result = get_color_from_term (node, decl->value, &node->foreground_color);
 
1290
              if (result == VALUE_FOUND)
 
1291
                goto out;
 
1292
              else if (result == VALUE_INHERIT)
 
1293
                break;
 
1294
            }
 
1295
        }
 
1296
 
 
1297
      if (node->parent_node)
 
1298
        st_theme_node_get_foreground_color (node->parent_node, &node->foreground_color);
 
1299
      else
 
1300
        node->foreground_color = BLACK_COLOR; /* default to black */
 
1301
    }
 
1302
 
 
1303
 out:
 
1304
  *color = node->foreground_color;
 
1305
}
 
1306
 
 
1307
void
 
1308
st_theme_node_get_border_color (StThemeNode  *node,
 
1309
                                StSide        side,
 
1310
                                ClutterColor *color)
 
1311
{
 
1312
  g_return_if_fail (ST_IS_THEME_NODE (node));
 
1313
  g_return_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT);
 
1314
 
 
1315
  ensure_borders (node);
 
1316
 
 
1317
  *color = node->border_color[side];
 
1318
}
 
1319
 
 
1320
double
 
1321
st_theme_node_get_padding (StThemeNode *node,
 
1322
                           StSide       side)
 
1323
{
 
1324
  g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.);
 
1325
  g_return_val_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT, 0.);
 
1326
 
 
1327
  ensure_borders (node);
 
1328
 
 
1329
  return node->padding[side];
 
1330
}
 
1331
 
 
1332
StTextDecoration
 
1333
st_theme_node_get_text_decoration (StThemeNode *node)
 
1334
{
 
1335
  int i;
 
1336
 
 
1337
  ensure_properties (node);
 
1338
 
 
1339
  for (i = node->n_properties - 1; i >= 0; i--)
 
1340
    {
 
1341
      CRDeclaration *decl = node->properties[i];
 
1342
 
 
1343
      if (strcmp (decl->property->stryng->str, "text-decoration") == 0)
 
1344
        {
 
1345
          CRTerm *term = decl->value;
 
1346
          StTextDecoration decoration = 0;
 
1347
 
 
1348
          /* Specification is none | [ underline || overline || line-through || blink ] | inherit
 
1349
           *
 
1350
           * We're a bit more liberal, and for example treat 'underline none' as the same as
 
1351
           * none.
 
1352
           */
 
1353
          for (; term; term = term->next)
 
1354
            {
 
1355
              if (term->type != TERM_IDENT)
 
1356
                goto next_decl;
 
1357
 
 
1358
              if (strcmp (term->content.str->stryng->str, "none") == 0)
 
1359
                {
 
1360
                  return 0;
 
1361
                }
 
1362
              else if (strcmp (term->content.str->stryng->str, "inherit") == 0)
 
1363
                {
 
1364
                  if (node->parent_node)
 
1365
                    return st_theme_node_get_text_decoration (node->parent_node);
 
1366
                }
 
1367
              else if (strcmp (term->content.str->stryng->str, "underline") == 0)
 
1368
                {
 
1369
                  decoration |= ST_TEXT_DECORATION_UNDERLINE;
 
1370
                }
 
1371
              else if (strcmp (term->content.str->stryng->str, "overline") == 0)
 
1372
                {
 
1373
                  decoration |= ST_TEXT_DECORATION_OVERLINE;
 
1374
                }
 
1375
              else if (strcmp (term->content.str->stryng->str, "line-through") == 0)
 
1376
                {
 
1377
                  decoration |= ST_TEXT_DECORATION_LINE_THROUGH;
 
1378
                }
 
1379
              else if (strcmp (term->content.str->stryng->str, "blink") == 0)
 
1380
                {
 
1381
                  decoration |= ST_TEXT_DECORATION_BLINK;
 
1382
                }
 
1383
              else
 
1384
                {
 
1385
                  goto next_decl;
 
1386
                }
 
1387
            }
 
1388
 
 
1389
          return decoration;
 
1390
        }
 
1391
 
 
1392
    next_decl:
 
1393
      ;
 
1394
    }
 
1395
 
 
1396
  return 0;
 
1397
}
 
1398
 
 
1399
static gboolean
 
1400
font_family_from_terms (CRTerm *term,
 
1401
                        char  **family)
 
1402
{
 
1403
  GString *family_string;
 
1404
  gboolean result = FALSE;
 
1405
  gboolean last_was_quoted = FALSE;
 
1406
 
 
1407
  if (!term)
 
1408
    return FALSE;
 
1409
 
 
1410
  family_string = g_string_new (NULL);
 
1411
 
 
1412
  while (term)
 
1413
    {
 
1414
      if (term->type != TERM_STRING && term->type != TERM_IDENT)
 
1415
        {
 
1416
          goto out;
 
1417
        }
 
1418
 
 
1419
      if (family_string->len > 0)
 
1420
        {
 
1421
          if (term->the_operator != COMMA && term->the_operator != NO_OP)
 
1422
            goto out;
 
1423
          /* Can concatenate two bare words, but not two quoted strings */
 
1424
          if ((term->the_operator == NO_OP && last_was_quoted) || term->type == TERM_STRING)
 
1425
            goto out;
 
1426
 
 
1427
          if (term->the_operator == NO_OP)
 
1428
            g_string_append (family_string, " ");
 
1429
          else
 
1430
            g_string_append (family_string, ", ");
 
1431
        }
 
1432
      else
 
1433
        {
 
1434
          if (term->the_operator != NO_OP)
 
1435
            goto out;
 
1436
        }
 
1437
 
 
1438
      g_string_append (family_string, term->content.str->stryng->str);
 
1439
 
 
1440
      term = term->next;
 
1441
    }
 
1442
 
 
1443
  result = TRUE;
 
1444
 
 
1445
 out:
 
1446
  if (result)
 
1447
    {
 
1448
      *family = g_string_free (family_string, FALSE);
 
1449
      return TRUE;
 
1450
    }
 
1451
  else
 
1452
    {
 
1453
      *family = g_string_free (family_string, TRUE);
 
1454
      return FALSE;
 
1455
    }
 
1456
}
 
1457
 
 
1458
/* In points */
 
1459
static int font_sizes[] = {
 
1460
  6 * 1024,   /* xx-small */
 
1461
  8 * 1024,   /* x-small */
 
1462
  10 * 1024,  /* small */
 
1463
  12 * 1024,  /* medium */
 
1464
  16 * 1024,  /* large */
 
1465
  20 * 1024,  /* x-large */
 
1466
  24 * 1024,  /* xx-large */
 
1467
};
 
1468
 
 
1469
static gboolean
 
1470
font_size_from_term (StThemeNode *node,
 
1471
                     CRTerm      *term,
 
1472
                     double      *size)
 
1473
{
 
1474
  if (term->type == TERM_IDENT)
 
1475
    {
 
1476
      double resolution = st_theme_context_get_resolution (node->context);
 
1477
      /* We work in integers to avoid double comparisons when converting back
 
1478
       * from a size in pixels to a logical size.
 
1479
       */
 
1480
      int size_points = (int)(0.5 + *size * (72. / resolution));
 
1481
 
 
1482
      if (strcmp (term->content.str->stryng->str, "xx-small") == 0)
 
1483
        size_points = font_sizes[0];
 
1484
      else if (strcmp (term->content.str->stryng->str, "x-small") == 0)
 
1485
        size_points = font_sizes[1];
 
1486
      else if (strcmp (term->content.str->stryng->str, "small") == 0)
 
1487
        size_points = font_sizes[2];
 
1488
      else if (strcmp (term->content.str->stryng->str, "medium") == 0)
 
1489
        size_points = font_sizes[3];
 
1490
      else if (strcmp (term->content.str->stryng->str, "large") == 0)
 
1491
        size_points = font_sizes[4];
 
1492
      else if (strcmp (term->content.str->stryng->str, "x-large") == 0)
 
1493
        size_points = font_sizes[5];
 
1494
      else if (strcmp (term->content.str->stryng->str, "xx-large") == 0)
 
1495
        size_points = font_sizes[6];
 
1496
      else if (strcmp (term->content.str->stryng->str, "smaller") == 0)
 
1497
        {
 
1498
          /* Find the standard size equal to or smaller than the current size */
 
1499
          int i = 0;
 
1500
 
 
1501
          while (i <= 6 && font_sizes[i] < size_points)
 
1502
            i++;
 
1503
 
 
1504
          if (i > 6)
 
1505
            {
 
1506
              /* original size greater than any standard size */
 
1507
              size_points = (int)(0.5 + size_points / 1.2);
 
1508
            }
 
1509
          else
 
1510
            {
 
1511
              /* Go one smaller than that, if possible */
 
1512
              if (i > 0)
 
1513
                i--;
 
1514
 
 
1515
              size_points = font_sizes[i];
 
1516
            }
 
1517
        }
 
1518
      else if (strcmp (term->content.str->stryng->str, "larger") == 0)
 
1519
        {
 
1520
          /* Find the standard size equal to or larger than the current size */
 
1521
          int i = 6;
 
1522
 
 
1523
          while (i >= 0 && font_sizes[i] > size_points)
 
1524
            i--;
 
1525
 
 
1526
          if (i < 0) /* original size smaller than any standard size */
 
1527
            i = 0;
 
1528
 
 
1529
          /* Go one larger than that, if possible */
 
1530
          if (i < 6)
 
1531
            i++;
 
1532
 
 
1533
          size_points = font_sizes[i];
 
1534
        }
 
1535
      else
 
1536
        {
 
1537
          return FALSE;
 
1538
        }
 
1539
 
 
1540
      *size = size_points * (resolution / 72.);
 
1541
      return TRUE;
 
1542
 
 
1543
    }
 
1544
  else if (term->type == TERM_NUMBER && term->content.num->type == NUM_PERCENTAGE)
 
1545
    {
 
1546
      *size *= term->content.num->val / 100.;
 
1547
      return TRUE;
 
1548
    }
 
1549
  else if (get_length_from_term (node, term, TRUE, size) == VALUE_FOUND)
 
1550
    {
 
1551
      /* Convert from pixels to Pango units */
 
1552
      *size *= 1024;
 
1553
      return TRUE;
 
1554
    }
 
1555
 
 
1556
  return FALSE;
 
1557
}
 
1558
 
 
1559
static gboolean
 
1560
font_weight_from_term (CRTerm      *term,
 
1561
                       PangoWeight *weight,
 
1562
                       gboolean    *weight_absolute)
 
1563
{
 
1564
  if (term->type == TERM_NUMBER)
 
1565
    {
 
1566
      int weight_int;
 
1567
 
 
1568
      /* The spec only allows numeric weights from 100-900, though Pango
 
1569
       * will handle any number. We just let anything through.
 
1570
       */
 
1571
      if (term->content.num->type != NUM_GENERIC)
 
1572
        return FALSE;
 
1573
 
 
1574
      weight_int = (int)(0.5 + term->content.num->val);
 
1575
 
 
1576
      *weight = weight_int;
 
1577
      *weight_absolute = TRUE;
 
1578
 
 
1579
    }
 
1580
  else if (term->type == TERM_IDENT)
 
1581
    {
 
1582
      /* FIXME: handle INHERIT */
 
1583
 
 
1584
      if (strcmp (term->content.str->stryng->str, "bold") == 0)
 
1585
        {
 
1586
          *weight = PANGO_WEIGHT_BOLD;
 
1587
          *weight_absolute = TRUE;
 
1588
        }
 
1589
      else if (strcmp (term->content.str->stryng->str, "normal") == 0)
 
1590
        {
 
1591
          *weight = PANGO_WEIGHT_NORMAL;
 
1592
          *weight_absolute = TRUE;
 
1593
        }
 
1594
      else if (strcmp (term->content.str->stryng->str, "bolder") == 0)
 
1595
        {
 
1596
          *weight = PANGO_WEIGHT_BOLD;
 
1597
          *weight_absolute = FALSE;
 
1598
        }
 
1599
      else if (strcmp (term->content.str->stryng->str, "lighter") == 0)
 
1600
        {
 
1601
          *weight = PANGO_WEIGHT_LIGHT;
 
1602
          *weight_absolute = FALSE;
 
1603
        }
 
1604
      else
 
1605
        {
 
1606
          return FALSE;
 
1607
        }
 
1608
 
 
1609
    }
 
1610
  else
 
1611
    {
 
1612
      return FALSE;
 
1613
    }
 
1614
 
 
1615
  return TRUE;
 
1616
}
 
1617
 
 
1618
static gboolean
 
1619
font_style_from_term (CRTerm     *term,
 
1620
                      PangoStyle *style)
 
1621
{
 
1622
  if (term->type != TERM_IDENT)
 
1623
    return FALSE;
 
1624
 
 
1625
  /* FIXME: handle INHERIT */
 
1626
 
 
1627
  if (strcmp (term->content.str->stryng->str, "normal") == 0)
 
1628
    *style = PANGO_STYLE_NORMAL;
 
1629
  else if (strcmp (term->content.str->stryng->str, "oblique") == 0)
 
1630
    *style = PANGO_STYLE_OBLIQUE;
 
1631
  else if (strcmp (term->content.str->stryng->str, "italic") == 0)
 
1632
    *style = PANGO_STYLE_ITALIC;
 
1633
  else
 
1634
    return FALSE;
 
1635
 
 
1636
  return TRUE;
 
1637
}
 
1638
 
 
1639
static gboolean
 
1640
font_variant_from_term (CRTerm       *term,
 
1641
                        PangoVariant *variant)
 
1642
{
 
1643
  if (term->type != TERM_IDENT)
 
1644
    return FALSE;
 
1645
 
 
1646
  /* FIXME: handle INHERIT */
 
1647
 
 
1648
  if (strcmp (term->content.str->stryng->str, "normal") == 0)
 
1649
    *variant = PANGO_VARIANT_NORMAL;
 
1650
  else if (strcmp (term->content.str->stryng->str, "small-caps") == 0)
 
1651
    *variant = PANGO_VARIANT_SMALL_CAPS;
 
1652
  else
 
1653
    return FALSE;
 
1654
 
 
1655
  return TRUE;
 
1656
}
 
1657
 
 
1658
const PangoFontDescription *
 
1659
st_theme_node_get_font (StThemeNode *node)
 
1660
{
 
1661
  PangoStyle font_style;
 
1662
  gboolean font_style_set = FALSE;
 
1663
  PangoVariant variant;
 
1664
  gboolean variant_set = FALSE;
 
1665
  PangoWeight weight;
 
1666
  gboolean weight_absolute;
 
1667
  gboolean weight_set = FALSE;
 
1668
  double parent_size;
 
1669
  double size = 0.; /* Suppress warning */
 
1670
  gboolean size_set = FALSE;
 
1671
  char *family = NULL;
 
1672
  int i;
 
1673
 
 
1674
  if (node->font_desc)
 
1675
    return node->font_desc;
 
1676
 
 
1677
  node->font_desc = pango_font_description_copy (get_parent_font (node));
 
1678
  parent_size = pango_font_description_get_size (node->font_desc);
 
1679
  if (!pango_font_description_get_size_is_absolute (node->font_desc))
 
1680
    {
 
1681
      double resolution = st_theme_context_get_resolution (node->context);
 
1682
      parent_size *= (resolution / 72.);
 
1683
    }
 
1684
 
 
1685
  ensure_properties (node);
 
1686
 
 
1687
  for (i = 0; i < node->n_properties; i++)
 
1688
    {
 
1689
      CRDeclaration *decl = node->properties[i];
 
1690
 
 
1691
      if (strcmp (decl->property->stryng->str, "font") == 0)
 
1692
        {
 
1693
          PangoStyle tmp_style = PANGO_STYLE_NORMAL;
 
1694
          PangoVariant tmp_variant = PANGO_VARIANT_NORMAL;
 
1695
          PangoWeight tmp_weight = PANGO_WEIGHT_NORMAL;
 
1696
          gboolean tmp_weight_absolute = TRUE;
 
1697
          double tmp_size;
 
1698
          CRTerm *term = decl->value;
 
1699
 
 
1700
          /* A font specification starts with node/variant/weight
 
1701
           * in any order. Each is allowed to be specified only once,
 
1702
           * but we don't enforce that.
 
1703
           */
 
1704
          for (; term; term = term->next)
 
1705
            {
 
1706
              if (font_style_from_term (term, &tmp_style))
 
1707
                continue;
 
1708
              if (font_variant_from_term (term, &tmp_variant))
 
1709
                continue;
 
1710
              if (font_weight_from_term (term, &tmp_weight, &tmp_weight_absolute))
 
1711
                continue;
 
1712
 
 
1713
              break;
 
1714
            }
 
1715
 
 
1716
          /* The size is mandatory */
 
1717
 
 
1718
          if (term == NULL || term->type != TERM_NUMBER)
 
1719
            {
 
1720
              g_warning ("Size missing from font property");
 
1721
              continue;
 
1722
            }
 
1723
 
 
1724
          tmp_size = parent_size;
 
1725
          if (!font_size_from_term (node, term, &tmp_size))
 
1726
            {
 
1727
              g_warning ("Couldn't parse size in font property");
 
1728
              continue;
 
1729
            }
 
1730
 
 
1731
          term = term->next;
 
1732
 
 
1733
          if (term != NULL && term->type && TERM_NUMBER && term->the_operator == DIVIDE)
 
1734
            {
 
1735
              /* Ignore line-height specification */
 
1736
              term = term->next;
 
1737
            }
 
1738
 
 
1739
          /* the font family is mandatory - it is a comma-separated list of
 
1740
           * names.
 
1741
           */
 
1742
          if (!font_family_from_terms (term, &family))
 
1743
            {
 
1744
              g_warning ("Couldn't parse family in font property");
 
1745
              continue;
 
1746
            }
 
1747
 
 
1748
          font_style = tmp_style;
 
1749
          font_style_set = TRUE;
 
1750
          weight = tmp_weight;
 
1751
          weight_absolute = tmp_weight_absolute;
 
1752
          weight_set = TRUE;
 
1753
          variant = tmp_variant;
 
1754
          variant_set = TRUE;
 
1755
 
 
1756
          size = tmp_size;
 
1757
          size_set = TRUE;
 
1758
 
 
1759
        }
 
1760
      else if (strcmp (decl->property->stryng->str, "font-family") == 0)
 
1761
        {
 
1762
          if (!font_family_from_terms (decl->value, &family))
 
1763
            {
 
1764
              g_warning ("Couldn't parse family in font property");
 
1765
              continue;
 
1766
            }
 
1767
        }
 
1768
      else if (strcmp (decl->property->stryng->str, "font-weight") == 0)
 
1769
        {
 
1770
          if (decl->value == NULL || decl->value->next != NULL)
 
1771
            continue;
 
1772
 
 
1773
          if (font_weight_from_term (decl->value, &weight, &weight_absolute))
 
1774
            weight_set = TRUE;
 
1775
        }
 
1776
      else if (strcmp (decl->property->stryng->str, "font-style") == 0)
 
1777
        {
 
1778
          if (decl->value == NULL || decl->value->next != NULL)
 
1779
            continue;
 
1780
 
 
1781
          if (font_style_from_term (decl->value, &font_style))
 
1782
            font_style_set = TRUE;
 
1783
        }
 
1784
      else if (strcmp (decl->property->stryng->str, "font-variant") == 0)
 
1785
        {
 
1786
          if (decl->value == NULL || decl->value->next != NULL)
 
1787
            continue;
 
1788
 
 
1789
          if (font_variant_from_term (decl->value, &variant))
 
1790
            variant_set = TRUE;
 
1791
        }
 
1792
      else if (strcmp (decl->property->stryng->str, "font-size") == 0)
 
1793
        {
 
1794
          gdouble tmp_size;
 
1795
          if (decl->value == NULL || decl->value->next != NULL)
 
1796
            continue;
 
1797
 
 
1798
          tmp_size = parent_size;
 
1799
          if (font_size_from_term (node, decl->value, &tmp_size))
 
1800
            {
 
1801
              size = tmp_size;
 
1802
              size_set = TRUE;
 
1803
            }
 
1804
        }
 
1805
    }
 
1806
 
 
1807
  if (family)
 
1808
    pango_font_description_set_family (node->font_desc, family);
 
1809
 
 
1810
  if (size_set)
 
1811
    pango_font_description_set_absolute_size (node->font_desc, size);
 
1812
 
 
1813
  if (weight_set)
 
1814
    {
 
1815
      if (!weight_absolute)
 
1816
        {
 
1817
          /* bolder/lighter are supposed to switch between available styles, but with
 
1818
           * font substitution, that gets to be a pretty fuzzy concept. So we use
 
1819
           * a fixed step of 200. (The spec says 100, but that might not take us from
 
1820
           * normal to bold.
 
1821
           */
 
1822
 
 
1823
          PangoWeight old_weight = pango_font_description_get_weight (node->font_desc);
 
1824
          if (weight == PANGO_WEIGHT_BOLD)
 
1825
            weight = old_weight + 200;
 
1826
          else
 
1827
            weight = old_weight - 200;
 
1828
 
 
1829
          if (weight < 100)
 
1830
            weight = 100;
 
1831
          if (weight > 900)
 
1832
            weight = 900;
 
1833
        }
 
1834
 
 
1835
      pango_font_description_set_weight (node->font_desc, weight);
 
1836
    }
 
1837
 
 
1838
  if (font_style_set)
 
1839
    pango_font_description_set_style (node->font_desc, font_style);
 
1840
  if (variant_set)
 
1841
    pango_font_description_set_variant (node->font_desc, variant);
 
1842
 
 
1843
  return node->font_desc;
 
1844
}
 
1845
 
 
1846
/**
 
1847
 * st_theme_node_get_border_image:
 
1848
 * @node: a #StThemeNode
 
1849
 *
 
1850
 * Gets the value for the border-image style property
 
1851
 *
 
1852
 * Return value: (transfer none): the border image, or %NULL
 
1853
 *   if there is no border image.
 
1854
 */
 
1855
StBorderImage *
 
1856
st_theme_node_get_border_image (StThemeNode *node)
 
1857
{
 
1858
  int i;
 
1859
 
 
1860
  if (node->border_image_computed)
 
1861
    return node->border_image;
 
1862
 
 
1863
  node->border_image = NULL;
 
1864
  node->border_image_computed = TRUE;
 
1865
 
 
1866
  ensure_properties (node);
 
1867
 
 
1868
  for (i = node->n_properties - 1; i >= 0; i--)
 
1869
    {
 
1870
      CRDeclaration *decl = node->properties[i];
 
1871
 
 
1872
      if (strcmp (decl->property->stryng->str, "border-image") == 0)
 
1873
        {
 
1874
          CRTerm *term = decl->value;
 
1875
          int borders[4];
 
1876
          int n_borders = 0;
 
1877
          int i;
 
1878
 
 
1879
          const char *url;
 
1880
          int border_top;
 
1881
          int border_right;
 
1882
          int border_bottom;
 
1883
          int border_left;
 
1884
 
 
1885
          char *filename;
 
1886
 
 
1887
          /* First term must be the URL to the image */
 
1888
          if (term->type != TERM_URI)
 
1889
            goto next_property;
 
1890
 
 
1891
          url = term->content.str->stryng->str;
 
1892
 
 
1893
          term = term->next;
 
1894
 
 
1895
          /* Followed by 0 to 4 numbers or percentages. *Not lengths*. The interpretation
 
1896
           * of a number is supposed to be pixels if the image is pixel based, otherwise CSS pixels.
 
1897
           */
 
1898
          for (i = 0; i < 4; i++)
 
1899
            {
 
1900
              if (term == NULL)
 
1901
                break;
 
1902
 
 
1903
              if (term->type != TERM_NUMBER)
 
1904
                goto next_property;
 
1905
 
 
1906
              if (term->content.num->type == NUM_GENERIC)
 
1907
                {
 
1908
                  borders[n_borders] = (int)(0.5 + term->content.num->val);
 
1909
                  n_borders++;
 
1910
                }
 
1911
              else if (term->content.num->type == NUM_PERCENTAGE)
 
1912
                {
 
1913
                  /* This would be easiest to support if we moved image handling into StBorderImage */
 
1914
                  g_warning ("Percentages not supported for border-image");
 
1915
                  goto next_property;
 
1916
                }
 
1917
              else
 
1918
                goto next_property;
 
1919
 
 
1920
              term = term->next;
 
1921
            }
 
1922
 
 
1923
          switch (n_borders)
 
1924
            {
 
1925
            case 0:
 
1926
              border_top = border_right = border_bottom = border_left = 0;
 
1927
              break;
 
1928
            case 1:
 
1929
              border_top = border_right = border_bottom = border_left = borders[0];
 
1930
              break;
 
1931
            case 2:
 
1932
              border_top = border_bottom = borders[0];
 
1933
              border_left = border_right = borders[1];
 
1934
              break;
 
1935
            case 3:
 
1936
              border_top = borders[0];
 
1937
              border_left = border_right = borders[1];
 
1938
              border_bottom = borders[2];
 
1939
              break;
 
1940
            case 4:
 
1941
            default:
 
1942
              border_top = borders[0];
 
1943
              border_right = borders[1];
 
1944
              border_bottom = borders[2];
 
1945
              border_left = borders[3];
 
1946
              break;
 
1947
            }
 
1948
 
 
1949
          filename = _st_theme_resolve_url (node->theme, decl->parent_statement->parent_sheet, url);
 
1950
          if (filename == NULL)
 
1951
            goto next_property;
 
1952
 
 
1953
          node->border_image = st_border_image_new (filename,
 
1954
                                                    border_top, border_right, border_bottom, border_left);
 
1955
 
 
1956
          g_free (filename);
 
1957
 
 
1958
          return node->border_image;
 
1959
        }
 
1960
 
 
1961
    next_property:
 
1962
      ;
 
1963
    }
 
1964
 
 
1965
  return NULL;
 
1966
}
 
1967
 
 
1968
static float
 
1969
get_width_inc (StThemeNode *node)
 
1970
{
 
1971
  return ((int)(0.5 + node->border_width[ST_SIDE_LEFT]) + node->padding[ST_SIDE_LEFT] +
 
1972
          (int)(0.5 + node->border_width[ST_SIDE_RIGHT]) + node->padding[ST_SIDE_RIGHT]);
 
1973
}
 
1974
 
 
1975
static float
 
1976
get_height_inc (StThemeNode *node)
 
1977
{
 
1978
  return ((int)(0.5 + node->border_width[ST_SIDE_TOP]) + node->padding[ST_SIDE_TOP] +
 
1979
          (int)(0.5 + node->border_width[ST_SIDE_BOTTOM]) + node->padding[ST_SIDE_BOTTOM]);
 
1980
}
 
1981
 
 
1982
/**
 
1983
 * st_theme_node_adjust_for_height:
 
1984
 * @node: a #StThemeNode
 
1985
 * @for_height: (inout): the "for height" to adjust
 
1986
 *
 
1987
 * Adjusts a "for height" passed to clutter_actor_get_preferred_width() to
 
1988
 * account for borders and padding. This is a convenience function meant
 
1989
 * to be called from a get_preferred_width() method of a #ClutterActor
 
1990
 * subclass. The value after adjustment is the height available for the actor's
 
1991
 * content.
 
1992
 */
 
1993
void
 
1994
st_theme_node_adjust_for_height (StThemeNode  *node,
 
1995
                                 float        *for_height)
 
1996
{
 
1997
  g_return_if_fail (ST_IS_THEME_NODE (node));
 
1998
  g_return_if_fail (for_height != NULL);
 
1999
 
 
2000
  if (*for_height >= 0)
 
2001
    {
 
2002
      float height_inc = get_height_inc (node);
 
2003
      *for_height = MAX (0, *for_height - height_inc);
 
2004
    }
 
2005
}
 
2006
 
 
2007
/**
 
2008
 * st_theme_node_adjust_preferred_width:
 
2009
 * @node: a #StThemeNode
 
2010
 * @min_width_p: (inout) (allow-none): the width to adjust
 
2011
 * @for_height: (inout): the height to adjust
 
2012
 *
 
2013
 * Adjusts the minimum and natural width computed for an actor by
 
2014
 * adding on the necessary space for borders and padding. This is a
 
2015
 * convenience function meant to be called from the get_preferred_width()
 
2016
 * method of a #ClutterActor subclass
 
2017
 */
 
2018
void
 
2019
st_theme_node_adjust_preferred_width (StThemeNode  *node,
 
2020
                                      float        *min_width_p,
 
2021
                                      float        *natural_width_p)
 
2022
{
 
2023
  float width_inc;
 
2024
 
 
2025
  g_return_if_fail (ST_IS_THEME_NODE (node));
 
2026
 
 
2027
  ensure_borders (node);
 
2028
 
 
2029
  width_inc = get_width_inc (node);
 
2030
 
 
2031
  if (min_width_p)
 
2032
    *min_width_p += width_inc;
 
2033
  if (natural_width_p)
 
2034
    *natural_width_p += width_inc;
 
2035
}
 
2036
 
 
2037
/**
 
2038
 * st_theme_node_adjust_for_width:
 
2039
 * @node: a #StThemeNode
 
2040
 * @for_width: (inout): the "for width" to adjust
 
2041
 *
 
2042
 * Adjusts a "for width" passed to clutter_actor_get_preferred_height() to
 
2043
 * account for borders and padding. This is a convenience function meant
 
2044
 * to be called from a get_preferred_height() method of a #ClutterActor
 
2045
 * subclass. The value after adjustmnet is the width available for the actor's
 
2046
 * content.
 
2047
 */
 
2048
void
 
2049
st_theme_node_adjust_for_width (StThemeNode  *node,
 
2050
                                float        *for_width)
 
2051
{
 
2052
  g_return_if_fail (ST_IS_THEME_NODE (node));
 
2053
  g_return_if_fail (for_width != NULL);
 
2054
 
 
2055
  if (*for_width >= 0)
 
2056
    {
 
2057
      float width_inc = get_width_inc (node);
 
2058
      *for_width = MAX (0, *for_width - width_inc);
 
2059
    }
 
2060
}
 
2061
 
 
2062
/**
 
2063
 * st_theme_node_adjust_preferred_height:
 
2064
 * @node: a #StThemeNode
 
2065
 * @min_height_p: (inout) (allow-none): the height to adjust
 
2066
 * @for_height: (inout): the height to adjust
 
2067
 *
 
2068
 * Adjusts the minimum and natural height computed for an actor by
 
2069
 * adding on the necessary space for borders and padding. This is a
 
2070
 * convenience function meant to be called from the get_preferred_height()
 
2071
 * method of a #ClutterActor subclass
 
2072
 */
 
2073
void
 
2074
st_theme_node_adjust_preferred_height (StThemeNode  *node,
 
2075
                                       float           *min_height_p,
 
2076
                                       float           *natural_height_p)
 
2077
{
 
2078
  float height_inc;
 
2079
 
 
2080
  g_return_if_fail (ST_IS_THEME_NODE (node));
 
2081
 
 
2082
  ensure_borders (node);
 
2083
 
 
2084
  height_inc = get_height_inc (node);
 
2085
 
 
2086
  if (min_height_p)
 
2087
    *min_height_p += height_inc;
 
2088
  if (natural_height_p)
 
2089
    *natural_height_p += height_inc;
 
2090
}
 
2091
 
 
2092
/**
 
2093
 * st_theme_node_get_content_box:
 
2094
 * @node: a #StThemeNode
 
2095
 * @allocation: the box allocated to a #ClutterAlctor
 
2096
 * @content_box: (out): computed box occupied by the actor's content
 
2097
 *
 
2098
 * Gets the box within an actor's allocation that contents the content
 
2099
 * of an actor (excluding borders and padding). This is a convenience function
 
2100
 * meant to be used from the allocate() or paint() methods of a #ClutterActor
 
2101
 * subclass.
 
2102
 */
 
2103
void
 
2104
st_theme_node_get_content_box (StThemeNode           *node,
 
2105
                               const ClutterActorBox *allocation,
 
2106
                               ClutterActorBox       *content_box)
 
2107
{
 
2108
  g_return_if_fail (ST_IS_THEME_NODE (node));
 
2109
 
 
2110
  ensure_borders (node);
 
2111
 
 
2112
  content_box->x1 = (int)(0.5 + node->border_width[ST_SIDE_LEFT]) + node->padding[ST_SIDE_LEFT];
 
2113
  content_box->y1 = (int)(0.5 + node->border_width[ST_SIDE_TOP]) + node->padding[ST_SIDE_TOP];
 
2114
  content_box->x2 = allocation->x2 - allocation->x1 - ((int)(0.5 + node->border_width[ST_SIDE_RIGHT]) + node->padding[ST_SIDE_RIGHT]);
 
2115
  content_box->y2 = allocation->y2 - allocation->y1 - ((int)(0.5 + node->border_width[ST_SIDE_BOTTOM]) + node->padding[ST_SIDE_BOTTOM]);
 
2116
}
 
2117
 
 
2118
 
 
2119
/**
 
2120
 * st_theme_node_geometry_equal:
 
2121
 * @node: a #StThemeNode
 
2122
 * @node: a different #StThemeNode
 
2123
 *
 
2124
 * Tests if two theme nodes have the same borders and padding; this can be
 
2125
 * used to optimize having to relayout when the style applied to a Clutter
 
2126
 * actor changes colors without changing the geometry.
 
2127
 */
 
2128
gboolean
 
2129
st_theme_node_geometry_equal (StThemeNode *node,
 
2130
                              StThemeNode *other)
 
2131
{
 
2132
  StSide side;
 
2133
 
 
2134
  ensure_borders (node);
 
2135
  ensure_borders (other);
 
2136
 
 
2137
  for (side = ST_SIDE_TOP; side <= ST_SIDE_LEFT; side++)
 
2138
    {
 
2139
      if (node->border_width[side] != other->border_width[side])
 
2140
        return FALSE;
 
2141
      if (node->padding[side] != other->padding[side])
 
2142
        return FALSE;
 
2143
    }
 
2144
 
 
2145
  return TRUE;
 
2146
}