]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssshorthandpropertyimpl.c
5d93dfd32e428cb5888b406f343be8b1dbe366fa
[~andy/gtk] / gtk / gtkcssshorthandpropertyimpl.c
1 /*
2  * Copyright © 2011 Red Hat Inc.
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.1 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 Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  * Authors: Benjamin Otte <otte@gnome.org>
19  */
20
21 #include "config.h"
22
23 #include "gtkcssshorthandpropertyprivate.h"
24
25 #include <cairo-gobject.h>
26 #include <math.h>
27
28 #include "gtkcssimageprivate.h"
29 #include "gtkcssstylefuncsprivate.h"
30 #include "gtkcsstypesprivate.h"
31 #include "gtkprivatetypebuiltins.h"
32 #include "gtktypebuiltins.h"
33
34 /* this is in case round() is not provided by the compiler, 
35  * such as in the case of C89 compilers, like MSVC
36  */
37 #include "fallback-c89.c"
38
39 /*** PARSING ***/
40
41 static gboolean
42 value_is_done_parsing (GtkCssParser *parser)
43 {
44   return _gtk_css_parser_is_eof (parser) ||
45          _gtk_css_parser_begins_with (parser, ';') ||
46          _gtk_css_parser_begins_with (parser, '}');
47 }
48
49 static gboolean
50 parse_border (GtkCssShorthandProperty *shorthand,
51               GValue                  *values,
52               GtkCssParser            *parser,
53               GFile                   *base)
54 {
55   GValue temp = G_VALUE_INIT;
56   GtkBorder *border;
57
58   g_value_init (&temp, GTK_TYPE_BORDER);
59   if (!_gtk_css_style_parse_value (&temp, parser, base))
60     {
61       g_value_unset (&temp);
62       return FALSE;
63     }
64
65   border = g_value_get_boxed (&temp);
66
67   g_value_init (&values[0], G_TYPE_INT);
68   g_value_init (&values[1], G_TYPE_INT);
69   g_value_init (&values[2], G_TYPE_INT);
70   g_value_init (&values[3], G_TYPE_INT);
71   g_value_set_int (&values[0], border->top);
72   g_value_set_int (&values[1], border->right);
73   g_value_set_int (&values[2], border->bottom);
74   g_value_set_int (&values[3], border->left);
75
76   g_value_unset (&temp);
77
78   return TRUE;
79 }
80                     
81 static gboolean 
82 parse_border_radius (GtkCssShorthandProperty *shorthand,
83                      GValue                  *values,
84                      GtkCssParser            *parser,
85                      GFile                   *base)
86 {
87   GtkCssBorderCornerRadius borders[4];
88   guint i;
89
90   for (i = 0; i < G_N_ELEMENTS (borders); i++)
91     {
92       if (!_gtk_css_parser_try_double (parser, &borders[i].horizontal))
93         break;
94       if (borders[i].horizontal < 0)
95         {
96           _gtk_css_parser_error (parser, "Border radius values cannot be negative");
97           return FALSE;
98         }
99     }
100
101   if (i == 0)
102     {
103       _gtk_css_parser_error (parser, "Expected a number");
104       return FALSE;
105     }
106
107   /* The magic (i - 1) >> 1 below makes it take the correct value
108    * according to spec. Feel free to check the 4 cases */
109   for (; i < G_N_ELEMENTS (borders); i++)
110     borders[i].horizontal = borders[(i - 1) >> 1].horizontal;
111
112   if (_gtk_css_parser_try (parser, "/", TRUE))
113     {
114       for (i = 0; i < G_N_ELEMENTS (borders); i++)
115         {
116           if (!_gtk_css_parser_try_double (parser, &borders[i].vertical))
117             break;
118           if (borders[i].vertical < 0)
119             {
120               _gtk_css_parser_error (parser, "Border radius values cannot be negative");
121               return FALSE;
122             }
123         }
124
125       if (i == 0)
126         {
127           _gtk_css_parser_error (parser, "Expected a number");
128           return FALSE;
129         }
130
131       for (; i < G_N_ELEMENTS (borders); i++)
132         borders[i].vertical = borders[(i - 1) >> 1].vertical;
133
134     }
135   else
136     {
137       for (i = 0; i < G_N_ELEMENTS (borders); i++)
138         borders[i].vertical = borders[i].horizontal;
139     }
140
141   for (i = 0; i < G_N_ELEMENTS (borders); i++)
142     {
143       g_value_init (&values[i], GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
144       g_value_set_boxed (&values[i], &borders[i]);
145     }
146
147   return TRUE;
148 }
149
150 static gboolean 
151 parse_border_color (GtkCssShorthandProperty *shorthand,
152                     GValue                  *values,
153                     GtkCssParser            *parser,
154                     GFile                   *base)
155 {
156   GtkSymbolicColor *symbolic;
157   guint i;
158
159   for (i = 0; i < 4; i++)
160     {
161       symbolic = _gtk_css_parser_read_symbolic_color (parser);
162       if (symbolic == NULL)
163         return FALSE;
164
165       g_value_init (&values[i], GTK_TYPE_SYMBOLIC_COLOR);
166       g_value_set_boxed (&values[i], symbolic);
167
168       if (value_is_done_parsing (parser))
169         break;
170     }
171
172   for (i++; i < 4; i++)
173     {
174       g_value_init (&values[i], GTK_TYPE_SYMBOLIC_COLOR);
175       g_value_copy (&values[(i - 1) >> 1], &values[i]);
176     }
177
178   return TRUE;
179 }
180
181 static gboolean
182 parse_border_style (GtkCssShorthandProperty *shorthand,
183                     GValue                  *values,
184                     GtkCssParser            *parser,
185                     GFile                   *base)
186 {
187   GtkBorderStyle styles[4];
188   guint i;
189
190   for (i = 0; i < 4; i++)
191     {
192       if (!_gtk_css_parser_try_enum (parser, GTK_TYPE_BORDER_STYLE, (int *)&styles[i]))
193         break;
194     }
195
196   if (i == 0)
197     {
198       _gtk_css_parser_error (parser, "Expected a border style");
199       return FALSE;
200     }
201
202   for (; i < G_N_ELEMENTS (styles); i++)
203     styles[i] = styles[(i - 1) >> 1];
204
205   for (i = 0; i < G_N_ELEMENTS (styles); i++)
206     {
207       g_value_init (&values[i], GTK_TYPE_BORDER_STYLE);
208       g_value_set_enum (&values[i], styles[i]);
209     }
210
211   return TRUE;
212 }
213
214 static gboolean
215 parse_border_image (GtkCssShorthandProperty *shorthand,
216                     GValue                  *values,
217                     GtkCssParser            *parser,
218                     GFile                   *base)
219 {
220   GtkCssImage *image;
221   
222   if (_gtk_css_parser_try (parser, "none", TRUE))
223     image = NULL;
224   else
225     {
226       image = _gtk_css_image_new_parse (parser, base);
227       if (!image)
228         return FALSE;
229     }
230   g_value_init (&values[0], GTK_TYPE_CSS_IMAGE);
231   g_value_set_object (&values[0], image);
232
233   if (value_is_done_parsing (parser))
234     return TRUE;
235
236   g_value_init (&values[1], GTK_TYPE_BORDER);
237   if (!_gtk_css_style_parse_value (&values[1], parser, base))
238     return FALSE;
239
240   if (_gtk_css_parser_try (parser, "/", TRUE))
241     {
242       g_value_init (&values[2], GTK_TYPE_BORDER);
243       if (!_gtk_css_style_parse_value (&values[2], parser, base))
244         return FALSE;
245     }
246
247   if (value_is_done_parsing (parser))
248     return TRUE;
249
250   g_value_init (&values[3], GTK_TYPE_CSS_BORDER_IMAGE_REPEAT);
251   if (!_gtk_css_style_parse_value (&values[3], parser, base))
252     return FALSE;
253
254   return TRUE;
255 }
256
257 static gboolean
258 parse_font (GtkCssShorthandProperty *shorthand,
259             GValue                  *values,
260             GtkCssParser            *parser,
261             GFile                   *base)
262 {
263   PangoFontDescription *desc;
264   guint mask;
265   char *str;
266
267   str = _gtk_css_parser_read_value (parser);
268   if (str == NULL)
269     return FALSE;
270
271   desc = pango_font_description_from_string (str);
272   g_free (str);
273
274   mask = pango_font_description_get_set_fields (desc);
275
276   if (mask & PANGO_FONT_MASK_FAMILY)
277     {
278       GPtrArray *strv = g_ptr_array_new ();
279
280       g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (desc)));
281       g_ptr_array_add (strv, NULL);
282       g_value_init (&values[0], G_TYPE_STRV);
283       g_value_take_boxed (&values[0], g_ptr_array_free (strv, FALSE));
284     }
285   if (mask & PANGO_FONT_MASK_STYLE)
286     {
287       g_value_init (&values[1], PANGO_TYPE_STYLE);
288       g_value_set_enum (&values[1], pango_font_description_get_style (desc));
289     }
290   if (mask & PANGO_FONT_MASK_VARIANT)
291     {
292       g_value_init (&values[2], PANGO_TYPE_VARIANT);
293       g_value_set_enum (&values[2], pango_font_description_get_variant (desc));
294     }
295   if (mask & PANGO_FONT_MASK_WEIGHT)
296     {
297       g_value_init (&values[3], PANGO_TYPE_WEIGHT);
298       g_value_set_enum (&values[3], pango_font_description_get_weight (desc));
299     }
300   if (mask & PANGO_FONT_MASK_SIZE)
301     {
302       g_value_init (&values[4], G_TYPE_DOUBLE);
303       g_value_set_double (&values[4],
304                           (double) pango_font_description_get_size (desc) / PANGO_SCALE);
305     }
306
307   pango_font_description_free (desc);
308
309   return TRUE;
310 }
311
312 static gboolean
313 parse_background (GtkCssShorthandProperty *shorthand,
314                   GValue                  *values,
315                   GtkCssParser            *parser,
316                   GFile                   *base)
317 {
318   int enum_value;
319
320   do
321     {
322       /* the image part */
323       if (!G_IS_VALUE (&values[0]) &&
324           (_gtk_css_parser_has_prefix (parser, "none") ||
325            _gtk_css_image_can_parse (parser)))
326         {
327           GtkCssImage *image;
328
329           if (_gtk_css_parser_try (parser, "none", TRUE))
330             image = NULL;
331           else
332             {
333               image = _gtk_css_image_new_parse (parser, base);
334               if (image == NULL)
335                 return FALSE;
336             }
337
338           g_value_init (&values[0], GTK_TYPE_CSS_IMAGE);
339           g_value_take_object (&values[0], image);
340         }
341       else if (!G_IS_VALUE (&values[1]) &&
342                _gtk_css_parser_try_enum (parser, GTK_TYPE_CSS_BACKGROUND_REPEAT, &enum_value))
343         {
344           if (enum_value <= GTK_CSS_BACKGROUND_REPEAT_MASK)
345             {
346               int vertical;
347
348               if (_gtk_css_parser_try_enum (parser, GTK_TYPE_CSS_BACKGROUND_REPEAT, &vertical))
349                 {
350                   if (vertical >= GTK_CSS_BACKGROUND_REPEAT_MASK)
351                     {
352                       _gtk_css_parser_error (parser, "Not a valid 2nd value for border-repeat");
353                       return FALSE;
354                     }
355                   else
356                     enum_value |= vertical << GTK_CSS_BACKGROUND_REPEAT_SHIFT;
357                 }
358               else
359                 enum_value |= enum_value << GTK_CSS_BACKGROUND_REPEAT_SHIFT;
360             }
361
362           g_value_init (&values[1], GTK_TYPE_CSS_BACKGROUND_REPEAT);
363           g_value_set_enum (&values[1], enum_value);
364         }
365       else if ((!G_IS_VALUE (&values[2]) || !G_IS_VALUE (&values[3])) &&
366                _gtk_css_parser_try_enum (parser, GTK_TYPE_CSS_AREA, &enum_value))
367         {
368           guint idx = !G_IS_VALUE (&values[2]) ? 2 : 3;
369           g_value_init (&values[idx], GTK_TYPE_CSS_AREA);
370           g_value_set_enum (&values[idx], enum_value);
371         }
372       else if (!G_IS_VALUE (&values[4]))
373         {
374           GtkSymbolicColor *symbolic;
375           
376           symbolic = _gtk_css_parser_read_symbolic_color (parser);
377           if (symbolic == NULL)
378             return FALSE;
379
380           g_value_init (&values[4], GTK_TYPE_SYMBOLIC_COLOR);
381           g_value_take_boxed (&values[4], symbolic);
382         }
383       else
384         {
385           /* We parsed everything and there's still stuff left?
386            * Pretend we didn't notice and let the normal code produce
387            * a 'junk at end of value' error */
388           break;
389         }
390     }
391   while (!value_is_done_parsing (parser));
392
393   return TRUE;
394 }
395
396 /*** PACKING ***/
397
398 static GParameter *
399 unpack_border (const GValue *value,
400                guint        *n_params,
401                const char   *top,
402                const char   *left,
403                const char   *bottom,
404                const char   *right)
405 {
406   GParameter *parameter = g_new0 (GParameter, 4);
407   GtkBorder *border = g_value_get_boxed (value);
408
409   parameter[0].name = top;
410   g_value_init (&parameter[0].value, G_TYPE_INT);
411   g_value_set_int (&parameter[0].value, border->top);
412   parameter[1].name = left;
413   g_value_init (&parameter[1].value, G_TYPE_INT);
414   g_value_set_int (&parameter[1].value, border->left);
415   parameter[2].name = bottom;
416   g_value_init (&parameter[2].value, G_TYPE_INT);
417   g_value_set_int (&parameter[2].value, border->bottom);
418   parameter[3].name = right;
419   g_value_init (&parameter[3].value, G_TYPE_INT);
420   g_value_set_int (&parameter[3].value, border->right);
421
422   *n_params = 4;
423   return parameter;
424 }
425
426 static void
427 pack_border (GValue             *value,
428              GtkStyleProperties *props,
429              GtkStateFlags       state,
430              const char         *top,
431              const char         *left,
432              const char         *bottom,
433              const char         *right)
434 {
435   GtkBorder border;
436   int t, l, b, r;
437
438   gtk_style_properties_get (props,
439                             state,
440                             top, &t,
441                             left, &l,
442                             bottom, &b,
443                             right, &r,
444                             NULL);
445
446   border.top = t;
447   border.left = l;
448   border.bottom = b;
449   border.right = r;
450
451   g_value_set_boxed (value, &border);
452 }
453
454 static GParameter *
455 unpack_border_width (const GValue *value,
456                      guint        *n_params)
457 {
458   return unpack_border (value, n_params,
459                         "border-top-width", "border-left-width",
460                         "border-bottom-width", "border-right-width");
461 }
462
463 static void
464 pack_border_width (GValue             *value,
465                    GtkStyleProperties *props,
466                    GtkStateFlags       state)
467 {
468   pack_border (value, props, state,
469                "border-top-width", "border-left-width",
470                "border-bottom-width", "border-right-width");
471 }
472
473 static GParameter *
474 unpack_padding (const GValue *value,
475                 guint        *n_params)
476 {
477   return unpack_border (value, n_params,
478                         "padding-top", "padding-left",
479                         "padding-bottom", "padding-right");
480 }
481
482 static void
483 pack_padding (GValue             *value,
484               GtkStyleProperties *props,
485               GtkStateFlags       state)
486 {
487   pack_border (value, props, state,
488                "padding-top", "padding-left",
489                "padding-bottom", "padding-right");
490 }
491
492 static GParameter *
493 unpack_margin (const GValue *value,
494                guint        *n_params)
495 {
496   return unpack_border (value, n_params,
497                         "margin-top", "margin-left",
498                         "margin-bottom", "margin-right");
499 }
500
501 static void
502 pack_margin (GValue             *value,
503              GtkStyleProperties *props,
504              GtkStateFlags       state)
505 {
506   pack_border (value, props, state,
507                "margin-top", "margin-left",
508                "margin-bottom", "margin-right");
509 }
510
511 static GParameter *
512 unpack_border_radius (const GValue *value,
513                       guint        *n_params)
514 {
515   GParameter *parameter = g_new0 (GParameter, 4);
516   GtkCssBorderCornerRadius border;
517   
518   border.horizontal = border.vertical = g_value_get_int (value);
519
520   parameter[0].name = "border-top-left-radius";
521   g_value_init (&parameter[0].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
522   g_value_set_boxed (&parameter[0].value, &border);
523   parameter[1].name = "border-top-right-radius";
524   g_value_init (&parameter[1].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
525   g_value_set_boxed (&parameter[1].value, &border);
526   parameter[2].name = "border-bottom-right-radius";
527   g_value_init (&parameter[2].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
528   g_value_set_boxed (&parameter[2].value, &border);
529   parameter[3].name = "border-bottom-left-radius";
530   g_value_init (&parameter[3].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
531   g_value_set_boxed (&parameter[3].value, &border);
532
533   *n_params = 4;
534   return parameter;
535 }
536
537 static void
538 pack_border_radius (GValue             *value,
539                     GtkStyleProperties *props,
540                     GtkStateFlags       state)
541 {
542   GtkCssBorderCornerRadius *top_left;
543
544   /* NB: We are an int property, so we have to resolve to an int here.
545    * So we just resolve to an int. We pick one and stick to it.
546    * Lesson learned: Don't query border-radius shorthand, query the 
547    * real properties instead. */
548   gtk_style_properties_get (props,
549                             state,
550                             "border-top-left-radius", &top_left,
551                             NULL);
552
553   if (top_left)
554     g_value_set_int (value, top_left->horizontal);
555
556   g_free (top_left);
557 }
558
559 static GParameter *
560 unpack_font_description (const GValue *value,
561                          guint        *n_params)
562 {
563   GParameter *parameter = g_new0 (GParameter, 5);
564   PangoFontDescription *description;
565   PangoFontMask mask;
566   guint n;
567   
568   /* For backwards compat, we only unpack values that are indeed set.
569    * For strict CSS conformance we need to unpack all of them.
570    * Note that we do set all of them in the parse function, so it
571    * will not have effects when parsing CSS files. It will though
572    * for custom style providers.
573    */
574
575   description = g_value_get_boxed (value);
576   n = 0;
577
578   if (description)
579     mask = pango_font_description_get_set_fields (description);
580   else
581     mask = 0;
582
583   if (mask & PANGO_FONT_MASK_FAMILY)
584     {
585       GPtrArray *strv = g_ptr_array_new ();
586
587       g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (description)));
588       g_ptr_array_add (strv, NULL);
589       parameter[n].name = "font-family";
590       g_value_init (&parameter[n].value, G_TYPE_STRV);
591       g_value_take_boxed (&parameter[n].value,
592                           g_ptr_array_free (strv, FALSE));
593       n++;
594     }
595
596   if (mask & PANGO_FONT_MASK_STYLE)
597     {
598       parameter[n].name = "font-style";
599       g_value_init (&parameter[n].value, PANGO_TYPE_STYLE);
600       g_value_set_enum (&parameter[n].value,
601                         pango_font_description_get_style (description));
602       n++;
603     }
604
605   if (mask & PANGO_FONT_MASK_VARIANT)
606     {
607       parameter[n].name = "font-variant";
608       g_value_init (&parameter[n].value, PANGO_TYPE_VARIANT);
609       g_value_set_enum (&parameter[n].value,
610                         pango_font_description_get_variant (description));
611       n++;
612     }
613
614   if (mask & PANGO_FONT_MASK_WEIGHT)
615     {
616       parameter[n].name = "font-weight";
617       g_value_init (&parameter[n].value, PANGO_TYPE_WEIGHT);
618       g_value_set_enum (&parameter[n].value,
619                         pango_font_description_get_weight (description));
620       n++;
621     }
622
623   if (mask & PANGO_FONT_MASK_SIZE)
624     {
625       parameter[n].name = "font-size";
626       g_value_init (&parameter[n].value, G_TYPE_DOUBLE);
627       g_value_set_double (&parameter[n].value,
628                           (double) pango_font_description_get_size (description) / PANGO_SCALE);
629       n++;
630     }
631
632   *n_params = n;
633
634   return parameter;
635 }
636
637 static void
638 pack_font_description (GValue             *value,
639                        GtkStyleProperties *props,
640                        GtkStateFlags       state)
641 {
642   PangoFontDescription *description;
643   char **families;
644   PangoStyle style;
645   PangoVariant variant;
646   PangoWeight weight;
647   double size;
648
649   gtk_style_properties_get (props,
650                             state,
651                             "font-family", &families,
652                             "font-style", &style,
653                             "font-variant", &variant,
654                             "font-weight", &weight,
655                             "font-size", &size,
656                             NULL);
657
658   description = pango_font_description_new ();
659   /* xxx: Can we set all the families here somehow? */
660   if (families)
661     pango_font_description_set_family (description, families[0]);
662   pango_font_description_set_size (description, round (size * PANGO_SCALE));
663   pango_font_description_set_style (description, style);
664   pango_font_description_set_variant (description, variant);
665   pango_font_description_set_weight (description, weight);
666
667   g_strfreev (families);
668
669   g_value_take_boxed (value, description);
670 }
671
672 static GParameter *
673 unpack_border_color (const GValue *value,
674                      guint        *n_params)
675 {
676   GParameter *parameter = g_new0 (GParameter, 4);
677   GType type;
678   
679   type = G_VALUE_TYPE (value);
680   if (type == G_TYPE_PTR_ARRAY)
681     type = GTK_TYPE_SYMBOLIC_COLOR;
682
683   parameter[0].name = "border-top-color";
684   g_value_init (&parameter[0].value, type);
685   parameter[1].name = "border-right-color";
686   g_value_init (&parameter[1].value, type);
687   parameter[2].name = "border-bottom-color";
688   g_value_init (&parameter[2].value, type);
689   parameter[3].name = "border-left-color";
690   g_value_init (&parameter[3].value, type);
691
692   if (G_VALUE_TYPE (value) == G_TYPE_PTR_ARRAY)
693     {
694       GPtrArray *array = g_value_get_boxed (value);
695       guint i;
696
697       for (i = 0; i < 4; i++)
698         g_value_set_boxed (&parameter[i].value, g_ptr_array_index (array, i));
699     }
700   else
701     {
702       /* can be RGBA or symbolic color */
703       gpointer p = g_value_get_boxed (value);
704
705       g_value_set_boxed (&parameter[0].value, p);
706       g_value_set_boxed (&parameter[1].value, p);
707       g_value_set_boxed (&parameter[2].value, p);
708       g_value_set_boxed (&parameter[3].value, p);
709     }
710
711   *n_params = 4;
712   return parameter;
713 }
714
715 static void
716 pack_border_color (GValue             *value,
717                    GtkStyleProperties *props,
718                    GtkStateFlags       state)
719 {
720   /* NB: We are a color property, so we have to resolve to a color here.
721    * So we just resolve to a color. We pick one and stick to it.
722    * Lesson learned: Don't query border-color shorthand, query the 
723    * real properties instead. */
724   g_value_unset (value);
725   gtk_style_properties_get_property (props, "border-top-color", state, value);
726 }
727
728 static GParameter *
729 unpack_border_style (const GValue *value,
730                      guint        *n_params)
731 {
732   GParameter *parameter = g_new0 (GParameter, 4);
733   GtkBorderStyle style;
734
735   style = g_value_get_enum (value);
736
737   parameter[0].name = "border-top-style";
738   g_value_init (&parameter[0].value, GTK_TYPE_BORDER_STYLE);
739   g_value_set_enum (&parameter[0].value, style);
740   parameter[1].name = "border-right-style";
741   g_value_init (&parameter[1].value, GTK_TYPE_BORDER_STYLE);
742   g_value_set_enum (&parameter[1].value, style);
743   parameter[2].name = "border-bottom-style";
744   g_value_init (&parameter[2].value, GTK_TYPE_BORDER_STYLE);
745   g_value_set_enum (&parameter[2].value, style);
746   parameter[3].name = "border-left-style";
747   g_value_init (&parameter[3].value, GTK_TYPE_BORDER_STYLE);
748   g_value_set_enum (&parameter[3].value, style);
749
750   *n_params = 4;
751   return parameter;
752 }
753
754 static void
755 pack_border_style (GValue             *value,
756                    GtkStyleProperties *props,
757                    GtkStateFlags       state)
758 {
759   /* NB: We can just resolve to a style. We pick one and stick to it.
760    * Lesson learned: Don't query border-style shorthand, query the
761    * real properties instead. */
762   g_value_unset (value);
763   gtk_style_properties_get_property (props, "border-top-style", state, value);
764 }
765
766 static void
767 _gtk_css_shorthand_property_register (const char                        *name,
768                                       GType                              value_type,
769                                       const char                       **subproperties,
770                                       GtkCssShorthandPropertyParseFunc   parse_func,
771                                       GtkStyleUnpackFunc                 unpack_func,
772                                       GtkStylePackFunc                   pack_func)
773 {
774   GtkStyleProperty *node;
775
776   node = g_object_new (GTK_TYPE_CSS_SHORTHAND_PROPERTY,
777                        "name", name,
778                        "value-type", value_type,
779                        "subproperties", subproperties,
780                        NULL);
781
782   GTK_CSS_SHORTHAND_PROPERTY (node)->parse = parse_func;
783   node->pack_func = pack_func;
784   node->unpack_func = unpack_func;
785 }
786
787 void
788 _gtk_css_shorthand_property_init_properties (void)
789 {
790   /* The order is important here, be careful when changing it */
791   const char *font_subproperties[] = { "font-family", "font-style", "font-variant", "font-weight", "font-size", NULL };
792   const char *margin_subproperties[] = { "margin-top", "margin-right", "margin-bottom", "margin-left", NULL };
793   const char *padding_subproperties[] = { "padding-top", "padding-right", "padding-bottom", "padding-left", NULL };
794   const char *border_width_subproperties[] = { "border-top-width", "border-right-width", "border-bottom-width", "border-left-width", NULL };
795   const char *border_radius_subproperties[] = { "border-top-left-radius", "border-top-right-radius",
796                                                 "border-bottom-right-radius", "border-bottom-left-radius", NULL };
797   const char *border_color_subproperties[] = { "border-top-color", "border-right-color", "border-bottom-color", "border-left-color", NULL };
798   const char *border_style_subproperties[] = { "border-top-style", "border-right-style", "border-bottom-style", "border-left-style", NULL };
799   const char *border_image_subproperties[] = { "border-image-source", "border-image-slice", "border-image-width", "border-image-repeat", NULL };
800   const char *background_subproperties[] = { "background-image", "background-repeat", "background-clip", "background-origin",
801                                              "background-color", NULL };
802
803   _gtk_css_shorthand_property_register   ("font",
804                                           PANGO_TYPE_FONT_DESCRIPTION,
805                                           font_subproperties,
806                                           parse_font,
807                                           unpack_font_description,
808                                           pack_font_description);
809   _gtk_css_shorthand_property_register   ("margin",
810                                           GTK_TYPE_BORDER,
811                                           margin_subproperties,
812                                           parse_border,
813                                           unpack_margin,
814                                           pack_margin);
815   _gtk_css_shorthand_property_register   ("padding",
816                                           GTK_TYPE_BORDER,
817                                           padding_subproperties,
818                                           parse_border,
819                                           unpack_padding,
820                                           pack_padding);
821   _gtk_css_shorthand_property_register   ("border-width",
822                                           GTK_TYPE_BORDER,
823                                           border_width_subproperties,
824                                           parse_border,
825                                           unpack_border_width,
826                                           pack_border_width);
827   _gtk_css_shorthand_property_register   ("border-radius",
828                                           G_TYPE_INT,
829                                           border_radius_subproperties,
830                                           parse_border_radius,
831                                           unpack_border_radius,
832                                           pack_border_radius);
833   _gtk_css_shorthand_property_register   ("border-color",
834                                           GDK_TYPE_RGBA,
835                                           border_color_subproperties,
836                                           parse_border_color,
837                                           unpack_border_color,
838                                           pack_border_color);
839   _gtk_css_shorthand_property_register   ("border-style",
840                                           GTK_TYPE_BORDER_STYLE,
841                                           border_style_subproperties,
842                                           parse_border_style,
843                                           unpack_border_style,
844                                           pack_border_style);
845   _gtk_css_shorthand_property_register   ("border-image",
846                                           G_TYPE_NONE,
847                                           border_image_subproperties,
848                                           parse_border_image,
849                                           NULL,
850                                           NULL);
851   _gtk_css_shorthand_property_register   ("background",
852                                           G_TYPE_NONE,
853                                           background_subproperties,
854                                           parse_background,
855                                           NULL,
856                                           NULL);
857 }