gnome-shell r42 - in trunk/src: . tidy



Author: otaylor
Date: Wed Nov 12 21:09:27 2008
New Revision: 42
URL: http://svn.gnome.org/viewvc/gnome-shell?rev=42&view=rev

Log:
Experimentally add build infrastructure and a few Tidy widgets

For experimenting with using tidy, import TidyButton and TidyGrid
(+ dependencies) into our source tree and set up build machinery
to build them and build a typelib for them.

The sources are build right into libgnome-shell.so, so the Shell.gir
and Tidy.gir actually point to the same shared library.

src/Makefile-tidy.am: Build libtidy-1.0.la
src/Makefile.am: Include built tidy into gnome-shell.la and
  build Tidy-1.0.typelib

src/tidy/*: Add some source files from Tidy


Added:
   trunk/src/Makefile-tidy.am
   trunk/src/tidy/
   trunk/src/tidy/tidy-actor.c
   trunk/src/tidy/tidy-actor.h
   trunk/src/tidy/tidy-button.c
   trunk/src/tidy/tidy-button.h
   trunk/src/tidy/tidy-debug.h
   trunk/src/tidy/tidy-enum-types.c.in
   trunk/src/tidy/tidy-enum-types.h.in
   trunk/src/tidy/tidy-frame.c
   trunk/src/tidy/tidy-frame.h
   trunk/src/tidy/tidy-grid.c
   trunk/src/tidy/tidy-grid.h
   trunk/src/tidy/tidy-marshal.list
   trunk/src/tidy/tidy-private.h
   trunk/src/tidy/tidy-stylable.c
   trunk/src/tidy/tidy-stylable.h
   trunk/src/tidy/tidy-style.c
   trunk/src/tidy/tidy-style.h
   trunk/src/tidy/tidy-types.h
   trunk/src/tidy/tidy-util.c
   trunk/src/tidy/tidy-util.h
Modified:
   trunk/src/   (props changed)
   trunk/src/Makefile.am

Added: trunk/src/Makefile-tidy.am
==============================================================================
--- (empty file)
+++ trunk/src/Makefile-tidy.am	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,109 @@
+NULL =
+
+GLIB_GENMARSHAL = `pkg-config --variable=glib_genmarshal glib-2.0`
+GLIB_MKENUMS = `pkg-config --variable=glib_mkenums glib-2.0`
+
+tidy_cflags =					\
+	-I$(top_srcdir)/src			\
+	-DPREFIX=\""$(prefix)"\"		\
+	-DLIBDIR=\""$(libdir)"\"		\
+	-DG_DISABLE_DEPRECATED			\
+	-DG_LOG_DOMAIN=\"Tidy\"			\
+	$(MUTTER_PLUGIN_CFLAGS)			\
+	$(NULL)
+
+tidy_built_sources = \
+	tidy-enum-types.h 	\
+	tidy-enum-types.c 	\
+	tidy-marshal.h 		\
+	tidy-marshal.c
+
+BUILT_SOURCES += $(tidy_built_sources)
+
+STAMP_FILES = stamp-tidy-marshal.h stamp-tidy-enum-types.h
+
+# please, keep this sorted alphabetically
+tidy_source_h =					\
+	tidy/tidy-actor.h			\
+	tidy/tidy-button.h			\
+	tidy/tidy-frame.h			\
+	tidy/tidy-grid.h			\
+	tidy/tidy-stylable.h			\
+	tidy/tidy-style.h			\
+	tidy/tidy-types.h			\
+	tidy/tidy-util.h			\
+	$(NULL)
+
+tidy_source_h_private = \
+	tidy/tidy-debug.h \
+	$(NULL)
+
+# please, keep this sorted alphabetically
+tidy_source_c =					\
+	tidy/tidy-actor.c			\
+	tidy/tidy-button.c			\
+	tidy/tidy-frame.c			\
+	tidy/tidy-grid.c			\
+	tidy/tidy-stylable.c			\
+	tidy/tidy-style.c			\
+	tidy/tidy-util.c			\
+	$(NULL)
+
+tidy-marshal.h: stamp-tidy-marshal.h
+	@true
+stamp-tidy-marshal.h: Makefile tidy/tidy-marshal.list
+	$(GLIB_GENMARSHAL) \
+		--prefix=_tidy_marshal \
+		--header \
+	$(srcdir)/tidy/tidy-marshal.list > xgen-tmh && \
+	(cmp -s xgen-tmh tidy-marshal.h || cp -f xgen-tmh tidy-marshal.h) && \
+	rm -f xgen-tmh && \
+	echo timestamp > $(@F)
+
+tidy-marshal.c: Makefile tidy/tidy-marshal.list
+	(echo "#include \"tidy-marshal.h\"" ; \
+	 $(GLIB_GENMARSHAL) \
+		--prefix=_tidy_marshal \
+		--body \
+	 $(srcdir)/tidy/tidy-marshal.list ) > xgen-tmc && \
+	cp -f xgen-tmc tidy-marshal.c && \
+	rm -f xgen-tmc
+
+tidy-enum-types.h: stamp-tidy-enum-types.h Makefile
+	@true
+stamp-tidy-enum-types.h: $(tidy_source_h) tidy/tidy-enum-types.h.in
+	( cd $(srcdir) && \
+	  $(GLIB_MKENUMS) \
+	    --template $(srcdir)/tidy/tidy-enum-types.h.in \
+	  $(tidy_source_h) ) >> xgen-teth && \
+	(cmp xgen-teth tidy-enum-types.h || cp xgen-teth tidy-enum-types.h) && \
+	rm -f xgen-teth && \
+	echo timestamp > $(@F)
+
+tidy-enum-types.c: stamp-tidy-enum-types.h tidy/tidy-enum-types.c.in
+	( cd $(srcdir) && \
+	  $(GLIB_MKENUMS) \
+	    --template $(srcdir)/tidy/tidy-enum-types.c.in \
+	  $(tidy_source_h) ) >> xgen-tetc && \
+	cp xgen-tetc tidy-enum-types.c && \
+	rm -f xgen-tetc
+
+lib_LTLIBRARIES = libtidy-1.0.la
+
+libtidy_1_0_la_LIBADD = $(TIDY_LIBS)
+libtidy_1_0_la_SOURCES = \
+	$(tidy_source_c) \
+	$(tidy_source_h) \
+	$(tidy_source_h_priv) \
+	$(tidy_built_sources) \
+	$(NULL)
+libtidy_1_0_la_CPPFLAGS = $(tidy_cflags)
+libtidy_1_0_la_LDFLAGS = $(LDADD)
+
+CLEANFILES += $(STAMP_FILES) $(BUILT_SOURCES)
+
+EXTRA_DIST =					\
+	tidy/tidy-enum-types.h.in		\
+	tidy/tidy-enum-types.c.in		\
+	tidy/tidy-private.h			\
+	tidy/tidy-marshal.list

Modified: trunk/src/Makefile.am
==============================================================================
--- trunk/src/Makefile.am	(original)
+++ trunk/src/Makefile.am	Wed Nov 12 21:09:27 2008
@@ -1,4 +1,9 @@
-INCLUDES =					\
+BUILT_SOURCES =
+CLEANFILES =
+
+include Makefile-tidy.am
+
+gnome_shell_cflags =				\
 	$(MUTTER_PLUGIN_CFLAGS)			\
 	-DGETTEXT_PACKAGE=gnome-shell		\
 	-DJSDIR=\"$(pkgdatadir)/js\"
@@ -14,7 +19,8 @@
 	shell-global.h
 
 libgnome_shell_la_LDFLAGS = -avoid-version -module
-libgnome_shell_la_LIBADD = $(MUTTER_PLUGIN_LIBS)
+libgnome_shell_la_LIBADD = $(MUTTER_PLUGIN_LIBS) libtidy-1.0.la
+libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
 
 # We can't have any undefined symbols when g-ir-scanner dlopens the library
 # to introspect it, so we link everything a _second_ time, including a
@@ -27,14 +33,15 @@
 # The dummy -rpath here is needed to convince libtool to build a
 # noinst_LTLIBRARY shared
 libgnome_shell_introspect_la_LDFLAGS = -avoid-version -module -rpath $(libdir)
-libgnome_shell_introspect_la_LIBADD = $(MUTTER_PLUGIN_LIBS)
+libgnome_shell_introspect_la_LIBADD = $(MUTTER_PLUGIN_LIBS) libtidy-1.0.la
+libgnome_shell_introspect_la_CPPFLAGS = $(gnome_shell_cflags)
 
 typelibdir = $(pkglibdir)/girepository
-typelib_DATA = Shell-0.1.typelib
+typelib_DATA = Shell-0.1.typelib Tidy-1.0.typelib
 
 # After we run g-ir-scanner, we need to change the library name written in
 # the .gir file from the "fake" second copy of the library to the real name
-Shell-0.1.gir: libgnome-shell-introspect.la $(libgnome_shell_la_SOURCES)
+Shell-0.1.gir: libgnome-shell-introspect.la $(libgnome_shell_la_SOURCES) Makefile
 	g-ir-scanner					\
 		--namespace=Shell			\
 		--nsversion=0.1				\
@@ -42,8 +49,8 @@
 		--include=Clutter-0.8			\
 		--include=Meta-2.25			\
 		--library=gnome-shell-introspect	\
-		$(libgnome_shell_la_SOURCES)		\
-		$(INCLUDES)				\
+-		$(libgnome_shell_la_SOURCES)		\
+		$(libgnome_shell_la_CPPFLAGS)		\
 		-o $  tmp
 	sed 's/gnome-shell-introspect/gnome-shell/' < $  tmp > $@ && rm $  tmp
 
@@ -51,3 +58,23 @@
 # (not the fake library, since we've already done the rewriting)
 Shell-0.1.typelib: libgnome-shell.la Shell-0.1.gir
 	LD_LIBRARY_PATH=$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}. g-ir-compiler Shell-0.1.gir -o $@
+
+# After we run g-ir-scanner, we need to change the library name written in
+# the .gir file from the "fake" second copy of the library to the real name
+Tidy-1.0.gir: libgnome-shell-introspect.la $(libgnome_shell_la_SOURCES) Makefile
+	g-ir-scanner					\
+		--namespace=Tidy			\
+		--nsversion=1.0				\
+		--include=GObject-2.0			\
+		--include=Clutter-0.8			\
+		--library=gnome-shell-introspect	\
+		$(tidy_source_h)			\
+		$(tidy_source_c)			\
+		$(tidy_cflags)				\
+		-o $  tmp
+	sed 's/gnome-shell-introspect/gnome-shell/' < $  tmp > $@ && rm $  tmp
+
+# The dependency on libgnome-shell.la here is because g-ir-compiler opens it
+# (not the fake library, since we've already done the rewriting)
+Tidy-1.0.typelib: libgnome-shell.la Tidy-1.0.gir
+	LD_LIBRARY_PATH=$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}. g-ir-compiler Tidy-1.0.gir -o $@

Added: trunk/src/tidy/tidy-actor.c
==============================================================================
--- (empty file)
+++ trunk/src/tidy/tidy-actor.c	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,529 @@
+/* tidy-actor.c: Base class for Tidy actors
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:tidy-actor
+ * @short_description: Base class for stylable actors
+ *
+ * #TidyActor is a simple abstract class on top of #ClutterActor. It
+ * provides basic themeing properties, support for padding and alignment.
+ *
+ * Actors in the Tidy library should subclass #TidyActor if they plan
+ * to obey to a certain #TidyStyle or if they implement #ClutterContainer
+ * and want to offer basic layout capabilities.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "tidy-actor.h"
+
+#include "tidy-debug.h"
+#include "tidy-marshal.h"
+#include "tidy-private.h"
+#include "tidy-stylable.h"
+
+enum
+{
+  PROP_0,
+
+  PROP_STYLE,
+  PROP_PADDING,
+  PROP_X_ALIGN,
+  PROP_Y_ALIGN
+};
+
+enum
+{
+  LAST_SIGNAL
+};
+
+static void tidy_stylable_iface_init (TidyStylableIface *iface);
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (TidyActor, tidy_actor, CLUTTER_TYPE_ACTOR,
+                                  G_IMPLEMENT_INTERFACE (TIDY_TYPE_STYLABLE,
+                                                         tidy_stylable_iface_init));
+
+#define TIDY_ACTOR_GET_PRIVATE(obj) \
+        (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TIDY_TYPE_ACTOR, TidyActorPrivate))
+
+struct _TidyActorPrivate
+{
+  TidyStyle *style;
+
+  TidyPadding padding;
+
+  ClutterFixed x_align;
+  ClutterFixed y_align;
+};
+
+static void
+tidy_actor_set_property (GObject      *gobject,
+                         guint         prop_id,
+                         const GValue *value,
+                         GParamSpec   *pspec)
+{
+  TidyActor *actor = TIDY_ACTOR (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_PADDING:
+      tidy_actor_set_padding (actor, g_value_get_boxed (value));
+      break;
+
+    case PROP_X_ALIGN:
+      actor->priv->x_align =
+        CLUTTER_FIXED_TO_FLOAT (g_value_get_double (value));
+      break;
+
+    case PROP_Y_ALIGN:
+      actor->priv->y_align =
+        CLUTTER_FIXED_TO_FLOAT (g_value_get_double (value));
+      break;
+
+    case PROP_STYLE:
+      tidy_stylable_set_style (TIDY_STYLABLE (actor),
+                               g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+tidy_actor_get_property (GObject    *gobject,
+                         guint       prop_id,
+                         GValue     *value,
+                         GParamSpec *pspec)
+{
+  TidyActor *actor = TIDY_ACTOR (gobject);
+  TidyActorPrivate *priv = actor->priv;
+
+  switch (prop_id)
+    {
+    case PROP_PADDING:
+      {
+        TidyPadding padding = { 0, };
+
+        tidy_actor_get_padding (actor, &padding);
+        g_value_set_boxed (value, &padding);
+      }
+      break;
+
+    case PROP_X_ALIGN:
+      g_value_set_double (value, CLUTTER_FIXED_TO_FLOAT (priv->x_align));
+      break;
+
+    case PROP_Y_ALIGN:
+      g_value_set_double (value, CLUTTER_FIXED_TO_FLOAT (priv->y_align));
+      break;
+
+    case PROP_STYLE:
+      g_value_set_object (value, priv->style);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+tidy_actor_dispose (GObject *gobject)
+{
+  TidyActor *actor = TIDY_ACTOR (gobject);
+
+  if (actor->priv->style)
+    {
+      g_object_unref (actor->priv->style);
+      actor->priv->style = NULL;
+    }
+
+  G_OBJECT_CLASS (tidy_actor_parent_class)->dispose (gobject);
+}
+
+static void
+tidy_actor_class_init (TidyActorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (TidyActorPrivate));
+
+  gobject_class->set_property = tidy_actor_set_property;
+  gobject_class->get_property = tidy_actor_get_property;
+  gobject_class->dispose = tidy_actor_dispose;
+
+  /**
+   * TidyActor:padding:
+   *
+   * Padding around an actor, expressed in #ClutterUnit<!-- -->s. Padding
+   * is the internal space between an actors bounding box and its internal
+   * children.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_PADDING,
+                                   g_param_spec_boxed ("padding",
+                                                       "Padding",
+                                                       "Units of padding around an actor",
+                                                       TIDY_TYPE_PADDING,
+                                                       TIDY_PARAM_READWRITE));
+  /**
+   * TidyActor:x-align:
+   *
+   * Alignment of internal children along the X axis, relative to the
+   * actor's bounding box origin, and in relative units (1.0 is the
+   * current width of the actor).
+   *
+   * A value of 0.0 will left-align the children; 0.5 will align them at
+   * the middle of the actor's width; 1.0 will right align the children.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_X_ALIGN,
+                                   g_param_spec_double ("x-align",
+                                                        "X Alignment",
+                                                        "Alignment (between 0.0 and 1.0) on the X axis",
+                                                        0.0, 1.0, 0.5,
+                                                        TIDY_PARAM_READWRITE));
+  /**
+   * TidyActor:y-align:
+   *
+   * Alignment of internal children along the Y axis, relative to the
+   * actor's bounding box origin, and in relative units (1.0 is the
+   * current height of the actor).
+   *
+   * A value of 0.0 will top-align the children; 0.5 will align them at
+   * the middle of the actor's height; 1.0 will bottom align the children.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_Y_ALIGN,
+                                   g_param_spec_double ("y-align",
+                                                        "Y Alignement",
+                                                        "Alignment (between 0.0 and 1.0) on the Y axis",
+                                                        0.0, 1.0, 0.5,
+                                                        TIDY_PARAM_READWRITE));
+  g_object_class_override_property (gobject_class, PROP_STYLE, "style");
+}
+
+static TidyStyle *
+tidy_actor_get_style (TidyStylable *stylable)
+{
+  TidyActorPrivate *priv = TIDY_ACTOR (stylable)->priv;
+
+  if (!priv->style)
+    priv->style = g_object_ref (tidy_style_get_default ());
+
+  return priv->style;
+}
+
+static void
+tidy_actor_set_style (TidyStylable *stylable,
+                      TidyStyle    *style)
+{
+  TidyActorPrivate *priv = TIDY_ACTOR (stylable)->priv;
+
+  if (priv->style)
+    g_object_unref (priv->style);
+
+  priv->style = g_object_ref_sink (style);
+}
+
+static void
+tidy_stylable_iface_init (TidyStylableIface *iface)
+{
+  static gboolean is_initialized = FALSE;
+
+  if (!is_initialized)
+    {
+      GParamSpec *pspec;
+
+      pspec = g_param_spec_string ("font-name",
+                                   "Font Name",
+                                   "The font to use for displaying text",
+                                   "Sans 12px",
+                                   G_PARAM_READWRITE);
+      tidy_stylable_iface_install_property (iface, TIDY_TYPE_ACTOR, pspec);
+
+      pspec = g_param_spec_boxed ("bg-color",
+                                  "Background Color",
+                                  "The background color of an actor",
+                                  CLUTTER_TYPE_COLOR,
+                                  G_PARAM_READWRITE);
+      tidy_stylable_iface_install_property (iface, TIDY_TYPE_ACTOR, pspec);
+
+      pspec = g_param_spec_boxed ("active-color",
+                                  "Active Color",
+                                  "The color of an active actor",
+                                  CLUTTER_TYPE_COLOR,
+                                  G_PARAM_READWRITE);
+      tidy_stylable_iface_install_property (iface, TIDY_TYPE_ACTOR, pspec);
+
+      pspec = g_param_spec_boxed ("text-color",
+                                  "Text Color",
+                                  "The color of the text of an actor",
+                                  CLUTTER_TYPE_COLOR,
+                                  G_PARAM_READWRITE);
+      tidy_stylable_iface_install_property (iface, TIDY_TYPE_ACTOR, pspec);
+
+      iface->get_style = tidy_actor_get_style;
+      iface->set_style = tidy_actor_set_style;
+    }
+}
+
+static void
+tidy_actor_init (TidyActor *actor)
+{
+  TidyActorPrivate *priv;
+
+  actor->priv = priv = TIDY_ACTOR_GET_PRIVATE (actor);
+
+  /* no padding */
+  priv->padding.top = priv->padding.bottom = 0;
+  priv->padding.right = priv->padding.left = 0;
+
+  /* middle align */
+  priv->x_align = priv->y_align = CLUTTER_FLOAT_TO_FIXED (0.5);
+
+  clutter_actor_set_reactive (CLUTTER_ACTOR (actor), TRUE);
+}
+
+/**
+ * tidy_actor_set_padding:
+ * @actor: a #TidyActor
+ * @padding: padding for internal children
+ *
+ * Sets @padding around @actor.
+ */
+void
+tidy_actor_set_padding (TidyActor         *actor,
+                        const TidyPadding *padding)
+{
+  g_return_if_fail (TIDY_IS_ACTOR (actor));
+  g_return_if_fail (padding != NULL);
+
+  actor->priv->padding = *padding;
+
+  g_object_notify (G_OBJECT (actor), "padding");
+
+  if (CLUTTER_ACTOR_IS_VISIBLE (actor))
+    clutter_actor_queue_redraw (CLUTTER_ACTOR (actor));
+}
+
+/**
+ * tidy_actor_get_padding:
+ * @actor: a #TidyActor
+ * @padding: return location for the padding
+ *
+ * Retrieves the padding aound @actor.
+ */
+void
+tidy_actor_get_padding (TidyActor   *actor,
+                        TidyPadding *padding)
+{
+  g_return_if_fail (TIDY_IS_ACTOR (actor));
+  g_return_if_fail (padding != NULL);
+
+  *padding = actor->priv->padding;
+}
+
+/**
+ * tidy_actor_set_alignment:
+ * @actor: a #TidyActor
+ * @x_align: relative alignment on the X axis
+ * @y_align: relative alignment on the Y axis
+ *
+ * Sets the alignment, relative to the @actor's width and height, of
+ * the internal children.
+ */
+void
+tidy_actor_set_alignment (TidyActor *actor,
+                          gdouble    x_align,
+                          gdouble    y_align)
+{
+  TidyActorPrivate *priv;
+
+  g_return_if_fail (TIDY_IS_ACTOR (actor));
+
+  g_object_ref (actor);
+  g_object_freeze_notify (G_OBJECT (actor));
+
+  priv = actor->priv;
+
+  x_align = CLAMP (x_align, 0.0, 1.0);
+  y_align = CLAMP (y_align, 0.0, 1.0);
+
+  priv->x_align = CLUTTER_FLOAT_TO_FIXED (x_align);
+  g_object_notify (G_OBJECT (actor), "x-align");
+  
+  priv->y_align = CLUTTER_FLOAT_TO_FIXED (y_align);
+  g_object_notify (G_OBJECT (actor), "y-align");
+
+  if (CLUTTER_ACTOR_IS_VISIBLE (actor))
+    clutter_actor_queue_redraw (CLUTTER_ACTOR (actor));
+
+  g_object_thaw_notify (G_OBJECT (actor));
+  g_object_unref (actor);
+}
+
+/**
+ * tidy_actor_get_alignment:
+ * @actor: a #TidyActor
+ * @x_align: return location for the relative alignment on the X axis,
+ *   or %NULL
+ * @y_align: return location for the relative alignment on the Y axis,
+ *   or %NULL
+ *
+ * Retrieves the alignment, relative to the @actor's width and height, of
+ * the internal children.
+ */
+void
+tidy_actor_get_alignment (TidyActor *actor,
+                          gdouble   *x_align,
+                          gdouble   *y_align)
+{
+  TidyActorPrivate *priv;
+
+  g_return_if_fail (TIDY_IS_ACTOR (actor));
+
+  priv = actor->priv;
+
+  if (x_align)
+    *x_align = CLUTTER_FIXED_TO_FLOAT (priv->x_align);
+
+  if (y_align)
+    *y_align = CLUTTER_FIXED_TO_FLOAT (priv->y_align);
+}
+
+/**
+ * tidy_actor_set_alignmentx:
+ * @actor: a #TidyActor
+ * @x_align: relative alignment on the X axis
+ * @y_align: relative alignment on the Y axis
+ *
+ * Fixed point version of tidy_actor_set_alignment().
+ *
+ * Sets the alignment, relative to the @actor's width and height, of
+ * the internal children.
+ */
+void
+tidy_actor_set_alignmentx (TidyActor    *actor,
+                           ClutterFixed  x_align,
+                           ClutterFixed  y_align)
+{
+  TidyActorPrivate *priv;
+
+  g_return_if_fail (TIDY_IS_ACTOR (actor));
+
+  g_object_ref (actor);
+  g_object_freeze_notify (G_OBJECT (actor));
+
+  priv = actor->priv;
+
+  x_align = CLAMP (x_align, 0, CFX_ONE);
+  y_align = CLAMP (y_align, 0, CFX_ONE);
+
+  if (priv->x_align != x_align)
+    {
+      priv->x_align = x_align;
+      g_object_notify (G_OBJECT (actor), "x-align");
+    }
+
+  if (priv->y_align != y_align)
+    {
+      priv->y_align = y_align;
+      g_object_notify (G_OBJECT (actor), "y-align");
+    }
+
+  if (CLUTTER_ACTOR_IS_VISIBLE (actor))
+    clutter_actor_queue_redraw (CLUTTER_ACTOR (actor));
+
+  g_object_thaw_notify (G_OBJECT (actor));
+  g_object_unref (actor);
+}
+
+/**
+ * tidy_actor_get_alignmentx:
+ * @actor: a #TidyActor
+ * @x_align: return location for the relative alignment on the X axis,
+ *   or %NULL
+ * @y_align: return location for the relative alignment on the Y axis,
+ *   or %NULL
+ *
+ * Fixed point version of tidy_actor_get_alignment().
+ *
+ * Retrieves the alignment, relative to the @actor's width and height, of
+ * the internal children.
+ */
+void
+tidy_actor_get_alignmentx (TidyActor    *actor,
+                           ClutterFixed *x_align,
+                           ClutterFixed *y_align)
+{
+  TidyActorPrivate *priv;
+
+  g_return_if_fail (TIDY_IS_ACTOR (actor));
+
+  priv = actor->priv;
+
+  if (x_align)
+    *x_align = priv->x_align;
+
+  if (y_align)
+    *y_align = priv->y_align;
+}
+
+static TidyPadding *
+tidy_padding_copy (const TidyPadding *padding)
+{
+  TidyPadding *copy;
+
+  g_return_val_if_fail (padding != NULL, NULL);
+
+  copy = g_slice_new (TidyPadding);
+  *copy = *padding;
+
+  return copy;
+}
+
+static void
+tidy_padding_free (TidyPadding *padding)
+{
+  if (G_LIKELY (padding))
+    g_slice_free (TidyPadding, padding);
+}
+
+GType
+tidy_padding_get_type (void)
+{
+  static GType our_type = 0;
+
+  if (G_UNLIKELY (our_type == 0))
+    our_type =
+      g_boxed_type_register_static (I_("TidyPadding"),
+                                    (GBoxedCopyFunc) tidy_padding_copy,
+                                    (GBoxedFreeFunc) tidy_padding_free);
+
+  return our_type;
+}

