gtkstyleproperty.c 50.7 KB
Newer Older
Benjamin Otte's avatar
Benjamin Otte committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/* GTK - The GIMP Toolkit
 * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include "config.h"

22
#include "gtkstylepropertyprivate.h"
Benjamin Otte's avatar
Benjamin Otte committed
23 24 25 26 27 28 29 30 31

#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include <gdk-pixbuf/gdk-pixbuf.h>
#include <cairo-gobject.h>

#include "gtkcssprovider.h"
Benjamin Otte's avatar
Benjamin Otte committed
32
#include "gtkcssparserprivate.h"
Benjamin Otte's avatar
Benjamin Otte committed
33 34 35

/* the actual parsers we have */
#include "gtkanimationdescription.h"
36
#include "gtkbindings.h"
Benjamin Otte's avatar
Benjamin Otte committed
37 38
#include "gtk9slice.h"
#include "gtkgradient.h"
39
#include "gtkshadowprivate.h"
Benjamin Otte's avatar
Benjamin Otte committed
40
#include "gtkthemingengine.h"
41
#include "gtktypebuiltins.h"
Benjamin Otte's avatar
Benjamin Otte committed
42

43
typedef gboolean (* ParseFunc)        (GtkCssParser  *parser,
Benjamin Otte's avatar
Benjamin Otte committed
44
                                       GFile         *base,
45
                                       GValue        *value);
46 47
typedef void     (* PrintFunc)        (const GValue  *value,
                                       GString       *string);
Benjamin Otte's avatar
Benjamin Otte committed
48

49
static GHashTable *parse_funcs = NULL;
50
static GHashTable *print_funcs = NULL;
51
static GHashTable *properties = NULL;
Benjamin Otte's avatar
Benjamin Otte committed
52 53 54

static void
register_conversion_function (GType          type,
55
                              ParseFunc      parse,
56
                              PrintFunc      print)
Benjamin Otte's avatar
Benjamin Otte committed
57
{
58 59
  if (parse)
    g_hash_table_insert (parse_funcs, GSIZE_TO_POINTER (type), parse);
60 61
  if (print)
    g_hash_table_insert (print_funcs, GSIZE_TO_POINTER (type), print);
Benjamin Otte's avatar
Benjamin Otte committed
62 63 64 65 66
}

/*** IMPLEMENTATIONS ***/

static gboolean 
67 68 69
rgba_value_parse (GtkCssParser *parser,
                  GFile        *base,
                  GValue       *value)
Benjamin Otte's avatar
Benjamin Otte committed
70 71 72 73
{
  GtkSymbolicColor *symbolic;
  GdkRGBA rgba;

74
  symbolic = _gtk_css_parser_read_symbolic_color (parser);
Benjamin Otte's avatar
Benjamin Otte committed
75 76 77
  if (symbolic == NULL)
    return FALSE;

78 79 80
  if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
    {
      g_value_set_boxed (value, &rgba);
81
      gtk_symbolic_color_unref (symbolic);
82 83 84 85 86 87 88 89
    }
  else
    {
      g_value_unset (value);
      g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
      g_value_take_boxed (value, symbolic);
    }

Benjamin Otte's avatar
Benjamin Otte committed
90 91 92
  return TRUE;
}

93 94 95
static void
rgba_value_print (const GValue *value,
                  GString      *string)
Benjamin Otte's avatar
Benjamin Otte committed
96 97 98 99
{
  const GdkRGBA *rgba = g_value_get_boxed (value);

  if (rgba == NULL)
100 101 102 103 104 105 106
    g_string_append (string, "none");
  else
    {
      char *s = gdk_rgba_to_string (rgba);
      g_string_append (string, s);
      g_free (s);
    }
Benjamin Otte's avatar
Benjamin Otte committed
107 108 109
}

static gboolean 
110 111 112
color_value_parse (GtkCssParser *parser,
                   GFile        *base,
                   GValue       *value)
Benjamin Otte's avatar
Benjamin Otte committed
113 114
{
  GtkSymbolicColor *symbolic;
115
  GdkRGBA rgba;
Benjamin Otte's avatar
Benjamin Otte committed
116

117
  symbolic = _gtk_css_parser_read_symbolic_color (parser);
Benjamin Otte's avatar
Benjamin Otte committed
118 119 120
  if (symbolic == NULL)
    return FALSE;

121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
  if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
    {
      GdkColor color;

      color.red = rgba.red * 65535. + 0.5;
      color.green = rgba.green * 65535. + 0.5;
      color.blue = rgba.blue * 65535. + 0.5;

      g_value_set_boxed (value, &color);
    }
  else
    {
      g_value_unset (value);
      g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
      g_value_take_boxed (value, symbolic);
    }

Benjamin Otte's avatar
Benjamin Otte committed
138 139 140
  return TRUE;
}

141 142 143
static void
color_value_print (const GValue *value,
                   GString      *string)
