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