]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssstylefuncs.c
Change FSF Address
[~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 "gtkcsstypesprivate.h"
34 #include "gtkgradient.h"
35 #include "gtkprivatetypebuiltins.h"
36 #include "gtkshadowprivate.h"
37 #include "gtkstylecontextprivate.h"
38 #include "gtksymboliccolorprivate.h"
39 #include "gtkthemingengine.h"
40 #include "gtktypebuiltins.h"
41 #include "gtkwin32themeprivate.h"
42
43 /* this is in case round() is not provided by the compiler, 
44  * such as in the case of C89 compilers, like MSVC
45  */
46 #include "fallback-c89.c"
47
48 static GHashTable *parse_funcs = NULL;
49 static GHashTable *print_funcs = NULL;
50 static GHashTable *compute_funcs = NULL;
51
52 typedef gboolean         (* GtkStyleParseFunc)             (GtkCssParser           *parser,
53                                                             GFile                  *base,
54                                                             GValue                 *value);
55 typedef void             (* GtkStylePrintFunc)             (const GValue           *value,
56                                                             GString                *string);
57 typedef void             (* GtkStyleComputeFunc)           (GValue                 *computed,
58                                                             GtkStyleContext        *context,
59                                                             const GValue           *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 void
216 rgba_value_compute (GValue          *computed,
217                     GtkStyleContext *context,
218                     const GValue    *specified)
219 {
220   GdkRGBA rgba, white = { 1, 1, 1, 1 };
221
222   if (G_VALUE_HOLDS (specified, GTK_TYPE_CSS_SPECIAL_VALUE))
223     {
224     }
225   else if (G_VALUE_HOLDS (specified, GTK_TYPE_SYMBOLIC_COLOR))
226     {
227       GtkSymbolicColor *symbolic = g_value_get_boxed (specified);
228       
229       if (symbolic == _gtk_symbolic_color_get_current_color ())
230         g_value_copy (_gtk_style_context_peek_property (context, "color"), computed);
231       else if (_gtk_style_context_resolve_color (context,
232                                                  symbolic,
233                                                  &rgba))
234         g_value_set_boxed (computed, &rgba);
235       else
236         g_value_set_boxed (computed, &white);
237     }
238   else
239     g_value_copy (specified, computed);
240 }
241
242 static gboolean 
243 color_value_parse (GtkCssParser *parser,
244                    GFile        *base,
245                    GValue       *value)
246 {
247   GtkSymbolicColor *symbolic;
248   GdkRGBA rgba;
249
250   symbolic = _gtk_css_parser_read_symbolic_color (parser);
251   if (symbolic == NULL)
252     return FALSE;
253
254   if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
255     {
256       GdkColor color;
257
258       color.red = rgba.red * 65535. + 0.5;
259       color.green = rgba.green * 65535. + 0.5;
260       color.blue = rgba.blue * 65535. + 0.5;
261
262       g_value_set_boxed (value, &color);
263     }
264   else
265     {
266       g_value_unset (value);
267       g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
268       g_value_take_boxed (value, symbolic);
269     }
270
271   return TRUE;
272 }
273
274 static void
275 color_value_print (const GValue *value,
276                    GString      *string)
277 {
278   const GdkColor *color = g_value_get_boxed (value);
279
280   if (color == NULL)
281     g_string_append (string, "none");
282   else
283     {
284       char *s = gdk_color_to_string (color);
285       g_string_append (string, s);
286       g_free (s);
287     }
288 }
289
290 static void
291 color_value_compute (GValue          *computed,
292                      GtkStyleContext *context,
293                      const GValue    *specified)
294 {
295   GdkRGBA rgba;
296   GdkColor color = { 0, 65535, 65535, 65535 };
297
298   if (G_VALUE_HOLDS (specified, GTK_TYPE_SYMBOLIC_COLOR))
299     {
300       if (_gtk_style_context_resolve_color (context,
301                                             g_value_get_boxed (specified),
302                                             &rgba))
303         {
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         }
308       
309       g_value_set_boxed (computed, &color);
310     }
311   else
312     g_value_copy (specified, computed);
313 }
314
315 static gboolean
316 symbolic_color_value_parse (GtkCssParser *parser,
317                             GFile        *base,
318                             GValue       *value)
319 {
320   GtkSymbolicColor *symbolic;
321
322   if (_gtk_css_parser_try (parser, "currentcolor", TRUE))
323     {
324       symbolic = gtk_symbolic_color_ref (_gtk_symbolic_color_get_current_color ());
325     }
326   else
327     {
328       symbolic = _gtk_css_parser_read_symbolic_color (parser);
329       if (symbolic == NULL)
330         return FALSE;
331     }
332
333   g_value_take_boxed (value, symbolic);
334   return TRUE;
335 }
336
337 static void
338 symbolic_color_value_print (const GValue *value,
339                             GString      *string)
340 {
341   GtkSymbolicColor *symbolic = g_value_get_boxed (value);
342
343   if (symbolic == NULL)
344     g_string_append (string, "none");
345   else
346     {
347       char *s = gtk_symbolic_color_to_string (symbolic);
348       g_string_append (string, s);
349       g_free (s);
350     }
351 }
352
353 static gboolean 
354 font_description_value_parse (GtkCssParser *parser,
355                               GFile        *base,
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
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 
396 boolean_value_parse (GtkCssParser *parser,
397                      GFile        *base,
398                      GValue       *value)
399 {
400   if (_gtk_css_parser_try (parser, "true", TRUE) ||
401       _gtk_css_parser_try (parser, "1", TRUE))
402     {
403       g_value_set_boolean (value, TRUE);
404       return TRUE;
405     }
406   else if (_gtk_css_parser_try (parser, "false", TRUE) ||
407            _gtk_css_parser_try (parser, "0", TRUE))
408     {
409       g_value_set_boolean (value, FALSE);
410       return TRUE;
411     }
412   else
413     {
414       _gtk_css_parser_error (parser, "Expected a boolean value");
415       return FALSE;
416     }
417 }
418
419 static void
420 boolean_value_print (const GValue *value,
421                      GString      *string)
422 {
423   if (g_value_get_boolean (value))
424     g_string_append (string, "true");
425   else
426     g_string_append (string, "false");
427 }
428
429 static gboolean 
430 int_value_parse (GtkCssParser *parser,
431                  GFile        *base,
432                  GValue       *value)
433 {
434   gint i;
435
436   if (_gtk_css_parser_begins_with (parser, '-'))
437     {
438       int res = _gtk_win32_theme_int_parse (parser, base, &i);
439       if (res >= 0)
440         {
441           g_value_set_int (value, i);
442           return res > 0;
443         }
444       /* < 0 => continue */
445     }
446
447   if (!_gtk_css_parser_try_int (parser, &i))
448     {
449       _gtk_css_parser_error (parser, "Expected a valid integer value");
450       return FALSE;
451     }
452
453   g_value_set_int (value, i);
454   return TRUE;
455 }
456
457 static void
458 int_value_print (const GValue *value,
459                  GString      *string)
460 {
461   g_string_append_printf (string, "%d", g_value_get_int (value));
462 }
463
464 static gboolean 
465 uint_value_parse (GtkCssParser *parser,
466                   GFile        *base,
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
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 
489 double_value_parse (GtkCssParser *parser,
490                     GFile        *base,
491                     GValue       *value)
492 {
493   gdouble d;
494
495   if (!_gtk_css_parser_try_double (parser, &d))
496     {
497       _gtk_css_parser_error (parser, "Expected a number");
498       return FALSE;
499     }
500
501   g_value_set_double (value, d);
502   return TRUE;
503 }
504
505 static void
506 double_value_print (const GValue *value,
507                     GString      *string)
508 {
509   string_append_double (string, g_value_get_double (value));
510 }
511
512 static gboolean 
513 float_value_parse (GtkCssParser *parser,
514                    GFile        *base,
515                    GValue       *value)
516 {
517   gdouble d;
518
519   if (!_gtk_css_parser_try_double (parser, &d))
520     {
521       _gtk_css_parser_error (parser, "Expected a number");
522       return FALSE;
523     }
524
525   g_value_set_float (value, d);
526   return TRUE;
527 }
528
529 static void
530 float_value_print (const GValue *value,
531                    GString      *string)
532 {
533   string_append_double (string, g_value_get_float (value));
534 }
535
536 static gboolean 
537 string_value_parse (GtkCssParser *parser,
538                     GFile        *base,
539                     GValue       *value)
540 {
541   char *str = _gtk_css_parser_read_string (parser);
542
543   if (str == NULL)
544     return FALSE;
545
546   g_value_take_string (value, str);
547   return TRUE;
548 }
549
550 static void
551 string_value_print (const GValue *value,
552                     GString      *str)
553 {
554   string_append_string (str, g_value_get_string (value));
555 }
556
557 static gboolean 
558 theming_engine_value_parse (GtkCssParser *parser,
559                             GFile        *base,
560                             GValue       *value)
561 {
562   GtkThemingEngine *engine;
563   char *str;
564
565   if (_gtk_css_parser_try (parser, "none", TRUE))
566     {
567       g_value_set_object (value, gtk_theming_engine_load (NULL));
568       return TRUE;
569     }
570
571   str = _gtk_css_parser_try_ident (parser, TRUE);
572   if (str == NULL)
573     {
574       _gtk_css_parser_error (parser, "Expected a valid theme name");
575       return FALSE;
576     }
577
578   engine = gtk_theming_engine_load (str);
579
580   if (engine == NULL)
581     {
582       _gtk_css_parser_error (parser, "Theming engine '%s' not found", str);
583       g_free (str);
584       return FALSE;
585     }
586
587   g_value_set_object (value, engine);
588   g_free (str);
589   return TRUE;
590 }
591
592 static void
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 
612 animation_description_value_parse (GtkCssParser *parser,
613                                    GFile        *base,
614                                    GValue       *value)
615 {
616   GtkAnimationDescription *desc;
617   char *str;
618
619   str = _gtk_css_parser_read_value (parser);
620   if (str == NULL)
621     return FALSE;
622
623   desc = _gtk_animation_description_from_string (str);
624   g_free (str);
625
626   if (desc == NULL)
627     {
628       _gtk_css_parser_error (parser, "Invalid animation description");
629       return FALSE;
630     }
631   
632   g_value_take_boxed (value, desc);
633   return TRUE;
634 }
635
636 static void
637 animation_description_value_print (const GValue *value,
638                                    GString      *string)
639 {
640   GtkAnimationDescription *desc = g_value_get_boxed (value);
641
642   if (desc == NULL)
643     g_string_append (string, "none");
644   else
645     _gtk_animation_description_print (desc, string);
646 }
647
648 static gboolean 
649 border_value_parse (GtkCssParser *parser,
650                     GFile        *base,
651                     GValue       *value)
652 {
653   GtkBorder border = { 0, };
654   guint i;
655   int numbers[4];
656
657   for (i = 0; i < G_N_ELEMENTS (numbers); i++)
658     {
659       if (_gtk_css_parser_begins_with (parser, '-'))
660         {
661           /* These are strictly speaking signed, but we want to be able to use them
662              for unsigned types too, as the actual ranges of values make this safe */
663           int res = _gtk_win32_theme_int_parse (parser, base, &numbers[i]);
664
665           if (res == 0) /* Parse error, report */
666             return FALSE;
667
668           if (res < 0) /* Nothing known to expand */
669             break;
670         }
671       else
672         {
673           if (!_gtk_css_parser_try_length (parser, &numbers[i]))
674             break;
675         }
676     }
677
678   if (i == 0)
679     {
680       _gtk_css_parser_error (parser, "Expected valid border");
681       return FALSE;
682     }
683
684   border.top = numbers[0];
685   if (i > 1)
686     border.right = numbers[1];
687   else
688     border.right = border.top;
689   if (i > 2)
690     border.bottom = numbers[2];
691   else
692     border.bottom = border.top;
693   if (i > 3)
694     border.left = numbers[3];
695   else
696     border.left = border.right;
697
698   g_value_set_boxed (value, &border);
699   return TRUE;
700 }
701
702 static void
703 border_value_print (const GValue *value, GString *string)
704 {
705   const GtkBorder *border = g_value_get_boxed (value);
706
707   if (border == NULL)
708     g_string_append (string, "none");
709   else if (border->left != border->right)
710     g_string_append_printf (string, "%d %d %d %d", border->top, border->right, border->bottom, border->left);
711   else if (border->top != border->bottom)
712     g_string_append_printf (string, "%d %d %d", border->top, border->right, border->bottom);
713   else if (border->top != border->left)
714     g_string_append_printf (string, "%d %d", border->top, border->right);
715   else
716     g_string_append_printf (string, "%d", border->top);
717 }
718
719 static gboolean 
720 gradient_value_parse (GtkCssParser *parser,
721                       GFile        *base,
722                       GValue       *value)
723 {
724   GtkGradient *gradient;
725
726   gradient = _gtk_gradient_parse (parser);
727   if (gradient == NULL)
728     return FALSE;
729
730   g_value_take_boxed (value, gradient);
731   return TRUE;
732 }
733
734 static void
735 gradient_value_print (const GValue *value,
736                       GString      *string)
737 {
738   GtkGradient *gradient = g_value_get_boxed (value);
739
740   if (gradient == NULL)
741     g_string_append (string, "none");
742   else
743     {
744       char *s = gtk_gradient_to_string (gradient);
745       g_string_append (string, s);
746       g_free (s);
747     }
748 }
749
750 static gboolean 
751 pattern_value_parse (GtkCssParser *parser,
752                      GFile        *base,
753                      GValue       *value)
754 {
755   if (_gtk_css_parser_try (parser, "none", TRUE))
756     {
757       /* nothing to do here */
758     }
759   else if (_gtk_css_parser_begins_with (parser, '-'))
760     {
761       g_value_unset (value);
762       g_value_init (value, GTK_TYPE_GRADIENT);
763       return gradient_value_parse (parser, base, value);
764     }
765   else
766     {
767       GError *error = NULL;
768       gchar *path;
769       GdkPixbuf *pixbuf;
770       GFile *file;
771       cairo_surface_t *surface;
772       cairo_pattern_t *pattern;
773       cairo_t *cr;
774       cairo_matrix_t matrix;
775
776       file = _gtk_css_parser_read_url (parser, base);
777       if (file == NULL)
778         return FALSE;
779
780       path = g_file_get_path (file);
781       g_object_unref (file);
782
783       pixbuf = gdk_pixbuf_new_from_file (path, &error);
784       g_free (path);
785       if (pixbuf == NULL)
786         {
787           _gtk_css_parser_take_error (parser, error);
788           return FALSE;
789         }
790
791       surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
792                                             gdk_pixbuf_get_width (pixbuf),
793                                             gdk_pixbuf_get_height (pixbuf));
794       cr = cairo_create (surface);
795       gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
796       cairo_paint (cr);
797       pattern = cairo_pattern_create_for_surface (surface);
798
799       cairo_matrix_init_scale (&matrix,
800                                gdk_pixbuf_get_width (pixbuf),
801                                gdk_pixbuf_get_height (pixbuf));
802       cairo_pattern_set_matrix (pattern, &matrix);
803
804       cairo_surface_destroy (surface);
805       cairo_destroy (cr);
806       g_object_unref (pixbuf);
807
808       g_value_take_boxed (value, pattern);
809     }
810   
811   return TRUE;
812 }
813
814 static cairo_status_t
815 surface_write (void                *closure,
816                const unsigned char *data,
817                unsigned int         length)
818 {
819   g_byte_array_append (closure, data, length);
820
821   return CAIRO_STATUS_SUCCESS;
822 }
823
824 static void
825 surface_print (cairo_surface_t *surface,
826                GString *        string)
827 {
828 #if CAIRO_HAS_PNG_FUNCTIONS
829   GByteArray *array;
830   char *base64;
831   
832   array = g_byte_array_new ();
833   cairo_surface_write_to_png_stream (surface, surface_write, array);
834   base64 = g_base64_encode (array->data, array->len);
835   g_byte_array_free (array, TRUE);
836
837   g_string_append (string, "url(\"data:image/png;base64,");
838   g_string_append (string, base64);
839   g_string_append (string, "\")");
840
841   g_free (base64);
842 #else
843   g_string_append (string, "none /* you need cairo png functions enabled to make this work */");
844 #endif
845 }
846
847 static void
848 pattern_value_print (const GValue *value,
849                      GString      *string)
850 {
851   cairo_pattern_t *pattern;
852   cairo_surface_t *surface;
853
854   pattern = g_value_get_boxed (value);
855
856   if (pattern == NULL)
857     {
858       g_string_append (string, "none");
859       return;
860     }
861
862   switch (cairo_pattern_get_type (pattern))
863     {
864     case CAIRO_PATTERN_TYPE_SURFACE:
865       if (cairo_pattern_get_surface (pattern, &surface) != CAIRO_STATUS_SUCCESS)
866         {
867           g_assert_not_reached ();
868         }
869       surface_print (surface, string);
870       break;
871     case CAIRO_PATTERN_TYPE_SOLID:
872     case CAIRO_PATTERN_TYPE_LINEAR:
873     case CAIRO_PATTERN_TYPE_RADIAL:
874     default:
875       g_assert_not_reached ();
876       break;
877     }
878 }
879
880 static void
881 pattern_value_compute (GValue          *computed,
882                        GtkStyleContext *context,
883                        const GValue    *specified)
884 {
885   if (G_VALUE_HOLDS (specified, GTK_TYPE_GRADIENT))
886     {
887       cairo_pattern_t *gradient;
888       
889       gradient = gtk_gradient_resolve_for_context (g_value_get_boxed (specified), context);
890
891       g_value_take_boxed (computed, gradient);
892     }
893   else
894     g_value_copy (specified, computed);
895 }
896
897 static gboolean
898 shadow_value_parse (GtkCssParser *parser,
899                     GFile *base,
900                     GValue *value)
901 {
902   gboolean have_inset, have_color, have_lengths;
903   gdouble hoffset, voffset, blur, spread;
904   GtkSymbolicColor *color;
905   GtkShadow *shadow;
906   guint i;
907
908   if (_gtk_css_parser_try (parser, "none", TRUE))
909     return TRUE;
910
911   shadow = _gtk_shadow_new ();
912
913   do
914     {
915       have_inset = have_lengths = have_color = FALSE;
916
917       for (i = 0; i < 3; i++)
918         {
919           if (!have_inset && 
920               _gtk_css_parser_try (parser, "inset", TRUE))
921             {
922               have_inset = TRUE;
923               continue;
924             }
925             
926           if (!have_lengths &&
927               _gtk_css_parser_try_double (parser, &hoffset))
928             {
929               have_lengths = TRUE;
930
931               if (!_gtk_css_parser_try_double (parser, &voffset))
932                 {
933                   _gtk_css_parser_error (parser, "Horizontal and vertical offsets are required");
934                   _gtk_shadow_unref (shadow);
935                   return FALSE;
936                 }
937
938               if (!_gtk_css_parser_try_double (parser, &blur))
939                 blur = 0;
940
941               if (!_gtk_css_parser_try_double (parser, &spread))
942                 spread = 0;
943
944               continue;
945             }
946
947           if (!have_color)
948             {
949               have_color = TRUE;
950
951               /* XXX: the color is optional and UA-defined if it's missing,
952                * but it doesn't really make sense for us...
953                */
954               color = _gtk_css_parser_read_symbolic_color (parser);
955
956               if (color == NULL)
957                 {
958                   _gtk_shadow_unref (shadow);
959                   return FALSE;
960                 }
961             }
962         }
963
964       if (!have_color || !have_lengths)
965         {
966           _gtk_css_parser_error (parser, "Must specify at least color and offsets");
967           _gtk_shadow_unref (shadow);
968           return FALSE;
969         }
970
971       _gtk_shadow_append (shadow,
972                           hoffset, voffset,
973                           blur, spread,
974                           have_inset, color);
975
976       gtk_symbolic_color_unref (color);
977
978     }
979   while (_gtk_css_parser_try (parser, ",", TRUE));
980
981   g_value_take_boxed (value, shadow);
982   return TRUE;
983 }
984
985 static void
986 shadow_value_print (const GValue *value,
987                     GString      *string)
988 {
989   GtkShadow *shadow;
990
991   shadow = g_value_get_boxed (value);
992
993   if (shadow == NULL)
994     g_string_append (string, "none");
995   else
996     _gtk_shadow_print (shadow, string);
997 }
998
999 static void
1000 shadow_value_compute (GValue          *computed,
1001                       GtkStyleContext *context,
1002                       const GValue    *specified)
1003 {
1004   GtkShadow *shadow;
1005
1006   shadow = g_value_get_boxed (specified);
1007   if (shadow)
1008     shadow = _gtk_shadow_resolve (shadow, context);
1009
1010   g_value_take_boxed (computed, shadow);
1011 }
1012
1013 static gboolean
1014 border_image_repeat_value_parse (GtkCssParser *parser,
1015                                  GFile *file,
1016                                  GValue *value)
1017 {
1018   GtkCssBorderImageRepeat image_repeat;
1019   GtkCssBorderRepeatStyle styles[2];
1020   gint i, v;
1021
1022   for (i = 0; i < 2; i++)
1023     {
1024       if (_gtk_css_parser_try_enum (parser, GTK_TYPE_CSS_BORDER_REPEAT_STYLE, &v))
1025         styles[i] = v;
1026       else if (i == 0)
1027         {
1028           styles[1] = styles[0] = GTK_CSS_REPEAT_STYLE_STRETCH;
1029           break;
1030         }
1031       else
1032         styles[i] = styles[0];
1033     }
1034
1035   image_repeat.hrepeat = styles[0];
1036   image_repeat.vrepeat = styles[1];
1037
1038   g_value_set_boxed (value, &image_repeat);
1039
1040   return TRUE;
1041 }
1042
1043 static void
1044 border_image_repeat_value_print (const GValue *value,
1045                                  GString      *string)
1046 {
1047   GtkCssBorderImageRepeat *image_repeat;
1048
1049   image_repeat = g_value_get_boxed (value);
1050
1051   enum_print (image_repeat->hrepeat, GTK_TYPE_CSS_BORDER_REPEAT_STYLE, string);
1052   if (image_repeat->hrepeat != image_repeat->vrepeat)
1053     {
1054       g_string_append (string, " ");
1055       enum_print (image_repeat->vrepeat, GTK_TYPE_CSS_BORDER_REPEAT_STYLE, string);
1056     }
1057 }
1058
1059 static void
1060 css_number_print (const GValue *value,
1061                   GString      *string)
1062 {
1063   _gtk_css_number_print (g_value_get_boxed (value), string);
1064 }
1065
1066 static gboolean 
1067 enum_value_parse (GtkCssParser *parser,
1068                   GFile        *base,
1069                   GValue       *value)
1070 {
1071   int v;
1072
1073   if (enum_parse (parser, G_VALUE_TYPE (value), &v))
1074     {
1075       g_value_set_enum (value, v);
1076       return TRUE;
1077     }
1078
1079   return FALSE;
1080 }
1081
1082 static void
1083 enum_value_print (const GValue *value,
1084                   GString      *string)
1085 {
1086   enum_print (g_value_get_enum (value), G_VALUE_TYPE (value), string);
1087 }
1088
1089 static gboolean 
1090 flags_value_parse (GtkCssParser *parser,
1091                    GFile        *base,
1092                    GValue       *value)
1093 {
1094   GFlagsClass *flags_class;
1095   GFlagsValue *flag_value;
1096   guint flags = 0;
1097   char *str;
1098
1099   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1100
1101   do {
1102     str = _gtk_css_parser_try_ident (parser, TRUE);
1103     if (str == NULL)
1104       {
1105         _gtk_css_parser_error (parser, "Expected an identifier");
1106         g_type_class_unref (flags_class);
1107         return FALSE;
1108       }
1109
1110       flag_value = g_flags_get_value_by_nick (flags_class, str);
1111       if (!flag_value)
1112         {
1113           _gtk_css_parser_error (parser,
1114                                  "Unknown flag value '%s' for type '%s'",
1115                                  str, g_type_name (G_VALUE_TYPE (value)));
1116           /* XXX Do we want to return FALSE here? We can get
1117            * forward-compatibility for new values this way
1118            */
1119           g_free (str);
1120           g_type_class_unref (flags_class);
1121           return FALSE;
1122         }
1123
1124       g_free (str);
1125     }
1126   while (_gtk_css_parser_try (parser, ",", FALSE));
1127
1128   g_type_class_unref (flags_class);
1129
1130   g_value_set_enum (value, flags);
1131
1132   return TRUE;
1133 }
1134
1135 static void
1136 flags_value_print (const GValue *value,
1137                    GString      *string)
1138 {
1139   GFlagsClass *flags_class;
1140   guint i, flags;
1141
1142   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1143   flags = g_value_get_flags (value);
1144
1145   for (i = 0; i < flags_class->n_values; i++)
1146     {
1147       GFlagsValue *flags_value = &flags_class->values[i];
1148
1149       if (flags & flags_value->value)
1150         {
1151           if (string->len != 0)
1152             g_string_append (string, ", ");
1153
1154           g_string_append (string, flags_value->value_nick);
1155         }
1156     }
1157
1158   g_type_class_unref (flags_class);
1159 }
1160
1161 /*** API ***/
1162
1163 static void
1164 gtk_css_style_funcs_init (void)
1165 {
1166   if (G_LIKELY (parse_funcs != NULL))
1167     return;
1168
1169   parse_funcs = g_hash_table_new (NULL, NULL);
1170   print_funcs = g_hash_table_new (NULL, NULL);
1171   compute_funcs = g_hash_table_new (NULL, NULL);
1172
1173   register_conversion_function (GDK_TYPE_RGBA,
1174                                 rgba_value_parse,
1175                                 rgba_value_print,
1176                                 rgba_value_compute);
1177   register_conversion_function (GDK_TYPE_COLOR,
1178                                 color_value_parse,
1179                                 color_value_print,
1180                                 color_value_compute);
1181   register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR,
1182                                 symbolic_color_value_parse,
1183                                 symbolic_color_value_print,
1184                                 NULL);
1185   register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION,
1186                                 font_description_value_parse,
1187                                 font_description_value_print,
1188                                 NULL);
1189   register_conversion_function (G_TYPE_BOOLEAN,
1190                                 boolean_value_parse,
1191                                 boolean_value_print,
1192                                 NULL);
1193   register_conversion_function (G_TYPE_INT,
1194                                 int_value_parse,
1195                                 int_value_print,
1196                                 NULL);
1197   register_conversion_function (G_TYPE_UINT,
1198                                 uint_value_parse,
1199                                 uint_value_print,
1200                                 NULL);
1201   register_conversion_function (G_TYPE_DOUBLE,
1202                                 double_value_parse,
1203                                 double_value_print,
1204                                 NULL);
1205   register_conversion_function (G_TYPE_FLOAT,
1206                                 float_value_parse,
1207                                 float_value_print,
1208                                 NULL);
1209   register_conversion_function (G_TYPE_STRING,
1210                                 string_value_parse,
1211                                 string_value_print,
1212                                 NULL);
1213   register_conversion_function (GTK_TYPE_THEMING_ENGINE,
1214                                 theming_engine_value_parse,
1215                                 theming_engine_value_print,
1216                                 NULL);
1217   register_conversion_function (GTK_TYPE_ANIMATION_DESCRIPTION,
1218                                 animation_description_value_parse,
1219                                 animation_description_value_print,
1220                                 NULL);
1221   register_conversion_function (GTK_TYPE_BORDER,
1222                                 border_value_parse,
1223                                 border_value_print,
1224                                 NULL);
1225   register_conversion_function (GTK_TYPE_GRADIENT,
1226                                 gradient_value_parse,
1227                                 gradient_value_print,
1228                                 NULL);
1229   register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
1230                                 pattern_value_parse,
1231                                 pattern_value_print,
1232                                 pattern_value_compute);
1233   register_conversion_function (GTK_TYPE_CSS_BORDER_IMAGE_REPEAT,
1234                                 border_image_repeat_value_parse,
1235                                 border_image_repeat_value_print,
1236                                 NULL);
1237   register_conversion_function (GTK_TYPE_SHADOW,
1238                                 shadow_value_parse,
1239                                 shadow_value_print,
1240                                 shadow_value_compute);
1241   register_conversion_function (GTK_TYPE_CSS_NUMBER,
1242                                 NULL,
1243                                 css_number_print,
1244                                 NULL);
1245   register_conversion_function (G_TYPE_ENUM,
1246                                 enum_value_parse,
1247                                 enum_value_print,
1248                                 NULL);
1249   register_conversion_function (G_TYPE_FLAGS,
1250                                 flags_value_parse,
1251                                 flags_value_print,
1252                                 NULL);
1253 }
1254
1255 /**
1256  * _gtk_css_style_parse_value:
1257  * @value: the value to parse into. Must be a valid initialized #GValue
1258  * @parser: the parser to parse from
1259  * @base: the base URL for @parser
1260  *
1261  * This is the generic parsing function used for CSS values. If the
1262  * function fails to parse a value, it will emit an error on @parser,
1263  * return %FALSE and not touch @value.
1264  *
1265  * Returns: %TRUE if parsing succeeded.
1266  **/
1267 gboolean
1268 _gtk_css_style_parse_value (GValue       *value,
1269                             GtkCssParser *parser,
1270                             GFile        *base)
1271 {
1272   GtkStyleParseFunc func;
1273
1274   g_return_val_if_fail (value != NULL, FALSE);
1275   g_return_val_if_fail (parser != NULL, FALSE);
1276
1277   gtk_css_style_funcs_init ();
1278
1279   func = g_hash_table_lookup (parse_funcs,
1280                               GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1281   if (func == NULL)
1282     func = g_hash_table_lookup (parse_funcs,
1283                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1284
1285   if (func == NULL)
1286     {
1287       _gtk_css_parser_error (parser,
1288                              "Cannot convert to type '%s'",
1289                              g_type_name (G_VALUE_TYPE (value)));
1290       return FALSE;
1291     }
1292
1293   return (*func) (parser, base, value);
1294 }
1295
1296 /**
1297  * _gtk_css_style_print_value:
1298  * @value: an initialized GValue returned from _gtk_css_style_parse()
1299  * @string: the string to print into
1300  *
1301  * Prints @value into @string as a CSS value. If @value is not a
1302  * valid value, a random string will be printed instead.
1303  **/
1304 void
1305 _gtk_css_style_print_value (const GValue *value,
1306                             GString      *string)
1307 {
1308   GtkStylePrintFunc func;
1309
1310   gtk_css_style_funcs_init ();
1311
1312   func = g_hash_table_lookup (print_funcs,
1313                               GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
1314   if (func == NULL)
1315     func = g_hash_table_lookup (print_funcs,
1316                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
1317
1318   if (func == NULL)
1319     {
1320       char *s = g_strdup_value_contents (value);
1321       g_string_append (string, s);
1322       g_free (s);
1323       return;
1324     }
1325   
1326   func (value, string);
1327 }
1328
1329 /**
1330  * _gtk_css_style_compute_value:
1331  * @computed: (out): a value to be filled with the result
1332  * @context: the context to use for computing the value
1333  * @specified: the value to use for the computation
1334  *
1335  * Converts the @specified value into the @computed value using the
1336  * information in @context. The values must have matching types, ie
1337  * @specified must be a result of a call to
1338  * _gtk_css_style_parse_value() with the same type as @computed.
1339  **/
1340 void
1341 _gtk_css_style_compute_value (GValue          *computed,
1342                               GtkStyleContext *context,
1343                               const GValue    *specified)
1344 {
1345   GtkStyleComputeFunc func;
1346
1347   g_return_if_fail (G_IS_VALUE (computed));
1348   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
1349   g_return_if_fail (G_IS_VALUE (specified));
1350
1351   gtk_css_style_funcs_init ();
1352
1353   func = g_hash_table_lookup (compute_funcs,
1354                               GSIZE_TO_POINTER (G_VALUE_TYPE (computed)));
1355   if (func == NULL)
1356     func = g_hash_table_lookup (compute_funcs,
1357                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (computed))));
1358
1359   if (func)
1360     func (computed, context, specified);
1361   else
1362     g_value_copy (specified, computed);
1363 }
1364