Added: trunk/src/tidy/tidy-actor.h
==============================================================================
--- (empty file)
+++ trunk/src/tidy/tidy-actor.h	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,88 @@
+/* tidy-actor.h: Base class for Tidy actors
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __TIDY_ACTOR_H__
+#define __TIDY_ACTOR_H__
+
+#include <clutter/clutter-actor.h>
+#include <tidy/tidy-types.h>
+
+G_BEGIN_DECLS
+
+#define TIDY_TYPE_ACTOR                 (tidy_actor_get_type ())
+#define TIDY_ACTOR(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIDY_TYPE_ACTOR, TidyActor))
+#define TIDY_IS_ACTOR(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIDY_TYPE_ACTOR))
+#define TIDY_ACTOR_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), TIDY_TYPE_ACTOR, TidyActorClass))
+#define TIDY_IS_ACTOR_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), TIDY_TYPE_ACTOR))
+#define TIDY_ACTOR_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), TIDY_TYPE_ACTOR, TidyActorClass))
+
+typedef struct _TidyActor               TidyActor;
+typedef struct _TidyActorPrivate        TidyActorPrivate;
+typedef struct _TidyActorClass          TidyActorClass;
+
+/**
+ * TidyActor:
+ *
+ * Base class for stylable actors. The contents of the #TidyActor
+ * structure are private and should only be accessed through the
+ * public API.
+ */
+struct _TidyActor
+{
+  /*< private >*/
+  ClutterActor parent_instance;
+
+  TidyActorPrivate *priv;
+};
+
+/**
+ * TidyActorClass:
+ *
+ * Base class for stylable actors.
+ */
+struct _TidyActorClass
+{
+  /*< private >*/
+  ClutterActorClass parent_class;
+};
+
+GType      tidy_actor_get_type       (void) G_GNUC_CONST;
+
+void       tidy_actor_set_padding    (TidyActor         *actor,
+                                      const TidyPadding *padding);
+void       tidy_actor_get_padding    (TidyActor         *actor,
+                                      TidyPadding       *padding);
+
+void       tidy_actor_set_alignment  (TidyActor         *actor,
+                                      gdouble            x_align,
+                                      gdouble            y_align);
+void       tidy_actor_get_alignment  (TidyActor         *actor,
+                                      gdouble           *x_align,
+                                      gdouble           *y_align);
+void       tidy_actor_set_alignmentx (TidyActor         *actor,
+                                      ClutterFixed       x_align,
+                                      ClutterFixed       y_align);
+void       tidy_actor_get_alignmentx (TidyActor         *actor,
+                                      ClutterFixed      *x_align,
+                                      ClutterFixed      *y_align);
+
+G_END_DECLS
+
+#endif /* __TIDY_ACTOR_H__ */

Added: trunk/src/tidy/tidy-button.c
==============================================================================
--- (empty file)
+++ trunk/src/tidy/tidy-button.c	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,374 @@
+/* tidy-button.c: Plain button actor
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Emmanuele Bassi <ebassi openedhand com>
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <clutter/clutter.h>
+
+#include "tidy-button.h"
+
+#include "tidy-debug.h"
+#include "tidy-marshal.h"
+#include "tidy-stylable.h"
+
+enum
+{
+  PROP_0,
+
+  PROP_LABEL
+};
+
+enum
+{
+  CLICKED,
+
+  LAST_SIGNAL
+};
+
+#define TIDY_BUTTON_GET_PRIVATE(obj)    \
+        (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TIDY_TYPE_BUTTON, TidyButtonPrivate))
+
+struct _TidyButtonPrivate
+{
+  gchar *text;
+
+  ClutterTimeline *timeline;
+  ClutterEffectTemplate *press_tmpl;
+
+  guint8 old_opacity;
+
+  guint is_pressed : 1;
+};
+
+static guint button_signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (TidyButton, tidy_button, TIDY_TYPE_FRAME);
+
+static void
+tidy_button_real_pressed (TidyButton *button)
+{
+  TidyButtonPrivate *priv = button->priv;
+  ClutterActor *actor = CLUTTER_ACTOR (button);
+
+  if (G_UNLIKELY (!priv->press_tmpl))
+    {
+      priv->timeline = clutter_timeline_new_for_duration (250);
+      priv->press_tmpl = clutter_effect_template_new (priv->timeline,
+                                                      clutter_sine_inc_func);
+      clutter_effect_template_set_timeline_clone (priv->press_tmpl, FALSE);
+    }
+
+  if (clutter_timeline_is_playing (priv->timeline))
+    {
+      clutter_timeline_stop (priv->timeline);
+      clutter_actor_set_opacity (actor, priv->old_opacity);
+    }
+
+  priv->old_opacity = clutter_actor_get_opacity (actor);
+
+  clutter_effect_fade (priv->press_tmpl, actor,
+                       0x44,
+                       NULL, NULL);
+}
+
+static void
+tidy_button_real_released (TidyButton *button)
+{
+  TidyButtonPrivate *priv = button->priv;
+  ClutterActor *actor = CLUTTER_ACTOR (button);
+
+  if (G_UNLIKELY (!priv->press_tmpl))
+    {
+      priv->timeline = clutter_timeline_new_for_duration (250);
+      priv->press_tmpl = clutter_effect_template_new (priv->timeline,
+                                                      clutter_sine_inc_func);
+      clutter_effect_template_set_timeline_clone (priv->press_tmpl, FALSE);
+    }
+
+  if (clutter_timeline_is_playing (priv->timeline))
+    clutter_timeline_stop (priv->timeline);
+
+  clutter_effect_fade (priv->press_tmpl, actor,
+                       priv->old_opacity,
+                       NULL, NULL);
+}
+
+static void
+tidy_button_construct_child (TidyButton *button)
+{
+  TidyButtonPrivate *priv = button->priv;
+  gchar *font_name;
+  ClutterColor *text_color;
+  ClutterActor *label;
+
+  if (!priv->text)
+    return;
+
+  tidy_stylable_get (TIDY_STYLABLE (button),
+                     "font-name", &font_name,
+                     "text-color", &text_color,
+                     NULL);
+
+  label = g_object_new (CLUTTER_TYPE_LABEL,
+                        "font-name", font_name,
+                        "text", priv->text,
+                        "color", text_color,
+                        "alignment", PANGO_ALIGN_CENTER,
+                        "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
+                        "use-markup", TRUE,
+                        "wrap", FALSE,
+                        NULL);
+
+  clutter_actor_show (label);
+  clutter_container_add_actor (CLUTTER_CONTAINER (button), label);
+
+  clutter_color_free (text_color);
+  g_free (font_name);
+}
+
+static gboolean
+tidy_button_button_press (ClutterActor       *actor,
+                          ClutterButtonEvent *event)
+{
+  if (event->button == 1 &&
+      event->click_count == 1)
+    {
+      TidyButton *button = TIDY_BUTTON (actor);
+      TidyButtonClass *klass = TIDY_BUTTON_GET_CLASS (button);
+
+      button->priv->is_pressed = TRUE;
+
+      clutter_grab_pointer (actor);
+
+      if (klass->pressed)
+        klass->pressed (button);
+
+      return TRUE;
+    }
+  
+  return FALSE;
+}
+
+static gboolean
+tidy_button_button_release (ClutterActor       *actor,
+                            ClutterButtonEvent *event)
+{
+  if (event->button == 1)
+    {
+      TidyButton *button = TIDY_BUTTON (actor);
+      TidyButtonClass *klass = TIDY_BUTTON_GET_CLASS (button);
+
+      if (!button->priv->is_pressed)
+        return FALSE;
+
+      clutter_ungrab_pointer ();
+
+      button->priv->is_pressed = FALSE;
+
+      if (klass->released)
+        klass->released (button);
+
+      g_signal_emit (button, button_signals[CLICKED], 0);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+tidy_button_leave (ClutterActor         *actor,
+                   ClutterCrossingEvent *event)
+{
+  TidyButton *button = TIDY_BUTTON (actor);
+
+  if (button->priv->is_pressed)
+    {
+      TidyButtonClass *klass = TIDY_BUTTON_GET_CLASS (button);
+
+      clutter_ungrab_pointer ();
+
+      button->priv->is_pressed = FALSE;
+
+      if (klass->released)
+        klass->released (button);
+    }
+
+  return FALSE;
+}
+
+static void
+tidy_button_set_property (GObject      *gobject,
+                          guint         prop_id,
+                          const GValue *value,
+                          GParamSpec   *pspec)
+{
+  TidyButton *button = TIDY_BUTTON (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_LABEL:
+      tidy_button_set_label (button, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+tidy_button_get_property (GObject    *gobject,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  TidyButtonPrivate *priv = TIDY_BUTTON (gobject)->priv;
+
+  switch (prop_id)
+    {
+    case PROP_LABEL:
+      g_value_set_string (value, priv->text);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+tidy_button_finalize (GObject *gobject)
+{
+  TidyButtonPrivate *priv = TIDY_BUTTON (gobject)->priv;
+
+  g_free (priv->text);
+
+  G_OBJECT_CLASS (tidy_button_parent_class)->finalize (gobject);
+}
+
+static void
+tidy_button_dispose (GObject *gobject)
+{
+  TidyButtonPrivate *priv = TIDY_BUTTON (gobject)->priv;
+
+  if (priv->press_tmpl)
+    {
+      g_object_unref (priv->press_tmpl);
+      g_object_unref (priv->timeline);
+
+      priv->press_tmpl = NULL;
+      priv->timeline = NULL;
+    }
+
+  G_OBJECT_CLASS (tidy_button_parent_class)->dispose (gobject);
+}
+
+static void
+tidy_button_class_init (TidyButtonClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (TidyButtonPrivate));
+
+  klass->pressed = tidy_button_real_pressed;
+  klass->released = tidy_button_real_released;
+
+  gobject_class->set_property = tidy_button_set_property;
+  gobject_class->get_property = tidy_button_get_property;
+  gobject_class->dispose = tidy_button_dispose;
+  gobject_class->finalize = tidy_button_finalize;
+
+  actor_class->button_press_event = tidy_button_button_press;
+  actor_class->button_release_event = tidy_button_button_release;
+  actor_class->leave_event = tidy_button_leave;
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_LABEL,
+                                   g_param_spec_string ("label",
+                                                        "Label",
+                                                        "Label of the button",
+                                                        NULL,
+                                                        G_PARAM_READWRITE));
+
+  button_signals[CLICKED] =
+    g_signal_new ("clicked",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (TidyButtonClass, clicked),
+                  NULL, NULL,
+                  _tidy_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+}
+
+static void
+tidy_button_init (TidyButton *button)
+{
+  button->priv = TIDY_BUTTON_GET_PRIVATE (button);
+}
+
+ClutterActor *
+tidy_button_new (void)
+{
+  return g_object_new (TIDY_TYPE_BUTTON, NULL);
+}
+
+ClutterActor *
+tidy_button_new_with_label (const gchar *text)
+{
+  return g_object_new (TIDY_TYPE_BUTTON, "label", text, NULL);
+}
+
+G_CONST_RETURN gchar *
+tidy_button_get_label (TidyButton *button)
+{
+  g_return_val_if_fail (TIDY_IS_BUTTON (button), NULL);
+
+  return button->priv->text;
+}
+
+void
+tidy_button_set_label (TidyButton  *button,
+                       const gchar *text)
+{
+  TidyButtonPrivate *priv;
+
+  g_return_if_fail (TIDY_IS_BUTTON (button));
+
+  priv = button->priv;
+
+  g_free (priv->text);
+  priv->text = g_strdup (text);
+
+  tidy_button_construct_child (button);
+
+  g_object_notify (G_OBJECT (button), "label");
+}

Added: trunk/src/tidy/tidy-button.h
==============================================================================
--- (empty file)
+++ trunk/src/tidy/tidy-button.h	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,70 @@
+/* tidy-button.h: Plain button actor
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Emmanuele Bassi <ebassi openedhand com>
+ */
+
+#ifndef __TIDY_BUTTON_H__
+#define __TIDY_BUTTON_H__
+
+#include <tidy/tidy-frame.h>
+
+G_BEGIN_DECLS
+
+#define TIDY_TYPE_BUTTON                (tidy_button_get_type ())
+#define TIDY_BUTTON(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIDY_TYPE_BUTTON, TidyButton))
+#define TIDY_IS_BUTTON(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIDY_TYPE_BUTTON))
+#define TIDY_BUTTON_CLASS(klass)        (G_TYPE_CHECK_CLASS_CAST ((klass), TIDY_TYPE_BUTTON, TidyButtonClass))
+#define TIDY_IS_BUTTON_CLASS(klass)     (G_TYPE_CHECK_CLASS_TYPE ((klass), TIDY_TYPE_BUTTON))
+#define TIDY_BUTTON_GET_CLASS(obj)      (G_TYPE_INSTANCE_GET_CLASS ((obj), TIDY_TYPE_BUTTON, TidyButtonClass))
+
+typedef struct _TidyButton              TidyButton;
+typedef struct _TidyButtonPrivate       TidyButtonPrivate;
+typedef struct _TidyButtonClass         TidyButtonClass;
+
+struct _TidyButton
+{
+  TidyFrame parent_instance;
+
+  TidyButtonPrivate *priv;
+};
+
+struct _TidyButtonClass
+{
+  TidyFrameClass parent_class;
+
+  /* vfuncs, not signals */
+  void (* pressed)  (TidyButton *button);
+  void (* released) (TidyButton *button);
+
+  /* signals */
+  void (* clicked) (TidyButton *button);
+};
+
+GType tidy_button_get_type (void) G_GNUC_CONST;
+
+ClutterActor *        tidy_button_new            (void);
+ClutterActor *        tidy_button_new_with_label (const gchar  *text);
+G_CONST_RETURN gchar *tidy_button_get_label      (TidyButton   *button);
+void                  tidy_button_set_label      (TidyButton   *button,
+                                                  const gchar  *text);
+
+G_END_DECLS
+
+#endif /* __TIDY_BUTTON_H__ */

