hippo-canvas r7242 - in trunk: . common/hippo linux/hippo python tests



Author: otaylor
Date: Mon Mar 24 19:05:00 2008
New Revision: 7242
URL: http://svn.gnome.org/viewvc/hippo-canvas?rev=7242&view=rev

Log:
CSS Theming:
 http://mail.gnome.org/archives/online-desktop-list/2008-March/msg00030.html

configure.ac: Add a dependency on libcroco

hippo-canvas-theme.[ch]: "Theme" API binding together a set
  of stylesheets and a theme engine.

hippo-canvas-style.[ch]: Add API's for getting styled-propertise
  like colors and borders

hippo-canvas-theme-engine.[ch]: Very simple 'theme engine' 
  interface for drawing a named part.

hippo-canvas-*.c: Use the new style engine API's to get themed
  properties

tests/test-theme.py tests/test.css: Simple test of CSS/theme
  functionality


Added:
   trunk/common/hippo/hippo-canvas-theme-engine.c
   trunk/common/hippo/hippo-canvas-theme-engine.h
   trunk/common/hippo/hippo-canvas-theme-internal.h
   trunk/common/hippo/hippo-canvas-theme.c
   trunk/common/hippo/hippo-canvas-theme.h
   trunk/tests/test-theme.py
   trunk/tests/test.css
Modified:
   trunk/Makefile-canvas-sources.am
   trunk/common/hippo/hippo-canvas-box.c
   trunk/common/hippo/hippo-canvas-box.h
   trunk/common/hippo/hippo-canvas-context.c
   trunk/common/hippo/hippo-canvas-context.h
   trunk/common/hippo/hippo-canvas-item.c
   trunk/common/hippo/hippo-canvas-link.c
   trunk/common/hippo/hippo-canvas-link.h
   trunk/common/hippo/hippo-canvas-style.c
   trunk/common/hippo/hippo-canvas-style.h
   trunk/common/hippo/hippo-canvas-text.c
   trunk/common/hippo/hippo-canvas-type-builtins.c
   trunk/common/hippo/hippo-canvas-type-builtins.h
   trunk/configure.ac
   trunk/linux/hippo/hippo-canvas-helper.c
   trunk/linux/hippo/hippo-canvas-helper.h
   trunk/linux/hippo/hippo-canvas-widget.c
   trunk/linux/hippo/hippo-canvas-widget.h
   trunk/linux/hippo/hippo-canvas-widgets.c
   trunk/linux/hippo/hippo-canvas-window.c
   trunk/linux/hippo/hippo-canvas-window.h
   trunk/linux/hippo/hippo-canvas.c
   trunk/linux/hippo/hippo-canvas.h
   trunk/python/hippo.defs
   trunk/python/hippo.override

Modified: trunk/Makefile-canvas-sources.am
==============================================================================
--- trunk/Makefile-canvas-sources.am	(original)
+++ trunk/Makefile-canvas-sources.am	Mon Mar 24 19:05:00 2008
@@ -79,6 +79,8 @@
 	$(CANVASSRCDIR)/common/hippo/hippo-canvas-link.h		\
 	$(CANVASSRCDIR)/common/hippo/hippo-canvas-style.h		\
 	$(CANVASSRCDIR)/common/hippo/hippo-canvas-text.h		\
+	$(CANVASSRCDIR)/common/hippo/hippo-canvas-theme.h		\
+	$(CANVASSRCDIR)/common/hippo/hippo-canvas-theme-engine.h	\
 	$(CANVASSRCDIR)/common/hippo/hippo-canvas-widgets.h		\
 	$(CANVASSRCDIR)/common/hippo/hippo-canvas-util.h		\
 	$(CANVASSRCDIR)/common/hippo/hippo-event.h			\
@@ -105,6 +107,9 @@
 	$(CANVASSRCDIR)/common/hippo/hippo-canvas-text.c		\
 	$(CANVASSRCDIR)/common/hippo/hippo-canvas-test.c		\
 	$(CANVASSRCDIR)/common/hippo/hippo-canvas-test.h		\
+	$(CANVASSRCDIR)/common/hippo/hippo-canvas-theme.c		\
+	$(CANVASSRCDIR)/common/hippo/hippo-canvas-theme-engine.c	\
+	$(CANVASSRCDIR)/common/hippo/hippo-canvas-theme-internal.h	\
 	$(CANVASSRCDIR)/common/hippo/hippo-canvas-util.c		\
 	$(CANVASSRCDIR)/common/hippo/hippo-event.c			\
 	$(CANVASSRCDIR)/common/hippo/hippo-graphics.c

Modified: trunk/common/hippo/hippo-canvas-box.c
==============================================================================
--- trunk/common/hippo/hippo-canvas-box.c	(original)
+++ trunk/common/hippo/hippo-canvas-box.c	Mon Mar 24 19:05:00 2008
@@ -7,6 +7,9 @@
 #include "hippo-canvas-container.h"
 #include "hippo-canvas-style.h"
 
+#define HIPPO_CANVAS_DEFAULT_COLOR 0x000000ff
+#define HIPPO_CANVAS_DEFAULT_BACKGROUND_COLOR 0xffffff00
+
 typedef struct {
     int minimum;
     int natural;
@@ -14,6 +17,10 @@
     unsigned int does_not_fit : 1;
 } AdjustInfo;
 
+typedef struct {
+    double border[4];
+    double padding[4];
+} Borders;
 
 static void hippo_canvas_box_init                 (HippoCanvasBox            *box);
 static void hippo_canvas_box_class_init           (HippoCanvasBoxClass       *klass);
@@ -54,10 +61,11 @@
                                                                  HippoCanvasItem      *item,
                                                                  int                  *x_p,
                                                                  int                  *y_p);
-static void             hippo_canvas_box_affect_color           (HippoCanvasContext   *context,
-                                                                 guint32              *color_rgba_p);
-static void             hippo_canvas_box_affect_font_desc       (HippoCanvasContext   *context,
-                                                                 PangoFontDescription *font_desc);
+
+static double                hippo_canvas_box_get_resolution (HippoCanvasContext *context);
+static HippoCanvasStyle *    hippo_canvas_box_get_style      (HippoCanvasContext *context);
+static PangoFontDescription *hippo_canvas_box_get_font       (HippoCanvasContext *context);
+
 static void             hippo_canvas_box_style_changed          (HippoCanvasContext   *context,
                                                                  gboolean              resize_needed);
 
@@ -176,6 +184,8 @@
 
 enum {
     PROP_0,
+    PROP_ID,
+    PROP_CLASSES,
     PROP_ORIENTATION,
     PROP_PADDING_TOP,
     PROP_PADDING_BOTTOM,
@@ -192,14 +202,14 @@
     PROP_XALIGN,
     PROP_YALIGN,
     PROP_BACKGROUND_COLOR,
+    PROP_BACKGROUND_COLOR_SET,
     PROP_BORDER_COLOR,
+    PROP_BORDER_COLOR_SET,
     PROP_SPACING,
     PROP_COLOR,
-    PROP_COLOR_CASCADE,
     PROP_COLOR_SET,
     PROP_FONT,
     PROP_FONT_DESC,
-    PROP_FONT_CASCADE,
     PROP_TOOLTIP,
     PROP_DEBUG_NAME
 };
@@ -242,8 +252,9 @@
     klass->unregister_widget_item = hippo_canvas_box_unregister_widget_item;
     klass->translate_to_widget = hippo_canvas_box_translate_to_widget;
     klass->translate_to_screen = hippo_canvas_box_translate_to_screen;
-    klass->affect_color = hippo_canvas_box_affect_color;
-    klass->affect_font_desc = hippo_canvas_box_affect_font_desc;
+    klass->get_style = hippo_canvas_box_get_style;
+    klass->get_resolution = hippo_canvas_box_get_resolution;
+    klass->get_font = hippo_canvas_box_get_font;
     klass->style_changed = hippo_canvas_box_style_changed;
 }
 
@@ -263,14 +274,10 @@
     box->y_align = HIPPO_ALIGNMENT_FILL;
     box->box_width = -1;
     box->box_height = -1;
-    box->background_color_rgba = HIPPO_CANVAS_DEFAULT_BACKGROUND_COLOR;
     box->content_min_width = -1;
     box->content_min_height = -1;
     box->needs_width_request = TRUE; /* be sure we do at least one allocation */
     box->needs_height_request = TRUE;
-
-    box->color_cascade = HIPPO_CASCADE_MODE_INHERIT;
-    box->font_cascade = HIPPO_CASCADE_MODE_INHERIT;
 }
 
 static void
@@ -284,8 +291,6 @@
     object_class->dispose = hippo_canvas_box_dispose;
     object_class->finalize = hippo_canvas_box_finalize;
 
-    klass->default_color = HIPPO_CANVAS_DEFAULT_COLOR;
-    
     klass->paint_background = hippo_canvas_box_paint_background;
     klass->paint_children = hippo_canvas_box_paint_children;
     klass->get_content_width_request = hippo_canvas_box_get_content_width_request;
@@ -299,7 +304,10 @@
                       NULL, NULL,
                       g_cclosure_marshal_VOID__BOOLEAN,
                       G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
