]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkstyleproperty.c
Rename s/GtkCssRepeatStyle/GtkCssBorderRepeatStyle/g
[~andy/gtk] / gtk / gtkstyleproperty.c
index a6bf2345633ce3f6884477781a5175d74908d2fa..69972936297d643c88b74ad32522d9bac7446498 100644 (file)
 /* the actual parsers we have */
 #include "gtkanimationdescription.h"
 #include "gtkbindings.h"
-#include "gtk9slice.h"
+#include "gtkborderimageprivate.h"
 #include "gtkgradient.h"
 #include "gtkshadowprivate.h"
 #include "gtkthemingengine.h"
 #include "gtktypebuiltins.h"
 
+/* this is in case round() is not provided by the compiler, 
+ * such as in the case of C89 compilers, like MSVC
+ */
+#include "fallback-c89.c"
+
 static GHashTable *parse_funcs = NULL;
 static GHashTable *print_funcs = NULL;
 static GHashTable *properties = NULL;
@@ -302,6 +307,7 @@ font_description_value_parse (GtkCssParser *parser,
                               GValue       *value)
 {
   PangoFontDescription *font_desc;
+  guint mask;
   char *str;
 
   str = _gtk_css_parser_read_value (parser);
@@ -309,6 +315,13 @@ font_description_value_parse (GtkCssParser *parser,
     return FALSE;
 
   font_desc = pango_font_description_from_string (str);
+  mask = pango_font_description_get_set_fields (font_desc);
+  /* These values are not really correct,
+   * but the fields must be set, so we set them to something */
+  if ((mask & PANGO_FONT_MASK_FAMILY) == 0)
+    pango_font_description_set_family_static (font_desc, "Sans");
+  if ((mask & PANGO_FONT_MASK_SIZE) == 0)
+    pango_font_description_set_size (font_desc, 10 * PANGO_SCALE);
   g_free (str);
   g_value_take_boxed (value, font_desc);
   return TRUE;
@@ -523,7 +536,7 @@ theming_engine_value_print (const GValue *value,
     {
       /* XXX: gtk_theming_engine_get_name()? */
       g_object_get (engine, "name", &name, NULL);
-      g_string_append (string, name);
+      g_string_append (string, name ? name : "none");
       g_free (name);
     }
 }
@@ -850,7 +863,7 @@ gtk_css_parse_url (GtkCssParser *parser,
               
               error = g_error_new_literal (GTK_CSS_PROVIDER_ERROR,
                                            GTK_CSS_PROVIDER_ERROR_DEPRECATED,
-                                           "Whitespace between 'url' and '(' is not allowed");
+                                           "Whitespace between 'url' and '(' is deprecated");
                              
               _gtk_css_parser_take_error (parser, error);
             }
@@ -948,43 +961,139 @@ pattern_value_parse (GtkCssParser *parser,
   return TRUE;
 }
 
+static cairo_status_t
+surface_write (void                *closure,
+               const unsigned char *data,
+               unsigned int         length)
+{
+  g_byte_array_append (closure, data, length);
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+surface_print (cairo_surface_t *surface,
+               GString *        string)
+{
+#if CAIRO_HAS_PNG_FUNCTIONS
+  GByteArray *array;
+  char *base64;
+  
+  array = g_byte_array_new ();
+  cairo_surface_write_to_png_stream (surface, surface_write, array);
+  base64 = g_base64_encode (array->data, array->len);
+  g_byte_array_free (array, TRUE);
+
+  g_string_append (string, "url(\"data:image/png;base64,");
+  g_string_append (string, base64);
+  g_string_append (string, "\")");
+
+  g_free (base64);
+#else
+  g_string_append (string, "none /* you need cairo png functions enabled to make this work */");
+#endif
+}
+
+static void
+pattern_value_print (const GValue *value,
+                     GString      *string)
+{
+  cairo_pattern_t *pattern;
+  cairo_surface_t *surface;
+
+  pattern = g_value_get_boxed (value);
+
+  if (pattern == NULL)
+    {
+      g_string_append (string, "none");
+      return;
+    }
+
+  switch (cairo_pattern_get_type (pattern))
+    {
+    case CAIRO_PATTERN_TYPE_SURFACE:
+      if (cairo_pattern_get_surface (pattern, &surface) != CAIRO_STATUS_SUCCESS)
+        {
+          g_assert_not_reached ();
+        }
+      surface_print (surface, string);
+      break;
+    case CAIRO_PATTERN_TYPE_SOLID:
+    case CAIRO_PATTERN_TYPE_LINEAR:
+    case CAIRO_PATTERN_TYPE_RADIAL:
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+}
+
 static gboolean
 shadow_value_parse (GtkCssParser *parser,
                     GFile *base,
                     GValue *value)
 {
-  gboolean inset;
+  gboolean have_inset, have_color, have_lengths;
   gdouble hoffset, voffset, blur, spread;
   GtkSymbolicColor *color;
   GtkShadow *shadow;
+  guint i;
 
   shadow = _gtk_shadow_new ();
 
   do
     {
-      inset = _gtk_css_parser_try (parser, "inset", TRUE);
+      have_inset = have_lengths = have_color = FALSE;
 
-      if (!_gtk_css_parser_try_double (parser, &hoffset) ||
-          !_gtk_css_parser_try_double (parser, &voffset))
+      for (i = 0; i < 3; i++)
         {
-          _gtk_css_parser_error (parser, "Horizontal and vertical offsets are required");
-          _gtk_shadow_unref (shadow);
-          return FALSE;
-        }
+          if (!have_inset && 
+              _gtk_css_parser_try (parser, "inset", TRUE))
+            {
+              have_inset = TRUE;
+              continue;
+            }
+            
+          if (!have_lengths &&
+              _gtk_css_parser_try_double (parser, &hoffset))
+            {
+              have_lengths = TRUE;
 
-      if (!_gtk_css_parser_try_double (parser, &blur))
-        blur = 0;
+              if (!_gtk_css_parser_try_double (parser, &voffset))
+                {
+                  _gtk_css_parser_error (parser, "Horizontal and vertical offsets are required");
+                  _gtk_shadow_unref (shadow);
+                  return FALSE;
+                }
 
-      if (!_gtk_css_parser_try_double (parser, &spread))
-        spread = 0;
+              if (!_gtk_css_parser_try_double (parser, &blur))
+                blur = 0;
 
-      /* XXX: the color is optional and UA-defined if it's missing,
-       * but it doesn't really make sense for us...
-       */
-      color = _gtk_css_parser_read_symbolic_color (parser);
+              if (!_gtk_css_parser_try_double (parser, &spread))
+                spread = 0;
 
-      if (color == NULL)
+              continue;
+            }
+
+          if (!have_color)
+            {
+              have_color = TRUE;
+
+              /* XXX: the color is optional and UA-defined if it's missing,
+               * but it doesn't really make sense for us...
+               */
+              color = _gtk_css_parser_read_symbolic_color (parser);
+
+              if (color == NULL)
+                {
+                  _gtk_shadow_unref (shadow);
+                  return FALSE;
+                }
+            }
+        }
+
+      if (!have_color || !have_lengths)
         {
+          _gtk_css_parser_error (parser, "Must specify at least color and offsets");
           _gtk_shadow_unref (shadow);
           return FALSE;
         }
@@ -992,7 +1101,7 @@ shadow_value_parse (GtkCssParser *parser,
       _gtk_shadow_append (shadow,
                           hoffset, voffset,
                           blur, spread,
-                          inset, color);
+                          have_inset, color);
 
       gtk_symbolic_color_unref (color);
 
@@ -1017,69 +1126,152 @@ shadow_value_print (const GValue *value,
     _gtk_shadow_print (shadow, string);
 }
 
-static gboolean 
-slice_value_parse (GtkCssParser *parser,
-                   GFile        *base,
-                   GValue       *value)
+static gboolean
+border_image_repeat_value_parse (GtkCssParser *parser,
+                                 GFile *file,
+                                 GValue *value)
 {
-  gdouble distance_top, distance_bottom;
-  gdouble distance_left, distance_right;
-  GtkSliceSideModifier mods[2];
-  GdkPixbuf *pixbuf;
-  Gtk9Slice *slice;
-  GFile *file;
-  GError *error = NULL;
+  GtkCssBorderImageRepeat image_repeat;
+  GtkCssBorderRepeatStyle styles[2];
   gint i;
-  char *path;
-
-  /* Parse image url */
-  file = gtk_css_parse_url (parser, base);
-  if (!file)
-      return FALSE;
-
-  if (!_gtk_css_parser_try_double (parser, &distance_top) ||
-      !_gtk_css_parser_try_double (parser, &distance_right) ||
-      !_gtk_css_parser_try_double (parser, &distance_bottom) ||
-      !_gtk_css_parser_try_double (parser, &distance_left))
-    {
-      _gtk_css_parser_error (parser, "Expected a number");
-      g_object_unref (file);
-      return FALSE;
-    }
 
   for (i = 0; i < 2; i++)
     {
       if (_gtk_css_parser_try (parser, "stretch", TRUE))
-        mods[i] = GTK_SLICE_STRETCH;
+        styles[i] = GTK_CSS_REPEAT_STYLE_NONE;
       else if (_gtk_css_parser_try (parser, "repeat", TRUE))
-        mods[i] = GTK_SLICE_REPEAT;
+        styles[i] = GTK_CSS_REPEAT_STYLE_REPEAT;
+      else if (_gtk_css_parser_try (parser, "round", TRUE))
+        styles[i] = GTK_CSS_REPEAT_STYLE_ROUND;
+      else if (_gtk_css_parser_try (parser, "space", TRUE))
+        styles[i] = GTK_CSS_REPEAT_STYLE_SPACE;
       else if (i == 0)
         {
-          mods[1] = mods[0] = GTK_SLICE_STRETCH;
+          styles[1] = styles[0] = GTK_CSS_REPEAT_STYLE_NONE;
           break;
         }
       else
-        mods[i] = mods[0];
+        styles[i] = styles[0];
     }
 
-  path = g_file_get_path (file);
-  g_object_unref (file);
-  pixbuf = gdk_pixbuf_new_from_file (path, &error);
-  g_free (path);
-  if (!pixbuf)
+  image_repeat.hrepeat = styles[0];
+  image_repeat.vrepeat = styles[1];
+
+  g_value_set_boxed (value, &image_repeat);
+
+  return TRUE;
+}
+
+static const gchar *
+border_image_repeat_style_to_string (GtkCssBorderRepeatStyle repeat)
+{
+  switch (repeat)
     {
-      _gtk_css_parser_take_error (parser, error);
-      return FALSE;
+    case GTK_CSS_REPEAT_STYLE_NONE:
+      return "stretch";
+    case GTK_CSS_REPEAT_STYLE_REPEAT:
+      return "repeat";
+    case GTK_CSS_REPEAT_STYLE_ROUND:
+      return "round";
+    case GTK_CSS_REPEAT_STYLE_SPACE:
+      return "space";
+    default:
+      return NULL;
     }
+}
 
-  slice = _gtk_9slice_new (pixbuf,
-                           distance_top, distance_bottom,
-                           distance_left, distance_right,
-                           mods[0], mods[1]);
-  g_object_unref (pixbuf);
+static void
+border_image_repeat_value_print (const GValue *value,
+                                 GString      *string)
+{
+  GtkCssBorderImageRepeat *image_repeat;
 
-  g_value_take_boxed (value, slice);
-  return TRUE;
+  image_repeat = g_value_get_boxed (value);
+
+  g_string_append (string, border_image_repeat_style_to_string (image_repeat->hrepeat));
+  if (image_repeat->hrepeat != image_repeat->vrepeat)
+    {
+      g_string_append (string, " ");
+      g_string_append (string, border_image_repeat_style_to_string (image_repeat->vrepeat));
+    }
+}
+
+static gboolean
+border_image_value_parse (GtkCssParser *parser,
+                          GFile *base,
+                          GValue *value)
+{
+  GValue temp = G_VALUE_INIT;
+  cairo_pattern_t *pattern = NULL;
+  GtkGradient *gradient = NULL;
+  GtkBorder slice, *width = NULL, *parsed_slice;
+  GtkCssBorderImageRepeat repeat, *parsed_repeat;
+  gboolean retval = FALSE;
+  GtkBorderImage *image = NULL;
+
+  g_value_init (&temp, CAIRO_GOBJECT_TYPE_PATTERN);
+
+  if (!pattern_value_parse (parser, base, &temp))
+    return FALSE;
+
+  if (G_VALUE_TYPE (&temp) == GTK_TYPE_GRADIENT)
+    gradient = g_value_dup_boxed (&temp);
+  else
+    pattern = g_value_dup_boxed (&temp);
+
+  g_value_unset (&temp);
+  g_value_init (&temp, GTK_TYPE_BORDER);
+
+  if (!border_value_parse (parser, base, &temp))
+    goto out;
+
+  parsed_slice = g_value_get_boxed (&temp);
+  slice = *parsed_slice;
+
+  if (_gtk_css_parser_try (parser, "/", TRUE))
+    {
+      g_value_unset (&temp);
+      g_value_init (&temp, GTK_TYPE_BORDER);
+
+      if (!border_value_parse (parser, base, &temp))
+        goto out;
+
+      width = g_value_dup_boxed (&temp);
+    }
+
+  g_value_unset (&temp);
+  g_value_init (&temp, GTK_TYPE_CSS_BORDER_IMAGE_REPEAT);
+
+  if (!border_image_repeat_value_parse (parser, base, &temp))
+    goto out;
+
+  parsed_repeat = g_value_get_boxed (&temp);
+  repeat = *parsed_repeat;
+
+  g_value_unset (&temp);
+
+  if (gradient != NULL)
+    image = _gtk_border_image_new_for_gradient (gradient, &slice, width, &repeat);
+  else if (pattern != NULL)
+    image = _gtk_border_image_new (pattern, &slice, width, &repeat);
+
+  if (image != NULL)
+    {
+      retval = TRUE;
+      g_value_take_boxed (value, image);
+    }
+
+ out:
+  if (pattern != NULL)
+    cairo_pattern_destroy (pattern);
+
+  if (gradient != NULL)
+    gtk_gradient_unref (gradient);
+
+  if (width != NULL)
+    gtk_border_free (width);
+
+  return retval;
 }
 
 static gboolean 
@@ -1469,6 +1661,81 @@ border_radius_value_print (const GValue *value,
     }
 }
 
+static gboolean 
+transparent_color_value_parse (GtkCssParser *parser,
+                               GFile        *base,
+                               GValue       *value)
+{
+  if (_gtk_css_parser_try (parser, "transparent", TRUE))
+    {
+      GdkRGBA transparent = { 0, 0, 0, 0 };
+          
+      g_value_set_boxed (value, &transparent);
+
+      return TRUE;
+    }
+
+  return rgba_value_parse (parser, base, value);
+}
+
+static gboolean 
+border_color_shorthand_value_parse (GtkCssParser *parser,
+                                    GFile        *base,
+                                    GValue       *value)
+{
+  GtkSymbolicColor *symbolic;
+  GPtrArray *array;
+
+  array = g_ptr_array_new_with_free_func ((GDestroyNotify) gtk_symbolic_color_unref);
+
+  do
+    {
+      if (_gtk_css_parser_try (parser, "transparent", TRUE))
+        {
+          GdkRGBA transparent = { 0, 0, 0, 0 };
+          
+          symbolic = gtk_symbolic_color_new_literal (&transparent);
+        }
+      else
+        {
+          symbolic = _gtk_css_parser_read_symbolic_color (parser);
+      
+          if (symbolic == NULL)
+            return FALSE;
+        }
+      
+      g_ptr_array_add (array, symbolic);
+    }
+  while (array->len < 4 && 
+         !_gtk_css_parser_is_eof (parser) &&
+         !_gtk_css_parser_begins_with (parser, ';') &&
+         !_gtk_css_parser_begins_with (parser, '}'));
+
+  switch (array->len)
+    {
+      default:
+        g_assert_not_reached ();
+        break;
+      case 1:
+        g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 0)));
+        /* fall through */
+      case 2:
+        g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 0)));
+        /* fall through */
+      case 3:
+        g_ptr_array_add (array, gtk_symbolic_color_ref (g_ptr_array_index (array, 1)));
+        /* fall through */
+      case 4:
+        break;
+    }
+
+  g_value_unset (value);
+  g_value_init (value, G_TYPE_PTR_ARRAY);
+  g_value_take_boxed (value, array);
+
+  return TRUE;
+}
+
 /*** PACKING ***/
 
 static GParameter *
