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