Benjamin Otte's avatar
Benjamin Otte committed
144 145 146 147
{
  const GdkColor *color = g_value_get_boxed (value);

  if (color == NULL)
148 149 150 151 152 153 154
    g_string_append (string, "none");
  else
    {
      char *s = gdk_color_to_string (color);
      g_string_append (string, s);
      g_free (s);
    }
Benjamin Otte's avatar
Benjamin Otte committed
155 156
}

157 158 159 160
static gboolean
symbolic_color_value_parse (GtkCssParser *parser,
                            GFile        *base,
                            GValue       *value)
Benjamin Otte's avatar
Benjamin Otte committed
161 162 163
{
  GtkSymbolicColor *symbolic;

164
  symbolic = _gtk_css_parser_read_symbolic_color (parser);
Benjamin Otte's avatar
Benjamin Otte committed
165 166 167 168 169 170 171
  if (symbolic == NULL)
    return FALSE;

  g_value_take_boxed (value, symbolic);
  return TRUE;
}

172 173 174
static void
symbolic_color_value_print (const GValue *value,
                            GString      *string)
Benjamin Otte's avatar
Benjamin Otte committed
175 176 177 178
{
  GtkSymbolicColor *symbolic = g_value_get_boxed (value);

  if (symbolic == NULL)
179 180 181 182 183 184 185
    g_string_append (string, "none");
  else
    {
      char *s = gtk_symbolic_color_to_string (symbolic);
      g_string_append (string, s);
      g_free (s);
    }
Benjamin Otte's avatar
Benjamin Otte committed
186 187 188
}

static gboolean 
189 190 191
font_description_value_parse (GtkCssParser *parser,
                              GFile        *base,
                              GValue       *value)
Benjamin Otte's avatar
Benjamin Otte committed
192 193
{
  PangoFontDescription *font_desc;
194 195 196 197 198
  char *str;

  str = _gtk_css_parser_read_value (parser);
  if (str == NULL)
    return FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
199 200

  font_desc = pango_font_description_from_string (str);
201
  g_free (str);
Benjamin Otte's avatar
Benjamin Otte committed
202 203 204 205
  g_value_take_boxed (value, font_desc);
  return TRUE;
}

206 207 208
static void
font_description_value_print (const GValue *value,
                              GString      *string)
Benjamin Otte's avatar
Benjamin Otte committed
209 210 211 212
{
  const PangoFontDescription *desc = g_value_get_boxed (value);

  if (desc == NULL)
213 214 215 216 217 218 219
    g_string_append (string, "none");
  else
    {
      char *s = pango_font_description_to_string (desc);
      g_string_append (string, s);
      g_free (s);
    }
Benjamin Otte's avatar
Benjamin Otte committed
220 221 222
}

static gboolean 
223 224 225
boolean_value_parse (GtkCssParser *parser,
                     GFile        *base,
                     GValue       *value)
Benjamin Otte's avatar
Benjamin Otte committed
226
{
227 228
  if (_gtk_css_parser_try (parser, "true", TRUE) ||
      _gtk_css_parser_try (parser, "1", TRUE))
Benjamin Otte's avatar
Benjamin Otte committed
229 230 231 232
    {
      g_value_set_boolean (value, TRUE);
      return TRUE;
    }
233 234
  else if (_gtk_css_parser_try (parser, "false", TRUE) ||
           _gtk_css_parser_try (parser, "0", TRUE))
Benjamin Otte's avatar
Benjamin Otte committed
235 236 237 238
    {
      g_value_set_boolean (value, FALSE);
      return TRUE;
    }
239 240 241 242 243
  else
    {
      _gtk_css_parser_error (parser, "Expected a boolean value");
      return FALSE;
    }
Benjamin Otte's avatar
Benjamin Otte committed
244 245
}

246 247 248
static void
boolean_value_print (const GValue *value,
                     GString      *string)
Benjamin Otte's avatar
Benjamin Otte committed
249 250
{
  if (g_value_get_boolean (value))
251
    g_string_append (string, "true");
Benjamin Otte's avatar
Benjamin Otte committed
252
  else
253
    g_string_append (string, "false");
Benjamin Otte's avatar
Benjamin Otte committed
254 255 256
}

static gboolean 
257 258 259
int_value_parse (GtkCssParser *parser,
                 GFile        *base,
                 GValue       *value)
Benjamin Otte's avatar
Benjamin Otte committed
260
{
261
  gint i;
Benjamin Otte's avatar
Benjamin Otte committed
262

263
  if (!_gtk_css_parser_try_int (parser, &i))
Benjamin Otte's avatar
Benjamin Otte committed
264
    {
265
      _gtk_css_parser_error (parser, "Expected a valid integer value");
Benjamin Otte's avatar
Benjamin Otte committed
266 267 268 269 270 271 272
      return FALSE;
    }

  g_value_set_int (value, i);
  return TRUE;
}

273 274 275
static void
int_value_print (const GValue *value,
                 GString      *string)
Benjamin Otte's avatar
Benjamin Otte committed
276
{
277
  g_string_append_printf (string, "%d", g_value_get_int (value));
Benjamin Otte's avatar
Benjamin Otte committed
278 279 280
}