@@ -1746,11 +2013,168 @@ pack_font_description (GValue             *value,
   pango_font_description_set_variant (description, variant);
   pango_font_description_set_weight (description, weight);
 
-  g_free (families);
+  g_strfreev (families);
 
   g_value_take_boxed (value, description);
 }
 
+static GParameter *
+unpack_border_color (const GValue *value,
+                     guint        *n_params)
+{
+  GParameter *parameter = g_new0 (GParameter, 4);
+  GType type;
+  
+  type = G_VALUE_TYPE (value);
+  if (type == G_TYPE_PTR_ARRAY)
+    type = GTK_TYPE_SYMBOLIC_COLOR;
+
+  parameter[0].name = "border-top-color";
+  g_value_init (&parameter[0].value, type);
+  parameter[1].name = "border-right-color";
+  g_value_init (&parameter[1].value, type);
+  parameter[2].name = "border-bottom-color";
+  g_value_init (&parameter[2].value, type);
+  parameter[3].name = "border-left-color";
+  g_value_init (&parameter[3].value, type);
+
+  if (G_VALUE_TYPE (value) == G_TYPE_PTR_ARRAY)
+    {
+      GPtrArray *array = g_value_get_boxed (value);
+      guint i;
+
+      for (i = 0; i < 4; i++)
+        g_value_set_boxed (&parameter[i].value, g_ptr_array_index (array, i));
+    }
+  else
+    {
+      /* can be RGBA or symbolic color */
+      gpointer p = g_value_get_boxed (value);
+
+      g_value_set_boxed (&parameter[0].value, p);
+      g_value_set_boxed (&parameter[1].value, p);
+      g_value_set_boxed (&parameter[2].value, p);
+      g_value_set_boxed (&parameter[3].value, p);
+    }
+
+  *n_params = 4;
+  return parameter;
+}
+
+static void
+pack_border_color (GValue             *value,
+                   GtkStyleProperties *props,
+                   GtkStateFlags       state)
+{
+  /* NB: We are a color property, so we have to resolve to a color here.
+   * So we just resolve to a color. We pick one and stick to it.
+   * Lesson learned: Don't query border-color shorthand, query the 
+   * real properties instead. */
+  g_value_unset (value);
+  gtk_style_properties_get_property (props, "border-top-color", state, value);
+}
+
+/*** UNSET FUNCS ***/
+
+static void
+unset_font_description (GtkStyleProperties *props,
+                        GtkStateFlags       state)
+{
+  gtk_style_properties_unset_property (props, "font-family", state);
+  gtk_style_properties_unset_property (props, "font-style", state);
+  gtk_style_properties_unset_property (props, "font-variant", state);
+  gtk_style_properties_unset_property (props, "font-weight", state);
+  gtk_style_properties_unset_property (props, "font-size", state);
+}
+
+static void
+unset_margin (GtkStyleProperties *props,
+              GtkStateFlags       state)
+{
+  gtk_style_properties_unset_property (props, "margin-top", state);
+  gtk_style_properties_unset_property (props, "margin-right", state);
+  gtk_style_properties_unset_property (props, "margin-bottom", state);
+  gtk_style_properties_unset_property (props, "margin-left", state);
+}
+
+static void
+unset_padding (GtkStyleProperties *props,
+               GtkStateFlags       state)
+{
+  gtk_style_properties_unset_property (props, "padding-top", state);
+  gtk_style_properties_unset_property (props, "padding-right", state);
+  gtk_style_properties_unset_property (props, "padding-bottom", state);
+  gtk_style_properties_unset_property (props, "padding-left", state);
+}
+
+static void
+unset_border_width (GtkStyleProperties *props,
+                    GtkStateFlags       state)
+{
+  gtk_style_properties_unset_property (props, "border-top-width", state);
+  gtk_style_properties_unset_property (props, "border-right-width", state);
+  gtk_style_properties_unset_property (props, "border-bottom-width", state);
+  gtk_style_properties_unset_property (props, "border-left-width", state);
+}
+
+static void
+unset_border_radius (GtkStyleProperties *props,
+                     GtkStateFlags       state)
+{
+  gtk_style_properties_unset_property (props, "border-top-right-radius", state);
+  gtk_style_properties_unset_property (props, "border-bottom-right-radius", state);
+  gtk_style_properties_unset_property (props, "border-bottom-left-radius", state);
+  gtk_style_properties_unset_property (props, "border-top-left-radius", state);
+}
+
+static void
+unset_border_color (GtkStyleProperties *props,
+                    GtkStateFlags       state)
+{
+  gtk_style_properties_unset_property (props, "border-top-color", state);
+  gtk_style_properties_unset_property (props, "border-right-color", state);
+  gtk_style_properties_unset_property (props, "border-bottom-color", state);
+  gtk_style_properties_unset_property (props, "border-left-color", state);
+}
+
+static void
+unset_border_image (GtkStyleProperties *props,
+                    GtkStateFlags       state)
+{
+  gtk_style_properties_unset_property (props, "border-image-source", state);
+  gtk_style_properties_unset_property (props, "border-image-slice", state);
+  gtk_style_properties_unset_property (props, "border-image-repeat", state);
+  gtk_style_properties_unset_property (props, "border-image-width", state);
+}
+
+/*** default values ***/
+
+static void
+border_image_width_default_value (GtkStyleProperties *props,
+                                  GtkStateFlags       state,
+                                  GValue             *value)
+{
+}
+
+static void
+background_color_default_value (GtkStyleProperties *props,
+                                GtkStateFlags       state,
+                                GValue             *value)
+{
+  GdkRGBA transparent_black = { 0, 0, 0, 0 };
+
+  g_value_set_boxed (value, &transparent_black);
+}
+
+static void
+border_color_default_value (GtkStyleProperties *props,
+                            GtkStateFlags       state,
+                            GValue             *value)
+{
+  g_value_unset (value);
+  gtk_style_properties_get_property (props, "color", state, value);
+}
+
 /*** API ***/
 
 static void