Added: trunk/src/tidy/tidy-debug.h
==============================================================================
--- (empty file)
+++ trunk/src/tidy/tidy-debug.h	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,4 @@
+#ifndef __TIDY_DEBUG_H__
+#define __TIDY_DEBUG_H__
+
+#endif /* __TIDY_DEBUG_H__ */

Added: trunk/src/tidy/tidy-enum-types.c.in
==============================================================================
--- (empty file)
+++ trunk/src/tidy/tidy-enum-types.c.in	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,30 @@
+/*** BEGIN file-header ***/
+#include "tidy-enum-types.h"
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+#include "@filename@"
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+ enum_name@_get_type(void) {
+  static GType enum_type_id = 0;
+  if (G_UNLIKELY (!enum_type_id))
+    {
+      static const G Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+        { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+        { 0, NULL, NULL }
+      };
+      enum_type_id = g_ type@_register_static("@EnumName@", values);
+    }
+  return enum_type_id;
+}
+/*** END value-tail ***/

Added: trunk/src/tidy/tidy-enum-types.h.in
==============================================================================
--- (empty file)
+++ trunk/src/tidy/tidy-enum-types.h.in	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,25 @@
+/*** BEGIN file-header ***/
+#ifndef __TIDY_ENUM_TYPES_H__
+#define __TIDY_ENUM_TYPES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* !__TIDY_ENUM_TYPES_H__ */
+/*** END file-tail ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name _get_type (void) G_GNUC_CONST;
+#define TIDY_TYPE_ ENUMSHORT@ (@enum_name _get_type())
+
+/*** END value-header ***/