-    
+
+    g_object_class_override_property (object_class, PROP_ID, "id");
+    g_object_class_override_property (object_class, PROP_CLASSES, "classes");
+
     /**
      * HippoCanvasBox:orientation
      *
@@ -574,6 +582,8 @@
      * Sets an RGBA background color (pack the color into 32-bit unsigned int, just type
      * "0xff0000ff" for example for opaque red). The background color covers the
      * padding but not the border of the box.
+     *
+     * This property is ignored if the background-color-set property is %FALSE.
      */
     g_object_class_install_property(object_class,
                                     PROP_BACKGROUND_COLOR,
@@ -586,6 +596,21 @@
                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
 
     /**
+     * HippoCanvasBox:background-color-set
+     *
+     * Determines whether the background-color property is used, or whether the color is determined
+     * from the item's style. This flag gets set automatically if you write to the background-color
+     * property.
+     */        
+    g_object_class_install_property(object_class,
+                                    PROP_BACKGROUND_COLOR_SET,
+                                    g_param_spec_boolean("background-color-set",
+                                                         _("Background Color Set"),
+                                                         _("Whether a background color was set"),
+                                                         FALSE,
+                                                         G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+    /**
      * HippoCanvasBox:border-color
      *
      * Sets an RGBA border color (pack the color into 32-bit unsigned int, just type
@@ -606,6 +631,21 @@
                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
 
     /**
+     * HippoCanvasBox:border-color-set
+     *
+     * Determines whether the border-color property is used, or whether the color is determined
+     * from the item's style. This flag gets set automatically if you write to the border-color
+     * property.
+     */        
+    g_object_class_install_property(object_class,
+                                    PROP_BORDER_COLOR_SET,
+                                    g_param_spec_boolean("border-color-set",
+                                                         _("Border Color Set"),
+                                                         _("Whether a border color was set"),
+                                                         FALSE,
+                                                         G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+    /**
      * HippoCanvasBox:spacing
      *
      * The spacing is a gap to leave between all child items in the box. If you want a gap
@@ -642,27 +682,13 @@
                                                       G_MAXUINT,
                                                       HIPPO_CANVAS_DEFAULT_COLOR,
                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
-    /**
-     * HippoCanvasBox:color-cascade
-     *
-     * If the color-set property is %FALSE, this property determines whether the item inherits
-     * its foreground color property from its containing canvas item, or whether the item uses
-     * a default color which may depend on the canvas item type.
-     */    
-    g_object_class_install_property(object_class,
-                                    PROP_COLOR_CASCADE,
-                                    g_param_spec_enum("color-cascade",
-                                                      _("Foreground Color Cascade"),
-                                                      _("Whether to use parent's color if ours is unset"),
-                                                      HIPPO_TYPE_CASCADE_MODE,
-                                                      HIPPO_CASCADE_MODE_INHERIT,
-                                                      G_PARAM_READABLE | G_PARAM_WRITABLE));
+    
     /**
      * HippoCanvasBox:color-set
      *
-     * Determines whether the color property is used, or whether a default color is
-     * used according to the color-cascade property. This flag gets set automatically
-     * if you write to the color property.
+     * Determines whether the color property is used, or whether the color is determined
+     * from the item's style. This flag gets set automatically if you write to the color
+     * property.
      */        
     g_object_class_install_property(object_class,
                                     PROP_COLOR_SET,
@@ -675,10 +701,8 @@
      * HippoCanvasBox:font
      * 
      * The font to use as a Pango font description string. Only matters for text items, or for
-     * boxes that contain text items. If a box contains text items, and the child items
-     * have the font-cascade property set to #HIPPO_CASCADE_MODE_INHERIT, then the child
-     * items will use the font from the containing box unless they have explicitly set
-     * their own font.
+     * boxes that contain text items, overriding any font from the item's style. The font will not
+     * be inherited by child items.
      *
      * This property is just a way to set the font-desc property, using a string
      * instead of a #PangoFontDescription object.
@@ -693,12 +717,8 @@
     /**
      * HippoCanvasBox:font-desc
      *
-     * The font to use for text in the item and its children. Children will inherit this font
-     * if their font-cascade is set to #HIPPO_CASCADE_MODE_INHERIT and they don't set a font
-     * themselves.
-     *
-     * If the font description is %NULL then the font will be inherited or a default will be
-     * used, according to the font-cascade property.
+     * The font to use for text in the item, overriding any font from the style. The font
+     * will not be inherited by child items.
      */
     g_object_class_install_property(object_class,
                                     PROP_FONT_DESC,
@@ -709,23 +729,6 @@
                                                        G_PARAM_READABLE | G_PARAM_WRITABLE));
 
     /**
-     * HippoCanvasBox:font-cascade
-     *
-     * If the font-desc property is %NULL, this property determines whether the item inherits
-     * its font property from its containing canvas item, or whether the item uses
-     * a default font.
-     * 
-     */    
-    g_object_class_install_property(object_class,
-                                    PROP_FONT_CASCADE,
-                                    g_param_spec_enum("font-cascade",
-                                                      _("Font Cascade"),
-                                                      _("Whether to use parent's font if ours is unset"),
-                                                      HIPPO_TYPE_CASCADE_MODE,
-                                                      HIPPO_CASCADE_MODE_INHERIT,
-                                                      G_PARAM_READABLE | G_PARAM_WRITABLE));
-
-    /**
      * HippoCanvasBox:tooltip
      *
      * A string to display as a tooltip on this canvas item.
@@ -754,6 +757,15 @@
 }
 
 static void
+clear_style(HippoCanvasBox *box)
+{
+    if (box->style) {
+        g_object_unref(box->style);
+        box->style = NULL;
+    }
+}
+
+static void
 hippo_canvas_box_dispose(GObject *object)
 {
     HippoCanvasBox *box = HIPPO_CANVAS_BOX(object);
@@ -761,10 +773,7 @@
     hippo_canvas_box_clear(box);
     hippo_canvas_box_set_layout(box, NULL);
 
-    if (box->style) {
-        g_object_unref(box->style);
-        box->style = NULL;
-    }
+    clear_style(box);
 
     hippo_canvas_item_emit_destroy(HIPPO_CANVAS_ITEM(object));
     
@@ -794,11 +803,23 @@
 }
 
 static void
-ensure_style(HippoCanvasBox *box)
+set_font_description(HippoCanvasBox       *box,
+                     PangoFontDescription *font_desc,
+                     gboolean              copy)
 {
-    if (box->style == NULL) {
-        box->style = g_object_new(HIPPO_TYPE_CANVAS_STYLE, NULL);
-    }
+    if (box->font_desc == font_desc)
+        return;
+
+    if (box->font_desc != NULL)
+        pango_font_description_free(box->font_desc);
+    
+    if (copy && font_desc != NULL)
+        box->font_desc = pango_font_description_copy(font_desc);
+    else
+        box->font_desc = font_desc;
+
+    g_object_notify(G_OBJECT(box), "font-desc");
+    g_object_notify(G_OBJECT(box), "font");
 }
 
 static void
@@ -814,45 +835,144 @@
 
     need_resize = TRUE; /* for most of them it's true */
     switch (prop_id) {
+    case PROP_ID:
+        {
+            const char *str = g_value_get_string(value);
+            if (str != box->element_id) {
+                if (box->element_id)
+                    g_free(box->element_id);
+                box->element_id = g_strdup(str);
+
+                clear_style(box);
+                hippo_canvas_context_emit_style_changed(HIPPO_CANVAS_CONTEXT(box), TRUE);
+            }
+        }
+        break;
+    case PROP_CLASSES:
+        {
+            const char *str = g_value_get_string(value);
+            if (str != box->element_class) {
+                if (box->element_class)
+                    g_free(box->element_class);
+                box->element_class = g_strdup(str);
+                
+                clear_style(box);
+                hippo_canvas_context_emit_style_changed(HIPPO_CANVAS_CONTEXT(box), TRUE);
+            }
+        }
+        break;
     case PROP_ORIENTATION:
         box->orientation = g_value_get_enum(value);
         break;
     case PROP_PADDING_TOP:
-        box->padding_top = g_value_get_int(value);
+        {
+            int p = g_value_get_int(value);
+            if (p >= 0) {
+                box->padding_top = p;
+                box->padding_top_set = TRUE;
+            } else {
+                box->padding_top_set = FALSE;
+            }
+        }
         break;
     case PROP_PADDING_BOTTOM:
-        box->padding_bottom = g_value_get_int(value);
+        {
+            int p = g_value_get_int(value);
+            if (p >= 0) {
+                box->padding_bottom = p;
+                box->padding_bottom_set = TRUE;
+            } else {
+                box->padding_bottom_set = FALSE;
+            }
+        }
         break;
     case PROP_PADDING_LEFT:
-        box->padding_left = g_value_get_int(value);
+        {
+            int p = g_value_get_int(value);
+            if (p >= 0) {
+                box->padding_left = p;
+                box->padding_left_set = TRUE;
+            } else {
+                box->padding_left_set = FALSE;
+            }
+        }
         break;
     case PROP_PADDING_RIGHT:
-        box->padding_right = g_value_get_int(value);
+        {
+            int p = g_value_get_int(value);
+            if (p >= 0) {
+                box->padding_right = p;
+                box->padding_right_set = TRUE;
+            } else {
+                box->padding_right_set = FALSE;
+            }
+        }
         break;
     case PROP_PADDING:
         {
             int p = g_value_get_int(value);
-            box->padding_top = box->padding_bottom = p;
-            box->padding_left = box->padding_right = p;
+            if (p >= 0) {
+                box->padding_top = box->padding_bottom = box->padding_left = box->padding_right = p;
+                box->padding_top_set = box->padding_bottom_set = box->padding_left_set = box->padding_right_set = TRUE;
+            } else {
+                box->padding_top_set = box->padding_bottom_set = box->padding_left_set = box->padding_right_set = FALSE;
+            }
         }
         break;
     case PROP_BORDER_TOP:
-        box->border_top = g_value_get_int(value);
+        {
+            int p = g_value_get_int(value);
+            if (p >= 0) {
+                box->border_top = p;
+                box->border_top_set = TRUE;
+            } else {
+                box->border_top_set = FALSE;
+            }
+        }
         break;
     case PROP_BORDER_BOTTOM:
-        box->border_bottom = g_value_get_int(value);
+        {
+            int p = g_value_get_int(value);
+            if (p >= 0) {
+                box->border_bottom = p;
+                box->border_bottom_set = TRUE;
+            } else {
+                box->border_bottom_set = FALSE;
+            }
+        }
         break;
     case PROP_BORDER_LEFT:
-        box->border_left = g_value_get_int(value);
+        {
+            int p = g_value_get_int(value);
+            if (p >= 0) {
+                box->border_left = p;
+                box->border_left_set = TRUE;
+            } else {
+                box->border_left_set = FALSE;
+            }
+        }
         break;
     case PROP_BORDER_RIGHT:
+        {
+            int p = g_value_get_int(value);
+            if (p >= 0) {
+                box->border_right = p;
+                box->border_right_set = TRUE;
+            } else {
+                box->border_right_set = FALSE;
+            }
+        }
         box->border_right = g_value_get_int(value);
         break;
     case PROP_BORDER:
         {
             int p = g_value_get_int(value);
-            box->border_top = box->border_bottom = p;
-            box->border_left = box->border_right = p;
+            if (p >= 0) {
+                box->border_top = box->border_bottom = box->border_left = box->border_right = p;
+                box->border_top_set = box->border_bottom_set = box->border_left_set = box->border_right_set = TRUE;
+            } else {
+                box->border_top_set = box->border_bottom_set = box->border_left_set = box->border_right_set = FALSE;
+            }
         }
         break;        
     case PROP_BOX_WIDTH:
@@ -869,11 +989,25 @@
         break;
     case PROP_BACKGROUND_COLOR:
         box->background_color_rgba = g_value_get_uint(value);
+        box->background_color_set = TRUE;
+        g_object_notify(object, "background-color-set");
+        hippo_canvas_item_emit_paint_needed(HIPPO_CANVAS_ITEM(box), 0, 0, -1, -1);
+        need_resize = FALSE;
+        break;
+    case PROP_BACKGROUND_COLOR_SET:
+        box->background_color_set = g_value_get_boolean(value);
         hippo_canvas_item_emit_paint_needed(HIPPO_CANVAS_ITEM(box), 0, 0, -1, -1);
         need_resize = FALSE;
         break;
     case PROP_BORDER_COLOR:
         box->border_color_rgba = g_value_get_uint(value);
+        box->border_color_set = TRUE;
+        g_object_notify(object, "border-color-set");
+        hippo_canvas_item_emit_paint_needed(HIPPO_CANVAS_ITEM(box), 0, 0, -1, -1);
+        need_resize = FALSE;
+        break;
+    case PROP_BORDER_COLOR_SET:
+        box->border_color_set = g_value_get_boolean(value);
         hippo_canvas_item_emit_paint_needed(HIPPO_CANVAS_ITEM(box), 0, 0, -1, -1);
         need_resize = FALSE;
         break;
@@ -881,57 +1015,28 @@
         box->spacing = g_value_get_int(value);
         break;
     case PROP_COLOR:
-        ensure_style(box);
-        g_object_set_property(G_OBJECT(box->style), "color", value);
-        hippo_canvas_context_emit_style_changed(HIPPO_CANVAS_CONTEXT(box),
-                                                FALSE);
+        box->color_rgba = g_value_get_uint(value);
+        box->color_set = TRUE;
+        g_object_notify(object, "color-set");
+        hippo_canvas_item_emit_paint_needed(HIPPO_CANVAS_ITEM(box), 0, 0, -1, -1);
         need_resize = FALSE;
         break;
     case PROP_COLOR_SET:
-        if (g_value_get_boolean(value) || box->style != NULL) {
-            ensure_style(box);
-            g_object_set_property(G_OBJECT(box->style), "color-set", value);
-            hippo_canvas_context_emit_style_changed(HIPPO_CANVAS_CONTEXT(box),
-                                                    FALSE);
-        }
-        need_resize = FALSE;
-        break;
-    case PROP_COLOR_CASCADE:
-        {
-            HippoCascadeMode new_mode = g_value_get_enum(value);
-            if (new_mode != box->color_cascade) {
-                box->color_cascade = new_mode;
-                hippo_canvas_context_emit_style_changed(HIPPO_CANVAS_CONTEXT(box),
-                                                        FALSE);
-            }
-        }
+        box->color_set = g_value_get_boolean(value);
+        hippo_canvas_item_emit_paint_needed(HIPPO_CANVAS_ITEM(box), 0, 0, -1, -1);
         need_resize = FALSE;
         break;
     case PROP_FONT:
-        if (!(g_value_get_string(value) == NULL && box->style == NULL)) {
-            ensure_style(box);
-            g_object_set_property(G_OBJECT(box->style), "font", value);
-            hippo_canvas_context_emit_style_changed(HIPPO_CANVAS_CONTEXT(box),
-                                                    TRUE);
+        {
+            const char *str = g_value_get_string(value);
+            if (str != NULL)
+                set_font_description(box, pango_font_description_from_string(str), FALSE);
+            else
+                set_font_description(box, NULL, FALSE);
         }
         break;
     case PROP_FONT_DESC:
-        if (!(g_value_get_boxed(value) == NULL && box->style == NULL)) {
-            ensure_style(box);
-            g_object_set_property(G_OBJECT(box->style), "font-desc", value);
-            hippo_canvas_context_emit_style_changed(HIPPO_CANVAS_CONTEXT(box),
-                                                    TRUE);
-        }
-        break;
-    case PROP_FONT_CASCADE:
-        {
-            HippoCascadeMode new_mode = g_value_get_enum(value);
-            if (new_mode != box->font_cascade) {
-                box->font_cascade = new_mode;
-                hippo_canvas_context_emit_style_changed(HIPPO_CANVAS_CONTEXT(box),
-                                                        TRUE);
-            }
-        }
+        set_font_description(box, g_value_get_boxed(value), TRUE);
         break;
     case PROP_TOOLTIP:
         {
@@ -983,32 +1088,38 @@
     box = HIPPO_CANVAS_BOX (object);
 
     switch (prop_id) {
+    case PROP_ID:
+        g_value_set_string(value, box->element_id);
+        break;
+    case PROP_CLASSES:
+        g_value_set_string(value, box->element_class);
+        break;
     case PROP_ORIENTATION:
         g_value_set_enum(value, box->orientation);
         break;
     case PROP_PADDING_TOP:
-        g_value_set_int(value, box->padding_top);
+        g_value_set_int(value, box->padding_top_set ? box->padding_top : -1);
         break;
     case PROP_PADDING_BOTTOM:
-        g_value_set_int(value, box->padding_bottom);
+        g_value_set_int(value, box->padding_bottom_set ? box->padding_bottom : -1);
         break;
     case PROP_PADDING_LEFT:
-        g_value_set_int(value, box->padding_left);
+        g_value_set_int(value, box->padding_left_set ? box->padding_left : -1);
         break;
     case PROP_PADDING_RIGHT:
-        g_value_set_int(value, box->padding_right);
+        g_value_set_int(value, box->padding_right_set ? box->padding_right : -1);
         break;
     case PROP_BORDER_TOP:
-        g_value_set_int(value, box->border_top);
+        g_value_set_int(value, box->border_top_set ? box->border_top : -1);
         break;
     case PROP_BORDER_BOTTOM:
-        g_value_set_int(value, box->border_bottom);
+        g_value_set_int(value, box->border_bottom_set ? box->border_bottom : -1);
         break;
     case PROP_BORDER_LEFT:
-        g_value_set_int(value, box->border_left);
+        g_value_set_int(value, box->border_left_set ? box->border_left : -1);
         break;
     case PROP_BORDER_RIGHT:
-        g_value_set_int(value, box->border_right);
+        g_value_set_int(value, box->border_right_set ? box->border_right : -1);
         break;        
     case PROP_BOX_WIDTH:
         g_value_set_int(value, box->box_width);
@@ -1025,45 +1136,34 @@
     case PROP_BACKGROUND_COLOR:
         g_value_set_uint(value, box->background_color_rgba);
         break;
+    case PROP_BACKGROUND_COLOR_SET:
+        g_value_set_boolean(value, box->background_color_set);
+        break;
     case PROP_BORDER_COLOR:
         g_value_set_uint(value, box->border_color_rgba);
         break;
+    case PROP_BORDER_COLOR_SET:
+        g_value_set_boolean(value, box->border_color_set);
+        break;
     case PROP_SPACING:
         g_value_set_int(value, box->spacing);
         break;
     case PROP_COLOR:
-        if (box->style) {
-            g_object_get_property(G_OBJECT(box->style), "color", value);
-        } else {
-            g_value_set_uint(value, HIPPO_CANVAS_DEFAULT_BACKGROUND_COLOR);
-        }
-        break;
-    case PROP_COLOR_CASCADE:
-        g_value_set_enum(value, box->color_cascade);
+        g_value_set_uint(value, box->color_rgba);
         break;
     case PROP_COLOR_SET:
-        if (box->style) {
-            g_object_get_property(G_OBJECT(box->style), "color-set", value);
-        } else {
-            g_value_set_boolean(value, FALSE);
-        }
+        g_value_set_boolean(value, box->color_set);
         break;
     case PROP_FONT:
-        if (box->style) {
-            g_object_get_property(G_OBJECT(box->style), "font", value);
+        if (box->font_desc) {
+            char *str = pango_font_description_to_string(box->font_desc);
+            g_value_take_string(value, str);
         } else {
             g_value_set_string(value, NULL);
         }
         break;
     case PROP_FONT_DESC:
-        if (box->style) {
-            g_object_get_property(G_OBJECT(box->style), "font-desc", value);
-        } else {
-            g_value_set_boxed(value, NULL);
-        }
-        break;
-    case PROP_FONT_CASCADE:
-        g_value_set_enum(value, box->font_cascade);
+        g_value_set_boxed(value, box->font_desc);
         break;
     case PROP_TOOLTIP:
         g_value_set_string(value, box->tooltip);
@@ -1198,30 +1298,36 @@
                                              HIPPO_CANVAS_ITEM(box), x_p, y_p);
 }
 
-static void
-hippo_canvas_box_affect_color(HippoCanvasContext *context,
-                              guint32            *color_rgba_p)
+static HippoCanvasStyle *
+hippo_canvas_box_get_style(HippoCanvasContext *context)
 {
     HippoCanvasBox *box = HIPPO_CANVAS_BOX(context);
+
+    if (box->style == NULL) {
+        box->style = hippo_canvas_style_new(box->context,
+                                            hippo_canvas_context_get_style(box->context),
+                                            NULL, /* Inherit theme from parent */
+                                            G_OBJECT_TYPE(box), box->element_id, box->element_class);
+        hippo_canvas_style_set_link_type(box->style, box->link_type);
+    }
     
-    if (box->context && box->color_cascade == HIPPO_CASCADE_MODE_INHERIT)
-        hippo_canvas_context_affect_color(box->context, color_rgba_p);
-    
-    if (box->style)
-        hippo_canvas_style_affect_color(box->style, color_rgba_p);
+    return box->style;
 }
 
-static void
-hippo_canvas_box_affect_font_desc(HippoCanvasContext   *context,
-                                  PangoFontDescription *font_desc)
+static double
+hippo_canvas_box_get_resolution(HippoCanvasContext *context)
 {
     HippoCanvasBox *box = HIPPO_CANVAS_BOX(context);
 
-    if (box->context && box->font_cascade == HIPPO_CASCADE_MODE_INHERIT)
-        hippo_canvas_context_affect_font_desc(box->context, font_desc);
-    
-    if (box->style)
-        hippo_canvas_style_affect_font_desc(box->style, font_desc);
+    return hippo_canvas_context_get_resolution(box->context);
+}
+
+static PangoFontDescription *
+hippo_canvas_box_get_font(HippoCanvasContext *context)
+{
+    HippoCanvasBox *box = HIPPO_CANVAS_BOX(context);
+
+    return hippo_canvas_context_get_font(box->context);
 }
 
 static void
@@ -1251,7 +1357,9 @@
 on_context_style_changed(HippoCanvasContext *context,
                          gboolean            resize_needed,
                          HippoCanvasBox     *box)
-{    
+{
+    clear_style(box);
+    
     /* If our context's style changed, then our own style also
      * changed since we chain up to the outer context.
      */
@@ -1347,13 +1455,109 @@
 }
 
 void
+hippo_canvas_box_set_link_type (HippoCanvasBox     *box,
+                                HippoCanvasLinkType link_type)
+{
+    g_return_if_fail(HIPPO_IS_CANVAS_BOX(box));
+    
+    if (link_type == box->link_type)
+        return;
+    
+    box->link_type = link_type;
+    
+    clear_style(box);
+    hippo_canvas_context_emit_style_changed(HIPPO_CANVAS_CONTEXT(box), TRUE);
+    hippo_canvas_item_emit_request_changed(HIPPO_CANVAS_ITEM(box));
+}
+
+static void
+get_borders(HippoCanvasBox *box,
+            Borders        *borders)
+{
+    HippoCanvasStyle *style = hippo_canvas_context_get_style(HIPPO_CANVAS_CONTEXT(box));
+    
+    if (box->border_left_set)
+        borders->border[HIPPO_CANVAS_SIDE_LEFT] = box->border_left;
+    else
+        borders->border[HIPPO_CANVAS_SIDE_LEFT] = hippo_canvas_style_get_border_width(style, HIPPO_CANVAS_SIDE_LEFT);
+    
+    if (box->border_right_set)
+        borders->border[HIPPO_CANVAS_SIDE_RIGHT] = box->border_right;
+    else
+        borders->border[HIPPO_CANVAS_SIDE_RIGHT] = hippo_canvas_style_get_border_width(style, HIPPO_CANVAS_SIDE_RIGHT);
+    
+    if (box->border_top_set)
+        borders->border[HIPPO_CANVAS_SIDE_TOP] = box->border_top;
+    else
+        borders->border[HIPPO_CANVAS_SIDE_TOP] = hippo_canvas_style_get_border_width(style, HIPPO_CANVAS_SIDE_TOP);
+    
+    if (box->border_bottom_set)
+        borders->border[HIPPO_CANVAS_SIDE_BOTTOM] = box->border_bottom;
+    else
+        borders->border[HIPPO_CANVAS_SIDE_BOTTOM] = hippo_canvas_style_get_border_width(style, HIPPO_CANVAS_SIDE_BOTTOM);
+
+    if (box->padding_left_set)
+        borders->padding[HIPPO_CANVAS_SIDE_LEFT] = box->padding_left;
+    else
+        borders->padding[HIPPO_CANVAS_SIDE_LEFT] = hippo_canvas_style_get_padding(style, HIPPO_CANVAS_SIDE_LEFT);
+    
+    if (box->padding_right_set)
+        borders->padding[HIPPO_CANVAS_SIDE_RIGHT] = box->padding_right;
+    else
+        borders->padding[HIPPO_CANVAS_SIDE_RIGHT] = hippo_canvas_style_get_padding(style, HIPPO_CANVAS_SIDE_RIGHT);
+    
+    if (box->padding_top_set)
+        borders->padding[HIPPO_CANVAS_SIDE_TOP] = box->padding_top;
+    else
+        borders->padding[HIPPO_CANVAS_SIDE_TOP] = hippo_canvas_style_get_padding(style, HIPPO_CANVAS_SIDE_TOP);
+    
+    if (box->padding_bottom_set)
+        borders->padding[HIPPO_CANVAS_SIDE_BOTTOM] = box->padding_bottom;
+    else
+        borders->padding[HIPPO_CANVAS_SIDE_BOTTOM] = hippo_canvas_style_get_padding(style, HIPPO_CANVAS_SIDE_BOTTOM);
+}
+
+void
 hippo_canvas_box_get_background_area (HippoCanvasBox *box,
                                       HippoRectangle *area)
 {
-    area->x = box->border_left;
-    area->y = box->border_top;
-    area->width = box->allocated_width - box->border_left - box->border_right;
-    area->height = box->allocated_height - box->border_top - box->border_bottom;
+    Borders borders;
+    get_borders(box, &borders);
+    
+    area->x = borders.border[HIPPO_CANVAS_SIDE_LEFT];
+    area->y = borders.border[HIPPO_CANVAS_SIDE_TOP];
+    area->width = box->allocated_width - borders.border[HIPPO_CANVAS_SIDE_LEFT] - borders.border[HIPPO_CANVAS_SIDE_RIGHT];
+    area->height = box->allocated_height - borders.border[HIPPO_CANVAS_SIDE_TOP] - borders.border[HIPPO_CANVAS_SIDE_BOTTOM];
+}
+
+static void
+draw_border(HippoCanvasBox   *box,
+            HippoCanvasStyle *style,
+            cairo_t          *cr,
+            HippoCanvasSide   side,
+            double            x,
+            double            y,
+            double            width,
+            double            height)
+{
+    guint32 border_color_rgba;
+
+    if (box->border_color_set)
+        border_color_rgba = box->border_color_set;
+    else
+        border_color_rgba = hippo_canvas_style_get_border_color(style, side);
+
+    if ((border_color_rgba & 0xff) == 0) /* Transparent */
+        return;
+
+    hippo_cairo_set_source_rgba32(cr, border_color_rgba);
+    cairo_rectangle(cr, x, y, width, height);
+
+    /* In the common case of all the borders the same, we could do a single
+     * fill, and optimize things a bit, but we'll leave the complexity aside
+     * for now.
+     */
+    cairo_fill(cr);
 }
 
 static void
@@ -1361,47 +1565,49 @@
                                   cairo_t        *cr,
                                   HippoRectangle *damaged_box)
 {
+    HippoCanvasStyle *style = hippo_canvas_context_get_style(HIPPO_CANVAS_CONTEXT(box));
+    guint background_color_rgba;
+    Borders borders;
+    
+    if (box->background_color_set)
+        background_color_rgba = box->background_color_rgba;
+    else
+        background_color_rgba = hippo_canvas_style_get_background_color(style);
+
     /* fill background, with html div type semantics - covers entire
      * item allocation, including padding but not border
      */
-    if ((box->background_color_rgba & 0xff) != 0) {
+    if ((background_color_rgba & 0xff) != 0) {
         HippoRectangle area;
 
         hippo_canvas_box_get_background_area(box, &area);
         
-        hippo_cairo_set_source_rgba32(cr, box->background_color_rgba);
+        hippo_cairo_set_source_rgba32(cr, background_color_rgba);
         cairo_rectangle(cr,
                         area.x, area.y,
                         area.width, area.height);
         cairo_fill(cr);
     }
 
-    /* draw the borders, in four non-overlapping rectangles */
-    if ((box->border_color_rgba & 0xff) != 0) {
-        hippo_cairo_set_source_rgba32(cr, box->border_color_rgba);
-        /* top */
-        cairo_rectangle(cr,
-                        0, 0,
-                        box->allocated_width,
-                        box->border_top);
-        /* left */
-        cairo_rectangle(cr,
-                        0, box->border_top,
-                        box->border_left,
-                        box->allocated_height - box->border_top - box->border_bottom);
-        /* right */
-        cairo_rectangle(cr,
-                        box->allocated_width - box->border_right,
-                        box->border_top,
-                        box->border_right,
-                        box->allocated_height - box->border_top - box->border_bottom);
-        /* bottom */
-        cairo_rectangle(cr,
-                        0, box->allocated_height - box->border_bottom,
-                        box->allocated_width,
-                        box->border_bottom);
-        cairo_fill(cr);
-    }
+    get_borders(box, &borders);
+
+    draw_border(box, style, cr, HIPPO_CANVAS_SIDE_TOP,
+                0, 0,
+                box->allocated_width,
+                borders.border[HIPPO_CANVAS_SIDE_TOP]);
+    draw_border(box, style, cr, HIPPO_CANVAS_SIDE_LEFT,
+                0, borders.border[HIPPO_CANVAS_SIDE_TOP],
+                borders.border[HIPPO_CANVAS_SIDE_LEFT],
+                box->allocated_height - borders.border[HIPPO_CANVAS_SIDE_TOP] - borders.border[HIPPO_CANVAS_SIDE_BOTTOM]);
+    draw_border(box, style, cr, HIPPO_CANVAS_SIDE_RIGHT,
+                box->allocated_width - borders.border[HIPPO_CANVAS_SIDE_RIGHT],
+                borders.border[HIPPO_CANVAS_SIDE_TOP]	,
+                borders.border[HIPPO_CANVAS_SIDE_RIGHT],
+                box->allocated_height - borders.border[HIPPO_CANVAS_SIDE_TOP] - borders.border[HIPPO_CANVAS_SIDE_BOTTOM]);
+    draw_border(box, style, cr, HIPPO_CANVAS_SIDE_BOTTOM,
+                0, box->allocated_height - borders.border[HIPPO_CANVAS_SIDE_BOTTOM],
+                box->allocated_width,
+                borders.border[HIPPO_CANVAS_SIDE_BOTTOM]);
 }
 
 static void
@@ -1485,11 +1691,16 @@
                             int            *x_p,
                             int            *width_p)
 {
-    int left = box->border_left + box->padding_left;
-    int right = box->border_right + box->padding_right;
+    Borders borders;
+    int left;
+    int right;
     int unpadded_box_width;
     int content_width;
 
+    get_borders(box, &borders);
+    left = borders.border[HIPPO_CANVAS_SIDE_LEFT] + borders.padding[HIPPO_CANVAS_SIDE_LEFT];
+    right = borders.border[HIPPO_CANVAS_SIDE_RIGHT] + borders.padding[HIPPO_CANVAS_SIDE_RIGHT];
+
     g_return_if_fail(requested_content_width >= 0);
 
     if (natural_content_width < allocated_box_width)
@@ -1535,12 +1746,17 @@
                           int            *y_p,
                           int            *height_p)
 {
-    int top = box->border_top + box->padding_top;
-    int bottom = box->border_bottom + box->padding_bottom;
+    Borders borders;
+    int top;
+    int bottom;
     int unpadded_box_height;
     int content_height;
 
     g_return_if_fail(requested_content_height >= 0);
+
+    get_borders(box, &borders);
+    top = borders.border[HIPPO_CANVAS_SIDE_TOP] + borders.padding[HIPPO_CANVAS_SIDE_TOP];
+    bottom = borders.border[HIPPO_CANVAS_SIDE_BOTTOM] + borders.padding[HIPPO_CANVAS_SIDE_BOTTOM];
     
     if (natural_content_height < allocated_box_height)
         content_height = natural_content_height;
@@ -2749,11 +2965,14 @@
 {
     int content_min_width, content_natural_width;
     HippoCanvasBox *box;
-
+    Borders borders;
+    
     box = HIPPO_CANVAS_BOX(item);
-
+    
     box->needs_width_request = FALSE;
 
+    get_borders(box, &borders);
+
     /* We need to call this even if just returning the box-width prop,
      * so that children can rely on getting the full request, allocate
      * cycle in order every time, and so we compute the cached requests.
@@ -2771,8 +2990,8 @@
     } else {
         int outside;
 
-        outside = box->padding_left + box->padding_right
-            + box->border_left + box->border_right;
+        outside = borders.padding[HIPPO_CANVAS_SIDE_LEFT] + borders.padding[HIPPO_CANVAS_SIDE_RIGHT]
+            + borders.border[HIPPO_CANVAS_SIDE_LEFT] + borders.border[HIPPO_CANVAS_SIDE_RIGHT];
 
         if (min_width_p)
             *min_width_p = content_min_width + outside;
@@ -2798,14 +3017,17 @@
     int content_min_height, content_natural_height;
     int content_for_width;
     HippoCanvasBox *box;
+    Borders borders;
 
     box = HIPPO_CANVAS_BOX(item);
 
     box->needs_height_request = FALSE;
 
+    get_borders(box, &borders);
+
     content_for_width = for_width
-        - box->padding_left - box->padding_right
-        - box->border_left - box->border_right;
+        - borders.padding[HIPPO_CANVAS_SIDE_LEFT] - borders.padding[HIPPO_CANVAS_SIDE_RIGHT]
+        - borders.border[HIPPO_CANVAS_SIDE_LEFT] - borders.border[HIPPO_CANVAS_SIDE_RIGHT];
 
     /* We need to call this even if just returning the box-height prop,
      * so that children can rely on getting the full request, allocate
@@ -2822,8 +3044,8 @@
             *natural_height_p = box->box_height;
     } else {
         int outside;
-        outside = box->padding_top + box->padding_bottom
-            + box->border_top + box->border_bottom;
+        outside = borders.padding[HIPPO_CANVAS_SIDE_TOP] + borders.padding[HIPPO_CANVAS_SIDE_BOTTOM]
+            + borders.border[HIPPO_CANVAS_SIDE_TOP] + borders.border[HIPPO_CANVAS_SIDE_BOTTOM];
 
         if (min_height_p)
             *min_height_p = content_min_height + outside;

Modified: trunk/common/hippo/hippo-canvas-box.h
==============================================================================
--- trunk/common/hippo/hippo-canvas-box.h	(original)
+++ trunk/common/hippo/hippo-canvas-box.h	Mon Mar 24 19:05:00 2008
@@ -4,12 +4,10 @@
 
 #include <hippo/hippo-canvas-item.h>
 #include <hippo/hippo-canvas-container.h>
+#include <hippo/hippo-canvas-style.h>
 
 G_BEGIN_DECLS
 
-typedef struct _HippoCanvasStyle      HippoCanvasStyle;
-typedef struct _HippoCanvasStyleClass HippoCanvasStyleClass;
-
 typedef enum
 {
     HIPPO_PACK_EXPAND = 1,  /**< This is equivalent to both EXPAND and FILL for GtkBox,
@@ -33,11 +31,6 @@
     HIPPO_PACK_CLEAR_BOTH = 192   /* Pack below left-and right floated children */
 } HippoPackFlags;
 
-typedef enum {
-    HIPPO_CASCADE_MODE_NONE,
-    HIPPO_CASCADE_MODE_INHERIT
-} HippoCascadeMode;
-
 typedef int  (* HippoCanvasCompareChildFunc) (HippoCanvasItem *child_a,
                                               HippoCanvasItem *child_b,
                                               void            *data);
@@ -66,6 +59,9 @@
     HippoCanvasStyle *style; /* may be NULL if no relevant props set */
     GSList *children;
 
+    char *element_id;
+    char *element_class;
+
     HippoCanvasLayout *layout;
 
     char *tooltip;
@@ -90,6 +86,8 @@
     int box_width;
     int box_height;
 
+    PangoFontDescription *font_desc;
+    guint32 color_rgba;
     guint32 background_color_rgba;
     guint32 border_color_rgba;
 
@@ -119,16 +117,26 @@
     guint x_align : 3;     /* enum only has 4 values so it fits with extra */
     guint y_align : 3;     /* enum only has 4 values so it fits with extra */
     guint clickable : 1;   /* show a hand pointer and emit activated signal */
+    guint link_type : 2;   /* enum only has 3 values so it fits with extra */
     guint hovering : 1;    /* the box or some child contains the pointer (have gotten enter without leave) */
-    guint color_cascade : 2; /* enum has only 2 values */
-    guint font_cascade : 2;  /* enum has only 2 values */
+    guint color_set : 1;
+    guint background_color_set : 1;
+    guint border_color_set : 1;
+
+    guint border_top_set : 1;
+    guint border_bottom_set : 1;
+    guint border_left_set : 1;
+    guint border_right_set : 1;
+
+    guint padding_top_set : 1;
+    guint padding_bottom_set : 1;
+    guint padding_left_set : 1;
+    guint padding_right_set : 1;
 };
 
 struct _HippoCanvasBoxClass {
     GObjectClass base_class;
 
-    guint32  default_color;
-    
     void     (* paint_background)             (HippoCanvasBox   *box,
                                                cairo_t          *cr,
                                                HippoRectangle   *damaged_box);
@@ -227,6 +235,9 @@
 
 gboolean            hippo_canvas_box_is_clickable        (HippoCanvasBox *box);
 
+void                hippo_canvas_box_set_link_type       (HippoCanvasBox     *box,
+                                                          HippoCanvasLinkType link_type);
+
 /* API for layout managers */
 
 HippoCanvasBoxChild *hippo_canvas_box_find_box_child (HippoCanvasBox      *box,

Modified: trunk/common/hippo/hippo-canvas-context.c
==============================================================================
--- trunk/common/hippo/hippo-canvas-context.c	(original)
+++ trunk/common/hippo/hippo-canvas-context.c	Mon Mar 24 19:05:00 2008
@@ -121,32 +121,28 @@
     HIPPO_CANVAS_CONTEXT_GET_IFACE(context)->translate_to_screen(context, item, x_p, y_p);
 }
 
-void
-hippo_canvas_context_affect_color(HippoCanvasContext     *context,
-                                  guint32                *color_rgba_p)
+HippoCanvasStyle *
+hippo_canvas_context_get_style (HippoCanvasContext *context)
 {
-    HippoCanvasContextIface *iface;
+    g_return_val_if_fail(HIPPO_IS_CANVAS_CONTEXT(context), NULL);
     
-    g_return_if_fail(HIPPO_IS_CANVAS_CONTEXT(context));
-
-    iface = HIPPO_CANVAS_CONTEXT_GET_IFACE(context);
-
-    if (iface->affect_color)
-        (* iface->affect_color) (context, color_rgba_p);
+    return HIPPO_CANVAS_CONTEXT_GET_IFACE(context)->get_style(context);
 }
 
-void
-hippo_canvas_context_affect_font_desc(HippoCanvasContext     *context,
-                                      PangoFontDescription   *font_desc)
+double
+hippo_canvas_context_get_resolution (HippoCanvasContext *context)
 {
-    HippoCanvasContextIface *iface;
+    g_return_val_if_fail(HIPPO_IS_CANVAS_CONTEXT(context), 96.);
     
-    g_return_if_fail(HIPPO_IS_CANVAS_CONTEXT(context));
-    
-    iface = HIPPO_CANVAS_CONTEXT_GET_IFACE(context);
+    return HIPPO_CANVAS_CONTEXT_GET_IFACE(context)->get_resolution(context);
+}
+
+PangoFontDescription *
+hippo_canvas_context_get_font (HippoCanvasContext *context)
+{
+    g_return_val_if_fail(HIPPO_IS_CANVAS_CONTEXT(context), NULL);
     
-    if (iface->affect_font_desc)
-        (* iface->affect_font_desc) (context, font_desc);
+    return HIPPO_CANVAS_CONTEXT_GET_IFACE(context)->get_font(context);
 }
 
 void

Modified: trunk/common/hippo/hippo-canvas-context.h
==============================================================================
--- trunk/common/hippo/hippo-canvas-context.h	(original)
+++ trunk/common/hippo/hippo-canvas-context.h	Mon Mar 24 19:05:00 2008
@@ -32,9 +32,15 @@
 
 typedef enum {
     HIPPO_STOCK_COLOR_BG_NORMAL,
-    HIPPO_STOCK_COLOR_BG_PRELIGHT
+    HIPPO_STOCK_COLOR_BG_PRELIGHT,
+    HIPPO_STOCK_COLOR_FG
 } HippoStockColor;
 
+typedef struct _HippoCanvasStyle      HippoCanvasStyle;
+typedef struct _HippoCanvasStyleClass HippoCanvasStyleClass;
+typedef struct _HippoCanvasTheme      HippoCanvasTheme;
+typedef struct _HippoCanvasThemeClass HippoCanvasThemeClass;
+
 typedef struct _HippoCanvasContext      HippoCanvasContext;
 typedef struct _HippoCanvasContextIface HippoCanvasContextIface;
 
@@ -66,13 +72,10 @@
                                                  HippoCanvasItem    *item,
                                                  int                *x_p,
                                                  int                *y_p);
+    HippoCanvasStyle *(* get_style)             (HippoCanvasContext *context);
+    double            (* get_resolution)        (HippoCanvasContext *context);
+    PangoFontDescription *(* get_font)          (HippoCanvasContext *context);
 
-    /* Style methods (should probably be on a separate interface eventually) */
-    void             (* affect_color)           (HippoCanvasContext   *context,
-                                                 guint32              *color_rgba_p);
-    void             (* affect_font_desc)       (HippoCanvasContext   *context,
-                                                 PangoFontDescription *font_desc);
-    
     /* Signals */
 
     /* Inherited style properties (see affect_* methods) have changed.
@@ -104,12 +107,12 @@
                                                   int                *x_p,
                                                   int                *y_p);
 
-void hippo_canvas_context_affect_color       (HippoCanvasContext   *context,
-                                              guint32              *color_rgba_p);
-void hippo_canvas_context_affect_font_desc   (HippoCanvasContext   *context,
-                                              PangoFontDescription *font_desc);
-void hippo_canvas_context_emit_style_changed (HippoCanvasContext   *context,
-                                              gboolean              resize_needed);
+HippoCanvasStyle     *hippo_canvas_context_get_style      (HippoCanvasContext *context);
+double                hippo_canvas_context_get_resolution (HippoCanvasContext *context);
+PangoFontDescription *hippo_canvas_context_get_font       (HippoCanvasContext *context);
+
+void hippo_canvas_context_emit_style_changed(HippoCanvasContext *context,
+                                             gboolean            resize_needed);
 
 G_END_DECLS
 

Modified: trunk/common/hippo/hippo-canvas-item.c
==============================================================================
--- trunk/common/hippo/hippo-canvas-item.c	(original)
+++ trunk/common/hippo/hippo-canvas-item.c	Mon Mar 24 19:05:00 2008
@@ -6,7 +6,7 @@
 #include "hippo-canvas-container.h"
 #include "hippo-canvas-marshal.h"
 
-static void     hippo_canvas_item_base_init (void                  *klass);
+static void     hippo_canvas_item_class_init (void *g_iface);
 
 enum {
     DESTROY,
@@ -29,193 +29,209 @@
 {
     static GType type = 0;
     if (type == 0) {
-        static const GTypeInfo info =
-            {
-                sizeof(HippoCanvasItemIface),
-                hippo_canvas_item_base_init,
-                NULL /* base_finalize */
-            };
-        type = g_type_register_static(G_TYPE_INTERFACE, "HippoCanvasItem",
-                                      &info, 0);
+        type = g_type_register_static_simple (G_TYPE_INTERFACE,
+                                              _("HippoCanvasItem"),
+                                              sizeof (HippoCanvasItemIface),
+                                              (GClassInitFunc) hippo_canvas_item_class_init,
+                                              0, NULL, 0);
     }
 
     return type;
 }
 
 static void
-hippo_canvas_item_base_init(void *klass)
+hippo_canvas_item_class_init(void *g_iface)
 {
-    static gboolean initialized = FALSE;
-
-   if (!initialized) {
-        /* create signals in here */
+    /**
+     * HippoCanvasItem::destroy
+     *
+     * This canvas item has explicitely been destroyed. All references it holds
+     * to other objects will be dropped, and all signal handlers removed.
+     */
+    signals[DESTROY] =
+        g_signal_new ("destroy",
+                      HIPPO_TYPE_CANVAS_ITEM,
+                      G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
+                      0, /* No handler; in C implement cleanups in ->dispose() */
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0);
+    
+    /**
+     * HippoCanvasItem::paint
+     *
+     * All drawing of a canvas item happens in the handlers for
+     * this signal. The rectangle is the bounding box of the
+     * damage region. Most concrete items derive from #HippoCanvasBox,
+     * whose default paint handler invokes a series of more fine-grained
+     * paint handlers to paint the background, content, etc.; usually you
+     * should override one of those fine-grained handlers rather than this
+     * all-encompassing paint.
+     */
+    signals[PAINT] =
+        g_signal_new ("paint",
+                      HIPPO_TYPE_CANVAS_ITEM,
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET(HippoCanvasItemIface, paint),
+                      NULL, NULL,
+                      hippo_canvas_marshal_VOID__POINTER_BOXED,
+                      G_TYPE_NONE, 2, G_TYPE_POINTER, HIPPO_TYPE_RECTANGLE);
+    /**
+     * HippoCanvasItem::request-changed
+     *
+     * Signal emitted when the natural or minimum size of the canvas item
+     * may have changed. The parent canvas or parent canvas item will normally
+     * need to recompute its layout in response.
+     */
+    signals[REQUEST_CHANGED] =
+        g_signal_new ("request-changed",
+                      HIPPO_TYPE_CANVAS_ITEM,
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET(HippoCanvasItemIface, request_changed),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0);
+    /**
+     * HippoCanvasItem::paint-needed
+     *
+     * Signal emitted when a canvas item needs to be repainted. The
+     * rectangle is the bounding box of the areas that need repainting.
+     */
+    signals[PAINT_NEEDED] =
+        g_signal_new ("paint-needed",
+                      HIPPO_TYPE_CANVAS_ITEM,
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET(HippoCanvasItemIface, paint_needed),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__BOXED,
+                      G_TYPE_NONE, 1, HIPPO_TYPE_RECTANGLE);
+    /**
+     * HippoCanvasItem::button-press-event
+     *
+     * Signal emitted when a mouse button is pressed down on the canvas item.
+     */
+    signals[BUTTON_PRESS_EVENT] =
+        g_signal_new ("button-press-event",
+                      HIPPO_TYPE_CANVAS_ITEM,
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET(HippoCanvasItemIface, button_press_event),
+                      g_signal_accumulator_true_handled, NULL,
+                      hippo_canvas_marshal_BOOLEAN__BOXED,
+                      G_TYPE_BOOLEAN, 1, HIPPO_TYPE_EVENT);
+    /**
+     * HippoCanvasItem::button-release-event
+     *
+     * Signal emitted when a mouse button is released on the canvas item.
+     */        
+    signals[BUTTON_RELEASE_EVENT] =
+        g_signal_new ("button-release-event",
+                      HIPPO_TYPE_CANVAS_ITEM,
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET(HippoCanvasItemIface, button_release_event),
+                      g_signal_accumulator_true_handled, NULL,
+                      hippo_canvas_marshal_BOOLEAN__BOXED,
+                      G_TYPE_BOOLEAN, 1, HIPPO_TYPE_EVENT);
+    /**
+     * HippoCanvasItem::motion-notify-event
+     *
+     * Signal emitted when the mouse pointer enters, leaves, or moves within
+     * a canvas item. Note that unlike #GtkWidget, there are not separate
+     * events for enter and leave.
+     */                
+    signals[MOTION_NOTIFY_EVENT] =
+        g_signal_new ("motion-notify-event",
+                      HIPPO_TYPE_CANVAS_ITEM,
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET(HippoCanvasItemIface, motion_notify_event),
+                      g_signal_accumulator_true_handled, NULL,
+                      hippo_canvas_marshal_BOOLEAN__BOXED,
+                      G_TYPE_BOOLEAN, 1, HIPPO_TYPE_EVENT);
 
-        /**
-         * HippoCanvasItem::destroy
-         *
-         * This canvas item has explicitely been destroyed. All references it holds
-         * to other objects will be dropped, and all signal handlers removed.
-         *
-         * (If you 
-         */
-        signals[DESTROY] =
-            g_signal_new ("destroy",
-                          HIPPO_TYPE_CANVAS_ITEM,
-                          G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
-                          0, /* No handler; in C implement cleanups in ->dispose() */
-                          NULL, NULL,
-                          g_cclosure_marshal_VOID__VOID,
-                          G_TYPE_NONE, 0);
-            
-        /**
-         * HippoCanvasItem::paint
-         *
-         * All drawing of a canvas item happens in the handlers for
-         * this signal. The rectangle is the bounding box of the
-         * damage region. Most concrete items derive from #HippoCanvasBox,
-         * whose default paint handler invokes a series of more fine-grained
-         * paint handlers to paint the background, content, etc.; usually you
-         * should override one of those fine-grained handlers rather than this
-         * all-encompassing paint.
-         */
-        signals[PAINT] =
-            g_signal_new ("paint",
-                          HIPPO_TYPE_CANVAS_ITEM,
-                          G_SIGNAL_RUN_LAST,
-                          G_STRUCT_OFFSET(HippoCanvasItemIface, paint),
-                          NULL, NULL,
-                          hippo_canvas_marshal_VOID__POINTER_BOXED,
-                          G_TYPE_NONE, 2, G_TYPE_POINTER, HIPPO_TYPE_RECTANGLE);
-        /**
-         * HippoCanvasItem::request-changed
-         *
-         * Signal emitted when the natural or minimum size of the canvas item
-         * may have changed. The parent canvas or parent canvas item will normally
-         * need to recompute its layout in response.
-         */
-        signals[REQUEST_CHANGED] =
-            g_signal_new ("request-changed",
-                          HIPPO_TYPE_CANVAS_ITEM,
-                          G_SIGNAL_RUN_LAST,
-                          G_STRUCT_OFFSET(HippoCanvasItemIface, request_changed),
-                          NULL, NULL,
-                          g_cclosure_marshal_VOID__VOID,
-                          G_TYPE_NONE, 0);
-        /**
-         * HippoCanvasItem::paint-needed
-         *
-         * Signal emitted when a canvas item needs to be repainted. The
-         * rectangle is the bounding box of the areas that need repainting.
-         */
-        signals[PAINT_NEEDED] =
-            g_signal_new ("paint-needed",
-                          HIPPO_TYPE_CANVAS_ITEM,
-                          G_SIGNAL_RUN_LAST,
-                          G_STRUCT_OFFSET(HippoCanvasItemIface, paint_needed),
-                          NULL, NULL,
-                          g_cclosure_marshal_VOID__BOXED,
-                          G_TYPE_NONE, 1, HIPPO_TYPE_RECTANGLE);
-        /**
-         * HippoCanvasItem::button-press-event
-         *
-         * Signal emitted when a mouse button is pressed down on the canvas item.
-         */
-        signals[BUTTON_PRESS_EVENT] =
-            g_signal_new ("button-press-event",
-                          HIPPO_TYPE_CANVAS_ITEM,
-                          G_SIGNAL_RUN_LAST,
-                          G_STRUCT_OFFSET(HippoCanvasItemIface, button_press_event),
-                          g_signal_accumulator_true_handled, NULL,
-                          hippo_canvas_marshal_BOOLEAN__BOXED,
+    /**
+     * HippoCanvasItem::scroll-event
+     *
+     * Signal emitted when the mouse wheel or other mechanism requests scrolling.
+     */                
+    signals[SCROLL_EVENT] =
+        g_signal_new ("scroll-event",
+                      HIPPO_TYPE_CANVAS_ITEM,
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET(HippoCanvasItemIface, scroll_event),
+                      g_signal_accumulator_true_handled, NULL,
+                      hippo_canvas_marshal_BOOLEAN__BOXED,
                       G_TYPE_BOOLEAN, 1, HIPPO_TYPE_EVENT);
-        /**
-         * HippoCanvasItem::button-release-event
-         *
-         * Signal emitted when a mouse button is released on the canvas item.
-         */        
-        signals[BUTTON_RELEASE_EVENT] =
-            g_signal_new ("button-release-event",
-                          HIPPO_TYPE_CANVAS_ITEM,
-                          G_SIGNAL_RUN_LAST,
-                          G_STRUCT_OFFSET(HippoCanvasItemIface, button_release_event),
-                          g_signal_accumulator_true_handled, NULL,
-                          hippo_canvas_marshal_BOOLEAN__BOXED,
-                          G_TYPE_BOOLEAN, 1, HIPPO_TYPE_EVENT);
-        /**
-         * HippoCanvasItem::motion-notify-event
-         *
-         * Signal emitted when the mouse pointer enters, leaves, or moves within
-         * a canvas item. Note that unlike #GtkWidget, there are not separate
-         * events for enter and leave.
-         */                
-        signals[MOTION_NOTIFY_EVENT] =
-            g_signal_new ("motion-notify-event",
-                          HIPPO_TYPE_CANVAS_ITEM,
-                          G_SIGNAL_RUN_LAST,
-                          G_STRUCT_OFFSET(HippoCanvasItemIface, motion_notify_event),
-                          g_signal_accumulator_true_handled, NULL,
-                          hippo_canvas_marshal_BOOLEAN__BOXED,
-                          G_TYPE_BOOLEAN, 1, HIPPO_TYPE_EVENT);
-
-        /**
-         * HippoCanvasItem::scroll-event
-         *
-         * Signal emitted when the mouse wheel or other mechanism requests scrolling.
-         */                
-        signals[SCROLL_EVENT] =
-            g_signal_new ("scroll-event",
-                          HIPPO_TYPE_CANVAS_ITEM,
-                          G_SIGNAL_RUN_LAST,
-                          G_STRUCT_OFFSET(HippoCanvasItemIface, scroll_event),
-                          g_signal_accumulator_true_handled, NULL,
-                          hippo_canvas_marshal_BOOLEAN__BOXED,
-                          G_TYPE_BOOLEAN, 1, HIPPO_TYPE_EVENT);
         
-        /**
-         * HippoCanvasItem::key-press-event
-         *
-         * Signal emitted when a key is pressed while the canvas item is focused.
-         */
-        signals[KEY_PRESS_EVENT] =
-            g_signal_new ("key-press-event",
-                          HIPPO_TYPE_CANVAS_ITEM,
-                          G_SIGNAL_RUN_LAST,
-                          G_STRUCT_OFFSET(HippoCanvasItemIface, key_press_event),
-                          g_signal_accumulator_true_handled, NULL,
-                          hippo_canvas_marshal_BOOLEAN__BOXED,
-                          G_TYPE_BOOLEAN, 1, HIPPO_TYPE_EVENT);
-        /**
-         * HippoCanvasItem::activated
-         *
-         * Signal emitted when the canvas item is "activated" (e.g. if a button is clicked or
-         * an url is clicked).
-         */        
-        signals[ACTIVATED] =
-            g_signal_new ("activated",
-                          HIPPO_TYPE_CANVAS_ITEM,
-                          G_SIGNAL_RUN_LAST,
-                          G_STRUCT_OFFSET(HippoCanvasItemIface, activated),
-                          NULL, NULL,
-                          g_cclosure_marshal_VOID__VOID,
-                          G_TYPE_NONE, 0);
-        /**
-         * HippoCanvasItem::tooltip-changed
-         *
-         * Signal emitted when the canvas item's tooltip changes. The code displaying the
-         * tooltip may need this signal in order to update in response to changes.
-         */                
-        signals[TOOLTIP_CHANGED] =
-            g_signal_new ("tooltip-changed",
-                          HIPPO_TYPE_CANVAS_ITEM,
-                          G_SIGNAL_RUN_LAST,
-                          G_STRUCT_OFFSET(HippoCanvasItemIface, tooltip_changed),
-                          NULL, NULL,
-                          g_cclosure_marshal_VOID__VOID,
-                          G_TYPE_NONE, 0);        
+    /**
+     * HippoCanvasItem::key-press-event
+     *
+     * Signal emitted when a key is pressed while the canvas item is focused.
+     */
+    signals[KEY_PRESS_EVENT] =
+        g_signal_new ("key-press-event",
+                      HIPPO_TYPE_CANVAS_ITEM,
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET(HippoCanvasItemIface, key_press_event),
+                      g_signal_accumulator_true_handled, NULL,
+                      hippo_canvas_marshal_BOOLEAN__BOXED,
+                      G_TYPE_BOOLEAN, 1, HIPPO_TYPE_EVENT);
+    /**
+     * HippoCanvasItem::activated
+     *
+     * Signal emitted when the canvas item is "activated" (e.g. if a button is clicked or
+     * an url is clicked).
+     */        
+    signals[ACTIVATED] =
+        g_signal_new ("activated",
+                      HIPPO_TYPE_CANVAS_ITEM,
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET(HippoCanvasItemIface, activated),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0);
+    /**
+     * HippoCanvasItem::tooltip-changed
+     *
+     * Signal emitted when the canvas item's tooltip changes. The code displaying the
+     * tooltip may need this signal in order to update in response to changes.
+     */                
+    signals[TOOLTIP_CHANGED] =
+        g_signal_new ("tooltip-changed",
+                      HIPPO_TYPE_CANVAS_ITEM,
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET(HippoCanvasItemIface, tooltip_changed),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0);
 
+    /**** properties *****/
+        
+    /**
+     * HippoCanvasItem:id
+     * 
+     * The ID of the element; this is used when matching a CSS rule selector against
+     * the element tree.
+     */
+    g_object_interface_install_property(g_iface,
+                                        g_param_spec_string("id",
+                                                            _("ID"),
+                                                            _("ID of the item used for CSS rule matching"),
+                                                            NULL,
+                                                            G_PARAM_READABLE | G_PARAM_WRITABLE));
+        
+    /**
+     * HippoCanvasItem:classes
+     * 
+     * Class names for the element; this is used when matching a CSS rule selector against
+     * the element tree. Multiple classes can be specified, specified by spaces. (This property
+     * is called 'classes' and not 'class' to avoid problems with language bindings.)
+     */
+    g_object_interface_install_property(g_iface,
+                                        g_param_spec_string("classes",
+                                                            _("Classes"),
+                                                            _("Class names for the item used for CSS rule matching"),
+                                                            NULL,
+                                                            G_PARAM_READABLE | G_PARAM_WRITABLE));
         
-        initialized = TRUE;
-    }
 }
 
 void

Modified: trunk/common/hippo/hippo-canvas-link.c
==============================================================================
--- trunk/common/hippo/hippo-canvas-link.c	(original)
+++ trunk/common/hippo/hippo-canvas-link.c	Mon Mar 24 19:05:00 2008
@@ -1,13 +1,15 @@
 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
 #include "hippo-canvas-internal.h"
 #include "hippo-canvas-link.h"
+#include "hippo-canvas-style.h"
 #include "hippo-canvas-text.h"
 #include "hippo-canvas-box.h"
 
-static void      hippo_canvas_link_init                (HippoCanvasLink       *link);
-static void      hippo_canvas_link_class_init          (HippoCanvasLinkClass  *klass);
-static void      hippo_canvas_link_iface_init          (HippoCanvasItemIface   *item_class);
-static void      hippo_canvas_link_finalize            (GObject                *object);
+static void      hippo_canvas_link_init                (HippoCanvasLink         *link);
+static void      hippo_canvas_link_class_init          (HippoCanvasLinkClass    *klass);
+static void      hippo_canvas_link_item_iface_init     (HippoCanvasItemIface    *item_class);
+static void      hippo_canvas_link_context_iface_init  (HippoCanvasContextIface *context_class);
+static void      hippo_canvas_link_finalize            (GObject                 *object);
 
 static void hippo_canvas_link_set_property (GObject      *object,
                                             guint         prop_id,
@@ -18,7 +20,6 @@
                                             GValue       *value,
                                             GParamSpec   *pspec);
 
-
 #if 0
 enum {
     LAST_SIGNAL
@@ -33,34 +34,38 @@
 };
 
 G_DEFINE_TYPE_WITH_CODE(HippoCanvasLink, hippo_canvas_link, HIPPO_TYPE_CANVAS_TEXT,
-                        G_IMPLEMENT_INTERFACE(HIPPO_TYPE_CANVAS_ITEM, hippo_canvas_link_iface_init));
+                        G_IMPLEMENT_INTERFACE(HIPPO_TYPE_CANVAS_ITEM, hippo_canvas_link_item_iface_init);
+                        G_IMPLEMENT_INTERFACE(HIPPO_TYPE_CANVAS_CONTEXT, hippo_canvas_link_context_iface_init));
 
 static void
 hippo_canvas_link_init(HippoCanvasLink *link)
 {
-    /* HippoCanvasText *text = HIPPO_CANVAS_TEXT(link); */
-    
-    HIPPO_CANVAS_BOX(link)->clickable = TRUE;
+    HippoCanvasBox *box = HIPPO_CANVAS_BOX(link);
 
-    link->base_attrs = NULL;
+    hippo_canvas_box_set_link_type(box, HIPPO_CANVAS_LINK_LINK);
+    hippo_canvas_box_set_clickable(box, TRUE);
 }
 
 static HippoCanvasItemIface *item_parent_class;
+static HippoCanvasContextIface *context_parent_class;
 
 static void
-hippo_canvas_link_iface_init(HippoCanvasItemIface *item_class)
+hippo_canvas_link_item_iface_init(HippoCanvasItemIface *item_class)
 {
     item_parent_class = g_type_interface_peek_parent(item_class);
 }
 
 static void
+hippo_canvas_link_context_iface_init(HippoCanvasContextIface *context_class)
+{
+    context_parent_class = g_type_interface_peek_parent(context_class);
+}
+
+static void
 hippo_canvas_link_class_init(HippoCanvasLinkClass *klass)
 {
     GObjectClass *object_class = G_OBJECT_CLASS(klass);
-    HippoCanvasBoxClass *box_class = HIPPO_CANVAS_BOX_CLASS(klass);
 
-    box_class->default_color = 0x0033ffff;
-    
     object_class->set_property = hippo_canvas_link_set_property;
     object_class->get_property = hippo_canvas_link_get_property;
 
@@ -79,11 +84,6 @@
 static void
 hippo_canvas_link_finalize(GObject *object)
 {
-    HippoCanvasLink *link = HIPPO_CANVAS_LINK(object);
-
-    if (link->base_attrs)
-        pango_attr_list_unref(link->base_attrs);
-
     G_OBJECT_CLASS(hippo_canvas_link_parent_class)->finalize(object);
 }
 
@@ -96,34 +96,19 @@
 }
 
 static void
-sync_attributes(HippoCanvasLink *link)
-{
-    PangoAttribute *a;
-
-    if (link->base_attrs)
-        pango_attr_list_unref(link->base_attrs);
-    link->base_attrs = pango_attr_list_new();
-
-    a = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
-    a->start_index = 0;
-    a->end_index = G_MAXUINT;
-    pango_attr_list_insert(link->base_attrs, a);
-
-    if (link->visited) {
-        a = pango_attr_foreground_new(0x6666, 0x6666, 0x6666);
-        a->start_index = 0;
-        a->end_index = G_MAXUINT;
-        pango_attr_list_insert(link->base_attrs, a);
-    }
-    g_object_set(link, "attributes", link->base_attrs, NULL);
-}
-
-static void
 hippo_canvas_link_set_visited(HippoCanvasLink    *link,
                               gboolean            visited)
 {
-    link->visited = visited;
-    sync_attributes(link);
+    visited = visited != FALSE;
+    
+    if (visited != link->visited) {
+        HippoCanvasBox *box = HIPPO_CANVAS_BOX(link);
+
+        link->visited = visited;
+        
+        hippo_canvas_style_set_link_type(box->style,
+                                         visited ? HIPPO_CANVAS_LINK_VISITED : HIPPO_CANVAS_LINK_LINK);
+    }
 }
 
 static void

Modified: trunk/common/hippo/hippo-canvas-link.h
==============================================================================
--- trunk/common/hippo/hippo-canvas-link.h	(original)
+++ trunk/common/hippo/hippo-canvas-link.h	Mon Mar 24 19:05:00 2008
@@ -22,7 +22,6 @@
 struct _HippoCanvasLink {
     HippoCanvasText text;
     gboolean visited;
-    PangoAttrList *base_attrs;
 };
 
 struct _HippoCanvasLinkClass {

Modified: trunk/common/hippo/hippo-canvas-style.c
==============================================================================
--- trunk/common/hippo/hippo-canvas-style.c	(original)
+++ trunk/common/hippo/hippo-canvas-style.c	Mon Mar 24 19:05:00 2008
@@ -1,9 +1,14 @@
 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <libcroco/libcroco.h>
+
 #include "hippo-canvas-internal.h"
+#include "hippo-canvas-theme-internal.h"
 #include "hippo-canvas-type-builtins.h"
 #include "hippo-canvas-style.h"
-#include <stdlib.h>
-#include <string.h>
 
 static void hippo_canvas_style_init               (HippoCanvasStyle          *style);
 static void hippo_canvas_style_class_init         (HippoCanvasStyleClass     *klass);
@@ -11,16 +16,6 @@
 static void hippo_canvas_style_finalize           (GObject                 *object);
 
 
-static void hippo_canvas_style_set_property (GObject      *object,
-                                             guint         prop_id,
-                                             const GValue *value,
-                                             GParamSpec   *pspec);
-static void hippo_canvas_style_get_property (GObject      *object,
-                                             guint         prop_id,
-                                             GValue       *value,
-                                             GParamSpec   *pspec);
-
-
 #if 0
 enum {
     LAST_SIGNAL
@@ -29,21 +24,33 @@
 static int signals[LAST_SIGNAL];
 #endif
 
-enum {
-    PROP_0,
-    PROP_FONT,
-    PROP_FONT_DESC,
-    PROP_COLOR,
-    PROP_COLOR_SET
-};
-
 struct _HippoCanvasStyle {
     GObject parent;
 
-    guint32 color_rgba; 
+    HippoCanvasContext *context;
+    HippoCanvasStyle *parent_style;
+    HippoCanvasTheme *theme;
+    
     PangoFontDescription *font_desc;
+
+    guint32 background_color;
+    guint32 foreground_color;
+    guint32 border_color[4];
+    double border_width[4];
+    guint padding[4];
+
+    GType element_type;
+    char *element_id;
+    char *element_class;
+
+    CRDeclaration **properties;
+    int n_properties;
     
-    guint color_set : 1;
+    guint properties_computed : 1;
+    guint borders_computed : 1;
+    guint background_computed : 1;
+    guint foreground_computed : 1;
+    guint link_type : 2;
 };
 
 struct _HippoCanvasStyleClass {
@@ -56,7 +63,7 @@
 static void
 hippo_canvas_style_init(HippoCanvasStyle *style)
 {
-    style->color_rgba = HIPPO_CANVAS_DEFAULT_COLOR;
+    style->link_type = HIPPO_CANVAS_LINK_NONE;
 }
 
 static void
@@ -64,45 +71,8 @@
 {
     GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-    object_class->set_property = hippo_canvas_style_set_property;
-    object_class->get_property = hippo_canvas_style_get_property;
-
     object_class->dispose = hippo_canvas_style_dispose;
     object_class->finalize = hippo_canvas_style_finalize;
-
-
-    g_object_class_install_property(object_class,
-                                    PROP_COLOR,
-                                    g_param_spec_uint("color",
-                                                      _("Foreground Color"),
-                                                      _("32-bit RGBA foreground color"),
-                                                      0,
-                                                      G_MAXUINT,
-                                                      HIPPO_CANVAS_DEFAULT_COLOR,
-                                                      G_PARAM_READABLE | G_PARAM_WRITABLE));
-
-    g_object_class_install_property(object_class,
-                                    PROP_COLOR_SET,
-                                    g_param_spec_boolean("color-set",
-                                                         _("Foreground Color Set"),
-                                                         _("Whether a foreground color was set"),
-                                                         FALSE,
-                                                         G_PARAM_READABLE | G_PARAM_WRITABLE));
-
-    g_object_class_install_property(object_class,
-                                    PROP_FONT,
-                                    g_param_spec_string("font",
-                                                        _("Font"),
-                                                        _("Font description as a string"),
-                                                        NULL,
-                                                        G_PARAM_READABLE | G_PARAM_WRITABLE));
-    g_object_class_install_property(object_class,
-                                    PROP_FONT_DESC,
-                                    g_param_spec_boxed("font-desc",
-                                                       _("Font Description"),
-                                                       _("Font description as a PangoFontDescription object"),
-                                                       PANGO_TYPE_FONT_DESCRIPTION,
-                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
 }
 
 static void
@@ -118,6 +88,15 @@
 {
     HippoCanvasStyle *style = HIPPO_CANVAS_STYLE(object);
 
+    g_free (style->element_id);
+    g_free (style->element_class);
+
+    if (style->properties) {
+        g_free(style->properties);
+        style->properties = NULL;
+        style->n_properties = 0;
+    }
+
     if (style->font_desc) {
         pango_font_description_free(style->font_desc);
         style->font_desc = NULL;
@@ -126,186 +105,1196 @@
     G_OBJECT_CLASS(hippo_canvas_style_parent_class)->finalize(object);
 }
 
-static int
-parse_int32(const char *s)
+HippoCanvasStyle *
+hippo_canvas_style_new (HippoCanvasContext    *context,
+                        HippoCanvasStyle      *parent_style,
+                        HippoCanvasTheme      *theme,
+                        GType                  element_type,
+                        const char            *element_id,
+                        const char            *element_class)
+{
+    HippoCanvasStyle *style;
+    
+    g_return_val_if_fail(HIPPO_IS_CANVAS_CONTEXT(context), NULL);
+    g_return_val_if_fail(parent_style == NULL || HIPPO_IS_CANVAS_STYLE(parent_style), NULL);
+
+    style = g_object_new(HIPPO_TYPE_CANVAS_STYLE, NULL);
+    
+    style->context = g_object_ref(context);
+    if (parent_style != NULL)
+        style->parent_style = g_object_ref(parent_style);
+    else
+        style->parent_style = NULL;
+
+    if (theme == NULL && parent_style != NULL)
+        theme = parent_style->theme;
+    
+    if (theme != NULL)
+        style->theme = g_object_ref(theme);
+
+    style->element_type = element_type;
+    style->element_id = g_strdup(element_id);
+    style->element_class = g_strdup(element_class);
+
+    return style;
+}
+
+void
+hippo_canvas_style_set_link_type (HippoCanvasStyle    *style,
+                                  HippoCanvasLinkType  link_type)
+{
+    g_return_if_fail(HIPPO_IS_CANVAS_STYLE(style));
+
+    style->link_type = link_type;
+}
+
+HippoCanvasLinkType
+hippo_canvas_style_get_link_type (HippoCanvasStyle *style)
+{
+    g_return_val_if_fail(HIPPO_IS_CANVAS_STYLE(style), HIPPO_CANVAS_LINK_NONE);
+    
+    return style->link_type;
+}
+
+HippoCanvasStyle *
+hippo_canvas_style_get_parent (HippoCanvasStyle *style)
 {
-    char *end;
-    long v;
+    g_return_val_if_fail(HIPPO_IS_CANVAS_STYLE(style), NULL);
+    
+    return style->parent_style;
+}
+
+HippoCanvasTheme *
+hippo_canvas_style_get_theme (HippoCanvasStyle *style)
+{
+    g_return_val_if_fail(HIPPO_IS_CANVAS_STYLE(style), NULL);
+    
+    return style->theme;
+}
 
-    end = NULL;
-    v = strtol(s, &end, 10);
+GType
+hippo_canvas_style_get_element_type (HippoCanvasStyle *style)
+{
+    g_return_val_if_fail(HIPPO_IS_CANVAS_STYLE(style), G_TYPE_NONE);
+
+    return style->element_type;
+}
+
+const char *
+hippo_canvas_style_get_element_id (HippoCanvasStyle *style)
+{
+    g_return_val_if_fail(HIPPO_IS_CANVAS_STYLE(style), NULL);
 
-    if (end == NULL) {
-        g_warning("Failed to parse '%s' as 32-bit integer", s);
-        return 0;
+    return style->element_id;
+}
+
+const char *
+hippo_canvas_style_get_element_class (HippoCanvasStyle *style)
+{
+    g_return_val_if_fail(HIPPO_IS_CANVAS_STYLE(style), NULL);
+
+    return style->element_class;
+}
+
+static void
+ensure_properties(HippoCanvasStyle *style)
+{
+    if (!style->properties_computed) {
+        style->properties_computed = TRUE;
+
+        if (style->theme)
+            _hippo_canvas_theme_get_matched_properties(style->theme, style,
+                                                       &style->properties, &style->n_properties);
     }
+}
 
-    return v;
+typedef enum {
+    VALUE_FOUND,
+    VALUE_NOT_FOUND,
+    VALUE_INHERIT
+} GetFromTermResult;
+
+static GetFromTermResult
+get_color_from_term(HippoCanvasStyle *style,
+                    CRTerm           *term,
+                    guint32          *color)
+{
+    CRRgb rgb;
+    enum CRStatus status;
+
+    status = cr_rgb_set_from_term(&rgb, term);
+    if (status != CR_OK)
+        return VALUE_NOT_FOUND;
+            
+    if (rgb.inherit)
+        return VALUE_INHERIT;
+            
+    if (rgb.is_percentage)
+        cr_rgb_compute_from_percentage(&rgb);
+            
+    *color = (rgb.red << 24) | (rgb.green << 16) | (rgb.blue << 8) | 0xff;
+
+    return VALUE_FOUND;
 }
 
-/* Latest pango supports "NNpx" sizes, but FC5 Pango (1.12) does not */
-static int
-parse_absolute_size_hack(const char *s)
+gboolean
+hippo_canvas_style_get_color (HippoCanvasStyle     *style,
+                              const char           *property_name,
+                              gboolean              inherit,
+                              guint32              *color)
 {
-    const char *p;
-    const char *number;
 
-    p = strstr(s, "px");
-    if (p == NULL)
-        return -1;
+    int i;
 
-    number = p;
-    --number;
-    while (number > s) {
-        if (!g_ascii_isdigit(*number)) {
-            ++number;
-            break;
+    ensure_properties(style);
+
+    for (i = style->n_properties - 1; i >= 0; i--) {
+        CRDeclaration *decl = style->properties[i];
+        
+        if (strcmp(decl->property->stryng->str, property_name) == 0) {
+            GetFromTermResult result = get_color_from_term(style, decl->value, color);
+            if (result == VALUE_FOUND) {
+                return TRUE;
+            } else if (result == VALUE_INHERIT) {
+                if (style->parent_style)
+                    return hippo_canvas_style_get_color(style->parent_style, property_name, inherit, color);
+                else
+                    break;
+            }
         }
-        --number;
     }
 
-    return parse_int32(number);
+    return FALSE;
 }
 
-static void
-hippo_canvas_style_set_property(GObject         *object,
-                                guint            prop_id,
-                                const GValue    *value,
-                                GParamSpec      *pspec)
+gboolean
+hippo_canvas_style_get_double (HippoCanvasStyle     *style,
+                               const char           *property_name,
+                               gboolean              inherit,
+                               double               *value)
 {
-    HippoCanvasStyle *style;
+    gboolean result = FALSE;
+    int i;
+
+    ensure_properties(style);
+
+    for (i = style->n_properties - 1; i >= 0; i--) {
+        CRDeclaration *decl = style->properties[i];
+        
+        if (strcmp(decl->property->stryng->str, property_name) == 0) {
+            CRTerm *term = decl->value;
 
-    style = HIPPO_CANVAS_STYLE(object);
+            if (term->type != TERM_NUMBER || term->content.num->type != NUM_GENERIC)
+                continue;
 
-    switch (prop_id) {
-    case PROP_COLOR:
-        style->color_rgba = g_value_get_uint(value);
-        if (style->color_set != TRUE) {
-            style->color_set = TRUE;
-            g_object_notify(G_OBJECT(style), "color-set");
+            *value = term->content.num->val;
+            result = TRUE;
+            break;
         }
+    }
+
+    if (!result && inherit && style->parent_style)
+        result = hippo_canvas_style_get_double(style->parent_style, property_name, inherit, value);
+
+    return result;
+}
+
+static PangoFontDescription *
+get_parent_font(HippoCanvasStyle *style)
+{
+    if (style->parent_style)
+        return hippo_canvas_style_get_font(style->parent_style);
+    else
+        return hippo_canvas_context_get_font(style->context);
+}
+
+static GetFromTermResult
+get_length_from_term(HippoCanvasStyle *style,
+                     CRTerm           *term,
+                     gboolean          use_parent_font,
+                     gdouble          *length)
+{
+    CRNum *num;
+            
+    enum {
+        ABSOLUTE,
+        POINTS,
+        FONT_RELATIVE,
+    } type = ABSOLUTE;
+    
+    double multiplier = 1.0;
+    
+    if (term->type != TERM_NUMBER) {
+        g_warning("Ignoring length property that isn't a number");
+        return FALSE;
+    }
+    
+    num = term->content.num;
+    
+    switch (num->type) {
+    case NUM_LENGTH_PX:
+        type = ABSOLUTE;
+        multiplier = 1;
+        break;
+    case NUM_LENGTH_PT:
+        type = POINTS;
+        multiplier = 1;
+        break;
+    case NUM_LENGTH_IN:
+        type = POINTS;
+        multiplier = 72;
         break;
-    case PROP_COLOR_SET:
-        style->color_set = g_value_get_boolean(value);
+    case NUM_LENGTH_CM:
+        type = POINTS;
+        multiplier = 72. / 2.54;
         break;
-    case PROP_FONT:
+    case NUM_LENGTH_MM:
+        type = POINTS;
+        multiplier = 72. / 25.4;
+        break;
+    case NUM_LENGTH_PC:
+        type = POINTS;
+        multiplier = 12. / 25.4;
+        break;
+    case NUM_LENGTH_EM:
+        {
+            type = FONT_RELATIVE;
+            multiplier = 1;
+            break; 
+        }
+    case NUM_LENGTH_EX:
+        {
+            /* Doing better would require actually resolving the font description
+             * to a specific font, and Pango doesn't have an ex metric anyways,
+             * so we'd have to try and synthesize it by complicated means.
+             *
+             * The 0.5em is the CSS spec suggested thing to use when nothing
+             * better is available.
+             */
+            type = FONT_RELATIVE;
+            multiplier = 0.5;
+            break; 
+        }
+        
+    case NUM_INHERIT:
+        return VALUE_INHERIT;
+        
+    case NUM_AUTO:
+        g_warning("'auto' not supported for lengths");
+        return VALUE_NOT_FOUND;
+        
+    case NUM_GENERIC:
+        g_warning("length values must specify a unit");
+        return VALUE_NOT_FOUND;
+        
+    case NUM_PERCENTAGE:
+        g_warning("percentage lengths not currently supported");
+        return VALUE_NOT_FOUND;
+        
+    case NUM_ANGLE_DEG:
+    case NUM_ANGLE_RAD:
+    case NUM_ANGLE_GRAD:
+    case NUM_TIME_MS:
+    case NUM_TIME_S:
+    case NUM_FREQ_HZ:
+    case NUM_FREQ_KHZ:
+    case NUM_UNKNOWN_TYPE:
+    case NB_NUM_TYPE:
+        g_warning("Ignoring invalid type of number of length property");
+        return VALUE_NOT_FOUND;
+    }
+    
+    switch (type) {
+    case ABSOLUTE:
+        *length = num->val * multiplier;
+        break;
+    case POINTS:
+        {
+            double resolution = hippo_canvas_context_get_resolution(style->context);
+            *length = num->val * multiplier * (resolution / 72.);
+        }
+        break;
+    case FONT_RELATIVE:
         {
-            const char *s;
             PangoFontDescription *desc;
-            int absolute;
-            s = g_value_get_string(value);
-            if (s != NULL) {
-                char *no_px = NULL;
-                absolute = parse_absolute_size_hack(s);
-                if (absolute >= 0) {
-                    // get the "px" out of the string
-                    GString *no_px_g = g_string_new(NULL);
-                    const char *p;
-                    p = strstr(s, "px");
-                    g_assert(p != NULL);
-                    g_string_append_len(no_px_g, s, p - s);
-                    g_string_append_len(no_px_g, p + 2, strlen(p + 2));
-                    no_px = g_string_free(no_px_g, FALSE);
+            
+            if (use_parent_font)
+                desc = get_parent_font(style);
+            else
+                desc = hippo_canvas_style_get_font(style);
+            
+            if (pango_font_description_get_size_is_absolute(desc)) {
+                *length = num->val * multiplier * pango_font_description_get_size(desc);
+            } else {
+                double resolution = hippo_canvas_context_get_resolution(style->context);
+                *length = num->val * multiplier * (resolution / 72.) * pango_font_description_get_size(desc);
+            }
+        }
+        break;
+    default:
+        g_assert_not_reached();
+    }
+    
+    return VALUE_FOUND;
+}
+
+static GetFromTermResult
+get_length_internal(HippoCanvasStyle     *style,
+                    const char           *property_name,
+                    const char           *suffixed,
+                    gdouble              *length)
+{
+    int i;
+
+    ensure_properties(style);
+
+    for (i = style->n_properties - 1; i >= 0; i--) {
+        CRDeclaration *decl = style->properties[i];
+        
+        if (strcmp(decl->property->stryng->str, property_name) == 0 ||
+            (suffixed != NULL && strcmp(decl->property->stryng->str, suffixed) == 0)) {
+            GetFromTermResult result = get_length_from_term(style, decl->value, FALSE, length);
+            if (result != VALUE_NOT_FOUND)
+                return result;
+        }
+    }
+
+    return VALUE_NOT_FOUND;
+}
+
+gboolean
+hippo_canvas_style_get_length (HippoCanvasStyle     *style,
+                               const char           *property_name,
+                               gboolean              inherit,
+                               gdouble              *length)
+{
+    GetFromTermResult result = get_length_internal(style, property_name, NULL, length);
+    if (result == VALUE_FOUND)
+        return TRUE;
+    else if (result == VALUE_INHERIT)
+        inherit = TRUE;
+
+    if (inherit && style->parent_style &&
+        hippo_canvas_style_get_length(style->parent_style, property_name, inherit, length))
+        return TRUE;
+    else
+        return FALSE;
+}
+
+static void
+do_border_property(HippoCanvasStyle *style,
+                   CRDeclaration    *decl)
+{
+    const char *property_name = decl->property->stryng->str + 6; /* Skip 'border' */
+    HippoCanvasSide side = (HippoCanvasSide)-1;
+    guint32 color;
+    gboolean color_set = FALSE;
+    double width;
+    gboolean width_set = FALSE;
+    int j;
+
+    if (g_str_has_prefix(property_name, "-left")) {
+        side = HIPPO_CANVAS_SIDE_LEFT;
+        property_name += 5;
+    } else if (g_str_has_prefix(property_name, "-right")) {
+        side = HIPPO_CANVAS_SIDE_RIGHT;
+        property_name += 6;
+    } else if (g_str_has_prefix(property_name, "-top")) {
+        side = HIPPO_CANVAS_SIDE_TOP;
+        property_name += 4;
+    } else if (g_str_has_prefix(property_name, "-bottom")) {
+        side = HIPPO_CANVAS_SIDE_BOTTOM;
+        property_name += 7;
+    }
+    
+    if (strcmp(property_name, "") == 0) {
+        /* Set value for width/color/style in any order */
+        CRTerm *term;
+        
+        for (term = decl->value; term; term = term->next) {
+            GetFromTermResult result;
+            
+            if (term->type == TERM_IDENT) {
+                const char *ident = term->content.str->stryng->str;
+                if (strcmp(ident, "none") == 0 || strcmp(ident, "hidden") == 0) {
+                    width = 0.;
+                    continue;
+                } else if (strcmp(ident, "solid") == 0) {
+                    /* The only thing we support */
+                    continue;
+                } else if (strcmp(ident, "dotted") == 0 ||
+                           strcmp(ident, "dashed") == 0 ||
+                           strcmp(ident, "solid") == 0 ||
+                           strcmp(ident, "double") == 0 ||
+                           strcmp(ident, "groove") == 0 ||
+                           strcmp(ident, "ridge") == 0 ||
+                           strcmp(ident, "inset") == 0 ||
+                           strcmp(ident, "outset") == 0) {
+                    /* Treat the same as solid */
+                    continue;
                 }
-                desc = pango_font_description_from_string(no_px ? no_px : s);
-                g_free(no_px);
-                if (desc == NULL) {
-                    g_warning("Failed to parse font description string '%s'", s);
-                } else {
-                    if (absolute >= 0) {
-                        pango_font_description_set_absolute_size(desc, absolute * PANGO_SCALE);
-                    }
+                
+                /* Presumably a color, fall through */
+            }
+            
+            if (term->type == TERM_NUMBER) {
+                result = get_length_from_term(style, term, FALSE, &width);
+                if (result != VALUE_NOT_FOUND) {
+                    width_set = result == VALUE_FOUND;
+                    continue;
+                }
+            }
+            
+            result = get_color_from_term(style, term, &color);
+            if (result != VALUE_NOT_FOUND) {
+                color_set = result == VALUE_FOUND;
+                continue;
+            }
+        }
+        
+    } else if (strcmp(property_name, "-color") == 0) {
+        if (decl->value == NULL || decl->value->next != NULL)
+            return;
+        
+        if (get_color_from_term(style, decl->value, &color) == VALUE_FOUND) { /* Ignore inherit */
+            color_set = TRUE;
+        }
+    } else if (strcmp(property_name, "-width") == 0) {
+        if (decl->value == NULL || decl->value->next != NULL)
+            return;
+        
+        if (get_length_from_term(style, decl->value, FALSE, &width) == VALUE_FOUND) { /* Ignore inherit */
+            width_set = TRUE;
+        }
+    }
+    
+    if (side == (HippoCanvasSide)-1) {
+        for (j = 0; j < 4; j++) {
+            if (color_set)
+                style->border_color[j] = color;
+            if (width_set)
+                style->border_width[j] = width;
+        }
+    } else {
+        if (color_set)
+            style->border_color[side] = color;
+        if (width_set)
+            style->border_width[side] = width;
+    }
+}
+
+static void
+do_padding_property_term(HippoCanvasStyle *style,
+                         CRTerm           *term,
+                         gboolean          left,
+                         gboolean          right,
+                         gboolean          top,
+                         gboolean          bottom)
+{
+    gdouble value;
+
+    if (get_length_from_term(style, term, FALSE, &value) != VALUE_FOUND) 
+        return;
+    
+    if (left)
+        style->padding[HIPPO_CANVAS_SIDE_LEFT] = value;
+    if (right)
+        style->padding[HIPPO_CANVAS_SIDE_RIGHT] = value;
+    if (top)
+        style->padding[HIPPO_CANVAS_SIDE_TOP] = value;
+    if (bottom)
+        style->padding[HIPPO_CANVAS_SIDE_BOTTOM] = value;
+}
+
+static void
+do_padding_property(HippoCanvasStyle *style,
+                    CRDeclaration    *decl)
+{
+    const char *property_name = decl->property->stryng->str + 7; /* Skip 'padding' */
 
-                    if ((pango_font_description_get_set_fields(desc) & PANGO_FONT_MASK_SIZE) != 0 &&
-                        pango_font_description_get_size(desc) <= 0) {
-                        g_warning("font size set to 0, not going to work well");
+    if (strcmp(property_name, "") == 0) {
+        /* Slight deviation ... if we don't understand some of the terms and understand others,
+         * then we set the ones we understand and ignore the others instead of ignoring the
+         * whole thing
+         */
+        if (decl->value == NULL) /* 0 values */
+            return;
+        else if (decl->value->next == NULL) { /* 1 value */
+            do_padding_property_term(style, decl->value, TRUE, TRUE, TRUE, TRUE); /* left/right/top/bottom */
+            return;
+        }  else if (decl->value->next->next == NULL) { /* 2 values */
+            do_padding_property_term(style, decl->value,       FALSE, FALSE, TRUE,  TRUE);  /* top/bottom */
+            do_padding_property_term(style, decl->value->next, TRUE, TRUE,   FALSE, FALSE); /* left/right */
+        }  else if (decl->value->next->next->next == NULL) { /* 3 values */
+            do_padding_property_term(style, decl->value,             FALSE, FALSE, TRUE,  FALSE); /* top */
+            do_padding_property_term(style, decl->value->next,       TRUE,  TRUE,  FALSE, FALSE); /* left/right */
+            do_padding_property_term(style, decl->value->next->next, FALSE, FALSE, FALSE, TRUE);  /* bottom */
+        } else  if (decl->value->next->next->next == NULL) { /* 4 values */
+            do_padding_property_term(style, decl->value,                   FALSE, FALSE, TRUE,  FALSE);
+            do_padding_property_term(style, decl->value->next,             FALSE, TRUE, FALSE, FALSE); /* left */
+            do_padding_property_term(style, decl->value->next->next,       FALSE, FALSE, FALSE, TRUE);
+            do_padding_property_term(style, decl->value->next->next->next, TRUE,  FALSE, FALSE, TRUE); /* left */
+        } else {
+            g_warning("Too many values for padding property");
+            return;
+        }
+    } else {
+        if (decl->value == NULL || decl->value->next != NULL)
+            return;
+        
+        if (strcmp(property_name, "-left") == 0) {
+            do_padding_property_term(style, decl->value, TRUE,  FALSE, FALSE, FALSE);
+        } else if (strcmp(property_name, "-right") == 0) {
+            do_padding_property_term(style, decl->value, FALSE, TRUE,  FALSE, FALSE);
+        } else if (strcmp(property_name, "-top") == 0) {
+            do_padding_property_term(style, decl->value, FALSE, FALSE, TRUE,  FALSE);
+        } else if (strcmp(property_name, "-bottom") == 0) {
+            do_padding_property_term(style, decl->value, FALSE, FALSE, FALSE, TRUE);
+        }
+    }
+}
+
+static void
+ensure_borders(HippoCanvasStyle *style)
+{
+    int i, j;
+    
+    if (style->borders_computed)
+        return;
+
+    style->borders_computed = TRUE;
+    
+    ensure_properties(style);
+
+    for (j = 0; j < 4; j++) {
+        style->border_width[j] = 0;
+        style->border_color[j] = 0x00000000;
+    }
+    
+    for (i = 0; i < style->n_properties; i++) {
+        CRDeclaration *decl = style->properties[i];
+        const char *property_name = decl->property->stryng->str;
+
+        if (g_str_has_prefix(property_name, "border")) {
+            do_border_property(style, decl);
+        } else if (g_str_has_prefix(property_name, "padding")) {
+            do_padding_property(style, decl);
+        }
+    }
+}
+
+double
+hippo_canvas_style_get_border_width(HippoCanvasStyle *style,
+                                    HippoCanvasSide   side)
+{
+    g_return_val_if_fail(HIPPO_IS_CANVAS_STYLE(style), 0.);
+    g_return_val_if_fail(side >= HIPPO_CANVAS_SIDE_LEFT && side <= HIPPO_CANVAS_SIDE_BOTTOM, 0.);
+    
+    ensure_borders(style);
+
+    return style->border_width[side];
+}
+
+static GetFromTermResult
+get_background_color_from_term(HippoCanvasStyle *style,
+                               CRTerm           *term,
+                               guint32          *color)
+{
+    GetFromTermResult result = get_color_from_term(style, term, color);
+    if (result == VALUE_NOT_FOUND) {
+        if (term->type == TERM_IDENT &&
+            strcmp(term->content.str->stryng->str, "transparent") == 0)
+        {
+            *color = 0x00000000;
+            return VALUE_FOUND;
+        }
+    }
+
+    return result;
+}
+
+guint32
+hippo_canvas_style_get_background_color (HippoCanvasStyle *style)
+{
+    g_return_val_if_fail(HIPPO_IS_CANVAS_STYLE(style), 0);
+    
+    if (!style->background_computed) {
+        int i;
+        
+        style->background_computed = TRUE;
+        style->background_color = 0x00000000; /* Transparent */
+        
+        ensure_properties(style);
+        
+        for (i = style->n_properties - 1; i >= 0; i--) {
+            CRDeclaration *decl = style->properties[i];
+            const char *property_name = decl->property->stryng->str;
+            
+            if (!g_str_has_prefix(property_name, "background"))
+                continue;
+
+            if (strcmp(property_name, "") == 0) {
+                /* We're very liberal here ... if any term in the expression we take it, and 
+                 * we ignore the rest. The actual specification is:
+                 *
+                 * background: [<'background-color'> || <'background-image'> || <'background-repeat'> || <'background-attachment'> || <'background-position'>] | inherit
+                 */
+
+                CRTerm *term;
+                for (term = decl->value; term; term = term->next) {
+                    GetFromTermResult result = get_background_color_from_term(style, term, &style->background_color);
+                    if (result == VALUE_FOUND) {
+                        return style->background_color;
+                    } else if (result == VALUE_INHERIT) {
+                        if (style->parent_style)
+                            style->background_color = hippo_canvas_style_get_background_color(style->parent_style);
+
+                        return style->background_color;
                     }
                 }
-            } else {
-                desc = NULL;
+                
+            } else if (strcmp(property_name, "-color") == 0) {
+                GetFromTermResult result;
+                
+                if (decl->value == NULL || decl->value->next != NULL)
+                    continue;
+
+                result = get_background_color_from_term(style, decl->value, &style->background_color);
+                if (result == VALUE_FOUND) {
+                    return style->background_color;
+                } else if (result == VALUE_INHERIT) {
+                    if (style->parent_style)
+                        style->background_color = hippo_canvas_style_get_background_color(style->parent_style);
+                    
+                    return style->background_color;
+                }
             }
+        }
+    }
+     
+    return style->background_color;
+}
 
+guint32
+hippo_canvas_style_get_foreground_color (HippoCanvasStyle *style)
+{
+    g_return_val_if_fail(HIPPO_IS_CANVAS_STYLE(style), 0);
+    
+    if (!style->foreground_computed) {
+        int i;
+
+        style->foreground_computed = TRUE;
+        
+        ensure_properties(style);
+        
+        for (i = style->n_properties - 1; i >= 0; i--) {
+            CRDeclaration *decl = style->properties[i];
             
-            g_object_set(object, "font-desc", desc, NULL);
-            if (desc)
-                pango_font_description_free(desc);
-        }
-        break;
-    case PROP_FONT_DESC:
-        {
-            PangoFontDescription *desc = g_value_get_boxed(value);
+            if (strcmp(decl->property->stryng->str, "color") == 0) {
+                GetFromTermResult result = get_color_from_term(style, decl->value, &style->foreground_color);
+                if (result == VALUE_FOUND) {
+                    return style->foreground_color;
+                } else if (result == VALUE_INHERIT) {
+                    if (style->parent_style) {
+                        style->foreground_color = hippo_canvas_style_get_foreground_color(style->parent_style);
+                        return style->foreground_color;
+                    }
 
-            if (!(desc == NULL && style->font_desc == NULL)) {
-                if (style->font_desc) {
-                    pango_font_description_free(style->font_desc);
-                    style->font_desc = NULL;
+                    break;
                 }
-                if (desc != NULL) {
-                    style->font_desc = pango_font_description_copy(desc);
+            }
+        }
+
+        style->foreground_color = hippo_canvas_context_get_color(style->context, HIPPO_STOCK_COLOR_FG);
+    }
+     
+    return style->foreground_color;
+    
+}
+
+guint32
+hippo_canvas_style_get_border_color(HippoCanvasStyle *style,
+                                    HippoCanvasSide   side)
+{
+    g_return_val_if_fail(HIPPO_IS_CANVAS_STYLE(style), 0);
+    g_return_val_if_fail(side >= HIPPO_CANVAS_SIDE_LEFT && side <= HIPPO_CANVAS_SIDE_BOTTOM, 0.);
+    
+    ensure_borders(style);
+
+    return style->border_color[side];
+}
+
+double
+hippo_canvas_style_get_padding(HippoCanvasStyle *style,
+                               HippoCanvasSide   side)
+{
+    g_return_val_if_fail(HIPPO_IS_CANVAS_STYLE(style), 0.);
+    g_return_val_if_fail(side >= HIPPO_CANVAS_SIDE_LEFT && side <= HIPPO_CANVAS_SIDE_BOTTOM, 0.);
+    
+    ensure_borders(style);
+
+    return style->padding[side];
+}
+
+HippoTextDecoration
+hippo_canvas_style_get_text_decoration (HippoCanvasStyle *style)
+{
+    int i;
+
+    ensure_properties(style);
+
+    for (i = style->n_properties - 1; i >= 0; i--) {
+        CRDeclaration *decl = style->properties[i];
+        
+        if (strcmp(decl->property->stryng->str, "text-decoration") == 0) {
+            CRTerm *term = decl->value;
+            HippoTextDecoration decoration = 0;
+
+            /* Specification is none | [ underline || overline || line-through || blink ] | inherit
+             *
+             * We're a bit more liberal, and for example treat 'underline none' as the same as
+             * none.
+             */
+            for (; term; term = term->next) {
+                if (term->type != TERM_IDENT)
+                    goto next_decl;
+
+                if (strcmp(term->content.str->stryng->str, "none") == 0) {
+                    return 0;
+                } else if (strcmp(term->content.str->stryng->str, "inherit") == 0) {
+                    if (style->parent_style)
+                        return hippo_canvas_style_get_text_decoration(style->parent_style);
+                } else if (strcmp(term->content.str->stryng->str, "underline") == 0) {
+                    decoration |= HIPPO_TEXT_DECORATION_UNDERLINE;
+                } else if (strcmp(term->content.str->stryng->str, "overline") == 0) {
+                    decoration |= HIPPO_TEXT_DECORATION_OVERLINE;
+                } else if (strcmp(term->content.str->stryng->str, "line-through") == 0) {
+                    decoration |= HIPPO_TEXT_DECORATION_LINE_THROUGH;
+                } else if (strcmp(term->content.str->stryng->str, "blink") == 0) {
+                    decoration |= HIPPO_TEXT_DECORATION_BLINK;
+                } else {
+                    goto next_decl;
                 }
             }
+
+            return decoration;
         }
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
-        break;
+
+    next_decl:
+        ;
     }
+
+    return 0;
 }
 
-static void
-hippo_canvas_style_get_property(GObject         *object,
-                                guint            prop_id,
-                                GValue          *value,
-                                GParamSpec      *pspec)
+static gboolean
+font_family_from_terms(CRTerm *term,
+                       char  **family)
 {
-    HippoCanvasStyle *style;
+    GString *family_string;
+    gboolean result = FALSE;
+    gboolean last_was_quoted = FALSE;
+    
+    if (!term)
+        return FALSE;
 
-    style = HIPPO_CANVAS_STYLE (object);
+    family_string = g_string_new(NULL);
 
-    switch (prop_id) {
-    case PROP_COLOR:
-        g_value_set_uint(value, style->color_rgba);
-        break;
-    case PROP_COLOR_SET:
-        g_value_set_boolean(value, style->color_set);
-        break;
-    case PROP_FONT:
-        {
-            char *s;
-            if (style->font_desc)
-                s = pango_font_description_to_string(style->font_desc);
-            else
-                s = NULL;
-            g_value_take_string(value, s);
+    while (term) {
+        if (term->type != TERM_STRING && term->type != TERM_IDENT) {
+            goto out;
         }
-        break;
-    case PROP_FONT_DESC:
-        g_value_set_boxed(value, style->font_desc);
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
-        break;
+
+        if (family_string->len > 0) {
+            if (term->the_operator != COMMA && term->the_operator != NO_OP)
+                goto out;
+            /* Can concetenate to bare words, but not two quoted strings */
+            if ((term->the_operator == NO_OP && last_was_quoted) || term->type == TERM_STRING)
+                goto out;
+
+            if (term->the_operator == NO_OP) {
+                g_string_append(family_string, " ");
+            } else {
+                g_string_append(family_string, ", ");
+            }
+        } else {
+            if (term->the_operator != NO_OP)
+                goto out;
+        }
+
+        g_string_append(family_string, term->content.str->stryng->str);
+        
+        term = term->next;
+    }
+
+    result = TRUE;
+
+ out:
+    if (result) {
+        *family = g_string_free(family_string, FALSE);
+        return TRUE;
+    } else {
+        *family = g_string_free(family_string, TRUE);
+        return FALSE;
     }
 }
 
-void
-hippo_canvas_style_affect_color(HippoCanvasStyle     *style,
-                                guint32              *color_rgba_p)
+/* In points */
+static int font_sizes[] = {
+    6 * 1024,   /* xx-small */
+    8 * 1024,   /* x-small */
+    10 * 1024,  /* small */
+    12 * 1024,  /* medium */
+    16 * 1024,  /* large */
+    20 * 1024,  /* x-large */
+    24 * 1024,  /* xx-large */
+};
+
+static gboolean
+font_size_from_term(HippoCanvasStyle *style,
+                    CRTerm           *term,
+                    double           *size)
 {
-    if (!style->color_set)
-        return;
+    if (term->type == TERM_IDENT) {
+        double resolution = hippo_canvas_context_get_resolution(style->context);
+        /* We work in integers to avoid double comparisons when converting back
+         * from a size in pixels to a logical size.
+         */
+        int size_points = (int)(0.5 + *size * (72. / resolution));
+        
+        if (strcmp(term->content.str->stryng->str, "xx-small") == 0) {
+            size_points = font_sizes[0];
+        } else if (strcmp(term->content.str->stryng->str, "x-small") == 0) {
+            size_points = font_sizes[1];
+        } else if (strcmp(term->content.str->stryng->str, "small") == 0) {
+            size_points = font_sizes[2];
+        } else if (strcmp(term->content.str->stryng->str, "medium") == 0) {
+            size_points = font_sizes[3];
+        } else if (strcmp(term->content.str->stryng->str, "large") == 0) {
+            size_points = font_sizes[4];
+        } else if (strcmp(term->content.str->stryng->str, "x-large") == 0) {
+            size_points = font_sizes[5];
+        } else if (strcmp(term->content.str->stryng->str, "xx-large") == 0) {
+            size_points = font_sizes[6];
+        } else if (strcmp(term->content.str->stryng->str, "smaller") == 0) {
+            /* Find the standard size equal to or smaller than the current size */
+            int i = 0;
+
+            while (i <= 6 && font_sizes[i] < size_points)
+                i++;
+            
+            if (i > 6) { /* original size greater than any standard size */
+                size_points = (int)(0.5 + size_points / 1.2);
+            } else {
+                /* Go one smaller than that, if possible */
+                if (i > 0)
+                    i--;
 
-    *color_rgba_p = style->color_rgba;
+                size_points = font_sizes[i];
+            }
+        } else if (strcmp(term->content.str->stryng->str, "larger") == 0) {
+            /* Find the standard size equal to or larger than the current size */
+            int i = 6;
+
+            while (i >= 0 && font_sizes[i] > size_points)
+                i--;
+            
+            if (i < 0) /* original size smaller than any standard size */
+                i = 0;
+            
+            /* Go one larger than that, if possible */
+            if (i < 6)
+                i++;
+
+            size_points = font_sizes[i];
+        } else {
+            return FALSE;
+        }
+
+        *size = size_points * (resolution / 72.);
+        return TRUE;
+        
+    } else if (term->type == TERM_NUMBER && term->content.num->type == NUM_PERCENTAGE) {
+        *size *= term->content.num->val;
+    } else if (get_length_from_term(style, term, TRUE, size) == VALUE_FOUND) {
+        /* Convert from pixels to Pango units */
+        *size *= 1024;
+        return TRUE;
+    }
+    
+    return FALSE;
 }
 
-void
-hippo_canvas_style_affect_font_desc(HippoCanvasStyle     *style,
-                                    PangoFontDescription *font_desc)
+static gboolean
+font_weight_from_term(CRTerm      *term,
+                      PangoWeight *weight,
+                      gboolean    *weight_absolute)
 {
-    if (style->font_desc == NULL)
-        return;
+    if (term->type == TERM_NUMBER) {
+        int weight_int;
+        
+        /* The spec only allows numeric weights from 100-900, though Pango
+         * will handle any number. We just let anything through.
+         */
+        if (term->content.num->type != NUM_GENERIC)
+            return FALSE;
+
+        weight_int = (int)(term->content.num->val + 0.5);
+
+        *weight = weight_int;
+        *weight_absolute = TRUE;
+                
+    } else if (term->type == TERM_IDENT) {
+        /* FIXME: handle INHERIT */
+        
+        if (strcmp(term->content.str->stryng->str, "bold") == 0) {
+            *weight = PANGO_WEIGHT_BOLD;
+            *weight_absolute = TRUE;
+        } else if (strcmp(term->content.str->stryng->str, "normal") == 0) {
+            *weight = PANGO_WEIGHT_NORMAL;
+            *weight_absolute = TRUE;
+        } else if (strcmp(term->content.str->stryng->str, "bolder") == 0) {
+            *weight = PANGO_WEIGHT_BOLD;
+            *weight_absolute = FALSE;
+        } else if (strcmp(term->content.str->stryng->str, "lighter") == 0) {
+            *weight = PANGO_WEIGHT_LIGHT;
+            *weight_absolute = FALSE;
+        } else {
+            return FALSE;
+        }
+
+    } else {
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static gboolean
+font_style_from_term(CRTerm     *term,
+                     PangoStyle *style)
+{
+    if (term->type != TERM_IDENT)
+        return FALSE;
+
+    /* FIXME: handle INHERIT */
+
+    if (strcmp(term->content.str->stryng->str, "normal") == 0)
+        *style = PANGO_STYLE_NORMAL;
+    else if (strcmp(term->content.str->stryng->str, "oblique") == 0)
+        *style = PANGO_STYLE_OBLIQUE;
+    else if (strcmp(term->content.str->stryng->str, "italic") == 0)
+        *style = PANGO_STYLE_ITALIC;
+    else
+        return FALSE;
+
+    return TRUE;
+}
+
+static gboolean
+font_variant_from_term(CRTerm       *term,
+                       PangoVariant *variant)
+{
+    if (term->type != TERM_IDENT)
+        return FALSE;
+
+    /* FIXME: handle INHERIT */
+
+    if (strcmp(term->content.str->stryng->str, "normal") == 0)
+        *variant = PANGO_VARIANT_NORMAL;
+    else if (strcmp(term->content.str->stryng->str, "small-caps") == 0)
+        *variant = PANGO_VARIANT_SMALL_CAPS;
+    else
+        return FALSE;
+
+    return TRUE;
+}
+
+PangoFontDescription *
+hippo_canvas_style_get_font (HippoCanvasStyle *style)
+{
+    PangoStyle font_style;
+    gboolean font_style_set = FALSE;
+    PangoVariant variant;
+    gboolean variant_set = FALSE;
+    PangoWeight weight;
+    gboolean weight_absolute;
+    gboolean weight_set = FALSE;
+    double parent_size;
+    double size = 0.; /* Suppress warning */
+    gboolean size_set = FALSE;
+    char *family = NULL;
+    int i;
+
+    if (style->font_desc)
+        return style->font_desc;
+    
+    style->font_desc = pango_font_description_copy(get_parent_font(style));
+    parent_size = pango_font_description_get_size(style->font_desc);
+    if (!pango_font_description_get_size_is_absolute(style->font_desc)) {
+        double resolution = hippo_canvas_context_get_resolution(style->context);
+        parent_size *= (resolution / 72.);
+    }
+
+    ensure_properties(style);
+
+    for (i = 0; i < style->n_properties; i++) {
+        CRDeclaration *decl = style->properties[i];
+        
+        if (strcmp(decl->property->stryng->str, "font") == 0) {
+            PangoStyle tmp_style = PANGO_STYLE_NORMAL;
+            PangoVariant tmp_variant = PANGO_VARIANT_NORMAL;
+            PangoWeight tmp_weight = PANGO_WEIGHT_NORMAL;
+            gboolean tmp_weight_absolute = FALSE;
+            double tmp_size;
+            CRTerm *term = decl->value;
+
+            /* A font specification starts with style/variant/weight
+             * in any order. Each is allowed to be specified only once,
+             * but we don't enforce that.
+             */
+            for (; term; term = term->next) {
+                if (font_style_from_term(term, &tmp_style))
+                    ;
+                else if (font_variant_from_term(term, &tmp_variant))
+                    ;
+                else if (font_weight_from_term(term, &tmp_weight, &tmp_weight_absolute))
+                    ;
+                else
+                    break;
+            }
+
+            /* The size is mandatory */
+
+            if (term == NULL || term->type != TERM_NUMBER) {
+                g_warning("Size missing from font property");
+                continue;
+            }
+
+            tmp_size = parent_size;
+            if (!font_size_from_term(style, term, &tmp_size)) {
+                g_warning("Couldn't parse size in font property");
+                continue;
+            }
+            
+            if (term != NULL && term->type && TERM_NUMBER && term->the_operator == DIVIDE) {
+                /* Ignore line-height specification */
+                term = term->next;
+            }
+            
+            /* the font family is mandatory - it is a comma-separated list of
+             * names.
+             */
+            if (!font_family_from_terms(term, &family)) {
+                g_warning("Couldn't parse family in font property");
+                continue;
+            }
+
+            font_style = tmp_style;
+            font_style_set = TRUE;
+            weight = tmp_weight;
+            weight_absolute = tmp_weight_absolute;
+            weight_set = TRUE;
+            variant = tmp_variant;
+            variant_set = TRUE;
+            
+            size = tmp_size;
+            size_set = TRUE;
+
+        } else if (strcmp(decl->property->stryng->str, "family") == 0) {
+            if (!font_family_from_terms(decl->value, &family)) {
+                g_warning("Couldn't parse family in font property");
+                continue;
+            }
+        } else if (strcmp(decl->property->stryng->str, "font-weight") == 0) {
+            if (decl->value == NULL || decl->value->next != NULL)
+                continue;
+
+            if (font_weight_from_term(decl->value, &weight, &weight_absolute))
+                weight_set = TRUE;
+        } else if (strcmp(decl->property->stryng->str, "font-style") == 0) {
+            if (decl->value == NULL || decl->value->next != NULL)
+                continue;
+
+            if (font_style_from_term(decl->value, &font_style))
+                font_style_set = TRUE;
+        } else if (strcmp(decl->property->stryng->str, "font-variant") == 0) {
+            if (decl->value == NULL || decl->value->next != NULL)
+                continue;
+
+            if (font_variant_from_term(decl->value, &variant))
+                variant_set = TRUE;
+        } else if (strcmp(decl->property->stryng->str, "font-size") == 0) {
+            gdouble tmp_size;
+            if (decl->value == NULL || decl->value->next != NULL)
+                continue;
+
+            tmp_size = parent_size;
+            if (font_size_from_term(style, decl->value, &tmp_size)) {
+                size = tmp_size;
+                size_set = TRUE;
+            }
+        } 
+    }
+
+    if (family)
+        pango_font_description_set_family(style->font_desc, family);
+
+    if (size_set)
+        pango_font_description_set_absolute_size(style->font_desc, size);
+
+    if (weight_set) {
+        if (!weight_absolute) {
+            /* bolder/lighter are supposed to switch between available styles, but with
+             * font substitution, that gets to be a pretty fuzzy concept. So we use
+             * a fixed step of 200. (The spec says 100, but that might not take us from
+             * normal to bold.
+             */
+            
+            PangoWeight old_weight = pango_font_description_get_weight(style->font_desc);
+            if (weight == PANGO_WEIGHT_BOLD)
+                weight = old_weight + 200;
+            else
+                weight = old_weight - 200;
+
+            if (weight < 100)
+                weight = 100;
+            if (weight > 900)
+                weight = 900;
+        }
+        
+        pango_font_description_set_weight(style->font_desc, weight);
+    }
+    
+    if (font_style_set)
+        pango_font_description_set_style(style->font_desc, font_style);
+    if (variant_set)
+        pango_font_description_set_variant(style->font_desc, variant);
+
+    return style->font_desc;
+}
+
+gboolean
+hippo_canvas_style_paint (HippoCanvasStyle       *style,
+                          cairo_t                *cr,
+                          const char             *name,
+                          double                  x,
+                          double                  y,
+                          double                  width,
+                          double                  height)
+{
+    HippoCanvasThemeEngine *theme_engine;
+    
+    g_return_val_if_fail(HIPPO_IS_CANVAS_STYLE(style), FALSE);
+
+    if (!style->theme)
+        return FALSE;
 
-    pango_font_description_merge(font_desc,
-                                 style->font_desc,
-                                 TRUE); /* TRUE = overwrite anything in the target */
+    theme_engine = hippo_canvas_theme_get_theme_engine(style->theme);
+    if (theme_engine == NULL)
+        return FALSE;
+
+    return hippo_canvas_theme_engine_paint(theme_engine,
+                                           style, cr, name,
+                                           x, y, width, height);
 }

Modified: trunk/common/hippo/hippo-canvas-style.h
==============================================================================
--- trunk/common/hippo/hippo-canvas-style.h	(original)
+++ trunk/common/hippo/hippo-canvas-style.h	Mon Mar 24 19:05:00 2008
@@ -3,20 +3,9 @@
 #define __HIPPO_CANVAS_STYLE_H__
 
 #include "hippo-canvas-context.h"
-#include "hippo-canvas-box.h"
-
-/* HippoCanvasStyle contains styles that are inherited by children
- * from their container in a tree of canvas items.
- *
- * Right now this is an implementation detail of HippoCanvasBox, not
- * exposed in the HippoCanvasItem/HippoCanvasContext interfaces.
- */
 
 G_BEGIN_DECLS
 
-#define HIPPO_CANVAS_DEFAULT_COLOR 0x000000ff
-#define HIPPO_CANVAS_DEFAULT_BACKGROUND_COLOR 0xffffff00
-
 #define HIPPO_TYPE_CANVAS_STYLE              (hippo_canvas_style_get_type ())
 #define HIPPO_CANVAS_STYLE(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), HIPPO_TYPE_CANVAS_STYLE, HippoCanvasStyle))
 #define HIPPO_CANVAS_STYLE_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), HIPPO_TYPE_CANVAS_STYLE, HippoCanvasStyleClass))
@@ -24,12 +13,101 @@
 #define HIPPO_IS_CANVAS_STYLE_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), HIPPO_TYPE_CANVAS_STYLE))
 #define HIPPO_CANVAS_STYLE_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), HIPPO_TYPE_CANVAS_STYLE, HippoCanvasStyleClass))
 
+typedef enum {
+    HIPPO_CANVAS_LINK_NONE,
+    HIPPO_CANVAS_LINK_LINK,
+    HIPPO_CANVAS_LINK_VISITED
+} HippoCanvasLinkType;
+
+typedef enum {
+    HIPPO_CANVAS_SIDE_LEFT,
+    HIPPO_CANVAS_SIDE_RIGHT,
+    HIPPO_CANVAS_SIDE_TOP,
+    HIPPO_CANVAS_SIDE_BOTTOM
+} HippoCanvasSide;
+
+/* These are the CSS values; that doesn't mean we have to implement blink... */
+typedef enum {
+    HIPPO_TEXT_DECORATION_UNDERLINE    = 1 << 0,
+    HIPPO_TEXT_DECORATION_OVERLINE     = 1 << 1,
+    HIPPO_TEXT_DECORATION_LINE_THROUGH = 1 << 2,
+    HIPPO_TEXT_DECORATION_BLINK        = 1 << 3
+} HippoTextDecoration;
+
 GType             hippo_canvas_style_get_type          (void) G_GNUC_CONST;
 
-void hippo_canvas_style_affect_color     (HippoCanvasStyle     *style,
-                                          guint32              *color_rgba_p);
-void hippo_canvas_style_affect_font_desc (HippoCanvasStyle     *style,
-                                          PangoFontDescription *font_desc);
+/* An element_type of G_TYPE_NONE means this style was created for the canvas root
+ * element and matches a selector element name of 'canvas'
+ */
+HippoCanvasStyle *hippo_canvas_style_new (HippoCanvasContext    *context,
+                                          HippoCanvasStyle      *parent_style,  /* can be null */
+                                          HippoCanvasTheme      *theme,         /* can be null */
+                                          GType                  element_type,
+                                          const char            *element_id,
+                                          const char            *element_class);
+
+void                hippo_canvas_style_set_link_type (HippoCanvasStyle    *style,
+                                                      HippoCanvasLinkType  link_type);
+HippoCanvasLinkType hippo_canvas_style_get_link_type (HippoCanvasStyle    *style);
+
+HippoCanvasStyle *hippo_canvas_style_get_parent (HippoCanvasStyle *style);
+
+HippoCanvasTheme *hippo_canvas_style_get_theme (HippoCanvasStyle *style);
+
+GType       hippo_canvas_style_get_element_type  (HippoCanvasStyle *style);
+const char *hippo_canvas_style_get_element_id    (HippoCanvasStyle *style);
+const char *hippo_canvas_style_get_element_class (HippoCanvasStyle *style);
+
+/* Generic getters ... these are not cached so are less efficient. The other
+ * reason for adding the more specific version is that we can handle the
+ * details of the actual CSS rules, which can be complicated, especially
+ * for fonts
+ */
+gboolean hippo_canvas_style_get_color (HippoCanvasStyle     *style,
+                                       const char           *property_name,
+                                       gboolean              inherit,
+                                       guint32              *color);
+
+gboolean hippo_canvas_style_get_double (HippoCanvasStyle     *style,
+                                        const char           *property_name,
+                                        gboolean              inherit,
+                                        double               *value);
+
+/* The length here is already resolved to pixels
+ */
+gboolean hippo_canvas_style_get_length (HippoCanvasStyle     *style,
+                                        const char           *property_name,
+                                        gboolean              inherit,
+                                        gdouble              *length);
+
+/* Specific getters for particular properties: cached
+ */
+guint32 hippo_canvas_style_get_background_color (HippoCanvasStyle *style);
+guint32 hippo_canvas_style_get_foreground_color (HippoCanvasStyle *style);
+
+double hippo_canvas_style_get_border_width (HippoCanvasStyle *style,
+                                            HippoCanvasSide   side);
+guint32 hippo_canvas_style_get_border_color(HippoCanvasStyle *style,
+                                            HippoCanvasSide   side);
+double hippo_canvas_style_get_padding      (HippoCanvasStyle *style,
+                                            HippoCanvasSide   side);
+
+HippoTextDecoration hippo_canvas_style_get_text_decoration (HippoCanvasStyle *style);
+
+/* Font rule processing is pretty complicated, so we just hardcode it
+ * under the standard font/font-family/font-size/etc names. This means
+ * you can't have multiple separate styled fonts for a single item,
+ * but that should be OK.
+ */
+PangoFontDescription *hippo_canvas_style_get_font (HippoCanvasStyle *style);
+
+gboolean hippo_canvas_style_paint (HippoCanvasStyle       *style,
+                                   cairo_t                *cr,
+                                   const char             *name,
+                                   double                  x,
+                                   double                  y,
+                                   double                  width,
+                                   double                  height);
 
 G_END_DECLS
 

Modified: trunk/common/hippo/hippo-canvas-text.c
==============================================================================
--- trunk/common/hippo/hippo-canvas-text.c	(original)
+++ trunk/common/hippo/hippo-canvas-text.c	Mon Mar 24 19:05:00 2008
@@ -1,6 +1,7 @@
 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
 #include "hippo-canvas-type-builtins.h"
 #include "hippo-canvas-internal.h"
+#include "hippo-canvas-style.h"
 #include "hippo-canvas-text.h"
 #include "hippo-canvas-box.h"
 #include <pango/pangocairo.h>
@@ -60,8 +61,6 @@
     PROP_SIZE_MODE
 };
 
-#define DEFAULT_FOREGROUND 0x000000ff
-
 G_DEFINE_TYPE_WITH_CODE(HippoCanvasText, hippo_canvas_text, HIPPO_TYPE_CANVAS_BOX,
                         G_IMPLEMENT_INTERFACE(HIPPO_TYPE_CANVAS_ITEM, hippo_canvas_text_iface_init));
 
@@ -373,51 +372,33 @@
 create_layout(HippoCanvasText *text,
               int              allocation_width)
 {
-    HippoCanvasContext *context;
+    HippoCanvasBox *box = HIPPO_CANVAS_BOX(text);
     PangoLayout *layout;
+    HippoCanvasStyle *style = hippo_canvas_context_get_style(HIPPO_CANVAS_CONTEXT(text));
 
-    /* Note that our context is *ourselves* not box->context i.e. we want
-     * our own style, etc. to affect what we render. Our context methods
-     * will chain up as needed.
-     */
-    context = HIPPO_CANVAS_CONTEXT(text);
-
-    g_return_val_if_fail(context != NULL, NULL);
+    g_return_val_if_fail(box->context != NULL, NULL);
     
-    layout = hippo_canvas_context_create_layout(context);
-
-    {
-        const PangoFontDescription *old;
-        PangoFontDescription *composite;
-        
-        composite = pango_font_description_new();
-        
-        old = pango_layout_get_font_description(layout);
-        /* if no font desc is set on the layout, the layout uses the one
-         * from the context, so emulate that here.
-         */
-        if (old == NULL)
-            old = pango_context_get_font_description(pango_layout_get_context(layout));
-        
-        if (old != NULL)
-            pango_font_description_merge(composite, old, TRUE);
+    layout = hippo_canvas_context_create_layout(box->context);
 
-        hippo_canvas_context_affect_font_desc(context,
-                                              composite);
-        
-        pango_layout_set_font_description(layout, composite);
-    
-        pango_font_description_free(composite);
+    if (box->font_desc) {
+        PangoFontDescription *merged = pango_font_description_copy(hippo_canvas_style_get_font(style));
+        pango_font_description_merge(merged, box->font_desc, TRUE);
+        pango_layout_set_font_description(layout, merged);
+        pango_font_description_free(merged);
+    } else {
+        pango_layout_set_font_description(layout, hippo_canvas_style_get_font(style));
     }
     
     {
         PangoAttrList *attrs;
+        HippoTextDecoration decoration = hippo_canvas_style_get_text_decoration(style);
         
         if (text->attributes)
             attrs = pango_attr_list_copy(text->attributes);
         else
             attrs = pango_attr_list_new();
 
+        
         if (ABS(1.0 - text->font_scale) > .000001) {
             PangoAttribute *attr = pango_attr_scale_new(text->font_scale);
             attr->start_index = 0;
@@ -425,6 +406,20 @@
             pango_attr_list_insert(attrs, attr);
         }
 
+        if ((decoration & HIPPO_TEXT_DECORATION_UNDERLINE) != 0) {
+            PangoAttribute *attr = pango_attr_underline_new(TRUE);
+            attr->start_index = 0;
+            attr->end_index = G_MAXUINT;
+            pango_attr_list_insert(attrs, attr);
+        }
+
+        if ((decoration & HIPPO_TEXT_DECORATION_LINE_THROUGH) != 0) {
+            PangoAttribute *attr = pango_attr_strikethrough_new(TRUE);
+            attr->start_index = 0;
+            attr->end_index = G_MAXUINT;
+            pango_attr_list_insert(attrs, attr);
+        }
+
         pango_layout_set_attributes(layout, attrs);
         pango_attr_list_unref(attrs);
     }
@@ -555,14 +550,14 @@
 {
     HippoCanvasText *text = HIPPO_CANVAS_TEXT(box);
     guint32 color_rgba;
-    HippoCanvasContext *context;
 
-    /* note, we want to use _ourselves_ as context so we get our own style */
-    context = HIPPO_CANVAS_CONTEXT(box);
-
-    color_rgba = HIPPO_CANVAS_BOX_GET_CLASS(box)->default_color;
-    hippo_canvas_context_affect_color(context, &color_rgba);
-    
+    if (box->color_set) {
+        color_rgba = box->color_rgba;
+    } else {
+        HippoCanvasStyle *style = hippo_canvas_context_get_style(HIPPO_CANVAS_CONTEXT(text));
+        color_rgba = hippo_canvas_style_get_foreground_color(style);
+    }
+        
     /* It would seem more natural to compute whether we are ellipsized or
      * not when we are allocated to some width, but we don't have a layout
      * at that point. We could do it in get_content_height_request(), but

Added: trunk/common/hippo/hippo-canvas-theme-engine.c
==============================================================================
--- (empty file)
+++ trunk/common/hippo/hippo-canvas-theme-engine.c	Mon Mar 24 19:05:00 2008
@@ -0,0 +1,62 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+#include <string.h>
+
+#include "hippo-canvas-theme-engine.h"
+
+static void     hippo_canvas_item_base_init (void                  *klass);
+
+#if 0
+enum {
+    LAST_SIGNAL
+};
+
+static int signals[LAST_SIGNAL];
+#endif
+
+GType
+hippo_canvas_theme_engine_get_type(void)
+{
+    static GType type = 0;
+    if (type == 0) {
+        static const GTypeInfo info =
+            {
+                sizeof(HippoCanvasThemeEngineIface),
+                hippo_canvas_item_base_init,
+                NULL /* base_finalize */
+            };
+        type = g_type_register_static(G_TYPE_INTERFACE, "HippoCanvasThemeEngine",
+                                      &info, 0);
+        
+        g_type_interface_add_prerequisite (type, G_TYPE_OBJECT);
+    }
+
+    return type;
+}
+
+static void
+hippo_canvas_item_base_init(void *klass)
+{
+    static gboolean initialized = FALSE;
+
+   if (!initialized) {
+        /* create signals in here */
+    }
+}
+
+gboolean
+hippo_canvas_theme_engine_paint (HippoCanvasThemeEngine *engine,
+                                 HippoCanvasStyle       *style,
+                                 cairo_t                *cr,
+                                 const char             *name,
+                                 double                  x,
+                                 double                  y,
+                                 double                  width,
+                                 double                  height)
+{
+    g_return_val_if_fail(HIPPO_IS_CANVAS_THEME_ENGINE(engine), FALSE);
+    
+    return HIPPO_CANVAS_THEME_ENGINE_GET_IFACE(engine)->paint(engine,
+                                                              style, cr, name,
+                                                              x, y, width, height);
+}
+

Added: trunk/common/hippo/hippo-canvas-theme-engine.h
==============================================================================
--- (empty file)
+++ trunk/common/hippo/hippo-canvas-theme-engine.h	Mon Mar 24 19:05:00 2008
@@ -0,0 +1,46 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+#ifndef __HIPPO_CANVAS_THEME_ENGINE_H__
+#define __HIPPO_CANVAS_THEME_ENGINE_H__
+
+#include <cairo.h>
+#include <hippo/hippo-event.h>
+#include <hippo/hippo-graphics.h>
+#include <hippo/hippo-canvas-style.h>
+
+G_BEGIN_DECLS
+
+typedef struct _HippoCanvasThemeEngine      HippoCanvasThemeEngine;
+typedef struct _HippoCanvasThemeEngineIface HippoCanvasThemeEngineIface;
+
+#define HIPPO_TYPE_CANVAS_THEME_ENGINE              (hippo_canvas_theme_engine_get_type ())
+#define HIPPO_CANVAS_THEME_ENGINE(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), HIPPO_TYPE_CANVAS_THEME_ENGINE, HippoCanvasThemeEngine))
+#define HIPPO_IS_CANVAS_THEME_ENGINE(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), HIPPO_TYPE_CANVAS_THEME_ENGINE))
+#define HIPPO_CANVAS_THEME_ENGINE_GET_IFACE(obj)    (G_TYPE_INSTANCE_GET_INTERFACE ((obj), HIPPO_TYPE_CANVAS_THEME_ENGINE, HippoCanvasThemeEngineIface))
+
+struct _HippoCanvasThemeEngineIface {
+    GTypeInterface base_iface;
+
+    gboolean               (* paint)                (HippoCanvasThemeEngine *engine,
+                                                     HippoCanvasStyle       *style,
+                                                     cairo_t                *cr,
+                                                     const char             *name,
+                                                     double                  x,
+                                                     double                  y,
+                                                     double                  width,
+                                                     double                  height);
+};
+
+GType hippo_canvas_theme_engine_get_type (void) G_GNUC_CONST;
+
+gboolean hippo_canvas_theme_engine_paint (HippoCanvasThemeEngine *engine,
+                                          HippoCanvasStyle       *style,
+                                          cairo_t                *cr,
+                                          const char             *name,
+                                          double                  x,
+                                          double                  y,
+                                          double                  width,
+                                          double                  height);
+
+G_END_DECLS
+
+#endif /* __HIPPO_CANVAS_THEME_ENGINE_H__ */

Added: trunk/common/hippo/hippo-canvas-theme-internal.h
==============================================================================
--- (empty file)
+++ trunk/common/hippo/hippo-canvas-theme-internal.h	Mon Mar 24 19:05:00 2008
@@ -0,0 +1,17 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+#ifndef __HIPPO_CANVAS_THEME_INTERNAL_H__
+#define __HIPPO_CANVAS_THEME_INTERNAL_H__
+
+#include <libcroco/libcroco.h>
+#include "hippo-canvas-theme.h"
+
+G_BEGIN_DECLS
+
+void _hippo_canvas_theme_get_matched_properties (HippoCanvasTheme *theme,
+                                                 HippoCanvasStyle *style,
+                                                 CRDeclaration  ***properties,
+                                                 int              *n_properties);
+
+G_END_DECLS
+
+#endif /* __HIPPO_CANVAS_THEME_INTERNAL_H__ */

Added: trunk/common/hippo/hippo-canvas-theme.c
==============================================================================
--- (empty file)
+++ trunk/common/hippo/hippo-canvas-theme.c	Mon Mar 24 19:05:00 2008
@@ -0,0 +1,937 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+
+/* This file started as a cut-and-paste of cr-sel-eng.c from libcroco.
+ *
+ * It has been:
+ * - Reformatted and otherwise edited to match our coding style
+ * - Switched from handling xmlNode to handling HippoStyle
+ * - Simplified by removing things that we don't need or that don't
+ *   make sense in our context.
+ * - The code to get a list of matching properties works quite differently;
+ *   we order things in priority order, but we don't actually try to
+ *   coelesce properties with the same name.
+ */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 
+ * General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * Copyright (C) 2003-2004 Dodji Seketeli.  All Rights Reserved.
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "hippo-canvas-internal.h"
+#include "hippo-canvas-style.h"
+#include "hippo-canvas-theme-internal.h"
+
+static GObject *hippo_canvas_theme_constructor (GType                  type,
+                                                guint                  n_construct_properties,
+                                                GObjectConstructParam *construct_properties);
+
+static void hippo_canvas_theme_dispose            (GObject                 *object);
+static void hippo_canvas_theme_finalize           (GObject                 *object);
+
+static void     hippo_canvas_theme_set_property (GObject               *object,
+                                                 guint                  prop_id,
+                                                 const GValue          *value,
+                                                 GParamSpec            *pspec);
+static void     hippo_canvas_theme_get_property (GObject               *object,
+                                                 guint                  prop_id,
+                                                 GValue                *value,
+                                                 GParamSpec            *pspec);
+
+#if 0
+enum {
+    LAST_SIGNAL
+};
+
+static int signals[LAST_SIGNAL];
+#endif
+
+struct _HippoCanvasTheme {
+    GObject parent;
+
+    HippoCanvasThemeEngine *theme_engine;
+
+    char *application_stylesheet;
+    char *default_stylesheet;
+    char *theme_stylesheet;
+
+    CRCascade *cascade;
+};
+
+struct _HippoCanvasThemeClass {
+    GObjectClass parent_class;
+
+};
+
+enum {
+    PROP_0,
+    PROP_THEME_ENGINE,
+    PROP_APPLICATION_STYLESHEET,
+    PROP_THEME_STYLESHEET,
+    PROP_DEFAULT_STYLESHEET
+};
+
+G_DEFINE_TYPE(HippoCanvasTheme, hippo_canvas_theme, G_TYPE_OBJECT)
+
+/* Quick strcmp.  Test only for == 0 or != 0, not < 0 or > 0.  */
+#define strqcmp(str,lit,lit_len) \
+  (strlen (str) != (lit_len) || memcmp (str, lit, lit_len))
+
+static void
+hippo_canvas_theme_init(HippoCanvasTheme *theme)
+{
+}
+
+static void
+hippo_canvas_theme_class_init(HippoCanvasThemeClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    object_class->constructor = hippo_canvas_theme_constructor;
+    object_class->dispose = hippo_canvas_theme_dispose;
+    object_class->finalize = hippo_canvas_theme_finalize;
+    object_class->set_property = hippo_canvas_theme_set_property;
+    object_class->get_property = hippo_canvas_theme_get_property;
+    
+    /**
+     * HippoCanvasImage:theme-engine
+     *
+     * The theme engine object, used to draw control parts in a theme-specific manner.
+     */    
+    g_object_class_install_property(object_class,
+                                    PROP_THEME_ENGINE,
+                                    g_param_spec_object("theme-engine",
+                                                        _("Theme Engine"),
+                                                        _("Theme engine object used to draw control parts"),
+                                                        HIPPO_TYPE_CANVAS_THEME_ENGINE,
+                                                        G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+    /**
+     * HippoCanvasImage:application-stylesheet
+     *
+     * The highest priority stylesheet, representing application-specific
+     * styling; this is associated with the CSS "author" stylesheet.
+     */    
+    g_object_class_install_property(object_class,
+                                    PROP_APPLICATION_STYLESHEET,
+                                    g_param_spec_string("application-stylesheet",
+                                                        _("Application Stylesheet"),
+                                                        _("Stylesheet with application-specific styling"),
+                                                        NULL,
+                                                        G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+    /**
+     * HippoCanvasImage:theme-stylesheet
+     *
+     * The second priority stylesheet, representing theme-specific styling;
+     * this is associated with the CSS "user" stylesheet.
+     */    
+    g_object_class_install_property(object_class,
+                                    PROP_THEME_STYLESHEET,
+                                    g_param_spec_string("theme-stylesheet",
+                                                        _("Theme Stylesheet"),
+                                                        _("Stylesheet with theme-specific styling"),
+                                                        NULL,
+                                                        G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+    /**
+     * HippoCanvasImage:default-stylesheet
+     *
+     * The lowest priority stylesheet, representing global default
+     * styling; this is associated with the CSS "user agent" stylesheet.
+     */    
+    g_object_class_install_property(object_class,
+                                    PROP_DEFAULT_STYLESHEET,
+                                    g_param_spec_string("default-stylesheet",
+                                                        _("Default Stylesheet"),
+                                                        _("Stylesheet with global default styling"),
+                                                        NULL,
+                                                        G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+}
+
+static CRStyleSheet *
+parse_stylesheet(const char *filename)
+{
+  enum CRStatus status;
+  CRStyleSheet *stylesheet;
+  
+  if (filename == NULL)
+      return NULL;
+    
+  status = cr_om_parser_simply_parse_file ((const guchar *)filename,
+                                           CR_UTF_8,
+                                           &stylesheet);
+
+  if (status != CR_OK) {
+      g_warning("Error parsing stylesheet '%s'", filename);
+      return NULL;
+  }
+
+  return stylesheet;
+}
+
+static GObject *
+hippo_canvas_theme_constructor (GType                  type,
+                                guint                  n_construct_properties,
+                                GObjectConstructParam *construct_properties)
+{
+  GObject *object;
+  HippoCanvasTheme *theme;
+
+  object = (*G_OBJECT_CLASS (hippo_canvas_theme_parent_class)->constructor) (type,
+                                                                             n_construct_properties,
+                                                                             construct_properties);
+  theme = HIPPO_CANVAS_THEME (object);
+
+  theme->cascade = cr_cascade_new(parse_stylesheet(theme->application_stylesheet),
+                                  parse_stylesheet(theme->theme_stylesheet),
+                                  parse_stylesheet(theme->default_stylesheet));
+
+  if (theme->cascade == NULL)
+      g_error("Out of memory when creating cascade object");
+  
+  return object;
+}
+
+static void
+hippo_canvas_theme_dispose(GObject *object)
+{
+    /* HippoCanvasTheme *theme = HIPPO_CANVAS_THEME(object); */
+
+    G_OBJECT_CLASS(hippo_canvas_theme_parent_class)->dispose(object);
+}
+
+static void
+hippo_canvas_theme_finalize(GObject *object)
+{
+    HippoCanvasTheme *theme = HIPPO_CANVAS_THEME(object);
+
+    if (theme->theme_engine)
+        g_object_unref(theme->theme_engine);
+    
+    g_free(theme->application_stylesheet);
+    g_free(theme->theme_stylesheet);
+    g_free(theme->default_stylesheet);
+
+    if (theme->cascade) {
+        cr_cascade_unref(theme->cascade);
+        theme->cascade = NULL;
+    }
+
+    G_OBJECT_CLASS(hippo_canvas_theme_parent_class)->finalize(object);
+}
+
+static void
+hippo_canvas_theme_set_property(GObject         *object,
+                                guint            prop_id,
+                                const GValue    *value,
+                                GParamSpec      *pspec)
+{
+    HippoCanvasTheme *theme = HIPPO_CANVAS_THEME(object);
+    
+    switch (prop_id) {
+    case PROP_THEME_ENGINE:
+        {
+            HippoCanvasThemeEngine *theme_engine = g_value_get_object(value);
+            
+            if (theme_engine != theme->theme_engine) {
+                if (theme->theme_engine)
+                    g_object_unref(theme->theme_engine);
+                theme->theme_engine = theme_engine;
+                if (theme->theme_engine)
+                    g_object_ref(theme->theme_engine);
+            }
+        }
+        break;
+    case PROP_APPLICATION_STYLESHEET:
+        {
+            const char *path = g_value_get_string(value);
+
+            if (path != theme->application_stylesheet) {
+                g_free(theme->application_stylesheet);
+                theme->application_stylesheet = g_strdup(path);
+            }
+            
+            break;
+        }
+    case PROP_THEME_STYLESHEET:
+        {
+            const char *path = g_value_get_string(value);
+
+            if (path != theme->theme_stylesheet) {
+                g_free(theme->theme_stylesheet);
+                theme->theme_stylesheet = g_strdup(path);
+            }
+            
+            break;
+        }
+    case PROP_DEFAULT_STYLESHEET:
+        {
+            const char *path = g_value_get_string(value);
+
+            if (path != theme->default_stylesheet) {
+                g_free(theme->default_stylesheet);
+                theme->default_stylesheet = g_strdup(path);
+            }
+
+            break;
+        }
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+hippo_canvas_theme_get_property(GObject         *object,
+                                guint            prop_id,
+                                GValue          *value,
+                                GParamSpec      *pspec)
+{
+    HippoCanvasTheme *theme = HIPPO_CANVAS_THEME (object);
+
+    switch (prop_id) {
+    case PROP_THEME_ENGINE:
+        g_value_set_object(value, theme->theme_engine);
+        break;
+    case PROP_APPLICATION_STYLESHEET:
+        g_value_set_string(value, theme->application_stylesheet);
+        break;
+    case PROP_THEME_STYLESHEET:
+        g_value_set_string(value, theme->theme_stylesheet);
+        break;
+    case PROP_DEFAULT_STYLESHEET:
+        g_value_set_string(value, theme->default_stylesheet);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        break;
+    }
+}
+
+/**
+ * hippo_canvas_theme_new:
+ * @theme_engine: The theme engine object used for theme-specific drawing code, may be %NULL
+ * @application_stylesheet: The highest priority stylesheet, representing application-specific
+ *   styling; this is associated with the CSS "author" stylesheet, may be %NULL
+ * @theme_stylesheet: The second priority stylesheet, representing theme-specific styling ;
+ *   this is associated with the CSS "user" stylesheet, may be %NULL
+ * @default_stylesheet: The lowest priority stylesheet, representing global default styling;
+ *   this is associated with the CSS "user agent" stylesheet, may be %NULL
+ * 
+ * Return value: the newly created theme object
+ **/
+HippoCanvasTheme *
+hippo_canvas_theme_new (HippoCanvasThemeEngine *theme_engine,
+                        const char             *application_stylesheet,
+                        const char             *theme_stylesheet,
+                        const char             *default_stylesheet)
+{
+    HippoCanvasTheme *theme = g_object_new(HIPPO_TYPE_CANVAS_THEME,
+                                           "theme-engine", theme_engine,
+                                           "application-stylesheet", application_stylesheet,
+                                           "theme-stylesheet", theme_stylesheet,
+                                           "default-stylesheet", default_stylesheet,
+                                           NULL);
+                                           
+    return theme;
+}
+
+HippoCanvasThemeEngine *
+hippo_canvas_theme_get_theme_engine (HippoCanvasTheme *theme)
+{
+    g_return_val_if_fail(HIPPO_IS_CANVAS_THEME(theme), NULL);
+
+    return theme->theme_engine;
+}
+                    
+
+static gboolean
+link_class_add_sel_matches_style(HippoCanvasTheme *a_this,
+                                 CRAdditionalSel  *a_add_sel,
+                                 HippoCanvasStyle *a_style)
+{
+    return hippo_canvas_style_get_link_type(a_style) == HIPPO_CANVAS_LINK_LINK;
+}
+
+static gboolean
+visited_class_add_sel_matches_style(HippoCanvasTheme *a_this,
+                                    CRAdditionalSel  *a_add_sel,
+                                    HippoCanvasStyle *a_style)
+{
+    return hippo_canvas_style_get_link_type(a_style) == HIPPO_CANVAS_LINK_VISITED;
+}
+
+static gboolean
+pseudo_class_add_sel_matches_style (HippoCanvasTheme *a_this,
+                                    CRAdditionalSel  *a_add_sel,
+                                    HippoCanvasStyle *a_style)
+{
+    g_return_val_if_fail(a_this
+                         && a_add_sel
+                         && a_add_sel->content.pseudo
+                         && a_add_sel->content.pseudo->name
+                         && a_add_sel->content.pseudo->name->stryng
+                         && a_add_sel->content.pseudo->name->stryng->str
+                         && a_style, FALSE);
+    
+    if (strcmp(a_add_sel->content.pseudo->name->stryng->str, "link") == 0) {
+        return link_class_add_sel_matches_style(a_this, a_add_sel, a_style);
+    } else if (strcmp(a_add_sel->content.pseudo->name->stryng->str, "visited") == 0) {
+        return visited_class_add_sel_matches_style(a_this, a_add_sel, a_style);
+    } else {
+        return FALSE;
+    }
+}
+
+/**
+ * param a_add_sel the class additional selector to consider.
+ * param a_style the style object to consider.
+ * return TRUE if the class additional selector matches
+ *the style object given in argument, FALSE otherwise.
+ */
+static gboolean
+class_add_sel_matches_style (CRAdditionalSel  *a_add_sel,
+                             HippoCanvasStyle *a_style)
+{
+    const char *klass;
+
+    g_return_val_if_fail (a_add_sel
+                          && a_add_sel->type == CLASS_ADD_SELECTOR
+                          && a_add_sel->content.class_name
+                          && a_add_sel->content.class_name->stryng
+                          && a_add_sel->content.class_name->stryng->str
+                          && a_style, FALSE);
+    
+    klass = hippo_canvas_style_get_element_class(a_style);
+    if (klass != NULL) {
+        const char *cur;
+        
+        for (cur = klass; *cur;) {
+            while (*cur && cr_utils_is_white_space (*cur))
+                cur++;
+            
+            if (strncmp (cur, 
+                         a_add_sel->content.class_name->stryng->str,
+                         a_add_sel->content.class_name->stryng->len) == 0) {
+                cur += a_add_sel->content.class_name->stryng->len;
+                if ((!*cur) || cr_utils_is_white_space (*cur))
+                    return TRUE;
+            }
+            
+            /*  skip to next whitespace character  */
+            while (*cur && !cr_utils_is_white_space(*cur))
+                cur++;
+            
+        }
+    }
+    
+    return FALSE;
+}
+
+/**
+ * return TRUE if the additional attribute selector matches
+ *the current style object given in argument, FALSE otherwise.
+ * param a_add_sel the additional attribute selector to consider.
+ * param a_style the style object to consider.
+ */
+static gboolean
+id_add_sel_matches_style (CRAdditionalSel  *a_add_sel,
+                          HippoCanvasStyle *a_style)
+{
+        gboolean result = FALSE;
+        const char *id;
+
+        g_return_val_if_fail (a_add_sel
+                              && a_add_sel->type == ID_ADD_SELECTOR
+                              && a_add_sel->content.id_name
+                              && a_add_sel->content.id_name->stryng
+                              && a_add_sel->content.id_name->stryng->str
+                              && a_style, FALSE);
+        g_return_val_if_fail (a_add_sel
+                              && a_add_sel->type == ID_ADD_SELECTOR
+                              && a_style, FALSE);
+
+        id = hippo_canvas_style_get_element_id(a_style);
+
+        if (id != NULL) {
+            if (!strqcmp (id, a_add_sel->content.id_name->stryng->str,
+                          a_add_sel->content.id_name->stryng->len)) {
+                result = TRUE;
+            }
+        }
+        
+        return result;
+}
+
+/**
+ *Evaluates if a given additional selector matches an style object.
+ * param a_add_sel the additional selector to consider.
+ * param a_style the style object to consider.
+ * return TRUE is a_add_sel matches a_style, FALSE otherwise.
+ */
+static gboolean
+additional_selector_matches_style (HippoCanvasTheme *a_this,
+                                   CRAdditionalSel  *a_add_sel,
+                                   HippoCanvasStyle *a_style)
+{
+    CRAdditionalSel *cur_add_sel = NULL;
+    CRAdditionalSel *tail;
+    gboolean evaluated = FALSE;
+
+    g_return_val_if_fail (a_add_sel, FALSE) ;
+
+    for (tail = a_add_sel; 
+         tail && tail->next; 
+         tail = tail->next)
+        ;
+
+    for (cur_add_sel = tail ;
+         cur_add_sel ;
+         cur_add_sel = cur_add_sel->prev) {
+
+        evaluated = TRUE ;
+        if (cur_add_sel->type == NO_ADD_SELECTOR) {
+            return FALSE;
+        }
+        
+        if (cur_add_sel->type == CLASS_ADD_SELECTOR
+            && cur_add_sel->content.class_name
+            && cur_add_sel->content.class_name->stryng
+            && cur_add_sel->content.class_name->stryng->str) {
+            if (!class_add_sel_matches_style (cur_add_sel, a_style)) {
+                return FALSE;
+            }
+            continue ;
+        } else if (cur_add_sel->type == ID_ADD_SELECTOR
+                   && cur_add_sel->content.id_name
+                   && cur_add_sel->content.id_name->stryng
+                   && cur_add_sel->content.id_name->stryng->str) {
+            if (!id_add_sel_matches_style (cur_add_sel, a_style)) {
+                return FALSE;
+            }
+            continue ;
+        } else if (cur_add_sel->type == ATTRIBUTE_ADD_SELECTOR
+                   && cur_add_sel->content.attr_sel) {
+            g_warning("Attribute selectors not supported");
+            return FALSE;
+        } else if (cur_add_sel->type == PSEUDO_CLASS_ADD_SELECTOR
+                   && cur_add_sel->content.pseudo) {
+            /* FIXME: is this right?????; it's backwards from the rest */
+            if (pseudo_class_add_sel_matches_style(a_this, cur_add_sel, a_style)) {
+                return TRUE;
+            }
+            return FALSE;
+        }
+    }
+    
+    if (evaluated)
+        return TRUE;
+    
+    return FALSE ;
+}
+
+/* This is a workaround for:
+ *  - Python encodes '.' as '+' in type names
+ *  - libcroco doesn't correctly parse the \+ escape in types
+ *  - it's far too ugly to write foo\2b\bar\2bWidget...
+ *
+ * So we consider an element name of foo-bar-Widget to match a type
+ * of foo+bar+Widget
+ */
+static gboolean
+element_name_matches_type_name(const char *element_name,
+                               const char *type_name)
+{
+    const char *p = element_name;
+    const char *q = type_name;
+
+    while (*p || *q) {
+        if (*p == '-') {
+            if (*q != '-' && *q != '+')
+                return FALSE;
+        } else {
+            if (*p != *q)
+                return FALSE;
+        }
+
+        p++;
+        q++;
+    }
+
+    return TRUE;
+}
+
+static gboolean
+element_name_matches_type(const char *element_name,
+                          GType       element_type)
+{
+    /* This is not efficient, but the number of selectors with an element_name
+     * is probably fairly small.
+     */
+    if (element_type == G_TYPE_NONE) {
+        return strcmp(element_name, "canvas") == 0;
+    } else {
+        while (TRUE) {
+            if (element_name_matches_type_name(element_name, g_type_name(element_type)))
+                return TRUE;
+            
+            if (element_type == G_TYPE_OBJECT)
+                return FALSE;
+
+            element_type = g_type_parent(element_type);
+        }
+    }
+}
+
+/**
+ *Evaluate a selector (a simple selectors list) and says
+ *if it matches the style object given in parameter.
+ *The algorithm used here is the following:
+ *Walk the combinator separated list of simple selectors backward, starting
+ *from the end of the list. For each simple selector, looks if
+ *if matches the current style.
+ *
+ * param a_this the selection engine.
+ * param a_sel the simple selection list.
+ * param a_style the style object.
+ * param a_result out parameter. Set to true if the
+ *selector matches the style object, FALSE otherwise.
+ * param a_recurse if set to TRUE, the function will walk to
+ *the next simple selector (after the evaluation of the current one) 
+ *and recursively evaluate it. Must be usually set to TRUE unless you
+ *know what you are doing.
+ */
+static enum CRStatus
+sel_matches_style_real (HippoCanvasTheme *a_this,
+                        CRSimpleSel      *a_sel,
+                        HippoCanvasStyle *a_style,
+                        gboolean         *a_result,
+                        gboolean          a_eval_sel_list_from_end,
+                        gboolean          a_recurse)
+{
+    CRSimpleSel *cur_sel = NULL;
+    HippoCanvasStyle *cur_style = NULL;
+    GType cur_type;
+    
+    *a_result = FALSE;
+
+    if (a_eval_sel_list_from_end) {
+        /*go and get the last simple selector of the list */
+        for (cur_sel = a_sel; cur_sel && cur_sel->next; cur_sel = cur_sel->next) ;
+    } else {
+        cur_sel = a_sel;
+    }
+
+    cur_style = a_style;
+    cur_type = hippo_canvas_style_get_element_type(cur_style);
+
+    while (cur_sel) {
+        if (((cur_sel->type_mask & TYPE_SELECTOR)
+             && (cur_sel->name 
+                 && cur_sel->name->stryng
+                 && cur_sel->name->stryng->str)
+             && (element_name_matches_type (cur_sel->name->stryng->str, cur_type)))
+            || (cur_sel->type_mask & UNIVERSAL_SELECTOR)) {
+            /*
+             *this simple selector
+             *matches the current style object
+             *Let's see if the preceding
+             *simple selectors also match
+             *their style object counterpart.
+             */
+            if (cur_sel->add_sel) {
+                if (additional_selector_matches_style (a_this, cur_sel->add_sel, cur_style)) {
+                    goto walk_a_step_in_expr;
+                } else {
+                    goto done;
+                }
+            } else {
+                goto walk_a_step_in_expr;
+            }                                
+        } 
+        if (!(cur_sel->type_mask & TYPE_SELECTOR)
+            && !(cur_sel->type_mask & UNIVERSAL_SELECTOR)) {
+            if (!cur_sel->add_sel) {
+                goto done;
+            }
+            if (additional_selector_matches_style(a_this, cur_sel->add_sel, cur_style)) {
+                goto walk_a_step_in_expr;
+            } else {
+                goto done;
+            }
+        } else {
+            goto done ;
+        }
+        
+    walk_a_step_in_expr:
+        if (a_recurse == FALSE) {
+            *a_result = TRUE;
+            goto done;
+        }
+        
+        /*
+         *here, depending on the combinator of cur_sel
+         *choose the axis of the element tree traversal
+         *and walk one step in the element tree.
+         */
+        if (!cur_sel->prev)
+            break;
+        
+        switch (cur_sel->combinator) {
+        case NO_COMBINATOR:
+            break;
+            
+        case COMB_WS:  /*descendant selector */
+            {
+                HippoCanvasStyle *n = NULL;
+                
+                /*
+                 *walk the element tree upward looking for a parent
+                 *style that matches the preceding selector.
+                 */
+                for (n = hippo_canvas_style_get_parent(a_style); n; n = hippo_canvas_style_get_parent(n)) {
+                    enum CRStatus status;
+                    gboolean matches = FALSE;
+                    
+                    status = sel_matches_style_real(a_this, cur_sel->prev, n, &matches, FALSE, TRUE);
+                    
+                    if (status != CR_OK)
+                        goto done;
+                    
+                    if (matches) {
+                        cur_style = n;
+                        cur_type = hippo_canvas_style_get_element_type(cur_style);
+                        break;
+                    }
+                }
+                
+                if (!n) {
+                    /*
+                     *didn't find any ancestor that matches
+                     *the previous simple selector.
+                     */
+                    goto done;
+                }
+                /*
+                 *in this case, the preceding simple sel
+                 *will have been interpreted twice, which
+                 *is a cpu and mem waste ... I need to find
+                 *another way to do this. Anyway, this is
+                 *my first attempt to write this function and
+                 *I am a bit clueless.
+                 */
+                break;
+            }
+            
+        case COMB_PLUS:
+            g_warning("+ combinators are not supported");
+            goto done;
+            
+        case COMB_GT:
+            cur_style = hippo_canvas_style_get_parent (cur_style);
+            if (!cur_style)
+                goto done;
+            cur_type = hippo_canvas_style_get_element_type(cur_style);
+            break;
+            
+        default:
+            goto done;
+        }
+
+        cur_sel = cur_sel->prev;
+    }
+    
+    /*
+     *if we reached this point, it means the selector matches
+     *the style object.
+     */
+    *a_result = TRUE;
+    
+ done:
+    return CR_OK;
+}
+
+
+static void
+add_matched_properties (HippoCanvasTheme *a_this,
+                        CRStyleSheet     *a_stylesheet,
+                        HippoCanvasStyle *a_style,
+                        GPtrArray        *props)
+{
+    CRStatement *cur_stmt = NULL;
+    CRSelector *sel_list = NULL;
+    CRSelector *cur_sel = NULL;
+    gboolean matches = FALSE;
+    enum CRStatus status = CR_OK;
+    
+    /*
+     *walk through the list of statements and,
+     *get the selectors list inside the statements that
+     *contain some, and try to match our style object in these
+     *selectors lists.
+     */
+    for (cur_stmt = a_stylesheet->statements; cur_stmt; cur_stmt = cur_stmt->next) {
+        /*
+         *initialyze the selector list in which we will
+         *really perform the search.
+         */
+        sel_list = NULL;
+
+        /*
+         *get the the damn selector list in 
+         *which we have to look
+         */
+        switch (cur_stmt->type) {
+        case RULESET_STMT:
+            if (cur_stmt->kind.ruleset
+                && cur_stmt->kind.ruleset->sel_list) {
+                sel_list = cur_stmt->kind.ruleset->sel_list;
+            }
+            break;
+            
+        case AT_MEDIA_RULE_STMT:
+            if (cur_stmt->kind.media_rule
+                && cur_stmt->kind.media_rule->rulesets
+                && cur_stmt->kind.media_rule->rulesets->kind.ruleset
+                && cur_stmt->kind.media_rule->rulesets->kind.ruleset->sel_list) {
+                sel_list = cur_stmt->kind.media_rule->rulesets->kind.ruleset->sel_list;
+            }
+            break;
+            
+        case AT_IMPORT_RULE_STMT:
+            /*
+             *some recursivity may be needed here.
+             *I don't like this :(
+             */
+            break;
+        default:
+            break;
+        }
+        
+        if (!sel_list)
+            continue;
+        
+        /*
+         *now, we have a comma separated selector list to look in.
+         *let's walk it and try to match the style object
+         *on each item of the list.
+         */
+        for (cur_sel = sel_list; cur_sel; cur_sel = cur_sel->next) {
+            if (!cur_sel->simple_sel)
+                continue;
+            
+            status = sel_matches_style_real(a_this, cur_sel->simple_sel, a_style, &matches, TRUE, TRUE);
+            
+            if (status == CR_OK && matches) {
+                CRDeclaration *cur_decl = NULL;
+                
+                /* In order to sort the matching properties, we need to compute the
+                 * specificity of the selector that actually matched this
+                 * element. In a non-thread-safe fashion, we store it in the
+                 * ruleset. (Fixing this would mean cut-and-pasting 
+                 * cr_simple_sel_compute_specificity(), and have no need for
+                 * thread-safety anyways.)
+                 *
+                 * Once we've sorted the properties, the specificity no longer
+                 * matters and it can be safely overriden.
+                 */
+                cr_simple_sel_compute_specificity (cur_sel->simple_sel);
+                    
+                cur_stmt->specificity = cur_sel->simple_sel->specificity;
+                
+                for (cur_decl = cur_stmt->kind.ruleset->decl_list; cur_decl; cur_decl = cur_decl->next)
+                    g_ptr_array_add(props, cur_decl);
+            }
+        }
+    }
+}
+
+#define ORIGIN_AUTHOR_IMPORTANT (ORIGIN_AUTHOR + 1)
+#define ORIGIN_USER_IMPORTANT   (ORIGIN_AUTHOR + 2)
+
+static inline int
+get_origin(const CRDeclaration *decl)
+{
+    enum CRStyleOrigin origin = decl->parent_statement->parent_sheet->origin;
+
+    if (decl->important) {
+        if (origin == ORIGIN_AUTHOR)
+            return ORIGIN_AUTHOR_IMPORTANT;
+        else if (origin == ORIGIN_USER)
+            return ORIGIN_USER_IMPORTANT;
+    }
+
+    return origin;
+}
+
+/* Order of comparison is so that higher priority statements compare after
+ * lower priority statements */
+static int
+compare_declarations(gconstpointer a,
+                     gconstpointer b)
+{
+    /* g_ptr_array_sort() is broooken */
+    CRDeclaration *decl_a = *(CRDeclaration **)a;
+    CRDeclaration *decl_b = *(CRDeclaration **)b;
+    
+    int origin_a = get_origin(decl_a);
+    int origin_b = get_origin(decl_b);
+
+    if (origin_a != origin_b)
+        return origin_a - origin_b;
+    
+    if (decl_a->parent_statement->specificity != decl_b->parent_statement->specificity)
+        return decl_a->parent_statement->specificity - decl_b->parent_statement->specificity;
+
+    return 0;
+}
+                     
+void
+_hippo_canvas_theme_get_matched_properties (HippoCanvasTheme *theme,
+                                            HippoCanvasStyle *style,
+                                            CRDeclaration  ***properties,
+                                            int              *n_properties)
+{
+    enum CRStyleOrigin origin = 0;
+    CRStyleSheet *sheet = NULL;
+    GPtrArray *props = g_ptr_array_new();
+    
+    g_return_if_fail (HIPPO_IS_CANVAS_THEME(theme));
+    g_return_if_fail (HIPPO_IS_CANVAS_STYLE(style));
+    
+    for (origin = ORIGIN_UA; origin < NB_ORIGINS; origin++) {
+        sheet = cr_cascade_get_sheet (theme->cascade, origin);
+        if (!sheet)
+            continue;
+
+        add_matched_properties(theme, sheet, style, props);
+    }
+
+    /* We count on a stable sort here so that later declarations come
+     * after earlier declarations */
+    g_ptr_array_sort(props, compare_declarations);
+
+    *n_properties = props->len;
+    *properties = (CRDeclaration **)g_ptr_array_free(props, FALSE);
+}

Added: trunk/common/hippo/hippo-canvas-theme.h
==============================================================================
--- (empty file)
+++ trunk/common/hippo/hippo-canvas-theme.h	Mon Mar 24 19:05:00 2008
@@ -0,0 +1,30 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+#ifndef __HIPPO_CANVAS_THEME_H__
+#define __HIPPO_CANVAS_THEME_H__
+
+#include <hippo/hippo-canvas-context.h>
+#include <hippo/hippo-canvas-box.h>
+#include <hippo/hippo-canvas-theme-engine.h>
+
+G_BEGIN_DECLS
+
+#define HIPPO_TYPE_CANVAS_THEME              (hippo_canvas_theme_get_type ())
+#define HIPPO_CANVAS_THEME(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), HIPPO_TYPE_CANVAS_THEME, HippoCanvasTheme))
+#define HIPPO_CANVAS_THEME_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), HIPPO_TYPE_CANVAS_THEME, HippoCanvasThemeClass))
+#define HIPPO_IS_CANVAS_THEME(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), HIPPO_TYPE_CANVAS_THEME))
+#define HIPPO_IS_CANVAS_THEME_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), HIPPO_TYPE_CANVAS_THEME))
+#define HIPPO_CANVAS_THEME_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), HIPPO_TYPE_CANVAS_THEME, HippoCanvasThemeClass))
+
+GType             hippo_canvas_theme_get_type          (void) G_GNUC_CONST;
+
+HippoCanvasTheme *hippo_canvas_theme_new (HippoCanvasThemeEngine *theme_engine,
+                                          const char             *application_stylesheet,
+                                          const char             *theme_stylesheet,
+                                          const char             *default_stylesheet);
+
+HippoCanvasThemeEngine *hippo_canvas_theme_get_theme_engine (HippoCanvasTheme *theme);
+
+
+G_END_DECLS
+
+#endif /* __HIPPO_CANVAS_THEME_H__ */

Modified: trunk/common/hippo/hippo-canvas-type-builtins.c
==============================================================================
--- trunk/common/hippo/hippo-canvas-type-builtins.c	(original)
+++ trunk/common/hippo/hippo-canvas-type-builtins.c	Mon Mar 24 19:05:00 2008
@@ -30,29 +30,13 @@
   return type;
 }
 
-const GEnumValue _hippo_cascade_mode_values[] = {
-  { HIPPO_CASCADE_MODE_NONE, "HIPPO_CASCADE_MODE_NONE", "none" },
-  { HIPPO_CASCADE_MODE_INHERIT, "HIPPO_CASCADE_MODE_INHERIT", "inherit" },
-  { 0, NULL, NULL }
-};
-
-GType
-hippo_cascade_mode_get_type (void)
-{
-  static GType type = 0;
-
-  if (G_UNLIKELY (type == 0))
-    type = g_enum_register_static ("HippoCascadeMode", _hippo_cascade_mode_values);
-
-  return type;
-}
-
 
 /* enumerations from "hippo/hippo-canvas-context.h" */
 #include "hippo/hippo-canvas-context.h"
 const GEnumValue _hippo_stock_color_values[] = {
-  { HIPPO_STOCK_COLOR_BG_NORMAL, "HIPPO_STOCK_COLOR_BG_NORMAL", "normal" },
-  { HIPPO_STOCK_COLOR_BG_PRELIGHT, "HIPPO_STOCK_COLOR_BG_PRELIGHT", "prelight" },
+  { HIPPO_STOCK_COLOR_BG_NORMAL, "HIPPO_STOCK_COLOR_BG_NORMAL", "bg-normal" },
+  { HIPPO_STOCK_COLOR_BG_PRELIGHT, "HIPPO_STOCK_COLOR_BG_PRELIGHT", "bg-prelight" },
+  { HIPPO_STOCK_COLOR_FG, "HIPPO_STOCK_COLOR_FG", "fg" },
   { 0, NULL, NULL }
 };
 
@@ -108,6 +92,65 @@
 }
 
 
+/* enumerations from "hippo/hippo-canvas-style.h" */
+#include "hippo/hippo-canvas-style.h"
+const GEnumValue _hippo_canvas_link_type_values[] = {
+  { HIPPO_CANVAS_LINK_NONE, "HIPPO_CANVAS_LINK_NONE", "none" },
+  { HIPPO_CANVAS_LINK_LINK, "HIPPO_CANVAS_LINK_LINK", "link" },
+  { HIPPO_CANVAS_LINK_VISITED, "HIPPO_CANVAS_LINK_VISITED", "visited" },
+  { 0, NULL, NULL }
+};
+
+GType
+hippo_canvas_link_type_get_type (void)
+{
+  static GType type = 0;
+
+  if (G_UNLIKELY (type == 0))
+    type = g_enum_register_static ("HippoCanvasLinkType", _hippo_canvas_link_type_values);
+
+  return type;
+}
+
+const GEnumValue _hippo_canvas_side_values[] = {
+  { HIPPO_CANVAS_SIDE_LEFT, "HIPPO_CANVAS_SIDE_LEFT", "left" },
+  { HIPPO_CANVAS_SIDE_RIGHT, "HIPPO_CANVAS_SIDE_RIGHT", "right" },
+  { HIPPO_CANVAS_SIDE_TOP, "HIPPO_CANVAS_SIDE_TOP", "top" },
+  { HIPPO_CANVAS_SIDE_BOTTOM, "HIPPO_CANVAS_SIDE_BOTTOM", "bottom" },
+  { 0, NULL, NULL }
+};
+
+GType
+hippo_canvas_side_get_type (void)
+{
+  static GType type = 0;
+
+  if (G_UNLIKELY (type == 0))
+    type = g_enum_register_static ("HippoCanvasSide", _hippo_canvas_side_values);
+
+  return type;
+}
+
+const GFlagsValue _hippo_text_decoration_values[] = {
+  { HIPPO_TEXT_DECORATION_UNDERLINE, "HIPPO_TEXT_DECORATION_UNDERLINE", "underline" },
+  { HIPPO_TEXT_DECORATION_OVERLINE, "HIPPO_TEXT_DECORATION_OVERLINE", "overline" },
+  { HIPPO_TEXT_DECORATION_LINE_THROUGH, "HIPPO_TEXT_DECORATION_LINE_THROUGH", "line-through" },
+  { HIPPO_TEXT_DECORATION_BLINK, "HIPPO_TEXT_DECORATION_BLINK", "blink" },
+  { 0, NULL, NULL }
+};
+
+GType
+hippo_text_decoration_get_type (void)
+{
+  static GType type = 0;
+
+  if (G_UNLIKELY (type == 0))
+    type = g_flags_register_static ("HippoTextDecoration", _hippo_text_decoration_values);
+
+  return type;
+}
+
+
 /* enumerations from "hippo/hippo-canvas-text.h" */
 #include "hippo/hippo-canvas-text.h"
 const GEnumValue _hippo_canvas_size_mode_values[] = {

Modified: trunk/common/hippo/hippo-canvas-type-builtins.h
==============================================================================
--- trunk/common/hippo/hippo-canvas-type-builtins.h	(original)
+++ trunk/common/hippo/hippo-canvas-type-builtins.h	Mon Mar 24 19:05:00 2008
@@ -12,8 +12,6 @@
 /* --- hippo/hippo-canvas-box.h --- */
 #define HIPPO_TYPE_PACK_FLAGS hippo_pack_flags_get_type()
 GType hippo_pack_flags_get_type (void);
-#define HIPPO_TYPE_CASCADE_MODE hippo_cascade_mode_get_type()
-GType hippo_cascade_mode_get_type (void);
 
 /* --- hippo/hippo-canvas-context.h --- */
 #define HIPPO_TYPE_STOCK_COLOR hippo_stock_color_get_type()
@@ -25,6 +23,14 @@
 #define HIPPO_TYPE_ITEM_ALIGNMENT hippo_item_alignment_get_type()
 GType hippo_item_alignment_get_type (void);
 
+/* --- hippo/hippo-canvas-style.h --- */
+#define HIPPO_TYPE_CANVAS_LINK_TYPE hippo_canvas_link_type_get_type()
+GType hippo_canvas_link_type_get_type (void);
+#define HIPPO_TYPE_CANVAS_SIDE hippo_canvas_side_get_type()
+GType hippo_canvas_side_get_type (void);
+#define HIPPO_TYPE_TEXT_DECORATION hippo_text_decoration_get_type()
+GType hippo_text_decoration_get_type (void);
+
 /* --- hippo/hippo-canvas-text.h --- */
 #define HIPPO_TYPE_CANVAS_SIZE_MODE hippo_canvas_size_mode_get_type()
 GType hippo_canvas_size_mode_get_type (void);

Modified: trunk/configure.ac
==============================================================================
--- trunk/configure.ac	(original)
+++ trunk/configure.ac	Mon Mar 24 19:05:00 2008
@@ -142,8 +142,10 @@
 
 GLIB2_REQUIRED=2.6.0
 GTK2_REQUIRED=2.6.0
+LIBCROCO_REQUIRED=0.6.0
+PANGO_REQUIRED=1.14
 
-PKG_CHECK_MODULES(LIBHIPPOCANVAS, gobject-2.0 >= $GLIB2_REQUIRED gtk+-2.0 >= $GTK2_REQUIRED cairo pango)
+PKG_CHECK_MODULES(LIBHIPPOCANVAS, gobject-2.0 >= $GLIB2_REQUIRED gtk+-2.0 >= $GTK2_REQUIRED libcroco-0.6 >= $LIBCROCO_REQUIRED cairo pango >= $PANGO_REQUIRED)
 
 GLIB_GENMARSHAL=`$PKG_CONFIG --variable=glib_genmarshal glib-2.0`
 AC_SUBST(GLIB_GENMARSHAL)

Modified: trunk/linux/hippo/hippo-canvas-helper.c
==============================================================================
--- trunk/linux/hippo/hippo-canvas-helper.c	(original)
+++ trunk/linux/hippo/hippo-canvas-helper.c	Mon Mar 24 19:05:00 2008
@@ -2,6 +2,8 @@
 #include <config.h>
 #include <glib/gi18n-lib.h>
 #include <hippo/hippo-canvas-context.h>
+#include <hippo/hippo-canvas-style.h>
+#include <hippo/hippo-canvas-theme.h>
 #include <gtk/gtkcontainer.h>
 #include "hippo-canvas-widget.h"
 #include "hippo-canvas-helper.h"
@@ -58,6 +60,10 @@
                                                                     int                *x_p,
                                                                     int                *y_p);
 
+HippoCanvasStyle *    hippo_canvas_helper_get_style      (HippoCanvasContext *context);
+double                hippo_canvas_helper_get_resolution (HippoCanvasContext *context);
+PangoFontDescription *hippo_canvas_helper_get_font       (HippoCanvasContext *context);
+
 static void             hippo_canvas_helper_fixup_resize_state     (HippoCanvasHelper  *canvas);
 
 static void       tooltip_window_update   (GtkWidget      *tip,
@@ -75,6 +81,9 @@
 
     GtkWidget *widget;
 
+    HippoCanvasTheme *theme;
+    HippoCanvasStyle *style;
+    
     HippoCanvasItem *root;
 
     HippoCanvasPointer pointer;
@@ -143,6 +152,9 @@
     klass->unregister_widget_item = hippo_canvas_helper_unregister_widget_item;
     klass->translate_to_widget = hippo_canvas_helper_translate_to_widget;
     klass->translate_to_screen = hippo_canvas_helper_translate_to_screen;
+    klass->get_style = hippo_canvas_helper_get_style;
+    klass->get_resolution = hippo_canvas_helper_get_resolution;
+    klass->get_font = hippo_canvas_helper_get_font;
 }
 
 static void
@@ -165,6 +177,7 @@
     if (helper->root != NULL) {
         HippoCanvasItem *old_root = g_object_ref(helper->root);
         hippo_canvas_helper_set_root(helper, NULL);
+        hippo_canvas_helper_set_theme(helper, NULL);
         hippo_canvas_item_destroy(old_root);
         g_object_unref(old_root);
     }
@@ -671,6 +684,32 @@
 }
 
 void
+hippo_canvas_helper_set_window_background (HippoCanvasHelper *helper,
+                                           GdkWindow         *window)
+{
+    HippoCanvasStyle *style = hippo_canvas_context_get_style(HIPPO_CANVAS_CONTEXT(helper));
+    guint32 color;
+    
+    if (hippo_canvas_style_get_color(style, "background-color", FALSE, &color)) {
+        GdkColormap *colormap = gdk_window_get_colormap(window);
+        GdkColor color_gdk;
+
+        color_gdk.red =   (color & 0xFF000000) >> 24;
+        color_gdk.red = color_gdk.red * 0x101;
+        color_gdk.green = (color & 0x00FF0000) >> 16;
+        color_gdk.green = color_gdk.green * 0x101;
+        color_gdk.blue =  (color & 0x0000FF00) >> 8;
+        color_gdk.blue = color_gdk.blue * 0x101;
+
+        gdk_rgb_find_color(colormap, &color_gdk);
+
+        gdk_window_set_background(window, &color_gdk);
+    } else {
+        gtk_style_set_background (helper->widget->style, window, GTK_STATE_NORMAL);
+    }
+}
+
+void
 hippo_canvas_helper_add(HippoCanvasHelper *helper,
                         GtkWidget         *widget)
 {
@@ -789,6 +828,9 @@
     case HIPPO_STOCK_COLOR_BG_PRELIGHT:
         return convert_color(&style->bg[GTK_STATE_PRELIGHT]);
         break;
+    case HIPPO_STOCK_COLOR_FG:
+        return convert_color(&style->fg[GTK_STATE_NORMAL]);
+        break;
     }
 
     g_warning("unknown stock color %d", color);
@@ -975,6 +1017,35 @@
         *y_p += window_y;
 }
 
+HippoCanvasStyle *
+hippo_canvas_helper_get_style (HippoCanvasContext *context)
+{
+    HippoCanvasHelper *helper = HIPPO_CANVAS_HELPER(context);
+
+    if (helper->style == NULL) {
+        helper->style = hippo_canvas_style_new(context, NULL, helper->theme,
+                                               G_TYPE_NONE, NULL, NULL);
+    }
+
+    return helper->style;
+}
+
+double
+hippo_canvas_helper_get_resolution (HippoCanvasContext *context)
+{
+    HippoCanvasHelper *helper = HIPPO_CANVAS_HELPER(context);
+    
+    return gdk_screen_get_resolution(gtk_widget_get_screen(helper->widget));
+}
+
+PangoFontDescription *
+hippo_canvas_helper_get_font (HippoCanvasContext *context)
+{
+    HippoCanvasHelper *helper = HIPPO_CANVAS_HELPER(context);
+
+    return helper->widget->style->font_desc;
+}
+
 static void
 canvas_root_destroy(HippoCanvasItem   *root,
                     HippoCanvasHelper *helper)
@@ -1077,6 +1148,32 @@
     gtk_widget_queue_resize(widget);
 }
 
+void
+hippo_canvas_helper_set_theme(HippoCanvasHelper *canvas_helper,
+                              HippoCanvasTheme  *theme)
+{
+    g_return_if_fail(HIPPO_IS_CANVAS_HELPER(canvas_helper));
+    g_return_if_fail(theme == NULL || HIPPO_IS_CANVAS_THEME(theme));
+
+    if (theme == canvas_helper->theme)
+        return;
+
+    if (canvas_helper->theme)
+        g_object_unref(canvas_helper->theme);
+
+    canvas_helper->theme = theme;
+
+    if (canvas_helper->theme)
+        g_object_ref(canvas_helper->theme);
+    
+    if (canvas_helper->style) {
+        g_object_unref(canvas_helper->style);
+        canvas_helper->style = NULL;
+    }
+
+    hippo_canvas_context_emit_style_changed(HIPPO_CANVAS_CONTEXT(canvas_helper), TRUE);
+}
+
 GtkWidget *
 hippo_canvas_helper_get_widget(HippoCanvasHelper *helper)
 {

Modified: trunk/linux/hippo/hippo-canvas-helper.h
==============================================================================
--- trunk/linux/hippo/hippo-canvas-helper.h	(original)
+++ trunk/linux/hippo/hippo-canvas-helper.h	Mon Mar 24 19:05:00 2008
@@ -31,6 +31,9 @@
 HippoCanvasHelper* hippo_canvas_helper_new (GtkContainer *base_container);
 void         hippo_canvas_helper_set_root (HippoCanvasHelper *helper,
                                            HippoCanvasItem   *root);
+void         hippo_canvas_helper_set_theme(HippoCanvasHelper *canvas_helper,
+                                           HippoCanvasTheme  *theme);
+
 GtkWidget   *hippo_canvas_helper_get_widget(HippoCanvasHelper *helper);
 
 /* Set an explicit width on the helper; this will be used instead of the minimum
@@ -69,6 +72,9 @@
 void  hippo_canvas_helper_hierarchy_changed (HippoCanvasHelper *helper,
                                              GtkWidget         *old_toplevel);
 
+void  hippo_canvas_helper_set_window_background (HippoCanvasHelper *helper,
+                                                 GdkWindow         *window);
+
 void  hippo_canvas_helper_add               (HippoCanvasHelper *helper,
                                              GtkWidget         *widget);
 void  hippo_canvas_helper_remove            (HippoCanvasHelper *helper,

Modified: trunk/linux/hippo/hippo-canvas-widget.c
==============================================================================
--- trunk/linux/hippo/hippo-canvas-widget.c	(original)
+++ trunk/linux/hippo/hippo-canvas-widget.c	Mon Mar 24 19:05:00 2008
@@ -4,6 +4,7 @@
 #include <string.h>
 #include <gtk/gtkwidget.h>
 #include "hippo-canvas-widget.h"
+#include "hippo-canvas-style.h"
 
 static void      hippo_canvas_widget_init                (HippoCanvasWidget       *widget);
 static void      hippo_canvas_widget_class_init          (HippoCanvasWidgetClass  *klass);
@@ -202,6 +203,27 @@
 }
 
 static void
+on_context_style_changed(HippoCanvasContext   *context,
+                         gboolean              resize_needed,
+                         HippoCanvasWidget    *widget)
+{
+    HippoCanvasStyle *style = NULL;
+    HippoCanvasTheme *theme = NULL;
+
+    if (context != NULL)
+        style = hippo_canvas_context_get_style(context);
+    if (style != NULL)
+        theme = hippo_canvas_style_get_theme(style);
+
+    if (theme != widget->theme) {
+        widget->theme = theme;
+
+        if (HIPPO_CANVAS_WIDGET_GET_CLASS(widget)->theme_changed)
+            HIPPO_CANVAS_WIDGET_GET_CLASS(widget)->theme_changed(widget);
+    }
+}
+
+static void
 hippo_canvas_widget_set_context(HippoCanvasItem    *item,
                                 HippoCanvasContext *context)
 {
@@ -210,14 +232,26 @@
     if (context == box->context)
         return;
 
-    if (box->context)
+    if (box->context) {
         hippo_canvas_context_unregister_widget_item(box->context, item);
 
+        g_signal_handlers_disconnect_by_func(box->context,
+                                             (gpointer)on_context_style_changed,
+                                             context);
+    }
+
     /* chain up, which invalidates our old context */
     item_parent_class->set_context(item, context);
 
-    if (box->context)
+    if (box->context) {
         hippo_canvas_context_register_widget_item(box->context, item);
+
+        g_signal_connect(box->context, "style-changed",
+                         G_CALLBACK(on_context_style_changed), item);
+
+    }
+
+    on_context_style_changed(context, TRUE, HIPPO_CANVAS_WIDGET(item));
 }
 
 static void

Modified: trunk/linux/hippo/hippo-canvas-widget.h
==============================================================================
--- trunk/linux/hippo/hippo-canvas-widget.h	(original)
+++ trunk/linux/hippo/hippo-canvas-widget.h	Mon Mar 24 19:05:00 2008
@@ -23,11 +23,14 @@
 
 struct _HippoCanvasWidget {
     HippoCanvasBox box;
+    HippoCanvasTheme *theme;
     GtkWidget *widget;
 };
 
 struct _HippoCanvasWidgetClass {
     HippoCanvasBoxClass parent_class;
+
+    void (*theme_changed) (HippoCanvasWidget *widget);
 };
 
 GType        	 hippo_canvas_widget_get_type               (void) G_GNUC_CONST;

Modified: trunk/linux/hippo/hippo-canvas-widgets.c
==============================================================================
--- trunk/linux/hippo/hippo-canvas-widgets.c	(original)
+++ trunk/linux/hippo/hippo-canvas-widgets.c	Mon Mar 24 19:05:00 2008
@@ -13,18 +13,11 @@
 #define HIPPO_DEFINE_WIDGET_ITEM(lower, Camel)                                         \
     struct _HippoCanvas##Camel { HippoCanvasWidget parent; };                          \
     struct _HippoCanvas##Camel##Class { HippoCanvasWidgetClass parent; };              \
-    static void hippo_canvas_##lower##_class_init(HippoCanvas##Camel##Class *lower) {} \
     G_DEFINE_TYPE(HippoCanvas##Camel, hippo_canvas_##lower, HIPPO_TYPE_CANVAS_WIDGET)
 
-#define HIPPO_DEFINE_WIDGET_ITEM_CUSTOM_INIT(lower, Camel)                             \
-    struct _HippoCanvas##Camel { HippoCanvasWidget parent; };                          \
-    struct _HippoCanvas##Camel##Class { HippoCanvasWidgetClass parent; };              \
-    G_DEFINE_TYPE(HippoCanvas##Camel, hippo_canvas_##lower, HIPPO_TYPE_CANVAS_WIDGET)
-
-
-HIPPO_DEFINE_WIDGET_ITEM_CUSTOM_INIT(button, Button);
+HIPPO_DEFINE_WIDGET_ITEM(button, Button);
 HIPPO_DEFINE_WIDGET_ITEM(scrollbars, Scrollbars);
-HIPPO_DEFINE_WIDGET_ITEM_CUSTOM_INIT(entry, Entry);
+HIPPO_DEFINE_WIDGET_ITEM(entry, Entry);
 
 enum {
     BUTTON_PROP_0,
@@ -178,6 +171,23 @@
 }
 
 static void
+hippo_canvas_scrollbars_theme_changed(HippoCanvasWidget *widget_item)
+{
+    GtkWidget *viewport = gtk_bin_get_child(GTK_BIN(widget_item->widget));
+    GtkWidget *canvas = gtk_bin_get_child(GTK_BIN(viewport));
+
+    hippo_canvas_set_theme(HIPPO_CANVAS(canvas), widget_item->theme);
+}
+
+static void
+hippo_canvas_scrollbars_class_init(HippoCanvasScrollbarsClass *class)
+{
+    HippoCanvasWidgetClass *widget_class = HIPPO_CANVAS_WIDGET_CLASS(class);
+
+    widget_class->theme_changed = hippo_canvas_scrollbars_theme_changed;
+}
+
+static void
 hippo_canvas_scrollbars_init(HippoCanvasScrollbars *scrollbars)
 {
     GtkWidget *widget;

Modified: trunk/linux/hippo/hippo-canvas-window.c
==============================================================================
--- trunk/linux/hippo/hippo-canvas-window.c	(original)
+++ trunk/linux/hippo/hippo-canvas-window.c	Mon Mar 24 19:05:00 2008
@@ -22,6 +22,8 @@
                                            GValue       *value,
                                            GParamSpec   *pspec);
 
+static void  hippo_canvas_window_realize (GtkWidget    *widget);
+
 static gboolean  hippo_canvas_window_button_press        (GtkWidget         *widget,
                                                           GdkEventButton    *event);
 static gboolean  hippo_canvas_window_button_release      (GtkWidget         *widget,
@@ -71,6 +73,7 @@
     object_class->dispose = hippo_canvas_window_dispose;
     object_class->finalize = hippo_canvas_window_finalize;
 
+    widget_class->realize = hippo_canvas_window_realize;
     widget_class->button_press_event = hippo_canvas_window_button_press;
     widget_class->button_release_event = hippo_canvas_window_button_release;
     widget_class->motion_notify_event = hippo_canvas_window_motion_notify;
@@ -85,6 +88,8 @@
     GtkWidget *widget = GTK_WIDGET(canvas_window);
     GtkWidget *window_child;
 
+    gtk_widget_set_app_paintable(widget, TRUE);
+
     gtk_widget_add_events(widget, HIPPO_CANVAS_EVENT_MASK);
 
     window_child = hippo_canvas_window_child_new();
@@ -164,6 +169,16 @@
     }
 }
 
+static void
+hippo_canvas_window_realize (GtkWidget *widget)
+{
+    HippoCanvasWindow *canvas_window = HIPPO_CANVAS_WINDOW(widget);
+
+    GTK_WIDGET_CLASS(hippo_canvas_window_parent_class)->realize(widget);
+
+    hippo_canvas_helper_set_window_background(canvas_window->helper, widget->window);
+}
+
 static gboolean
 hippo_canvas_window_button_press(GtkWidget         *widget,
                                  GdkEventButton    *event)
@@ -262,3 +277,23 @@
 
     hippo_canvas_helper_set_root(canvas_window->helper, item);
 }
+
+void
+hippo_canvas_window_set_theme(HippoCanvasWindow *canvas_window,
+                              HippoCanvasTheme  *theme)
+{
+    g_return_if_fail(HIPPO_IS_CANVAS_WINDOW(canvas_window));
+
+    hippo_canvas_helper_set_theme(canvas_window->helper, theme);
+    if (GTK_WIDGET_REALIZED(canvas_window))
+        hippo_canvas_helper_set_window_background(canvas_window->helper, GTK_WIDGET(canvas_window)->window);
+}
+
+HippoCanvasContext *
+hippo_canvas_window_get_context(HippoCanvasWindow *canvas_window)
+{
+    g_return_val_if_fail(HIPPO_IS_CANVAS_WINDOW(canvas_window), NULL);
+
+    return HIPPO_CANVAS_CONTEXT(canvas_window->helper);
+}
+

Modified: trunk/linux/hippo/hippo-canvas-window.h
==============================================================================
--- trunk/linux/hippo/hippo-canvas-window.h	(original)
+++ trunk/linux/hippo/hippo-canvas-window.h	Mon Mar 24 19:05:00 2008
@@ -26,6 +26,10 @@
 void hippo_canvas_window_set_root(HippoCanvasWindow *canvas_window,
                                   HippoCanvasItem   *item);
 
+void hippo_canvas_window_set_theme(HippoCanvasWindow *canvas_window,
+                                   HippoCanvasTheme  *theme);
+
+HippoCanvasContext *hippo_canvas_window_get_context(HippoCanvasWindow *canvas_window);
 
 G_END_DECLS
 

Modified: trunk/linux/hippo/hippo-canvas.c
==============================================================================
--- trunk/linux/hippo/hippo-canvas.c	(original)
+++ trunk/linux/hippo/hippo-canvas.c	Mon Mar 24 19:05:00 2008
@@ -365,7 +365,7 @@
     gdk_window_set_user_data (widget->window, widget);
     
     widget->style = gtk_style_attach (widget->style, widget->window);
-    gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+    hippo_canvas_helper_set_window_background(canvas->helper, widget->window);
     
     hippo_canvas_helper_realize(canvas->helper);
 }
@@ -448,6 +448,17 @@
 }
 
 void
+hippo_canvas_set_theme(HippoCanvas       *canvas,
+                       HippoCanvasTheme  *theme)
+{
+    g_return_if_fail(HIPPO_IS_CANVAS(canvas));
+
+    hippo_canvas_helper_set_theme(canvas->helper, theme);
+    if (GTK_WIDGET_REALIZED(canvas))
+        hippo_canvas_helper_set_window_background(canvas->helper, GTK_WIDGET(canvas)->window);
+}
+
+void
 hippo_canvas_set_width(HippoCanvas     *canvas,
                        int              width)
 {
@@ -456,6 +467,14 @@
     hippo_canvas_helper_set_width(canvas->helper, width);
 }
 
+HippoCanvasContext *
+hippo_canvas_get_context(HippoCanvas *canvas)
+{
+    g_return_val_if_fail(HIPPO_IS_CANVAS(canvas), NULL);
+
+    return HIPPO_CANVAS_CONTEXT(canvas->helper);
+}
+
 GtkWidget *
 hippo_canvas_get_canvas_for_item(HippoCanvasItem *item)
 {

Modified: trunk/linux/hippo/hippo-canvas.h
==============================================================================
--- trunk/linux/hippo/hippo-canvas.h	(original)
+++ trunk/linux/hippo/hippo-canvas.h	Mon Mar 24 19:05:00 2008
@@ -30,11 +30,16 @@
 GtkWidget*   hippo_canvas_new      (void);
 void         hippo_canvas_set_root (HippoCanvas     *canvas,
                                     HippoCanvasItem *root);
+void         hippo_canvas_set_theme(HippoCanvas      *canvas,
+                                    HippoCanvasTheme *theme);
+
 void         hippo_canvas_set_width (HippoCanvas    *canvas,
                                      int             width);
 
 void         hippo_canvas_set_load_image_hook(HippoCanvasLoadImageHook hook);
 
+HippoCanvasContext *hippo_canvas_get_context(HippoCanvas *canvas);
+
 GtkWidget   *hippo_canvas_get_canvas_for_item(HippoCanvasItem *item);
 
 void hippo_canvas_open_test_window(void);

Modified: trunk/python/hippo.defs
==============================================================================
--- trunk/python/hippo.defs	(original)
+++ trunk/python/hippo.defs	Mon Mar 24 19:05:00 2008
@@ -85,6 +85,26 @@
   (gtype-id "HIPPO_TYPE_CANVAS_LINK")
 )
 
+(define-object CanvasStyle
+  (in-module "Hippo")
+  (parent "GObject")
+  (c-name "HippoCanvasStyle")
+  (gtype-id "HIPPO_TYPE_CANVAS_STYLE")
+)
+
+(define-object CanvasTheme
+  (in-module "Hippo")
+  (parent "GObject")
+  (c-name "HippoCanvasTheme")
+  (gtype-id "HIPPO_TYPE_CANVAS_THEME")
+)
+
+(define-interface CanvasThemeEngine
+  (in-module "Hippo")
+  (c-name "HippoCanvasThemeEngine")
+  (gtype-id "HIPPO_TYPE_CANVAS_THEME_ENGINE")
+)
+
 (define-object CanvasWidget
   (in-module "Hippo")
   (parent "HippoCanvasBox")
@@ -147,16 +167,6 @@
   )
 )
 
-(define-enum CascadeMode
-  (in-module "Hippo")
-  (c-name "HippoCascadeMode")
-  (gtype-id "HIPPO_TYPE_CASCADE_MODE")
-  (values
-    '("none" "HIPPO_CASCADE_MODE_NONE")
-    '("inherit" "HIPPO_CASCADE_MODE_INHERIT")
-  )
-)
-
 (define-enum StockColor
   (in-module "Hippo")
   (c-name "HippoStockColor")
@@ -329,6 +339,21 @@
   )
 )
 
+(define-method set_theme
+  (of-object "HippoCanvas")
+  (c-name "hippo_canvas_set_theme")
+  (return-type "none")
+  (parameters
+    '("HippoCanvasTheme*" "theme")
+  )
+)
+
+(define-method get_context
+  (of-object "HippoCanvas")
+  (c-name "hippo_canvas_get_context")
+  (return-type "HippoCanvasContext*")
+)
+
 (define-function canvas_set_load_image_hook
   (c-name "hippo_canvas_set_load_image_hook")
   (return-type "none")
@@ -405,7 +430,20 @@
   )
 )
 
+(define-method set_theme
+  (of-object "HippoCanvasWindow")
+  (c-name "hippo_canvas_window_set_theme")
+  (return-type "none")
+  (parameters
+    '("HippoCanvasTheme*" "theme")
+  )
+)
 
+(define-method get_context
+  (of-object "HippoCanvasWindow")
+  (c-name "hippo_canvas_window_get_context")
+  (return-type "HippoCanvasContext*")
+)
 
 ;; From hippo-canvas-box.h
 
@@ -767,6 +805,12 @@
   )
 )
 
+(define-method get_style
+  (of-object "HippoCanvasContext")
+  (c-name "hippo_canvas_context_get_style")
+  (return-type "HippoCanvasStyle*")
+)
+
 (define-method register_widget_item
   (of-object "HippoCanvasContext")
   (c-name "hippo_canvas_context_register_widget_item")
@@ -807,24 +851,6 @@
   )
 )
 
-(define-method affect_color
-  (of-object "HippoCanvasContext")
-  (c-name "hippo_canvas_context_affect_color")
-  (return-type "none")
-  (parameters
-    '("guint32*" "color_rgba_p")
-  )
-)
-
-(define-method affect_font_desc
-  (of-object "HippoCanvasContext")
-  (c-name "hippo_canvas_context_affect_font_desc")
-  (return-type "none")
-  (parameters
-    '("PangoFontDescription*" "font_desc")
-  )
-)
-
 (define-method emit_style_changed
   (of-object "HippoCanvasContext")
   (c-name "hippo_canvas_context_emit_style_changed")
@@ -1274,7 +1300,6 @@
 )
 
 
-
 ;; From hippo-canvas-style.h
 
 (define-function canvas_style_get_type
@@ -1282,24 +1307,47 @@
   (return-type "GType")
 )
 
-(define-method affect_color
+(define-method get_theme
   (of-object "HippoCanvasStyle")
-  (c-name "hippo_canvas_style_affect_color")
-  (return-type "none")
+  (c-name "hippo_canvas_style_get_theme")
+  (return-type "HippoCanvasTheme*")
+)
+
+(define-method get_color
+  (of-object "HippoCanvasStyle")
+  (c-name "hippo_canvas_style_get_color")
+  (return-type "gboolean")
   (parameters
-    '("guint32*" "color_rgba_p")
+    '("const-char*" "property_name")
+    '("gboolean" "inherit")
+    '("guint32*" "color")
   )
 )
 
-(define-method affect_font_desc
+(define-method get_double
   (of-object "HippoCanvasStyle")
-  (c-name "hippo_canvas_style_affect_font_desc")
-  (return-type "none")
+  (c-name "hippo_canvas_style_get_double")
+  (return-type "gboolean")
   (parameters
-    '("PangoFontDescription*" "font_desc")
+    '("const-char*" "property_name")
+    '("gboolean" "inherit")
+    '("double*" "color")
   )
 )
 
+(define-method paint
+  (of-object "HippoCanvasStyle")
+  (c-name "hippo_canvas_style_paint")
+  (return-type "gboolean")
+  (parameters
+    '("cairo_t*" "cr")
+    '("const-char*" "name")
+    '("double" "x")
+    '("double" "y")
+    '("double" "width")
+    '("double" "height")
+  )
+)
 
 
 ;; From hippo-canvas-test.h
@@ -1324,6 +1372,52 @@
   (return-type "HippoCanvasItem*")
 )
 
+;; From hippo-canvas-theme-engine.h
+
+(define-function canvas_theme_engine_get_type
+  (c-name "hippo_canvas_theme_engine_get_type")
+  (return-type "GType")
+)
+
+(define-virtual paint
+  (of-object "HippoCanvasThemeEngine")
+  (return-type "gboolean")
+  (parameters
+    '("HippoCanvasStyle*" "style")
+    '("cairo_t*" "cr")
+    '("const-char*" "name")
+    '("double" "x")
+    '("double" "y")
+    '("double" "width")
+    '("double" "height")
+  )
+)
+
+;; From hippo-canvas-theme.h
+
+(define-function canvas_theme_get_type
+  (c-name "hippo_canvas_theme_get_type")
+  (return-type "GType")
+)
+
+(define-function canvas_theme_new
+  (c-name "hippo_canvas_theme_new")
+  (is-constructor-of "HippoCanvasTheme")
+  (return-type "HippoCanvasTheme*")
+  (properties
+    '("theme_engine" (optional))
+    '("application_stylesheet" (optional))
+    '("theme_stylesheet" (optional))
+    '("default_stylesheet" (optional))
+  )
+)
+
+(define-method get_theme_engine
+  (of-object "HippoCanvasTheme")
+  (c-name "hippo_canvas_theme_get_theme_engine")
+  (return-type "HippoCanvasThemeEngine*")
+)
+
 ;; From hippo-canvas-widgets.h
 
 (define-function button_get_type

Modified: trunk/python/hippo.override
==============================================================================
--- trunk/python/hippo.override	(original)
+++ trunk/python/hippo.override	Mon Mar 24 19:05:00 2008
@@ -15,6 +15,7 @@
 #include "hippo/hippo-canvas-link.h"
 #include "hippo/hippo-canvas-gradient.h"
 #include "hippo/hippo-canvas.h"
+#include "hippo/hippo-canvas-theme.h"
 #include "hippo/hippo-canvas-widget.h"
 #include "hippo/hippo-canvas-window.h"
 #include "hippo/hippo-canvas-widgets.h"
@@ -836,6 +837,52 @@
     pyg_gil_state_release(__py_state);
 }
 %%
+override hippo_canvas_style_get_color kwargs
+static PyObject *
+_wrap_hippo_canvas_style_get_color(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+    static char *kwlist[] = { "property_name", "inherit", NULL };
+    char *property_name;
+    int inherit;
+    guint32 color;
+    PyObject *result; 
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"si:HippoCanvasStyle.get_color", kwlist, &property_name, &inherit))
+        return NULL;
+
+    if (hippo_canvas_style_get_color(HIPPO_CANVAS_STYLE(self->obj), property_name, inherit, &color)) {
+	result = PyLong_FromUnsignedLong(color);
+    } else {
+        Py_INCREF(Py_None);
+        result = Py_None;
+    }
+
+    return result;
+}
+%%
+override hippo_canvas_style_get_double kwargs
+static PyObject *
+_wrap_hippo_canvas_style_get_double(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+    static char *kwlist[] = { "property_name", "inherit", NULL };
+    char *property_name;
+    int inherit;
+    double value;
+    PyObject *result; 
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"si:HippoCanvasStyle.get_double", kwlist, &property_name, &inherit))
+        return NULL;
+
+    if (hippo_canvas_style_get_double(HIPPO_CANVAS_STYLE(self->obj), property_name, inherit, &value)) {
+	result = PyFloat_FromDouble(value);
+    } else {
+        Py_INCREF(Py_None);
+        result = Py_None;
+    }
+
+    return result;
+}
+%%
 override hippo_cairo_surface_from_gdk_pixbuf kwargs
 static cairo_surface_t*
 _cairo_surface_from_pixbuf (GdkPixbuf *pixbuf)

Added: trunk/tests/test-theme.py
==============================================================================
--- (empty file)
+++ trunk/tests/test-theme.py	Mon Mar 24 19:05:00 2008
@@ -0,0 +1,50 @@
+import pygtk
+pygtk.require('2.0')
+import gtk
+import hippo
+
+window = gtk.Window()
+
+canvas = hippo.Canvas()
+theme = hippo.CanvasTheme(theme_stylesheet="test.css")
+canvas.set_theme(theme)
+
+window.add(canvas)
+canvas.show()
+
+box = hippo.CanvasBox(xalign=hippo.ALIGNMENT_FILL,
+		      yalign=hippo.ALIGNMENT_FILL,
+                      orientation=hippo.ORIENTATION_VERTICAL)
+canvas.set_root(box)
+canvas.set_size_request(400, 400)
+
+text = hippo.CanvasText(text="Default Text")
+box.append(text)
+
+box2 = hippo.CanvasBox(xalign=hippo.ALIGNMENT_FILL,
+                       yalign=hippo.ALIGNMENT_FILL,
+                       orientation=hippo.ORIENTATION_VERTICAL,
+                       id="box2")
+box.append(box2)
+text = hippo.CanvasText(text="Larger Text")
+box2.append(text)
+text = hippo.CanvasText(text="Red Text", classes="important")
+box2.append(text)
+
+link = hippo.CanvasLink(text="A Link")
+box2.append(link)
+
+link = hippo.CanvasLink(text="Visited Link", visited=True)
+box2.append(link)
+
+parent = box
+for i in xrange(0, 5):
+    nested = hippo.CanvasBox(xalign=hippo.ALIGNMENT_FILL,
+                             yalign=hippo.ALIGNMENT_FILL,
+                             classes="nested")
+    parent.append(nested)
+    parent = nested
+    
+window.show()
+
+gtk.main()

Added: trunk/tests/test.css
==============================================================================
--- (empty file)
+++ trunk/tests/test.css	Mon Mar 24 19:05:00 2008
@@ -0,0 +1,39 @@
+/* The canvas itself doesn't paint a background, but it's immediate box
+ * child does.
+ */
+canvas {
+    background-color: white;
+}
+
+:link {
+    text-decoration: underline;
+    color: blue;
+}
+
+:visited {
+    text-decoration: underline;
+    color: #666666;
+}
+
+#box2 {
+    font-size: large;
+    border-top: 5px solid red;
+    border-bottom: 5px solid green;
+    border-left: 5px solid blue;
+    border-right: 5px solid yellow;
+}
+
+.important {
+    color: red;
+    font-size: larger;
+    font-style: italic;
+    border: 1px solid green;
+}
+
+.nested {
+    border: 1px solid black;
+    background-color: red;
+    padding: 2px;
+    padding-left: 4px;
+    padding-right: 4px;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]