@@ -1806,10 +2230,13 @@ css_string_funcs_init (void)
                                 gradient_value_print);
   register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
                                 pattern_value_parse,
+                                pattern_value_print);
+  register_conversion_function (GTK_TYPE_BORDER_IMAGE,
+                                border_image_value_parse,
                                 NULL);
-  register_conversion_function (GTK_TYPE_9SLICE,
-                                slice_value_parse,
-                                NULL);
+  register_conversion_function (GTK_TYPE_CSS_BORDER_IMAGE_REPEAT,
+                                border_image_repeat_value_parse,
+                                border_image_repeat_value_print);
   register_conversion_function (GTK_TYPE_SHADOW,
                                 shadow_value_parse,
                                 shadow_value_print);
@@ -1919,10 +2346,11 @@ _gtk_style_property_print_value (const GtkStyleProperty *property,
 void
 _gtk_style_property_default_value (const GtkStyleProperty *property,
                                    GtkStyleProperties     *properties,
+                                   GtkStateFlags           state,
                                    GValue                 *value)
 {
   if (property->default_value_func)
-    property->default_value_func (properties, value);
+    property->default_value_func (properties, state, value);
   else if (property->pspec->value_type == GTK_TYPE_THEMING_ENGINE)
     g_value_set_object (value, gtk_theming_engine_load (NULL));
   else if (property->pspec->value_type == PANGO_TYPE_FONT_DESCRIPTION)
@@ -1946,7 +2374,132 @@ _gtk_style_property_is_inherit (const GtkStyleProperty *property)
 {
   g_return_val_if_fail (property != NULL, FALSE);
 
-  return gtk_style_param_get_inherit (property->pspec);
+  return property->flags & GTK_STYLE_PROPERTY_INHERIT ? TRUE : FALSE;
+}
+
+static gboolean
+resolve_color (GtkStyleProperties *props,
+              GValue             *value)
+{
+  GdkRGBA color;
+
+  /* Resolve symbolic color to GdkRGBA */
+  if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &color))
+    return FALSE;
+
+  /* Store it back, this is where GdkRGBA caching happens */
+  g_value_unset (value);
+  g_value_init (value, GDK_TYPE_RGBA);
+  g_value_set_boxed (value, &color);
+
+  return TRUE;
+}
+
+static gboolean
+resolve_color_rgb (GtkStyleProperties *props,
+                   GValue             *value)
+{
+  GdkColor color = { 0 };
+  GdkRGBA rgba;
+
+  if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &rgba))
+    return FALSE;
+
+  color.red = rgba.red * 65535. + 0.5;
+  color.green = rgba.green * 65535. + 0.5;
+  color.blue = rgba.blue * 65535. + 0.5;
+
+  g_value_unset (value);
+  g_value_init (value, GDK_TYPE_COLOR);
+  g_value_set_boxed (value, &color);
+
+  return TRUE;
+}
+
+static gboolean
+resolve_gradient (GtkStyleProperties *props,
+                  GValue             *value)
+{
+  cairo_pattern_t *gradient;
+
+  if (!gtk_gradient_resolve (g_value_get_boxed (value), props, &gradient))
+    return FALSE;
+
+  /* Store it back, this is where cairo_pattern_t caching happens */
+  g_value_unset (value);
+  g_value_init (value, CAIRO_GOBJECT_TYPE_PATTERN);
+  g_value_take_boxed (value, gradient);
+
+  return TRUE;
+}
+
+static gboolean
+resolve_shadow (GtkStyleProperties *props,
+                GValue *value)
+{
+  GtkShadow *resolved, *base;
+
+  base = g_value_get_boxed (value);
+
+  if (base == NULL)
+    return TRUE;
+  
+  if (_gtk_shadow_get_resolved (base))
+    return TRUE;
+
+  resolved = _gtk_shadow_resolve (base, props);
+  if (resolved == NULL)
+    return FALSE;
+
+  g_value_take_boxed (value, resolved);
+
+  return TRUE;
+}
+
+void
+_gtk_style_property_resolve (const GtkStyleProperty *property,
+                             GtkStyleProperties     *props,
+                             GtkStateFlags           state,
+                             GValue                 *val)
+{
+  if (G_VALUE_TYPE (val) == GTK_TYPE_SYMBOLIC_COLOR)
+    {
+      if (property->pspec->value_type == GDK_TYPE_RGBA)
+        {
+          if (resolve_color (props, val))
+            return;
+        }
+      else if (property->pspec->value_type == GDK_TYPE_COLOR)
+        {
+          if (resolve_color_rgb (props, val))
+            return;
+        }
+      
+      g_value_unset (val);
+      g_value_init (val, property->pspec->value_type);
+      _gtk_style_property_default_value (property, props, state, val);
+    }
+  else if (G_VALUE_TYPE (val) == GDK_TYPE_RGBA)
+    {
+      if (g_value_get_boxed (val) == NULL)
+        _gtk_style_property_default_value (property, props, state, val);
+    }
+  else if (G_VALUE_TYPE (val) == GTK_TYPE_GRADIENT)
+    {
+      g_return_if_fail (property->pspec->value_type == CAIRO_GOBJECT_TYPE_PATTERN);
+
+      if (!resolve_gradient (props, val))
+        {
+          g_value_unset (val);
+          g_value_init (val, CAIRO_GOBJECT_TYPE_PATTERN);
+          _gtk_style_property_default_value (property, props, state, val);
+        }
+    }
+  else if (G_VALUE_TYPE (val) == GTK_TYPE_SHADOW)
+    {
+      if (!resolve_shadow (props, val))
+        _gtk_style_property_default_value (property, props, state, val);
+    }
 }
 
 gboolean
