1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "config.h"
19
20 #include "gtkcssstylefuncsprivate.h"
21
22 #include <errno.h>
23 #include <math.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include <gdk-pixbuf/gdk-pixbuf.h>
28 #include <cairo-gobject.h>
29
30 #include "gtkcsscolorvalueprivate.h"
31 #include "gtkcssimagegradientprivate.h"
32 #include "gtkcssprovider.h"
33 #include "gtkcssrgbavalueprivate.h"
34 #include "gtkcsstypedvalueprivate.h"
35 #include "gtkcsstypesprivate.h"
36 #include "gtkprivatetypebuiltins.h"
37 #include "gtkstylecontextprivate.h"
38 #include "gtktypebuiltins.h"
39 #include "gtkcsswin32sizevalueprivate.h"
40
41 #include "deprecated/gtkthemingengine.h"
42 #include "deprecated/gtkgradientprivate.h"
43 #include "deprecated/gtksymboliccolorprivate.h"
44
45 /* this is in case round() is not provided by the compiler,
46 * such as in the case of C89 compilers, like MSVC
47 */
48 #include "fallback-c89.c"
49
50 static GHashTable *parse_funcs = NULL;
51 static GHashTable *print_funcs = NULL;
52 static GHashTable *compute_funcs = NULL;
53
54 typedef gboolean (* GtkStyleParseFunc) (GtkCssParser *parser,
55 GValue *value);
56 typedef void (* GtkStylePrintFunc) (const GValue *value,
57 GString *string);
58 typedef GtkCssValue * (* GtkStyleComputeFunc) (GtkStyleProviderPrivate *provider,
59 GtkCssStyle *values,
60 GtkCssStyle *parent_values,
61 GtkCssValue *specified);
62
63 static void
register_conversion_function(GType type,GtkStyleParseFunc parse,GtkStylePrintFunc print,GtkStyleComputeFunc compute)64 register_conversion_function (GType type,
65 GtkStyleParseFunc parse,
66 GtkStylePrintFunc print,
67 GtkStyleComputeFunc compute)
68 {
69 if (parse)
70 g_hash_table_insert (parse_funcs, GSIZE_TO_POINTER (type), parse);
71 if (print)
72 g_hash_table_insert (print_funcs, GSIZE_TO_POINTER (type), print);
73 if (compute)
74 g_hash_table_insert (compute_funcs, GSIZE_TO_POINTER (type), compute);
75 }
76
77 static void
string_append_double(GString * string,double d)78 string_append_double (GString *string,
79 double d)
80 {
81 char buf[G_ASCII_DTOSTR_BUF_SIZE];
82
83 g_ascii_dtostr (buf, sizeof (buf), d);
84 g_string_append (string, buf);
85 }
86
87 static void
string_append_string(GString * str,const char * string)88 string_append_string (GString *str,
89 const char *string)
90 {
91 _gtk_css_print_string (str, string);
92 }
93
94 /*** IMPLEMENTATIONS ***/
95
96 static gboolean
enum_parse(GtkCssParser * parser,GType type,int * res)97 enum_parse (GtkCssParser *parser,
98 GType type,
99 int *res)
100 {
101 char *str;
102
103 if (_gtk_css_parser_try_enum (parser, type, res))
104 return TRUE;
105
106 str = _gtk_css_parser_try_ident (parser, TRUE);
107 if (str == NULL)
108 {
109 _gtk_css_parser_error (parser, "Expected an identifier");
110 return FALSE;
111 }
112
113 _gtk_css_parser_error (parser,
114 "Unknown value '%s' for enum type '%s'",
115 str, g_type_name (type));
116 g_free (str);
117
118 return FALSE;
119 }
120
121 static void
enum_print(int value,GType type,GString * string)122 enum_print (int value,
123 GType type,
124 GString *string)
125 {
126 GEnumClass *enum_class;
127 GEnumValue *enum_value;
128
129 enum_class = g_type_class_ref (type);
130 enum_value = g_enum_get_value (enum_class, value);
131
132 g_string_append (string, enum_value->value_nick);
133
134 g_type_class_unref (enum_class);
135 }
136
137 static gboolean
rgba_value_parse(GtkCssParser * parser,GValue * value)138 rgba_value_parse (GtkCssParser *parser,
139 GValue *value)
140 {
141 GtkSymbolicColor *symbolic;
142 GdkRGBA rgba;
143
144 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
145
146 symbolic = _gtk_css_symbolic_value_new (parser);
147 if (symbolic == NULL)
148 return FALSE;
149
150 if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
151 {
152 g_value_set_boxed (value, &rgba);
153 gtk_symbolic_color_unref (symbolic);
154 }
155 else
156 {
157 g_value_unset (value);
158 g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
159 g_value_take_boxed (value, symbolic);
160 }
161
162 G_GNUC_END_IGNORE_DEPRECATIONS;
163
164 return TRUE;
165 }
166
167 static void
rgba_value_print(const GValue * value,GString * string)168 rgba_value_print (const GValue *value,
169 GString *string)
170 {
171 const GdkRGBA *rgba = g_value_get_boxed (value);
172
173 if (rgba == NULL)
174 g_string_append (string, "none");
175 else
176 {
177 char *s = gdk_rgba_to_string (rgba);
178 g_string_append (string, s);
179 g_free (s);
180 }
181 }
182
183 static GtkCssValue *
rgba_value_compute(GtkStyleProviderPrivate * provider,GtkCssStyle * values,GtkCssStyle * parent_values,GtkCssValue * specified)184 rgba_value_compute (GtkStyleProviderPrivate *provider,
185 GtkCssStyle *values,
186 GtkCssStyle *parent_values,
187 GtkCssValue *specified)
188 {
189 GdkRGBA white = { 1, 1, 1, 1 };
190 const GValue *value;
191
192 value = _gtk_css_typed_value_get (specified);
193
194 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
195
196 if (G_VALUE_HOLDS (value, GTK_TYPE_SYMBOLIC_COLOR))
197 {
198 GtkSymbolicColor *symbolic = g_value_get_boxed (value);
199 GtkCssValue *val;
200 GValue new_value = G_VALUE_INIT;
201 GdkRGBA rgba;
202
203 val = _gtk_css_color_value_resolve (_gtk_symbolic_color_get_css_value (symbolic),
204 provider,
205 gtk_css_style_get_value (values, GTK_CSS_PROPERTY_COLOR),
206 NULL);
207 if (val != NULL)
208 {
209 rgba = *_gtk_css_rgba_value_get_rgba (val);
210 _gtk_css_value_unref (val);
211 }
212 else
213 rgba = white;
214
215 g_value_init (&new_value, GDK_TYPE_RGBA);
216 g_value_set_boxed (&new_value, &rgba);
217 return _gtk_css_typed_value_new_take (&new_value);
218 }
219 else
220 return _gtk_css_value_ref (specified);
221
222 G_GNUC_END_IGNORE_DEPRECATIONS;
223 }
224
225 static gboolean
color_value_parse(GtkCssParser * parser,GValue * value)226 color_value_parse (GtkCssParser *parser,
227 GValue *value)
228 {
229 GtkSymbolicColor *symbolic;
230 GdkRGBA rgba;
231
232 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
233
234 symbolic = _gtk_css_symbolic_value_new (parser);
235 if (symbolic == NULL)
236 return FALSE;
237
238 if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
239 {
240 GdkColor color;
241
242 color.red = rgba.red * 65535. + 0.5;
243 color.green = rgba.green * 65535. + 0.5;
244 color.blue = rgba.blue * 65535. + 0.5;
245
246 g_value_set_boxed (value, &color);
247 gtk_symbolic_color_unref (symbolic);
248 }
249 else
250 {
251 g_value_unset (value);
252 g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
253 g_value_take_boxed (value, symbolic);
254 }
255
256 G_GNUC_END_IGNORE_DEPRECATIONS;
257
258 return TRUE;
259 }
260
261 static void
color_value_print(const GValue * value,GString * string)262 color_value_print (const GValue *value,
263 GString *string)
264 {
265 const GdkColor *color = g_value_get_boxed (value);
266
267 if (color == NULL)
268 g_string_append (string, "none");
269 else
270 {
271 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
272 char *s = gdk_color_to_string (color);
273 G_GNUC_END_IGNORE_DEPRECATIONS
274 g_string_append (string, s);
275 g_free (s);
276 }
277 }
278
279 static GtkCssValue *
color_value_compute(GtkStyleProviderPrivate * provider,GtkCssStyle * values,GtkCssStyle * parent_values,GtkCssValue * specified)280 color_value_compute (GtkStyleProviderPrivate *provider,
281 GtkCssStyle *values,
282 GtkCssStyle *parent_values,
283 GtkCssValue *specified)
284 {
285 GdkColor color = { 0, 65535, 65535, 65535 };
286 const GValue *value;
287
288 value = _gtk_css_typed_value_get (specified);
289
290 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
291
292 if (G_VALUE_HOLDS (value, GTK_TYPE_SYMBOLIC_COLOR))
293 {
294 GValue new_value = G_VALUE_INIT;
295 GtkCssValue *val;
296
297 val = _gtk_css_color_value_resolve (_gtk_symbolic_color_get_css_value (g_value_get_boxed (value)),
298 provider,
299 gtk_css_style_get_value (values, GTK_CSS_PROPERTY_COLOR),
300 NULL);
301 if (val != NULL)
302 {
303 const GdkRGBA *rgba = _gtk_css_rgba_value_get_rgba (val);
304 color.red = rgba->red * 65535. + 0.5;
305 color.green = rgba->green * 65535. + 0.5;
306 color.blue = rgba->blue * 65535. + 0.5;
307 _gtk_css_value_unref (val);
308 }
309
310 g_value_init (&new_value, GDK_TYPE_COLOR);
311 g_value_set_boxed (&new_value, &color);
312 return _gtk_css_typed_value_new_take (&new_value);
313 }
314 else
315 return _gtk_css_value_ref (specified);
316
317 G_GNUC_END_IGNORE_DEPRECATIONS;
318 }
319
320 static gboolean
symbolic_color_value_parse(GtkCssParser * parser,GValue * value)321 symbolic_color_value_parse (GtkCssParser *parser,
322 GValue *value)
323 {
324 GtkSymbolicColor *symbolic;
325
326 symbolic = _gtk_css_symbolic_value_new (parser);
327 if (symbolic == NULL)
328 return FALSE;
329
330 g_value_take_boxed (value, symbolic);
331 return TRUE;
332 }
333
334 static void
symbolic_color_value_print(const GValue * value,GString * string)335 symbolic_color_value_print (const GValue *value,
336 GString *string)
337 {
338 GtkSymbolicColor *symbolic = g_value_get_boxed (value);
339
340 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
341
342 if (symbolic == NULL)
343 g_string_append (string, "none");
344 else
345 {
346 char *s = gtk_symbolic_color_to_string (symbolic);
347 g_string_append (string, s);
348 g_free (s);
349 }
350
351 G_GNUC_END_IGNORE_DEPRECATIONS;
352 }
353
354 static gboolean
font_description_value_parse(GtkCssParser * parser,GValue * value)355 font_description_value_parse (GtkCssParser *parser,
356 GValue *value)
357 {
358 PangoFontDescription *font_desc;
359 guint mask;
360 char *str;
361
362 str = _gtk_css_parser_read_value (parser);
363 if (str == NULL)
364 return FALSE;
365
366 font_desc = pango_font_description_from_string (str);
367 mask = pango_font_description_get_set_fields (font_desc);
368 /* These values are not really correct,
369 * but the fields must be set, so we set them to something */
370 if ((mask & PANGO_FONT_MASK_FAMILY) == 0)
371 pango_font_description_set_family_static (font_desc, "Sans");
372 if ((mask & PANGO_FONT_MASK_SIZE) == 0)
373 pango_font_description_set_size (font_desc, 10 * PANGO_SCALE);
374 g_free (str);
375 g_value_take_boxed (value, font_desc);
376 return TRUE;
377 }
378
379 static void
font_description_value_print(const GValue * value,GString * string)380 font_description_value_print (const GValue *value,
381 GString *string)
382 {
383 const PangoFontDescription *desc = g_value_get_boxed (value);
384
385 if (desc == NULL)
386 g_string_append (string, "none");
387 else
388 {
389 char *s = pango_font_description_to_string (desc);
390 g_string_append (string, s);
391 g_free (s);
392 }
393 }
394
395 static gboolean
boolean_value_parse(GtkCssParser * parser,GValue * value)396 boolean_value_parse (GtkCssParser *parser,
397 GValue *value)
398 {
399 if (_gtk_css_parser_try (parser, "true", TRUE) ||
400 _gtk_css_parser_try (parser, "1", TRUE))
401 {
402 g_value_set_boolean (value, TRUE);
403 return TRUE;
404 }
405 else if (_gtk_css_parser_try (parser, "false", TRUE) ||
406 _gtk_css_parser_try (parser, "0", TRUE))
407 {
408 g_value_set_boolean (value, FALSE);
409 return TRUE;
410 }
411 else
412 {
413 _gtk_css_parser_error (parser, "Expected a boolean value");
414 return FALSE;
415 }
416 }
417
418 static void
boolean_value_print(const GValue * value,GString * string)419 boolean_value_print (const GValue *value,
420 GString *string)
421 {
422 if (g_value_get_boolean (value))
423 g_string_append (string, "true");
424 else
425 g_string_append (string, "false");
426 }
427
428 static gboolean
int_value_parse(GtkCssParser * parser,GValue * value)429 int_value_parse (GtkCssParser *parser,
430 GValue *value)
431 {
432 gint i;
433
434 if (_gtk_css_parser_has_prefix (parser, "-gtk"))
435 {
436 GtkCssValue *cssvalue = gtk_css_win32_size_value_parse (parser, GTK_CSS_PARSE_NUMBER | GTK_CSS_NUMBER_AS_PIXELS);
437
438 if (cssvalue)
439 {
440 g_value_set_int (value, _gtk_css_number_value_get (cssvalue, 100));
441 _gtk_css_value_unref (cssvalue);
442 return TRUE;
443 }
444
445 return FALSE;
446 }
447
448 if (!_gtk_css_parser_try_int (parser, &i))
449 {
450 _gtk_css_parser_error (parser, "Expected a valid integer value");
451 return FALSE;
452 }
453
454 g_value_set_int (value, i);
455 return TRUE;
456 }
457
458 static void
int_value_print(const GValue * value,GString * string)459 int_value_print (const GValue *value,
460 GString *string)
461 {
462 g_string_append_printf (string, "%d", g_value_get_int (value));
463 }
464
465 static gboolean
uint_value_parse(GtkCssParser * parser,GValue * value)466 uint_value_parse (GtkCssParser *parser,
467 GValue *value)
468 {
469 guint u;
470
471 if (!_gtk_css_parser_try_uint (parser, &u))
472 {
473 _gtk_css_parser_error (parser, "Expected a valid unsigned value");
474 return FALSE;
475 }
476
477 g_value_set_uint (value, u);
478 return TRUE;
479 }
480
481 static void
uint_value_print(const GValue * value,GString * string)482 uint_value_print (const GValue *value,
483 GString *string)
484 {
485 g_string_append_printf (string, "%u", g_value_get_uint (value));
486 }
487
488 static gboolean
double_value_parse(GtkCssParser * parser,GValue * value)489 double_value_parse (GtkCssParser *parser,
490 GValue *value)
491 {
492 gdouble d;
493
494 if (!_gtk_css_parser_try_double (parser, &d))
495 {
496 _gtk_css_parser_error (parser, "Expected a number");
497 return FALSE;
498 }
499
500 g_value_set_double (value, d);
501 return TRUE;
502 }
503
504 static void
double_value_print(const GValue * value,GString * string)505 double_value_print (const GValue *value,
506 GString *string)
507 {
508 string_append_double (string, g_value_get_double (value));
509 }
510
511 static gboolean
float_value_parse(GtkCssParser * parser,GValue * value)512 float_value_parse (GtkCssParser *parser,
513 GValue *value)
514 {
515 gdouble d;
516
517 if (!_gtk_css_parser_try_double (parser, &d))
518 {
519 _gtk_css_parser_error (parser, "Expected a number");
520 return FALSE;
521 }
522
523 g_value_set_float (value, d);
524 return TRUE;
525 }
526
527 static void
float_value_print(const GValue * value,GString * string)528 float_value_print (const GValue *value,
529 GString *string)
530 {
531 string_append_double (string, g_value_get_float (value));
532 }
533
534 static gboolean
string_value_parse(GtkCssParser * parser,GValue * value)535 string_value_parse (GtkCssParser *parser,
536 GValue *value)
537 {
538 char *str = _gtk_css_parser_read_string (parser);
539
540 if (str == NULL)
541 return FALSE;
542
543 g_value_take_string (value, str);
544 return TRUE;
545 }
546
547 static void
string_value_print(const GValue * value,GString * str)548 string_value_print (const GValue *value,
549 GString *str)
550 {
551 string_append_string (str, g_value_get_string (value));
552 }
553
554 static gboolean
theming_engine_value_parse(GtkCssParser * parser,GValue * value)555 theming_engine_value_parse (GtkCssParser *parser,
556 GValue *value)
557 {
558 GtkThemingEngine *engine;
559 char *str;
560
561 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
562
563 if (_gtk_css_parser_try (parser, "none", TRUE))
564 {
565 g_value_set_object (value, gtk_theming_engine_load (NULL));
566 return TRUE;
567 }
568
569 str = _gtk_css_parser_try_ident (parser, TRUE);
570 if (str == NULL)
571 {
572 _gtk_css_parser_error (parser, "Expected a valid theme name");
573 return FALSE;
574 }
575
576 engine = gtk_theming_engine_load (str);
577
578 if (engine == NULL)
579 {
580 _gtk_css_parser_error (parser, "Theming engine '%s' not found", str);
581 g_free (str);
582 return FALSE;
583 }
584
585 g_value_set_object (value, engine);
586 g_free (str);
587 return TRUE;
588
589 G_GNUC_END_IGNORE_DEPRECATIONS
590 }
591
592 static void
theming_engine_value_print(const GValue * value,GString * string)593 theming_engine_value_print (const GValue *value,
594 GString *string)
595 {
596 GtkThemingEngine *engine;
597 char *name;
598
599 engine = g_value_get_object (value);
600 if (engine == NULL)
601 g_string_append (string, "none");
602 else
603 {
604 /* XXX: gtk_theming_engine_get_name()? */
605 g_object_get (engine, "name", &name, NULL);
606 g_string_append (string, name ? name : "none");
607 g_free (name);
608 }
609 }
610
611 static gboolean
border_value_parse(GtkCssParser * parser,GValue * value)612 border_value_parse (GtkCssParser *parser,
613 GValue *value)
614 {
615 GtkBorder border = { 0, };
616 guint i;
617 int numbers[4];
618
619 for (i = 0; i < G_N_ELEMENTS (numbers); i++)
620 {
621 if (_gtk_css_parser_has_prefix (parser, "-gtk"))
622 {
623 GtkCssValue *cssvalue = gtk_css_win32_size_value_parse (parser, GTK_CSS_PARSE_NUMBER | GTK_CSS_NUMBER_AS_PIXELS);
624
625 if (cssvalue)
626 {
627 numbers[i] = _gtk_css_number_value_get (cssvalue, 100);
628 _gtk_css_value_unref (cssvalue);
629 return TRUE;
630 }
631
632 return FALSE;
633 }
634 else
635 {
636 if (!_gtk_css_parser_try_length (parser, &numbers[i]))
637 break;
638 }
639 }
640
641 if (i == 0)
642 {
643 _gtk_css_parser_error (parser, "Expected valid border");
644 return FALSE;
645 }
646
647 border.top = numbers[0];
648 if (i > 1)
649 border.right = numbers[1];
650 else
651 border.right = border.top;
652 if (i > 2)
653 border.bottom = numbers[2];
654 else
655 border.bottom = border.top;
656 if (i > 3)
657 border.left = numbers[3];
658 else
659 border.left = border.right;
660
661 g_value_set_boxed (value, &border);
662 return TRUE;
663 }
664
665 static void
border_value_print(const GValue * value,GString * string)666 border_value_print (const GValue *value, GString *string)
667 {
668 const GtkBorder *border = g_value_get_boxed (value);
669
670 if (border == NULL)
671 g_string_append (string, "none");
672 else if (border->left != border->right)
673 g_string_append_printf (string, "%d %d %d %d", border->top, border->right, border->bottom, border->left);
674 else if (border->top != border->bottom)
675 g_string_append_printf (string, "%d %d %d", border->top, border->right, border->bottom);
676 else if (border->top != border->left)
677 g_string_append_printf (string, "%d %d", border->top, border->right);
678 else
679 g_string_append_printf (string, "%d", border->top);
680 }
681
682 static gboolean
gradient_value_parse(GtkCssParser * parser,GValue * value)683 gradient_value_parse (GtkCssParser *parser,
684 GValue *value)
685 {
686 GtkGradient *gradient;
687
688 gradient = _gtk_gradient_parse (parser);
689 if (gradient == NULL)
690 return FALSE;
691
692 g_value_take_boxed (value, gradient);
693 return TRUE;
694 }
695
696 static void
gradient_value_print(const GValue * value,GString * string)697 gradient_value_print (const GValue *value,
698 GString *string)
699 {
700 GtkGradient *gradient = g_value_get_boxed (value);
701
702 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
703
704 if (gradient == NULL)
705 g_string_append (string, "none");
706 else
707 {
708 char *s = gtk_gradient_to_string (gradient);
709 g_string_append (string, s);
710 g_free (s);
711 }
712
713 G_GNUC_END_IGNORE_DEPRECATIONS;
714 }
715
716 static gboolean
pattern_value_parse(GtkCssParser * parser,GValue * value)717 pattern_value_parse (GtkCssParser *parser,
718 GValue *value)
719 {
720 if (_gtk_css_parser_try (parser, "none", TRUE))
721 {
722 /* nothing to do here */
723 }
724 else if (_gtk_css_parser_begins_with (parser, '-'))
725 {
726 g_value_unset (value);
727
728 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
729
730 g_value_init (value, GTK_TYPE_GRADIENT);
731 return gradient_value_parse (parser, value);
732
733 G_GNUC_END_IGNORE_DEPRECATIONS;
734 }
735 else
736 {
737 GError *error = NULL;
738 gchar *path;
739 GdkPixbuf *pixbuf;
740 GFile *file;
741 cairo_surface_t *surface;
742 cairo_pattern_t *pattern;
743 cairo_matrix_t matrix;
744
745 file = _gtk_css_parser_read_url (parser);
746 if (file == NULL)
747 return FALSE;
748
749 path = g_file_get_path (file);
750 g_object_unref (file);
751
752 pixbuf = gdk_pixbuf_new_from_file (path, &error);
753 g_free (path);
754 if (pixbuf == NULL)
755 {
756 _gtk_css_parser_take_error (parser, error);
757 return FALSE;
758 }
759
760 surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, 1, NULL);
761 pattern = cairo_pattern_create_for_surface (surface);
762 cairo_surface_destroy (surface);
763
764 cairo_matrix_init_scale (&matrix,
765 gdk_pixbuf_get_width (pixbuf),
766 gdk_pixbuf_get_height (pixbuf));
767 cairo_pattern_set_matrix (pattern, &matrix);
768
769 g_object_unref (pixbuf);
770
771 g_value_take_boxed (value, pattern);
772 }
773
774 return TRUE;
775 }
776
777 static cairo_status_t
surface_write(void * closure,const unsigned char * data,unsigned int length)778 surface_write (void *closure,
779 const unsigned char *data,
780 unsigned int length)
781 {
782 g_byte_array_append (closure, data, length);
783
784 return CAIRO_STATUS_SUCCESS;
785 }
786
787 static void
surface_print(cairo_surface_t * surface,GString * string)788 surface_print (cairo_surface_t *surface,
789 GString * string)
790 {
791 #if CAIRO_HAS_PNG_FUNCTIONS
792 GByteArray *array;
793 char *base64;
794
795 array = g_byte_array_new ();
796 cairo_surface_write_to_png_stream (surface, surface_write, array);
797 base64 = g_base64_encode (array->data, array->len);
798 g_byte_array_free (array, TRUE);
799
800 g_string_append (string, "url(\"data:image/png;base64,");
801 g_string_append (string, base64);
802 g_string_append (string, "\")");
803
804 g_free (base64);
805 #else
806 g_string_append (string, "none /* you need cairo png functions enabled to make this work */");
807 #endif
808 }
809
810 static void
pattern_value_print(const GValue * value,GString * string)811 pattern_value_print (const GValue *value,
812 GString *string)
813 {
814 cairo_pattern_t *pattern;
815 cairo_surface_t *surface;
816
817 pattern = g_value_get_boxed (value);
818
819 if (pattern == NULL)
820 {
821 g_string_append (string, "none");
822 return;
823 }
824
825 switch (cairo_pattern_get_type (pattern))
826 {
827 case CAIRO_PATTERN_TYPE_SURFACE:
828 if (cairo_pattern_get_surface (pattern, &surface) != CAIRO_STATUS_SUCCESS)
829 {
830 g_assert_not_reached ();
831 }
832 surface_print (surface, string);
833 break;
834 case CAIRO_PATTERN_TYPE_LINEAR:
835 case CAIRO_PATTERN_TYPE_RADIAL:
836 g_string_append (string, "none /* FIXME: add support for printing gradients */");
837 break;
838 case CAIRO_PATTERN_TYPE_SOLID:
839 default:
840 g_assert_not_reached ();
841 break;
842 }
843 }
844
845 static GtkCssValue *
pattern_value_compute(GtkStyleProviderPrivate * provider,GtkCssStyle * values,GtkCssStyle * parent_values,GtkCssValue * specified)846 pattern_value_compute (GtkStyleProviderPrivate *provider,
847 GtkCssStyle *values,
848 GtkCssStyle *parent_values,
849 GtkCssValue *specified)
850 {
851 const GValue *value = _gtk_css_typed_value_get (specified);
852
853 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
854
855 if (G_VALUE_HOLDS (value, GTK_TYPE_GRADIENT))
856 {
857 GValue new_value = G_VALUE_INIT;
858 cairo_pattern_t *gradient;
859
860 gradient = _gtk_gradient_resolve_full (g_value_get_boxed (value), provider, values, parent_values);
861
862 g_value_init (&new_value, CAIRO_GOBJECT_TYPE_PATTERN);
863 g_value_take_boxed (&new_value, gradient);
864 return _gtk_css_typed_value_new_take (&new_value);
865 }
866 else
867 return _gtk_css_value_ref (specified);
868
869 G_GNUC_END_IGNORE_DEPRECATIONS;
870 }
871
872 static gboolean
enum_value_parse(GtkCssParser * parser,GValue * value)873 enum_value_parse (GtkCssParser *parser,
874 GValue *value)
875 {
876 int v;
877
878 if (enum_parse (parser, G_VALUE_TYPE (value), &v))
879 {
880 g_value_set_enum (value, v);
881 return TRUE;
882 }
883
884 return FALSE;
885 }
886
887 static void
enum_value_print(const GValue * value,GString * string)888 enum_value_print (const GValue *value,
889 GString *string)
890 {
891 enum_print (g_value_get_enum (value), G_VALUE_TYPE (value), string);
892 }
893
894 static gboolean
flags_value_parse(GtkCssParser * parser,GValue * value)895 flags_value_parse (GtkCssParser *parser,
896 GValue *value)
897 {
898 GFlagsClass *flags_class;
899 GFlagsValue *flag_value;
900 guint flags = 0;
901 char *str;
902
903 flags_class = g_type_class_ref (G_VALUE_TYPE (value));
904
905 do {
906 str = _gtk_css_parser_try_ident (parser, TRUE);
907 if (str == NULL)
908 {
909 _gtk_css_parser_error (parser, "Expected an identifier");
910 g_type_class_unref (flags_class);
911 return FALSE;
912 }
913
914 flag_value = g_flags_get_value_by_nick (flags_class, str);
915 if (!flag_value)
916 {
917 _gtk_css_parser_error (parser,
918 "Unknown flag value '%s' for type '%s'",
919 str, g_type_name (G_VALUE_TYPE (value)));
920 /* XXX Do we want to return FALSE here? We can get
921 * forward-compatibility for new values this way
922 */
923 g_free (str);
924 g_type_class_unref (flags_class);
925 return FALSE;
926 }
927
928 g_free (str);
929 }
930 while (_gtk_css_parser_try (parser, ",", FALSE));
931
932 g_type_class_unref (flags_class);
933
934 g_value_set_enum (value, flags);
935
936 return TRUE;
937 }
938
939 static void
flags_value_print(const GValue * value,GString * string)940 flags_value_print (const GValue *value,
941 GString *string)
942 {
943 GFlagsClass *flags_class;
944 guint i, flags;
945
946 flags_class = g_type_class_ref (G_VALUE_TYPE (value));
947 flags = g_value_get_flags (value);
948
949 for (i = 0; i < flags_class->n_values; i++)
950 {
951 GFlagsValue *flags_value = &flags_class->values[i];
952
953 if (flags & flags_value->value)
954 {
955 if (string->len != 0)
956 g_string_append (string, ", ");
957
958 g_string_append (string, flags_value->value_nick);
959 }
960 }
961
962 g_type_class_unref (flags_class);
963 }
964
965 /*** API ***/
966
967 static void
gtk_css_style_funcs_init(void)968 gtk_css_style_funcs_init (void)
969 {
970 if (G_LIKELY (parse_funcs != NULL))
971 return;
972
973 parse_funcs = g_hash_table_new (NULL, NULL);
974 print_funcs = g_hash_table_new (NULL, NULL);
975 compute_funcs = g_hash_table_new (NULL, NULL);
976
977 register_conversion_function (GDK_TYPE_RGBA,
978 rgba_value_parse,
979 rgba_value_print,
980 rgba_value_compute);
981
982 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
983
984 register_conversion_function (GDK_TYPE_COLOR,
985 color_value_parse,
986 color_value_print,
987 color_value_compute);
988
989 register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR,
990 symbolic_color_value_parse,
991 symbolic_color_value_print,
992 NULL);
993
994 G_GNUC_END_IGNORE_DEPRECATIONS;
995
996 register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION,
997 font_description_value_parse,
998 font_description_value_print,
999 NULL);
1000 register_conversion_function (G_TYPE_BOOLEAN,
1001 boolean_value_parse,
1002 boolean_value_print,
1003 NULL);
1004 register_conversion_function (G_TYPE_INT,
1005 int_value_parse,
1006 int_value_print,
1007 NULL);
1008 register_conversion_function (G_TYPE_UINT,
1009 uint_value_parse,
1010 uint_value_print,
1011 NULL);
1012 register_conversion_function (G_TYPE_DOUBLE,
1013 double_value_parse,
1014 double_value_print,
1015 NULL);
1016 register_conversion_function (G_TYPE_FLOAT,
1017 float_value_parse,
1018 float_value_print,
1019 NULL);
1020 register_conversion_function (G_TYPE_STRING,
1021 string_value_parse,
1022 string_value_print,
1023 NULL);
1024
1025 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1026
1027 register_conversion_function (GTK_TYPE_THEMING_ENGINE,
1028 theming_engine_value_parse,
1029 theming_engine_value_print,
1030 NULL);
1031
1032 G_GNUC_END_IGNORE_DEPRECATIONS
1033
1034 register_conversion_function (GTK_TYPE_BORDER,
1035 border_value_parse,
1036 border_value_print,
1037 NULL);
1038
1039 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1040
1041 register_conversion_function (GTK_TYPE_GRADIENT,
1042 gradient_value_parse,
1043 gradient_value_print,
1044 NULL);
1045
1046 G_GNUC_END_IGNORE_DEPRECATIONS;
1047
1048 register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
1049 pattern_value_parse,
1050 pattern_value_print,
1051 pattern_value_compute);
1052 register_conversion_function (G_TYPE_ENUM,
1053 enum_value_parse,
1054 enum_value_print,
1055 NULL);
1056 register_conversion_function (G_TYPE_FLAGS,
1057 flags_value_parse,
1058 flags_value_print,
1059 NULL);
1060 }
1061
1062 /**
1063 * _gtk_css_style_parse_value:
1064 * @value: the value to parse into. Must be a valid initialized #GValue
1065 * @parser: the parser to parse from
1066 *
1067 * This is the generic parsing function used for CSS values. If the
1068 * function fails to parse a value, it will emit an error on @parser,
1069 * return %FALSE and not touch @value.
1070 *
1071 * Returns: %TRUE if parsing succeeded.
1072 **/
1073 gboolean
_gtk_css_style_funcs_parse_value(GValue * value,GtkCssParser * parser)1074 _gtk_css_style_funcs_parse_value (GValue *value,
1075 GtkCssParser *parser)
1076 {
1077 GtkStyleParseFunc func;
1078
1079 g_return_val_if_fail (value != NULL, FALSE);
1080 g_return_val_if_fail (parser != NULL, FALSE);
1081
1082 gtk_css_style_funcs_init ();
1083
1084 func = g_hash_table_lookup (parse_funcs,
1085 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1086 if (func == NULL)
1087 func = g_hash_table_lookup (parse_funcs,
1088 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1089
1090 if (func == NULL)
1091 {
1092 _gtk_css_parser_error (parser,
1093 "Cannot convert to type '%s'",
1094 g_type_name (G_VALUE_TYPE (value)));
1095 return FALSE;
1096 }
1097
1098 return (*func) (parser, value);
1099 }
1100
1101 /**
1102 * _gtk_css_style_print_value:
1103 * @value: an initialized GValue returned from _gtk_css_style_parse()
1104 * @string: the string to print into
1105 *
1106 * Prints @value into @string as a CSS value. If @value is not a
1107 * valid value, a random string will be printed instead.
1108 **/
1109 void
_gtk_css_style_funcs_print_value(const GValue * value,GString * string)1110 _gtk_css_style_funcs_print_value (const GValue *value,
1111 GString *string)
1112 {
1113 GtkStylePrintFunc func;
1114
1115 gtk_css_style_funcs_init ();
1116
1117 func = g_hash_table_lookup (print_funcs,
1118 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1119 if (func == NULL)
1120 func = g_hash_table_lookup (print_funcs,
1121 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1122
1123 if (func == NULL)
1124 {
1125 char *s = g_strdup_value_contents (value);
1126 g_string_append (string, s);
1127 g_free (s);
1128 return;
1129 }
1130
1131 func (value, string);
1132 }
1133
1134 /**
1135 * _gtk_css_style_compute_value:
1136 * @provider: Style provider to look up information from
1137 * @values: The values to compute for
1138 * @parent_values: Values to look up inherited values from
1139 * @target_type: Type the resulting value should have
1140 * @specified: the value to use for the computation
1141 *
1142 * Converts the @specified value into the @computed value using the
1143 * information in @context. The values must have matching types, ie
1144 * @specified must be a result of a call to
1145 * _gtk_css_style_parse_value() with the same type as @computed.
1146 *
1147 * Returns: the resulting value
1148 **/
1149 GtkCssValue *
_gtk_css_style_funcs_compute_value(GtkStyleProviderPrivate * provider,GtkCssStyle * style,GtkCssStyle * parent_style,GType target_type,GtkCssValue * specified)1150 _gtk_css_style_funcs_compute_value (GtkStyleProviderPrivate *provider,
1151 GtkCssStyle *style,
1152 GtkCssStyle *parent_style,
1153 GType target_type,
1154 GtkCssValue *specified)
1155 {
1156 GtkStyleComputeFunc func;
1157
1158 g_return_val_if_fail (GTK_IS_STYLE_PROVIDER (provider), NULL);
1159 g_return_val_if_fail (GTK_IS_CSS_STYLE (style), NULL);
1160 g_return_val_if_fail (parent_style == NULL || GTK_IS_CSS_STYLE (parent_style), NULL);
1161
1162 gtk_css_style_funcs_init ();
1163
1164 func = g_hash_table_lookup (compute_funcs,
1165 GSIZE_TO_POINTER (target_type));
1166 if (func == NULL)
1167 func = g_hash_table_lookup (compute_funcs,
1168 GSIZE_TO_POINTER (g_type_fundamental (target_type)));
1169
1170 if (func)
1171 return func (provider, style, parent_style, specified);
1172 else
1173 return _gtk_css_value_ref (specified);
1174 }
1175
1176