static gboolean 
281 282 283
uint_value_parse (GtkCssParser *parser,
                  GFile        *base,
                  GValue       *value)
Benjamin Otte's avatar
Benjamin Otte committed
284
{
285
  guint u;
Benjamin Otte's avatar
Benjamin Otte committed
286

287
  if (!_gtk_css_parser_try_uint (parser, &u))
Benjamin Otte's avatar
Benjamin Otte committed
288
    {
289
      _gtk_css_parser_error (parser, "Expected a valid unsigned value");
Benjamin Otte's avatar
Benjamin Otte committed
290 291 292 293 294 295 296
      return FALSE;
    }

  g_value_set_uint (value, u);
  return TRUE;
}

297 298 299
static void
uint_value_print (const GValue *value,
                  GString      *string)
Benjamin Otte's avatar
Benjamin Otte committed
300
{
301
  g_string_append_printf (string, "%u", g_value_get_uint (value));
Benjamin Otte's avatar
Benjamin Otte committed
302 303 304
}

static gboolean 
305 306 307
double_value_parse (GtkCssParser *parser,
                    GFile        *base,
                    GValue       *value)
Benjamin Otte's avatar
Benjamin Otte committed
308
{
309
  gdouble d;
Benjamin Otte's avatar
Benjamin Otte committed
310

311
  if (!_gtk_css_parser_try_double (parser, &d))
Benjamin Otte's avatar
Benjamin Otte committed
312
    {
313
      _gtk_css_parser_error (parser, "Expected a number");
Benjamin Otte's avatar
Benjamin Otte committed
314 315 316 317 318 319 320
      return FALSE;
    }

  g_value_set_double (value, d);
  return TRUE;
}

321 322 323
static void
double_value_print (const GValue *value,
                    GString      *string)
Benjamin Otte's avatar
Benjamin Otte committed
324 325 326 327
{
  char buf[G_ASCII_DTOSTR_BUF_SIZE];

  g_ascii_dtostr (buf, sizeof (buf), g_value_get_double (value));
328
  g_string_append (string, buf);
Benjamin Otte's avatar
Benjamin Otte committed
329 330 331
}

static gboolean 
332 333 334
float_value_parse (GtkCssParser *parser,
                   GFile        *base,
                   GValue       *value)
Benjamin Otte's avatar
Benjamin Otte committed
335
{
336
  gdouble d;
Benjamin Otte's avatar
Benjamin Otte committed
337

338
  if (!_gtk_css_parser_try_double (parser, &d))
Benjamin Otte's avatar
Benjamin Otte committed
339
    {
340
      _gtk_css_parser_error (parser, "Expected a number");
Benjamin Otte's avatar
Benjamin Otte committed
341 342 343 344 345 346 347
      return FALSE;
    }

  g_value_set_float (value, d);
  return TRUE;
}

348 349 350
static void
float_value_print (const GValue *value,
                   GString      *string)
Benjamin Otte's avatar
Benjamin Otte committed
351 352 353 354
{
  char buf[G_ASCII_DTOSTR_BUF_SIZE];

  g_ascii_dtostr (buf, sizeof (buf), g_value_get_float (value));
355
  g_string_append (string, buf);
Benjamin Otte's avatar
Benjamin Otte committed
356 357
}

358
static gboolean 
359 360 361
string_value_parse (GtkCssParser *parser,
                    GFile        *base,
                    GValue       *value)
362
{
363
  char *str = _gtk_css_parser_read_string (parser);
364

365
  if (str == NULL)
366 367
    return FALSE;

368
  g_value_take_string (value, str);
369 370 371
  return TRUE;
}

372 373 374
static void
string_value_print (const GValue *value,
                    GString      *str)
375 376 377 378 379
{
  const char *string;
  gsize len;

  string = g_value_get_string (value);
380
  g_string_append_c (str, '"');
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410

  do {
    len = strcspn (string, "\"\n\r\f");
    g_string_append (str, string);
    string += len;
    switch (*string)
      {
      case '\0':
        break;
      case '\n':
        g_string_append (str, "\\A ");
        break;
      case '\r':
        g_string_append (str, "\\D ");
        break;
      case '\f':
        g_string_append (str, "\\C ");
        break;
      case '\"':
        g_string_append (str, "\\\"");
        break;
      default:
        g_assert_not_reached ();
        break;
      }
  } while (*string);

  g_string_append_c (str, '"');
}

Benjamin Otte's avatar
Benjamin Otte committed
411
static gboolean 
412 413 414
theming_engine_value_parse (GtkCssParser *parser,
                            GFile        *base,
                            GValue       *value)
Benjamin Otte's avatar
Benjamin Otte committed
415 416
{
  GtkThemingEngine *engine;
417 418 419 420 421 422 423 424
  char *str;

  str = _gtk_css_parser_try_ident (parser, TRUE);
  if (str == NULL)
    {
      _gtk_css_parser_error (parser, "Expected a valid theme name");
      return FALSE;
    }
Benjamin Otte's avatar
Benjamin Otte committed
425 426 427 428

  engine = gtk_theming_engine_load (str);
  if (engine == NULL)
    {
429 430
      _gtk_css_parser_error (parser, "Themeing engine '%s' not found", str);
      g_free (str);
Benjamin Otte's avatar
Benjamin Otte committed
431 432 433 434
      return FALSE;
    }

  g_value_set_object (value, engine);
435
  g_free (str);
Benjamin Otte's avatar
Benjamin Otte committed
436 437 438
  return TRUE;
}