@@ -1987,8 +2540,6 @@ _gtk_style_property_pack (const GtkStyleProperty *property,
 static void
 gtk_style_property_init (void)
 {
-  GParamSpec *pspec;
-
   if (G_LIKELY (properties))
     return;
 
@@ -1998,79 +2549,139 @@ gtk_style_property_init (void)
   /* note that gtk_style_properties_register_property() calls this function,
    * so make sure we're sanely inited to avoid infloops */
 
-  pspec = g_param_spec_boxed ("color",
-                              "Foreground color",
-                              "Foreground color",
-                              GDK_TYPE_RGBA, 0);
-  gtk_style_param_set_inherit (pspec, TRUE);
-  gtk_style_properties_register_property (NULL, pspec);
-
-  gtk_style_properties_register_property (NULL,
-                                          g_param_spec_boxed ("background-color",
-                                                              "Background color",
-                                                              "Background color",
-                                                              GDK_TYPE_RGBA, 0));
+  _gtk_style_property_register           (g_param_spec_boxed ("color",
+                                          "Foreground color",
+                                          "Foreground color",
+                                          GDK_TYPE_RGBA, 0),
+                                          GTK_STYLE_PROPERTY_INHERIT,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL);
+  _gtk_style_property_register           (g_param_spec_boxed ("background-color",
+                                          "Background color",
+                                          "Background color",
+                                          GDK_TYPE_RGBA, 0),
+                                          0,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          transparent_color_value_parse,
+                                          NULL,
+                                          background_color_default_value,
+                                          NULL);
 
-  pspec = g_param_spec_boxed ("font-family",
-                              "Font family",
-                              "Font family",
-                              G_TYPE_STRV, 0);
-  gtk_style_param_set_inherit (pspec, TRUE);
-  _gtk_style_property_register           (pspec,
+  _gtk_style_property_register           (g_param_spec_boxed ("font-family",
+                                                              "Font family",
+                                                              "Font family",
+                                                              G_TYPE_STRV, 0),
+                                          GTK_STYLE_PROPERTY_INHERIT,
                                           NULL,
                                           NULL,
                                           NULL,
                                           font_family_parse,
                                           font_family_value_print,
+                                          NULL,
+                                          NULL);
+  _gtk_style_property_register           (g_param_spec_enum ("font-style",
+                                                             "Font style",
+                                                             "Font style",
+                                                             PANGO_TYPE_STYLE,
+                                                             PANGO_STYLE_NORMAL, 0),
+                                          GTK_STYLE_PROPERTY_INHERIT,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL);
+  _gtk_style_property_register           (g_param_spec_enum ("font-variant",
+                                                             "Font variant",
+                                                             "Font variant",
+                                                             PANGO_TYPE_VARIANT,
+                                                             PANGO_VARIANT_NORMAL, 0),
+                                          GTK_STYLE_PROPERTY_INHERIT,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
                                           NULL);
-  pspec = g_param_spec_enum ("font-style",
-                             "Font style",
-                             "Font style",
-                             PANGO_TYPE_STYLE,
-                             PANGO_STYLE_NORMAL, 0);
-  gtk_style_param_set_inherit (pspec, TRUE);
-  gtk_style_properties_register_property (NULL, pspec);
-  pspec = g_param_spec_enum ("font-variant",
-                             "Font variant",
-                             "Font variant",
-                             PANGO_TYPE_VARIANT,
-                             PANGO_VARIANT_NORMAL, 0);
-  gtk_style_param_set_inherit (pspec, TRUE);
-  gtk_style_properties_register_property (NULL, pspec);
   /* xxx: need to parse this properly, ie parse the numbers */
-  pspec = g_param_spec_enum ("font-weight",
-                             "Font weight",
-                             "Font weight",
-                             PANGO_TYPE_WEIGHT,
-                             PANGO_WEIGHT_NORMAL, 0);
-  gtk_style_param_set_inherit (pspec, TRUE);
-  gtk_style_properties_register_property (NULL, pspec);
-  pspec = g_param_spec_double ("font-size",
-                               "Font size",
-                               "Font size",
-                               0, G_MAXDOUBLE, 0, 0);
-  gtk_style_param_set_inherit (pspec, TRUE);
-  gtk_style_properties_register_property (NULL, pspec);
-  pspec = g_param_spec_boxed ("font",
-                              "Font Description",
-                              "Font Description",
-                              PANGO_TYPE_FONT_DESCRIPTION, 0);
-  gtk_style_param_set_inherit (pspec, TRUE);
-  _gtk_style_property_register           (pspec,
+  _gtk_style_property_register           (g_param_spec_enum ("font-weight",
+                                                             "Font weight",
+                                                             "Font weight",
+                                                             PANGO_TYPE_WEIGHT,
+                                                             PANGO_WEIGHT_NORMAL, 0),
+                                          GTK_STYLE_PROPERTY_INHERIT,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL);
+  _gtk_style_property_register           (g_param_spec_double ("font-size",
+                                                               "Font size",
+                                                               "Font size",
+                                                               0, G_MAXDOUBLE, 0, 0),
+                                          GTK_STYLE_PROPERTY_INHERIT,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL);
+  _gtk_style_property_register           (g_param_spec_boxed ("font",
+                                                              "Font Description",
+                                                              "Font Description",
+                                                              PANGO_TYPE_FONT_DESCRIPTION, 0),
+                                          GTK_STYLE_PROPERTY_INHERIT,
                                           NULL,
                                           unpack_font_description,
                                           pack_font_description,
                                           font_description_value_parse,
                                           font_description_value_print,
+                                          NULL,
+                                          unset_font_description);
+
+  _gtk_style_property_register           (g_param_spec_boxed ("text-shadow",
+                                                              "Text shadow",
+                                                              "Text shadow",
+                                                              GTK_TYPE_SHADOW, 0),
+                                          GTK_STYLE_PROPERTY_INHERIT,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
                                           NULL);
 
-  pspec = g_param_spec_boxed ("text-shadow",
-                              "Text shadow",
-                              "Text shadow",
-                              GTK_TYPE_SHADOW, 0);
-  gtk_style_param_set_inherit (pspec, TRUE);
-  gtk_style_properties_register_property (NULL, pspec);
+  _gtk_style_property_register           (g_param_spec_boxed ("icon-shadow",
+                                                              "Icon shadow",
+                                                              "Icon shadow",
+                                                              GTK_TYPE_SHADOW, 0),
+                                          GTK_STYLE_PROPERTY_INHERIT,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL);
 
+  gtk_style_properties_register_property (NULL,
+                                          g_param_spec_boxed ("box-shadow",
+                                                              "Box shadow",
+                                                              "Box shadow",
+                                                              GTK_TYPE_SHADOW, 0));
   gtk_style_properties_register_property (NULL,
                                           g_param_spec_int ("margin-top",
                                                             "margin top",
@@ -2095,12 +2706,14 @@ gtk_style_property_init (void)
                                                               "Margin",
                                                               "Margin",
                                                               GTK_TYPE_BORDER, 0),
+                                          0,
                                           NULL,
                                           unpack_margin,
                                           pack_margin,
                                           NULL,
                                           NULL,
-                                          NULL);
+                                          NULL,
+                                          unset_margin);
   gtk_style_properties_register_property (NULL,
                                           g_param_spec_int ("padding-top",
                                                             "padding top",
@@ -2125,12 +2738,14 @@ gtk_style_property_init (void)
                                                               "Padding",
                                                               "Padding",
                                                               GTK_TYPE_BORDER, 0),
+                                          0,
                                           NULL,
                                           unpack_padding,
                                           pack_padding,
                                           NULL,
                                           NULL,
-                                          NULL);
+                                          NULL,
+                                          unset_padding);
   gtk_style_properties_register_property (NULL,
                                           g_param_spec_int ("border-top-width",
                                                             "border top width",
@@ -2155,63 +2770,75 @@ gtk_style_property_init (void)
                                                               "Border width",
                                                               "Border width, in pixels",
                                                               GTK_TYPE_BORDER, 0),
+                                          0,
                                           NULL,
                                           unpack_border_width,
                                           pack_border_width,
                                           NULL,
                                           NULL,
-                                          NULL);
+                                          NULL,
+                                          unset_border_width);
 
   _gtk_style_property_register           (g_param_spec_boxed ("border-top-left-radius",
                                                               "Border top left radius",
                                                               "Border radius of top left corner, in pixels",
                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
+                                          0,
                                           NULL,
                                           NULL,
                                           NULL,
                                           border_corner_radius_value_parse,
                                           border_corner_radius_value_print,
+                                          NULL,
                                           NULL);
   _gtk_style_property_register           (g_param_spec_boxed ("border-top-right-radius",
                                                               "Border top right radius",
                                                               "Border radius of top right corner, in pixels",
                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
+                                          0,
                                           NULL,
                                           NULL,
                                           NULL,
                                           border_corner_radius_value_parse,
                                           border_corner_radius_value_print,
+                                          NULL,
                                           NULL);
   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-right-radius",
                                                               "Border bottom right radius",
                                                               "Border radius of bottom right corner, in pixels",
                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
+                                          0,
                                           NULL,
                                           NULL,
                                           NULL,
                                           border_corner_radius_value_parse,
                                           border_corner_radius_value_print,
+                                          NULL,
                                           NULL);
   _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-left-radius",
                                                               "Border bottom left radius",
                                                               "Border radius of bottom left corner, in pixels",
                                                               GTK_TYPE_CSS_BORDER_CORNER_RADIUS, 0),
+                                          0,
                                           NULL,
                                           NULL,
                                           NULL,
                                           border_corner_radius_value_parse,
                                           border_corner_radius_value_print,
+                                          NULL,
                                           NULL);
   _gtk_style_property_register           (g_param_spec_int ("border-radius",
                                                             "Border radius",
                                                             "Border radius, in pixels",
                                                             0, G_MAXINT, 0, 0),
+                                          0,
                                           NULL,
                                           unpack_border_radius,
                                           pack_border_radius,
                                           border_radius_value_parse,
                                           border_radius_value_print,
-                                          NULL);
+                                          NULL,
+                                          unset_border_radius);
 
   gtk_style_properties_register_property (NULL,
                                           g_param_spec_enum ("border-style",
@@ -2219,21 +2846,111 @@ gtk_style_property_init (void)
                                                              "Border style",
                                                              GTK_TYPE_BORDER_STYLE,
                                                              GTK_BORDER_STYLE_NONE, 0));
