[monet/bberg-playground: 4/4] More changes, some basic features are working.



commit 07ddbd8647d5e0cd12c158e570c3b6413a35bbff
Author: Benjamin Berg <benjamin sipsolutions net>
Date:   Thu Aug 20 18:51:01 2009 +0200

    More changes, some basic features are working.

 configure.ac          |    4 +
 monet/mn-context.c    |  415 ++++++++++++++++++++++++++++++++++++++++++++++++-
 monet/mn-context.h    |   31 ++++-
 monet/mn-display.c    |   13 ++-
 monet/mn-display.h    |    5 +-
 monet/mn-stylesheet.c |  296 ++++++++++++++++++++++++++++++++++-
 monet/mn-stylesheet.h |   21 +++-
 monet/mn-theme-type.c |   18 ++
 monet/mn-theme-type.h |   18 ++-
 tests/Makefile.am     |    8 +-
 tests/test.c          |  156 ++++++++++++++++++
 11 files changed, 953 insertions(+), 32 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 8b00038..887a614 100644
--- a/configure.ac
+++ b/configure.ac
@@ -16,6 +16,10 @@ PKG_CHECK_MODULES(MONET, [cairo glib-2.0 gobject-2.0])
 AC_SUBST(MONET_LIBS)
 AC_SUBST(MONET_CFLAGS)
 
