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