-  gtk_style_properties_register_property (NULL,
-                                          g_param_spec_boxed ("border-color",
+  _gtk_style_property_register           (g_param_spec_boxed ("border-top-color",
+                                                              "Border top color",
+                                                              "Border top color",
+                                                              GDK_TYPE_RGBA, 0),
+                                          0,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          transparent_color_value_parse,
+                                          NULL,
+                                          border_color_default_value,
+                                          NULL);
+  _gtk_style_property_register           (g_param_spec_boxed ("border-right-color",
+                                                              "Border right color",
+                                                              "Border right color",
+                                                              GDK_TYPE_RGBA, 0),
+                                          0,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          transparent_color_value_parse,
+                                          NULL,
+                                          border_color_default_value,
+                                          NULL);
+  _gtk_style_property_register           (g_param_spec_boxed ("border-bottom-color",
+                                                              "Border bottom color",
+                                                              "Border bottom color",
+                                                              GDK_TYPE_RGBA, 0),
+                                          0,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          transparent_color_value_parse,
+                                          NULL,
+                                          border_color_default_value,
+                                          NULL);
+  _gtk_style_property_register           (g_param_spec_boxed ("border-left-color",
+                                                              "Border left color",
+                                                              "Border left color",
+                                                              GDK_TYPE_RGBA, 0),
+                                          0,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          transparent_color_value_parse,
+                                          NULL,
+                                          border_color_default_value,
+                                          NULL);
+  _gtk_style_property_register           (g_param_spec_boxed ("border-color",
                                                               "Border color",
                                                               "Border color",
-                                                              GDK_TYPE_RGBA, 0));
+                                                              GDK_TYPE_RGBA, 0),
+                                          0,
+                                          NULL,
+                                          unpack_border_color,
+                                          pack_border_color,
+                                          border_color_shorthand_value_parse,
+                                          NULL,
+                                          NULL,
+                                          unset_border_color);
+
   gtk_style_properties_register_property (NULL,
                                           g_param_spec_boxed ("background-image",
                                                               "Background Image",
                                                               "Background Image",
                                                               CAIRO_GOBJECT_TYPE_PATTERN, 0));
   gtk_style_properties_register_property (NULL,
-                                          g_param_spec_boxed ("border-image",
+                                          g_param_spec_boxed ("border-image-source",
+                                                              "Border image source",
+                                                              "Border image source",
+                                                              CAIRO_GOBJECT_TYPE_PATTERN, 0));
+  gtk_style_properties_register_property (NULL,
+                                          g_param_spec_boxed ("border-image-repeat",
+                                                              "Border image repeat",
+                                                              "Border image repeat",
+                                                              GTK_TYPE_CSS_BORDER_IMAGE_REPEAT, 0));
+  gtk_style_properties_register_property (NULL,
+                                          g_param_spec_boxed ("border-image-slice",
+                                                              "Border image slice",
+                                                              "Border image slice",
+                                                              GTK_TYPE_BORDER, 0));
+  _gtk_style_property_register           (g_param_spec_boxed ("border-image-width",
+                                                              "Border image width",
+                                                              "Border image width",
+                                                              GTK_TYPE_BORDER, 0),
+                                          0,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          border_image_width_default_value,
+                                          NULL);
+  _gtk_style_property_register           (g_param_spec_boxed ("border-image",
                                                               "Border Image",
                                                               "Border Image",
-                                                              GTK_TYPE_9SLICE, 0));
+                                                              GTK_TYPE_BORDER_IMAGE, 0),
+                                          0,
+                                          NULL,
+                                          _gtk_border_image_unpack,
+                                          _gtk_border_image_pack,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          unset_border_image);
   gtk_style_properties_register_property (NULL,
                                           g_param_spec_object ("engine",
                                                                "Theming Engine",
@@ -2250,11 +2967,13 @@ gtk_style_property_init (void)
                                                               "Key bindings",
                                                               "Key bindings",
                                                               G_TYPE_PTR_ARRAY, 0),
+                                          0,
                                           NULL,
                                           NULL,
                                           NULL,
                                           bindings_value_parse,
                                           bindings_value_print,
+                                          NULL,
                                           NULL);
 }
 