439 440 441
static void
theming_engine_value_print (const GValue *value,
                            GString      *string)
Benjamin Otte's avatar
Benjamin Otte committed
442 443 444 445 446 447
{
  GtkThemingEngine *engine;
  char *name;

  engine = g_value_get_object (value);
  if (engine == NULL)
448 449 450 451 452 453 454 455
    g_string_append (string, "none");
  else
    {
      /* XXX: gtk_theming_engine_get_name()? */
      g_object_get (engine, "name", &name, NULL);
      g_string_append (string, name);
      g_free (name);
    }
Benjamin Otte's avatar
Benjamin Otte committed
456 457 458
}

static gboolean 
459 460 461
animation_description_value_parse (GtkCssParser *parser,
                                   GFile        *base,
                                   GValue       *value)
Benjamin Otte's avatar
Benjamin Otte committed
462 463
{
  GtkAnimationDescription *desc;
464 465 466 467 468
  char *str;

  str = _gtk_css_parser_read_value (parser);
  if (str == NULL)
    return FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
469 470

  desc = _gtk_animation_description_from_string (str);
471
  g_free (str);
Benjamin Otte's avatar
Benjamin Otte committed
472 473

  if (desc == NULL)
474 475 476 477
    {
      _gtk_css_parser_error (parser, "Invalid animation description");
      return FALSE;
    }
Benjamin Otte's avatar
Benjamin Otte committed
478 479 480 481 482
  
  g_value_take_boxed (value, desc);
  return TRUE;
}

483 484 485
static void
animation_description_value_print (const GValue *value,
                                   GString      *string)
Benjamin Otte's avatar
Benjamin Otte committed
486 487 488 489
{
  GtkAnimationDescription *desc = g_value_get_boxed (value);

  if (desc == NULL)
490 491 492
    g_string_append (string, "none");
  else
    _gtk_animation_description_print (desc, string);
Benjamin Otte's avatar
Benjamin Otte committed
493 494
}

495 496 497 498
static gboolean 
border_value_parse (GtkCssParser *parser,
                    GFile        *base,
                    GValue       *value)
Benjamin Otte's avatar
Benjamin Otte committed
499
{
500 501
  GtkBorder border = { 0, };
  guint i, numbers[4];
Benjamin Otte's avatar
Benjamin Otte committed
502

503
  for (i = 0; i < G_N_ELEMENTS (numbers); i++)
Benjamin Otte's avatar
Benjamin Otte committed
504
    {
505 506
      if (!_gtk_css_parser_try_uint (parser, &numbers[i]))
        break;
Benjamin Otte's avatar
Benjamin Otte committed
507

508 509
      /* XXX: shouldn't allow spaces here? */
      _gtk_css_parser_try (parser, "px", TRUE);
Benjamin Otte's avatar
Benjamin Otte committed
510 511
    }

512
  if (i == 0)
Benjamin Otte's avatar
Benjamin Otte committed
513
    {
514
      _gtk_css_parser_error (parser, "Expected valid border");
Benjamin Otte's avatar
Benjamin Otte committed
515 516 517
      return FALSE;
    }

518 519 520
  border.top = numbers[0];
  if (i > 1)
    border.right = numbers[1];
Benjamin Otte's avatar
Benjamin Otte committed
521
  else
522 523 524
    border.right = border.top;
  if (i > 2)
    border.bottom = numbers[2];
Benjamin Otte's avatar
Benjamin Otte committed
525
  else
526 527 528
    border.bottom = border.top;
  if (i > 3)
    border.left = numbers[3];
Benjamin Otte's avatar
Benjamin Otte committed
529
  else
530
    border.left = border.right;
Benjamin Otte's avatar
Benjamin Otte committed
531

532
  g_value_set_boxed (value, &border);
Benjamin Otte's avatar
Benjamin Otte committed
533 534 535
  return TRUE;
}

536 537
static void
border_value_print (const GValue *value, GString *string)
Benjamin Otte's avatar
Benjamin Otte committed
538 539 540 541
{
  const GtkBorder *border = g_value_get_boxed (value);

  if (border == NULL)
542
    g_string_append (string, "none");
Benjamin Otte's avatar
Benjamin Otte committed
543
  else if (border->left != border->right)
544
    g_string_append_printf (string, "%d %d %d %d", border->top, border->right, border->bottom, border->left);
Benjamin Otte's avatar
Benjamin Otte committed
545
  else if (border->top != border->bottom)
546
    g_string_append_printf (string, "%d %d %d", border->top, border->right, border->bottom);
Benjamin Otte's avatar
Benjamin Otte committed
547
  else if (border->top != border->left)
548
    g_string_append_printf (string, "%d %d", border->top, border->right);
Benjamin Otte's avatar
Benjamin Otte committed
549
  else
550
    g_string_append_printf (string, "%d", border->top);
Benjamin Otte's avatar
Benjamin Otte committed
551 552
}

