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