]> Pileus Git - ~andy/gtk/blob - gtk/gtkstyleproperty.c
win32-theme: Support -gtk-win32-size CSS value
[~andy/gtk] / gtk / gtkstyleproperty.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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21
22 #include "gtkstylepropertyprivate.h"
23
24 #include <errno.h>
25 #include <math.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <gdk-pixbuf/gdk-pixbuf.h>
30 #include <cairo-gobject.h>
31
32 #include "gtkcssprovider.h"
33 #include "gtkcssparserprivate.h"
34 #include "gtkcsstypesprivate.h"
35
36 /* the actual parsers we have */
37 #include "gtkanimationdescription.h"
38 #include "gtkbindings.h"
39 #include "gtkborderimageprivate.h"
40 #include "gtkgradient.h"
41 #include "gtkshadowprivate.h"
42 #include "gtkthemingengine.h"
43 #include "gtktypebuiltins.h"
44 #include "gtkwin32themeprivate.h"
45
46 /* this is in case round() is not provided by the compiler, 
47  * such as in the case of C89 compilers, like MSVC
48  */
49 #include "fallback-c89.c"
50
51 static GHashTable *parse_funcs = NULL;
52 static GHashTable *print_funcs = NULL;
53 static GHashTable *properties = NULL;
54
55 static void
56 register_conversion_function (GType             type,
57                               GtkStyleParseFunc parse,
58                               GtkStylePrintFunc print)
59 {
60   if (parse)
61     g_hash_table_insert (parse_funcs, GSIZE_TO_POINTER (type), parse);
62   if (print)
63     g_hash_table_insert (print_funcs, GSIZE_TO_POINTER (type), print);
64 }
65
66 static void
67 string_append_double (GString *string,
68                       double   d)
69 {
70   char buf[G_ASCII_DTOSTR_BUF_SIZE];
71
72   g_ascii_dtostr (buf, sizeof (buf), d);
73   g_string_append (string, buf);
74 }
75
76 static void
77 string_append_string (GString    *str,
78                       const char *string)
79 {
80   gsize len;
81
82   g_string_append_c (str, '"');
83
84   do {
85     len = strcspn (string, "\"\n\r\f");
86     g_string_append (str, string);
87     string += len;
88     switch (*string)
89       {
90       case '\0':
91         break;
92       case '\n':
93         g_string_append (str, "\\A ");
94         break;
95       case '\r':
96         g_string_append (str, "\\D ");
97         break;
98       case '\f':
99         g_string_append (str, "\\C ");
100         break;
101       case '\"':
102         g_string_append (str, "\\\"");
103         break;
104       default:
105         g_assert_not_reached ();
106         break;
107       }
108   } while (*string);
109
110   g_string_append_c (str, '"');
111 }
112
113 /*** IMPLEMENTATIONS ***/
114
115 static gboolean 
116 rgba_value_parse (GtkCssParser *parser,
117                   GFile        *base,
118                   GValue       *value)
119 {
120   GtkSymbolicColor *symbolic;
121   GdkRGBA rgba;
122
123   symbolic = _gtk_css_parser_read_symbolic_color (parser);
124   if (symbolic == NULL)
125     return FALSE;
126
127   if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
128     {
129       g_value_set_boxed (value, &rgba);
130       gtk_symbolic_color_unref (symbolic);
131     }
132   else
133     {
134       g_value_unset (value);
135       g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
136       g_value_take_boxed (value, symbolic);
137     }
138
139   return TRUE;
140 }
141
142 static void
143 rgba_value_print (const GValue *value,
144                   GString      *string)
145 {
146   const GdkRGBA *rgba = g_value_get_boxed (value);
147
148   if (rgba == NULL)
149     g_string_append (string, "none");
150   else
151     {
152       char *s = gdk_rgba_to_string (rgba);
153       g_string_append (string, s);
154       g_free (s);
155     }
156 }
157
158 static gboolean 
159 color_value_parse (GtkCssParser *parser,
160                    GFile        *base,
161                    GValue       *value)
162 {
163   GtkSymbolicColor *symbolic;
164   GdkRGBA rgba;
165
166   symbolic = _gtk_css_parser_read_symbolic_color (parser);
167   if (symbolic == NULL)
168     return FALSE;
169
170   if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
171     {
172       GdkColor color;
173
174       color.red = rgba.red * 65535. + 0.5;
175       color.green = rgba.green * 65535. + 0.5;
176       color.blue = rgba.blue * 65535. + 0.5;
177
178       g_value_set_boxed (value, &color);
179     }
180   else
181     {
182       g_value_unset (value);
183       g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
184       g_value_take_boxed (value, symbolic);
185     }
186
187   return TRUE;
188 }
189
190 static void
191 color_value_print (const GValue *value,
192                    GString      *string)
193 {
194   const GdkColor *color = g_value_get_boxed (value);
195
196   if (color == NULL)
197     g_string_append (string, "none");
198   else
199     {
200       char *s = gdk_color_to_string (color);
201       g_string_append (string, s);
202       g_free (s);
203     }
204 }
205
206 static gboolean
207 symbolic_color_value_parse (GtkCssParser *parser,
208                             GFile        *base,
209                             GValue       *value)
210 {
211   GtkSymbolicColor *symbolic;
212
213   symbolic = _gtk_css_parser_read_symbolic_color (parser);
214   if (symbolic == NULL)
215     return FALSE;
216
217   g_value_take_boxed (value, symbolic);
218   return TRUE;
219 }
220
221 static void
222 symbolic_color_value_print (const GValue *value,
223                             GString      *string)
224 {
225   GtkSymbolicColor *symbolic = g_value_get_boxed (value);
226
227   if (symbolic == NULL)
228     g_string_append (string, "none");
229   else
230     {
231       char *s = gtk_symbolic_color_to_string (symbolic);
232       g_string_append (string, s);
233       g_free (s);
234     }
235 }
236
237 static gboolean
238 font_family_parse (GtkCssParser *parser,
239                    GFile        *base,
240                    GValue       *value)
241 {
242   GPtrArray *names;
243   char *name;
244
245   /* We don't special case generic families. Pango should do
246    * that for us */
247
248   names = g_ptr_array_new ();
249
250   do {
251     name = _gtk_css_parser_try_ident (parser, TRUE);
252     if (name)
253       {
254         GString *string = g_string_new (name);
255         g_free (name);
256         while ((name = _gtk_css_parser_try_ident (parser, TRUE)))
257           {
258             g_string_append_c (string, ' ');
259             g_string_append (string, name);
260             g_free (name);
261           }
262         name = g_string_free (string, FALSE);
263       }
264     else 
265       {
266         name = _gtk_css_parser_read_string (parser);
267         if (name == NULL)
268           {
269             g_ptr_array_free (names, TRUE);
270             return FALSE;
271           }
272       }
273
274     g_ptr_array_add (names, name);
275   } while (_gtk_css_parser_try (parser, ",", TRUE));
276
277   /* NULL-terminate array */
278   g_ptr_array_add (names, NULL);
279   g_value_set_boxed (value, g_ptr_array_free (names, FALSE));
280   return TRUE;
281 }
282
283 static void
284 font_family_value_print (const GValue *value,
285                          GString      *string)
286 {
287   const char **names = g_value_get_boxed (value);
288
289   if (names == NULL || *names == NULL)
290     {
291       g_string_append (string, "none");
292       return;
293     }
294
295   string_append_string (string, *names);
296   names++;
297   while (*names)
298     {
299       g_string_append (string, ", ");
300       string_append_string (string, *names);
301       names++;
302     }
303 }
304
305 static gboolean 
306 font_description_value_parse (GtkCssParser *parser,
307                               GFile        *base,
308                               GValue       *value)
309 {
310   PangoFontDescription *font_desc;
311   guint mask;
312   char *str;
313
314   str = _gtk_css_parser_read_value (parser);
315   if (str == NULL)
316     return FALSE;
317
318   font_desc = pango_font_description_from_string (str);
319   mask = pango_font_description_get_set_fields (font_desc);
320   /* These values are not really correct,
321    * but the fields must be set, so we set them to something */
322   if ((mask & PANGO_FONT_MASK_FAMILY) == 0)
323     pango_font_description_set_family_static (font_desc, "Sans");
324   if ((mask & PANGO_FONT_MASK_SIZE) == 0)
325     pango_font_description_set_size (font_desc, 10 * PANGO_SCALE);
326   g_free (str);
327   g_value_take_boxed (value, font_desc);
328   return TRUE;
329 }
330
331 static void
332 font_description_value_print (const GValue *value,
333                               GString      *string)
334 {
335   const PangoFontDescription *desc = g_value_get_boxed (value);
336
337   if (desc == NULL)
338     g_string_append (string, "none");
339   else
340     {
341       char *s = pango_font_description_to_string (desc);
342       g_string_append (string, s);
343       g_free (s);
344     }
345 }
346
347 static gboolean 
348 boolean_value_parse (GtkCssParser *parser,
349                      GFile        *base,
350                      GValue       *value)
351 {
352   if (_gtk_css_parser_try (parser, "true", TRUE) ||
353       _gtk_css_parser_try (parser, "1", TRUE))
354     {
355       g_value_set_boolean (value, TRUE);
356       return TRUE;
357     }
358   else if (_gtk_css_parser_try (parser, "false", TRUE) ||
359            _gtk_css_parser_try (parser, "0", TRUE))
360     {
361       g_value_set_boolean (value, FALSE);
362       return TRUE;
363     }
364   else
365     {
366       _gtk_css_parser_error (parser, "Expected a boolean value");
367       return FALSE;
368     }
369 }
370
371 static void
372 boolean_value_print (const GValue *value,
373                      GString      *string)
374 {
375   if (g_value_get_boolean (value))
376     g_string_append (string, "true");
377   else
378     g_string_append (string, "false");
379 }
380
381 static gboolean 
382 int_value_parse (GtkCssParser *parser,
383                  GFile        *base,
384                  GValue       *value)
385 {
386   gint i;
387
388   if (_gtk_css_parser_begins_with (parser, '-'))
389     {
390       int res = _gtk_win32_theme_int_parse (parser, base, &i);
391       if (res >= 0)
392         {
393           g_value_set_int (value, i);
394           return res > 0;
395         }
396       /* < 0 => continue */
397     }
398
399   if (!_gtk_css_parser_try_int (parser, &i))
400     {
401       _gtk_css_parser_error (parser, "Expected a valid integer value");
402       return FALSE;
403     }
404
405   g_value_set_int (value, i);
406   return TRUE;
407 }
408
409 static void
410 int_value_print (const GValue *value,
411                  GString      *string)
412 {
413   g_string_append_printf (string, "%d", g_value_get_int (value));
414 }
415
416 static gboolean 
417 uint_value_parse (GtkCssParser *parser,
418                   GFile        *base,
419                   GValue       *value)
420 {
421   guint u;
422
423   if (!_gtk_css_parser_try_uint (parser, &u))
424     {
425       _gtk_css_parser_error (parser, "Expected a valid unsigned value");
426       return FALSE;
427     }
428
429   g_value_set_uint (value, u);
430   return TRUE;
431 }
432
433 static void
434 uint_value_print (const GValue *value,
435                   GString      *string)
436 {
437   g_string_append_printf (string, "%u", g_value_get_uint (value));
438 }
439
440 static gboolean 
441 double_value_parse (GtkCssParser *parser,
442                     GFile        *base,
443                     GValue       *value)
444 {
445   gdouble d;
446
447   if (!_gtk_css_parser_try_double (parser, &d))
448     {
449       _gtk_css_parser_error (parser, "Expected a number");
450       return FALSE;
451     }
452
453   g_value_set_double (value, d);
454   return TRUE;
455 }
456
457 static void
458 double_value_print (const GValue *value,
459                     GString      *string)
460 {
461   string_append_double (string, g_value_get_double (value));
462 }
463
464 static gboolean 
465 float_value_parse (GtkCssParser *parser,
466                    GFile        *base,
467                    GValue       *value)
468 {
469   gdouble d;
470
471   if (!_gtk_css_parser_try_double (parser, &d))
472     {
473       _gtk_css_parser_error (parser, "Expected a number");
474       return FALSE;
475     }
476
477   g_value_set_float (value, d);
478   return TRUE;
479 }
480
481 static void
482 float_value_print (const GValue *value,
483                    GString      *string)
484 {
485   string_append_double (string, g_value_get_float (value));
486 }
487
488 static gboolean 
489 string_value_parse (GtkCssParser *parser,
490                     GFile        *base,
491                     GValue       *value)
492 {
493   char *str = _gtk_css_parser_read_string (parser);
494
495   if (str == NULL)
496     return FALSE;
497
498   g_value_take_string (value, str);
499   return TRUE;
500 }
501
502 static void
503 string_value_print (const GValue *value,
504                     GString      *str)
505 {
506   string_append_string (str, g_value_get_string (value));
507 }
508
509 static gboolean 
510 theming_engine_value_parse (GtkCssParser *parser,
511                             GFile        *base,
512                             GValue       *value)
513 {
514   GtkThemingEngine *engine;
515   char *str;
516
517   str = _gtk_css_parser_try_ident (parser, TRUE);
518   if (str == NULL)
519     {
520       _gtk_css_parser_error (parser, "Expected a valid theme name");
521       return FALSE;
522     }
523
524   engine = gtk_theming_engine_load (str);
525   if (engine == NULL)
526     {
527       _gtk_css_parser_error (parser, "Themeing engine '%s' not found", str);
528       g_free (str);
529       return FALSE;
530     }
531
532   g_value_set_object (value, engine);
533   g_free (str);
534   return TRUE;
535 }
536
537 static void
538 theming_engine_value_print (const GValue *value,
539                             GString      *string)
540 {
541   GtkThemingEngine *engine;
542   char *name;
543
544   engine = g_value_get_object (value);
545   if (engine == NULL)
546     g_string_append (string, "none");
547   else
548     {
549       /* XXX: gtk_theming_engine_get_name()? */
550       g_object_get (engine, "name", &name, NULL);
551       g_string_append (string, name ? name : "none");
552       g_free (name);
553     }
554 }
555
556 static gboolean 
557 animation_description_value_parse (GtkCssParser *parser,
558                                    GFile        *base,
559                                    GValue       *value)
560 {
561   GtkAnimationDescription *desc;
562   char *str;
563
564   str = _gtk_css_parser_read_value (parser);
565   if (str == NULL)
566     return FALSE;
567
568   desc = _gtk_animation_description_from_string (str);
569   g_free (str);
570
571   if (desc == NULL)
572     {
573       _gtk_css_parser_error (parser, "Invalid animation description");
574       return FALSE;
575     }
576   
577   g_value_take_boxed (value, desc);
578   return TRUE;
579 }
580
581 static void
582 animation_description_value_print (const GValue *value,
583                                    GString      *string)
584 {
585   GtkAnimationDescription *desc = g_value_get_boxed (value);
586
587   if (desc == NULL)
588     g_string_append (string, "none");
589   else
590     _gtk_animation_description_print (desc, string);
591 }
592
593 static gboolean 
594 border_value_parse (GtkCssParser *parser,
595                     GFile        *base,
596                     GValue       *value)
597 {
598   GtkBorder border = { 0, };
599   guint i, numbers[4];
600
601   for (i = 0; i < G_N_ELEMENTS (numbers); i++)
602     {
603       if (_gtk_css_parser_begins_with (parser, '-'))
604         {
605           /* These are strictly speaking signed, but we want to be able to use them
606              for unsigned types too, as the actual ranges of values make this safe */
607           int res = _gtk_win32_theme_int_parse (parser, base, (int *)&numbers[i]);
608
609           if (res == 0) /* Parse error, report */
610             return FALSE;
611
612           if (res < 0) /* Nothing known to expand */
613             break;
614         }
615       else
616         {
617           if (!_gtk_css_parser_try_uint (parser, &numbers[i]))
618             break;
619
620           /* XXX: shouldn't allow spaces here? */
621           _gtk_css_parser_try (parser, "px", TRUE);
622         }
623     }
624
625   if (i == 0)
626     {
627       _gtk_css_parser_error (parser, "Expected valid border");
628       return FALSE;
629     }
630
631   border.top = numbers[0];
632   if (i > 1)
633     border.right = numbers[1];
634   else
635     border.right = border.top;
636   if (i > 2)
637     border.bottom = numbers[2];
638   else
639     border.bottom = border.top;
640   if (i > 3)
641     border.left = numbers[3];
642   else
643     border.left = border.right;
644
645   g_value_set_boxed (value, &border);
646   return TRUE;
647 }
648
649 static void
650 border_value_print (const GValue *value, GString *string)
651 {
652   const GtkBorder *border = g_value_get_boxed (value);
653
654   if (border == NULL)
655     g_string_append (string, "none");
656   else if (border->left != border->right)
657     g_string_append_printf (string, "%d %d %d %d", border->top, border->right, border->bottom, border->left);
658   else if (border->top != border->bottom)
659     g_string_append_printf (string, "%d %d %d", border->top, border->right, border->bottom);
660   else if (border->top != border->left)
661     g_string_append_printf (string, "%d %d", border->top, border->right);
662   else
663     g_string_append_printf (string, "%d", border->top);
664 }
665
666 static gboolean 
667 gradient_value_parse (GtkCssParser *parser,
668                       GFile        *base,
669                       GValue       *value)
670 {
671   GtkGradient *gradient;
672   cairo_pattern_type_t type;
673   gdouble coords[6];
674   guint i;
675
676   if (!_gtk_css_parser_try (parser, "-gtk-gradient", TRUE))
677     {
678       _gtk_css_parser_error (parser,
679                              "Expected '-gtk-gradient'");
680       return FALSE;
681     }
682
683   if (!_gtk_css_parser_try (parser, "(", TRUE))
684     {
685       _gtk_css_parser_error (parser,
686                              "Expected '(' after '-gtk-gradient'");
687       return FALSE;
688     }
689
690   /* Parse gradient type */
691   if (_gtk_css_parser_try (parser, "linear", TRUE))
692     type = CAIRO_PATTERN_TYPE_LINEAR;
693   else if (_gtk_css_parser_try (parser, "radial", TRUE))
694     type = CAIRO_PATTERN_TYPE_RADIAL;
695   else
696     {
697       _gtk_css_parser_error (parser,
698                              "Gradient type must be 'radial' or 'linear'");
699       return FALSE;
700     }
701
702   /* Parse start/stop position parameters */
703   for (i = 0; i < 2; i++)
704     {
705       if (! _gtk_css_parser_try (parser, ",", TRUE))
706         {
707           _gtk_css_parser_error (parser,
708                                  "Expected ','");
709           return FALSE;
710         }
711
712       if (_gtk_css_parser_try (parser, "left", TRUE))
713         coords[i * 3] = 0;
714       else if (_gtk_css_parser_try (parser, "right", TRUE))
715         coords[i * 3] = 1;
716       else if (_gtk_css_parser_try (parser, "center", TRUE))
717         coords[i * 3] = 0.5;
718       else if (!_gtk_css_parser_try_double (parser, &coords[i * 3]))
719         {
720           _gtk_css_parser_error (parser,
721                                  "Expected a valid X coordinate");
722           return FALSE;
723         }
724
725       if (_gtk_css_parser_try (parser, "top", TRUE))
726         coords[i * 3 + 1] = 0;
727       else if (_gtk_css_parser_try (parser, "bottom", TRUE))
728         coords[i * 3 + 1] = 1;
729       else if (_gtk_css_parser_try (parser, "center", TRUE))
730         coords[i * 3 + 1] = 0.5;
731       else if (!_gtk_css_parser_try_double (parser, &coords[i * 3 + 1]))
732         {
733           _gtk_css_parser_error (parser,
734                                  "Expected a valid Y coordinate");
735           return FALSE;
736         }
737
738       if (type == CAIRO_PATTERN_TYPE_RADIAL)
739         {
740           /* Parse radius */
741           if (! _gtk_css_parser_try (parser, ",", TRUE))
742             {
743               _gtk_css_parser_error (parser,
744                                      "Expected ','");
745               return FALSE;
746             }
747
748           if (! _gtk_css_parser_try_double (parser, &coords[(i * 3) + 2]))
749             {
750               _gtk_css_parser_error (parser,
751                                      "Expected a numer for the radius");
752               return FALSE;
753             }
754         }
755     }
756
757   if (type == CAIRO_PATTERN_TYPE_LINEAR)
758     gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[3], coords[4]);
759   else
760     gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2],
761                                         coords[3], coords[4], coords[5]);
762
763   while (_gtk_css_parser_try (parser, ",", TRUE))
764     {
765       GtkSymbolicColor *color;
766       gdouble position;
767
768       if (_gtk_css_parser_try (parser, "from", TRUE))
769         {
770           position = 0;
771
772           if (!_gtk_css_parser_try (parser, "(", TRUE))
773             {
774               gtk_gradient_unref (gradient);
775               _gtk_css_parser_error (parser,
776                                      "Expected '('");
777               return FALSE;
778             }
779
780         }
781       else if (_gtk_css_parser_try (parser, "to", TRUE))
782         {
783           position = 1;
784
785           if (!_gtk_css_parser_try (parser, "(", TRUE))
786             {
787               gtk_gradient_unref (gradient);
788               _gtk_css_parser_error (parser,
789                                      "Expected '('");
790               return FALSE;
791             }
792
793         }
794       else if (_gtk_css_parser_try (parser, "color-stop", TRUE))
795         {
796           if (!_gtk_css_parser_try (parser, "(", TRUE))
797             {
798               gtk_gradient_unref (gradient);
799               _gtk_css_parser_error (parser,
800                                      "Expected '('");
801               return FALSE;
802             }
803
804           if (!_gtk_css_parser_try_double (parser, &position))
805             {
806               gtk_gradient_unref (gradient);
807               _gtk_css_parser_error (parser,
808                                      "Expected a valid number");
809               return FALSE;
810             }
811
812           if (!_gtk_css_parser_try (parser, ",", TRUE))
813             {
814               gtk_gradient_unref (gradient);
815               _gtk_css_parser_error (parser,
816                                      "Expected a comma");
817               return FALSE;
818             }
819         }
820       else
821         {
822           gtk_gradient_unref (gradient);
823           _gtk_css_parser_error (parser,
824                                  "Not a valid color-stop definition");
825           return FALSE;
826         }
827
828       color = _gtk_css_parser_read_symbolic_color (parser);
829       if (color == NULL)
830         {
831           gtk_gradient_unref (gradient);
832           return FALSE;
833         }
834
835       gtk_gradient_add_color_stop (gradient, position, color);
836       gtk_symbolic_color_unref (color);
837
838       if (!_gtk_css_parser_try (parser, ")", TRUE))
839         {
840           gtk_gradient_unref (gradient);
841           _gtk_css_parser_error (parser,
842                                  "Expected ')'");
843           return FALSE;
844         }
845     }
846
847   if (!_gtk_css_parser_try (parser, ")", TRUE))
848     {
849       gtk_gradient_unref (gradient);
850       _gtk_css_parser_error (parser,
851                              "Expected ')'");
852       return FALSE;
853     }
854
855   g_value_take_boxed (value, gradient);
856   return TRUE;
857 }
858
859 static void
860 gradient_value_print (const GValue *value,
861                       GString      *string)
862 {
863   GtkGradient *gradient = g_value_get_boxed (value);
864
865   if (gradient == NULL)
866     g_string_append (string, "none");
867   else
868     {
869       char *s = gtk_gradient_to_string (gradient);
870       g_string_append (string, s);
871       g_free (s);
872     }
873 }
874
875 static GFile *
876 gtk_css_parse_url (GtkCssParser *parser,
877                    GFile        *base)
878 {
879   gchar *path;
880   GFile *file;
881
882   if (_gtk_css_parser_try (parser, "url", FALSE))
883     {
884       if (!_gtk_css_parser_try (parser, "(", TRUE))
885         {
886           _gtk_css_parser_skip_whitespace (parser);
887           if (_gtk_css_parser_try (parser, "(", TRUE))
888             {
889               GError *error;
890               
891               error = g_error_new_literal (GTK_CSS_PROVIDER_ERROR,
892                                            GTK_CSS_PROVIDER_ERROR_DEPRECATED,
893                                            "Whitespace between 'url' and '(' is deprecated");
894                              
895               _gtk_css_parser_take_error (parser, error);
896             }
897           else
898             {
899               _gtk_css_parser_error (parser, "Expected '(' after 'url'");
900               return NULL;
901             }
902         }
903
904       path = _gtk_css_parser_read_string (parser);
905       if (path == NULL)
906         return NULL;
907
908       if (!_gtk_css_parser_try (parser, ")", TRUE))
909         {
910           _gtk_css_parser_error (parser, "No closing ')' found for 'url'");
911           g_free (path);
912           return NULL;
913         }
914     }
915   else
916     {
917       path = _gtk_css_parser_try_name (parser, TRUE);
918       if (path == NULL)
919         {
920           _gtk_css_parser_error (parser, "Not a valid url");
921           return NULL;
922         }
923     }
924
925   file = g_file_resolve_relative_path (base, path);
926   g_free (path);
927
928   return file;
929 }
930
931 static gboolean 
932 pattern_value_parse (GtkCssParser *parser,
933                      GFile        *base,
934                      GValue       *value)
935 {
936   if (_gtk_css_parser_begins_with (parser, '-'))
937     {
938       int res;
939       res = _gtk_win32_theme_part_parse (parser, base, value);
940       if (res >= 0)
941         return res > 0;
942       /* < 0 => continue */
943       g_value_unset (value);
944       g_value_init (value, GTK_TYPE_GRADIENT);
945       return gradient_value_parse (parser, base, value);
946     }
947   else
948     {
949       GError *error = NULL;
950       gchar *path;
951       GdkPixbuf *pixbuf;
952       GFile *file;
953       cairo_surface_t *surface;
954       cairo_pattern_t *pattern;
955       cairo_t *cr;
956       cairo_matrix_t matrix;
957
958       file = gtk_css_parse_url (parser, base);
959       if (file == NULL)
960         return FALSE;
961
962       path = g_file_get_path (file);
963       g_object_unref (file);
964
965       pixbuf = gdk_pixbuf_new_from_file (path, &error);
966       g_free (path);
967       if (pixbuf == NULL)
968         {
969           _gtk_css_parser_take_error (parser, error);
970           return FALSE;
971         }
972
973       surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
974                                             gdk_pixbuf_get_width (pixbuf),
975                                             gdk_pixbuf_get_height (pixbuf));
976       cr = cairo_create (surface);
977       gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
978       cairo_paint (cr);
979       pattern = cairo_pattern_create_for_surface (surface);
980
981       cairo_matrix_init_scale (&matrix,
982                                gdk_pixbuf_get_width (pixbuf),
983                                gdk_pixbuf_get_height (pixbuf));
984       cairo_pattern_set_matrix (pattern, &matrix);
985
986       cairo_surface_destroy (surface);
987       cairo_destroy (cr);
988       g_object_unref (pixbuf);
989
990       g_value_take_boxed (value, pattern);
991     }
992   
993   return TRUE;
994 }
995
996 static cairo_status_t
997 surface_write (void                *closure,
998                const unsigned char *data,
999                unsigned int         length)
1000 {
1001   g_byte_array_append (closure, data, length);
1002
1003   return CAIRO_STATUS_SUCCESS;
1004 }
1005
1006 static void
1007 surface_print (cairo_surface_t *surface,
1008                GString *        string)
1009 {
1010 #if CAIRO_HAS_PNG_FUNCTIONS
1011   GByteArray *array;
1012   char *base64;
1013   
1014   array = g_byte_array_new ();
1015   cairo_surface_write_to_png_stream (surface, surface_write, array);
1016   base64 = g_base64_encode (array->data, array->len);
1017   g_byte_array_free (array, TRUE);
1018
1019   g_string_append (string, "url(\"data:image/png;base64,");
1020   g_string_append (string, base64);
1021   g_string_append (string, "\")");
1022
1023   g_free (base64);
1024 #else
1025   g_string_append (string, "none /* you need cairo png functions enabled to make this work */");
1026 #endif
1027 }
1028
1029 static void
1030 pattern_value_print (const GValue *value,
1031                      GString      *string)
1032 {
1033   cairo_pattern_t *pattern;
1034   cairo_surface_t *surface;
1035
1036   pattern = g_value_get_boxed (value);
1037
1038   if (pattern == NULL)
1039     {
1040       g_string_append (string, "none");
1041       return;
1042     }
1043
1044   switch (cairo_pattern_get_type (pattern))
1045     {
1046     case CAIRO_PATTERN_TYPE_SURFACE:
1047       if (cairo_pattern_get_surface (pattern, &surface) != CAIRO_STATUS_SUCCESS)
1048         {
1049           g_assert_not_reached ();
1050         }
1051       surface_print (surface, string);
1052       break;
1053     case CAIRO_PATTERN_TYPE_SOLID:
1054     case CAIRO_PATTERN_TYPE_LINEAR:
1055     case CAIRO_PATTERN_TYPE_RADIAL:
1056     default:
1057       g_assert_not_reached ();
1058       break;
1059     }
1060 }
1061
1062 static gboolean
1063 shadow_value_parse (GtkCssParser *parser,
1064                     GFile *base,
1065                     GValue *value)
1066 {
1067   gboolean have_inset, have_color, have_lengths;
1068   gdouble hoffset, voffset, blur, spread;
1069   GtkSymbolicColor *color;
1070   GtkShadow *shadow;
1071   guint i;
1072
1073   shadow = _gtk_shadow_new ();
1074
1075   do
1076     {
1077       have_inset = have_lengths = have_color = FALSE;
1078
1079       for (i = 0; i < 3; i++)
1080         {
1081           if (!have_inset && 
1082               _gtk_css_parser_try (parser, "inset", TRUE))
1083             {
1084               have_inset = TRUE;
1085               continue;
1086             }
1087             
1088           if (!have_lengths &&
1089               _gtk_css_parser_try_double (parser, &hoffset))
1090             {
1091               have_lengths = TRUE;
1092
1093               if (!_gtk_css_parser_try_double (parser, &voffset))
1094                 {
1095                   _gtk_css_parser_error (parser, "Horizontal and vertical offsets are required");
1096                   _gtk_shadow_unref (shadow);
1097                   return FALSE;
1098                 }
1099
1100               if (!_gtk_css_parser_try_double (parser, &blur))
1101                 blur = 0;
1102
1103               if (!_gtk_css_parser_try_double (parser, &spread))
1104                 spread = 0;
1105
1106               continue;
1107             }
1108
1109           if (!have_color)
1110             {
1111               have_color = TRUE;
1112
1113               /* XXX: the color is optional and UA-defined if it's missing,
1114                * but it doesn't really make sense for us...
1115                */
1116               color = _gtk_css_parser_read_symbolic_color (parser);
1117
1118               if (color == NULL)
1119                 {
1120                   _gtk_shadow_unref (shadow);
1121                   return FALSE;
1122                 }
1123             }
1124         }
1125
1126       if (!have_color || !have_lengths)
1127         {
1128           _gtk_css_parser_error (parser, "Must specify at least color and offsets");
1129           _gtk_shadow_unref (shadow);
1130           return FALSE;
1131         }
1132
1133       _gtk_shadow_append (shadow,
1134                           hoffset, voffset,
1135                           blur, spread,
1136                           have_inset, color);
1137
1138       gtk_symbolic_color_unref (color);
1139
1140     }
1141   while (_gtk_css_parser_try (parser, ",", TRUE));
1142
1143   g_value_take_boxed (value, shadow);
1144   return TRUE;
1145 }
1146
1147 static void
1148 shadow_value_print (const GValue *value,
1149                     GString      *string)
1150 {
1151   GtkShadow *shadow;
1152
1153   shadow = g_value_get_boxed (value);
1154
1155   if (shadow == NULL)
1156     g_string_append (string, "none");
1157   else
1158     _gtk_shadow_print (shadow, string);
1159 }
1160
1161 static gboolean
1162 background_repeat_value_parse (GtkCssParser *parser,
1163                                GFile *file,
1164                                GValue *value)
1165 {
1166   GtkCssBackgroundRepeat repeat;
1167   GtkCssBackgroundRepeatStyle style;
1168
1169   if (_gtk_css_parser_try (parser, "repeat", TRUE))
1170     style = GTK_CSS_BACKGROUND_REPEAT_STYLE_REPEAT;
1171   else if (_gtk_css_parser_try (parser, "no-repeat", TRUE))
1172     style = GTK_CSS_BACKGROUND_REPEAT_STYLE_NO_REPEAT;
1173   else
1174     style = GTK_CSS_BACKGROUND_REPEAT_STYLE_NONE;
1175
1176   repeat.repeat = style;
1177
1178   g_value_set_boxed (value, &repeat);
1179
1180   return TRUE;
1181 }
1182
1183 static const gchar *
1184 background_repeat_style_to_string (GtkCssBackgroundRepeatStyle repeat)
1185 {
1186   switch (repeat)
1187     {
1188     case GTK_CSS_BACKGROUND_REPEAT_STYLE_REPEAT:
1189       return "repeat";
1190     case GTK_CSS_BACKGROUND_REPEAT_STYLE_NO_REPEAT:
1191       return "no-repeat";
1192     default:
1193       return NULL;
1194     }
1195 }
1196
1197 static void
1198 background_repeat_value_print (const GValue *value,
1199                                GString      *string)
1200 {
1201   GtkCssBackgroundRepeat *repeat;
1202
1203   repeat = g_value_get_boxed (value);
1204
1205   g_string_append (string, background_repeat_style_to_string (repeat->repeat));
1206 }
1207
1208 static gboolean
1209 border_image_repeat_value_parse (GtkCssParser *parser,
1210                                  GFile *file,
1211                                  GValue *value)
1212 {
1213   GtkCssBorderImageRepeat image_repeat;
1214   GtkCssBorderRepeatStyle styles[2];
1215   gint i;
1216
1217   for (i = 0; i < 2; i++)
1218     {
1219       if (_gtk_css_parser_try (parser, "stretch", TRUE))
1220         styles[i] = GTK_CSS_REPEAT_STYLE_NONE;
1221       else if (_gtk_css_parser_try (parser, "repeat", TRUE))
1222         styles[i] = GTK_CSS_REPEAT_STYLE_REPEAT;
1223       else if (_gtk_css_parser_try (parser, "round", TRUE))
1224         styles[i] = GTK_CSS_REPEAT_STYLE_ROUND;
1225       else if (_gtk_css_parser_try (parser, "space", TRUE))
1226         styles[i] = GTK_CSS_REPEAT_STYLE_SPACE;
1227       else if (i == 0)
1228         {
1229           styles[1] = styles[0] = GTK_CSS_REPEAT_STYLE_NONE;
1230           break;
1231         }
1232       else
1233         styles[i] = styles[0];
1234     }
1235
1236   image_repeat.hrepeat = styles[0];
1237   image_repeat.vrepeat = styles[1];
1238
1239   g_value_set_boxed (value, &image_repeat);
1240
1241   return TRUE;
1242 }
1243
1244 static const gchar *
1245 border_image_repeat_style_to_string (GtkCssBorderRepeatStyle repeat)
1246 {
1247   switch (repeat)
1248     {
1249     case GTK_CSS_REPEAT_STYLE_NONE:
1250       return "stretch";
1251     case GTK_CSS_REPEAT_STYLE_REPEAT:
1252       return "repeat";
1253     case GTK_CSS_REPEAT_STYLE_ROUND:
1254       return "round";
1255     case GTK_CSS_REPEAT_STYLE_SPACE:
1256       return "space";
1257     default:
1258       return NULL;
1259     }
1260 }
1261
1262 static void
1263 border_image_repeat_value_print (const GValue *value,
1264                                  GString      *string)
1265 {
1266   GtkCssBorderImageRepeat *image_repeat;
1267
1268   image_repeat = g_value_get_boxed (value);
1269
1270   g_string_append (string, border_image_repeat_style_to_string (image_repeat->hrepeat));
1271   if (image_repeat->hrepeat != image_repeat->vrepeat)
1272     {
1273       g_string_append (string, " ");
1274       g_string_append (string, border_image_repeat_style_to_string (image_repeat->vrepeat));
1275     }
1276 }
1277
1278 static gboolean
1279 border_image_value_parse (GtkCssParser *parser,
1280                           GFile *base,
1281                           GValue *value)
1282 {
1283   GValue temp = G_VALUE_INIT;
1284   cairo_pattern_t *pattern = NULL;
1285   gconstpointer *boxed = NULL;
1286   GType boxed_type;
1287   GtkBorder slice, *width = NULL, *parsed_slice;
1288   GtkCssBorderImageRepeat repeat, *parsed_repeat;
1289   gboolean retval = FALSE;
1290   GtkBorderImage *image = NULL;
1291
1292   g_value_init (&temp, CAIRO_GOBJECT_TYPE_PATTERN);
1293
1294   if (!pattern_value_parse (parser, base, &temp))
1295     return FALSE;
1296
1297   boxed_type = G_VALUE_TYPE (&temp);
1298   if (boxed_type != CAIRO_GOBJECT_TYPE_PATTERN)
1299     boxed = g_value_dup_boxed (&temp);
1300   else
1301     pattern = g_value_dup_boxed (&temp);
1302
1303   g_value_unset (&temp);
1304   g_value_init (&temp, GTK_TYPE_BORDER);
1305
1306   if (!border_value_parse (parser, base, &temp))
1307     goto out;
1308
1309   parsed_slice = g_value_get_boxed (&temp);
1310   slice = *parsed_slice;
1311
1312   if (_gtk_css_parser_try (parser, "/", TRUE))
1313     {
1314       g_value_unset (&temp);
1315       g_value_init (&temp, GTK_TYPE_BORDER);
1316
1317       if (!border_value_parse (parser, base, &temp))
1318         goto out;
1319
1320       width = g_value_dup_boxed (&temp);
1321     }
1322
1323   g_value_unset (&temp);
1324   g_value_init (&temp, GTK_TYPE_CSS_BORDER_IMAGE_REPEAT);
1325
1326   if (!border_image_repeat_value_parse (parser, base, &temp))
1327     goto out;
1328
1329   parsed_repeat = g_value_get_boxed (&temp);
1330   repeat = *parsed_repeat;
1331
1332   g_value_unset (&temp);
1333
1334   if (boxed != NULL)
1335     image = _gtk_border_image_new_for_boxed (boxed_type, boxed, &slice, width, &repeat);
1336   else if (pattern != NULL)
1337     image = _gtk_border_image_new (pattern, &slice, width, &repeat);
1338
1339   if (image != NULL)
1340     {
1341       retval = TRUE;
1342       g_value_take_boxed (value, image);
1343     }
1344
1345  out:
1346   if (pattern != NULL)
1347     cairo_pattern_destroy (pattern);
1348
1349   if (boxed != NULL)
1350     g_boxed_free (boxed_type, boxed);
1351
1352   if (width != NULL)
1353     gtk_border_free (width);
1354
1355   return retval;
1356 }
1357
1358 static gboolean 
1359 enum_value_parse (GtkCssParser *parser,
1360                   GFile        *base,
1361                   GValue       *value)
1362 {
1363   GEnumClass *enum_class;
1364   GEnumValue *enum_value;
1365   char *str;
1366
1367   str = _gtk_css_parser_try_ident (parser, TRUE);
1368   if (str == NULL)
1369     {
1370       _gtk_css_parser_error (parser, "Expected an identifier");
1371       return FALSE;
1372     }
1373
1374   enum_class = g_type_class_ref (G_VALUE_TYPE (value));
1375   enum_value = g_enum_get_value_by_nick (enum_class, str);
1376
1377   if (enum_value)
1378     g_value_set_enum (value, enum_value->value);
1379   else
1380     _gtk_css_parser_error (parser,
1381                            "Unknown value '%s' for enum type '%s'",
1382                            str, g_type_name (G_VALUE_TYPE (value)));
1383   
1384   g_type_class_unref (enum_class);
1385   g_free (str);
1386
1387   return enum_value != NULL;
1388 }
1389
1390 static void
1391 enum_value_print (const GValue *value,
1392                   GString      *string)
1393 {
1394   GEnumClass *enum_class;
1395   GEnumValue *enum_value;
1396
1397   enum_class = g_type_class_ref (G_VALUE_TYPE (value));
1398   enum_value = g_enum_get_value (enum_class, g_value_get_enum (value));
1399
1400   g_string_append (string, enum_value->value_nick);
1401
1402   g_type_class_unref (enum_class);
1403 }
1404
1405 static gboolean 
1406 flags_value_parse (GtkCssParser *parser,
1407                    GFile        *base,
1408                    GValue       *value)
1409 {
1410   GFlagsClass *flags_class;
1411   GFlagsValue *flag_value;
1412   guint flags = 0;
1413   char *str;
1414
1415   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1416
1417   do {
1418     str = _gtk_css_parser_try_ident (parser, TRUE);
1419     if (str == NULL)
1420       {
1421         _gtk_css_parser_error (parser, "Expected an identifier");
1422         g_type_class_unref (flags_class);
1423         return FALSE;
1424       }
1425
1426       flag_value = g_flags_get_value_by_nick (flags_class, str);
1427       if (!flag_value)
1428         {
1429           _gtk_css_parser_error (parser,
1430                                  "Unknown flag value '%s' for type '%s'",
1431                                  str, g_type_name (G_VALUE_TYPE (value)));
1432           /* XXX Do we want to return FALSE here? We can get
1433            * forward-compatibility for new values this way
1434            */
1435           g_free (str);
1436           g_type_class_unref (flags_class);
1437           return FALSE;
1438         }
1439
1440       g_free (str);
1441     }
1442   while (_gtk_css_parser_try (parser, ",", FALSE));
1443
1444   g_type_class_unref (flags_class);
1445
1446   g_value_set_enum (value, flags);
1447
1448   return TRUE;
1449 }
1450
1451 static void
1452 flags_value_print (const GValue *value,
1453                    GString      *string)
1454 {
1455   GFlagsClass *flags_class;
1456   guint i, flags;
1457
1458   flags_class = g_type_class_ref (G_VALUE_TYPE (value));
1459   flags = g_value_get_flags (value);
1460
1461   for (i = 0; i < flags_class->n_values; i++)
1462     {
1463       GFlagsValue *flags_value = &flags_class->values[i];
1464
1465       if (flags & flags_value->value)
1466         {
1467           if (string->len != 0)
1468             g_string_append (string, ", ");
1469
1470           g_string_append (string, flags_value->value_nick);
1471         }
1472     }
1473
1474   g_type_class_unref (flags_class);
1475 }
1476
1477 static gboolean 
1478 bindings_value_parse (GtkCssParser *parser,
1479                       GFile        *base,
1480                       GValue       *value)
1481 {
1482   GPtrArray *array;
1483   GtkBindingSet *binding_set;
1484   char *name;
1485
1486   array = g_ptr_array_new ();
1487
1488   do {
1489       name = _gtk_css_parser_try_ident (parser, TRUE);
1490       if (name == NULL)
1491         {
1492           _gtk_css_parser_error (parser, "Not a valid binding name");
1493           g_ptr_array_free (array, TRUE);
1494           return FALSE;
1495         }
1496
1497       binding_set = gtk_binding_set_find (name);
1498
1499       if (!binding_set)
1500         {
1501           _gtk_css_parser_error (parser, "No binding set named '%s'", name);
1502           g_free (name);
1503           continue;
1504         }
1505
1506       g_ptr_array_add (array, binding_set);
1507       g_free (name);
1508     }
1509   while (_gtk_css_parser_try (parser, ",", TRUE));
1510
1511   g_value_take_boxed (value, array);
1512
1513   return TRUE;
1514 }
1515
1516 static void
1517 bindings_value_print (const GValue *value,
1518                       GString      *string)
1519 {
1520   GPtrArray *array;
1521   guint i;
1522
1523   array = g_value_get_boxed (value);
1524
1525   for (i = 0; i < array->len; i++)
1526     {
1527       GtkBindingSet *binding_set = g_ptr_array_index (array, i);
1528
1529       if (i > 0)
1530         g_string_append (string, ", ");
1531       g_string_append (string, binding_set->set_name);
1532     }
1533 }
1534
1535 static gboolean 
1536 border_corner_radius_value_parse (GtkCssParser *parser,
1537                                   GFile        *base,
1538                                   GValue       *value)
1539 {
1540   GtkCssBorderCornerRadius corner;
1541
1542   if (!_gtk_css_parser_try_double (parser, &corner.horizontal))
1543     {
1544       _gtk_css_parser_error (parser, "Expected a number");
1545       return FALSE;
1546     }
1547   else if (corner.horizontal < 0)
1548     goto negative;
1549
1550   if (!_gtk_css_parser_try_double (parser, &corner.vertical))
1551     corner.vertical = corner.horizontal;
1552   else if (corner.vertical < 0)
1553     goto negative;
1554
1555   g_value_set_boxed (value, &corner);
1556   return TRUE;
1557
1558 negative:
1559   _gtk_css_parser_error (parser, "Border radius values cannot be negative");
1560   return FALSE;
1561 }
1562
1563 static void
1564 border_corner_radius_value_print (const GValue *value,
1565                                   GString      *string)
1566 {
1567   GtkCssBorderCornerRadius *corner;
1568
1569   corner = g_value_get_boxed (value);
1570
1571   if (corner == NULL)
1572     {
1573       g_string_append (string, "none");
1574       return;
1575     }
1576
1577   string_append_double (string, corner->horizontal);
1578   if (corner->horizontal != corner->vertical)
1579     {
1580       g_string_append_c (string, ' ');
1581       string_append_double (string, corner->vertical);
1582     }
1583 }
1584
1585 static gboolean 
1586 border_radius_value_parse (GtkCssParser *parser,
1587                            GFile        *base,
1588                            GValue       *value)
1589 {
1590   GtkCssBorderRadius border;
1591
1592   if (!_gtk_css_parser_try_double (parser, &border.top_left.horizontal))
1593     {
1594       _gtk_css_parser_error (parser, "Expected a number");
1595       return FALSE;
1596     }
1597   else if (border.top_left.horizontal < 0)
1598     goto negative;
1599
1600   if (_gtk_css_parser_try_double (parser, &border.top_right.horizontal))
1601     {
1602       if (border.top_right.horizontal < 0)
1603         goto negative;
1604       if (_gtk_css_parser_try_double (parser, &border.bottom_right.horizontal))
1605         {
1606           if (border.bottom_right.horizontal < 0)
1607             goto negative;
1608           if (!_gtk_css_parser_try_double (parser, &border.bottom_left.horizontal))
1609             border.bottom_left.horizontal = border.top_right.horizontal;
1610           else if (border.bottom_left.horizontal < 0)
1611             goto negative;
1612         }
1613       else
1614         {
1615           border.bottom_right.horizontal = border.top_left.horizontal;
1616           border.bottom_left.horizontal = border.top_right.horizontal;
1617         }
1618     }
1619   else
1620     {
1621       border.top_right.horizontal = border.top_left.horizontal;
1622       border.bottom_right.horizontal = border.top_left.horizontal;
1623       border.bottom_left.horizontal = border.top_left.horizontal;
1624     }
1625
1626   if (_gtk_css_parser_try (parser, "/", TRUE))
1627     {
1628       if (!_gtk_css_parser_try_double (parser, &border.top_left.vertical))
1629         {
1630           _gtk_css_parser_error (parser, "Expected a number");
1631           return FALSE;
1632         }
1633       else if (border.top_left.vertical < 0)
1634         goto negative;
1635
1636       if (_gtk_css_parser_try_double (parser, &border.top_right.vertical))
1637         {
1638           if (border.top_right.vertical < 0)
1639             goto negative;
1640           if (_gtk_css_parser_try_double (parser, &border.bottom_right.vertical))
1641             {
1642               if (border.bottom_right.vertical < 0)
1643                 goto negative;
1644               if (!_gtk_css_parser_try_double (parser, &border.bottom_left.vertical))
1645                 border.bottom_left.vertical = border.top_right.vertical;
1646               else if (border.bottom_left.vertical < 0)
1647                 goto negative;
1648             }
1649           else
1650             {
1651               border.bottom_right.vertical = border.top_left.vertical;
1652               border.bottom_left.vertical = border.top_right.vertical;
1653             }
1654         }
1655       else
1656         {
1657           border.top_right.vertical = border.top_left.vertical;
1658           border.bottom_right.vertical = border.top_left.vertical;
1659           border.bottom_left.vertical = border.top_left.vertical;
1660         }
1661     }
1662   else
1663     {
1664       border.top_left.vertical = border.top_left.horizontal;
1665       border.top_right.vertical = border.top_right.horizontal;
1666       border.bottom_right.vertical = border.bottom_right.horizontal;
1667       border.bottom_left.vertical = border.bottom_left.horizontal;
1668     }
1669
1670   /* border-radius is an int property for backwards-compat reasons */
1671   g_value_unset (value);
1672   g_value_init (value, GTK_TYPE_CSS_BORDER_RADIUS);
1673   g_value_set_boxed (value, &border);
1674
1675   return TRUE;
1676
1677 negative:
1678   _gtk_css_parser_error (parser, "Border radius values cannot be negative");
1679   return FALSE;
1680 }
1681
1682 static void
1683 border_radius_value_print (const GValue *value,
1684                            GString      *string)
1685 {
1686   GtkCssBorderRadius *border;
1687
1688   border = g_value_get_boxed (value);
1689
1690   if (border == NULL)
1691     {
1692       g_string_append (string, "none");
1693       return;
1694     }
1695
1696   string_append_double (string, border->top_left.horizontal);
1697   if (border->top_left.horizontal != border->top_right.horizontal ||
1698       border->top_left.horizontal != border->bottom_right.horizontal ||
1699       border->top_left.horizontal != border->bottom_left.horizontal)
1700     {
1701       g_string_append_c (string, ' ');
1702       string_append_double (string, border->top_right.horizontal);
1703       if (border->top_left.horizontal != border->bottom_right.horizontal ||
1704           border->top_right.horizontal != border->bottom_left.horizontal)
1705         {
1706           g_string_append_c (string, ' ');
1707           string_append_double (string, border->bottom_right.horizontal);
1708           if (border->top_right.horizontal != border->bottom_left.horizontal)
1709             {
1710               g_string_append_c (string, ' ');
1711               string_append_double (string, border->bottom_left.horizontal);
1712             }
1713         }
1714     }
1715
1716   if (border->top_left.horizontal != border->top_left.vertical ||
1717       border->top_right.horizontal != border->top_right.vertical ||
1718       border->bottom_right.horizontal != border->bottom_right.vertical ||
1719       border->bottom_left.horizontal != border->bottom_left.vertical)
1720     {
1721       g_string_append (string, " / ");
1722       string_append_double (string, border->top_left.vertical);
1723       if (border->top_left.vertical != border->top_right.vertical ||
1724           border->top_left.vertical != border->bottom_right.vertical ||
1725           border->top_left.vertical != border->bottom_left.vertical)
1726         {
1727           g_string_append_c (string, ' ');
1728           string_append_double (string, border->top_right.vertical);
1729           if (border->top_left.vertical != border->bottom_right.vertical ||
1730               border->top_right.vertical != border->bottom_left.vertical)
1731             {
1732               g_string_append_c (string, ' ');
1733               string_append_double (string, border->bottom_right.vertical);
1734               if (border->top_right.vertical != border->bottom_left.vertical)
1735                 {
1736                   g_string_append_c (string, ' ');
1737                   string_append_double (string, border->bottom_left.vertical);
1738                 }
1739             }
1740         }
1741
1742     }
1743 }
1744
1745 static gboolean 
1746 transparent_color_value_parse (GtkCssParser *parser,
1747                                GFile        *base,
1748                                GValue       *value)
1749 {
1750   if (_gtk_css_parser_try (parser, "transparent", TRUE))
1751     {
1752       GdkRGBA transparent = { 0, 0, 0, 0 };
1753           
1754       g_value_set_boxed (value, &transparent);
1755
1756       return TRUE;
1757     }
1758
1759   return rgba_value_parse (parser, base, value);
1760 }
1761
1762 static gboolean 
1763 border_color_shorthand_value_parse (GtkCssParser *parser,
1764                                     GFile        *base,
1765                                     GValue       *value)
1766 {
1767   GtkSymbolicColor *symbolic;
1768   GPtrArray *array;
1769
1770   array = g_ptr_array_new_with_free_func ((GDestroyNotify) gtk_symbolic_color_unref);
1771
1772   do
1773     {
1774       if (_gtk_css_parser_try (parser, "transparent", TRUE))
1775         {
1776           GdkRGBA transparent = { 0, 0, 0, 0 };
1777           
1778           symbolic = gtk_symbolic_color_new_literal (&transparent);
1779         }
1780       else
1781         {
1782           symbolic = _gtk_css_parser_read_symbolic_color (parser);
1783       
1784           if (symbolic == NULL)
1785             return FALSE;
1786         }
1787       
1788       g_ptr_array_add (array, symbolic);
1789     }
1790   while (array->len < 4 && 
1791          !_gtk_css_parser_is_eof (parser) &&
1792          !_gtk_css_parser_begins_with (parser, ';') &&
1793          !_gtk_css_parser_begins_with (parser, '}'));
1794
1795   switch (array->len)
1796     {
1797       default:
1798         g_assert_not_reached ();
1799         break;
1800       case 1:
1801         g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 0)));
1802         /* fall through */
1803       case 2:
1804         g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 0)));
1805         /* fall through */
1806       case 3:
1807         g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 1)));
1808         /* fall through */
1809       case 4:
1810         break;
1811     }
1812
1813   g_value_unset (value);
1814   g_value_init (value, G_TYPE_PTR_ARRAY);
1815   g_value_take_boxed (value, array);
1816
1817   return TRUE;
1818 }
1819
1820 /*** PACKING ***/
1821
1822 static GParameter *
1823 unpack_border (const GValue *value,
1824                guint        *n_params,
1825                const char   *top,
1826                const char   *left,
1827                const char   *bottom,
1828                const char   *right)
1829 {
1830   GParameter *parameter = g_new0 (GParameter, 4);
1831   GtkBorder *border = g_value_get_boxed (value);
1832
1833   parameter[0].name = top;
1834   g_value_init (&parameter[0].value, G_TYPE_INT);
1835   g_value_set_int (&parameter[0].value, border->top);
1836   parameter[1].name = left;
1837   g_value_init (&parameter[1].value, G_TYPE_INT);
1838   g_value_set_int (&parameter[1].value, border->left);
1839   parameter[2].name = bottom;
1840   g_value_init (&parameter[2].value, G_TYPE_INT);
1841   g_value_set_int (&parameter[2].value, border->bottom);
1842   parameter[3].name = right;
1843   g_value_init (&parameter[3].value, G_TYPE_INT);
1844   g_value_set_int (&parameter[3].value, border->right);
1845
1846   *n_params = 4;
1847   return parameter;
1848 }
1849
1850 static void
1851 pack_border (GValue             *value,
1852              GtkStyleProperties *props,
1853              GtkStateFlags       state,
1854              const char         *top,
1855              const char         *left,
1856              const char         *bottom,
1857              const char         *right)
1858 {
1859   GtkBorder border;
1860   int t, l, b, r;
1861
1862   gtk_style_properties_get (props,
1863                             state,
1864                             top, &t,
1865                             left, &l,
1866                             bottom, &b,
1867                             right, &r,
1868                             NULL);
1869
1870   border.top = t;
1871   border.left = l;
1872   border.bottom = b;
1873   border.right = r;
1874
1875   g_value_set_boxed (value, &border);
1876 }
1877
1878 static GParameter *
1879 unpack_border_width (const GValue *value,
1880                      guint        *n_params)
1881 {
1882   return unpack_border (value, n_params,
1883                         "border-top-width", "border-left-width",
1884                         "border-bottom-width", "border-right-width");
1885 }
1886
1887 static void
1888 pack_border_width (GValue             *value,
1889                    GtkStyleProperties *props,
1890                    GtkStateFlags       state,
1891                    GtkStylePropertyContext *context)
1892 {
1893   pack_border (value, props, state,
1894                "border-top-width", "border-left-width",
1895                "border-bottom-width", "border-right-width");
1896 }
1897
1898 static GParameter *
1899 unpack_padding (const GValue *value,
1900                 guint        *n_params)
1901 {
1902   return unpack_border (value, n_params,
1903                         "padding-top", "padding-left",
1904                         "padding-bottom", "padding-right");
1905 }
1906
1907 static void
1908 pack_padding (GValue             *value,
1909               GtkStyleProperties *props,
1910               GtkStateFlags       state,
1911               GtkStylePropertyContext *context)
1912 {
1913   pack_border (value, props, state,
1914                "padding-top", "padding-left",
1915                "padding-bottom", "padding-right");
1916 }
1917
1918 static GParameter *
1919 unpack_margin (const GValue *value,
1920                guint        *n_params)
1921 {
1922   return unpack_border (value, n_params,
1923                         "margin-top", "margin-left",
1924                         "margin-bottom", "margin-right");
1925 }
1926
1927 static void
1928 pack_margin (GValue             *value,
1929              GtkStyleProperties *props,
1930              GtkStateFlags       state,
1931              GtkStylePropertyContext *context)
1932 {
1933   pack_border (value, props, state,
1934                "margin-top", "margin-left",
1935                "margin-bottom", "margin-right");
1936 }
1937
1938 static GParameter *
1939 unpack_border_radius (const GValue *value,
1940                       guint        *n_params)
1941 {
1942   GParameter *parameter = g_new0 (GParameter, 4);
1943   GtkCssBorderRadius *border;
1944   
1945   if (G_VALUE_HOLDS_BOXED (value))
1946     border = g_value_get_boxed (value);
1947   else
1948     border = NULL;
1949
1950   parameter[0].name = "border-top-left-radius";
1951   g_value_init (&parameter[0].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1952   parameter[1].name = "border-top-right-radius";
1953   g_value_init (&parameter[1].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1954   parameter[2].name = "border-bottom-right-radius";
1955   g_value_init (&parameter[2].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1956   parameter[3].name = "border-bottom-left-radius";
1957   g_value_init (&parameter[3].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
1958   if (border)
1959     {
1960       g_value_set_boxed (&parameter[0].value, &border->top_left);
1961       g_value_set_boxed (&parameter[1].value, &border->top_right);
1962       g_value_set_boxed (&parameter[2].value, &border->bottom_right);
1963       g_value_set_boxed (&parameter[3].value, &border->bottom_left);
1964     }
1965
1966   *n_params = 4;
1967   return parameter;
1968 }
1969
1970 static void
1971 pack_border_radius (GValue             *value,
1972                     GtkStyleProperties *props,
1973                     GtkStateFlags       state,
1974                     GtkStylePropertyContext *context)
1975 {
1976   GtkCssBorderCornerRadius *top_left;
1977
1978   /* NB: We are an int property, so we have to resolve to an int here.
1979    * So we just resolve to an int. We pick one and stick to it.
1980    * Lesson learned: Don't query border-radius shorthand, query the 
1981    * real properties instead. */
1982   gtk_style_properties_get (props,
1983                             state,
1984                             "border-top-left-radius", &top_left,
1985                             NULL);
1986
1987   if (top_left)
1988     g_value_set_int (value, top_left->horizontal);
1989
1990   g_free (top_left);
1991 }
1992
1993 static GParameter *
1994 unpack_font_description (const GValue *value,
1995                          guint        *n_params)
1996 {
1997   GParameter *parameter = g_new0 (GParameter, 5);
1998   PangoFontDescription *description;
1999   PangoFontMask mask;
2000   guint n;
2001   
2002   /* For backwards compat, we only unpack values that are indeed set.
2003    * For strict CSS conformance we need to unpack all of them.
2004    * Note that we do set all of them in the parse function, so it
2005    * will not have effects when parsing CSS files. It will though
2006    * for custom style providers.
2007    */
2008
2009   description = g_value_get_boxed (value);
2010   n = 0;
2011
2012   if (description)
2013     mask = pango_font_description_get_set_fields (description);
2014   else
2015     mask = 0;
2016
2017   if (mask & PANGO_FONT_MASK_FAMILY)
2018     {
2019       GPtrArray *strv = g_ptr_array_new ();
2020
2021       g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (description)));
2022       g_ptr_array_add (strv, NULL);
2023       parameter[n].name = "font-family";
2024       g_value_init (&parameter[n].value, G_TYPE_STRV);
2025       g_value_take_boxed (&parameter[n].value,
2026                           g_ptr_array_free (strv, FALSE));
2027       n++;
2028     }
2029
2030   if (mask & PANGO_FONT_MASK_STYLE)
2031     {
2032       parameter[n].name = "font-style";
2033       g_value_init (&parameter[n].value, PANGO_TYPE_STYLE);
2034       g_value_set_enum (&parameter[n].value,
2035                         pango_font_description_get_style (description));
2036       n++;
2037     }
2038
2039   if (mask & PANGO_FONT_MASK_VARIANT)
2040     {
2041       parameter[n].name = "font-variant";
2042       g_value_init (&parameter[n].value, PANGO_TYPE_VARIANT);
2043       g_value_set_enum (&parameter[n].value,
2044                         pango_font_description_get_variant (description));
2045       n++;
2046     }
2047
2048   if (mask & PANGO_FONT_MASK_WEIGHT)
2049     {
2050       parameter[n].name = "font-weight";
2051       g_value_init (&parameter[n].value, PANGO_TYPE_WEIGHT);
2052       g_value_set_enum (&parameter[n].value,
2053                         pango_font_description_get_weight (description));
2054       n++;
2055     }
2056
2057   if (mask & PANGO_FONT_MASK_SIZE)
2058     {
2059       parameter[n].name = "font-size";
2060       g_value_init (&parameter[n].value, G_TYPE_DOUBLE);
2061       g_value_set_double (&parameter[n].value,
2062                           (double) pango_font_description_get_size (description) / PANGO_SCALE);
2063       n++;
2064     }
2065
2066   *n_params = n;
2067
2068   return parameter;
2069 }
2070
2071 static void
2072 pack_font_description (GValue             *value,
2073                        GtkStyleProperties *props,
2074                        GtkStateFlags       state,
2075                        GtkStylePropertyContext *context)
2076 {
2077   PangoFontDescription *description;
2078   char **families;
2079   PangoStyle style;
2080   PangoVariant variant;
2081   PangoWeight weight;
2082   double size;
2083
2084   gtk_style_properties_get (props,
2085                             state,
2086                             "font-family", &families,
2087                             "font-style", &style,
2088                             "font-variant", &variant,
2089                             "font-weight", &weight,
2090                             "font-size", &size,
2091                             NULL);
2092
2093   description = pango_font_description_new ();
2094   /* xxx: Can we set all the families here somehow? */
2095   if (families)
2096     pango_font_description_set_family (description, families[0]);
2097   pango_font_description_set_size (description, round (size * PANGO_SCALE));
2098   pango_font_description_set_style (description, style);
2099   pango_font_description_set_variant (description, variant);
2100   pango_font_description_set_weight (description, weight);
2101
2102   g_strfreev (families);
2103
2104   g_value_take_boxed (value, description);
2105 }
2106
2107 static GParameter *
2108 unpack_border_color (const GValue *value,
2109                      guint        *n_params)
2110 {
2111   GParameter *parameter = g_new0 (GParameter, 4);
2112   GType type;
2113   
2114   type = G_VALUE_TYPE (value);
2115   if (type == G_TYPE_PTR_ARRAY)
2116     type = GTK_TYPE_SYMBOLIC_COLOR;
2117
2118   parameter[0].name = "border-top-color";
2119   g_value_init (&parameter[0].value, type);
2120   parameter[1].name = "border-right-color";
2121   g_value_init (&parameter[1].value, type);
2122   parameter[2].name = "border-bottom-color";
2123   g_value_init (&parameter[2].value, type);
2124   parameter[3].name = "border-left-color";
2125   g_value_init (&parameter[3].value, type);
2126
2127   if (G_VALUE_TYPE (value) == G_TYPE_PTR_ARRAY)
2128     {
2129       GPtrArray *array = g_value_get_boxed (value);
2130       guint i;
2131
2132       for (i = 0; i < 4; i++)
2133         g_value_set_boxed (&parameter[i].value, g_ptr_array_index (array, i));
2134     }
2135   else
2136     {
2137       /* can be RGBA or symbolic color */
2138       gpointer p = g_value_get_boxed (value);
2139
2140       g_value_set_boxed (&parameter[0].value, p);
2141       g_value_set_boxed (&parameter[1].value, p);
2142       g_value_set_boxed (&parameter[2].value, p);
2143       g_value_set_boxed (&parameter[3].value, p);
2144     }
2145
2146   *n_params = 4;
2147   return parameter;
2148 }
2149
2150 static void
2151 pack_border_color (GValue             *value,
2152                    GtkStyleProperties *props,
2153                    GtkStateFlags       state,
2154                    GtkStylePropertyContext *context)
2155 {
2156   /* NB: We are a color property, so we have to resolve to a color here.
2157    * So we just resolve to a color. We pick one and stick to it.
2158    * Lesson learned: Don't query border-color shorthand, query the 
2159    * real properties instead. */
2160   g_value_unset (value);
2161   gtk_style_properties_get_property (props, "border-top-color", state, value);
2162 }
2163
2164 /*** UNSET FUNCS ***/
2165
2166 static void
2167 unset_font_description (GtkStyleProperties *props,
2168                         GtkStateFlags       state)
2169 {
2170   gtk_style_properties_unset_property (props, "font-family", state);
2171   gtk_style_properties_unset_property (props, "font-style", state);
2172   gtk_style_properties_unset_property (props, "font-variant", state);
2173   gtk_style_properties_unset_property (props, "font-weight", state);
2174   gtk_style_properties_unset_property (props, "font-size", state);
2175 }
2176
2177 static void
2178 unset_margin (GtkStyleProperties *props,
2179               GtkStateFlags       state)
2180 {
2181   gtk_style_properties_unset_property (props, "margin-top", state);
2182   gtk_style_properties_unset_property (props, "margin-right", state);
2183   gtk_style_properties_unset_property (props, "margin-bottom", state);
2184   gtk_style_properties_unset_property (props, "margin-left", state);
2185 }
2186
2187 static void
2188 unset_padding (GtkStyleProperties *props,
2189                GtkStateFlags       state)
2190 {
2191   gtk_style_properties_unset_property (props, "padding-top", state);
2192   gtk_style_properties_unset_property (props, "padding-right", state);
2193   gtk_style_properties_unset_property (props, "padding-bottom", state);
2194   gtk_style_properties_unset_property (props, "padding-left", state);
2195 }
2196
2197 static void
2198 unset_border_width (GtkStyleProperties *props,
2199                     GtkStateFlags       state)
2200 {
2201   gtk_style_properties_unset_property (props, "border-top-width", state);
2202   gtk_style_properties_unset_property (props, "border-right-width", state);
2203   gtk_style_properties_unset_property (props, "border-bottom-width", state);
2204   gtk_style_properties_unset_property (props, "border-left-width", state);
2205 }
2206
2207 static void
2208 unset_border_radius (GtkStyleProperties *props,
2209                      GtkStateFlags       state)
2210 {
2211   gtk_style_properties_unset_property (props, "border-top-right-radius", state);
2212   gtk_style_properties_unset_property (props, "border-bottom-right-radius", state);
2213   gtk_style_properties_unset_property (props, "border-bottom-left-radius", state);
2214   gtk_style_properties_unset_property (props, "border-top-left-radius", state);
2215 }
2216
2217 static void
2218 unset_border_color (GtkStyleProperties *props,
2219                     GtkStateFlags       state)
2220 {
2221   gtk_style_properties_unset_property (props, "border-top-color", state);
2222   gtk_style_properties_unset_property (props, "border-right-color", state);
2223   gtk_style_properties_unset_property (props, "border-bottom-color", state);
2224   gtk_style_properties_unset_property (props, "border-left-color", state);
2225 }
2226
2227 static void
2228 unset_border_image (GtkStyleProperties *props,
2229                     GtkStateFlags       state)
2230 {
2231   gtk_style_properties_unset_property (props, "border-image-source", state);
2232   gtk_style_properties_unset_property (props, "border-image-slice", state);
2233   gtk_style_properties_unset_property (props, "border-image-repeat", state);
2234   gtk_style_properties_unset_property (props, "border-image-width", state);
2235 }
2236
2237 /*** default values ***/
2238
2239 static void
2240 border_image_width_default_value (GtkStyleProperties *props,
2241                                   GtkStateFlags       state,
2242                                   GValue             *value)
2243 {
2244 }
2245
2246 static void
2247 background_color_default_value (GtkStyleProperties *props,
2248                                 GtkStateFlags       state,
2249                                 GValue             *value)
2250 {
2251   GdkRGBA transparent_black = { 0, 0, 0, 0 };
2252
2253   g_value_set_boxed (value, &transparent_black);
2254 }
2255
2256 static void
2257 border_color_default_value (GtkStyleProperties *props,
2258                             GtkStateFlags       state,
2259                             GValue             *value)
2260 {
2261   g_value_unset (value);
2262   gtk_style_properties_get_property (props, "color", state, value);
2263 }
2264
2265 /*** API ***/
2266
2267 static void
2268 css_string_funcs_init (void)
2269 {
2270   if (G_LIKELY (parse_funcs != NULL))
2271     return;
2272
2273   parse_funcs = g_hash_table_new (NULL, NULL);
2274   print_funcs = g_hash_table_new (NULL, NULL);
2275
2276   register_conversion_function (GDK_TYPE_RGBA,
2277                                 rgba_value_parse,
2278                                 rgba_value_print);
2279   register_conversion_function (GDK_TYPE_COLOR,
2280                                 color_value_parse,
2281                                 color_value_print);
2282   register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR,
2283                                 symbolic_color_value_parse,
2284                                 symbolic_color_value_print);
2285   register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION,
2286                                 font_description_value_parse,
2287                                 font_description_value_print);
2288   register_conversion_function (G_TYPE_BOOLEAN,
2289                                 boolean_value_parse,
2290                                 boolean_value_print);
2291   register_conversion_function (G_TYPE_INT,
2292                                 int_value_parse,
2293                                 int_value_print);
2294   register_conversion_function (G_TYPE_UINT,
2295                                 uint_value_parse,
2296                                 uint_value_print);
2297   register_conversion_function (G_TYPE_DOUBLE,
2298                                 double_value_parse,
2299                                 double_value_print);
2300   register_conversion_function (G_TYPE_FLOAT,
2301                                 float_value_parse,
2302                                 float_value_print);
2303   register_conversion_function (G_TYPE_STRING,
2304                                 string_value_parse,
2305                                 string_value_print);
2306   register_conversion_function (GTK_TYPE_THEMING_ENGINE,
2307                                 theming_engine_value_parse,
2308                                 theming_engine_value_print);
2309   register_conversion_function (GTK_TYPE_ANIMATION_DESCRIPTION,
2310                                 animation_description_value_parse,
2311                                 animation_description_value_print);
2312   register_conversion_function (GTK_TYPE_BORDER,
2313                                 border_value_parse,
2314                                 border_value_print);
2315   register_conversion_function (GTK_TYPE_GRADIENT,
2316                                 gradient_value_parse,
2317                                 gradient_value_print);
2318   register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
2319                                 pattern_value_parse,
2320                                 pattern_value_print);
2321   register_conversion_function (GTK_TYPE_BORDER_IMAGE,
2322                                 border_image_value_parse,
2323                                 NULL);
2324   register_conversion_function (GTK_TYPE_CSS_BORDER_IMAGE_REPEAT,
2325                                 border_image_repeat_value_parse,
2326                                 border_image_repeat_value_print);
2327   register_conversion_function (GTK_TYPE_SHADOW,
2328                                 shadow_value_parse,
2329                                 shadow_value_print);
2330   register_conversion_function (G_TYPE_ENUM,
2331                                 enum_value_parse,
2332                                 enum_value_print);
2333   register_conversion_function (G_TYPE_FLAGS,
2334                                 flags_value_parse,
2335                                 flags_value_print);
2336   register_conversion_function (GTK_TYPE_CSS_BACKGROUND_REPEAT,
2337                                 background_repeat_value_parse,
2338                                 background_repeat_value_print);
2339 }
2340
2341 gboolean
2342 _gtk_style_property_parse_value (const GtkStyleProperty *property,
2343                                  GValue                 *value,
2344                                  GtkCssParser           *parser,
2345                                  GFile                  *base)
2346 {
2347   GtkStyleParseFunc func;
2348
2349   g_return_val_if_fail (value != NULL, FALSE);
2350   g_return_val_if_fail (parser != NULL, FALSE);
2351
2352   css_string_funcs_init ();
2353
2354   if (property)
2355     {
2356       if (_gtk_css_parser_try (parser, "none", TRUE))
2357         {
2358           /* Insert the default value, so it has an opportunity
2359            * to override other style providers when merged
2360            */
2361           g_param_value_set_default (property->pspec, value);
2362           return TRUE;
2363         }
2364       else if (property->property_parse_func)
2365         {
2366           GError *error = NULL;
2367           char *value_str;
2368           gboolean success;
2369           
2370           value_str = _gtk_css_parser_read_value (parser);
2371           if (value_str == NULL)
2372             return FALSE;
2373           
2374           success = (*property->property_parse_func) (value_str, value, &error);
2375
2376           g_free (value_str);
2377
2378           return success;
2379         }
2380
2381       func = property->parse_func;
2382     }
2383   else
2384     func = NULL;
2385
2386   if (func == NULL)
2387     func = g_hash_table_lookup (parse_funcs,
2388                                 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
2389   if (func == NULL)
2390     func = g_hash_table_lookup (parse_funcs,
2391                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
2392
2393   if (func == NULL)
2394     {
2395       _gtk_css_parser_error (parser,
2396                              "Cannot convert to type '%s'",
2397                              g_type_name (G_VALUE_TYPE (value)));
2398       return FALSE;
2399     }
2400
2401   return (*func) (parser, base, value);
2402 }
2403
2404 void
2405 _gtk_style_property_print_value (const GtkStyleProperty *property,
2406                                  const GValue           *value,
2407                                  GString                *string)
2408 {
2409   GtkStylePrintFunc func;
2410
2411   css_string_funcs_init ();
2412
2413   if (property)
2414     func = property->print_func;
2415   else
2416     func = NULL;
2417
2418   if (func == NULL)
2419     func = g_hash_table_lookup (print_funcs,
2420                                 GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
2421   if (func == NULL)
2422     func = g_hash_table_lookup (print_funcs,
2423                                 GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
2424
2425   if (func == NULL)
2426     {
2427       char *s = g_strdup_value_contents (value);
2428       g_string_append (string, s);
2429       g_free (s);
2430       return;
2431     }
2432   
2433   func (value, string);
2434 }
2435
2436 void
2437 _gtk_style_property_default_value (const GtkStyleProperty *property,
2438                                    GtkStyleProperties     *properties,
2439                                    GtkStateFlags           state,
2440                                    GValue                 *value)
2441 {
2442   if (property->default_value_func)
2443     property->default_value_func (properties, state, value);
2444   else if (property->pspec->value_type == GTK_TYPE_THEMING_ENGINE)
2445     g_value_set_object (value, gtk_theming_engine_load (NULL));
2446   else if (property->pspec->value_type == PANGO_TYPE_FONT_DESCRIPTION)
2447     g_value_take_boxed (value, pango_font_description_from_string ("Sans 10"));
2448   else if (property->pspec->value_type == GDK_TYPE_RGBA)
2449     {
2450       GdkRGBA color;
2451       gdk_rgba_parse (&color, "pink");
2452       g_value_set_boxed (value, &color);
2453     }
2454   else if (property->pspec->value_type == GTK_TYPE_BORDER)
2455     {
2456       g_value_take_boxed (value, gtk_border_new ());
2457     }
2458   else
2459     g_param_value_set_default (property->pspec, value);
2460 }
2461
2462 gboolean
2463 _gtk_style_property_is_inherit (const GtkStyleProperty *property)
2464 {
2465   g_return_val_if_fail (property != NULL, FALSE);
2466
2467   return property->flags & GTK_STYLE_PROPERTY_INHERIT ? TRUE : FALSE;
2468 }
2469
2470 static gboolean
2471 resolve_color (GtkStyleProperties *props,
2472                GValue             *value)
2473 {
2474   GdkRGBA color;
2475
2476   /* Resolve symbolic color to GdkRGBA */
2477   if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &color))
2478     return FALSE;
2479
2480   /* Store it back, this is where GdkRGBA caching happens */
2481   g_value_unset (value);
2482   g_value_init (value, GDK_TYPE_RGBA);
2483   g_value_set_boxed (value, &color);
2484
2485   return TRUE;
2486 }
2487
2488 static gboolean
2489 resolve_color_rgb (GtkStyleProperties *props,
2490                    GValue             *value)
2491 {
2492   GdkColor color = { 0 };
2493   GdkRGBA rgba;
2494
2495   if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &rgba))
2496     return FALSE;
2497
2498   color.red = rgba.red * 65535. + 0.5;
2499   color.green = rgba.green * 65535. + 0.5;
2500   color.blue = rgba.blue * 65535. + 0.5;
2501
2502   g_value_unset (value);
2503   g_value_init (value, GDK_TYPE_COLOR);
2504   g_value_set_boxed (value, &color);
2505
2506   return TRUE;
2507 }
2508
2509 static gboolean
2510 resolve_win32_theme_part (GtkStyleProperties *props,
2511                           GValue             *value,
2512                           GValue             *value_out,
2513                           GtkStylePropertyContext *context)
2514 {
2515   GtkWin32ThemePart  *part;
2516   cairo_pattern_t *pattern;
2517
2518   part = g_value_get_boxed (value);
2519   if (part == NULL)
2520     return FALSE;
2521
2522   pattern = _gtk_win32_theme_part_render (part, context->width, context->height);
2523
2524   g_value_take_boxed (value_out, pattern);
2525
2526   return TRUE;
2527 }
2528
2529
2530 static gboolean
2531 resolve_gradient (GtkStyleProperties *props,
2532                   GValue             *value)
2533 {
2534   cairo_pattern_t *gradient;
2535
2536   if (!gtk_gradient_resolve (g_value_get_boxed (value), props, &gradient))
2537     return FALSE;
2538
2539   /* Store it back, this is where cairo_pattern_t caching happens */
2540   g_value_unset (value);
2541   g_value_init (value, CAIRO_GOBJECT_TYPE_PATTERN);
2542   g_value_take_boxed (value, gradient);
2543
2544   return TRUE;
2545 }
2546
2547 static gboolean
2548 resolve_shadow (GtkStyleProperties *props,
2549                 GValue *value)
2550 {
2551   GtkShadow *resolved, *base;
2552
2553   base = g_value_get_boxed (value);
2554
2555   if (base == NULL)
2556     return TRUE;
2557   
2558   if (_gtk_shadow_get_resolved (base))
2559     return TRUE;
2560
2561   resolved = _gtk_shadow_resolve (base, props);
2562   if (resolved == NULL)
2563     return FALSE;
2564
2565   g_value_take_boxed (value, resolved);
2566
2567   return TRUE;
2568 }
2569
2570 void
2571 _gtk_style_property_resolve (const GtkStyleProperty *property,
2572                              GtkStyleProperties     *props,
2573                              GtkStateFlags           state,
2574                              GtkStylePropertyContext *context,
2575                              GValue                 *val,
2576                              GValue                 *val_out)
2577 {
2578   if (G_VALUE_TYPE (val) == GTK_TYPE_SYMBOLIC_COLOR)
2579     {
2580       if (property->pspec->value_type == GDK_TYPE_RGBA)
2581         {
2582           if (resolve_color (props, val))
2583             goto out;
2584         }
2585       else if (property->pspec->value_type == GDK_TYPE_COLOR)
2586         {
2587           if (resolve_color_rgb (props, val))
2588             goto out;
2589         }
2590       
2591       g_value_unset (val);
2592       g_value_init (val, property->pspec->value_type);
2593       _gtk_style_property_default_value (property, props, state, val);
2594     }
2595   else if (G_VALUE_TYPE (val) == GDK_TYPE_RGBA)
2596     {
2597       if (g_value_get_boxed (val) == NULL)
2598         _gtk_style_property_default_value (property, props, state, val);
2599     }
2600   else if (G_VALUE_TYPE (val) == GTK_TYPE_GRADIENT)
2601     {
2602       g_return_if_fail (property->pspec->value_type == CAIRO_GOBJECT_TYPE_PATTERN);
2603
2604       if (!resolve_gradient (props, val))
2605         {
2606           g_value_unset (val);
2607           g_value_init (val, CAIRO_GOBJECT_TYPE_PATTERN);
2608           _gtk_style_property_default_value (property, props, state, val);
2609         }
2610     }
2611   else if (G_VALUE_TYPE (val) == GTK_TYPE_SHADOW)
2612     {
2613       if (!resolve_shadow (props, val))
2614         _gtk_style_property_default_value (property, props, state, val);
2615     }
2616   else if (G_VALUE_TYPE (val) == GTK_TYPE_WIN32_THEME_PART)
2617     {
2618       if (resolve_win32_theme_part (props, val, val_out, context))
2619         return; /* Don't copy val, this sets val_out */
2620       _gtk_style_property_default_value (property, props, state, val);
2621     }
2622
2623  out:
2624   g_value_copy (val, val_out);
2625 }
2626
2627 gboolean
2628 _gtk_style_property_is_shorthand  (const GtkStyleProperty *property)
2629 {
2630   g_return_val_if_fail (property != NULL, FALSE);
2631
2632   return property->pack_func != NULL;
2633 }
2634
2635 GParameter *
2636 _gtk_style_property_unpack (const GtkStyleProperty *property,
2637                             const GValue           *value,
2638                             guint                  *n_params)
2639 {
2640   g_return_val_if_fail (property != NULL, NULL);
2641   g_return_val_if_fail (property->unpack_func != NULL, NULL);
2642   g_return_val_if_fail (value != NULL, NULL);
2643   g_return_val_if_fail (n_params != NULL, NULL);
2644
2645   return property->unpack_func (value, n_params);
2646 }
2647
2648 void
2649 _gtk_style_property_pack (const GtkStyleProperty *property,
2650                           GtkStyleProperties     *props,
2651                           GtkStateFlags           state,
2652                           GtkStylePropertyContext *context,
2653                           GValue                 *value)
2654 {
2655   g_return_if_fail (property != NULL);
2656   g_return_if_fail (property->pack_func != NULL);
2657   g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
2658   g_return_if_fail (G_IS_VALUE (value));
2659
2660   property->pack_func (value, props, state, context);
2661 }
2662
2663 static void
2664 gtk_style_property_init (void)
2665 {
2666   if (G_LIKELY (properties))
2667     return;
2668
2669   /* stuff is never freed, so no need for free functions */
2670   properties = g_hash_table_new (g_str_hash, g_str_equal);
2671
2672   /* note that gtk_style_properties_register_property() calls this function,
2673    * so make sure we're sanely inited to avoid infloops */
2674
2675   _gtk_style_property_register           (g_param_spec_boxed ("color",
2676                                           "Foreground color",
2677                                           "Foreground color",
2678                                           GDK_TYPE_RGBA, 0),
2679                                           GTK_STYLE_PROPERTY_INHERIT,
2680                                           NULL,
2681                                           NULL,
2682                                           NULL,
2683                                           NULL,
2684                                           NULL,
2685                                           NULL,
2686                                           NULL);
2687   _gtk_style_property_register           (g_param_spec_boxed ("background-color",
2688                                           "Background color",
2689                                           "Background color",
2690                                           GDK_TYPE_RGBA, 0),
2691                                           0,
2692                                           NULL,
2693                                           NULL,
2694                                           NULL,
2695                                           transparent_color_value_parse,
2696                                           NULL,
2697                                           background_color_default_value,
2698                                           NULL);
2699
2700   _gtk_style_property_register           (g_param_spec_boxed ("font-family",
2701                                                               "Font family",
2702                                                               "Font family",
2703                                                               G_TYPE_STRV, 0),
2704                                           GTK_STYLE_PROPERTY_INHERIT,
2705                                           NULL,
2706                                           NULL,
2707                                           NULL,
2708                                           font_family_parse,
2709                                           font_family_value_print,
2710                                           NULL,
2711                                           NULL);
2712   _gtk_style_property_register           (g_param_spec_enum ("font-style",
2713                                                              "Font style",
2714                                                              "Font style",
2715                                                              PANGO_TYPE_STYLE,
2716                                                              PANGO_STYLE_NORMAL, 0),
2717                                           GTK_STYLE_PROPERTY_INHERIT,
2718                                           NULL,
2719                                           NULL,
2720                                           NULL,
2721                                           NULL,
2722                                           NULL,
2723                                           NULL,
2724                                           NULL);
2725   _gtk_style_property_register           (g_param_spec_enum ("font-variant",
2726                                                              "Font variant",
2727                                                              "Font variant",
2728                                                              PANGO_TYPE_VARIANT,
2729                                                              PANGO_VARIANT_NORMAL, 0),
2730                                           GTK_STYLE_PROPERTY_INHERIT,
2731                                           NULL,
2732                                           NULL,
2733                                           NULL,
2734                                           NULL,
2735                                           NULL,
2736                                           NULL,
2737                                           NULL);
2738   /* xxx: need to parse this properly, ie parse the numbers */
2739   _gtk_style_property_register           (g_param_spec_enum ("font-weight",
2740                                                              "Font weight",
2741                                                              "Font weight",
2742                                                              PANGO_TYPE_WEIGHT,
2743                                                              PANGO_WEIGHT_NORMAL, 0),
2744                                           GTK_STYLE_PROPERTY_INHERIT,
2745                                           NULL,
2746                                           NULL,
2747                                           NULL,
2748                                           NULL,
2749                                           NULL,
2750                                           NULL,
2751                                           NULL);
2752   _gtk_style_property_register           (g_param_spec_double ("font-size",
2753                                                                "Font size",
2754                                                                "Font size",
2755                                                                0, G_MAXDOUBLE, 0, 0),
2756                                           GTK_STYLE_PROPERTY_INHERIT,
2757                                           NULL,
2758                                           NULL,
2759                                           NULL,
2760                                           NULL,
2761                                           NULL,
2762                                           NULL,
2763                                           NULL);
2764   _gtk_style_property_register           (g_param_spec_boxed ("font",
2765                                                               "Font Description",
2766                                                               "Font Description",
2767                                                               PANGO_TYPE_FONT_DESCRIPTION, 0),
2768                                           GTK_STYLE_PROPERTY_INHERIT,
2769                                           NULL,
2770                                           unpack_font_description,
2771                                           pack_font_description,
2772                                           font_description_value_parse,
2773                                           font_description_value_print,
2774                                           NULL,
2775                                           unset_font_description);
2776
2777   _gtk_style_property_register           (g_param_spec_boxed ("text-shadow",
2778                                                               "Text shadow",
2779                                                               "Text shadow",
2780                                                               GTK_TYPE_SHADOW, 0),
2781                                           GTK_STYLE_PROPERTY_INHERIT,
2782                                           NULL,
2783                                           NULL,
2784                                           NULL,
2785                                           NULL,
2786                                           NULL,
2787                                           NULL,
2788                                           NULL);
2789
2790   _gtk_style_property_register           (g_param_spec_boxed ("icon-shadow",
2791                                                               "Icon shadow",
2792                                                               "Icon shadow",
2793                                                               GTK_TYPE_SHADOW, 0),
2794                                           GTK_STYLE_PROPERTY_INHERIT,
2795                                           NULL,
2796                                           NULL,
2797                                           NULL,
2798                                           NULL,
2799                                           NULL,
2800                                           NULL,
2801                                           NULL);
2802
2803   gtk_style_properties_register_property (NULL,
2804                                           g_param_spec_boxed ("box-shadow",
2805                                                               "Box shadow",
2806                                                               "Box shadow",
2807                                                               GTK_TYPE_SHADOW, 0));
2808   gtk_style_properties_register_property (NULL,
2809                                           g_param_spec_int ("margin-top",
2810                                                             "margin top",
2811                                                             "Margin at top",
2812                                                             0, G_MAXINT, 0, 0));
2813   gtk_style_properties_register_property (NULL,
2814                                           g_param_spec_int ("margin-left",
2815                                                             "margin left",
2816                                                             "Margin at left",
2817                                                             0, G_MAXINT, 0, 0));
2818   gtk_style_properties_register_property (NULL,
2819                                           g_param_spec_int ("margin-bottom",
2820                                                             "margin bottom",
2821                                                             "Margin at bottom",
2822                                                             0, G_MAXINT, 0, 0));
2823   gtk_style_properties_register_property (NULL,
2824                                           g_param_spec_int ("margin-right",
2825                                                             "margin right",
2826                                                             "Margin at right",
2827                                                             0, G_MAXINT, 0, 0));
2828   _gtk_style_property_register           (g_param_spec_boxed ("margin",
2829                                                               "Margin",
2830                                                               "Margin",
2831                                                               GTK_TYPE_BORDER, 0),
2832                                           0,
2833                                           NULL,
2834                                           unpack_margin,
2835                                           pack_margin,
2836                                           NULL,
2837                                           NULL,
2838                                           NULL,
2839                                           unset_margin);
2840   gtk_style_properties_register_property (NULL,
2841                                           g_param_spec_int ("padding-top",
2842                                                             "padding top",
2843                                                             "Padding at top",
2844                                                             0, G_MAXINT, 0, 0));
2845   gtk_style_properties_register_property (NULL,
2846                                           g_param_spec_int ("padding-left",
2847                                                             "padding left",
2848                                                             "Padding at left",
2849                                                             0, G_MAXINT, 0, 0));
2850   gtk_style_properties_register_property (NULL,
2851                                           g_param_spec_int ("padding-bottom",
2852                                                             "padding bottom",
2853                                                             "Padding at bottom",
2854                                                             0, G_MAXINT, 0, 0));
2855   gtk_style_properties_register_property (NULL,
2856                                           g_param_spec_int ("padding-right",
2857                                                             "padding right",
2858                                                             "Padding at right",
2859                                                             0, G_MAXINT, 0, 0));
2860   _gtk_style_property_register           (g_param_spec_boxed ("padding",
2861                                                               "Padding",
2862                                                               "Padding",
2863                                                               GTK_TYPE_BORDER, 0),
2864                                           0,
2865                                           NULL,
2866                                           unpack_padding,
2867                                           pack_padding,
2868                                           NULL,
2869                                           NULL,
2870                                           NULL,
2871                                           unset_padding);
2872   gtk_style_properties_register_property (NULL,
2873                                           g_param_spec_int ("border-top-width",
2874                                                             "border top width",
2875                                                             "Border width at top",
2876                                                             0, G_MAXINT, 0, 0));
2877   gtk_style_properties_register_property (NULL,
2878                                           g_param_spec_int ("border-left-width",
2879                                                             "border left width",
2880                                                             "Border width at left",
2881                                                             0, G_MAXINT, 0, 0));
2882   gtk_style_properties_register_property (NULL,
2883                                           g_param_spec_int ("border-bottom-width",
2884                                                             "border bottom width",
2885                                                             "Border width at bottom",
2886                                                             0, G_MAXINT, 0, 0));
2887   gtk_style_properties_register_property (NULL,
2888                                           g_param_spec_int ("border-right-width",
2889                                                             "border right width",
2890                                                             "Border width at right",
2891                                                             0, G_MAXINT, 0, 0));
2892   _gtk_style_property_register           (g_param_spec_boxed ("border-width",
2893                                                               "Border width",
2894                                                               "Border width, in pixels",
2895                                                               GTK_TYPE_BORDER, 0),
2896                                           0,
2897                                           NULL,
2898                                           unpack_border_width,
2899                                           pack_border_width,
2900                                           NULL,
2901                                           NULL,
2902                                           NULL,
2903                                           unset_border_width);
2904
2905   _gtk_style_property_register           (g_param_spec_boxed ("border-top-left-radius",
2906                                                               "Border top left radius",
2907                                                               "Border radius of top left corner, in pixels",
2908                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2909                                           0,
2910                                           NULL,
2911                                           NULL,
2912                                           NULL,
2913                                           border_corner_radius_value_parse,
2914                                           border_corner_radius_value_print,
2915                                           NULL,
2916                                           NULL);
2917   _gtk_style_property_register           (g_param_spec_boxed ("border-top-right-radius",
2918                                                               "Border top right radius",
2919                                                               "Border radius of top right corner, in pixels",
2920                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2921                                           0,
2922                                           NULL,
2923                                           NULL,
2924                                           NULL,
2925                                           border_corner_radius_value_parse,
2926                                           border_corner_radius_value_print,
2927                                           NULL,
2928                                           NULL);
2929   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-right-radius",
2930                                                               "Border bottom right radius",
2931                                                               "Border radius of bottom right corner, in pixels",
2932                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2933                                           0,
2934                                           NULL,
2935                                           NULL,
2936                                           NULL,
2937                                           border_corner_radius_value_parse,
2938                                           border_corner_radius_value_print,
2939                                           NULL,
2940                                           NULL);
2941   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-left-radius",
2942                                                               "Border bottom left radius",
2943                                                               "Border radius of bottom left corner, in pixels",
2944                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
2945                                           0,
2946                                           NULL,
2947                                           NULL,
2948                                           NULL,
2949                                           border_corner_radius_value_parse,
2950                                           border_corner_radius_value_print,
2951                                           NULL,
2952                                           NULL);
2953   _gtk_style_property_register           (g_param_spec_int ("border-radius",
2954                                                             "Border radius",
2955                                                             "Border radius, in pixels",
2956                                                             0, G_MAXINT, 0, 0),
2957                                           0,
2958                                           NULL,
2959                                           unpack_border_radius,
2960                                           pack_border_radius,
2961                                           border_radius_value_parse,
2962                                           border_radius_value_print,
2963                                           NULL,
2964                                           unset_border_radius);
2965
2966   gtk_style_properties_register_property (NULL,
2967                                           g_param_spec_enum ("border-style",
2968                                                              "Border style",
2969                                                              "Border style",
2970                                                              GTK_TYPE_BORDER_STYLE,
2971                                                              GTK_BORDER_STYLE_NONE, 0));
2972   _gtk_style_property_register           (g_param_spec_boxed ("border-top-color",
2973                                                               "Border top color",
2974                                                               "Border top color",
2975                                                               GDK_TYPE_RGBA, 0),
2976                                           0,
2977                                           NULL,
2978                                           NULL,
2979                                           NULL,
2980                                           transparent_color_value_parse,
2981                                           NULL,
2982                                           border_color_default_value,
2983                                           NULL);
2984   _gtk_style_property_register           (g_param_spec_boxed ("border-right-color",
2985                                                               "Border right color",
2986                                                               "Border right color",
2987                                                               GDK_TYPE_RGBA, 0),
2988                                           0,
2989                                           NULL,
2990                                           NULL,
2991                                           NULL,
2992                                           transparent_color_value_parse,
2993                                           NULL,
2994                                           border_color_default_value,
2995                                           NULL);
2996   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-color",
2997                                                               "Border bottom color",
2998                                                               "Border bottom color",
2999                                                               GDK_TYPE_RGBA, 0),
3000                                           0,
3001                                           NULL,
3002                                           NULL,
3003                                           NULL,
3004                                           transparent_color_value_parse,
3005                                           NULL,
3006                                           border_color_default_value,
3007                                           NULL);
3008   _gtk_style_property_register           (g_param_spec_boxed ("border-left-color",
3009                                                               "Border left color",
3010                                                               "Border left color",
3011                                                               GDK_TYPE_RGBA, 0),
3012                                           0,
3013                                           NULL,
3014                                           NULL,
3015                                           NULL,
3016                                           transparent_color_value_parse,
3017                                           NULL,
3018                                           border_color_default_value,
3019                                           NULL);
3020   _gtk_style_property_register           (g_param_spec_boxed ("border-color",
3021                                                               "Border color",
3022                                                               "Border color",
3023                                                               GDK_TYPE_RGBA, 0),
3024                                           0,
3025                                           NULL,
3026                                           unpack_border_color,
3027                                           pack_border_color,
3028                                           border_color_shorthand_value_parse,
3029                                           NULL,
3030                                           NULL,
3031                                           unset_border_color);
3032
3033   gtk_style_properties_register_property (NULL,
3034                                           g_param_spec_boxed ("background-image",
3035                                                               "Background Image",
3036                                                               "Background Image",
3037                                                               CAIRO_GOBJECT_TYPE_PATTERN, 0));
3038   gtk_style_properties_register_property (NULL,
3039                                           g_param_spec_boxed ("background-repeat",
3040                                                               "Background repeat",
3041                                                               "Background repeat",
3042                                                               GTK_TYPE_CSS_BACKGROUND_REPEAT, 0));
3043
3044   gtk_style_properties_register_property (NULL,
3045                                           g_param_spec_boxed ("border-image-source",
3046                                                               "Border image source",
3047                                                               "Border image source",
3048                                                               CAIRO_GOBJECT_TYPE_PATTERN, 0));
3049   gtk_style_properties_register_property (NULL,
3050                                           g_param_spec_boxed ("border-image-repeat",
3051                                                               "Border image repeat",
3052                                                               "Border image repeat",
3053                                                               GTK_TYPE_CSS_BORDER_IMAGE_REPEAT, 0));
3054   gtk_style_properties_register_property (NULL,
3055                                           g_param_spec_boxed ("border-image-slice",
3056                                                               "Border image slice",
3057                                                               "Border image slice",
3058                                                               GTK_TYPE_BORDER, 0));
3059   _gtk_style_property_register           (g_param_spec_boxed ("border-image-width",
3060                                                               "Border image width",
3061                                                               "Border image width",
3062                                                               GTK_TYPE_BORDER, 0),
3063                                           0,
3064                                           NULL,
3065                                           NULL,
3066                                           NULL,
3067                                           NULL,
3068                                           NULL,
3069                                           border_image_width_default_value,
3070                                           NULL);
3071   _gtk_style_property_register           (g_param_spec_boxed ("border-image",
3072                                                               "Border Image",
3073                                                               "Border Image",
3074                                                               GTK_TYPE_BORDER_IMAGE, 0),
3075                                           0,
3076                                           NULL,
3077                                           _gtk_border_image_unpack,
3078                                           _gtk_border_image_pack,
3079                                           NULL,
3080                                           NULL,
3081                                           NULL,
3082                                           unset_border_image);
3083   gtk_style_properties_register_property (NULL,
3084                                           g_param_spec_object ("engine",
3085                                                                "Theming Engine",
3086                                                                "Theming Engine",
3087                                                                GTK_TYPE_THEMING_ENGINE, 0));
3088   gtk_style_properties_register_property (NULL,
3089                                           g_param_spec_boxed ("transition",
3090                                                               "Transition animation description",
3091                                                               "Transition animation description",
3092                                                               GTK_TYPE_ANIMATION_DESCRIPTION, 0));
3093
3094   /* Private property holding the binding sets */
3095   _gtk_style_property_register           (g_param_spec_boxed ("gtk-key-bindings",
3096                                                               "Key bindings",
3097                                                               "Key bindings",
3098                                                               G_TYPE_PTR_ARRAY, 0),
3099                                           0,
3100                                           NULL,
3101                                           NULL,
3102                                           NULL,
3103                                           bindings_value_parse,
3104                                           bindings_value_print,
3105                                           NULL,
3106                                           NULL);
3107 }
3108
3109 const GtkStyleProperty *
3110 _gtk_style_property_lookup (const char *name)
3111 {
3112   gtk_style_property_init ();
3113
3114   return g_hash_table_lookup (properties, name);
3115 }
3116
3117 void
3118 _gtk_style_property_register (GParamSpec               *pspec,
3119                               GtkStylePropertyFlags     flags,
3120                               GtkStylePropertyParser    property_parse_func,
3121                               GtkStyleUnpackFunc        unpack_func,
3122                               GtkStylePackFunc          pack_func,
3123                               GtkStyleParseFunc         parse_func,
3124                               GtkStylePrintFunc         print_func,
3125                               GtkStyleDefaultValueFunc  default_value_func,
3126                               GtkStyleUnsetFunc         unset_func)
3127 {
3128   const GtkStyleProperty *existing;
3129   GtkStyleProperty *node;
3130
3131   g_return_if_fail ((pack_func == NULL) == (unpack_func == NULL));
3132
3133   gtk_style_property_init ();
3134
3135   existing = _gtk_style_property_lookup (pspec->name);
3136   if (existing != NULL)
3137     {
3138       g_warning ("Property \"%s\" was already registered with type %s",
3139                  pspec->name, g_type_name (existing->pspec->value_type));
3140       return;
3141     }
3142
3143   node = g_slice_new0 (GtkStyleProperty);
3144   node->flags = flags;
3145   node->pspec = pspec;
3146   node->property_parse_func = property_parse_func;
3147   node->pack_func = pack_func;
3148   node->unpack_func = unpack_func;
3149   node->parse_func = parse_func;
3150   node->print_func = print_func;
3151   node->default_value_func = default_value_func;
3152   node->unset_func = unset_func;
3153
3154   /* pspec owns name */
3155   g_hash_table_insert (properties, (gchar *)pspec->name, node);
3156 }