Added: trunk/src/tidy/tidy-frame.c
==============================================================================
--- (empty file)
+++ trunk/src/tidy/tidy-frame.c	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,515 @@
+/* tidy-frame.c: Simple container with a background
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Emmanuele Bassi <ebassi openedhand com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib-object.h>
+
+#include <cogl/cogl.h>
+#include <clutter/clutter.h>
+
+#include "tidy-frame.h"
+#include "tidy-private.h"
+#include "tidy-stylable.h"
+
+#define TIDY_FRAME_GET_PRIVATE(obj)     (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TIDY_TYPE_FRAME, TidyFramePrivate))
+
+enum
+{
+  PROP_0,
+
+  PROP_CHILD,
+  PROP_TEXTURE
+};
+
+struct _TidyFramePrivate
+{
+  ClutterActor *child;
+  ClutterActor *texture;
+};
+
+static ClutterColor default_bg_color = { 0xcc, 0xcc, 0xcc, 0xff };
+
+static void clutter_container_iface_init (ClutterContainerIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (TidyFrame, tidy_frame, TIDY_TYPE_ACTOR,
+                         G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
+                                                clutter_container_iface_init));
+
+static void
+tidy_frame_get_preferred_width (ClutterActor *actor,
+                                ClutterUnit   for_height,
+                                ClutterUnit  *min_width_p,
+                                ClutterUnit  *natural_width_p)
+{
+  TidyFramePrivate *priv = TIDY_FRAME (actor)->priv;
+  TidyPadding padding = { 0, };
+  ClutterUnit min_width, natural_width;
+
+  tidy_actor_get_padding (TIDY_ACTOR (actor), &padding);
+
+  min_width = 0;
+  natural_width = padding.left + padding.right;
+
+  if (priv->child)
+    {
+      ClutterUnit child_min, child_natural;
+
+      clutter_actor_get_preferred_width (priv->child, for_height,
+                                         &child_min,
+                                         &child_natural);
+
+      min_width += child_min;
+      natural_width += child_natural;
+    }
+
+  if (min_width_p)
+    *min_width_p = min_width;
+
+  if (natural_width_p)
+    *natural_width_p = natural_width;
+}
+
+static void
+tidy_frame_get_preferred_height (ClutterActor *actor,
+                                 ClutterUnit   for_width,
+                                 ClutterUnit  *min_height_p,
+                                 ClutterUnit  *natural_height_p)
+{
+  TidyFramePrivate *priv = TIDY_FRAME (actor)->priv;
+  TidyPadding padding = { 0, };
+  ClutterUnit min_height, natural_height;
+
+  tidy_actor_get_padding (TIDY_ACTOR (actor), &padding);
+
+  min_height = 0;
+  natural_height = padding.top + padding.bottom;
+
+  if (priv->child)
+    {
+      ClutterUnit child_min, child_natural;
+
+      clutter_actor_get_preferred_height (priv->child, for_width,
+                                          &child_min,
+                                          &child_natural);
+
+      min_height += child_min;
+      natural_height += child_natural;
+    }
+
+  if (min_height_p)
+    *min_height_p = min_height;
+
+  if (natural_height_p)
+    *natural_height_p = natural_height;
+}
+
+static void
+tidy_frame_allocate (ClutterActor          *actor,
+                     const ClutterActorBox *box,
+                     gboolean               origin_changed)
+{
+  TidyFramePrivate *priv = TIDY_FRAME (actor)->priv;
+  ClutterActorClass *klass;
+
+  klass = CLUTTER_ACTOR_CLASS (tidy_frame_parent_class);
+  klass->allocate (actor, box, origin_changed);
+
+  if (priv->texture)
+    {
+      ClutterActorBox texture_box = { 0, };
+
+      texture_box.x1 = 0;
+      texture_box.y1 = 0;
+      texture_box.x2 = box->x2 - box->x1;
+      texture_box.y2 = box->y2 - box->y1;
+
+      clutter_actor_allocate (priv->texture, &texture_box, origin_changed);
+    }
+
+  if (priv->child)
+    {
+      TidyPadding padding = { 0, };
+      ClutterFixed x_align, y_align;
+      ClutterUnit available_width, available_height;
+      ClutterUnit child_width, child_height;
+      ClutterActorBox child_box = { 0, };
+
+      tidy_actor_get_padding (TIDY_ACTOR (actor), &padding);
+      tidy_actor_get_alignmentx (TIDY_ACTOR (actor), &x_align, &y_align);
+
+      available_width  = box->x2 - box->x1
+                       - padding.left
+                       - padding.right;
+      available_height = box->y2 - box->y1
+                       - padding.top
+                       - padding.bottom;
+
+      if (available_width < 0)
+        available_width = 0;
+
+      if (available_height < 0)
+        available_height = 0;
+
+      clutter_actor_get_preferred_size (priv->child,
+                                        NULL, NULL,
+                                        &child_width,
+                                        &child_height);
+
+      if (child_width > available_width)
+        child_width = available_width;
+
+      if (child_height > available_height)
+        child_height = available_height;
+
+      child_box.x1 = CLUTTER_FIXED_MUL ((available_width - child_width),
+                                        x_align)
+                   + padding.left;
+      child_box.y1 = CLUTTER_FIXED_MUL ((available_height - child_height),
+                                        y_align)
+                   + padding.top;
+
+      child_box.x2 = child_box.x1 + child_width;
+      child_box.y2 = child_box.y1 + child_height;
+
+      clutter_actor_allocate (priv->child, &child_box, origin_changed);
+    }
+}
+
+static void
+tidy_frame_paint (ClutterActor *actor)
+{
+  TidyFrame *frame = TIDY_FRAME (actor);
+  TidyFramePrivate *priv = frame->priv;
+
+  cogl_push_matrix ();
+
+  if (priv->texture)
+    clutter_actor_paint (priv->texture);
+  else
+    {
+      ClutterActorBox allocation = { 0, };
+      ClutterColor *bg_color;
+      guint w, h;
+
+      tidy_stylable_get (TIDY_STYLABLE (frame), "bg-color", &bg_color, NULL);
+      if (!bg_color)
+        bg_color = &default_bg_color;
+
+      bg_color->alpha = clutter_actor_get_paint_opacity (actor)
+                      * bg_color->alpha
+                      / 255;
+
+      clutter_actor_get_allocation_box (actor, &allocation);
+
+      w = CLUTTER_UNITS_TO_DEVICE (allocation.x2 - allocation.x1);
+      h = CLUTTER_UNITS_TO_DEVICE (allocation.y2 - allocation.y1);
+
+      cogl_color (bg_color);
+      cogl_rectangle (0, 0, w, h);
+      
+      if (bg_color != &default_bg_color)
+        clutter_color_free (bg_color);
+    }
+
+  if (priv->child && CLUTTER_ACTOR_IS_VISIBLE (priv->child))
+    clutter_actor_paint (priv->child);
+
+  cogl_pop_matrix ();
+}
+
+static void
+tidy_frame_pick (ClutterActor       *actor,
+                 const ClutterColor *pick_color)
+{
+  TidyFramePrivate *priv = TIDY_FRAME (actor)->priv;
+
+  /* chain up, so we get a box with our coordinates */
+  CLUTTER_ACTOR_CLASS (tidy_frame_parent_class)->pick (actor, pick_color);
+
+  if (priv->child && CLUTTER_ACTOR_IS_VISIBLE (priv->child))
+    clutter_actor_paint (priv->child);
+}
+
+static void
+tidy_frame_dispose (GObject *gobject)
+{
+  TidyFramePrivate *priv = TIDY_FRAME (gobject)->priv;
+
+  if (priv->child)
+    {
+      clutter_actor_unparent (priv->child);
+      priv->child = NULL;
+    }
+
+  if (priv->texture)
+    {
+      clutter_actor_unparent (priv->texture);
+      priv->texture = NULL;
+    }
+
+  G_OBJECT_CLASS (tidy_frame_parent_class)->dispose (gobject);
+}
+
+static void
+tidy_frame_set_property (GObject      *gobject,
+                         guint         prop_id,
+                         const GValue *value,
+                         GParamSpec   *pspec)
+{
+  switch (prop_id)
+    {
+    case PROP_CHILD:
+      clutter_container_add_actor (CLUTTER_CONTAINER (gobject),
+                                   g_value_get_object (value));
+      break;
+
+    case PROP_TEXTURE:
+      tidy_frame_set_texture (TIDY_FRAME (gobject),
+                              g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+tidy_frame_get_property (GObject    *gobject,
+                         guint       prop_id,
+                         GValue     *value,
+                         GParamSpec *pspec)
+{
+  TidyFramePrivate *priv = TIDY_FRAME (gobject)->priv;
+
+  switch (prop_id)
+    {
+    case PROP_CHILD:
+      g_value_set_object (value, priv->child);
+      break;
+
+    case PROP_TEXTURE:
+      g_value_set_object (value, priv->texture);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+tidy_frame_class_init (TidyFrameClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (TidyFramePrivate));
+
+  gobject_class->set_property = tidy_frame_set_property;
+  gobject_class->get_property = tidy_frame_get_property;
+  gobject_class->dispose = tidy_frame_dispose;
+
+  actor_class->pick = tidy_frame_pick;
+  actor_class->paint = tidy_frame_paint;
+  actor_class->allocate = tidy_frame_allocate;
+  actor_class->get_preferred_width = tidy_frame_get_preferred_width;
+  actor_class->get_preferred_height = tidy_frame_get_preferred_height;
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_CHILD,
+                                   g_param_spec_object ("child",
+                                                        "Child",
+                                                        "The child of the frame",
+                                                        CLUTTER_TYPE_ACTOR,
+                                                        TIDY_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+                                   PROP_TEXTURE,
+                                   g_param_spec_object ("texture",
+                                                        "Texture",
+                                                        "The background texture of the frame",
+                                                        CLUTTER_TYPE_ACTOR,
+                                                        TIDY_PARAM_READWRITE));
+}
+
+static void
+tidy_frame_init (TidyFrame *frame)
+{
+  frame->priv = TIDY_FRAME_GET_PRIVATE (frame);
+}
+
+static void
+tidy_frame_add_actor (ClutterContainer *container,
+                      ClutterActor     *actor)
+{
+  TidyFramePrivate *priv = TIDY_FRAME (container)->priv;
+
+  if (priv->child)
+    clutter_actor_unparent (priv->child);
+
+  clutter_actor_set_parent (actor, CLUTTER_ACTOR (container));
+  priv->child = actor;
+
+  clutter_actor_queue_relayout (CLUTTER_ACTOR (container));
+
+  g_signal_emit_by_name (container, "actor-added", actor);
+
+  g_object_notify (G_OBJECT (container), "child");
+}
+
+static void
+tidy_frame_remove_actor (ClutterContainer *container,
+                         ClutterActor     *actor)
+{
+  TidyFramePrivate *priv = TIDY_FRAME (container)->priv;
+
+  if (priv->child == actor)
+    {
+      g_object_ref (priv->child);
+
+      clutter_actor_unparent (priv->child);
+      priv->child = NULL;
+
+      clutter_actor_queue_relayout (CLUTTER_ACTOR (container));
+
+      g_signal_emit_by_name (container, "actor-removed", priv->child);
+
+      g_object_unref (priv->child);
+    }
+}
+
+static void
+tidy_frame_foreach (ClutterContainer *container,
+                    ClutterCallback   callback,
+                    gpointer          callback_data)
+{
+  TidyFramePrivate *priv = TIDY_FRAME (container)->priv;
+
+  if (priv->texture)
+    callback (priv->texture, callback_data);
+
+  if (priv->child)
+    callback (priv->child, callback_data);
+}
+
+static void
+tidy_frame_lower (ClutterContainer *container,
+                  ClutterActor     *actor,
+                  ClutterActor     *sibling)
+{
+  /* single child */
+}
+
+static void
+tidy_frame_raise (ClutterContainer *container,
+                  ClutterActor     *actor,
+                  ClutterActor     *sibling)
+{
+  /* single child */
+}
+
+static void
+tidy_frame_sort_depth_order (ClutterContainer *container)
+{
+  /* single child */
+}
+
+static void
+clutter_container_iface_init (ClutterContainerIface *iface)
+{
+  iface->add = tidy_frame_add_actor;
+  iface->remove = tidy_frame_remove_actor;
+  iface->foreach = tidy_frame_foreach;
+  iface->lower = tidy_frame_lower;
+  iface->raise = tidy_frame_raise;
+  iface->sort_depth_order = tidy_frame_sort_depth_order;
+}
+
+ClutterActor *
+tidy_frame_new (void)
+{
+  return g_object_new (TIDY_TYPE_FRAME, NULL);
+}
+
+ClutterActor *
+tidy_frame_get_child (TidyFrame *frame)
+{
+  g_return_val_if_fail (TIDY_IS_FRAME (frame), NULL);
+
+  return frame->priv->child;
+}
+
+void
+tidy_frame_set_texture (TidyFrame    *frame,
+                        ClutterActor *texture)
+{
+  TidyFramePrivate *priv;
+
+  g_return_if_fail (TIDY_IS_FRAME (frame));
+  g_return_if_fail (CLUTTER_IS_ACTOR (texture));
+
+  priv = frame->priv;
+
+  if (priv->texture == texture)
+    return;
+
+  if (priv->texture)
+    {
+      clutter_actor_unparent (priv->texture);
+      priv->texture = NULL;
+    }
+
+  if (texture)
+    {
+      ClutterActor *parent = clutter_actor_get_parent (texture);
+
+      if (G_UNLIKELY (parent != NULL))
+        {
+          g_warning ("Unable to set the background texture of type `%s' for "
+                     "the frame of type `%s': the texture actor is already "
+                     "a child of a container of type `%s'",
+                     g_type_name (G_OBJECT_TYPE (texture)),
+                     g_type_name (G_OBJECT_TYPE (frame)),
+                     g_type_name (G_OBJECT_TYPE (parent)));
+          return;
+        }
+
+      priv->texture = texture;
+      clutter_actor_set_parent (texture, CLUTTER_ACTOR (frame));
+    }
+
+  clutter_actor_queue_relayout (CLUTTER_ACTOR (frame));
+
+  g_object_notify (G_OBJECT (frame), "texture");
+}
+
+ClutterActor *
+tidy_frame_get_texture (TidyFrame *frame)
+{
+  g_return_val_if_fail (TIDY_IS_FRAME (frame), NULL);
+
+  return frame->priv->texture;
+}
+

Added: trunk/src/tidy/tidy-frame.h
==============================================================================
--- (empty file)
+++ trunk/src/tidy/tidy-frame.h	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,64 @@
+/* tidy-frame.h: Simple container with a background
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Emmanuele Bassi <ebassi openedhand com>
+ */
+
+#ifndef __TIDY_FRAME_H__
+#define __TIDY_FRAME_H__
+
+#include <clutter/clutter-actor.h>
+#include <tidy/tidy-actor.h>
+
+G_BEGIN_DECLS
+
+#define TIDY_TYPE_FRAME                 (tidy_frame_get_type ())
+#define TIDY_FRAME(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIDY_TYPE_FRAME, TidyFrame))
+#define TIDY_IS_FRAME(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIDY_TYPE_FRAME))
+#define TIDY_FRAME_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), TIDY_TYPE_FRAME, TidyFrameClass))
+#define TIDY_IS_FRAME_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), TIDY_TYPE_FRAME))
+#define TIDY_FRAME_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), TIDY_TYPE_FRAME, TidyFrameClass))
+
+typedef struct _TidyFrame               TidyFrame;
+typedef struct _TidyFramePrivate        TidyFramePrivate;
+typedef struct _TidyFrameClass          TidyFrameClass;
+
+struct _TidyFrame
+{
+  TidyActor parent_instance;
+
+  TidyFramePrivate *priv;
+};
+
+struct _TidyFrameClass
+{
+  TidyActorClass parent_class;
+};
+
+GType         tidy_frame_get_type    (void) G_GNUC_CONST;
+
+ClutterActor *tidy_frame_new         (void);
+ClutterActor *tidy_frame_get_child   (TidyFrame    *frame);
+void          tidy_frame_set_texture (TidyFrame    *frame,
+                                      ClutterActor *actor);
+ClutterActor *tidy_frame_get_texture (TidyFrame    *frame);
+
+G_END_DECLS
+
+#endif /* __TIDY_FRAME_H__ */

Added: trunk/src/tidy/tidy-grid.c
==============================================================================
--- (empty file)
+++ trunk/src/tidy/tidy-grid.c	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,1005 @@
+/* tidy-grid.h: Reflowing grid layout container for clutter.
+ *
+ * Copyright (C) 2008 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Ãyvind KolÃs <pippin linux intel com>
+ */
+
+/* TODO:
+ *
+ * - Better names for properties.
+ * - Caching layouted positions? (perhaps needed for huge collections)
+ * - More comments / overall concept on how the layouting is done.
+ * - Allow more layout directions than just row major / column major.
+ */
+
+#include <clutter/clutter-actor.h>
+#include <clutter/clutter-container.h>
+#include <string.h>
+
+#include "tidy-grid.h"
+
+typedef struct _TidyGridActorData TidyGridActorData;
+
+static void tidy_grid_dispose             (GObject *object);
+static void tidy_grid_finalize            (GObject *object);
+
+static void tidy_grid_finalize            (GObject *object);
+
+static void tidy_grid_set_property        (GObject      *object,
+                                           guint         prop_id,
+                                           const GValue *value,
+                                           GParamSpec   *pspec);
+static void tidy_grid_get_property        (GObject      *object,
+                                           guint         prop_id,
+                                           GValue       *value,
+                                           GParamSpec   *pspec);
+
+static void clutter_container_iface_init  (ClutterContainerIface *iface);
+
+static void tidy_grid_real_add            (ClutterContainer *container,
+                                           ClutterActor     *actor);
+static void tidy_grid_real_remove         (ClutterContainer *container,
+                                           ClutterActor     *actor);
+static void tidy_grid_real_foreach        (ClutterContainer *container,
+                                           ClutterCallback   callback,
+                                           gpointer          user_data);
+static void tidy_grid_real_raise          (ClutterContainer *container,
+                                           ClutterActor     *actor,
+                                           ClutterActor     *sibling);
+static void tidy_grid_real_lower          (ClutterContainer *container,
+                                           ClutterActor     *actor,
+                                           ClutterActor     *sibling);
+static void
+tidy_grid_real_sort_depth_order (ClutterContainer *container);
+
+static void
+tidy_grid_free_actor_data (gpointer data);
+
+static void tidy_grid_paint (ClutterActor *actor);
+
+static void tidy_grid_pick (ClutterActor *actor,
+                                       const ClutterColor *color);
+
+static void
+tidy_grid_get_preferred_width (ClutterActor *self,
+                                         ClutterUnit for_height,
+                                         ClutterUnit *min_width_p,
+                                         ClutterUnit *natural_width_p);
+
+static void
+tidy_grid_get_preferred_height (ClutterActor *self,
+                                           ClutterUnit for_width,
+                                           ClutterUnit *min_height_p,
+                                           ClutterUnit *natural_height_p);
+
+static void tidy_grid_allocate (ClutterActor *self,
+                                           const ClutterActorBox *box,
+                                           gboolean absolute_origin_changed);
+
+G_DEFINE_TYPE_WITH_CODE (TidyGrid, tidy_grid,
+                         CLUTTER_TYPE_ACTOR,
+                         G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
+                                                clutter_container_iface_init));
+
+#define TIDY_GRID_GET_PRIVATE(obj) \
+  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TIDY_TYPE_GRID, \
+                                TidyGridPrivate))
+
+struct _TidyGridPrivate
+{
+  ClutterUnit for_height,  for_width;
+  ClutterUnit pref_width,  pref_height;
+  ClutterUnit alloc_width, alloc_height;
+
+  gboolean    absolute_origin_changed;
+  GHashTable *hash_table;
+  GList      *list;
+
+  gboolean    homogenous_rows;
+  gboolean    homogenous_columns;
+  gboolean    end_align;
+  ClutterUnit column_gap, row_gap;
+  gdouble     valign, halign;
+
+  gboolean    column_major;
+
+  gboolean    first_of_batch;
+  ClutterUnit a_current_sum, a_wrap;
+  ClutterUnit max_extent_a;
+  ClutterUnit max_extent_b;
+};
+
+enum
+{
+  PROP_0,
+  PROP_HOMOGENOUS_ROWS,
+  PROP_HOMOGENOUS_COLUMNS,
+  PROP_ROW_GAP,
+  PROP_COLUMN_GAP,
+  PROP_VALIGN,
+  PROP_HALIGN,
+  PROP_END_ALIGN,
+  PROP_COLUMN_MAJOR,
+};
+
+struct _TidyGridActorData
+{
+  gboolean    xpos_set,   ypos_set;
+  ClutterUnit xpos,       ypos;
+  ClutterUnit pref_width, pref_height;
+};
+
+static void
+tidy_grid_class_init (TidyGridClass *klass)
+{
+  GObjectClass *gobject_class = (GObjectClass *) klass;
+  ClutterActorClass *actor_class = (ClutterActorClass *) klass;
+
+  gobject_class->dispose = tidy_grid_dispose;
+  gobject_class->finalize = tidy_grid_finalize;
+
+  gobject_class->set_property = tidy_grid_set_property;
+  gobject_class->get_property = tidy_grid_get_property;
+
+  actor_class->paint                = tidy_grid_paint;
+  actor_class->pick                 = tidy_grid_pick;
+  actor_class->get_preferred_width  = tidy_grid_get_preferred_width;
+  actor_class->get_preferred_height = tidy_grid_get_preferred_height;
+  actor_class->allocate             = tidy_grid_allocate;
+
+  g_type_class_add_private (klass, sizeof (TidyGridPrivate));
+
+
+  g_object_class_install_property
+                   (gobject_class,
+                    PROP_ROW_GAP,
+                    clutter_param_spec_unit ("row-gap",
+                                             "Row gap",
+                                             "gap between rows in the layout",
+                                             0, CLUTTER_MAXUNIT,
+                                             0,
+                                             G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property
+                   (gobject_class,
+                    PROP_COLUMN_GAP,
+                    clutter_param_spec_unit ("column-gap",
+                                             "Column gap",
+                                             "gap between columns in the layout",
+                                             0, CLUTTER_MAXUNIT,
+                                             0,
+                                             G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+
+
+  g_object_class_install_property
+                   (gobject_class,
+                    PROP_HOMOGENOUS_ROWS,
+                    g_param_spec_boolean ("homogenous-rows",
+                                          "homogenous rows",
+                                          "Should all rows have the same height?",
+                                          FALSE,
+                                          G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property
+                   (gobject_class,
+                    PROP_HOMOGENOUS_COLUMNS,
+                    g_param_spec_boolean ("homogenous-columns",
+                                          "homogenous columns",
+                                          "Should all columns have the same height?",
+                                          FALSE,
+                                          G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property
+                   (gobject_class,
+                    PROP_COLUMN_MAJOR,
+                    g_param_spec_boolean ("column-major",
+                                          "column-major",
+                                          "Do a column filling first instead of row filling first",
+                                          FALSE,
+                                          G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property
+                   (gobject_class,
+                    PROP_END_ALIGN,
+                    g_param_spec_boolean ("end-align",
+                                          "end-align",
+                                          "Right/bottom aligned rows/columns",
+                                          FALSE,
+                                          G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property
+                   (gobject_class,
+                    PROP_VALIGN,
+                    g_param_spec_double ("valign",
+                                         "Vertical align",
+                                         "Vertical alignment of items within cells",
+                                          0.0, 1.0, 0.0,
+                                          G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property
+                   (gobject_class,
+                    PROP_HALIGN,
+                    g_param_spec_double ("halign",
+                                         "Horizontal align",
+                                         "Horizontal alignment of items within cells",
+                                          0.0, 1.0, 0.0,
+                                          G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+
+}
+
+static void
+clutter_container_iface_init (ClutterContainerIface *iface)
+{
+  iface->add              = tidy_grid_real_add;
+  iface->remove           = tidy_grid_real_remove;
+  iface->foreach          = tidy_grid_real_foreach;
+  iface->raise            = tidy_grid_real_raise;
+  iface->lower            = tidy_grid_real_lower;
+  iface->sort_depth_order = tidy_grid_real_sort_depth_order;
+}
+
+static void
+tidy_grid_init (TidyGrid *self)
+{
+  TidyGridPrivate *priv;
+
+  self->priv = priv = TIDY_GRID_GET_PRIVATE (self);
+
+  /* do not unref in the hashtable, the reference is for now kept by the list
+   * (double bookkeeping sucks)
+   */
+  priv->hash_table
+    = g_hash_table_new_full (g_direct_hash,
+                             g_direct_equal,
+                             NULL,
+                             tidy_grid_free_actor_data);
+}
+
+static void
+tidy_grid_dispose (GObject *object)
+{
+  TidyGrid *self = (TidyGrid *) object;
+  TidyGridPrivate *priv;
+ 
+  priv = self->priv;
+
+  /* Destroy all of the children. This will cause them to be removed
+     from the container and unparented */
+  clutter_container_foreach (CLUTTER_CONTAINER (object),
+                             (ClutterCallback) clutter_actor_destroy,
+                             NULL);
+
+  G_OBJECT_CLASS (tidy_grid_parent_class)->dispose (object);
+}
+
+static void
+tidy_grid_finalize (GObject *object)
+{
+  TidyGrid *self = (TidyGrid *) object;
+  TidyGridPrivate *priv = self->priv;
+  
+  g_hash_table_destroy (priv->hash_table);
+
+  G_OBJECT_CLASS (tidy_grid_parent_class)->finalize (object);
+}
+
+
+void
+tidy_grid_set_end_align (TidyGrid *self,
+                         gboolean  value)
+{
+  TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
+  priv->end_align = value;
+  clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+}
+
+gboolean
+tidy_grid_get_end_align (TidyGrid *self)
+{
+  TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
+  return priv->end_align;
+}
+
+void
+tidy_grid_set_homogenous_rows (TidyGrid *self,
+                               gboolean  value)
+{
+  TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
+  priv->homogenous_rows = value;
+  clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+}
+
+gboolean
+tidy_grid_get_homogenous_rows (TidyGrid *self)
+{
+  TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
+  return priv->homogenous_rows;
+}
+
+
+void
+tidy_grid_set_homogenous_columns (TidyGrid *self,
+                                  gboolean  value)
+{
+  TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
+  priv->homogenous_columns = value;
+  clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+}
+
+
+gboolean
+tidy_grid_get_homogenous_columns (TidyGrid *self)
+{
+  TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
+  return priv->homogenous_columns;
+}
+
+
+void
+tidy_grid_set_column_major (TidyGrid *self,
+                            gboolean  value)
+{
+  TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
+  priv->column_major = value;
+  clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+}
+
+gboolean
+tidy_grid_get_column_major (TidyGrid *self)
+{
+  TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
+  return priv->column_major;
+}
+
+void
+tidy_grid_set_column_gap (TidyGrid    *self,
+                          ClutterUnit  value)
+{
+  TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
+  priv->column_gap = value;
+  clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+}
+
+ClutterUnit
+tidy_grid_get_column_gap (TidyGrid *self)
+{
+  TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
+  return priv->column_gap;
+}
+
+
+
+void
+tidy_grid_set_row_gap (TidyGrid    *self,
+                       ClutterUnit  value)
+{
+  TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
+  priv->row_gap = value;
+  clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+}
+
+ClutterUnit
+tidy_grid_get_row_gap (TidyGrid *self)
+{
+  TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
+  return priv->row_gap;
+}
+
+
+void
+tidy_grid_set_valign (TidyGrid *self,
+                      gdouble   value)
+{
+  TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
+  priv->valign = value;
+  clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+}
+
+gdouble
+tidy_grid_get_valign (TidyGrid *self)
+{
+  TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
+  return priv->valign;
+}
+
+
+
+void
+tidy_grid_set_halign (TidyGrid *self,
+                      gdouble   value)
+                                 
+{
+  TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
+  priv->halign = value;
+  clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+}
+
+gdouble
+tidy_grid_get_halign (TidyGrid *self)
+{
+  TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
+  return priv->halign;
+}
+
+
+static void
+tidy_grid_set_property (GObject      *object,
+                        guint         prop_id,
+                        const GValue *value,
+                        GParamSpec   *pspec)
+{
+  TidyGrid *grid = TIDY_GRID (object);
+
+  TidyGridPrivate *priv;
+
+  priv = TIDY_GRID_GET_PRIVATE (object);
+
+  switch (prop_id)
+    {
+    case PROP_END_ALIGN:
+      tidy_grid_set_end_align (grid, g_value_get_boolean (value));
+      break;
+    case PROP_HOMOGENOUS_ROWS:
+      tidy_grid_set_homogenous_rows (grid, g_value_get_boolean (value));
+      break;
+    case PROP_HOMOGENOUS_COLUMNS:
+      tidy_grid_set_homogenous_columns (grid, g_value_get_boolean (value));
+      break;
+    case PROP_COLUMN_MAJOR:
+      tidy_grid_set_column_major (grid, g_value_get_boolean (value));
+      break;
+    case PROP_COLUMN_GAP:
+      tidy_grid_set_column_gap (grid, clutter_value_get_unit (value));
+      break;
+    case PROP_ROW_GAP:
+      tidy_grid_set_row_gap (grid, clutter_value_get_unit (value));
+      break;
+    case PROP_VALIGN:
+      tidy_grid_set_valign (grid, g_value_get_double (value));
+      break;
+    case PROP_HALIGN:
+      tidy_grid_set_halign (grid, g_value_get_double (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+tidy_grid_get_property (GObject    *object,
+                        guint       prop_id,
+                        GValue     *value,
+                        GParamSpec *pspec)
+{
+  TidyGrid *grid = TIDY_GRID (object);
+
+  TidyGridPrivate *priv;
+
+  priv = TIDY_GRID_GET_PRIVATE (object);
+
+  switch (prop_id)
+    {
+    case PROP_HOMOGENOUS_ROWS:
+      g_value_set_boolean (value, tidy_grid_get_homogenous_rows (grid));
+      break;
+    case PROP_HOMOGENOUS_COLUMNS:
+      g_value_set_boolean (value, tidy_grid_get_homogenous_columns (grid));
+      break;
+    case PROP_END_ALIGN:
+      g_value_set_boolean (value, tidy_grid_get_end_align (grid));
+      break;
+    case PROP_COLUMN_MAJOR:
+      g_value_set_boolean (value, tidy_grid_get_column_major (grid));
+      break;
+    case PROP_COLUMN_GAP:
+      clutter_value_set_unit (value, tidy_grid_get_column_gap (grid));
+      break;
+    case PROP_ROW_GAP:
+      clutter_value_set_unit (value, tidy_grid_get_row_gap (grid));
+      break;
+    case PROP_VALIGN:
+      g_value_set_double (value, tidy_grid_get_valign (grid));
+      break;
+    case PROP_HALIGN:
+      g_value_set_double (value, tidy_grid_get_halign (grid));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+
+static void
+tidy_grid_free_actor_data (gpointer data)
+{
+  g_slice_free (TidyGridActorData, data);
+}
+
+ClutterActor *
+tidy_grid_new (void)
+{
+  ClutterActor *self = g_object_new (TIDY_TYPE_GRID, NULL);
+
+  return self;
+}
+
+static void
+tidy_grid_real_add (ClutterContainer *container,
+                    ClutterActor     *actor)
+{
+  TidyGridPrivate *priv;
+  TidyGridActorData *data;
+
+  g_return_if_fail (TIDY_IS_GRID (container));
+
+  priv = TIDY_GRID (container)->priv;
+
+  g_object_ref (actor);
+
+  clutter_actor_set_parent (actor, CLUTTER_ACTOR (container));
+
+  data = g_slice_alloc0 (sizeof (TidyGridActorData));
+
+  priv->list = g_list_append (priv->list, actor);
+  g_hash_table_insert (priv->hash_table, actor, data);
+
+  g_signal_emit_by_name (container, "actor-added", actor);
+
+  clutter_actor_queue_relayout (CLUTTER_ACTOR (container));
+
+  g_object_unref (actor);
+}
+
+static void
+tidy_grid_real_remove (ClutterContainer *container,
+                       ClutterActor     *actor)
+{
+  TidyGrid *layout = TIDY_GRID (container);
+  TidyGridPrivate *priv = layout->priv;
+
+  g_object_ref (actor);
+  
+  if (g_hash_table_remove (priv->hash_table, actor))
+    {
+      clutter_actor_unparent (actor);
+
+      clutter_actor_queue_relayout (CLUTTER_ACTOR (layout));
+
+      g_signal_emit_by_name (container, "actor-removed", actor);
+
+      if (CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (layout)))
+        clutter_actor_queue_redraw (CLUTTER_ACTOR (layout));
+    }
+  priv->list = g_list_remove (priv->list, actor);
+
+  g_object_unref (actor);
+}
+
+static void
+tidy_grid_real_foreach (ClutterContainer *container,
+                                   ClutterCallback callback,
+                                   gpointer user_data)
+{
+  TidyGrid *layout = TIDY_GRID (container);
+  TidyGridPrivate *priv = layout->priv;
+
+  g_list_foreach (priv->list, (GFunc) callback, user_data);
+}
+
+static void
+tidy_grid_real_raise (ClutterContainer *container,
+                                 ClutterActor *actor,
+                                 ClutterActor *sibling)
+{
+  /* STUB */
+}
+
+static void
+tidy_grid_real_lower (ClutterContainer *container,
+                                 ClutterActor *actor,
+                                 ClutterActor *sibling)
+{
+  /* STUB */
+}
+
+static void
+tidy_grid_real_sort_depth_order (ClutterContainer *container)
+{
+  /* STUB */
+}
+
+static void
+tidy_grid_paint (ClutterActor *actor)
+{
+  TidyGrid *layout = (TidyGrid *) actor;
+  TidyGridPrivate *priv = layout->priv;
+  GList *child_item;
+
+  for (child_item = priv->list;
+       child_item != NULL;
+       child_item = child_item->next)
+    {
+      ClutterActor *child = child_item->data;
+
+      g_assert (child != NULL);
+
+      if (CLUTTER_ACTOR_IS_VISIBLE (child))
+        clutter_actor_paint (child);
+    }
+
+}
+
+static void
+tidy_grid_pick (ClutterActor *actor,
+                           const ClutterColor *color)
+{
+  /* Chain up so we get a bounding box pained (if we are reactive) */
+  CLUTTER_ACTOR_CLASS (tidy_grid_parent_class)->pick (actor, color);
+
+  /* Just forward to the paint call which in turn will trigger
+   * the child actors also getting 'picked'.
+   */
+  if (CLUTTER_ACTOR_IS_VISIBLE (actor))
+   tidy_grid_paint (actor);
+}
+
+static void
+tidy_grid_get_preferred_width (ClutterActor *self,
+                                          ClutterUnit for_height,
+                                          ClutterUnit *min_width_p,
+                                          ClutterUnit *natural_width_p)
+{
+  TidyGrid *layout = (TidyGrid *) self;
+  TidyGridPrivate *priv = layout->priv;
+  ClutterUnit natural_width;
+
+  natural_width = CLUTTER_UNITS_FROM_INT (200);
+  if (min_width_p)
+    *min_width_p = natural_width;
+  if (natural_width_p)
+    *natural_width_p = natural_width;
+
+  priv->pref_width = natural_width;
+}
+
+static void
+tidy_grid_get_preferred_height (ClutterActor *self,
+                                ClutterUnit for_width,
+                                ClutterUnit *min_height_p,
+                                ClutterUnit *natural_height_p)
+{
+  TidyGrid *layout = (TidyGrid *) self;
+  TidyGridPrivate *priv = layout->priv;
+  ClutterUnit natural_height;
+
+  natural_height = CLUTTER_UNITS_FROM_INT (200);
+
+  priv->for_width = for_width;
+  priv->pref_height = natural_height;
+
+  if (min_height_p)
+    *min_height_p = natural_height;
+  if (natural_height_p)
+    *natural_height_p = natural_height;
+}
+
+static ClutterUnit
+compute_row_height (GList                    *siblings,
+                    ClutterUnit               best_yet,
+                    ClutterUnit               current_a,
+                    TidyGridPrivate *priv)
+{
+  GList *l;
+
+  gboolean homogenous_a;
+  gboolean homogenous_b;
+  ClutterUnit gap;
+
+  if (priv->column_major)
+    {
+      homogenous_b = priv->homogenous_columns;
+      homogenous_a = priv->homogenous_rows;
+      gap          = priv->row_gap;
+    }
+  else
+    {
+      homogenous_a = priv->homogenous_columns;
+      homogenous_b = priv->homogenous_rows;
+      gap          = priv->column_gap;
+    }
+
+  for (l = siblings; l != NULL; l = l->next)
+    {
+      ClutterActor *child = l->data;
+      ClutterUnit natural_width, natural_height;
+
+      /* each child will get as much space as they require */
+      clutter_actor_get_preferred_size (CLUTTER_ACTOR (child),
+                                        NULL, NULL,
+                                        &natural_width, &natural_height);
+
+      if (priv->column_major)
+        {
+          ClutterUnit temp = natural_height;
+          natural_height = natural_width;
+          natural_width = temp;
+        }
+
+      /* if the primary axis is homogenous, each additional item is the same
+       * width */
+      if (homogenous_a)
+        natural_width = priv->max_extent_a; 
+
+      if (natural_height > best_yet)
+        best_yet = natural_height;
+
+      /* if the child is overflowing, we wrap to next line */
+      if (current_a + natural_width + gap > priv->a_wrap)
+        {
+          return best_yet;
+        }
+      current_a += natural_width + gap;
+    }
+  return best_yet;
+}
+
+
+
+
+static ClutterUnit
+compute_row_start (GList           *siblings,
+                   ClutterUnit      start_x,
+                   TidyGridPrivate *priv)
+{
+  ClutterUnit current_a = start_x;
+  GList *l;
+
+  gboolean homogenous_a;
+  gboolean homogenous_b;
+  ClutterUnit gap;
+
+  if (priv->column_major)
+    {
+      homogenous_b = priv->homogenous_columns;
+      homogenous_a = priv->homogenous_rows;
+      gap          = priv->row_gap;
+    }
+  else
+    {
+      homogenous_a = priv->homogenous_columns;
+      homogenous_b = priv->homogenous_rows;
+      gap          = priv->column_gap;
+    }
+
+  for (l = siblings; l != NULL; l = l->next)
+    {
+      ClutterActor *child = l->data;
+      ClutterUnit natural_width, natural_height;
+
+      /* each child will get as much space as they require */
+      clutter_actor_get_preferred_size (CLUTTER_ACTOR (child),
+                                        NULL, NULL,
+                                        &natural_width, &natural_height);
+
+
+      if (priv->column_major)
+        natural_width = natural_height;
+
+      /* if the primary axis is homogenous, each additional item is the same width */
+      if (homogenous_a)
+        natural_width = priv->max_extent_a; 
+
+      /* if the child is overflowing, we wrap to next line */
+      if (current_a + natural_width + gap > priv->a_wrap)
+        {
+          if (current_a == start_x)
+            return start_x;
+          return (priv->a_wrap - current_a);
+        }
+      current_a += natural_width + gap;
+    }
+  return (priv->a_wrap - current_a);
+}
+
+static void
+tidy_grid_allocate (ClutterActor          *self,
+                              const ClutterActorBox *box,
+                              gboolean               absolute_origin_changed)
+{
+  TidyGrid *layout = (TidyGrid *) self;
+  TidyGridPrivate *priv = layout->priv;
+
+  ClutterUnit current_a;
+  ClutterUnit current_b;
+  ClutterUnit next_b;
+  ClutterUnit agap;
+  ClutterUnit bgap;
+
+  gboolean homogenous_a;
+  gboolean homogenous_b;
+  gdouble  aalign;
+  gdouble  balign;
+ 
+  current_a = current_b = next_b = 0;
+
+  GList *iter;
+
+  /* chain up to set actor->allocation */
+  CLUTTER_ACTOR_CLASS (tidy_grid_parent_class)
+    ->allocate (self, box, absolute_origin_changed);
+
+  priv->alloc_width = box->x2 - box->x1;
+  priv->alloc_height = box->y2 - box->y1;
+  priv->absolute_origin_changed = absolute_origin_changed;
+
+  /* Make sure we have calculated the preferred size */
+  /* what does this do? */
+  clutter_actor_get_preferred_size (self, NULL, NULL, NULL, NULL);
+
+
+  if (priv->column_major)
+    {
+      priv->a_wrap = priv->alloc_height;
+      homogenous_b = priv->homogenous_columns;
+      homogenous_a = priv->homogenous_rows;
+      aalign = priv->valign;
+      balign = priv->halign;
+      agap          = priv->row_gap;
+      bgap          = priv->column_gap;
+    }
+  else
+    {
+      priv->a_wrap = priv->alloc_width;
+      homogenous_a = priv->homogenous_columns;
+      homogenous_b = priv->homogenous_rows;
+      aalign = priv->halign;
+      balign = priv->valign;
+      agap          = priv->column_gap;
+      bgap          = priv->row_gap;
+    }
+
+  priv->max_extent_a = 0;
+  priv->max_extent_b = 0;
+
+  priv->first_of_batch = TRUE;
+  
+  if (homogenous_a ||
+      homogenous_b)
+    {
+      for (iter = priv->list; iter; iter = iter->next)
+        {
+          ClutterActor *child = iter->data;
+          ClutterUnit natural_width;
+          ClutterUnit natural_height;
+
+          /* each child will get as much space as they require */
+          clutter_actor_get_preferred_size (CLUTTER_ACTOR (child),
+                                            NULL, NULL,
+                                            &natural_width, &natural_height);
+          if (natural_width > priv->max_extent_a)
+            priv->max_extent_a = natural_width;
+          if (natural_height > priv->max_extent_b)
+            priv->max_extent_b = natural_width;
+        }
+    }
+
+  if (priv->column_major)
+    {
+      ClutterUnit temp = priv->max_extent_a;
+      priv->max_extent_a = priv->max_extent_b;
+      priv->max_extent_b = temp;
+    }
+
+  for (iter = priv->list; iter; iter=iter->next)
+    {
+      ClutterActor *child = iter->data;
+      ClutterUnit natural_a;
+      ClutterUnit natural_b;
+
+      /* each child will get as much space as they require */
+      clutter_actor_get_preferred_size (CLUTTER_ACTOR (child),
+                                        NULL, NULL,
+                                        &natural_a, &natural_b);
+
+      if (priv->column_major) /* swap axes around if column is major */
+        {
+          ClutterUnit temp = natural_a;
+          natural_a = natural_b;
+          natural_b = temp;
+        }
+
+      /* if the child is overflowing, we wrap to next line */
+      if (current_a + natural_a > priv->a_wrap ||
+          (homogenous_a && current_a + priv->max_extent_a > priv->a_wrap))
+        {
+          current_b = next_b + bgap;
+          current_a = 0;
+          next_b = current_b + bgap;
+          priv->first_of_batch = TRUE;
+        }
+
+      if (priv->end_align &&
+          priv->first_of_batch)
+        {
+          current_a = compute_row_start (iter, current_a, priv);
+          priv->first_of_batch = FALSE;
+        }
+
+      if (next_b-current_b < natural_b)
+          next_b = current_b + natural_b;
+
+        {
+          ClutterUnit     row_height;
+          ClutterActorBox child_box;
+
+          if (homogenous_b)
+            {
+              row_height = priv->max_extent_b;
+            }
+          else
+            {
+              row_height = compute_row_height (iter, next_b-current_b,
+                                               current_a, priv);
+            }
+
+          if (homogenous_a)
+            {
+              child_box.x1 = current_a + (priv->max_extent_a-natural_a) * aalign;
+              child_box.x2 = child_box.x1 + natural_a;
+
+            }
+          else
+            {
+              child_box.x1 = current_a;
+              child_box.x2 = child_box.x1 + natural_a;
+            }
+
+          child_box.y1 = current_b + (row_height-natural_b) * balign;
+          child_box.y2 = child_box.y1 + natural_b;
+
+
+          if (priv->column_major)
+            {
+              ClutterUnit temp = child_box.x1;
+              child_box.x1 = child_box.y1;
+              child_box.y1 = temp;
+
+              temp = child_box.x2;
+              child_box.x2 = child_box.y2;
+              child_box.y2 = temp;
+            }
+
+          /* update the allocation */
+          clutter_actor_allocate (CLUTTER_ACTOR (child),
+                                  &child_box,
+                                  absolute_origin_changed);
+
+          if (homogenous_a)
+            {
+              current_a += priv->max_extent_a + agap;
+            }
+          else
+            {
+              current_a += natural_a + agap;
+            }
+        }
+    }
+}

Added: trunk/src/tidy/tidy-grid.h
==============================================================================
--- (empty file)
+++ trunk/src/tidy/tidy-grid.h	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,99 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Authored By Matthew Allum  <mallum openedhand com>
+ *
+ * Copyright (C) 2008 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __TIDY_GRID_H__
+#define __TIDY_GRID_H__
+
+#include <clutter/clutter-actor.h>
+
+G_BEGIN_DECLS
+
+#define TIDY_TYPE_GRID               (tidy_grid_get_type())
+#define TIDY_GRID(obj)                                       \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj),                        \
+                               TIDY_TYPE_GRID,               \
+                               TidyGrid))
+#define TIDY_GRID_CLASS(klass)                               \
+  (G_TYPE_CHECK_CLASS_CAST ((klass),                         \
+                            TIDY_TYPE_GRID,                  \
+                            TidyGridClass))
+#define TIDY_IS_GRID(obj)                                    \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj),                        \
+                               TIDY_TYPE_GRID))
+#define TIDY_IS_GRID_CLASS(klass)                            \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass),                         \
+                            TIDY_TYPE_GRID))
+#define TIDY_GRID_GET_CLASS(obj)                             \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj),                         \
+                              TIDY_TYPE_GRID,                \
+                              TidyGridClass))
+
+typedef struct _TidyGrid        TidyGrid;
+typedef struct _TidyGridClass   TidyGridClass;
+typedef struct _TidyGridPrivate TidyGridPrivate;
+
+struct _TidyGridClass
+{
+  ClutterActorClass parent_class;
+};
+
+struct _TidyGrid
+{
+  ClutterActor parent;
+
+  TidyGridPrivate *priv;
+};
+
+GType tidy_grid_get_type (void) G_GNUC_CONST;
+
+ClutterActor *tidy_grid_new                    (void);
+void          tidy_grid_set_end_align          (TidyGrid    *self,
+                                                gboolean     value);
+gboolean      tidy_grid_get_end_align          (TidyGrid    *self);
+void          tidy_grid_set_homogenous_rows    (TidyGrid    *self,
+                                                gboolean     value);
+gboolean      tidy_grid_get_homogenous_rows    (TidyGrid    *self);
+void          tidy_grid_set_homogenous_columns (TidyGrid    *self,
+                                                gboolean     value);
+gboolean      tidy_grid_get_homogenous_columns (TidyGrid    *self);
+void          tidy_grid_set_column_major       (TidyGrid    *self,
+                                                gboolean     value);
+gboolean      tidy_grid_get_column_major       (TidyGrid    *self);
+void          tidy_grid_set_row_gap            (TidyGrid    *self,
+                                                ClutterUnit  value);
+ClutterUnit   tidy_grid_get_row_gap            (TidyGrid    *self);
+void          tidy_grid_set_column_gap         (TidyGrid    *self,
+                                                ClutterUnit  value);
+ClutterUnit   tidy_grid_get_column_gap         (TidyGrid    *self);
+void          tidy_grid_set_valign             (TidyGrid    *self,
+                                                gdouble      value);
+gdouble       tidy_grid_get_valign             (TidyGrid    *self);
+void          tidy_grid_set_halign             (TidyGrid    *self,
+                                                gdouble      value);
+gdouble       tidy_grid_get_halign             (TidyGrid    *self);
+
+G_END_DECLS
+
+#endif /* __TIDY_GRID_H__ */

Added: trunk/src/tidy/tidy-marshal.list
==============================================================================
--- (empty file)
+++ trunk/src/tidy/tidy-marshal.list	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,8 @@
+VOID:OBJECT
+VOID:VOID
+VOID:PARAM
+VOID:POINTER
+VOID:UINT
+VOID:UINT,UINT
+VOID:OBJECT,OBJECT
+VOID:STRING,OBJECT

Added: trunk/src/tidy/tidy-private.h
==============================================================================
--- (empty file)
+++ trunk/src/tidy/tidy-private.h	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,40 @@
+/* tidy-private.h: Private declarations
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __TIDY_PRIVATE_H__
+#define __TIDY_PRIVATE_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define I_(str)         (g_intern_static_string ((str)))
+
+#define TIDY_PARAM_READABLE     \
+        (G_PARAM_READABLE |     \
+         G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
+
+#define TIDY_PARAM_READWRITE    \
+        (G_PARAM_READABLE | G_PARAM_WRITABLE | \
+         G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
+
+G_END_DECLS
+
+#endif /* __TIDY_PRIVATE_H__ */

Added: trunk/src/tidy/tidy-stylable.c
==============================================================================
--- (empty file)
+++ trunk/src/tidy/tidy-stylable.c	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,789 @@
+/* tidy-stylable.c: Interface for stylable objects
+ */
+
+/**
+ * SECTION:tidy-stylable
+ * @short_description: Interface for stylable objects
+ *
+ * Stylable objects are classes that can have "style properties", that is
+ * properties that can be changed by attaching a #TidyStyle to them.
+ *
+ * Objects can choose to subclass #TidyActor, and thus inherit all the
+ * #TidyActor style properties; or they can subclass #TidyActor and
+ * reimplement the #TidyStylable interface to add new style properties
+ * specific for them (and their subclasses); or, finally, they can simply
+ * subclass #GObject and implement #TidyStylable to install new properties.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib-object.h>
+#include <gobject/gvaluecollector.h>
+#include <gobject/gobjectnotifyqueue.c>
+
+#include "tidy-marshal.h"
+#include "tidy-private.h"
+#include "tidy-stylable.h"
+
+enum
+{
+  STYLE_SET,
+  STYLE_NOTIFY,
+
+  LAST_SIGNAL
+};
+
+static GObjectNotifyContext property_notify_context = { 0, };
+
+static GParamSpecPool *style_property_spec_pool = NULL;
+
+static GQuark          quark_real_owner         = 0;
+static GQuark          quark_style              = 0;
+
+static guint stylable_signals[LAST_SIGNAL] = { 0, };
+
+static void
+tidy_stylable_notify_dispatcher (GObject     *gobject,
+                                 guint        n_pspecs,
+                                 GParamSpec **pspecs)
+{
+  guint i;
+
+  for (i = 0; i < n_pspecs; i++)
+    g_signal_emit (gobject, stylable_signals[STYLE_NOTIFY],
+                   g_quark_from_string (pspecs[i]->name),
+                   pspecs[i]);
+}
+
+static void
+tidy_stylable_base_finalize (gpointer g_iface)
+{
+  GList *list, *node;
+
+  list = g_param_spec_pool_list_owned (style_property_spec_pool,
+                                       G_TYPE_FROM_INTERFACE (g_iface));
+
+  for (node = list; node; node = node->next)
+    {
+      GParamSpec *pspec = node->data;
+
+      g_param_spec_pool_remove (style_property_spec_pool, pspec);
+      g_param_spec_unref (pspec);
+    }
+
+  g_list_free (list);
+}
+
+static void
+tidy_stylable_base_init (gpointer g_iface)
+{
+  static gboolean initialised = FALSE;
+
+  if (G_UNLIKELY (!initialised))
+    {
+      GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
+
+      initialised = TRUE;
+
+      quark_real_owner = g_quark_from_static_string ("tidy-stylable-real-owner-quark");
+      quark_style = g_quark_from_static_string ("tidy-stylable-style-quark");
+
+      style_property_spec_pool = g_param_spec_pool_new (FALSE);
+
+      property_notify_context.quark_notify_queue = g_quark_from_static_string ("TidyStylable-style-property-notify-queue");
+      property_notify_context.dispatcher = tidy_stylable_notify_dispatcher;
+
+      /**
+       * TidyStylable:style:
+       *
+       * The #TidyStyle attached to a stylable object.
+       */
+      g_object_interface_install_property (g_iface,
+                                           g_param_spec_object ("style",
+                                                                "Style",
+                                                                "A style object",
+                                                                TIDY_TYPE_STYLE,
+                                                                TIDY_PARAM_READWRITE));
+
+      /**
+       * TidyStylable::style-set:
+       * @stylable: the #TidyStylable that received the signal
+       * @old_style: the previously set #TidyStyle for @stylable
+       *
+       * The ::style-set signal is emitted each time the #TidyStyle attached
+       * to @stylable has been changed.
+       */
+      stylable_signals[STYLE_SET] =
+        g_signal_new (I_("style-set"),
+                      iface_type,
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (TidyStylableIface, style_set),
+                      NULL, NULL,
+                      _tidy_marshal_VOID__OBJECT,
+                      G_TYPE_NONE, 1,
+                      TIDY_TYPE_STYLE);
+      stylable_signals[STYLE_NOTIFY] =
+        g_signal_new (I_("style-notify"),
+                      iface_type,
+                      G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED | G_SIGNAL_NO_HOOKS | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (TidyStylableIface, style_notify),
+                      NULL, NULL,
+                      _tidy_marshal_VOID__PARAM,
+                      G_TYPE_NONE, 1,
+                      G_TYPE_PARAM);
+    }
+}
+
+GType
+tidy_stylable_get_type (void)
+{
+  static GType our_type = 0;
+
+  if (G_UNLIKELY (our_type == 0))
+    {
+      GTypeInfo stylable_info = {
+        sizeof (TidyStylableIface),
+        tidy_stylable_base_init,
+        tidy_stylable_base_finalize
+      };
+
+      our_type = g_type_register_static (G_TYPE_INTERFACE,
+                                         I_("TidyStylable"),
+                                         &stylable_info, 0);
+    }
+
+  return our_type;
+}
+
+void
+tidy_stylable_freeze_notify (TidyStylable *stylable)
+{
+  g_return_if_fail (TIDY_IS_STYLABLE (stylable));
+
+  g_object_ref (stylable);
+  g_object_notify_queue_freeze (G_OBJECT (stylable), &property_notify_context);
+  g_object_unref (stylable);
+}
+
+void
+tidy_stylable_thaw_notify (TidyStylable *stylable)
+{
+  GObjectNotifyQueue *nqueue;
+  
+  g_return_if_fail (TIDY_IS_STYLABLE (stylable));
+  
+  g_object_ref (stylable);
+
+  nqueue = g_object_notify_queue_from_object (G_OBJECT (stylable),
+                                              &property_notify_context);
+
+  if (!nqueue || !nqueue->freeze_count)
+    g_warning ("%s: property-changed notification for %s(%p) is not frozen",
+               G_STRFUNC, G_OBJECT_TYPE_NAME (stylable), stylable);
+  else
+    g_object_notify_queue_thaw (G_OBJECT (stylable), nqueue);
+
+  g_object_unref (stylable);
+}
+
+void
+tidy_stylable_notify (TidyStylable *stylable,
+                      const gchar  *property_name)
+{
+  GParamSpec *pspec;
+    
+  g_return_if_fail (TIDY_IS_STYLABLE (stylable));
+  g_return_if_fail (property_name != NULL);
+
+  g_object_ref (stylable);
+
+  pspec = g_param_spec_pool_lookup (style_property_spec_pool,
+                                    property_name,
+                                    G_OBJECT_TYPE (stylable),
+                                    TRUE);
+
+  if (!pspec)
+    g_warning ("%s: object class `%s' has no style property named `%s'",
+               G_STRFUNC,
+               G_OBJECT_TYPE_NAME (stylable),
+               property_name);
+  else
+    {
+      GObjectNotifyQueue *nqueue;
+      
+      nqueue = g_object_notify_queue_freeze (G_OBJECT (stylable),
+                                              &property_notify_context);
+      g_object_notify_queue_add (G_OBJECT (stylable), nqueue, pspec);
+      g_object_notify_queue_thaw (G_OBJECT (stylable), nqueue);
+    }
+
+  g_object_unref (stylable);
+}
+
+/**
+ * tidy_stylable_iface_install_property:
+ * @iface: a #TidyStylableIface
+ * @owner_type: #GType of the style property owner
+ * @pspec: a #GParamSpec
+ *
+ * Installs a property for @owner_type using @pspec as the property
+ * description.
+ *
+ * This function should be used inside the #TidyStylableIface initialization
+ * function of a class, for instance:
+ *
+ * <informalexample><programlisting>
+ * G_DEFINE_TYPE_WITH_CODE (FooActor, foo_actor, CLUTTER_TYPE_ACTOR,
+ *                          G_IMPLEMENT_INTERFACE (TIDY_TYPE_STYLABLE,
+ *                                                 tidy_stylable_init));
+ * ...
+ * static void
+ * tidy_stylable_init (TidyStylableIface *iface)
+ * {
+ *   static gboolean is_initialized = FALSE;
+ *
+ *   if (!is_initialized)
+ *     {
+ *       ...
+ *       tidy_stylable_iface_install_property (stylable,
+ *                                             FOO_TYPE_ACTOR,
+ *                                             g_param_spec_int ("x-spacing",
+ *                                                               "X Spacing",
+ *                                                               "Horizontal spacing",
+ *                                                               -1, G_MAXINT,
+ *                                                               2,
+ *                                                               G_PARAM_READWRITE));
+ *       ...
+ *     }
+ * }
+ * </programlisting></informalexample>
+ */
+void
+tidy_stylable_iface_install_property (TidyStylableIface *iface,
+                                      GType              owner_type,
+                                      GParamSpec        *pspec)
+{
+  g_return_if_fail (TIDY_IS_STYLABLE_IFACE (iface));
+  g_return_if_fail (owner_type != G_TYPE_INVALID);
+  g_return_if_fail (G_IS_PARAM_SPEC (pspec));
+  g_return_if_fail (pspec->flags & G_PARAM_READABLE);
+  g_return_if_fail (!(pspec->flags & (G_PARAM_CONSTRUCT_ONLY | G_PARAM_CONSTRUCT
+)));
+
+  if (g_param_spec_pool_lookup (style_property_spec_pool, pspec->name,
+                                owner_type,
+                                FALSE))
+    {
+      g_warning ("%s: class `%s' already contains a style property named `%s'",
+                 G_STRLOC,
+                 g_type_name (owner_type),
+                 pspec->name);
+      return;
+    }
+
+  g_param_spec_ref_sink (pspec);
+  g_param_spec_set_qdata_full (pspec, quark_real_owner,
+                               g_strdup (g_type_name (owner_type)),
+                               g_free);
+
+  g_param_spec_pool_insert (style_property_spec_pool,
+                            pspec,
+                            owner_type);
+}
+
+/**
+ * tidy_stylable_list_properties:
+ * @stylable: a #TidyStylable
+ * @n_props: return location for the number of properties, or %NULL
+ *
+ * Retrieves all the #GParamSpec<!-- -->s installed by @stylable.
+ *
+ * Return value: an array of #GParamSpec<!-- -->s. Free it with
+ *   g_free() when done.
+ */
+GParamSpec **
+tidy_stylable_list_properties (TidyStylable *stylable,
+                               guint        *n_props)
+{
+  GParamSpec **pspecs = NULL;
+  guint n;
+
+  g_return_val_if_fail (TIDY_IS_STYLABLE (stylable), NULL);
+
+  pspecs = g_param_spec_pool_list (style_property_spec_pool,
+                                   G_OBJECT_TYPE (stylable),
+                                   &n);
+  if (n_props)
+    *n_props = n;
+
+  return pspecs;
+}
+
+/**
+ * tidy_stylable_find_property:
+ * @stylable: a #TidyStylable
+ * @property_name: the name of the property to find
+ *
+ * Finds the #GParamSpec installed by @stylable for the property
+ * with @property_name.
+ *
+ * Return value: a #GParamSpec for the given property, or %NULL if
+ *   no property with that name was found
+ */
+GParamSpec *
+tidy_stylable_find_property (TidyStylable *stylable,
+                             const gchar  *property_name)
+{
+  g_return_val_if_fail (TIDY_IS_STYLABLE (stylable), NULL);
+  g_return_val_if_fail (property_name != NULL, NULL);
+
+  return g_param_spec_pool_lookup (style_property_spec_pool,
+                                   property_name,
+                                   G_OBJECT_TYPE (stylable),
+                                   TRUE);
+}
+
+static inline void
+tidy_stylable_set_property_internal (TidyStylable       *stylable,
+                                     GParamSpec         *pspec,
+                                     const GValue       *value,
+                                     GObjectNotifyQueue *nqueue)
+{
+  GValue tmp_value = { 0, };
+
+  g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+
+  if (!g_value_transform (value, &tmp_value))
+    g_warning ("unable to set property `%s' of type `%s' from value of type `%s'",
+               pspec->name,
+               g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
+               G_VALUE_TYPE_NAME (value));
+  else if (g_param_value_validate (pspec, &tmp_value) &&
+           !(pspec->flags & G_PARAM_LAX_VALIDATION))
+    {
+      gchar *contents = g_strdup_value_contents (value);
+
+      g_warning ("value \"%s\" of type `%s' is invalid or out of range for property `%s' of type `%s'",
+                 contents,
+                 G_VALUE_TYPE_NAME (value),
+                 pspec->name,
+                 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
+      g_free (contents);
+    }
+  else
+    {
+      TidyStyle *style = tidy_stylable_get_style (stylable);
+      gchar *real_name;
+
+      real_name = g_strconcat (g_param_spec_get_qdata (pspec, quark_real_owner),
+                               "::",
+                               pspec->name,
+                               NULL);
+
+      if (!tidy_style_has_property (style, real_name))
+        tidy_style_add_property (style, real_name,
+                                 G_PARAM_SPEC_VALUE_TYPE (pspec));
+
+      tidy_style_set_property (style, real_name, &tmp_value);
+      g_object_notify_queue_add (G_OBJECT (stylable), nqueue, pspec);
+
+      g_free (real_name);
+    }
+
+  g_value_unset (&tmp_value);
+}
+
+static inline void
+tidy_stylable_get_property_internal (TidyStylable *stylable,
+                                     GParamSpec   *pspec,
+                                     GValue       *value)
+{
+  TidyStyle *style;
+  GValue real_value = { 0, };
+  gchar *real_name;
+
+  real_name = g_strconcat (g_param_spec_get_qdata (pspec, quark_real_owner),
+                           "::",
+                           pspec->name,
+                           NULL);
+
+  style = tidy_stylable_get_style (stylable);
+  if (!tidy_style_has_property (style, real_name))
+    {
+      /* the style has no property set, use the default value
+       * from the GParamSpec
+       */
+      g_param_value_set_default (pspec, value);
+      g_free (real_name);
+      return;
+    }
+
+  tidy_style_get_property (style, real_name, &real_value);
+
+  g_value_copy (&real_value, value);
+  g_value_unset (&real_value);
+
+  g_free (real_name);
+}
+
+/**
+ * tidy_stylable_get_property:
+ * @stylable: a #TidyStylable
+ * @property_name: the name of the property
+ * @value: return location for an empty #GValue
+ *
+ * Retrieves the value of @property_name for @stylable, and puts it
+ * into @value.
+ */
+void
+tidy_stylable_get_property (TidyStylable *stylable,
+                            const gchar  *property_name,
+                            GValue       *value)
+{
+  GParamSpec *pspec;
+
+  g_return_if_fail (TIDY_IS_STYLABLE (stylable));
+  g_return_if_fail (property_name != NULL);
+  g_return_if_fail (value != NULL);
+
+  pspec = tidy_stylable_find_property (stylable, property_name);
+  if (!pspec)
+    {
+      g_warning ("Stylable class `%s' doesn't have a property named `%s'",
+                 g_type_name (G_OBJECT_TYPE (stylable)),
+                 property_name);
+      return;
+    }
+
+  if (!(pspec->flags & G_PARAM_READABLE))
+    {
+      g_warning ("Style property `%s' of class `%s' is not readable",
+                 pspec->name,
+                 g_type_name (G_OBJECT_TYPE (stylable)));
+      return;
+    }
+
+  if (G_VALUE_TYPE (value) != G_PARAM_SPEC_VALUE_TYPE (pspec))
+    {
+      g_warning ("Passed value is not of the requested type `%s' for "
+                 "the style property `%s' of class `%s'",
+                 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
+                 pspec->name,
+                 g_type_name (G_OBJECT_TYPE (stylable)));
+      return;
+    }
+
+  tidy_stylable_get_property_internal (stylable, pspec, value);
+}
+
+/**
+ * tidy_stylable_set_property:
+ * @stylable: a #TidyStylable
+ * @property_name: the name of the property to set
+ * @value: an initialized #GValue
+ *
+ * Sets the property @property_name with @value.
+ */
+void
+tidy_stylable_set_property (TidyStylable *stylable,
+                            const gchar  *property_name,
+                            const GValue *value)
+{
+  GObjectNotifyQueue *nqueue;
+  GParamSpec *pspec;
+
+  g_return_if_fail (TIDY_IS_STYLABLE (stylable));
+  g_return_if_fail (property_name != NULL);
+  g_return_if_fail (value != NULL);
+
+  g_object_ref (stylable);
+
+  nqueue = g_object_notify_queue_freeze (G_OBJECT (stylable),
+                                         &property_notify_context);
+
+  pspec = tidy_stylable_find_property (stylable, property_name);
+  if (!pspec)
+    {
+      g_warning ("Stylable class `%s' doesn't have a property named `%s'",
+                 g_type_name (G_OBJECT_TYPE (stylable)),
+                 property_name);
+    }
+  else if (!(pspec->flags & G_PARAM_WRITABLE))
+    {
+      g_warning ("Style property `%s' of class `%s' is not readable",
+                 pspec->name,
+                 g_type_name (G_OBJECT_TYPE (stylable)));
+    }
+  else if (G_VALUE_TYPE (value) != G_PARAM_SPEC_VALUE_TYPE (pspec))
+    {
+      g_warning ("Passed value is not of the requested type `%s' for "
+                 "the style property `%s' of class `%s'",
+                 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
+                 pspec->name,
+                 g_type_name (G_OBJECT_TYPE (stylable)));
+    }
+  else
+    tidy_stylable_set_property_internal (stylable, pspec, value, nqueue);
+
+  g_object_notify_queue_thaw (G_OBJECT (stylable), nqueue);
+  g_object_unref (stylable);
+}
+
+static void
+tidy_stylable_get_valist (TidyStylable *stylable,
+                          const gchar  *first_property_name,
+                          va_list       varargs)
+{
+  const gchar *name;
+
+  g_object_ref (stylable);
+
+  name = first_property_name;
+
+  while (name)
+    {
+      GParamSpec *pspec;
+      GValue value = { 0, };
+      gchar *error;
+
+      pspec = tidy_stylable_find_property (stylable, name);
+      if (!pspec)
+        {
+          g_warning ("%s: no style property named `%s' found for class `%s'",
+                     G_STRLOC,
+                     name,
+                     g_type_name (G_OBJECT_TYPE (stylable)));
+          break;
+        }
+
+      if (!(pspec->flags & G_PARAM_READABLE))
+        {
+          g_warning ("Style property `%s' of class `%s' is not readable",
+                     pspec->name,
+                     g_type_name (G_OBJECT_TYPE (stylable)));
+          break;
+        }
+
+      g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+      tidy_stylable_get_property_internal (stylable, pspec, &value);
+
+      G_VALUE_LCOPY (&value, varargs, 0, &error);
+      if (error)
+        {
+          g_warning ("%s: %s", G_STRLOC, error);
+          g_free (error);
+          g_value_unset (&value);
+          break;
+        }
+
+      g_value_unset (&value);
+
+      name = va_arg (varargs, gchar*);
+    }
+
+  g_object_unref (stylable);
+}
+
+static void
+tidy_stylable_set_valist (TidyStylable *stylable,
+                          const gchar  *first_property_name,
+                          va_list       varargs)
+{
+  GObjectNotifyQueue *nqueue;
+  const gchar *name;
+
+  g_object_ref (stylable);
+
+  nqueue = g_object_notify_queue_freeze (G_OBJECT (stylable),
+                                         &property_notify_context);
+
+  name = first_property_name;
+
+  while (name)
+    {
+      GParamSpec *pspec;
+      GValue value = { 0, };
+      gchar *error;
+
+      pspec = tidy_stylable_find_property (stylable, name);
+      if (!pspec)
+        {
+          g_warning ("%s: no style property named `%s' found for class `%s'",
+                     G_STRLOC,
+                     name,
+                     g_type_name (G_OBJECT_TYPE (stylable)));
+          break;
+        }
+
+      if (!(pspec->flags & G_PARAM_WRITABLE) ||
+          (pspec->flags & G_PARAM_CONSTRUCT_ONLY))
+        {
+          g_warning ("Style property `%s' of class `%s' is not writable",
+                     pspec->name,
+                     g_type_name (G_OBJECT_TYPE (stylable)));
+          break;
+        }
+
+      g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+
+      G_VALUE_COLLECT (&value, varargs, 0, &error);
+      if (error)
+        {
+          g_warning ("%s: %s", G_STRLOC, error);
+          g_free (error);
+          g_value_unset (&value);
+          break;
+        }
+
+      tidy_stylable_set_property_internal (stylable, pspec, &value, nqueue);
+      g_value_unset (&value);
+
+      name = va_arg (varargs, gchar*);
+    }
+
+  g_object_notify_queue_thaw (G_OBJECT (stylable), nqueue);
+  g_object_unref (stylable);
+}
+
+/**
+ * tidy_stylable_get:
+ * @stylable: a #TidyStylable
+ * @first_property_name: name of the first property to get
+ * @Varargs: return location for the first property, followed optionally
+ *   by more name/return location pairs, followed by %NULL
+ *
+ * Gets the style properties for @stylable.
+ *
+ * In general, a copy is made of the property contents and the called
+ * is responsible for freeing the memory in the appropriate manner for
+ * the property type.
+ *
+ * <example>
+ * <title>Using tidy_stylable_get(<!-- -->)</title>
+ * <para>An example of using tidy_stylable_get() to get the contents of
+ * two style properties - one of type #G_TYPE_INT and one of type
+ * #CLUTTER_TYPE_COLOR:</para>
+ * <programlisting>
+ *   gint x_spacing;
+ *   ClutterColor *bg_color;
+ *
+ *   tidy_stylable_get (stylable,
+ *                      "x-spacing", &amp;x_spacing,
+ *                      "bg-color", &amp;bg_color,
+ *                      NULL);
+ *
+ *   /<!-- -->* do something with x_spacing and bg_color *<!-- -->/
+ *
+ *   clutter_color_free (bg_color);
+ * </programlisting>
+ * </example>
+ */
+void
+tidy_stylable_get (TidyStylable *stylable,
+                   const gchar  *first_property_name,
+                                 ...)
+{
+  va_list args;
+
+  g_return_if_fail (TIDY_IS_STYLABLE (stylable));
+  g_return_if_fail (first_property_name != NULL);
+
+  va_start (args, first_property_name);
+  tidy_stylable_get_valist (stylable, first_property_name, args);
+  va_end (args);
+}
+
+/**
+ * tidy_stylable_set:
+ * @stylable: a #TidyStylable
+ * @first_property_name: name of the first property to set
+ * @Varargs: value for the first property, followed optionally by
+ *   more name/value pairs, followed by %NULL
+ *
+ * Sets the style properties of @stylable.
+ */
+void
+tidy_stylable_set (TidyStylable *stylable,
+                   const gchar  *first_property_name,
+                   ...)
+{
+  va_list args;
+
+  g_return_if_fail (TIDY_IS_STYLABLE (stylable));
+  g_return_if_fail (first_property_name != NULL);
+
+  va_start (args, first_property_name);
+  tidy_stylable_set_valist (stylable, first_property_name, args);
+  va_end (args);
+}
+
+/**
+ * tidy_stylable_get_style:
+ * @stylable: a #TidyStylable
+ *
+ * Retrieves the #TidyStyle used by @stylable. This function does not
+ * alter the reference count of the returned object.
+ *
+ * Return value: a #TidyStyle
+ */
+TidyStyle *
+tidy_stylable_get_style (TidyStylable *stylable)
+{
+  TidyStylableIface *iface;
+
+  g_return_val_if_fail (TIDY_IS_STYLABLE (stylable), NULL);
+
+  iface = TIDY_STYLABLE_GET_IFACE (stylable);
+  if (iface->get_style)
+    return iface->get_style (stylable);
+
+  return g_object_get_data (G_OBJECT (stylable), "tidy-stylable-style");
+}
+
+/**
+ * tidy_stylable_set_style:
+ * @stylable: a #TidyStylable
+ * @style: a #TidyStyle
+ *
+ * Sets @style as the new #TidyStyle to be used by @stylable.
+ *
+ * The #TidyStylable will take ownership of the passed #TidyStyle.
+ *
+ * After the #TidyStle has been set, the TidyStylable::style-set signal
+ * will be emitted.
+ */
+void
+tidy_stylable_set_style (TidyStylable *stylable,
+                         TidyStyle    *style)
+{
+  TidyStylableIface *iface;
+  TidyStyle *old_style;
+
+  g_return_if_fail (TIDY_IS_STYLABLE (stylable));
+  g_return_if_fail (TIDY_IS_STYLE (style));
+
+  iface = TIDY_STYLABLE_GET_IFACE (stylable);
+
+  old_style = tidy_stylable_get_style (stylable);
+  g_object_ref (old_style);
+
+  if (iface->set_style)
+    iface->set_style (stylable, style);
+  else
+    {
+      g_object_set_qdata_full (G_OBJECT (stylable),
+                               quark_style,
+                               g_object_ref_sink (style),
+                               g_object_unref);
+    }
+
+  g_signal_emit (stylable, stylable_signals[STYLE_SET], 0, old_style);
+  g_object_unref (old_style);
+
+  g_object_notify (G_OBJECT (stylable), "style");
+}

Added: trunk/src/tidy/tidy-stylable.h
==============================================================================
--- (empty file)
+++ trunk/src/tidy/tidy-stylable.h	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,69 @@
+#ifndef __TIDY_STYLABLE_H__
+#define __TIDY_STYLABLE_H__
+
+#include <glib-object.h>
+#include <tidy/tidy-style.h>
+
+G_BEGIN_DECLS
+
+#define TIDY_TYPE_STYLABLE              (tidy_stylable_get_type ())
+#define TIDY_STYLABLE(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIDY_TYPE_STYLABLE, TidyStylable))
+#define TIDY_IS_STYLABLE(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIDY_TYPE_STYLABLE))
+#define TIDY_STYLABLE_IFACE(iface)      (G_TYPE_CHECK_CLASS_CAST ((iface), TIDY_TYPE_STYLABLE, TidyStylableIface))
+#define TIDY_IS_STYLABLE_IFACE(iface)   (G_TYPE_CHECK_CLASS_TYPE ((iface), TIDY_TYPE_STYLABLE))
+#define TIDY_STYLABLE_GET_IFACE(obj)    (G_TYPE_INSTANCE_GET_INTERFACE ((obj), TIDY_TYPE_STYLABLE, TidyStylableIface))
+
+typedef struct _TidyStylable            TidyStylable; /* dummy typedef */
+typedef struct _TidyStylableIface       TidyStylableIface;
+
+struct _TidyStylableIface
+{
+  GTypeInterface g_iface;
+
+  /* virtual functions */
+  TidyStyle *(* get_style) (TidyStylable *stylable);
+  void       (* set_style) (TidyStylable *stylable,
+                            TidyStyle    *style);
+
+  /* signals, not vfuncs */
+  void (* style_notify) (TidyStylable *stylable,
+                         GParamSpec   *pspec);
+
+  void (* style_set)    (TidyStylable *stylable,
+                         TidyStyle    *old_style);
+};
+
+GType        tidy_stylable_get_type               (void) G_GNUC_CONST;
+
+void         tidy_stylable_iface_install_property (TidyStylableIface *iface,
+                                                   GType              owner_type,
+                                                   GParamSpec        *pspec);
+
+void         tidy_stylable_freeze_notify          (TidyStylable      *stylable);
+void         tidy_stylable_notify                 (TidyStylable      *stylable,
+                                                   const gchar       *property_name);
+void         tidy_stylable_thaw_notify            (TidyStylable      *stylable);
+GParamSpec **tidy_stylable_list_properties        (TidyStylable      *stylable,
+                                                   guint             *n_props);
+GParamSpec * tidy_stylable_find_property          (TidyStylable      *stylable,
+                                                   const gchar       *property_name);
+void         tidy_stylable_set_style              (TidyStylable      *stylable,
+                                                   TidyStyle         *style);
+TidyStyle *  tidy_stylable_get_style              (TidyStylable      *stylable);
+
+void         tidy_stylable_set                    (TidyStylable      *stylable,
+                                                   const gchar       *first_property_name,
+                                                   ...) G_GNUC_NULL_TERMINATED;
+void         tidy_stylable_get                    (TidyStylable      *stylable,
+                                                   const gchar       *first_property_name,
+                                                   ...) G_GNUC_NULL_TERMINATED;
+void         tidy_stylable_set_property           (TidyStylable      *stylable,
+                                                   const gchar       *property_name,
+                                                   const GValue      *value);
+void         tidy_stylable_get_property           (TidyStylable      *stylable,
+                                                   const gchar       *property_name,
+                                                   GValue            *value);
+
+G_END_DECLS
+
+#endif /* __TIDY_STYLABLE_H__ */

Added: trunk/src/tidy/tidy-style.c
==============================================================================
--- (empty file)
+++ trunk/src/tidy/tidy-style.c	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,653 @@
+#ifndef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib-object.h>
+#include <gobject/gvaluecollector.h>
+
+#include <clutter/clutter-behaviour.h>
+#include <clutter/clutter-color.h>
+
+#include "tidy-style.h"
+#include "tidy-marshal.h"
+#include "tidy-debug.h"
+
+enum
+{
+  CHANGED,
+
+  LAST_SIGNAL
+};
+
+#define TIDY_STYLE_GET_PRIVATE(obj) \
+        (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TIDY_TYPE_STYLE, TidyStylePrivate))
+
+typedef struct {
+  GType value_type;
+  gchar *value_name;
+  GValue value;
+} StyleProperty;
+
+typedef struct {
+  gchar *name;
+  GType behaviour_type;
+  GArray *parameters;
+  guint duration;
+  ClutterAlphaFunc alpha_func;
+} StyleEffect;
+
+struct _TidyStylePrivate
+{
+  GHashTable *properties;
+  GHashTable *effects;
+};
+
+static guint style_signals[LAST_SIGNAL] = { 0, };
+
+static const gchar *tidy_default_font_name          = "Sans 12px";
+static const ClutterColor tidy_default_text_color   = { 0x00, 0x00, 0x00, 0xff };
+static const ClutterColor tidy_default_bg_color     = { 0xcc, 0xcc, 0xcc, 0xff };
+static const ClutterColor tidy_default_active_color = { 0xf5, 0x79, 0x00, 0xff };
+
+static TidyStyle *default_style = NULL;
+
+G_DEFINE_TYPE (TidyStyle, tidy_style, G_TYPE_OBJECT);
+
+static StyleProperty *
+style_property_new (const gchar *value_name,
+                    GType        value_type)
+{
+  StyleProperty *retval;
+
+  retval = g_slice_new0 (StyleProperty);
+  retval->value_type = value_type;
+  retval->value_name = g_strdup (value_name);
+  g_value_init (&retval->value, value_type);
+
+  return retval;
+}
+
+static void
+style_property_free (gpointer data)
+{
+  if (G_LIKELY (data))
+    {
+      StyleProperty *sp = data;
+
+      g_free (sp->value_name);
+      g_value_unset (&sp->value);
+    }
+}
+
+static StyleEffect *
+style_effect_new (const gchar *name)
+{
+  StyleEffect *retval;
+
+  retval = g_slice_new0 (StyleEffect);
+  retval->name = g_strdup (name);
+  retval->behaviour_type = G_TYPE_INVALID;
+
+  return retval;
+}
+
+static void
+style_effect_free (gpointer data)
+{
+  if (G_LIKELY (data))
+    {
+      StyleEffect *effect = data;
+
+      g_free (effect->name);
+
+      if (effect->parameters)
+        {
+          gint i;
+
+          for (i = 0; i < effect->parameters->len; i++)
+            {
+              GParameter *param;
+
+              param = &g_array_index (effect->parameters, GParameter, i);
+
+              g_free ((gchar *) param->name);
+              g_value_unset (&param->value);
+            }
+
+          g_array_free (effect->parameters, TRUE);
+          effect->parameters = NULL;
+        }
+
+      g_slice_free (StyleEffect, effect);
+    }
+}
+
+static void
+init_defaults (TidyStyle *style)
+{
+  TidyStylePrivate *priv = style->priv;
+  
+  {
+    StyleProperty *sp;
+
+    sp = style_property_new (TIDY_FONT_NAME, G_TYPE_STRING);
+    g_value_set_string (&sp->value, tidy_default_font_name);
+
+    g_hash_table_insert (priv->properties, sp->value_name, sp);
+  }
+
+  {
+    StyleProperty *sp;
+
+    sp = style_property_new (TIDY_BACKGROUND_COLOR, CLUTTER_TYPE_COLOR);
+    g_value_set_boxed (&sp->value, &tidy_default_bg_color);
+
+    g_hash_table_insert (priv->properties, sp->value_name, sp);
+  }
+
+  {
+    StyleProperty *sp;
+
+    sp = style_property_new (TIDY_ACTIVE_COLOR, CLUTTER_TYPE_COLOR);
+    g_value_set_boxed (&sp->value, &tidy_default_active_color);
+
+    g_hash_table_insert (priv->properties, sp->value_name, sp);
+  }
+
+  {
+    StyleProperty *sp;
+
+    sp = style_property_new (TIDY_TEXT_COLOR, CLUTTER_TYPE_COLOR);
+    g_value_set_boxed (&sp->value, &tidy_default_text_color);
+
+    g_hash_table_insert (priv->properties, sp->value_name, sp);
+  }
+}
+
+static gboolean
+tidy_style_load_from_file (TidyStyle    *style,
+                           const gchar  *filename,
+                           GError      **error)
+{
+  GKeyFile *rc_file;
+  GError *internal_error;
+
+  rc_file = g_key_file_new ();
+  
+  internal_error = NULL;
+  g_key_file_load_from_file (rc_file, filename, 0, &internal_error);
+  if (internal_error)
+    {
+      g_key_file_free (rc_file);
+      /* if the specified files does not exist then just ignore it
+       * and fall back to the default values; if, instead, the file
+       * is not accessible or is malformed, propagate the error
+       */
+      if (internal_error->domain == G_FILE_ERROR &&
+          internal_error->code == G_FILE_ERROR_NOENT)
+        {
+          g_error_free (internal_error);
+          return TRUE;
+        }
+
+      g_propagate_error (error, internal_error);
+      return FALSE;
+    }
+
+  g_key_file_free (rc_file);
+
+  return TRUE;
+}
+
+static void
+tidy_style_load (TidyStyle *style)
+{
+  const gchar *env_var;
+  gchar *rc_file = NULL;
+  GError *error;
+  
+  init_defaults (style);
+
+  env_var = g_getenv ("TIDY_RC_FILE");
+  if (env_var && *env_var)
+    rc_file = g_strdup (env_var);
+  
+  if (!rc_file)
+    rc_file = g_build_filename (g_get_user_config_dir (),
+                                "tidy",
+                                "tidyrc",
+                                NULL);
+
+  error = NULL;
+  if (!tidy_style_load_from_file (style, rc_file, &error))
+    {
+      g_critical ("Unable to load resource file `%s': %s",
+                  rc_file,
+                  error->message);
+      g_error_free (error);
+    }
+  
+  g_free (rc_file);
+}
+
+static void
+tidy_style_finalize (GObject *gobject)
+{
+  TidyStylePrivate *priv = TIDY_STYLE (gobject)->priv;
+
+  g_hash_table_destroy (priv->properties);
+  g_hash_table_destroy (priv->effects);
+
+  G_OBJECT_CLASS (tidy_style_parent_class)->finalize (gobject);
+}
+
+static void
+tidy_style_class_init (TidyStyleClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (TidyStylePrivate));
+
+  gobject_class->finalize = tidy_style_finalize;
+
+  style_signals[CHANGED] =
+    g_signal_new ("changed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (TidyStyleClass, changed),
+                  NULL, NULL,
+                  _tidy_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+}
+
+static void
+tidy_style_init (TidyStyle *style)
+{
+  TidyStylePrivate *priv;
+
+  style->priv = priv = TIDY_STYLE_GET_PRIVATE (style);
+
+  priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                            NULL,
+                                            style_property_free);
+  priv->effects = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                         NULL,
+                                         style_effect_free);
+
+  tidy_style_load (style);
+}
+
+/* need to unref */
+TidyStyle *
+tidy_style_new (void)
+{
+  return g_object_new (TIDY_TYPE_STYLE, NULL);
+}
+
+/* never ref/unref */
+TidyStyle *
+tidy_style_get_default (void)
+{
+  if (G_LIKELY (default_style))
+    return default_style;
+
+  default_style = g_object_new (TIDY_TYPE_STYLE, NULL);
+  
+  return default_style;
+}
+
+static StyleProperty *
+tidy_style_find_property (TidyStyle   *style,
+                          const gchar *property_name)
+{
+  return g_hash_table_lookup (style->priv->properties, property_name);
+}
+
+gboolean
+tidy_style_has_property (TidyStyle   *style,
+                         const gchar *property_name)
+{
+  g_return_val_if_fail (TIDY_IS_STYLE (style), FALSE);
+  g_return_val_if_fail (property_name != NULL, FALSE);
+
+  return (tidy_style_find_property (style, property_name) != NULL);
+}
+
+void
+tidy_style_add_property (TidyStyle   *style,
+                         const gchar *property_name,
+                         GType        property_type)
+{
+  StyleProperty *property;
+
+  g_return_if_fail (TIDY_IS_STYLE (style));
+  g_return_if_fail (property_name != NULL);
+  g_return_if_fail (property_type != G_TYPE_INVALID);
+
+  property = tidy_style_find_property (style, property_name);
+  if (G_UNLIKELY (property))
+    {
+      g_warning ("A property named `%s', with type %s already exists.",
+                 property->value_name,
+                 g_type_name (property->value_type));
+      return;
+    }
+
+  property = style_property_new (property_name, property_type);
+  g_hash_table_insert (style->priv->properties, property->value_name, property);
+
+  g_signal_emit (style, style_signals[CHANGED], 0);
+}
+
+void
+tidy_style_get_property (TidyStyle   *style,
+                         const gchar *property_name,
+                         GValue      *value)
+{
+  StyleProperty *property;
+
+  g_return_if_fail (TIDY_IS_STYLE (style));
+  g_return_if_fail (property_name != NULL);
+  g_return_if_fail (value != NULL);
+
+  property = tidy_style_find_property (style, property_name);
+  if (!property)
+    {
+      g_warning ("No style property named `%s' found.", property_name);
+      return;
+    }
+
+  g_value_init (value, property->value_type);
+  g_value_copy (&property->value, value);
+}
+
+void
+tidy_style_set_property (TidyStyle    *style,
+                         const gchar  *property_name,
+                         const GValue *value)
+{
+  StyleProperty *property;
+
+  g_return_if_fail (TIDY_IS_STYLE (style));
+  g_return_if_fail (property_name != NULL);
+  g_return_if_fail (value != NULL);
+
+  property = tidy_style_find_property (style, property_name);
+  if (!property)
+    {
+      g_warning ("No style property named `%s' found.", property_name);
+      return;
+    }
+
+  g_value_copy (value, &property->value);
+
+  g_signal_emit (style, style_signals[CHANGED], 0);
+}
+
+static StyleEffect *
+tidy_style_find_effect (TidyStyle   *style,
+                        const gchar *effect_name)
+{
+  return g_hash_table_lookup (style->priv->effects, effect_name);
+}
+
+void
+tidy_style_add_effect (TidyStyle   *style,
+                       const gchar *effect_name)
+{
+  StyleEffect *effect;
+
+  effect = tidy_style_find_effect (style, effect_name);
+  if (G_UNLIKELY (effect))
+    {
+      g_warning ("An effect named `%s', with type %s already exists.",
+                 effect->name,
+                 g_type_name (effect->behaviour_type));
+      return;
+    }
+
+  effect = style_effect_new (effect_name);
+  g_hash_table_replace (style->priv->effects,
+                        effect->name,
+                        effect);
+}
+
+gboolean
+tidy_style_has_effect (TidyStyle   *style,
+                       const gchar *effect_name)
+{
+  g_return_val_if_fail (TIDY_IS_STYLE (style), FALSE);
+  g_return_val_if_fail (effect_name != NULL, FALSE);
+
+  return (tidy_style_find_effect (style, effect_name) != NULL);
+}
+
+static void
+tidy_style_set_effect_valist (TidyStyle   *style,
+                              StyleEffect *effect,
+                              const gchar *first_property_name,
+                              va_list      varargs)
+{
+  GObjectClass *klass;
+  const gchar *name;
+
+  klass = g_type_class_ref (effect->behaviour_type);
+  if (G_UNLIKELY (!klass))
+    return;
+
+  name = first_property_name;
+  while (name)
+    {
+      GParamSpec *pspec;
+      GParameter param = { 0, };
+      GValue value = { 0, };
+      gchar *error = NULL;
+
+      pspec = g_object_class_find_property (klass, name);
+      if (!pspec)
+        {
+          g_warning ("Unable to find the property `%s' for the "
+                     "behaviour of type `%s'",
+                     name,
+                     g_type_name (effect->behaviour_type));
+          break;
+        }
+
+      if (!(pspec->flags & G_PARAM_WRITABLE))
+        {
+          g_warning ("The property `%s' for the behaviour of type "
+                     "`%s' is not writable",
+                     pspec->name,
+                     g_type_name (effect->behaviour_type));
+          break;
+        }
+
+      param.name = g_strdup (pspec->name);
+
+      g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+
+      G_VALUE_COLLECT (&value, varargs, 0, &error);
+      if (error)
+        {
+          g_warning ("%s: %s", G_STRLOC, error);
+          g_free (error);
+          g_value_unset (&value);
+          break;
+        }
+
+      g_value_init (&(param.value), G_PARAM_SPEC_VALUE_TYPE (pspec));
+      g_value_copy (&value, &(param.value));
+      g_value_unset (&value);
+
+      name = va_arg (varargs, gchar*);
+    }
+
+  g_type_class_unref (klass);
+}
+
+void
+tidy_style_set_effect  (TidyStyle        *style,
+                        const gchar      *effect_name,
+                        guint             duration,
+                        GType             behaviour_type,
+                        ClutterAlphaFunc  alpha_func,
+                        const gchar      *first_property_name,
+                        ...)
+{
+  StyleEffect *effect;
+  va_list args;
+
+  effect = tidy_style_find_effect (style, effect_name);
+  if (!effect)
+    {
+      g_warning ("No effect named `%s' found.", effect_name);
+      return;
+    }
+
+  if (effect->parameters)
+    {
+      gint i;
+
+      for (i = 0; i < effect->parameters->len; i++)
+        {
+          GParameter *param;
+
+          param = &g_array_index (effect->parameters, GParameter, i);
+
+          g_free ((gchar *) param->name);
+          g_value_unset (&param->value);
+        }
+
+      g_array_free (effect->parameters, TRUE);
+      effect->parameters = NULL;
+    }
+
+  effect->duration = duration;
+  effect->behaviour_type = behaviour_type;
+  effect->alpha_func = alpha_func;
+  effect->parameters = g_array_new (FALSE, FALSE, sizeof (GParameter));
+
+  va_start (args, first_property_name);
+  tidy_style_set_effect_valist (style, effect, first_property_name, args);
+  va_end (args);
+}
+
+void
+tidy_style_set_effectv (TidyStyle        *style,
+                        const gchar      *effect_name,
+                        guint             duration,
+                        GType             behaviour_type,
+                        ClutterAlphaFunc  alpha_func,
+                        guint             n_parameters,
+                        GParameter       *parameters)
+{
+  StyleEffect *effect;
+  gint i;
+
+  effect = tidy_style_find_effect (style, effect_name);
+  if (!effect)
+    {
+      g_warning ("No effect named `%s' found.", effect_name);
+      return;
+    }
+
+  if (effect->parameters)
+    {
+      gint i;
+
+      for (i = 0; i < effect->parameters->len; i++)
+        {
+          GParameter *param;
+
+          param = &g_array_index (effect->parameters, GParameter, i);
+
+          g_free ((gchar *) param->name);
+          g_value_unset (&param->value);
+        }
+
+      g_array_free (effect->parameters, TRUE);
+      effect->parameters = NULL;
+    }
+
+  effect->duration = duration;
+  effect->behaviour_type = behaviour_type;
+  effect->alpha_func = alpha_func;
+  effect->parameters = g_array_new (FALSE, FALSE, sizeof (GParameter));
+
+  for (i = 0; i < n_parameters; i++)
+    {
+      GParameter param = { NULL, };
+
+      param.name = g_strdup (parameters[i].name);
+
+      g_value_init (&param.value, G_VALUE_TYPE (&parameters[i].value));
+      g_value_copy (&parameters[i].value, &param.value);
+
+      g_array_append_val (effect->parameters, param);
+    }
+}
+
+static ClutterBehaviour *
+tidy_style_construct_effect (TidyStyle   *style,
+                             const gchar *effect_name)
+{
+  ClutterTimeline *timeline;
+  ClutterAlpha *alpha;
+  ClutterBehaviour *behaviour;
+  StyleEffect *effect;
+
+  effect = tidy_style_find_effect (style, effect_name);
+  if (!effect)
+    {
+      g_warning ("No effect named `%s' found.", effect_name);
+      return NULL;
+    }
+
+  timeline = clutter_timeline_new_for_duration (effect->duration);
+
+  alpha = clutter_alpha_new_full (timeline, effect->alpha_func, NULL, NULL);
+  g_object_unref (timeline);
+
+  behaviour = g_object_newv (effect->behaviour_type,
+                             effect->parameters->len,
+                             (GParameter *) effect->parameters->data);
+
+  clutter_behaviour_set_alpha (behaviour, alpha);
+
+  /* we just unref the behaviour, which will take care of cleaning
+   * up everything (alpha+timeline)
+   */
+  g_signal_connect_swapped (timeline,
+                            "completed", G_CALLBACK (g_object_unref),
+                            behaviour);
+
+  return behaviour;
+}
+
+ClutterTimeline *
+tidy_style_get_effect (TidyStyle    *style,
+                       const gchar  *effect_name,
+                       ClutterActor *actor)
+{
+  ClutterBehaviour *behaviour;
+  ClutterAlpha *alpha;
+  ClutterTimeline *timeline;
+
+  g_return_val_if_fail (TIDY_IS_STYLE (style), NULL);
+  g_return_val_if_fail (effect_name != NULL, NULL);
+  g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
+
+  behaviour = tidy_style_construct_effect (style, effect_name);
+  if (!behaviour)
+    return NULL;
+
+  clutter_behaviour_apply (behaviour, actor);
+
+  alpha = clutter_behaviour_get_alpha (behaviour);
+  timeline = clutter_alpha_get_timeline (alpha);
+
+  return timeline;
+}

Added: trunk/src/tidy/tidy-style.h
==============================================================================
--- (empty file)
+++ trunk/src/tidy/tidy-style.h	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,85 @@
+#ifndef __TIDY_STYLE_H__
+#define __TIDY_STYLE_H__
+
+#include <glib-object.h>
+#include <clutter/clutter-actor.h>
+#include <clutter/clutter-alpha.h>
+#include <clutter/clutter-timeline.h>
+
+G_BEGIN_DECLS
+
+#define TIDY_TYPE_STYLE                (tidy_style_get_type ())
+#define TIDY_STYLE(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIDY_TYPE_STYLE, TidyStyle))
+#define TIDY_IS_STYLE(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIDY_TYPE_STYLE))
+#define TIDY_STYLE_CLASS(klass)        (G_TYPE_CHECK_CLASS_CAST ((klass), TIDY_TYPE_STYLE, TidyStyleClass))
+#define TIDY_IS_STYLE_CLASS(klass)     (G_TYPE_CHECK_CLASS_TYPE ((klass), TIDY_TYPE_STYLE))
+#define TIDY_STYLE_GET_CLASS(obj)      (G_TYPE_INSTANCE_GET_CLASS ((obj), TIDY_TYPE_STYLE, TidyStyleClass))
+
+/* Default properties */
+#define TIDY_FONT_NAME                  "TidyActor::font-name"
+#define TIDY_BACKGROUND_COLOR           "TidyActor::bg-color"
+#define TIDY_ACTIVE_COLOR               "TidyActor::active-color"
+#define TIDY_TEXT_COLOR                 "TidyActor::text-color"
+
+typedef struct _TidyStyle              TidyStyle;
+typedef struct _TidyStylePrivate       TidyStylePrivate;
+typedef struct _TidyStyleClass         TidyStyleClass;
+
+struct _TidyStyle
+{
+  GObject parent_instance;
+
+  TidyStylePrivate *priv;
+};
+
+struct _TidyStyleClass
+{
+  GObjectClass parent_class;
+
+  void (* changed) (TidyStyle *style);
+};
+
+GType            tidy_style_get_type     (void) G_GNUC_CONST;
+
+TidyStyle *      tidy_style_get_default  (void);
+TidyStyle *      tidy_style_new          (void);
+
+gboolean         tidy_style_has_property (TidyStyle        *style,
+                                          const gchar      *property_name);
+gboolean         tidy_style_has_effect   (TidyStyle        *style,
+                                          const gchar      *effect_name);
+void             tidy_style_add_property (TidyStyle        *style,
+                                          const gchar      *property_name,
+                                          GType             property_type);
+void             tidy_style_add_effect   (TidyStyle        *style,
+                                          const gchar      *effect_name);
+
+void             tidy_style_get_property (TidyStyle        *style,
+                                          const gchar      *property_name,
+                                          GValue           *value);
+void             tidy_style_set_property (TidyStyle        *style,
+                                          const gchar      *property_name,
+                                          const GValue     *value);
+
+ClutterTimeline *tidy_style_get_effect   (TidyStyle        *style,
+                                          const gchar      *effect_name,
+                                          ClutterActor     *actor);
+void             tidy_style_set_effectv  (TidyStyle        *style,
+                                          const gchar      *effect_name,
+                                          guint             duration,
+                                          GType             behaviour_type,
+                                          ClutterAlphaFunc  alpha_func,
+                                          guint             n_parameters,
+                                          GParameter       *parameters);
+void             tidy_style_set_effect   (TidyStyle        *style,
+                                          const gchar      *effect_name,
+                                          guint             duration,
+                                          GType             behaviour_type,
+                                          ClutterAlphaFunc  alpha_func,
+                                          const gchar      *first_property_name,
+                                          ...) G_GNUC_NULL_TERMINATED;
+                                          
+
+G_END_DECLS
+
+#endif /* __TIDY_STYLE_H__ */

Added: trunk/src/tidy/tidy-types.h
==============================================================================
--- (empty file)
+++ trunk/src/tidy/tidy-types.h	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,25 @@
+#ifndef __TIDY_TYPES_H__
+#define __TIDY_TYPES_H__
+
+#include <glib-object.h>
+#include <clutter/clutter-units.h>
+
+G_BEGIN_DECLS
+
+#define TIDY_TYPE_PADDING               (tidy_padding_get_type ())
+
+typedef struct _TidyPadding             TidyPadding;
+
+struct _TidyPadding
+{
+  ClutterUnit top;
+  ClutterUnit right;
+  ClutterUnit bottom;
+  ClutterUnit left;
+};
+
+GType tidy_padding_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __TIDY_TYPES_H__ */

Added: trunk/src/tidy/tidy-util.c
==============================================================================
--- (empty file)
+++ trunk/src/tidy/tidy-util.c	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,37 @@
+#include "tidy-util.h"
+
+/*  Hack to (mostly) fill glyph cache, useful on MBX.
+ * 
+ *  FIXME: untested 
+*/
+void
+tidy_util_preload_glyphs (char *font, ...)
+{
+  va_list args;
+
+  va_start (args, font);
+
+  while (font)
+    {
+      /* Hold on to your hat.. */
+      ClutterActor *foo;
+      ClutterColor  text_color = { 0xff, 0xff, 0xff, 0xff };
+
+      foo = clutter_label_new_full 
+	                (font, 
+			 "abcdefghijklmnopqrstuvwxyz"
+			 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+			 "1234567890&()*.,';:-_+=[]{}# ?><\"!`%\\|/ ", 
+			 &text_color);
+      if (foo)
+	{
+	  clutter_actor_realize(foo);
+	  clutter_actor_paint(foo);
+	  g_object_unref (foo);
+	}
+
+      font = va_arg (args, char*);
+    }
+
+  va_end (args);
+}

Added: trunk/src/tidy/tidy-util.h
==============================================================================
--- (empty file)
+++ trunk/src/tidy/tidy-util.h	Wed Nov 12 21:09:27 2008
@@ -0,0 +1,9 @@
+#ifndef _TIDY_UTIL
+#define _TIDY_UTIL
+
+#include <clutter/clutter.h>
+
+void
+tidy_util_preload_glyphs (char *font, ...);
+
+#endif



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