553 554 555 556
static gboolean 
gradient_value_parse (GtkCssParser *parser,
                      GFile        *base,
                      GValue       *value)
Benjamin Otte's avatar
Benjamin Otte committed
557 558 559 560 561 562
{
  GtkGradient *gradient;
  cairo_pattern_type_t type;
  gdouble coords[6];
  guint i;

Benjamin Otte's avatar
Benjamin Otte committed
563
  if (!_gtk_css_parser_try (parser, "-gtk-gradient", TRUE))
Benjamin Otte's avatar
Benjamin Otte committed
564
    {
Benjamin Otte's avatar
Benjamin Otte committed
565 566
      _gtk_css_parser_error (parser,
                             "Expected '-gtk-gradient'");
567
      return FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
568 569
    }

Benjamin Otte's avatar
Benjamin Otte committed
570
  if (!_gtk_css_parser_try (parser, "(", TRUE))
Benjamin Otte's avatar
Benjamin Otte committed
571
    {
Benjamin Otte's avatar
Benjamin Otte committed
572 573
      _gtk_css_parser_error (parser,
                             "Expected '(' after '-gtk-gradient'");
574
      return FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
575
    }
Benjamin Otte's avatar
Benjamin Otte committed
576 577 578 579 580 581

  /* Parse gradient type */
  if (_gtk_css_parser_try (parser, "linear", TRUE))
    type = CAIRO_PATTERN_TYPE_LINEAR;
  else if (_gtk_css_parser_try (parser, "radial", TRUE))
    type = CAIRO_PATTERN_TYPE_RADIAL;
Benjamin Otte's avatar
Benjamin Otte committed
582 583
  else
    {
Benjamin Otte's avatar
Benjamin Otte committed
584 585
      _gtk_css_parser_error (parser,
                             "Gradient type must be 'radial' or 'linear'");
586
      return FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
587 588 589 590 591
    }

  /* Parse start/stop position parameters */
  for (i = 0; i < 2; i++)
    {
Benjamin Otte's avatar
Benjamin Otte committed
592
      if (! _gtk_css_parser_try (parser, ",", TRUE))
Benjamin Otte's avatar
Benjamin Otte committed
593
        {
Benjamin Otte's avatar
Benjamin Otte committed
594 595
          _gtk_css_parser_error (parser,
                                 "Expected ','");
596
          return FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
597 598
        }

Benjamin Otte's avatar
Benjamin Otte committed
599 600 601 602 603 604 605
      if (_gtk_css_parser_try (parser, "left", TRUE))
        coords[i * 3] = 0;
      else if (_gtk_css_parser_try (parser, "right", TRUE))
        coords[i * 3] = 1;
      else if (_gtk_css_parser_try (parser, "center", TRUE))
        coords[i * 3] = 0.5;
      else if (!_gtk_css_parser_try_double (parser, &coords[i * 3]))
Benjamin Otte's avatar
Benjamin Otte committed
606
        {
Benjamin Otte's avatar
Benjamin Otte committed
607 608
          _gtk_css_parser_error (parser,
                                 "Expected a valid X coordinate");
609
          return FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
610 611
        }

Benjamin Otte's avatar
Benjamin Otte committed
612 613 614 615 616 617 618
      if (_gtk_css_parser_try (parser, "top", TRUE))
        coords[i * 3 + 1] = 0;
      else if (_gtk_css_parser_try (parser, "bottom", TRUE))
        coords[i * 3 + 1] = 1;
      else if (_gtk_css_parser_try (parser, "center", TRUE))
        coords[i * 3 + 1] = 0.5;
      else if (!_gtk_css_parser_try_double (parser, &coords[i * 3 + 1]))
Benjamin Otte's avatar
Benjamin Otte committed
619
        {
Benjamin Otte's avatar
Benjamin Otte committed
620 621
          _gtk_css_parser_error (parser,
                                 "Expected a valid Y coordinate");
622
          return FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
623 624 625 626 627
        }

      if (type == CAIRO_PATTERN_TYPE_RADIAL)
        {
          /* Parse radius */
Benjamin Otte's avatar
Benjamin Otte committed
628
          if (! _gtk_css_parser_try (parser, ",", TRUE))
Benjamin Otte's avatar
Benjamin Otte committed
629
            {
Benjamin Otte's avatar
Benjamin Otte committed
630 631
              _gtk_css_parser_error (parser,
                                     "Expected ','");
632
              return FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
633 634
            }

Benjamin Otte's avatar
Benjamin Otte committed
635 636 637 638
          if (! _gtk_css_parser_try_double (parser, &coords[(i * 3) + 2]))
            {
              _gtk_css_parser_error (parser,
                                     "Expected a numer for the radius");
639
              return FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
640
            }
Benjamin Otte's avatar
Benjamin Otte committed
641 642 643 644 645 646 647 648 649
        }
    }

  if (type == CAIRO_PATTERN_TYPE_LINEAR)
    gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[3], coords[4]);
  else
    gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2],
                                        coords[3], coords[4], coords[5]);