@@ -2268,12 +2987,14 @@ _gtk_style_property_lookup (const char *name)
 
 void
 _gtk_style_property_register (GParamSpec               *pspec,
+                              GtkStylePropertyFlags     flags,
                               GtkStylePropertyParser    property_parse_func,
                               GtkStyleUnpackFunc        unpack_func,
                               GtkStylePackFunc          pack_func,
                               GtkStyleParseFunc         parse_func,
                               GtkStylePrintFunc         print_func,
-                              GtkStyleDefaultValueFunc  default_value_func)
+                              GtkStyleDefaultValueFunc  default_value_func,
+                              GtkStyleUnsetFunc         unset_func)
 {
   const GtkStyleProperty *existing;
   GtkStyleProperty *node;
@@ -2291,6 +3012,7 @@ _gtk_style_property_register (GParamSpec               *pspec,
     }
 
   node = g_slice_new0 (GtkStyleProperty);
+  node->flags = flags;
   node->pspec = pspec;
   node->property_parse_func = property_parse_func;
   node->pack_func = pack_func;
@@ -2298,6 +3020,8 @@ _gtk_style_property_register (GParamSpec               *pspec,
   node->parse_func = parse_func;
   node->print_func = print_func;
   node->default_value_func = default_value_func;
+  node->unset_func = unset_func;
 
-  g_hash_table_insert (properties, pspec->name, node);
+  /* pspec owns name */
+  g_hash_table_insert (properties, (gchar *)pspec->name, node);
 }