]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssshorthandpropertyimpl.c
Add the border css shorthand
[~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_width (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_border (GtkCssShorthandProperty *shorthand,
259               GValue                  *values,
260               GtkCssParser            *parser,
261               GFile                   *base)
262 {
263   int width;
264   int style;
265
266   do
267   {
268     if (!G_IS_VALUE (&values[0]) &&
269          _gtk_css_parser_try_length (parser, &width))
270       {
271         g_value_init (&values[0], G_TYPE_INT);
272         g_value_init (&values[1], G_TYPE_INT);
273         g_value_init (&values[2], G_TYPE_INT);
274         g_value_init (&values[3], G_TYPE_INT);
275         g_value_set_int (&values[0], width);
276         g_value_set_int (&values[1], width);
277         g_value_set_int (&values[2], width);
278         g_value_set_int (&values[3], width);
279       }
280     else if (!G_IS_VALUE (&values[4]) &&
281              _gtk_css_parser_try_enum (parser, GTK_TYPE_BORDER_STYLE, &style))
282       {
283         g_value_init (&values[4], GTK_TYPE_BORDER_STYLE);
284         g_value_init (&values[5], GTK_TYPE_BORDER_STYLE);
285         g_value_init (&values[6], GTK_TYPE_BORDER_STYLE);
286         g_value_init (&values[7], GTK_TYPE_BORDER_STYLE);
287         g_value_set_enum (&values[4], style);
288         g_value_set_enum (&values[5], style);
289         g_value_set_enum (&values[6], style);
290         g_value_set_enum (&values[7], style);
291       }
292     else if (!G_IS_VALUE (&values[8]))
293       {
294         GtkSymbolicColor *symbolic;
295
296         symbolic = _gtk_css_parser_read_symbolic_color (parser);
297         if (symbolic == NULL)
298           return FALSE;
299
300         g_value_init (&values[8], GTK_TYPE_SYMBOLIC_COLOR);
301         g_value_init (&values[9], GTK_TYPE_SYMBOLIC_COLOR);
302         g_value_init (&values[10], GTK_TYPE_SYMBOLIC_COLOR);
303         g_value_init (&values[11], GTK_TYPE_SYMBOLIC_COLOR);
304         g_value_set_boxed (&values[8], symbolic);
305         g_value_set_boxed (&values[9], symbolic);
306         g_value_set_boxed (&values[10], symbolic);
307         g_value_take_boxed (&values[11], symbolic);
308       }
309     else
310       {
311         /* We parsed everything and there's still stuff left?
312          * Pretend we didn't notice and let the normal code produce
313          * a 'junk at end of value' error */
314         break;
315       }
316   }
317   while (!value_is_done_parsing (parser));
318
319   /* Note that border-image values are not set: according to the spec
320      they just need to be reset when using the border shorthand */
321
322   return TRUE;
323 }
324
325 static gboolean
326 parse_font (GtkCssShorthandProperty *shorthand,
327             GValue                  *values,
328             GtkCssParser            *parser,
329             GFile                   *base)
330 {
331   PangoFontDescription *desc;
332   guint mask;
333   char *str;
334
335   str = _gtk_css_parser_read_value (parser);
336   if (str == NULL)
337     return FALSE;
338
339   desc = pango_font_description_from_string (str);
340   g_free (str);
341
342   mask = pango_font_description_get_set_fields (desc);
343
344   if (mask & PANGO_FONT_MASK_FAMILY)
345     {
346       GPtrArray *strv = g_ptr_array_new ();
347
348       g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (desc)));
349       g_ptr_array_add (strv, NULL);
350       g_value_init (&values[0], G_TYPE_STRV);
351       g_value_take_boxed (&values[0], g_ptr_array_free (strv, FALSE));
352     }
353   if (mask & PANGO_FONT_MASK_STYLE)
354     {
355       g_value_init (&values[1], PANGO_TYPE_STYLE);
356       g_value_set_enum (&values[1], pango_font_description_get_style (desc));
357     }
358   if (mask & PANGO_FONT_MASK_VARIANT)
359     {
360       g_value_init (&values[2], PANGO_TYPE_VARIANT);
361       g_value_set_enum (&values[2], pango_font_description_get_variant (desc));
362     }
363   if (mask & PANGO_FONT_MASK_WEIGHT)
364     {
365       g_value_init (&values[3], PANGO_TYPE_WEIGHT);
366       g_value_set_enum (&values[3], pango_font_description_get_weight (desc));
367     }
368   if (mask & PANGO_FONT_MASK_SIZE)
369     {
370       g_value_init (&values[4], G_TYPE_DOUBLE);
371       g_value_set_double (&values[4],
372                           (double) pango_font_description_get_size (desc) / PANGO_SCALE);
373     }
374
375   pango_font_description_free (desc);
376
377   return TRUE;
378 }
379
380 static gboolean
381 parse_background (GtkCssShorthandProperty *shorthand,
382                   GValue                  *values,
383                   GtkCssParser            *parser,
384                   GFile                   *base)
385 {
386   int enum_value;
387
388   do
389     {
390       /* the image part */
391       if (!G_IS_VALUE (&values[0]) &&
392           (_gtk_css_parser_has_prefix (parser, "none") ||
393            _gtk_css_image_can_parse (parser)))
394         {
395           GtkCssImage *image;
396
397           if (_gtk_css_parser_try (parser, "none", TRUE))
398             image = NULL;
399           else
400             {
401               image = _gtk_css_image_new_parse (parser, base);
402               if (image == NULL)
403                 return FALSE;
404             }
405
406           g_value_init (&values[0], GTK_TYPE_CSS_IMAGE);
407           g_value_take_object (&values[0], image);
408         }
409       else if (!G_IS_VALUE (&values[1]) &&
410                _gtk_css_parser_try_enum (parser, GTK_TYPE_CSS_BACKGROUND_REPEAT, &enum_value))
411         {
412           if (enum_value <= GTK_CSS_BACKGROUND_REPEAT_MASK)
413             {
414               int vertical;
415
416               if (_gtk_css_parser_try_enum (parser, GTK_TYPE_CSS_BACKGROUND_REPEAT, &vertical))
417                 {
418                   if (vertical >= GTK_CSS_BACKGROUND_REPEAT_MASK)
419                     {
420                       _gtk_css_parser_error (parser, "Not a valid 2nd value for border-repeat");
421                       return FALSE;
422                     }
423                   else
424                     enum_value |= vertical << GTK_CSS_BACKGROUND_REPEAT_SHIFT;
425                 }
426               else
427                 enum_value |= enum_value << GTK_CSS_BACKGROUND_REPEAT_SHIFT;
428             }
429
430           g_value_init (&values[1], GTK_TYPE_CSS_BACKGROUND_REPEAT);
431           g_value_set_enum (&values[1], enum_value);
432         }
433       else if ((!G_IS_VALUE (&values[2]) || !G_IS_VALUE (&values[3])) &&
434                _gtk_css_parser_try_enum (parser, GTK_TYPE_CSS_AREA, &enum_value))
435         {
436           guint idx = !G_IS_VALUE (&values[2]) ? 2 : 3;
437           g_value_init (&values[idx], GTK_TYPE_CSS_AREA);
438           g_value_set_enum (&values[idx], enum_value);
439         }
440       else if (!G_IS_VALUE (&values[4]))
441         {
442           GtkSymbolicColor *symbolic;
443           
444           symbolic = _gtk_css_parser_read_symbolic_color (parser);
445           if (symbolic == NULL)
446             return FALSE;
447
448           g_value_init (&values[4], GTK_TYPE_SYMBOLIC_COLOR);
449           g_value_take_boxed (&values[4], symbolic);
450         }
451       else
452         {
453           /* We parsed everything and there's still stuff left?
454            * Pretend we didn't notice and let the normal code produce
455            * a 'junk at end of value' error */
456           break;
457         }
458     }
459   while (!value_is_done_parsing (parser));
460
461   return TRUE;
462 }
463
464 /*** PACKING ***/
465
466 static GParameter *
467 unpack_border (const GValue *value,
468                guint        *n_params,
469                const char   *top,
470                const char   *left,
471                const char   *bottom,
472                const char   *right)
473 {
474   GParameter *parameter = g_new0 (GParameter, 4);
475   GtkBorder *border = g_value_get_boxed (value);
476
477   parameter[0].name = top;
478   g_value_init (&parameter[0].value, G_TYPE_INT);
479   g_value_set_int (&parameter[0].value, border->top);
480   parameter[1].name = left;
481   g_value_init (&parameter[1].value, G_TYPE_INT);
482   g_value_set_int (&parameter[1].value, border->left);
483   parameter[2].name = bottom;
484   g_value_init (&parameter[2].value, G_TYPE_INT);
485   g_value_set_int (&parameter[2].value, border->bottom);
486   parameter[3].name = right;
487   g_value_init (&parameter[3].value, G_TYPE_INT);
488   g_value_set_int (&parameter[3].value, border->right);
489
490   *n_params = 4;
491   return parameter;
492 }
493
494 static void
495 pack_border (GValue             *value,
496              GtkStyleProperties *props,
497              GtkStateFlags       state,
498              const char         *top,
499              const char         *left,
500              const char         *bottom,
501              const char         *right)
502 {
503   GtkBorder border;
504   int t, l, b, r;
505
506   gtk_style_properties_get (props,
507                             state,
508                             top, &t,
509                             left, &l,
510                             bottom, &b,
511                             right, &r,
512                             NULL);
513
514   border.top = t;
515   border.left = l;
516   border.bottom = b;
517   border.right = r;
518
519   g_value_set_boxed (value, &border);
520 }
521
522 static GParameter *
523 unpack_border_width (const GValue *value,
524                      guint        *n_params)
525 {
526   return unpack_border (value, n_params,
527                         "border-top-width", "border-left-width",
528                         "border-bottom-width", "border-right-width");
529 }
530
531 static void
532 pack_border_width (GValue             *value,
533                    GtkStyleProperties *props,
534                    GtkStateFlags       state)
535 {
536   pack_border (value, props, state,
537                "border-top-width", "border-left-width",
538                "border-bottom-width", "border-right-width");
539 }
540
541 static GParameter *
542 unpack_padding (const GValue *value,
543                 guint        *n_params)
544 {
545   return unpack_border (value, n_params,
546                         "padding-top", "padding-left",
547                         "padding-bottom", "padding-right");
548 }
549
550 static void
551 pack_padding (GValue             *value,
552               GtkStyleProperties *props,
553               GtkStateFlags       state)
554 {
555   pack_border (value, props, state,
556                "padding-top", "padding-left",
557                "padding-bottom", "padding-right");
558 }
559
560 static GParameter *
561 unpack_margin (const GValue *value,
562                guint        *n_params)
563 {
564   return unpack_border (value, n_params,
565                         "margin-top", "margin-left",
566                         "margin-bottom", "margin-right");
567 }
568
569 static void
570 pack_margin (GValue             *value,
571              GtkStyleProperties *props,
572              GtkStateFlags       state)
573 {
574   pack_border (value, props, state,
575                "margin-top", "margin-left",
576                "margin-bottom", "margin-right");
577 }
578
579 static GParameter *
580 unpack_border_radius (const GValue *value,
581                       guint        *n_params)
582 {
583   GParameter *parameter = g_new0 (GParameter, 4);
584   GtkCssBorderCornerRadius border;
585   
586   border.horizontal = border.vertical = g_value_get_int (value);
587
588   parameter[0].name = "border-top-left-radius";
589   g_value_init (&parameter[0].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
590   g_value_set_boxed (&parameter[0].value, &border);
591   parameter[1].name = "border-top-right-radius";
592   g_value_init (&parameter[1].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
593   g_value_set_boxed (&parameter[1].value, &border);
594   parameter[2].name = "border-bottom-right-radius";
595   g_value_init (&parameter[2].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
596   g_value_set_boxed (&parameter[2].value, &border);
597   parameter[3].name = "border-bottom-left-radius";
598   g_value_init (&parameter[3].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
599   g_value_set_boxed (&parameter[3].value, &border);
600
601   *n_params = 4;
602   return parameter;
603 }
604
605 static void
606 pack_border_radius (GValue             *value,
607                     GtkStyleProperties *props,
608                     GtkStateFlags       state)
609 {
610   GtkCssBorderCornerRadius *top_left;
611
612   /* NB: We are an int property, so we have to resolve to an int here.
613    * So we just resolve to an int. We pick one and stick to it.
614    * Lesson learned: Don't query border-radius shorthand, query the 
615    * real properties instead. */
616   gtk_style_properties_get (props,
617                             state,
618                             "border-top-left-radius", &top_left,
619                             NULL);
620
621   if (top_left)
622     g_value_set_int (value, top_left->horizontal);
623
624   g_free (top_left);
625 }
626
627 static GParameter *
628 unpack_font_description (const GValue *value,
629                          guint        *n_params)
630 {
631   GParameter *parameter = g_new0 (GParameter, 5);
632   PangoFontDescription *description;
633   PangoFontMask mask;
634   guint n;
635   
636   /* For backwards compat, we only unpack values that are indeed set.
637    * For strict CSS conformance we need to unpack all of them.
638    * Note that we do set all of them in the parse function, so it
639    * will not have effects when parsing CSS files. It will though
640    * for custom style providers.
641    */
642
643   description = g_value_get_boxed (value);
644   n = 0;
645
646   if (description)
647     mask = pango_font_description_get_set_fields (description);
648   else
649     mask = 0;
650
651   if (mask & PANGO_FONT_MASK_FAMILY)
652     {
653       GPtrArray *strv = g_ptr_array_new ();
654
655       g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (description)));
656       g_ptr_array_add (strv, NULL);
657       parameter[n].name = "font-family";
658       g_value_init (&parameter[n].value, G_TYPE_STRV);
659       g_value_take_boxed (&parameter[n].value,
660                           g_ptr_array_free (strv, FALSE));
661       n++;
662     }
663
664   if (mask & PANGO_FONT_MASK_STYLE)
665     {
666       parameter[n].name = "font-style";
667       g_value_init (&parameter[n].value, PANGO_TYPE_STYLE);
668       g_value_set_enum (&parameter[n].value,
669                         pango_font_description_get_style (description));
670       n++;
671     }
672
673   if (mask & PANGO_FONT_MASK_VARIANT)
674     {
675       parameter[n].name = "font-variant";
676       g_value_init (&parameter[n].value, PANGO_TYPE_VARIANT);
677       g_value_set_enum (&parameter[n].value,
678                         pango_font_description_get_variant (description));
679       n++;
680     }
681
682   if (mask & PANGO_FONT_MASK_WEIGHT)
683     {
684       parameter[n].name = "font-weight";
685       g_value_init (&parameter[n].value, PANGO_TYPE_WEIGHT);
686       g_value_set_enum (&parameter[n].value,
687                         pango_font_description_get_weight (description));
688       n++;
689     }
690
691   if (mask & PANGO_FONT_MASK_SIZE)
692     {
693       parameter[n].name = "font-size";
694       g_value_init (&parameter[n].value, G_TYPE_DOUBLE);
695       g_value_set_double (&parameter[n].value,
696                           (double) pango_font_description_get_size (description) / PANGO_SCALE);
697       n++;
698     }
699
700   *n_params = n;
701
702   return parameter;
703 }
704
705 static void
706 pack_font_description (GValue             *value,
707                        GtkStyleProperties *props,
708                        GtkStateFlags       state)
709 {
710   PangoFontDescription *description;
711   char **families;
712   PangoStyle style;
713   PangoVariant variant;
714   PangoWeight weight;
715   double size;
716
717   gtk_style_properties_get (props,
718                             state,
719                             "font-family", &families,
720                             "font-style", &style,
721                             "font-variant", &variant,
722                             "font-weight", &weight,
723                             "font-size", &size,
724                             NULL);
725
726   description = pango_font_description_new ();
727   /* xxx: Can we set all the families here somehow? */
728   if (families)
729     pango_font_description_set_family (description, families[0]);
730   pango_font_description_set_size (description, round (size * PANGO_SCALE));
731   pango_font_description_set_style (description, style);
732   pango_font_description_set_variant (description, variant);
733   pango_font_description_set_weight (description, weight);
734
735   g_strfreev (families);
736
737   g_value_take_boxed (value, description);
738 }
739
740 static GParameter *
741 unpack_border_color (const GValue *value,
742                      guint        *n_params)
743 {
744   GParameter *parameter = g_new0 (GParameter, 4);
745   GType type;
746   
747   type = G_VALUE_TYPE (value);
748   if (type == G_TYPE_PTR_ARRAY)
749     type = GTK_TYPE_SYMBOLIC_COLOR;
750
751   parameter[0].name = "border-top-color";
752   g_value_init (&parameter[0].value, type);
753   parameter[1].name = "border-right-color";
754   g_value_init (&parameter[1].value, type);
755   parameter[2].name = "border-bottom-color";
756   g_value_init (&parameter[2].value, type);
757   parameter[3].name = "border-left-color";
758   g_value_init (&parameter[3].value, type);
759
760   if (G_VALUE_TYPE (value) == G_TYPE_PTR_ARRAY)
761     {
762       GPtrArray *array = g_value_get_boxed (value);
763       guint i;
764
765       for (i = 0; i < 4; i++)
766         g_value_set_boxed (&parameter[i].value, g_ptr_array_index (array, i));
767     }
768   else
769     {
770       /* can be RGBA or symbolic color */
771       gpointer p = g_value_get_boxed (value);
772
773       g_value_set_boxed (&parameter[0].value, p);
774       g_value_set_boxed (&parameter[1].value, p);
775       g_value_set_boxed (&parameter[2].value, p);
776       g_value_set_boxed (&parameter[3].value, p);
777     }
778
779   *n_params = 4;
780   return parameter;
781 }
782
783 static void
784 pack_border_color (GValue             *value,
785                    GtkStyleProperties *props,
786                    GtkStateFlags       state)
787 {
788   /* NB: We are a color property, so we have to resolve to a color here.
789    * So we just resolve to a color. We pick one and stick to it.
790    * Lesson learned: Don't query border-color shorthand, query the 
791    * real properties instead. */
792   g_value_unset (value);
793   gtk_style_properties_get_property (props, "border-top-color", state, value);
794 }
795
796 static GParameter *
797 unpack_border_style (const GValue *value,
798                      guint        *n_params)
799 {
800   GParameter *parameter = g_new0 (GParameter, 4);
801   GtkBorderStyle style;
802
803   style = g_value_get_enum (value);
804
805   parameter[0].name = "border-top-style";
806   g_value_init (&parameter[0].value, GTK_TYPE_BORDER_STYLE);
807   g_value_set_enum (&parameter[0].value, style);
808   parameter[1].name = "border-right-style";
809   g_value_init (&parameter[1].value, GTK_TYPE_BORDER_STYLE);
810   g_value_set_enum (&parameter[1].value, style);
811   parameter[2].name = "border-bottom-style";
812   g_value_init (&parameter[2].value, GTK_TYPE_BORDER_STYLE);
813   g_value_set_enum (&parameter[2].value, style);
814   parameter[3].name = "border-left-style";
815   g_value_init (&parameter[3].value, GTK_TYPE_BORDER_STYLE);
816   g_value_set_enum (&parameter[3].value, style);
817
818   *n_params = 4;
819   return parameter;
820 }
821
822 static void
823 pack_border_style (GValue             *value,
824                    GtkStyleProperties *props,
825                    GtkStateFlags       state)
826 {
827   /* NB: We can just resolve to a style. We pick one and stick to it.
828    * Lesson learned: Don't query border-style shorthand, query the
829    * real properties instead. */
830   g_value_unset (value);
831   gtk_style_properties_get_property (props, "border-top-style", state, value);
832 }
833
834 static void
835 _gtk_css_shorthand_property_register (const char                        *name,
836                                       GType                              value_type,
837                                       const char                       **subproperties,
838                                       GtkCssShorthandPropertyParseFunc   parse_func,
839                                       GtkStyleUnpackFunc                 unpack_func,
840                                       GtkStylePackFunc                   pack_func)
841 {
842   GtkStyleProperty *node;
843
844   node = g_object_new (GTK_TYPE_CSS_SHORTHAND_PROPERTY,
845                        "name", name,
846                        "value-type", value_type,
847                        "subproperties", subproperties,
848                        NULL);
849
850   GTK_CSS_SHORTHAND_PROPERTY (node)->parse = parse_func;
851   node->pack_func = pack_func;
852   node->unpack_func = unpack_func;
853 }
854
855 void
856 _gtk_css_shorthand_property_init_properties (void)
857 {
858   /* The order is important here, be careful when changing it */
859   const char *font_subproperties[] = { "font-family", "font-style", "font-variant", "font-weight", "font-size", NULL };
860   const char *margin_subproperties[] = { "margin-top", "margin-right", "margin-bottom", "margin-left", NULL };
861   const char *padding_subproperties[] = { "padding-top", "padding-right", "padding-bottom", "padding-left", NULL };
862   const char *border_width_subproperties[] = { "border-top-width", "border-right-width", "border-bottom-width", "border-left-width", NULL };
863   const char *border_radius_subproperties[] = { "border-top-left-radius", "border-top-right-radius",
864                                                 "border-bottom-right-radius", "border-bottom-left-radius", NULL };
865   const char *border_color_subproperties[] = { "border-top-color", "border-right-color", "border-bottom-color", "border-left-color", NULL };
866   const char *border_style_subproperties[] = { "border-top-style", "border-right-style", "border-bottom-style", "border-left-style", NULL };
867   const char *border_image_subproperties[] = { "border-image-source", "border-image-slice", "border-image-width", "border-image-repeat", NULL };
868   const char *border_subproperties[] = { "border-top-width", "border-right-width", "border-bottom-width", "border-left-width",
869                                          "border-top-style", "border-right-style", "border-bottom-style", "border-left-style",
870                                          "border-top-color", "border-right-color", "border-bottom-color", "border-left-color",
871                                          "border-image-source", "border-image-slice", "border-image-width", "border-image-repeat", NULL };
872   const char *background_subproperties[] = { "background-image", "background-repeat", "background-clip", "background-origin",
873                                              "background-color", NULL };
874
875   _gtk_css_shorthand_property_register   ("font",
876                                           PANGO_TYPE_FONT_DESCRIPTION,
877                                           font_subproperties,
878                                           parse_font,
879                                           unpack_font_description,
880                                           pack_font_description);
881   _gtk_css_shorthand_property_register   ("margin",
882                                           GTK_TYPE_BORDER,
883                                           margin_subproperties,
884                                           parse_border_width,
885                                           unpack_margin,
886                                           pack_margin);
887   _gtk_css_shorthand_property_register   ("padding",
888                                           GTK_TYPE_BORDER,
889                                           padding_subproperties,
890                                           parse_border_width,
891                                           unpack_padding,
892                                           pack_padding);
893   _gtk_css_shorthand_property_register   ("border-width",
894                                           GTK_TYPE_BORDER,
895                                           border_width_subproperties,
896                                           parse_border_width,
897                                           unpack_border_width,
898                                           pack_border_width);
899   _gtk_css_shorthand_property_register   ("border-radius",
900                                           G_TYPE_INT,
901                                           border_radius_subproperties,
902                                           parse_border_radius,
903                                           unpack_border_radius,
904                                           pack_border_radius);
905   _gtk_css_shorthand_property_register   ("border-color",
906                                           GDK_TYPE_RGBA,
907                                           border_color_subproperties,
908                                           parse_border_color,
909                                           unpack_border_color,
910                                           pack_border_color);
911   _gtk_css_shorthand_property_register   ("border-style",
912                                           GTK_TYPE_BORDER_STYLE,
913                                           border_style_subproperties,
914                                           parse_border_style,
915                                           unpack_border_style,
916                                           pack_border_style);
917   _gtk_css_shorthand_property_register   ("border-image",
918                                           G_TYPE_NONE,
919                                           border_image_subproperties,
920                                           parse_border_image,
921                                           NULL,
922                                           NULL);
923   _gtk_css_shorthand_property_register   ("border",
924                                           G_TYPE_NONE,
925                                           border_subproperties,
926                                           parse_border,
927                                           NULL,
928                                           NULL);
929   _gtk_css_shorthand_property_register   ("background",
930                                           G_TYPE_NONE,
931                                           background_subproperties,
932                                           parse_background,
933                                           NULL,
934                                           NULL);
935 }