+PKG_CHECK_MODULES(MONET_TEST, [cairo glib-2.0 gobject-2.0 gdk-2.0])
+AC_SUBST(MONET_TEST_LIBS)
+AC_SUBST(MONET_TEST_CFLAGS)
+
 AC_OUTPUT([
 Makefile
 monet/Makefile
diff --git a/monet/mn-context.c b/monet/mn-context.c
index d56a4e7..acf47be 100644
--- a/monet/mn-context.c
+++ b/monet/mn-context.c
@@ -6,26 +6,404 @@ G_DEFINE_TYPE (MnContext, mn_context, G_TYPE_OBJECT)
   (G_TYPE_INSTANCE_GET_PRIVATE ((o), MN_TYPE_CONTEXT, MnContextPrivate))
 
 typedef struct _MnContextPrivate MnContextPrivate;
+typedef struct _MnStackItem MnStackItem;
+
+enum {
+  PROP_DISPLAY = 1
+};
+
+struct _MnStackItem {
+  MnStackItem *prev;
+  MnStackItem *next;
+  gboolean props_resolved;
+
+  const gchar *type;
+  GSList *classes;
+  GSList *pseudo_classes;
+
+  const gchar  *prop_type;
+  GSList *prop_classes;
+  GSList *prop_pseudo_classes;
+
+  GHashTable *properties;
+};
 
 struct _MnContextPrivate {
-    int dummy;
+  MnDisplay    *display;
+  MnStackItem  *stack;
+  MnStackItem  *depth;
 };
 
+
+MnDisplay*
+mn_context_get_display (MnContext* context)
+{
+  MnContextPrivate *priv = GET_PRIVATE (context);
+
+  return priv->display;
+}
+
+static void
+mn_g_value_free (GValue *value)
+{
+  g_value_unset (value);
+  g_slice_free (GValue, value);
+}
+
+static void
+mn_context_stack_item_clear (MnContext *context,
+                             MnStackItem* stack_item,
+                             gboolean clear_all)
+{
+  if (stack_item == NULL)
+    return;
+  mn_context_stack_item_clear (context, stack_item->next, clear_all);
+
+  stack_item->props_resolved = FALSE;
+
+  g_slist_free (stack_item->prop_classes);
+  stack_item->prop_classes = NULL;
+  g_slist_free (stack_item->prop_pseudo_classes);
+  stack_item->prop_pseudo_classes = NULL;
+
+  g_hash_table_remove_all (stack_item->properties);
+
+  if (!clear_all)
+    return;
+
+  stack_item->type = NULL;
+  g_slist_free (stack_item->classes);
+  stack_item->classes = NULL;
+  g_slist_free (stack_item->pseudo_classes);
+  stack_item->pseudo_classes = NULL;
+}
+
+static MnStackItem*
+mn_context_stack_item_new ()
+{
+  MnStackItem *stack_item;
+
+  stack_item = g_slice_new0 (MnStackItem);
+
+  stack_item->properties =
+    g_hash_table_new_full (g_direct_hash, g_direct_equal,
+                           NULL, (GDestroyNotify) mn_g_value_free);
+
+  return stack_item;
+}
+
+static void
+mn_context_stack_item_free (MnStackItem *stack_item)
+{
+  if (stack_item == NULL)
+    return;
+
+  mn_context_stack_item_free (stack_item->next);
+  if (stack_item->prev)
+    stack_item->prev->next = NULL;
+
+  g_hash_table_unref (stack_item->properties);
+  g_slist_free (stack_item->classes);
+  g_slist_free (stack_item->pseudo_classes);
+
+  g_slist_free (stack_item->prop_classes);
+  g_slist_free (stack_item->prop_pseudo_classes);
+
+  g_slice_free (MnStackItem, stack_item);
+}
+
 static void
-mn_context_get_property (GObject *object, guint property_id,
+mn_context_ensure_properties (MnContext *context, MnStackItem *stack_item)
+{
+  MnContextPrivate *priv = GET_PRIVATE (context);
+  MnThemeType *theme_type;
+  const GSList *stylesheets;
+  GList  *properties = NULL;
+  GSList *item;
+  MnStylesheet *stylesheet;
+
+  if (stack_item == NULL)
+    return;
+
+  if (stack_item->props_resolved)
+    {
+      gboolean redo = FALSE;
+      GSList *itema, *itemb;
+
+      if (stack_item->prop_type != stack_item->type)
+        redo = TRUE;
+
+      itema = stack_item->classes;
+      itemb = stack_item->prop_classes;
+      while (itema && itemb && (itema->data == itemb->data))
+        {
+          itema = itema->next;
+          itemb = itemb->next;
+        }
+      if (itema || itemb)
+        redo = TRUE;
+
+      itema = stack_item->pseudo_classes;
+      itemb = stack_item->prop_pseudo_classes;
+      while (itema && itemb && (itema->data == itemb->data))
+        {
+          itema = itema->next;
+          itemb = itemb->next;
+        }
+      if (itema || itemb)
+        redo = TRUE;
+      
+      if (redo == FALSE)
+        return;
+    }
+
+  mn_context_stack_item_clear (context, stack_item->next, FALSE);
+  mn_context_ensure_properties (context, stack_item->prev);
+
+  theme_type = mn_display_get_theme_type (priv->display);
+  stylesheets = mn_display_get_stylesheets (priv->display);
+
+  item = (GSList*) stylesheets;
+  while (item)
+    {
+      stylesheet = (MnStylesheet*) item->data;
+
+      properties = _mn_stylesheet_find_properties (stylesheet, context,
+                                                   (gpointer) stack_item,
+                                                   properties);
+
+      item = item->next;
+    }
+  _mn_stylesheet_resolve_properties (stylesheet, context, properties,
+                                     stack_item->properties,
+                                     stack_item->prev ?
+                                       stack_item->prev->properties :
+                                       NULL);
+  _mn_stylesheet_properties_list_free (stylesheet, properties);
+
+  stack_item->props_resolved = TRUE;
+  stack_item->type = stack_item->type;
+  stack_item->prop_classes = g_slist_copy (stack_item->classes);
+  stack_item->prop_pseudo_classes = g_slist_copy (stack_item->pseudo_classes);
+}
+
+void
+mn_context_push (MnContext *context, const gchar *type)
+{
+  MnContextPrivate *priv = GET_PRIVATE (context);
+
+  if (priv->depth == NULL || priv->depth->next == NULL)
+    {
+      MnStackItem *stack_item = mn_context_stack_item_new ();
+
+      stack_item->prev = priv->depth;
+
+      if (priv->depth != NULL)
+        {
+          priv->depth->next = stack_item;
+        }
+      else
+        {
+          priv->stack = stack_item;
+        }
+      priv->depth = stack_item;
+    }
+  else
+    {
+      priv->depth = priv->depth->next;
+    }
+
+  priv->depth->type = g_intern_string (type);
+
+  priv->depth->props_resolved = FALSE;
+  
+  g_slist_free (priv->depth->classes);
+  g_slist_free (priv->depth->pseudo_classes);
+}
+
+void
+mn_context_pop (MnContext *context)
+{
+  MnContextPrivate *priv = GET_PRIVATE (context);
+
+  g_assert (priv->depth != NULL);
+
+  /* FIXME: Do not clear the thing, be a bit smarter. */
+  mn_context_stack_item_clear (context, priv->depth, TRUE);
+  priv->depth = priv->depth->prev;
+}
+
+
+void
+mn_context_clear_classes (MnContext *context)
+{
+  MnContextPrivate *priv = GET_PRIVATE (context);
+
+  g_return_if_fail (priv->depth != NULL);
+
+  g_slist_free (priv->depth->classes);
+  priv->depth->classes = NULL;
+}
+
+void
+mn_context_clear_pseudo_classes (MnContext *context)
+{
+  MnContextPrivate *priv = GET_PRIVATE (context);
+
+  g_return_if_fail (priv->depth != NULL);
+
+  g_slist_free (priv->depth->pseudo_classes);
+  priv->depth->pseudo_classes = NULL;
+}
+
+void
+mn_context_add_class (MnContext   *context,
+                      const gchar *class)
+{
+  MnContextPrivate *priv = GET_PRIVATE (context);
+
+  g_return_if_fail (priv->depth != NULL);
+
+  priv->depth->classes =
+    g_slist_prepend (priv->depth->classes,
+                     (gpointer) g_intern_string (class));
+}
+
+void
+mn_context_add_pseudo_class (MnContext   *context,
+                             const gchar *pseudo_class)
+{
+  MnContextPrivate *priv = GET_PRIVATE (context);
+
+  g_return_if_fail (priv->depth != NULL);
+
+  priv->depth->pseudo_classes =
+    g_slist_prepend (priv->depth->pseudo_classes,
+                     (gpointer) g_intern_string (pseudo_class));
+}
+
+void
+mn_context_remove_class (MnContext   *context,
+                         const gchar *class)
+{
+  MnContextPrivate *priv = GET_PRIVATE (context);
+
+  g_return_if_fail (priv->depth != NULL);
+
+  priv->depth->classes =
+    g_slist_remove (priv->depth->classes,
+                    (gpointer) g_intern_string (class));
+}
+
+void
+mn_context_remove_pseudo_class (MnContext   *context,
+                                const gchar *pseudo_class)
+{
+  MnContextPrivate *priv = GET_PRIVATE (context);
+
+  g_return_if_fail (priv->depth != NULL);
+
+  priv->depth->pseudo_classes =
+    g_slist_remove (priv->depth->pseudo_classes,
+                    (gpointer) g_intern_string (pseudo_class));
+}
+
+void
+mn_context_get_property (MnContext    *context,
+                         const gchar  *property,
+                         GValue       *value)
+{
+  const gchar *name;
+  GValue *prop;
+  MnContextPrivate *priv = GET_PRIVATE (context);
+
+  g_return_if_fail (property != NULL);
+  g_return_if_fail (G_IS_VALUE (value));
+  g_return_if_fail (priv->depth != NULL);
+
+  name = g_intern_string (property);
+  mn_context_ensure_properties (context, priv->depth);
+  prop = g_hash_table_lookup (priv->depth->properties, name);
+
+  if (prop == NULL)
+    return;
+
+  if (G_VALUE_TYPE (value) == G_VALUE_TYPE (prop))
+    g_value_copy (prop, value);
+  else if (g_value_type_transformable (G_VALUE_TYPE (prop), G_VALUE_TYPE (value)))
+    g_value_transform (prop, value);
+  else
+    g_warning ("Cannot retrieve property '%s' of type '%s' as type '%s'.",
+               property,
+               G_VALUE_TYPE_NAME (value),
+               G_VALUE_TYPE_NAME (prop));
+}
+
+void
+mn_context_set_property (MnContext    *context,
+                         const gchar  *property,
+                         GValue       *value)
+{
+  MnContextPrivate *priv = GET_PRIVATE (context);
+
+  g_return_if_fail (property != NULL);
+  g_return_if_fail (value != NULL);
+  g_return_if_fail (priv->depth != NULL);
+
+  /* XXX: FIXME NOT IMPLEMENTED */
+}
+
+void
+_mn_context_obj_get_info (MnContext     *context,
+                          gpointer       obj,
+                          const gchar  **type,
+                          const GSList **classes,
+                          const GSList **pseudo_classes)
+{
+  MnStackItem *stack_item = (MnStackItem*) obj;
+  g_assert (stack_item != NULL);
+
+  *type = stack_item->type;
+  *classes = stack_item->classes;
+  *pseudo_classes = stack_item->pseudo_classes;
+}
+
+gpointer
+_mn_context_obj_get_parent (MnContext* context, gpointer obj)
+{
+  MnStackItem *stack_item = (MnStackItem*) obj;
+  g_assert (stack_item != NULL);
+
+  return stack_item->prev;  
+}
+
+static void
+mn_context_get_gproperty (GObject *object, guint property_id,
                               GValue *value, GParamSpec *pspec)
 {
+  MnContextPrivate *priv = GET_PRIVATE (object);
+
   switch (property_id) {
+  case PROP_DISPLAY:
+    g_value_set_object (value, priv->display);
+    break;
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
   }
 }
 
 static void
-mn_context_set_property (GObject *object, guint property_id,
+mn_context_set_gproperty (GObject *object, guint property_id,
                               const GValue *value, GParamSpec *pspec)
 {
+  MnContextPrivate *priv = GET_PRIVATE (object);
+
   switch (property_id) {
+  case PROP_DISPLAY:
+    if (priv->display != NULL)
+      g_object_unref (G_OBJECT (priv->display));
+    priv->display = g_value_dup_object (value);
+    break;
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
   }
@@ -34,12 +412,21 @@ mn_context_set_property (GObject *object, guint property_id,
 static void
 mn_context_dispose (GObject *object)
 {
+  MnContextPrivate *priv = GET_PRIVATE (object);
+
+  g_object_unref (G_OBJECT (priv->display));
+
   G_OBJECT_CLASS (mn_context_parent_class)->dispose (object);
 }
 
 static void
 mn_context_finalize (GObject *object)
 {
+  MnContextPrivate *priv = GET_PRIVATE (object);
+
+  mn_context_stack_item_free (priv->stack);
+  priv->stack = NULL;
+
   G_OBJECT_CLASS (mn_context_parent_class)->finalize (object);
 }
 
@@ -50,21 +437,35 @@ mn_context_class_init (MnContextClass *klass)
 
   g_type_class_add_private (klass, sizeof (MnContextPrivate));
 
-  object_class->get_property = mn_context_get_property;
-  object_class->set_property = mn_context_set_property;
+  object_class->get_property = mn_context_get_gproperty;
+  object_class->set_property = mn_context_set_gproperty;
   object_class->dispose = mn_context_dispose;
   object_class->finalize = mn_context_finalize;
+
+  g_object_class_install_property(object_class, PROP_DISPLAY,
+                                  g_param_spec_object ("display",
+                                                       "Monet Display",
+                                                       "The display that this monet context is for.",
+                                                       MN_TYPE_DISPLAY,
+                                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 }
 
 static void
 mn_context_init (MnContext *self)
 {
+  MnContextPrivate *priv = GET_PRIVATE (self);
+
+  priv->stack = mn_context_stack_item_new ();  
 }
 
 MnContext*
-mn_context_new (void)
+mn_context_new (MnDisplay *display)
 {
-  return g_object_new (MN_TYPE_CONTEXT, NULL);
+  g_return_val_if_fail (MN_IS_DISPLAY (display), NULL);
+
+  return g_object_new (MN_TYPE_CONTEXT,
+                       "display", display,
+                       NULL);
 }
 
 
diff --git a/monet/mn-context.h b/monet/mn-context.h
index 15125be..1bbbcca 100644
--- a/monet/mn-context.h
+++ b/monet/mn-context.h
@@ -2,6 +2,7 @@
 #define _MN_CONTEXT
 
 #include <glib-object.h>
+typedef struct _MnContext MnContext;
 #include "mn-display.h"
 
 G_BEGIN_DECLS
@@ -23,9 +24,9 @@ G_BEGIN_DECLS
 #define MN_CONTEXT_GET_CLASS(obj) \
   (G_TYPE_INSTANCE_GET_CLASS ((obj), MN_TYPE_CONTEXT, MnContextClass))
 
-typedef struct {
+struct _MnContext {
   GObject parent;
-} MnContext;
+};
 
 typedef struct {
   GObjectClass parent_class;
@@ -33,7 +34,31 @@ typedef struct {
 
 GType mn_context_get_type (void);
 
-MnContext* mn_context_new_for_display (MnDisplay *display);
+MnContext* mn_context_new (MnDisplay *display);
+
+void mn_context_push (MnContext *context, const gchar *type);
+void mn_context_pop (MnContext *context);
+
+void mn_context_clear_classes (MnContext *context);
+void mn_context_clear_pseudo_classes (MnContext *context);
+
+void mn_context_add_class (MnContext *context, const gchar *class);
+void mn_context_add_pseudo_class (MnContext *context, const gchar *pseudo_class);
+
+void mn_context_remove_class (MnContext *context, const gchar *class);
+void mn_context_remove_pseudo_class (MnContext *context, const gchar *pseudo_class);
+
+void mn_context_get_property (MnContext *context, const gchar *property, GValue *value);
+void mn_context_set_property (MnContext *context, const gchar *property, GValue *value);
+
+MnDisplay* mn_context_get_display (MnContext* context);
+
+void _mn_context_obj_get_info (MnContext* context, gpointer obj,
+                               const gchar  **type,
+                               const GSList **classes,
+                               const GSList **pseudo_classes);
+
+gpointer _mn_context_obj_get_parent (MnContext* context, gpointer obj);
 
 G_END_DECLS
 
diff --git a/monet/mn-display.c b/monet/mn-display.c
index d76be90..4282cf1 100644
--- a/monet/mn-display.c
+++ b/monet/mn-display.c
@@ -8,7 +8,7 @@ G_DEFINE_TYPE (MnDisplay, mn_display, G_TYPE_OBJECT)
 typedef struct _MnDisplayPrivate MnDisplayPrivate;
 
 struct _MnDisplayPrivate {
-    gchar *theme_type;
+    MnThemeType *theme_type;
     GSList *stylesheets;
 };
 
@@ -31,7 +31,7 @@ mn_display_new (MnThemeType *theme_type)
                        NULL);
 }
 
-const gchar*
+MnThemeType*
 mn_display_get_theme_type (MnDisplay* display)
 {
   MnDisplayPrivate *priv = GET_PRIVATE (display);
@@ -63,7 +63,7 @@ mn_display_add_stylesheet (MnDisplay *display, MnStylesheet *stylesheet)
 {
   MnDisplayPrivate *priv = GET_PRIVATE (display);
 
-  g_return_if_fail (g_slist_find (priv->stylesheets, stylesheet) != NULL);
+  g_return_if_fail (g_slist_find (priv->stylesheets, stylesheet) == NULL);
 
   g_object_ref_sink (stylesheet);
   g_signal_connect (G_OBJECT (stylesheet), "changed",
@@ -90,6 +90,13 @@ mn_display_remove_stylesheet (MnDisplay *display, MnStylesheet *stylesheet)
   priv->stylesheets = g_slist_delete_link (priv->stylesheets, item);
 }
 
+const GSList*
+mn_display_get_stylesheets (MnDisplay *display)
+{
+  MnDisplayPrivate *priv = GET_PRIVATE (display);
+
+  return priv->stylesheets;
+}
 
 static void
 mn_display_get_property (GObject *object, guint property_id,
diff --git a/monet/mn-display.h b/monet/mn-display.h
index 79f87b5..65f91a8 100644
--- a/monet/mn-display.h
+++ b/monet/mn-display.h
@@ -3,6 +3,7 @@
 
 #include <glib-object.h>
 #include "mn-stylesheet.h"
+#include "mn-theme-type.h"
 
 G_BEGIN_DECLS
 
@@ -35,11 +36,13 @@ GType mn_display_get_type (void);
 
 MnDisplay* mn_display_new (MnThemeType *theme_type);
 
-const gchar* mn_display_get_theme_type (MnDisplay* display);
+MnThemeType* mn_display_get_theme_type (MnDisplay* display);
 
 void mn_display_add_stylesheet    (MnDisplay *display, MnStylesheet *stylesheet);
 void mn_display_remove_stylesheet (MnDisplay *display, MnStylesheet *stylesheet);
 
+const GSList* mn_display_get_stylesheets (MnDisplay *display);
+
 G_END_DECLS
 
 #endif /* _MN_DISPLAY */
diff --git a/monet/mn-stylesheet.c b/monet/mn-stylesheet.c
index 04a90f8..8c909a0 100644
--- a/monet/mn-stylesheet.c
+++ b/monet/mn-stylesheet.c
@@ -6,6 +6,10 @@ G_DEFINE_TYPE (MnStylesheet, mn_stylesheet, G_TYPE_OBJECT)
 #define GET_PRIVATE(o) \
   (G_TYPE_INSTANCE_GET_PRIVATE ((o), MN_TYPE_STYLESHEET, MnStylesheetPrivate))
 
+#define SHIFT_TYPE            0
+#define SHIFT_CLASS           8
+#define SHIFT_PSEUDO_CLASS   16
+#define SHIFT_PRIORITY       24
 
 typedef struct _MnStylesheetPrivate MnStylesheetPrivate;
 typedef struct _MnStyle MnStyle;
@@ -36,6 +40,7 @@ struct _MnProperty {
 };
 
 struct _MnSelector {
+  guint64          specifity;
   MnStyle        *style;
   MnSelectorItem *sel;
 };
@@ -112,6 +117,39 @@ mn_stylesheet_selector_item_free (MnSelectorItem *selitem)
   g_slice_free (MnSelectorItem, selitem);
 }
 
+static void
+mn_stylesheet_selector_calculate_specifity (MnStylesheet *stylesheet, 
+                                            MnSelector   *selector)
+{
+  MnStylesheetPrivate *priv = GET_PRIVATE (stylesheet);
+
+  MnSelectorItem *sel_item;
+  selector->specifity = ((guint64) priv->priority) << SHIFT_PRIORITY;
+  sel_item = selector->sel;
+
+  while (sel_item)
+    {
+      gchar **tmp;
+      if (sel_item->type)
+        selector->specifity += 1 << SHIFT_TYPE;
+
+      tmp = sel_item->classes;
+      while (tmp && *tmp)
+        {
+          selector->specifity += 1 << SHIFT_CLASS;
+          tmp++;
+        }
+
+      tmp = sel_item->pseudo_classes;
+      while (tmp && *tmp)
+        {
+          selector->specifity += 1 << SHIFT_PSEUDO_CLASS;
+          tmp++;
+        }
+      sel_item = sel_item->next;
+    }
+}
+
 static MnSelector*
 mn_stylesheet_selector_new ()
 {
@@ -154,10 +192,13 @@ mn_stylesheet_add_style_with_selectors (MnStylesheet *stylesheet,
     {
       ((MnSelector*)selector->data)->style = style;
 
+      mn_stylesheet_selector_calculate_specifity (stylesheet,
+                                                  ((MnSelector*)selector->data));
       selector = selector->next;
     }
+
   priv->selectors = g_slist_concat (priv->selectors, selectors);
-  priv->styles = g_slist_append (priv->styles, style);
+  priv->styles = g_slist_prepend (priv->styles, style);
 }
 
 /***********************************************************/
@@ -322,7 +363,7 @@ scan_block_to_semicolon (GScanner *scanner)
           g_free (tmp);
           break;
         default:
-          g_print ("Not implemented! %c\n", token);
+          g_scanner_warn (scanner, "Not implemented! %c\n", token);
           break;
         }
 
@@ -354,8 +395,8 @@ mn_stylesheet_parse_selectors (MnStylesheet *stylesheet, GScanner *scanner)
   GTokenType token;
   GSList *selectors = NULL;
   GSList *item;
-  MnSelector *selector;
-  MnSelectorItem *selector_item;
+  MnSelector *selector = NULL;
+  MnSelectorItem *selector_item = NULL;
   gboolean new_selector = TRUE;
   gboolean new_sel_item = TRUE;
   enum {
@@ -386,8 +427,13 @@ mn_stylesheet_parse_selectors (MnStylesheet *stylesheet, GScanner *scanner)
 
           if (new_selector)
             {
+              if (selector_item)
+                selector_item->type = MN_SELECTOR_DIRECT_PARENT;
+
               selector = mn_stylesheet_selector_new ();
               selectors = g_slist_append (selectors, selector);
+              selector_item = NULL;
+              new_sel_item = TRUE;
               new_selector = FALSE;
             }
           if (new_sel_item)
@@ -501,10 +547,14 @@ mn_stylesheet_parse_selectors (MnStylesheet *stylesheet, GScanner *scanner)
       token = g_scanner_peek_next_token (scanner);
     }
 
+  if (selector_item)
+    selector_item->type = MN_SELECTOR_DIRECT_PARENT;
+
   scanner->config->cset_skip_characters = cset_skip_characters;
   scanner->config->numbers_2_int = numbers_2_int;
   scanner->config->scan_symbols = scan_symbols;
   scanner->config->scan_float = scan_float;
+
   return selectors;
 
 BAIL_PARSE_SELECTORS:
@@ -566,7 +616,7 @@ mn_stylesheet_parse_style (MnStylesheet *stylesheet, GScanner *scanner)
           if (token != ':')
             {
               g_free (identifier);
-              g_print ("Expected ':', found something else!\n");
+              g_scanner_warn (scanner, "Expected ':', found something else!\n");
               value = scan_block_to_semicolon (scanner);
               g_free (value);
               continue;
@@ -575,7 +625,7 @@ mn_stylesheet_parse_style (MnStylesheet *stylesheet, GScanner *scanner)
           if (value == NULL)
             {
               g_free (identifier);
-              g_print ("XXX: WARNING\n");
+              g_scanner_warn (scanner, "Error while parsing value. Ignoring the setting.\n");
               continue;
             }
 
@@ -583,7 +633,7 @@ mn_stylesheet_parse_style (MnStylesheet *stylesheet, GScanner *scanner)
             style = mn_stylesheet_style_new ();
 
           property = mn_stylesheet_property_new ();
-          property->identifier = identifier;
+          property->identifier = (gchar*) g_intern_string (identifier);
           property->value = value;
           style->properties = g_slist_append(style->properties, property);
         }
@@ -626,7 +676,10 @@ mn_stylesheet_parse (MnStylesheet *stylesheet)
         {
           mn_stylesheet_selectors_free (selectors);
         }
-      mn_stylesheet_add_style_with_selectors (stylesheet, style, selectors);
+      else
+        {
+          mn_stylesheet_add_style_with_selectors (stylesheet, style, selectors);
+        }
 
       token = g_scanner_peek_next_token (scanner);
     }
@@ -636,6 +689,233 @@ mn_stylesheet_parse (MnStylesheet *stylesheet)
 
 /***********************************************************/
 
+typedef struct {
+  guint64 specifity;
+  const gchar *property;
+  const gchar *value;
+} MnPListItem;
+
+static GList*
+mn_stylesheet_find_properties_for_selectors (GList *plist, GList *selectors)
+{
+  while (selectors)
+    {
+      MnSelector *selector = (MnSelector*) selectors->data;
+      MnStyle *style = selector->style;
+      GSList *prop_li;
+      MnProperty *property;
+      GList *plist_item;
+      MnPListItem *p_item;
+
+      prop_li = style->properties;
+      while (prop_li)
+        {
+          GList *insert_before = NULL;
+          property = (MnProperty*) prop_li->data;
+
+          plist_item = plist;
+          insert_before = NULL;
+          
+          while (plist_item && insert_before == NULL)
+            {
+              /* Search where it would be inserted. */
+              p_item = (MnPListItem*) plist_item->data;
+
+              if (selector->specifity < p_item->specifity)
+                insert_before = plist_item->prev;
+
+              plist_item = plist_item->next;
+            }
+
+          p_item = g_slice_new0 (MnPListItem);
+          p_item->specifity = selector->specifity;
+          p_item->property = property->identifier;
+          p_item->value = property->value;
+
+          plist = g_list_insert_before (plist, insert_before, p_item);
+
+          prop_li = prop_li->next;
+        }
+
+      selectors = selectors->next;
+    }
+
+  return plist;
+}
+
+GList*
+_mn_stylesheet_find_properties (MnStylesheet *stylesheet,
+                                MnContext    *context,
+                                gpointer     *obj,
+                                GList        *plist)
+{
+  MnStylesheetPrivate *priv = GET_PRIVATE (stylesheet);
+  MnSelector *selector;
+  MnSelectorItem *sel_item;
+  const gchar *type;
+  const GSList *classes;
+  const GSList *pseudo_classes;
+  GList *matched_selectors = NULL;
+  GSList *tmp_slist;
+  GList *tmp_s, *tmp_i;
+  GList *selectors = NULL;
+  GList *sel_items = NULL;
+
+  tmp_slist = priv->selectors;
+  while (tmp_slist)
+    {
+      selector = (MnSelector*) tmp_slist->data;
+      g_assert (selector->sel != NULL);
+      selectors = g_list_prepend (selectors, selector);
+      sel_items = g_list_prepend (sel_items, selector->sel);
+      tmp_slist = tmp_slist->next;
+    }
+
+  while (obj)
+    {
+      _mn_context_obj_get_info (context, obj, &type, &classes, &pseudo_classes);
+
+      tmp_s = selectors;
+      tmp_i = sel_items;
+      while (tmp_s)
+        {
+          gboolean match = TRUE;
+          gboolean remove = FALSE;
+          gchar **tmp;
+          selector = (MnSelector*) (tmp_s->data);
+          sel_item = (MnSelectorItem*) (tmp_i->data);
+
+          if ((sel_item->identifier != NULL) &&
+              (sel_item->identifier != type))
+            match = FALSE;
+
+          tmp = sel_item->classes;
+          while (tmp && *tmp && match)
+            {
+              if (g_slist_find ((GSList*) classes, *tmp) == NULL)
+                match = FALSE;
+              tmp++;
+            }
+
+          tmp = sel_item->pseudo_classes;
+          while (tmp && *tmp && match)
+            {
+              if (g_slist_find ((GSList*) pseudo_classes, *tmp) == NULL)
+                match = FALSE;
+              tmp++;
+            }
+
+          if (match == TRUE)
+            {
+              tmp_i->data = sel_item->next;
+              if (sel_item->next == NULL)
+                {
+                  /* Found a match. */
+                  matched_selectors = g_list_prepend (matched_selectors,
+                                                      selector);
+
+                  remove = TRUE;
+                }
+            }
+          else if (match == FALSE && sel_item->type == MN_SELECTOR_DIRECT_PARENT)
+            {
+              /* This selector cannot match anymore. */
+              remove = TRUE;
+            }
+
+          if (!remove)
+            {
+              tmp_s = tmp_s->next;
+              tmp_i = tmp_i->next;
+            }
+          else
+            {
+              GList *tmp;
+
+              tmp = tmp_s->next;
+              selectors = g_list_delete_link (selectors, tmp_s);
+              tmp_s = tmp;
+              tmp = tmp_i->next;
+              sel_items = g_list_delete_link (sel_items, tmp_i);
+              tmp_i = tmp;
+            }
+        }
+
+      obj = _mn_context_obj_get_parent (context, obj);
+    }
+
+  plist = mn_stylesheet_find_properties_for_selectors (plist, matched_selectors);
+
+  g_list_free (sel_items);
+  g_list_free (selectors);
+
+  return plist;
+}
+
+void
+_mn_stylesheet_resolve_properties (MnStylesheet *stylesheet,
+                                   MnContext    *context,
+                                   GList        *plist,
+                                   GHashTable   *properties,
+                                   GHashTable   *parent_properties)
+{
+  MnDisplay *display = mn_context_get_display (context);
+  MnThemeType *theme_type = mn_display_get_theme_type (display);
+  gboolean constant;
+  GValue *value;
+
+  while (plist)
+    {
+      MnPListItem *plist_item = (MnPListItem*) plist->data;
+      MnPropertyInfo *prop_info;
+      GValue *parent_value;
+      constant = TRUE;
+
+      prop_info = mn_theme_type_get_property_info (theme_type,
+                                                   plist_item->property);
+
+      /* Can't resolve this property? Just ignore it! */
+      if (prop_info == NULL)
+        {
+          plist = plist->next;
+          continue;
+        }
+
+      if (parent_properties)
+        parent_value = g_hash_table_lookup (parent_properties, prop_info->priv);
+
+      value = g_slice_new0 (GValue);
+      g_value_init (value, prop_info->property_type);
+      
+      prop_info->parser (prop_info,
+                         plist_item->property,
+                         context,
+                         plist_item->value,
+                         parent_value,
+                         value,
+                         &constant);
+
+      g_hash_table_insert (properties, prop_info->priv, value);
+
+      plist = plist->next;
+    }
+}
+
+void
+_mn_stylesheet_properties_list_free (MnStylesheet *stylesheet,
+                                     GList        *plist)
+{
+  GList *tmp = plist;
+  MnPListItem *plist_item;
+
+  while (tmp)
+    {
+      plist_item = (MnPListItem*) tmp->data;
+      g_slice_free (MnPListItem, plist_item);
+      tmp = tmp->next;
+    }
+  g_list_free (plist);
+}
 
 MnStylesheet*
 mn_stylesheet_new_from_string (const gchar *string, MnPriority priority)
diff --git a/monet/mn-stylesheet.h b/monet/mn-stylesheet.h
index 3f2811e..44171e4 100644
--- a/monet/mn-stylesheet.h
+++ b/monet/mn-stylesheet.h
@@ -3,6 +3,10 @@
 
 #include <glib-object.h>
 
+typedef struct _MnStylesheet MnStylesheet;
+
+#include "mn-context.h"
+
 G_BEGIN_DECLS
 
 #define MN_TYPE_STYLESHEET mn_stylesheet_get_type()
@@ -30,9 +34,9 @@ typedef enum
   MN_PRIORITY_HIGH = 200
 } MnPriority;
 
-typedef struct {
+struct _MnStylesheet {
   GObject parent;
-} MnStylesheet;
+};
 
 typedef struct {
   GObjectClass parent_class;
@@ -57,6 +61,19 @@ GType mn_stylesheet_get_type (void);
 MnStylesheet* mn_stylesheet_new_from_string (const gchar *string,
                                              MnPriority   priority);
 
+
+GList* _mn_stylesheet_find_properties (MnStylesheet *stylesheet,
+                                       MnContext    *context,
+                                       gpointer     *obj,
+                                       GList        *plist);
+void   _mn_stylesheet_resolve_properties (MnStylesheet *stylesheet,
+                                          MnContext    *context,
+                                          GList        *plist,
+                                          GHashTable   *properties,
+                                          GHashTable   *parent_properties);
+void   _mn_stylesheet_properties_list_free (MnStylesheet *stylesheet,
+                                            GList        *plist);
+
 MnPriority mn_stylesheet_get_priority (MnStylesheet *stylesheet);
 
 G_END_DECLS
diff --git a/monet/mn-theme-type.c b/monet/mn-theme-type.c
index 43fd09a..e748e24 100644
--- a/monet/mn-theme-type.c
+++ b/monet/mn-theme-type.c
@@ -44,6 +44,23 @@ mn_theme_type_get_name (MnThemeType* theme_type)
   return priv->name;
 }
 
+MnPropertyInfo*
+mn_theme_type_get_property_info (MnThemeType      *theme_type,
+                                 const gchar      *alias)
+{
+  MnThemeTypePrivate *priv = GET_PRIVATE (theme_type);
+  const gchar *real_alias;
+  MnPropertyInfo *result;
+
+  real_alias = g_intern_string (alias);
+
+  result = g_hash_table_lookup (priv->property_pool, real_alias);
+  if (result == NULL)
+    result = g_hash_table_lookup (priv->alias_pool, real_alias);
+
+  return result;
+}
+
 void
 mn_theme_type_install_property (MnThemeType      *theme_type,
                                 gchar            *property,
@@ -57,6 +74,7 @@ mn_theme_type_install_property (MnThemeType      *theme_type,
   g_return_if_fail (info != NULL);
 
   property = (gchar*) g_intern_string ((const gchar*) property);
+  info->priv = property;
   g_hash_table_insert (priv->property_pool, property, info);
 
   if (aliases == NULL)
diff --git a/monet/mn-theme-type.h b/monet/mn-theme-type.h
index b692561..c58e322 100644
--- a/monet/mn-theme-type.h
+++ b/monet/mn-theme-type.h
@@ -36,16 +36,22 @@ typedef struct _MnPropertyInfo MnPropertyInfo;
 #include "mn-context.h"
 
 /* XXX: Do we need the stylesheet here to open files? */
-typedef gboolean (*MnPropertyParser) (MnPropertyInfo       *info,
-                                      const gchar          *alias,
-                                      MnContext            *context,
-                                      const GString        *rc_string,
-                                      GValue               *property_value);
+typedef void (*MnPropertyParser) (MnPropertyInfo       *info,
+                                  const gchar          *alias,
+                                  MnContext            *context,
+                                  const gchar          *rc_string,
+                                  GValue               *parent_value,
+                                  GValue               *property_value,
+                                  gboolean             *constant);
 
 struct _MnPropertyInfo {
+  /* < public > */
   GType             property_type;
   MnPropertyParser  parser;
   GDestroyNotify    free_func;
+
+  /* < private > */
+  void*             priv;
 };
 
 GType mn_theme_type_get_type (void);
@@ -53,6 +59,8 @@ GType mn_theme_type_get_type (void);
 MnThemeType* mn_theme_type_find (gchar *name);
 MnThemeType* mn_theme_type_new (gchar *name);
 
+MnPropertyInfo* mn_theme_type_get_property_info (MnThemeType      *theme_type,
+                                                 const gchar      *alias);
 void mn_theme_type_install_property (MnThemeType      *theme_type,
                                      gchar            *property,
                                      GStrv             aliases,
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 4032492..fa335a9 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,9 +1,11 @@
-AM_CFLAGS = $(MONET_CFLAGS)
+AM_CFLAGS = $(MONET_TEST_CFLAGS)
 INCLUDES = -I$(top_srcdir)
-LDADD = $(MONET_LIBS) $(top_builddir)/monet/libmonet.la
+LDADD = $(MONET_TEST_LIBS) $(top_builddir)/monet/libmonet.la
 
-noinst_PROGRAMS = test-drawing
+noinst_PROGRAMS = test-drawing test
 
 test_drawing_SOURCES = test-drawing.c
 
+test_SOURCES = test.c
+
 -include $(top_srcdir)/git.mk
diff --git a/tests/test.c b/tests/test.c
new file mode 100644
index 0000000..2442362
--- /dev/null
+++ b/tests/test.c
@@ -0,0 +1,156 @@
+#include <monet/monet.h>
+#include <gdk/gdk.h>
+
+GType color_type;
+
+typedef struct {
+  gdouble r;
+  gdouble g;
+  gdouble b;
+  gdouble a;
+} TestColor;
+
+TestColor*
+color_copy (TestColor *src)
+{
+  TestColor *dest = g_slice_new (TestColor);
+  dest->r = src->r;
+  dest->g = src->g;
+  dest->b = src->b;
+  dest->a = src->a;
+
+  return dest;
+}
+
+void
+color_free (TestColor *color)
+{
+  g_slice_free (TestColor, color);
+}
+
+void
+color_parser (MnPropertyInfo       *info,
+              const gchar          *alias,
+              MnContext            *context,
+              const gchar          *rc_string,
+              GValue               *parent_value,
+              GValue               *property_value,
+              gboolean             *constant)
+{
+  GdkColor gdk_color;
+  gboolean success;
+  TestColor color;
+
+  success = gdk_color_parse (rc_string, &gdk_color);
+  if (success)
+    {
+      color.r = (gdouble) gdk_color.red / 65535.0;
+      color.g = (gdouble) gdk_color.green / 65535.0;
+      color.b = (gdouble) gdk_color.blue / 65535.0;
+      color.a = 1.0;
+
+      g_value_set_boxed (property_value, &color);
+    }
+  else if (g_str_has_prefix (rc_string, "inherit"))
+    {
+      if (parent_value)
+        {
+          g_assert (G_VALUE_TYPE (parent_value) == G_VALUE_TYPE (property_value));
+          g_print ("inheriting!\n");
+          g_value_copy (parent_value, property_value);
+        }
+    }
+}
+
+int
+main(int argc, char **argv)
+{
+  MnThemeType *theme_type;
+  MnContext *context;
+  MnDisplay *display;
+  MnStylesheet *stylesheet;
+  MnPropertyInfo *color_prop = g_malloc (sizeof(MnPropertyInfo));
+  GValue value = {0};
+  TestColor *color;
+
+  gdk_init (&argc, &argv);
+  monet_init (&argc, &argv);
+
+  color_type = g_boxed_type_register_static (g_intern_static_string ("TestColor"),
+                                             (GBoxedCopyFunc)color_copy,
+                                             (GBoxedFreeFunc)color_free);
+
+  theme_type = mn_theme_type_new ("widget-test");
+
+  color_prop->property_type = color_type;
+  color_prop->free_func = g_free;
+  color_prop->parser = color_parser;
+
+  mn_theme_type_install_property (theme_type, "color", NULL, color_prop);
+
+  display = mn_display_new (theme_type);
+
+  stylesheet = mn_stylesheet_new_from_string (
+  "window > button { \n"
+  "  color: gray; \n"
+  "} \n"
+  "button.urgent { \n"
+  "  color: blue; \n"
+  "} \n"
+  ":prelight { \n"
+  "  color: red; \n"
+  "} \n"
+  "window { \n"
+  "  color: green; \n"
+  "  background: black; \n"
+  "}\n",
+  MN_PRIORITY_DEFAULT);
+
+  mn_display_add_stylesheet (display, stylesheet);
+
+  context = mn_context_new (display);
+  
+  mn_context_push (context, "window");
+  g_value_init (&value, color_type);
+  mn_context_get_property (context, "color", &value);
+  color = g_value_get_boxed (&value);
+  if (color)
+    g_print ("window: %f, %f, %f, %f\n", (float)color->r, (float)color->g, (float)color->b, (float)color->a);
+
+  mn_context_push (context, "button");
+
+  g_value_reset (&value);
+  mn_context_get_property (context, "color", &value);
+  color = g_value_get_boxed (&value);
+  if (color)
+    g_print ("window > button: %f, %f, %f, %f\n", (float)color->r, (float)color->g, (float)color->b, (float)color->a);
+
+  mn_context_add_class (context, "urgent");
+  /*mn_context_add_pseudo_class (context, "active");
+  mn_context_add_pseudo_class (context, "prelight");
+  mn_context_remove_pseudo_class (context, "active");
+  mn_context_clear_pseudo_classes (context);
+  mn_context_add_pseudo_class (context, "insensitive");*/
+
+  g_value_reset (&value);
+  mn_context_get_property (context, "color", &value);
+  color = g_value_get_boxed (&value);
+  if (color)
+    g_print ("window > button.urgent: %f, %f, %f, %f\n", (float)color->r, (float)color->g, (float)color->b, (float)color->a);
+
+  mn_context_add_pseudo_class (context, "prelight");
+  g_value_reset (&value);
+  mn_context_get_property (context, "color", &value);
+  color = g_value_get_boxed (&value);
+  if (color)
+    g_print ("window > button.urgent:prelight: %f, %f, %f, %f\n", (float)color->r, (float)color->g, (float)color->b, (float)color->a);
+
+  mn_context_pop (context);
+  mn_context_pop (context);
+
+  g_object_unref (context);
+  g_object_unref (stylesheet);
+  g_object_unref (display);
+  
+  return 0;
+}



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