]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssshorthandpropertyimpl.c
shorthand: Implement border parsing with new parse func
[~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 "gtkborderimageprivate.h"
29 #include "gtkcssstylefuncsprivate.h"
30 #include "gtkcsstypesprivate.h"
31
32 /* this is in case round() is not provided by the compiler, 
33  * such as in the case of C89 compilers, like MSVC
34  */
35 #include "fallback-c89.c"
36
37 /*** PARSING ***/
38
39 static gboolean
40 parse_border (GtkCssShorthandProperty *shorthand,
41               GValue                  *values,
42               GtkCssParser            *parser,
43               GFile                   *base)
44 {
45   GValue temp = G_VALUE_INIT;
46   GtkBorder *border;
47
48   g_value_init (&temp, GTK_TYPE_BORDER);
49   if (!_gtk_css_style_parse_value (&temp, parser, base))
50     {
51       g_value_unset (&temp);
52       return FALSE;
53     }
54
55   border = g_value_get_boxed (&temp);
56
57   g_value_init (&values[0], G_TYPE_INT);
58   g_value_init (&values[1], G_TYPE_INT);
59   g_value_init (&values[2], G_TYPE_INT);
60   g_value_init (&values[3], G_TYPE_INT);
61   g_value_set_int (&values[0], border->top);
62   g_value_set_int (&values[1], border->right);
63   g_value_set_int (&values[2], border->bottom);
64   g_value_set_int (&values[3], border->left);
65
66   g_value_unset (&temp);
67
68   return TRUE;
69 }
70                     
71 /*** OLD PARSING ***/
72
73 static gboolean
74 border_image_value_parse (GtkCssParser *parser,
75                           GFile *base,
76                           GValue *value)
77 {
78   GValue temp = G_VALUE_INIT;
79   cairo_pattern_t *pattern = NULL;
80   gconstpointer *boxed = NULL;
81   GType boxed_type;
82   GtkBorder slice, *width = NULL, *parsed_slice;
83   GtkCssBorderImageRepeat repeat, *parsed_repeat;
84   gboolean retval = FALSE;
85   GtkBorderImage *image = NULL;
86
87   if (_gtk_css_parser_try (parser, "none", TRUE))
88     return TRUE;
89
90   g_value_init (&temp, CAIRO_GOBJECT_TYPE_PATTERN);
91
92   if (!_gtk_css_style_parse_value (&temp, parser, base))
93     return FALSE;
94
95   boxed_type = G_VALUE_TYPE (&temp);
96   if (boxed_type != CAIRO_GOBJECT_TYPE_PATTERN)
97     boxed = g_value_dup_boxed (&temp);
98   else
99     pattern = g_value_dup_boxed (&temp);
100
101   g_value_unset (&temp);
102   g_value_init (&temp, GTK_TYPE_BORDER);
103
104   if (!_gtk_css_style_parse_value (&temp, parser, base))
105     goto out;
106
107   parsed_slice = g_value_get_boxed (&temp);
108   slice = *parsed_slice;
109
110   if (_gtk_css_parser_try (parser, "/", TRUE))
111     {
112       g_value_unset (&temp);
113       g_value_init (&temp, GTK_TYPE_BORDER);
114
115       if (!_gtk_css_style_parse_value (&temp, parser, base))
116         goto out;
117
118       width = g_value_dup_boxed (&temp);
119     }
120
121   g_value_unset (&temp);
122   g_value_init (&temp, GTK_TYPE_CSS_BORDER_IMAGE_REPEAT);
123
124   if (!_gtk_css_style_parse_value (&temp, parser, base))
125     goto out;
126
127   parsed_repeat = g_value_get_boxed (&temp);
128   repeat = *parsed_repeat;
129
130   g_value_unset (&temp);
131
132   if (boxed != NULL)
133     image = _gtk_border_image_new_for_boxed (boxed_type, boxed, &slice, width, &repeat);
134   else if (pattern != NULL)
135     image = _gtk_border_image_new (pattern, &slice, width, &repeat);
136
137   if (image != NULL)
138     {
139       retval = TRUE;
140       g_value_take_boxed (value, image);
141     }
142
143  out:
144   if (pattern != NULL)
145     cairo_pattern_destroy (pattern);
146
147   if (boxed != NULL)
148     g_boxed_free (boxed_type, boxed);
149
150   if (width != NULL)
151     gtk_border_free (width);
152
153   return retval;
154 }
155
156 static gboolean 
157 border_radius_value_parse (GtkCssParser *parser,
158                            GFile        *base,
159                            GValue       *value)
160 {
161   GtkCssBorderRadius border;
162
163   if (!_gtk_css_parser_try_double (parser, &border.top_left.horizontal))
164     {
165       _gtk_css_parser_error (parser, "Expected a number");
166       return FALSE;
167     }
168   else if (border.top_left.horizontal < 0)
169     goto negative;
170
171   if (_gtk_css_parser_try_double (parser, &border.top_right.horizontal))
172     {
173       if (border.top_right.horizontal < 0)
174         goto negative;
175       if (_gtk_css_parser_try_double (parser, &border.bottom_right.horizontal))
176         {
177           if (border.bottom_right.horizontal < 0)
178             goto negative;
179           if (!_gtk_css_parser_try_double (parser, &border.bottom_left.horizontal))
180             border.bottom_left.horizontal = border.top_right.horizontal;
181           else if (border.bottom_left.horizontal < 0)
182             goto negative;
183         }
184       else
185         {
186           border.bottom_right.horizontal = border.top_left.horizontal;
187           border.bottom_left.horizontal = border.top_right.horizontal;
188         }
189     }
190   else
191     {
192       border.top_right.horizontal = border.top_left.horizontal;
193       border.bottom_right.horizontal = border.top_left.horizontal;
194       border.bottom_left.horizontal = border.top_left.horizontal;
195     }
196
197   if (_gtk_css_parser_try (parser, "/", TRUE))
198     {
199       if (!_gtk_css_parser_try_double (parser, &border.top_left.vertical))
200         {
201           _gtk_css_parser_error (parser, "Expected a number");
202           return FALSE;
203         }
204       else if (border.top_left.vertical < 0)
205         goto negative;
206
207       if (_gtk_css_parser_try_double (parser, &border.top_right.vertical))
208         {
209           if (border.top_right.vertical < 0)
210             goto negative;
211           if (_gtk_css_parser_try_double (parser, &border.bottom_right.vertical))
212             {
213               if (border.bottom_right.vertical < 0)
214                 goto negative;
215               if (!_gtk_css_parser_try_double (parser, &border.bottom_left.vertical))
216                 border.bottom_left.vertical = border.top_right.vertical;
217               else if (border.bottom_left.vertical < 0)
218                 goto negative;
219             }
220           else
221             {
222               border.bottom_right.vertical = border.top_left.vertical;
223               border.bottom_left.vertical = border.top_right.vertical;
224             }
225         }
226       else
227         {
228           border.top_right.vertical = border.top_left.vertical;
229           border.bottom_right.vertical = border.top_left.vertical;
230           border.bottom_left.vertical = border.top_left.vertical;
231         }
232     }
233   else
234     {
235       border.top_left.vertical = border.top_left.horizontal;
236       border.top_right.vertical = border.top_right.horizontal;
237       border.bottom_right.vertical = border.bottom_right.horizontal;
238       border.bottom_left.vertical = border.bottom_left.horizontal;
239     }
240
241   /* border-radius is an int property for backwards-compat reasons */
242   g_value_unset (value);
243   g_value_init (value, GTK_TYPE_CSS_BORDER_RADIUS);
244   g_value_set_boxed (value, &border);
245
246   return TRUE;
247
248 negative:
249   _gtk_css_parser_error (parser, "Border radius values cannot be negative");
250   return FALSE;
251 }
252
253 static gboolean 
254 border_color_shorthand_value_parse (GtkCssParser *parser,
255                                     GFile        *base,
256                                     GValue       *value)
257 {
258   GtkSymbolicColor *symbolic;
259   GPtrArray *array;
260
261   array = g_ptr_array_new_with_free_func ((GDestroyNotify) gtk_symbolic_color_unref);
262
263   do
264     {
265       if (_gtk_css_parser_try (parser, "transparent", TRUE))
266         {
267           GdkRGBA transparent = { 0, 0, 0, 0 };
268           
269           symbolic = gtk_symbolic_color_new_literal (&transparent);
270         }
271       else
272         {
273           symbolic = _gtk_css_parser_read_symbolic_color (parser);
274       
275           if (symbolic == NULL)
276             return FALSE;
277         }
278       
279       g_ptr_array_add (array, symbolic);
280     }
281   while (array->len < 4 && 
282          !_gtk_css_parser_is_eof (parser) &&
283          !_gtk_css_parser_begins_with (parser, ';') &&
284          !_gtk_css_parser_begins_with (parser, '}'));
285
286   switch (array->len)
287     {
288       default:
289         g_assert_not_reached ();
290         break;
291       case 1:
292         g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 0)));
293         /* fall through */
294       case 2:
295         g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 0)));
296         /* fall through */
297       case 3:
298         g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 1)));
299         /* fall through */
300       case 4:
301         break;
302     }
303
304   g_value_unset (value);
305   g_value_init (value, G_TYPE_PTR_ARRAY);
306   g_value_take_boxed (value, array);
307
308   return TRUE;
309 }
310
311 /*** PACKING ***/
312
313 static GParameter *
314 unpack_border (const GValue *value,
315                guint        *n_params,
316                const char   *top,
317                const char   *left,
318                const char   *bottom,
319                const char   *right)
320 {
321   GParameter *parameter = g_new0 (GParameter, 4);
322   GtkBorder *border = g_value_get_boxed (value);
323
324   parameter[0].name = top;
325   g_value_init (&parameter[0].value, G_TYPE_INT);
326   g_value_set_int (&parameter[0].value, border->top);
327   parameter[1].name = left;
328   g_value_init (&parameter[1].value, G_TYPE_INT);
329   g_value_set_int (&parameter[1].value, border->left);
330   parameter[2].name = bottom;
331   g_value_init (&parameter[2].value, G_TYPE_INT);
332   g_value_set_int (&parameter[2].value, border->bottom);
333   parameter[3].name = right;
334   g_value_init (&parameter[3].value, G_TYPE_INT);
335   g_value_set_int (&parameter[3].value, border->right);
336
337   *n_params = 4;
338   return parameter;
339 }
340
341 static void
342 pack_border (GValue             *value,
343              GtkStyleProperties *props,
344              GtkStateFlags       state,
345              const char         *top,
346              const char         *left,
347              const char         *bottom,
348              const char         *right)
349 {
350   GtkBorder border;
351   int t, l, b, r;
352
353   gtk_style_properties_get (props,
354                             state,
355                             top, &t,
356                             left, &l,
357                             bottom, &b,
358                             right, &r,
359                             NULL);
360
361   border.top = t;
362   border.left = l;
363   border.bottom = b;
364   border.right = r;
365
366   g_value_set_boxed (value, &border);
367 }
368
369 static GParameter *
370 unpack_border_width (const GValue *value,
371                      guint        *n_params)
372 {
373   return unpack_border (value, n_params,
374                         "border-top-width", "border-left-width",
375                         "border-bottom-width", "border-right-width");
376 }
377
378 static void
379 pack_border_width (GValue             *value,
380                    GtkStyleProperties *props,
381                    GtkStateFlags       state,
382                    GtkStylePropertyContext *context)
383 {
384   pack_border (value, props, state,
385                "border-top-width", "border-left-width",
386                "border-bottom-width", "border-right-width");
387 }
388
389 static GParameter *
390 unpack_padding (const GValue *value,
391                 guint        *n_params)
392 {
393   return unpack_border (value, n_params,
394                         "padding-top", "padding-left",
395                         "padding-bottom", "padding-right");
396 }
397
398 static void
399 pack_padding (GValue             *value,
400               GtkStyleProperties *props,
401               GtkStateFlags       state,
402               GtkStylePropertyContext *context)
403 {
404   pack_border (value, props, state,
405                "padding-top", "padding-left",
406                "padding-bottom", "padding-right");
407 }
408
409 static GParameter *
410 unpack_margin (const GValue *value,
411                guint        *n_params)
412 {
413   return unpack_border (value, n_params,
414                         "margin-top", "margin-left",
415                         "margin-bottom", "margin-right");
416 }
417
418 static void
419 pack_margin (GValue             *value,
420              GtkStyleProperties *props,
421              GtkStateFlags       state,
422              GtkStylePropertyContext *context)
423 {
424   pack_border (value, props, state,
425                "margin-top", "margin-left",
426                "margin-bottom", "margin-right");
427 }
428
429 static GParameter *
430 unpack_border_radius (const GValue *value,
431                       guint        *n_params)
432 {
433   GParameter *parameter = g_new0 (GParameter, 4);
434   GtkCssBorderRadius *border;
435   
436   if (G_VALUE_HOLDS_BOXED (value))
437     border = g_value_get_boxed (value);
438   else
439     border = NULL;
440
441   parameter[0].name = "border-top-left-radius";
442   g_value_init (&parameter[0].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
443   parameter[1].name = "border-top-right-radius";
444   g_value_init (&parameter[1].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
445   parameter[2].name = "border-bottom-right-radius";
446   g_value_init (&parameter[2].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
447   parameter[3].name = "border-bottom-left-radius";
448   g_value_init (&parameter[3].value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS);
449   if (border)
450     {
451       g_value_set_boxed (&parameter[0].value, &border->top_left);
452       g_value_set_boxed (&parameter[1].value, &border->top_right);
453       g_value_set_boxed (&parameter[2].value, &border->bottom_right);
454       g_value_set_boxed (&parameter[3].value, &border->bottom_left);
455     }
456
457   *n_params = 4;
458   return parameter;
459 }
460
461 static void
462 pack_border_radius (GValue             *value,
463                     GtkStyleProperties *props,
464                     GtkStateFlags       state,
465                     GtkStylePropertyContext *context)
466 {
467   GtkCssBorderCornerRadius *top_left;
468
469   /* NB: We are an int property, so we have to resolve to an int here.
470    * So we just resolve to an int. We pick one and stick to it.
471    * Lesson learned: Don't query border-radius shorthand, query the 
472    * real properties instead. */
473   gtk_style_properties_get (props,
474                             state,
475                             "border-top-left-radius", &top_left,
476                             NULL);
477
478   if (top_left)
479     g_value_set_int (value, top_left->horizontal);
480
481   g_free (top_left);
482 }
483
484 static GParameter *
485 unpack_font_description (const GValue *value,
486                          guint        *n_params)
487 {
488   GParameter *parameter = g_new0 (GParameter, 5);
489   PangoFontDescription *description;
490   PangoFontMask mask;
491   guint n;
492   
493   /* For backwards compat, we only unpack values that are indeed set.
494    * For strict CSS conformance we need to unpack all of them.
495    * Note that we do set all of them in the parse function, so it
496    * will not have effects when parsing CSS files. It will though
497    * for custom style providers.
498    */
499
500   description = g_value_get_boxed (value);
501   n = 0;
502
503   if (description)
504     mask = pango_font_description_get_set_fields (description);
505   else
506     mask = 0;
507
508   if (mask & PANGO_FONT_MASK_FAMILY)
509     {
510       GPtrArray *strv = g_ptr_array_new ();
511
512       g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (description)));
513       g_ptr_array_add (strv, NULL);
514       parameter[n].name = "font-family";
515       g_value_init (&parameter[n].value, G_TYPE_STRV);
516       g_value_take_boxed (&parameter[n].value,
517                           g_ptr_array_free (strv, FALSE));
518       n++;
519     }
520
521   if (mask & PANGO_FONT_MASK_STYLE)
522     {
523       parameter[n].name = "font-style";
524       g_value_init (&parameter[n].value, PANGO_TYPE_STYLE);
525       g_value_set_enum (&parameter[n].value,
526                         pango_font_description_get_style (description));
527       n++;
528     }
529
530   if (mask & PANGO_FONT_MASK_VARIANT)
531     {
532       parameter[n].name = "font-variant";
533       g_value_init (&parameter[n].value, PANGO_TYPE_VARIANT);
534       g_value_set_enum (&parameter[n].value,
535                         pango_font_description_get_variant (description));
536       n++;
537     }
538
539   if (mask & PANGO_FONT_MASK_WEIGHT)
540     {
541       parameter[n].name = "font-weight";
542       g_value_init (&parameter[n].value, PANGO_TYPE_WEIGHT);
543       g_value_set_enum (&parameter[n].value,
544                         pango_font_description_get_weight (description));
545       n++;
546     }
547
548   if (mask & PANGO_FONT_MASK_SIZE)
549     {
550       parameter[n].name = "font-size";
551       g_value_init (&parameter[n].value, G_TYPE_DOUBLE);
552       g_value_set_double (&parameter[n].value,
553                           (double) pango_font_description_get_size (description) / PANGO_SCALE);
554       n++;
555     }
556
557   *n_params = n;
558
559   return parameter;
560 }
561
562 static void
563 pack_font_description (GValue             *value,
564                        GtkStyleProperties *props,
565                        GtkStateFlags       state,
566                        GtkStylePropertyContext *context)
567 {
568   PangoFontDescription *description;
569   char **families;
570   PangoStyle style;
571   PangoVariant variant;
572   PangoWeight weight;
573   double size;
574
575   gtk_style_properties_get (props,
576                             state,
577                             "font-family", &families,
578                             "font-style", &style,
579                             "font-variant", &variant,
580                             "font-weight", &weight,
581                             "font-size", &size,
582                             NULL);
583
584   description = pango_font_description_new ();
585   /* xxx: Can we set all the families here somehow? */
586   if (families)
587     pango_font_description_set_family (description, families[0]);
588   pango_font_description_set_size (description, round (size * PANGO_SCALE));
589   pango_font_description_set_style (description, style);
590   pango_font_description_set_variant (description, variant);
591   pango_font_description_set_weight (description, weight);
592
593   g_strfreev (families);
594
595   g_value_take_boxed (value, description);
596 }
597
598 static GParameter *
599 unpack_border_color (const GValue *value,
600                      guint        *n_params)
601 {
602   GParameter *parameter = g_new0 (GParameter, 4);
603   GType type;
604   
605   type = G_VALUE_TYPE (value);
606   if (type == G_TYPE_PTR_ARRAY)
607     type = GTK_TYPE_SYMBOLIC_COLOR;
608
609   parameter[0].name = "border-top-color";
610   g_value_init (&parameter[0].value, type);
611   parameter[1].name = "border-right-color";
612   g_value_init (&parameter[1].value, type);
613   parameter[2].name = "border-bottom-color";
614   g_value_init (&parameter[2].value, type);
615   parameter[3].name = "border-left-color";
616   g_value_init (&parameter[3].value, type);
617
618   if (G_VALUE_TYPE (value) == G_TYPE_PTR_ARRAY)
619     {
620       GPtrArray *array = g_value_get_boxed (value);
621       guint i;
622
623       for (i = 0; i < 4; i++)
624         g_value_set_boxed (&parameter[i].value, g_ptr_array_index (array, i));
625     }
626   else
627     {
628       /* can be RGBA or symbolic color */
629       gpointer p = g_value_get_boxed (value);
630
631       g_value_set_boxed (&parameter[0].value, p);
632       g_value_set_boxed (&parameter[1].value, p);
633       g_value_set_boxed (&parameter[2].value, p);
634       g_value_set_boxed (&parameter[3].value, p);
635     }
636
637   *n_params = 4;
638   return parameter;
639 }
640
641 static void
642 pack_border_color (GValue             *value,
643                    GtkStyleProperties *props,
644                    GtkStateFlags       state,
645                    GtkStylePropertyContext *context)
646 {
647   /* NB: We are a color property, so we have to resolve to a color here.
648    * So we just resolve to a color. We pick one and stick to it.
649    * Lesson learned: Don't query border-color shorthand, query the 
650    * real properties instead. */
651   g_value_unset (value);
652   gtk_style_properties_get_property (props, "border-top-color", state, value);
653 }
654
655 static void
656 _gtk_css_shorthand_property_register (const char                        *name,
657                                       GType                              value_type,
658                                       const char                       **subproperties,
659                                       GtkCssShorthandPropertyParseFunc   parse_func,
660                                       GtkStyleUnpackFunc                 unpack_func,
661                                       GtkStylePackFunc                   pack_func,
662                                       GtkStyleParseFunc                  old_parse_func)
663 {
664   GtkStyleProperty *node;
665
666   g_return_if_fail (pack_func != NULL);
667   g_return_if_fail (unpack_func != NULL);
668
669   node = g_object_new (GTK_TYPE_CSS_SHORTHAND_PROPERTY,
670                        "name", name,
671                        "value-type", value_type,
672                        "subproperties", subproperties,
673                        NULL);
674
675   if (parse_func)
676     GTK_CSS_SHORTHAND_PROPERTY (node)->parse = parse_func;
677   node->pack_func = pack_func;
678   node->unpack_func = unpack_func;
679   node->parse_func = old_parse_func;
680 }
681
682 void
683 _gtk_css_shorthand_property_init_properties (void)
684 {
685   /* The order is important here, be careful when changing it */
686   const char *font_subproperties[] = { "font-family", "font-style", "font-variant", "font-weight", "font-size", NULL };
687   const char *margin_subproperties[] = { "margin-top", "margin-right", "margin-bottom", "margin-left", NULL };
688   const char *padding_subproperties[] = { "padding-top", "padding-right", "padding-bottom", "padding-left", NULL };
689   const char *border_width_subproperties[] = { "border-top-width", "border-right-width", "border-bottom-width", "border-left-width", NULL };
690   const char *border_radius_subproperties[] = { "border-top-left-radius", "border-top-right-radius",
691                                                 "border-bottom-right-radius", "border-bottom-left-radius", NULL };
692   const char *border_color_subproperties[] = { "border-top-color", "border-right-color", "border-bottom-color", "border-left-color", NULL };
693   const char *border_image_subproperties[] = { "border-image-source", "border-image-slice", "border-image-width", "border-image-repeat", NULL };
694
695   _gtk_css_shorthand_property_register   ("font",
696                                           PANGO_TYPE_FONT_DESCRIPTION,
697                                           font_subproperties,
698                                           NULL,
699                                           unpack_font_description,
700                                           pack_font_description,
701                                           NULL);
702   _gtk_css_shorthand_property_register   ("margin",
703                                           GTK_TYPE_BORDER,
704                                           margin_subproperties,
705                                           parse_border,
706                                           unpack_margin,
707                                           pack_margin,
708                                           NULL);
709   _gtk_css_shorthand_property_register   ("padding",
710                                           GTK_TYPE_BORDER,
711                                           padding_subproperties,
712                                           parse_border,
713                                           unpack_padding,
714                                           pack_padding,
715                                           NULL);
716   _gtk_css_shorthand_property_register   ("border-width",
717                                           GTK_TYPE_BORDER,
718                                           border_width_subproperties,
719                                           parse_border,
720                                           unpack_border_width,
721                                           pack_border_width,
722                                           NULL);
723   _gtk_css_shorthand_property_register   ("border-radius",
724                                           G_TYPE_INT,
725                                           border_radius_subproperties,
726                                           NULL,
727                                           unpack_border_radius,
728                                           pack_border_radius,
729                                           border_radius_value_parse);
730   _gtk_css_shorthand_property_register   ("border-color",
731                                           GDK_TYPE_RGBA,
732                                           border_color_subproperties,
733                                           NULL,
734                                           unpack_border_color,
735                                           pack_border_color,
736                                           border_color_shorthand_value_parse);
737   _gtk_css_shorthand_property_register   ("border-image",
738                                           GTK_TYPE_BORDER_IMAGE,
739                                           border_image_subproperties,
740                                           NULL,
741                                           _gtk_border_image_unpack,
742                                           _gtk_border_image_pack,
743                                           border_image_value_parse);
744 }