Benjamin Otte's avatar
Benjamin Otte committed
650
  while (_gtk_css_parser_try (parser, ",", TRUE))
Benjamin Otte's avatar
Benjamin Otte committed
651 652 653 654
    {
      GtkSymbolicColor *color;
      gdouble position;

Benjamin Otte's avatar
Benjamin Otte committed
655
      if (_gtk_css_parser_try (parser, "from", TRUE))
Benjamin Otte's avatar
Benjamin Otte committed
656 657 658
        {
          position = 0;

Benjamin Otte's avatar
Benjamin Otte committed
659
          if (!_gtk_css_parser_try (parser, "(", TRUE))
Benjamin Otte's avatar
Benjamin Otte committed
660
            {
Benjamin Otte's avatar
Benjamin Otte committed
661 662 663
              gtk_gradient_unref (gradient);
              _gtk_css_parser_error (parser,
                                     "Expected '('");
664
              return FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
665
            }
Benjamin Otte's avatar
Benjamin Otte committed
666

Benjamin Otte's avatar
Benjamin Otte committed
667
        }
Benjamin Otte's avatar
Benjamin Otte committed
668
      else if (_gtk_css_parser_try (parser, "to", TRUE))
Benjamin Otte's avatar
Benjamin Otte committed
669 670 671
        {
          position = 1;

Benjamin Otte's avatar
Benjamin Otte committed
672
          if (!_gtk_css_parser_try (parser, "(", TRUE))
Benjamin Otte's avatar
Benjamin Otte committed
673
            {
Benjamin Otte's avatar
Benjamin Otte committed
674 675 676
              gtk_gradient_unref (gradient);
              _gtk_css_parser_error (parser,
                                     "Expected '('");
677
              return FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
678
            }
Benjamin Otte's avatar
Benjamin Otte committed
679

Benjamin Otte's avatar
Benjamin Otte committed
680
        }
Benjamin Otte's avatar
Benjamin Otte committed
681
      else if (_gtk_css_parser_try (parser, "color-stop", TRUE))
Benjamin Otte's avatar
Benjamin Otte committed
682
        {
Benjamin Otte's avatar
Benjamin Otte committed
683
          if (!_gtk_css_parser_try (parser, "(", TRUE))
Benjamin Otte's avatar
Benjamin Otte committed
684
            {
Benjamin Otte's avatar
Benjamin Otte committed
685 686 687
              gtk_gradient_unref (gradient);
              _gtk_css_parser_error (parser,
                                     "Expected '('");
688
              return FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
689 690
            }

Benjamin Otte's avatar
Benjamin Otte committed
691 692 693 694 695
          if (!_gtk_css_parser_try_double (parser, &position))
            {
              gtk_gradient_unref (gradient);
              _gtk_css_parser_error (parser,
                                     "Expected a valid number");
696
              return FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
697
            }
Benjamin Otte's avatar
Benjamin Otte committed
698

Benjamin Otte's avatar
Benjamin Otte committed
699
          if (!_gtk_css_parser_try (parser, ",", TRUE))
Benjamin Otte's avatar
Benjamin Otte committed
700
            {
Benjamin Otte's avatar
Benjamin Otte committed
701 702 703
              gtk_gradient_unref (gradient);
              _gtk_css_parser_error (parser,
                                     "Expected a comma");
704
              return FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
705 706 707 708
            }
        }
      else
        {
Benjamin Otte's avatar
Benjamin Otte committed
709 710 711
          gtk_gradient_unref (gradient);
          _gtk_css_parser_error (parser,
                                 "Not a valid color-stop definition");
712
          return FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
713 714
        }

Benjamin Otte's avatar
Benjamin Otte committed
715 716
      color = _gtk_css_parser_read_symbolic_color (parser);
      if (color == NULL)
Benjamin Otte's avatar
Benjamin Otte committed
717
        {
Benjamin Otte's avatar
Benjamin Otte committed
718
          gtk_gradient_unref (gradient);
719
          return FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
720 721
        }

Benjamin Otte's avatar
Benjamin Otte committed
722 723
      gtk_gradient_add_color_stop (gradient, position, color);
      gtk_symbolic_color_unref (color);
Benjamin Otte's avatar
Benjamin Otte committed
724

Benjamin Otte's avatar
Benjamin Otte committed
725
      if (!_gtk_css_parser_try (parser, ")", TRUE))
Benjamin Otte's avatar
Benjamin Otte committed
726
        {
Benjamin Otte's avatar
Benjamin Otte committed
727 728 729
          gtk_gradient_unref (gradient);
          _gtk_css_parser_error (parser,
                                 "Expected ')'");
730
          return FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
731 732 733
        }
    }

Benjamin Otte's avatar
Benjamin Otte committed
734
  if (!_gtk_css_parser_try (parser, ")", TRUE))
Benjamin Otte's avatar
Benjamin Otte committed
735
    {
Benjamin Otte's avatar
Benjamin Otte committed
736 737 738
      gtk_gradient_unref (gradient);
      _gtk_css_parser_error (parser,
                             "Expected ')'");
739
      return FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
740 741
    }

742
  g_value_take_boxed (value, gradient);
Benjamin Otte's avatar
Benjamin Otte committed
743 744 745
  return TRUE;
}

746 747 748
static void
gradient_value_print (const GValue *value,
                      GString      *string)
Benjamin Otte's avatar
Benjamin Otte committed
749 750 751 752
{
  GtkGradient *gradient = g_value_get_boxed (value);

  if (gradient == NULL)
753 754 755 756 757 758 759
    g_string_append (string, "none");
  else
    {
      char *s = gtk_gradient_to_string (gradient);
      g_string_append (string, s);
      g_free (s);
    }
Benjamin Otte's avatar
Benjamin Otte committed
760 761
}

762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817
static GFile *
gtk_css_parse_url (GtkCssParser *parser,
                   GFile        *base)
{
  gchar *path;
  GFile *file;

  if (_gtk_css_parser_try (parser, "url", FALSE))
    {
      if (!_gtk_css_parser_try (parser, "(", TRUE))
        {
          _gtk_css_parser_skip_whitespace (parser);
          if (_gtk_css_parser_try (parser, "(", TRUE))
            {
              GError *error;
              
              error = g_error_new_literal (GTK_CSS_PROVIDER_ERROR,
                                           GTK_CSS_PROVIDER_ERROR_DEPRECATED,
                                           "Whitespace between 'url' and '(' is not allowed");
                             
              _gtk_css_parser_take_error (parser, error);
            }
          else
            {
              _gtk_css_parser_error (parser, "Expected '(' after 'url'");
              return NULL;
            }
        }

      path = _gtk_css_parser_read_string (parser);
      if (path == NULL)
        return NULL;

      if (!_gtk_css_parser_try (parser, ")", TRUE))
        {
          _gtk_css_parser_error (parser, "No closing ')' found for 'url'");
          g_free (path);
          return NULL;
        }
    }
  else
    {
      path = _gtk_css_parser_try_name (parser, TRUE);
      if (path == NULL)
        {
          _gtk_css_parser_error (parser, "Not a valid url");
          return NULL;
        }
    }

  file = g_file_resolve_relative_path (base, path);
  g_free (path);

  return file;
}

Benjamin Otte's avatar
Benjamin Otte committed
818
static gboolean 
819 820 821
pattern_value_parse (GtkCssParser *parser,
                     GFile        *base,
                     GValue       *value)
Benjamin Otte's avatar
Benjamin Otte committed
822
{
823
  if (_gtk_css_parser_begins_with (parser, '-'))
Benjamin Otte's avatar
Benjamin Otte committed
824 825 826
    {
      g_value_unset (value);
      g_value_init (value, GTK_TYPE_GRADIENT);
827
      return gradient_value_parse (parser, base, value);
Benjamin Otte's avatar
Benjamin Otte committed
828 829 830
    }
  else
    {
831
      GError *error = NULL;
Benjamin Otte's avatar
Benjamin Otte committed
832 833 834
      gchar *path;
      GdkPixbuf *pixbuf;
      GFile *file;
835 836 837 838
      cairo_surface_t *surface;
      cairo_pattern_t *pattern;
      cairo_t *cr;
      cairo_matrix_t matrix;
Benjamin Otte's avatar
Benjamin Otte committed
839

840
      file = gtk_css_parse_url (parser, base);
Benjamin Otte's avatar
Benjamin Otte committed
841 842 843 844 845 846
      if (file == NULL)
        return FALSE;

      path = g_file_get_path (file);
      g_object_unref (file);

847
      pixbuf = gdk_pixbuf_new_from_file (path, &error);
Benjamin Otte's avatar
Benjamin Otte committed
848 849
      g_free (path);
      if (pixbuf == NULL)
850 851 852 853
        {
          _gtk_css_parser_take_error (parser, error);
          return FALSE;
        }
Benjamin Otte's avatar
Benjamin Otte committed
854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877

      surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
                                            gdk_pixbuf_get_width (pixbuf),
                                            gdk_pixbuf_get_height (pixbuf));
      cr = cairo_create (surface);
      gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
      cairo_paint (cr);
      pattern = cairo_pattern_create_for_surface (surface);

      cairo_matrix_init_scale (&matrix,
                               gdk_pixbuf_get_width (pixbuf),
                               gdk_pixbuf_get_height (pixbuf));
      cairo_pattern_set_matrix (pattern, &matrix);

      cairo_surface_destroy (surface);
      cairo_destroy (cr);
      g_object_unref (pixbuf);

      g_value_take_boxed (value, pattern);
    }
  
  return TRUE;
}

878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932
static gboolean
shadow_value_parse (GtkCssParser *parser,
                    GFile *base,
                    GValue *value)
{
  gboolean inset;
  gdouble hoffset, voffset, blur, spread;
  GtkSymbolicColor *color;
  GtkShadow *shadow;

  shadow = _gtk_shadow_new ();

  do
    {
      inset = _gtk_css_parser_try (parser, "inset", TRUE);

      if (!_gtk_css_parser_try_double (parser, &hoffset) ||
          !_gtk_css_parser_try_double (parser, &voffset))
        {
          _gtk_css_parser_error (parser, "Horizontal and vertical offsets are required");
          _gtk_shadow_unref (shadow);
          return FALSE;
        }

      if (!_gtk_css_parser_try_double (parser, &blur))
        blur = 0;

      if (!_gtk_css_parser_try_double (parser, &spread))
        spread = 0;

      /* XXX: the color is optional and UA-defined if it's missing,
       * but it doesn't really make sense for us...
       */
      color = _gtk_css_parser_read_symbolic_color (parser);

      if (color == NULL)
        {
          _gtk_shadow_unref (shadow);
          return FALSE;
        }

      _gtk_shadow_append (shadow,
                          hoffset, voffset,
                          blur, spread,
                          inset, color);

      gtk_symbolic_color_unref (color);

    }
  while (_gtk_css_parser_try (parser, ",", TRUE));

  g_value_take_boxed (value, shadow);
  return TRUE;
}

933 934 935
static void
shadow_value_print (const GValue *value,
                    GString      *string)
936 937 938 939 940 941
{
  GtkShadow *shadow;

  shadow = g_value_get_boxed (value);

  if (shadow == NULL)
942 943 944
    g_string_append (string, "none");
  else
    _gtk_shadow_print (shadow, string);
945 946
}

Benjamin Otte's avatar
Benjamin Otte committed
947
static gboolean 
948 949 950
slice_value_parse (GtkCssParser *parser,
                   GFile        *base,
                   GValue       *value)
Benjamin Otte's avatar
Benjamin Otte committed
951 952 953 954 955 956 957
{
  gdouble distance_top, distance_bottom;
  gdouble distance_left, distance_right;
  GtkSliceSideModifier mods[2];
  GdkPixbuf *pixbuf;
  Gtk9Slice *slice;
  GFile *file;
958 959
  GError *error = NULL;
  gint i;
Benjamin Otte's avatar
Benjamin Otte committed
960 961 962
  char *path;

  /* Parse image url */
963
  file = gtk_css_parse_url (parser, base);
Benjamin Otte's avatar
Benjamin Otte committed
964 965 966
  if (!file)
      return FALSE;

967 968 969 970
  if (!_gtk_css_parser_try_double (parser, &distance_top) ||
      !_gtk_css_parser_try_double (parser, &distance_right) ||
      !_gtk_css_parser_try_double (parser, &distance_bottom) ||
      !_gtk_css_parser_try_double (parser, &distance_left))
Benjamin Otte's avatar
Benjamin Otte committed
971
    {
972
      _gtk_css_parser_error (parser, "Expected a number");
Benjamin Otte's avatar
Benjamin Otte committed
973
      g_object_unref (file);
974
      return FALSE;
Benjamin Otte's avatar
Benjamin Otte committed
975 976
    }

977
  for (i = 0; i < 2; i++)
Benjamin Otte's avatar
Benjamin Otte committed
978
    {
979 980 981 982 983 984 985 986 987 988 989
      if (_gtk_css_parser_try (parser, "stretch", TRUE))
        mods[i] = GTK_SLICE_STRETCH;
      else if (_gtk_css_parser_try (parser, "repeat", TRUE))
        mods[i] = GTK_SLICE_REPEAT;
      else if (i == 0)
        {
          mods[1] = mods[0] = GTK_SLICE_STRETCH;
          break;
        }
      else
        mods[i] = mods[0];
Benjamin Otte's avatar
Benjamin Otte committed
990 991 992
    }

  path = g_file_get_path (file);
993 994
  g_object_unref (file);
  pixbuf = gdk_pixbuf_new_from_file (path, &error);
Benjamin Otte's avatar
Benjamin Otte committed
995 996
  g_free (path);
  if (!pixbuf)
997 998 999 1000
    {
      _gtk_css_parser_take_error (parser, error);
      return FALSE;
    }
Benjamin Otte's avatar
Benjamin Otte committed
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012

  slice = _gtk_9slice_new (pixbuf,
                           distance_top, distance_bottom,
                           distance_left, distance_right,
                           mods[0], mods[1]);
  g_object_unref (pixbuf);

  g_value_take_boxed (value, slice);
  return TRUE;
}

static gboolean 
1013 1014 1015
enum_value_parse (GtkCssParser *parser,
                  GFile        *base,
                  GValue       *value)
Benjamin Otte's avatar
Benjamin Otte committed
1016 1017 1018
{
  GEnumClass *enum_class;
  GEnumValue *enum_value;
1019
  char *str;
Benjamin Otte's avatar
Benjamin Otte committed
1020

1021 1022
  str = _gtk_css_parser_try_ident (parser, TRUE);
  if (str == NULL)