[gnome-shell/nbtk-introduction: 1/8] Import nbtk core



commit 9f79296276fa30764a435de131001c88b8c2ec7e
Author: Colin Walters <walters verbum org>
Date:   Tue Sep 8 15:47:30 2009 -0400

    Import nbtk core
    
    Import the core NbtkWidget/NbtkBin and their dependencies.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=591245

 configure.ac                  |    1 +
 src/Makefile-nbtk.am          |  101 +++
 src/Makefile.am               |   35 +-
 src/nbtk/nbtk-bin.c           |  783 ++++++++++++++++++++++++
 src/nbtk/nbtk-bin.h           |   92 +++
 src/nbtk/nbtk-enum-types.c.in |   30 +
 src/nbtk/nbtk-enum-types.h.in |   29 +
 src/nbtk/nbtk-marshal.list    |   12 +
 src/nbtk/nbtk-private.c       |  111 ++++
 src/nbtk/nbtk-private.h       |   49 ++
 src/nbtk/nbtk-stylable.c      |  849 ++++++++++++++++++++++++++
 src/nbtk/nbtk-stylable.h      |  123 ++++
 src/nbtk/nbtk-style.c         |  742 ++++++++++++++++++++++
 src/nbtk/nbtk-style.h         |   94 +++
 src/nbtk/nbtk-subtexture.c    |  575 +++++++++++++++++
 src/nbtk/nbtk-subtexture.h    |   92 +++
 src/nbtk/nbtk-texture-cache.c |  456 ++++++++++++++
 src/nbtk/nbtk-texture-cache.h |   94 +++
 src/nbtk/nbtk-texture-frame.c |  620 +++++++++++++++++++
 src/nbtk/nbtk-texture-frame.h |   92 +++
 src/nbtk/nbtk-tooltip.c       |  710 +++++++++++++++++++++
 src/nbtk/nbtk-tooltip.h       |   79 +++
 src/nbtk/nbtk-types.h         |   83 +++
 src/nbtk/nbtk-widget.c        | 1354 +++++++++++++++++++++++++++++++++++++++++
 src/nbtk/nbtk-widget.h        |  107 ++++
 25 files changed, 7307 insertions(+), 6 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index d9d2617..edb9603 100644
--- a/configure.ac
+++ b/configure.ac
@@ -57,6 +57,7 @@ PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-unix-2.0 gtk+-2.0 dbus-glib-1 mutter-plugin
                                  gnome-desktop-2.0 >= 2.26 libstartup-notification-1.0
                                  gobject-introspection-1.0 >= 0.6.5)
 PKG_CHECK_MODULES(TIDY, clutter-1.0)
+PKG_CHECK_MODULES(NBTK, clutter-1.0 gtk+-2.0 libccss-1 >= 0.3.1)
 PKG_CHECK_MODULES(BIG, clutter-1.0 gtk+-2.0 librsvg-2.0)
 PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-2.0)
 PKG_CHECK_MODULES(TRAY, gtk+-2.0)
diff --git a/src/Makefile-nbtk.am b/src/Makefile-nbtk.am
new file mode 100644
index 0000000..adfec21
--- /dev/null
+++ b/src/Makefile-nbtk.am
@@ -0,0 +1,101 @@
+nbtk_cflags =                    \
+    -I$(top_srcdir)/src         \
+    -DPREFIX=\""$(prefix)"\"        \
+    -DLIBDIR=\""$(libdir)"\"        \
+    -DG_DISABLE_DEPRECATED          \
+    -DG_LOG_DOMAIN=\"Nbtk\"          \
+    -DNBTK_COMPILATION  \
+    $(NBTK_CFLAGS)           \
+    $(NULL)
+
+nbtk_built_sources = \
+    nbtk-enum-types.h   \
+    nbtk-enum-types.c   \
+    nbtk-marshal.h      \
+    nbtk-marshal.c
+
+BUILT_SOURCES += $(nbtk_built_sources)
+
+EXTRA_DIST += \
+	nbtk/nbtk-marshal.list \
+	nbtk/nbtk-enum-types.h.in \
+	nbtk/nbtk-enum-types.c.in
+
+CLEANFILES += stamp-nbtk-marshal.h stamp-nbtk-enum-types.h
+
+nbtk-marshal.h: stamp-nbtk-marshal.h
+	@true
+stamp-nbtk-marshal.h: Makefile nbtk/nbtk-marshal.list
+	$(GLIB_GENMARSHAL) \
+		--prefix=_nbtk_marshal \
+		--header \
+	$(srcdir)/nbtk/nbtk-marshal.list > $  tmp && \
+	(cmp -s $  tmp nbtk-marshal.h || cp -f $  tmp nbtk-marshal.h) && \
+	rm -f $  tmp && \
+	echo timestamp > $(@F)
+
+nbtk-marshal.c: Makefile nbtk/nbtk-marshal.list
+	(echo "#include \"nbtk-marshal.h\"" ; \
+	 $(GLIB_GENMARSHAL) \
+		--prefix=_nbtk_marshal \
+		--body \
+	 $(srcdir)/nbtk/nbtk-marshal.list ) > $  tmp && \
+	cp -f $  tmp nbtk-marshal.c && \
+	rm -f $  tmp
+
+nbtk-enum-types.h: stamp-nbtk-enum-types.h Makefile
+	@true
+stamp-nbtk-enum-types.h: $(source_h) nbtk/nbtk-enum-types.h.in
+	( cd $(srcdir) && \
+	  $(GLIB_MKENUMS) \
+	    --template nbtk/nbtk-enum-types.h.in \
+	  $(nbtk_source_h) ) >> $  tmp && \
+	(cmp -s $  tmp nbtk-enum-types.h || cp $  tmp nbtk-enum-types.h) && \
+	rm -f $  tmp && \
+	echo timestamp > $(@F)
+
+nbtk-enum-types.c: stamp-nbtk-enum-types.h nbtk/nbtk-enum-types.c.in
+	( cd $(srcdir) && \
+	  $(GLIB_MKENUMS) \
+	    --template nbtk/nbtk-enum-types.c.in \
+	  $(nbtk_source_h) ) >> $  tmp && \
+	cp $  tmp $@ && \
+	rm -f $  tmp
+
+# please, keep this sorted alphabetically
+nbtk_source_h =                  \
+    nbtk/nbtk-bin.h                   \
+    nbtk/nbtk-private.h               \
+    nbtk/nbtk-stylable.h              \
+    nbtk/nbtk-style.h                 \
+    nbtk/nbtk-subtexture.h            \
+    nbtk/nbtk-texture-cache.h         \
+    nbtk/nbtk-texture-frame.h         \
+    nbtk/nbtk-tooltip.h               \
+    nbtk/nbtk-types.h                 \
+    nbtk/nbtk-widget.h                \
+    $(NULL)
+
+# please, keep this sorted alphabetically
+nbtk_source_c =                  \
+    nbtk/nbtk-bin.c                   \
+    nbtk/nbtk-private.c               \
+    nbtk/nbtk-stylable.c              \
+    nbtk/nbtk-style.c                 \
+    nbtk/nbtk-subtexture.c            \
+    nbtk/nbtk-texture-cache.c         \
+    nbtk/nbtk-texture-frame.c         \
+    nbtk/nbtk-tooltip.c               \
+    nbtk/nbtk-widget.c                \
+    $(NULL)
+
+noinst_LTLIBRARIES += libnbtk-1.0.la
+
+libnbtk_1_0_la_LIBADD = $(NBTK_LIBS)
+libnbtk_1_0_la_SOURCES = \
+    $(nbtk_source_c) \
+    $(nbtk_source_h) \
+    $(nbtk_built_sources) \
+    $(NULL)
+libnbtk_1_0_la_CPPFLAGS = $(nbtk_cflags)
+libnbtk_1_0_la_LDFLAGS = $(LDADD)
diff --git a/src/Makefile.am b/src/Makefile.am
index da7de38..760cb15 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -24,6 +24,7 @@ EXTRA_DIST += gnome-shell.in
 
 include Makefile-big.am
 include Makefile-gdmuser.am
+include Makefile-nbtk.am
 include Makefile-tray.am
 
 gnome_shell_cflags =				\
@@ -152,14 +153,15 @@ libgnome_shell_la_LIBADD =	\
 	$(MUTTER_PLUGIN_LIBS)	\
         $(LIBGNOMEUI_LIBS)      \
 	libbig-1.0.la		\
+	libnbtk-1.0.la       \
 	libgdmuser-1.0.la	\
 	libtray.la
 libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
 
 typelibdir = $(pkglibdir)
-typelib_DATA = Shell-0.1.typelib Big-1.0.typelib
+typelib_DATA = Shell-0.1.typelib Big-1.0.typelib Nbtk-1.0.typelib
 
-Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir libgnome-shell.la Makefile
+Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir Nbtk-1.0.gir libgnome-shell.la Makefile
 	$(AM_V_GEN) $(G_IR_SCANNER)			\
 		--namespace=Shell			\
 		--nsversion=0.1				\
@@ -169,6 +171,7 @@ Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir libgnome-shell.la Makefile
 		--libtool="$(LIBTOOL)"    \
 		--add-include-path=$(builddir)     \
 		--include=Big-1.0     \
+		--include=Nbtk-1.0     \
 		--program=mutter			\
 	        --program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
 		$(addprefix $(srcdir)/,$(libgnome_shell_la_gir_sources)) \
@@ -179,14 +182,14 @@ CLEANFILES += Shell-0.1.gir
 # 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)
 Shell-0.1.typelib: libgnome-shell.la Shell-0.1.gir Big-1.0.gir
-	$(AM_V_GEN) LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH}	\
+	$(AM_V_GEN) \
 		$(G_IR_COMPILER)						\
 			--includedir=.						\
 			--includedir=$(MUTTER_LIB_DIR)/mutter/			\
 		Shell-0.1.gir -o $@
 CLEANFILES += Shell-0.1.typelib
 
-Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la $(srcdir)/big-enum-types.h Makefile
+Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la Makefile
 	$(AM_V_GEN) $(G_IR_SCANNER)			\
 		--namespace=Big				\
 		--nsversion=1.0				\
@@ -203,6 +206,26 @@ Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la $(srcdir)
 CLEANFILES += Big-1.0.gir
 
 Big-1.0.typelib: libbig-1.0.la Big-1.0.gir
-	$(AM_V_GEN) LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} \
-		$(G_IR_COMPILER) Big-1.0.gir -o $@
+	$(AM_V_GEN) $(G_IR_COMPILER) Big-1.0.gir -o $@
 CLEANFILES += Big-1.0.typelib
+
+Nbtk-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libnbtk-1.0.la Makefile
+	$(AM_V_GEN) $(G_IR_SCANNER)         \
+        --namespace=Nbtk             \
+        --nsversion=1.0             \
+        --include=Clutter-1.0           \
+        --include=Gtk-2.0         \
+        --libtool="$(LIBTOOL)"    \
+        --program=mutter            \
+            --program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
+        -DNBTK_COMPILATION \
+        $(addprefix $(srcdir)/,$(nbtk_source_h)) \
+        $(addprefix $(srcdir)/,$(nbtk_source_c)) \
+        $(srcdir)/nbtk-enum-types.h      \
+        $(NBTK_CFLAGS)               \
+        -o $@
+CLEANFILES += Nbtk-1.0.gir
+
+Nbtk-1.0.typelib: Nbtk-1.0.gir
+	$(AM_V_GEN) $(G_IR_COMPILER) Nbtk-1.0.gir -o $@
+CLEANFILES += Nbtk-1.0.typelib
diff --git a/src/nbtk/nbtk-bin.c b/src/nbtk/nbtk-bin.c
new file mode 100644
index 0000000..0bd1dab
--- /dev/null
+++ b/src/nbtk/nbtk-bin.c
@@ -0,0 +1,783 @@
+/*
+ * nbtk-bin.c: Basic container actor
+ *
+ * Copyright (c) 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by: Emmanuele Bassi <ebassi linux intel com>
+ *
+ */
+
+/**
+ * SECTION:nbtk-bin
+ * @short_description: a simple container with one actor
+ *
+ * #NbtkBin is a simple container capable of having only one
+ * #ClutterActor as a child.
+ *
+ * #NbtkBin inherits from #NbtkWidget, so it is fully themable.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <clutter/clutter.h>
+
+#include "nbtk-bin.h"
+#include "nbtk-enum-types.h"
+#include "nbtk-private.h"
+#include "nbtk-stylable.h"
+
+#define NBTK_BIN_GET_PRIVATE(obj)       (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NBTK_TYPE_BIN, NbtkBinPrivate))
+
+struct _NbtkBinPrivate
+{
+  ClutterActor *child;
+
+  NbtkAlignment x_align;
+  NbtkAlignment y_align;
+
+  guint x_fill : 1;
+  guint y_fill : 1;
+};
+
+enum
+{
+  PROP_0,
+
+  PROP_CHILD,
+  PROP_X_ALIGN,
+  PROP_Y_ALIGN,
+  PROP_X_FILL,
+  PROP_Y_FILL
+};
+
+static void clutter_container_iface_init (ClutterContainerIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (NbtkBin, nbtk_bin, NBTK_TYPE_WIDGET,
+                         G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
+                                                clutter_container_iface_init));
+
+void
+_nbtk_bin_get_align_factors (NbtkBin *bin,
+                             gdouble *x_align,
+                             gdouble *y_align)
+{
+  NbtkBinPrivate *priv = bin->priv;
+  gdouble factor;
+
+  switch (priv->x_align)
+    {
+    case NBTK_ALIGN_LEFT:
+      factor = 0.0;
+      break;
+
+    case NBTK_ALIGN_CENTER:
+      factor = 0.5;
+      break;
+
+    case NBTK_ALIGN_RIGHT:
+      factor = 1.0;
+      break;
+
+    default:
+      factor = 0.0;
+      break;
+    }
+
+  if (x_align)
+    *x_align = factor;
+
+  switch (priv->y_align)
+    {
+    case NBTK_ALIGN_TOP:
+      factor = 0.0;
+      break;
+
+    case NBTK_ALIGN_CENTER:
+      factor = 0.5;
+      break;
+
+    case NBTK_ALIGN_BOTTOM:
+      factor = 1.0;
+      break;
+
+    default:
+      factor = 0.0;
+      break;
+    }
+
+  if (y_align)
+    *y_align = factor;
+}
+
+static void
+nbtk_bin_add (ClutterContainer *container,
+              ClutterActor     *actor)
+{
+  nbtk_bin_set_child (NBTK_BIN (container), actor);
+}
+
+static void
+nbtk_bin_remove (ClutterContainer *container,
+                 ClutterActor     *actor)
+{
+  NbtkBinPrivate *priv = NBTK_BIN (container)->priv;
+
+  if (priv->child == actor)
+    nbtk_bin_set_child (NBTK_BIN (container), NULL);
+}
+
+static void
+nbtk_bin_foreach (ClutterContainer *container,
+                  ClutterCallback   callback,
+                  gpointer          user_data)
+{
+  NbtkBinPrivate *priv = NBTK_BIN (container)->priv;
+
+  if (priv->child)
+    callback (priv->child, user_data);
+}
+
+static void
+clutter_container_iface_init (ClutterContainerIface *iface)
+{
+  iface->add = nbtk_bin_add;
+  iface->remove = nbtk_bin_remove;
+  iface->foreach = nbtk_bin_foreach;
+}
+
+static void
+nbtk_bin_paint (ClutterActor *self)
+{
+  NbtkBinPrivate *priv = NBTK_BIN (self)->priv;
+
+  /* allow NbtkWidget to paint the background */
+  CLUTTER_ACTOR_CLASS (nbtk_bin_parent_class)->paint (self);
+
+  /* the pain our child */
+  if (priv->child)
+    clutter_actor_paint (priv->child);
+}
+
+static void
+nbtk_bin_pick (ClutterActor       *self,
+               const ClutterColor *pick_color)
+{
+  NbtkBinPrivate *priv = NBTK_BIN (self)->priv;
+
+  /* get the default pick implementation */
+  CLUTTER_ACTOR_CLASS (nbtk_bin_parent_class)->pick (self, pick_color);
+
+  if (priv->child)
+    clutter_actor_paint (priv->child);
+}
+
+static void
+nbtk_bin_allocate (ClutterActor          *self,
+                   const ClutterActorBox *box,
+                   ClutterAllocationFlags flags)
+{
+  NbtkBinPrivate *priv = NBTK_BIN (self)->priv;
+
+  CLUTTER_ACTOR_CLASS (nbtk_bin_parent_class)->allocate (self, box,
+                                                         flags);
+
+  if (priv->child)
+    {
+      gfloat natural_width, natural_height;
+      gfloat min_width, min_height;
+      gfloat child_width, child_height;
+      gfloat available_width, available_height;
+      ClutterRequestMode request;
+      ClutterActorBox allocation = { 0, };
+      NbtkPadding padding = { 0, };
+      gdouble x_align, y_align;
+
+      _nbtk_bin_get_align_factors (NBTK_BIN (self), &x_align, &y_align);
+
+      nbtk_widget_get_padding (NBTK_WIDGET (self), &padding);
+
+      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;
+
+      if (priv->x_fill)
+        {
+          allocation.x1 = (int) padding.top;
+          allocation.x2 = (int) (allocation.x1 + available_width);
+        }
+
+      if (priv->y_fill)
+        {
+          allocation.y1 = (int) padding.right;
+          allocation.y2 = (int) (allocation.y1 + available_height);
+        }
+
+      /* if we are filling horizontally and vertically then we're done */
+      if (priv->x_fill && priv->y_fill)
+        {
+          clutter_actor_allocate (priv->child, &allocation, flags);
+          return;
+        }
+
+      request = CLUTTER_REQUEST_HEIGHT_FOR_WIDTH;
+      g_object_get (G_OBJECT (priv->child), "request-mode", &request, NULL);
+
+      if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
+        {
+          clutter_actor_get_preferred_width (priv->child, available_height,
+                                             &min_width,
+                                             &natural_width);
+
+          child_width = CLAMP (natural_width, min_width, available_width);
+
+          clutter_actor_get_preferred_height (priv->child, child_width,
+                                              &min_height,
+                                              &natural_height);
+
+          child_height = CLAMP (natural_height, min_height, available_height);
+        }
+      else
+        {
+          clutter_actor_get_preferred_height (priv->child, available_width,
+                                              &min_height,
+                                              &natural_height);
+
+          child_height = CLAMP (natural_height, min_height, available_height);
+
+          clutter_actor_get_preferred_width (priv->child, child_height,
+                                             &min_width,
+                                             &natural_width);
+
+          child_width = CLAMP (natural_width, min_width, available_width);
+        }
+
+      if (!priv->x_fill)
+        {
+          allocation.x1 = (int) ((available_width - child_width) * x_align
+                        + padding.left);
+          allocation.x2 = allocation.x1 + child_width;
+        }
+
+      if (!priv->y_fill)
+        {
+          allocation.y1 = (int) ((available_height - child_height) * y_align
+                        + padding.top);
+          allocation.y2 = allocation.y1 + child_height;
+        }
+
+      clutter_actor_allocate (priv->child, &allocation, flags);
+    }
+}
+
+static void
+nbtk_bin_get_preferred_width (ClutterActor *self,
+                              gfloat   for_height,
+                              gfloat  *min_width_p,
+                              gfloat  *natural_width_p)
+{
+  NbtkBinPrivate *priv = NBTK_BIN (self)->priv;
+  gfloat min_width, natural_width;
+  NbtkPadding padding = { 0, };
+
+  nbtk_widget_get_padding (NBTK_WIDGET (self), &padding);
+
+  min_width = natural_width = padding.left + padding.right;
+
+  if (priv->child == NULL)
+    {
+      if (min_width_p)
+        *min_width_p = min_width;
+
+      if (natural_width_p)
+        *natural_width_p = natural_width;
+    }
+  else
+    {
+      clutter_actor_get_preferred_width (priv->child, for_height,
+                                         min_width_p,
+                                         natural_width_p);
+
+      if (min_width_p)
+        *min_width_p += min_width;
+
+      if (natural_width_p)
+        *natural_width_p += natural_width;
+    }
+}
+
+static void
+nbtk_bin_get_preferred_height (ClutterActor *self,
+                               gfloat   for_width,
+                               gfloat  *min_height_p,
+                               gfloat  *natural_height_p)
+{
+  NbtkBinPrivate *priv = NBTK_BIN (self)->priv;
+  gfloat min_height, natural_height;
+  NbtkPadding padding = { 0, };
+
+  nbtk_widget_get_padding (NBTK_WIDGET (self), &padding);
+
+  min_height = natural_height = padding.top + padding.bottom;
+
+  if (priv->child == NULL)
+    {
+      if (min_height_p)
+        *min_height_p = min_height;
+
+      if (natural_height_p)
+        *natural_height_p = natural_height;
+    }
+  else
+    {
+      clutter_actor_get_preferred_height (priv->child, for_width,
+                                          min_height_p,
+                                          natural_height_p);
+
+      if (min_height_p)
+        *min_height_p += min_height;
+
+      if (natural_height_p)
+        *natural_height_p += natural_height;
+    }
+}
+
+static void
+nbtk_bin_dispose (GObject *gobject)
+{
+  NbtkBinPrivate *priv = NBTK_BIN (gobject)->priv;
+
+  if (priv->child)
+    {
+      clutter_actor_unparent (priv->child);
+      priv->child = NULL;
+    }
+
+  G_OBJECT_CLASS (nbtk_bin_parent_class)->dispose (gobject);
+}
+
+static void
+nbtk_bin_set_property (GObject      *gobject,
+                       guint         prop_id,
+                       const GValue *value,
+                       GParamSpec   *pspec)
+{
+  NbtkBin *bin = NBTK_BIN (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_CHILD:
+      nbtk_bin_set_child (bin, g_value_get_object (value));
+      break;
+
+    case PROP_X_ALIGN:
+      nbtk_bin_set_alignment (bin,
+                              g_value_get_enum (value),
+                              bin->priv->y_align);
+      break;
+
+    case PROP_Y_ALIGN:
+      nbtk_bin_set_alignment (bin,
+                              bin->priv->x_align,
+                              g_value_get_enum (value));
+      break;
+
+    case PROP_X_FILL:
+      nbtk_bin_set_fill (bin,
+                         g_value_get_boolean (value),
+                         bin->priv->y_fill);
+      break;
+
+    case PROP_Y_FILL:
+      nbtk_bin_set_fill (bin,
+                         bin->priv->y_fill,
+                         g_value_get_boolean (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+    }
+}
+
+static void
+nbtk_bin_get_property (GObject    *gobject,
+                       guint       prop_id,
+                       GValue     *value,
+                       GParamSpec *pspec)
+{
+  NbtkBinPrivate *priv = NBTK_BIN (gobject)->priv;
+
+  switch (prop_id)
+    {
+    case PROP_CHILD:
+      g_value_set_object (value, priv->child);
+      break;
+
+    case PROP_X_FILL:
+      g_value_set_boolean (value, priv->x_fill);
+      break;
+
+    case PROP_Y_FILL:
+      g_value_set_boolean (value, priv->y_fill);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+    }
+}
+
+static void
+nbtk_bin_class_init (NbtkBinClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+  GParamSpec *pspec;
+
+  g_type_class_add_private (klass, sizeof (NbtkBinPrivate));
+
+  gobject_class->set_property = nbtk_bin_set_property;
+  gobject_class->get_property = nbtk_bin_get_property;
+  gobject_class->dispose = nbtk_bin_dispose;
+
+  actor_class->get_preferred_width = nbtk_bin_get_preferred_width;
+  actor_class->get_preferred_height = nbtk_bin_get_preferred_height;
+  actor_class->allocate = nbtk_bin_allocate;
+  actor_class->paint = nbtk_bin_paint;
+  actor_class->pick = nbtk_bin_pick;
+
+  /**
+   * NbtkBin:child:
+   *
+   * The child #ClutterActor of the #NbtkBin container.
+   */
+  pspec = g_param_spec_object ("child",
+                               "Child",
+                               "The child of the Bin",
+                               CLUTTER_TYPE_ACTOR,
+                               NBTK_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_CHILD, pspec);
+
+  /**
+   * NbtkBin:x-align:
+   *
+   * The horizontal alignment of the #NbtkBin child.
+   */
+  pspec = g_param_spec_enum ("x-align",
+                             "X Align",
+                             "The horizontal alignment",
+                             NBTK_TYPE_ALIGNMENT,
+                             NBTK_ALIGN_CENTER,
+                             NBTK_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_X_ALIGN, pspec);
+
+  /**
+   * NbtkBin:y-align:
+   *
+   * The vertical alignment of the #NbtkBin child.
+   */
+  pspec = g_param_spec_enum ("y-align",
+                             "Y Align",
+                             "The vertical alignment",
+                             NBTK_TYPE_ALIGNMENT,
+                             NBTK_ALIGN_CENTER,
+                             NBTK_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_Y_ALIGN, pspec);
+
+  /**
+   * NbtkBin:x-fill:
+   *
+   * Whether the child should fill the horizontal allocation
+   */
+  pspec = g_param_spec_boolean ("x-fill",
+                                "X Fill",
+                                "Whether the child should fill the "
+                                "horizontal allocation",
+                                FALSE,
+                                NBTK_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_X_FILL, pspec);
+
+  /**
+   * NbtkBin:y-fill:
+   *
+   * Whether the child should fill the vertical allocation
+   */
+  pspec = g_param_spec_boolean ("y-fill",
+                                "Y Fill",
+                                "Whether the child should fill the "
+                                "vertical allocation",
+                                FALSE,
+                                NBTK_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_Y_FILL, pspec);
+}
+
+static void
+nbtk_bin_init (NbtkBin *bin)
+{
+  bin->priv = NBTK_BIN_GET_PRIVATE (bin);
+
+  bin->priv->x_align = NBTK_ALIGN_CENTER;
+  bin->priv->y_align = NBTK_ALIGN_CENTER;
+}
+
+/**
+ * nbtk_bin_new:
+ *
+ * Creates a new #NbtkBin, a simple container for one child.
+ *
+ * Return value: the newly created #NbtkBin actor
+ */
+NbtkWidget *
+nbtk_bin_new (void)
+{
+  return g_object_new (NBTK_TYPE_BIN, NULL);
+}
+
+/**
+ * nbtk_bin_set_child:
+ * @bin: a #NbtkBin
+ * @child: a #ClutterActor, or %NULL
+ *
+ * Sets @child as the child of @bin.
+ *
+ * If @bin already has a child, the previous child is removed.
+ */
+void
+nbtk_bin_set_child (NbtkBin *bin,
+                    ClutterActor *child)
+{
+  NbtkBinPrivate *priv;
+
+  g_return_if_fail (NBTK_IS_BIN (bin));
+  g_return_if_fail (child == NULL || CLUTTER_IS_ACTOR (child));
+
+  priv = bin->priv;
+
+  if (priv->child == child)
+    return;
+
+  if (priv->child)
+    {
+      ClutterActor *old_child = priv->child;
+
+      g_object_ref (old_child);
+
+      priv->child = NULL;
+      clutter_actor_unparent (old_child);
+
+      g_signal_emit_by_name (bin, "actor-removed", old_child);
+
+      g_object_unref (old_child);
+    }
+
+  if (child)
+    {
+      priv->child = child;
+      clutter_actor_set_parent (child, CLUTTER_ACTOR (bin));
+
+      g_signal_emit_by_name (bin, "actor-added", priv->child);
+    }
+
+  clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
+
+  g_object_notify (G_OBJECT (bin), "child");
+}
+
+/**
+ * nbtk_bin_get_child:
+ * @bin: a #NbtkBin
+ *
+ * Retrieves a pointer to the child of @bin.
+ *
+ * Return value: a #ClutterActor, or %NULL
+ */
+ClutterActor *
+nbtk_bin_get_child (NbtkBin *bin)
+{
+  g_return_val_if_fail (NBTK_IS_BIN (bin), NULL);
+
+  return bin->priv->child;
+}
+
+/**
+ * nbtk_bin_set_alignment:
+ * @bin: a #NbtkBin
+ * @x_align: horizontal alignment
+ * @y_align: vertical alignment
+ *
+ * Sets the horizontal and vertical alignment of the child
+ * inside a #NbtkBin.
+ */
+void
+nbtk_bin_set_alignment (NbtkBin       *bin,
+                        NbtkAlignment  x_align,
+                        NbtkAlignment  y_align)
+{
+  NbtkBinPrivate *priv;
+  gboolean changed = FALSE;
+
+  g_return_if_fail (NBTK_IS_BIN (bin));
+
+  priv = bin->priv;
+
+  g_object_freeze_notify (G_OBJECT (bin));
+
+  if (priv->x_align != x_align)
+    {
+      priv->x_align = x_align;
+      g_object_notify (G_OBJECT (bin), "x-align");
+      changed = TRUE;
+    }
+
+  if (priv->y_align != y_align)
+    {
+      priv->y_align = y_align;
+      g_object_notify (G_OBJECT (bin), "y-align");
+      changed = TRUE;
+    }
+
+  if (changed)
+    clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
+
+  g_object_thaw_notify (G_OBJECT (bin));
+}
+
+/**
+ * nbtk_bin_get_alignment:
+ * @bin: a #NbtkBin
+ * @x_align: return location for the horizontal alignment, or %NULL
+ * @y_align: return location for the vertical alignment, or %NULL
+ *
+ * Retrieves the horizontal and vertical alignment of the child
+ * inside a #NbtkBin, as set by nbtk_bin_set_alignment().
+ */
+void
+nbtk_bin_get_alignment (NbtkBin       *bin,
+                        NbtkAlignment *x_align,
+                        NbtkAlignment *y_align)
+{
+  NbtkBinPrivate *priv;
+
+  g_return_if_fail (NBTK_IS_BIN (bin));
+
+  priv = bin->priv;
+
+  if (x_align)
+    *x_align = priv->x_align;
+
+  if (y_align)
+    *y_align = priv->y_align;
+}
+
+/**
+ * nbtk_bin_set_fill:
+ * @bin: a #NbtkBin
+ * @x_fill: %TRUE if the child should fill horizontally the @bin
+ * @y_fill: %TRUE if the child should fill vertically the @bin
+ *
+ * Sets whether the child of @bin should fill out the horizontal
+ * and/or vertical allocation of the parent
+ */
+void
+nbtk_bin_set_fill (NbtkBin  *bin,
+                   gboolean  x_fill,
+                   gboolean  y_fill)
+{
+  NbtkBinPrivate *priv;
+  gboolean changed = FALSE;
+
+  g_return_if_fail (NBTK_IS_BIN (bin));
+
+  priv = bin->priv;
+
+  g_object_freeze_notify (G_OBJECT (bin));
+
+  if (priv->x_fill != x_fill)
+    {
+      priv->x_fill = x_fill;
+      changed = TRUE;
+
+      g_object_notify (G_OBJECT (bin), "x-fill");
+    }
+
+  if (priv->y_fill != y_fill)
+    {
+      priv->y_fill = y_fill;
+      changed = TRUE;
+
+      g_object_notify (G_OBJECT (bin), "y-fill");
+    }
+
+  if (changed)
+    clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
+
+  g_object_thaw_notify (G_OBJECT (bin));
+}
+
+/**
+ * nbtk_bin_get_fill:
+ * @bin: a #NbtkBin
+ * @x_fill: (out): return location for the horizontal fill, or %NULL
+ * @y_fill: (out): return location for the vertical fill, or %NULL
+ *
+ * Retrieves the horizontal and vertical fill settings
+ */
+void
+nbtk_bin_get_fill (NbtkBin  *bin,
+                   gboolean *x_fill,
+                   gboolean *y_fill)
+{
+  g_return_if_fail (NBTK_IS_BIN (bin));
+
+  if (x_fill)
+    *x_fill = bin->priv->x_fill;
+
+  if (y_fill)
+    *y_fill = bin->priv->y_fill;
+}
+
+static gpointer
+nbtk_padding_copy (gpointer data)
+{
+  return g_slice_dup (NbtkPadding, data);
+}
+
+static void
+nbtk_padding_free (gpointer data)
+{
+  if (G_LIKELY (data))
+    g_slice_free (NbtkPadding, data);
+}
+
+GType
+nbtk_padding_get_type (void)
+{
+  static GType our_type = 0;
+
+  if (G_UNLIKELY (our_type == 0))
+    our_type = g_boxed_type_register_static (I_("NbtkPadding"),
+                                             nbtk_padding_copy,
+                                             nbtk_padding_free);
+
+  return our_type;
+}
diff --git a/src/nbtk/nbtk-bin.h b/src/nbtk/nbtk-bin.h
new file mode 100644
index 0000000..0e7e530
--- /dev/null
+++ b/src/nbtk/nbtk-bin.h
@@ -0,0 +1,92 @@
+/*
+ * nbtk-bin.h: Basic container actor
+ *
+ * Copyright 2009, 2008 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Emmanuele Bassi <ebassi linux intel com>
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_BIN_H__
+#define __NBTK_BIN_H__
+
+#include <nbtk/nbtk-types.h>
+#include <nbtk/nbtk-widget.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_BIN                   (nbtk_bin_get_type ())
+#define NBTK_BIN(obj)                   (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_BIN, NbtkBin))
+#define NBTK_IS_BIN(obj)                (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_BIN))
+#define NBTK_BIN_CLASS(klass)           (G_TYPE_CHECK_CLASS_CAST ((klass), NBTK_TYPE_BIN, NbtkBinClass))
+#define NBTK_IS_BIN_CLASS(klass)        (G_TYPE_CHECK_CLASS_TYPE ((klass), NBTK_TYPE_BIN))
+#define NBTK_BIN_GET_CLASS(obj)         (G_TYPE_INSTANCE_GET_CLASS ((obj), NBTK_TYPE_BIN, NbtkBinClass))
+
+typedef struct _NbtkBin                 NbtkBin;
+typedef struct _NbtkBinPrivate          NbtkBinPrivate;
+typedef struct _NbtkBinClass            NbtkBinClass;
+
+/**
+ * NbtkBin:
+ *
+ * The #NbtkBin struct contains only private data
+ */
+struct _NbtkBin
+{
+  /*< private >*/
+  NbtkWidget parent_instance;
+
+  NbtkBinPrivate *priv;
+};
+
+/**
+ * NbtkBinClass:
+ *
+ * The #NbtkBinClass struct contains only private data
+ */
+struct _NbtkBinClass
+{
+  /*< private >*/
+  NbtkWidgetClass parent_class;
+};
+
+GType nbtk_bin_get_type (void) G_GNUC_CONST;
+
+NbtkWidget   *nbtk_bin_new           (void);
+void          nbtk_bin_set_child     (NbtkBin           *bin,
+                                      ClutterActor      *child);
+ClutterActor *nbtk_bin_get_child     (NbtkBin           *bin);
+void          nbtk_bin_set_alignment (NbtkBin           *bin,
+                                      NbtkAlignment      x_align,
+                                      NbtkAlignment      y_align);
+void          nbtk_bin_get_alignment (NbtkBin           *bin,
+                                      NbtkAlignment     *x_align,
+                                      NbtkAlignment     *y_align);
+void          nbtk_bin_set_fill      (NbtkBin           *bin,
+                                      gboolean           x_fill,
+                                      gboolean           y_fill);
+void          nbtk_bin_get_fill      (NbtkBin           *bin,
+                                      gboolean          *x_fill,
+                                      gboolean          *y_fill);
+
+G_END_DECLS
+
+#endif /* __NBTK_BIN_H__ */
diff --git a/src/nbtk/nbtk-enum-types.c.in b/src/nbtk/nbtk-enum-types.c.in
new file mode 100644
index 0000000..22a6177
--- /dev/null
+++ b/src/nbtk/nbtk-enum-types.c.in
@@ -0,0 +1,30 @@
+/*** BEGIN file-header ***/
+#include "nbtk-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 (g_intern_static_string ("@EnumName@"), values);
+    }
+  return enum_type_id;
+}
+/*** END value-tail ***/
diff --git a/src/nbtk/nbtk-enum-types.h.in b/src/nbtk/nbtk-enum-types.h.in
new file mode 100644
index 0000000..4befa1c
--- /dev/null
+++ b/src/nbtk/nbtk-enum-types.h.in
@@ -0,0 +1,29 @@
+/*** BEGIN file-header ***/
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_ENUM_TYPES_H__
+#define __NBTK_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 /* !__NBTK_ENUM_TYPES_H__ */
+/*** END file-tail ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name _get_type (void) G_GNUC_CONST;
+#define NBTK_TYPE_ ENUMSHORT@ (@enum_name _get_type())
+
+/*** END value-header ***/
diff --git a/src/nbtk/nbtk-marshal.list b/src/nbtk/nbtk-marshal.list
new file mode 100644
index 0000000..9b3bed6
--- /dev/null
+++ b/src/nbtk/nbtk-marshal.list
@@ -0,0 +1,12 @@
+VOID:OBJECT
+VOID:VOID
+VOID:PARAM
+VOID:POINTER
+VOID:UINT
+VOID:UINT,UINT
+VOID:OBJECT,OBJECT
+VOID:STRING,OBJECT
+VOID:OBJECT,OBJECT,INT,INT
+VOID:OBJECT,FLOAT,FLOAT,INT,ENUM
+VOID:FLOAT,FLOAT,INT,ENUM
+VOID:FLOAT,FLOAT
diff --git a/src/nbtk/nbtk-private.c b/src/nbtk/nbtk-private.c
new file mode 100644
index 0000000..9f1a8cb
--- /dev/null
+++ b/src/nbtk/nbtk-private.c
@@ -0,0 +1,111 @@
+#include "nbtk-private.h"
+
+/* Utility function to modify a child allocation box with respect to the
+ * x/y-fill child properties. Expects childbox to contain the available
+ * allocation space.
+ */
+void
+_nbtk_allocate_fill (ClutterActor *child,
+                     ClutterActorBox *childbox,
+                     NbtkAlign x_alignment,
+                     NbtkAlign y_alignment,
+                     gboolean x_fill,
+                     gboolean y_fill)
+{
+  gfloat natural_width, natural_height;
+  gfloat min_width, min_height;
+  gfloat child_width, child_height;
+  gfloat available_width, available_height;
+  ClutterRequestMode request;
+  ClutterActorBox allocation = { 0, };
+  gdouble x_align, y_align;
+
+  if (x_alignment == NBTK_ALIGN_START)
+    x_align = 0.0;
+  else if (x_alignment == NBTK_ALIGN_MIDDLE)
+    x_align = 0.5;
+  else
+    x_align = 1.0;
+
+  if (y_alignment == NBTK_ALIGN_START)
+    y_align = 0.0;
+  else if (y_alignment == NBTK_ALIGN_MIDDLE)
+    y_align = 0.5;
+  else
+    y_align = 1.0;
+
+  available_width  = childbox->x2 - childbox->x1;
+  available_height = childbox->y2 - childbox->y1;
+
+  if (available_width < 0)
+    available_width = 0;
+
+  if (available_height < 0)
+    available_height = 0;
+
+  if (x_fill)
+    {
+      allocation.x1 = childbox->x1;
+      allocation.x2 = (int) (allocation.x1 + available_width);
+    }
+
+  if (y_fill)
+    {
+      allocation.y1 = childbox->y1;
+      allocation.y2 = (int) (allocation.y1 + available_height);
+    }
+
+  /* if we are filling horizontally and vertically then we're done */
+  if (x_fill && y_fill)
+    {
+      *childbox = allocation;
+      return;
+    }
+
+  request = CLUTTER_REQUEST_HEIGHT_FOR_WIDTH;
+  g_object_get (G_OBJECT (child), "request-mode", &request, NULL);
+
+  if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
+    {
+      clutter_actor_get_preferred_width (child, available_height,
+                                         &min_width,
+                                         &natural_width);
+
+      child_width = CLAMP (natural_width, min_width, available_width);
+
+      clutter_actor_get_preferred_height (child, child_width,
+                                          &min_height,
+                                          &natural_height);
+
+      child_height = CLAMP (natural_height, min_height, available_height);
+    }
+  else
+    {
+      clutter_actor_get_preferred_height (child, available_width,
+                                          &min_height,
+                                          &natural_height);
+
+      child_height = CLAMP (natural_height, min_height, available_height);
+
+      clutter_actor_get_preferred_width (child, child_height,
+                                         &min_width,
+                                         &natural_width);
+
+      child_width = CLAMP (natural_width, min_width, available_width);
+    }
+
+  if (!x_fill)
+    {
+      allocation.x1 = childbox->x1 + (int) ((available_width - child_width) * x_align);
+      allocation.x2 = allocation.x1 + (int) child_width;
+    }
+
+  if (!y_fill)
+    {
+      allocation.y1 = childbox->y1 + (int) ((available_height - child_height) * y_align);
+      allocation.y2 = allocation.y1 + (int) child_height;
+    }
+
+  *childbox = allocation;
+
+}
diff --git a/src/nbtk/nbtk-private.h b/src/nbtk/nbtk-private.h
new file mode 100644
index 0000000..2a6579b
--- /dev/null
+++ b/src/nbtk/nbtk-private.h
@@ -0,0 +1,49 @@
+/*
+ * nbtk-private.h: Private declarations
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __NBTK_PRIVATE_H__
+#define __NBTK_PRIVATE_H__
+
+#include <glib.h>
+#include "nbtk-widget.h"
+#include "nbtk-bin.h"
+
+G_BEGIN_DECLS
+
+#define I_(str)         (g_intern_static_string ((str)))
+
+#define NBTK_PARAM_READABLE     \
+        (G_PARAM_READABLE |     \
+         G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
+
+#define NBTK_PARAM_READWRITE    \
+        (G_PARAM_READABLE | G_PARAM_WRITABLE | \
+         G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
+
+G_END_DECLS
+
+ClutterActor *_nbtk_widget_get_dnd_clone (NbtkWidget *widget);
+void _nbtk_bin_get_align_factors (NbtkBin *bin, gdouble *x_align, gdouble *y_align);
+
+void _nbtk_allocate_fill (ClutterActor *child, ClutterActorBox *childbox, NbtkAlign x_align, NbtkAlign y_align, gboolean x_fill, gboolean y_fill);
+
+#endif /* __NBTK_PRIVATE_H__ */
diff --git a/src/nbtk/nbtk-stylable.c b/src/nbtk/nbtk-stylable.c
new file mode 100644
index 0000000..b3ca7fc
--- /dev/null
+++ b/src/nbtk/nbtk-stylable.c
@@ -0,0 +1,849 @@
+/*
+ * nbtk-stylable.c: Interface for stylable objects
+ *
+ * Copyright 2008 Intel Corporation
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Written by: Emmanuele Bassi <ebassi openedhand com>
+ *             Thomas Wood <thomas linux intel com>
+ *
+ */
+
+/**
+ * SECTION:nbtk-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 #NbtkStyle to them.
+ *
+ * Objects can choose to subclass #NbtkWidget, and thus inherit all the
+ * #NbtkWidget style properties; or they can subclass #NbtkWidget and
+ * reimplement the #NbtkStylable interface to add new style properties
+ * specific for them (and their subclasses); or, finally, they can simply
+ * subclass #GObject and implement #NbtkStylable 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 "nbtk-marshal.h"
+#include "nbtk-private.h"
+#include "nbtk-stylable.h"
+
+enum
+{
+  STYLE_CHANGED,
+  STYLE_NOTIFY,
+  CHANGED,
+
+  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
+nbtk_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
+nbtk_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
+nbtk_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 ("nbtk-stylable-real-owner-quark");
+      quark_style = g_quark_from_static_string ("nbtk-stylable-style-quark");
+
+      style_property_spec_pool = g_param_spec_pool_new (FALSE);
+
+      property_notify_context.quark_notify_queue = g_quark_from_static_string ("NbtkStylable-style-property-notify-queue");
+      property_notify_context.dispatcher = nbtk_stylable_notify_dispatcher;
+
+      /**
+       * NbtkStylable:style:
+       *
+       * The #NbtkStyle attached to a stylable object.
+       */
+      g_object_interface_install_property (g_iface,
+                                           g_param_spec_object ("style",
+                                                                "Style",
+                                                                "A style object",
+                                                                NBTK_TYPE_STYLE,
+                                                                NBTK_PARAM_READWRITE));
+
+      /**
+       * NbtkStylable::style-changed:
+       * @stylable: the #NbtkStylable that received the signal
+       * @old_style: the previously set #NbtkStyle for @stylable
+       *
+       * The ::style-changed signal is emitted each time one of the style
+       * properties have changed.
+       */
+      stylable_signals[STYLE_CHANGED] =
+        g_signal_new (I_("style-changed"),
+                      iface_type,
+                      G_SIGNAL_RUN_FIRST,
+                      G_STRUCT_OFFSET (NbtkStylableIface, style_changed),
+                      NULL, NULL,
+                      _nbtk_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0);
+
+  /**
+   * NbtkStylable::stylable-changed:
+   * @actor: the actor that received the signal
+   *
+   * The ::changed signal is emitted each time any of the properties of the
+   * stylable has changed.
+   */
+  stylable_signals[CHANGED] =
+    g_signal_new (I_("stylable-changed"),
+                  iface_type,
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (NbtkStylableIface, stylable_changed),
+                  NULL, NULL,
+                  _nbtk_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+
+      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 (NbtkStylableIface, style_notify),
+                      NULL, NULL,
+                      _nbtk_marshal_VOID__PARAM,
+                      G_TYPE_NONE, 1,
+                      G_TYPE_PARAM);
+    }
+}
+
+GType
+nbtk_stylable_get_type (void)
+{
+  static GType our_type = 0;
+
+  if (G_UNLIKELY (our_type == 0))
+    {
+      GTypeInfo stylable_info = {
+        sizeof (NbtkStylableIface),
+        nbtk_stylable_base_init,
+        nbtk_stylable_base_finalize
+      };
+
+      our_type = g_type_register_static (G_TYPE_INTERFACE,
+                                         I_("NbtkStylable"),
+                                         &stylable_info, 0);
+    }
+
+  return our_type;
+}
+
+void
+nbtk_stylable_freeze_notify (NbtkStylable *stylable)
+{
+  g_return_if_fail (NBTK_IS_STYLABLE (stylable));
+
+  g_object_ref (stylable);
+  g_object_notify_queue_freeze (G_OBJECT (stylable), &property_notify_context);
+  g_object_unref (stylable);
+}
+
+void
+nbtk_stylable_thaw_notify (NbtkStylable *stylable)
+{
+  GObjectNotifyQueue *nqueue;
+
+  g_return_if_fail (NBTK_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
+nbtk_stylable_notify (NbtkStylable *stylable,
+                      const gchar  *property_name)
+{
+  GParamSpec *pspec;
+
+  g_return_if_fail (NBTK_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);
+}
+
+/**
+ * nbtk_stylable_iface_install_property:
+ * @iface: a #NbtkStylableIface
+ * @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 #NbtkStylableIface initialization
+ * function of a class, for instance:
+ *
+ * <informalexample><programlisting>
+ * G_DEFINE_TYPE_WITH_CODE (FooActor, foo_actor, CLUTTER_TYPE_ACTOR,
+ *                          G_IMPLEMENT_INTERFACE (NBTK_TYPE_STYLABLE,
+ *                                                 nbtk_stylable_init));
+ * ...
+ * static void
+ * nbtk_stylable_init (NbtkStylableIface *iface)
+ * {
+ *   static gboolean is_initialized = FALSE;
+ *
+ *   if (!is_initialized)
+ *     {
+ *       ...
+ *       nbtk_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
+nbtk_stylable_iface_install_property (NbtkStylableIface *iface,
+                                      GType              owner_type,
+                                      GParamSpec        *pspec)
+{
+  g_return_if_fail (NBTK_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);
+}
+
+/**
+ * nbtk_stylable_list_properties:
+ * @stylable: a #NbtkStylable
+ * @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 **
+nbtk_stylable_list_properties (NbtkStylable *stylable,
+                               guint        *n_props)
+{
+  GParamSpec **pspecs = NULL;
+  guint n;
+
+  g_return_val_if_fail (NBTK_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;
+}
+
+/**
+ * nbtk_stylable_find_property:
+ * @stylable: a #NbtkStylable
+ * @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 *
+nbtk_stylable_find_property (NbtkStylable *stylable,
+                             const gchar  *property_name)
+{
+  g_return_val_if_fail (NBTK_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
+nbtk_stylable_get_property_internal (NbtkStylable *stylable,
+                                     GParamSpec   *pspec,
+                                     GValue       *value)
+{
+  NbtkStyle *style;
+  GValue real_value = { 0, };
+
+  style = nbtk_stylable_get_style (stylable);
+
+  if (!style)
+    {
+      g_value_reset (value);
+      return;
+    }
+
+  nbtk_style_get_property (style, stylable, pspec, &real_value);
+
+  g_value_copy (&real_value, value);
+  g_value_unset (&real_value);
+
+}
+
+/**
+ * nbtk_stylable_get_property:
+ * @stylable: a #NbtkStylable
+ * @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
+nbtk_stylable_get_property (NbtkStylable *stylable,
+                            const gchar  *property_name,
+                            GValue       *value)
+{
+  GParamSpec *pspec;
+
+  g_return_if_fail (NBTK_IS_STYLABLE (stylable));
+  g_return_if_fail (property_name != NULL);
+  g_return_if_fail (value != NULL);
+
+  pspec = nbtk_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;
+    }
+
+  nbtk_stylable_get_property_internal (stylable, pspec, value);
+}
+
+/**
+ * nbtk_stylable_get:
+ * @stylable: a #NbtkStylable
+ * @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 nbtk_stylable_get(<!-- -->)</title>
+ * <para>An example of using nbtk_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;
+ *
+ *   nbtk_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
+nbtk_stylable_get (NbtkStylable *stylable,
+                   const gchar  *first_property_name,
+                                 ...)
+{
+  NbtkStyle *style;
+  va_list args;
+
+  g_return_if_fail (NBTK_IS_STYLABLE (stylable));
+  g_return_if_fail (first_property_name != NULL);
+
+  style = nbtk_stylable_get_style (stylable);
+
+  va_start (args, first_property_name);
+  nbtk_style_get_valist (style, stylable, first_property_name, args);
+  va_end (args);
+}
+
+/**
+ * nbtk_stylable_get_default_value:
+ * @stylable: a #NbtkStylable
+ * @property_name: name of the property to query
+ * @value_out: return location for the default value
+ *
+ * Query @stylable for the default value of property @property_name and
+ * fill @value_out with the result.
+ *
+ * Returns: %TRUE if property @property_name exists and the default value has
+ * been returned.
+ */
+gboolean
+nbtk_stylable_get_default_value (NbtkStylable  *stylable,
+                                 const gchar   *property_name,
+                                 GValue        *value_out)
+{
+  GParamSpec *pspec;
+
+  pspec = nbtk_stylable_find_property (stylable, property_name);
+  if (!pspec)
+    {
+      g_warning ("%s: no style property named `%s' found for class `%s'",
+                  G_STRLOC,
+                  property_name,
+                  g_type_name (G_OBJECT_TYPE (stylable)));
+      return FALSE;
+    }
+
+  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 FALSE;
+    }
+
+  g_value_init (value_out, G_PARAM_SPEC_VALUE_TYPE (pspec));
+  g_param_value_set_default (pspec, value_out);
+  return TRUE;
+}
+
+/**
+ * nbtk_stylable_get_style:
+ * @stylable: a #NbtkStylable
+ *
+ * Retrieves the #NbtkStyle used by @stylable. This function does not
+ * alter the reference count of the returned object.
+ *
+ * Return value: a #NbtkStyle
+ */
+NbtkStyle *
+nbtk_stylable_get_style (NbtkStylable *stylable)
+{
+  NbtkStylableIface *iface;
+
+  g_return_val_if_fail (NBTK_IS_STYLABLE (stylable), NULL);
+
+  iface = NBTK_STYLABLE_GET_IFACE (stylable);
+  if (iface->get_style)
+    return iface->get_style (stylable);
+
+  return g_object_get_data (G_OBJECT (stylable), "nbtk-stylable-style");
+}
+
+/**
+ * nbtk_stylable_set_style:
+ * @stylable: a #NbtkStylable
+ * @style: a #NbtkStyle
+ *
+ * Sets @style as the new #NbtkStyle to be used by @stylable.
+ *
+ * The #NbtkStylable will take ownership of the passed #NbtkStyle.
+ *
+ * After the #NbtkStle has been set, the NbtkStylable::style-set signal
+ * will be emitted.
+ */
+void
+nbtk_stylable_set_style (NbtkStylable *stylable,
+                         NbtkStyle    *style)
+{
+  NbtkStylableIface *iface;
+  NbtkStyle *old_style;
+
+  g_return_if_fail (NBTK_IS_STYLABLE (stylable));
+  g_return_if_fail (NBTK_IS_STYLE (style));
+
+  iface = NBTK_STYLABLE_GET_IFACE (stylable);
+
+  old_style = nbtk_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_CHANGED], 0, old_style);
+  g_object_unref (old_style);
+
+  g_object_notify (G_OBJECT (stylable), "style");
+}
+
+/**
+ * nbtk_stylable_get_container:
+ * @stylable: a #NbtkStylable
+ *
+ * Obtain the parent #NbtkStylable that contains @stylable.
+ *
+ * Return value: The parent #NbtkStylable
+ */
+NbtkStylable*
+nbtk_stylable_get_container (NbtkStylable *stylable)
+{
+  NbtkStylableIface *iface;
+
+  g_return_val_if_fail (NBTK_IS_STYLABLE (stylable), NULL);
+
+  iface = NBTK_STYLABLE_GET_IFACE (stylable);
+
+  if (iface->get_container)
+    return iface->get_container (stylable);
+  else
+    return NULL;
+}
+
+/**
+ * nbtk_stylable_get_base_style:
+ * @stylable: a #NbtkStylable
+ *
+ * Get the parent ancestor #NbtkStylable of @stylable.
+ *
+ * Return value: the parent #NbtkStylable
+ */
+NbtkStylable*
+nbtk_stylable_get_base_style (NbtkStylable *stylable)
+{
+  NbtkStylableIface *iface;
+
+  g_return_val_if_fail (NBTK_IS_STYLABLE (stylable), NULL);
+
+  iface = NBTK_STYLABLE_GET_IFACE (stylable);
+
+  if (iface->get_base_style)
+    return iface->get_base_style (stylable);
+  else
+    return NULL;
+}
+
+
+/**
+ * nbtk_stylable_get_style_id:
+ * @stylable: a #NbtkStylable
+ *
+ * Get the ID value of @stylable
+ *
+ * Return value: the id of @stylable
+ */
+const gchar*
+nbtk_stylable_get_style_id (NbtkStylable *stylable)
+{
+  NbtkStylableIface *iface;
+
+  g_return_val_if_fail (NBTK_IS_STYLABLE (stylable), NULL);
+
+  iface = NBTK_STYLABLE_GET_IFACE (stylable);
+
+  if (iface->get_style_id)
+    return iface->get_style_id (stylable);
+  else
+    return NULL;
+}
+
+/**
+ * nbtk_stylable_get_style_type:
+ * @stylable: a #NbtkStylable
+ *
+ * Get the type name of @stylable
+ *
+ * Return value: the type name of @stylable
+ */
+const gchar*
+nbtk_stylable_get_style_type (NbtkStylable *stylable)
+{
+  NbtkStylableIface *iface;
+
+  g_return_val_if_fail (NBTK_IS_STYLABLE (stylable), NULL);
+
+  iface = NBTK_STYLABLE_GET_IFACE (stylable);
+
+  if (iface->get_style_type)
+    return iface->get_style_type (stylable);
+  else
+    return G_OBJECT_TYPE_NAME (stylable);
+}
+
+/**
+ * nbtk_stylable_get_style_class:
+ * @stylable: a #NbtkStylable
+ *
+ * Get the style class name of @stylable
+ *
+ * Return value: the type name of @stylable
+ */
+const gchar*
+nbtk_stylable_get_style_class (NbtkStylable *stylable)
+{
+  NbtkStylableIface *iface;
+
+  g_return_val_if_fail (NBTK_IS_STYLABLE (stylable), NULL);
+
+  iface = NBTK_STYLABLE_GET_IFACE (stylable);
+
+  if (iface->get_style_class)
+    return iface->get_style_class (stylable);
+  else
+    return NULL;
+}
+
+/**
+ * nbtk_stylable_get_pseudo_class:
+ * @stylable: a #NbtkStylable
+ *
+ * Get the pseudo class name of @stylable
+ *
+ * Return value: the pseudo class name of @stylable
+ */
+const gchar*
+nbtk_stylable_get_pseudo_class (NbtkStylable *stylable)
+{
+  NbtkStylableIface *iface;
+
+  g_return_val_if_fail (NBTK_IS_STYLABLE (stylable), NULL);
+
+  iface = NBTK_STYLABLE_GET_IFACE (stylable);
+
+  if (iface->get_pseudo_class)
+    return iface->get_pseudo_class (stylable);
+  else
+    return NULL;
+}
+
+/**
+ * nbtk_stylable_get_attribute:
+ * @stylable: a #NbtkStylable
+ * @name: attribute name
+ *
+ * Get the named attribute from @stylable
+ *
+ * Return value: the value of the attribute
+ */
+gchar*
+nbtk_stylable_get_attribute (NbtkStylable *stylable,
+                             const gchar  *name)
+{
+  NbtkStylableIface *iface;
+  GValue value = { 0, };
+  GValue string_value = { 0, };
+  gchar *ret;
+  GParamSpec *pspec;
+
+  g_return_val_if_fail (NBTK_IS_STYLABLE (stylable), NULL);
+
+  iface = NBTK_STYLABLE_GET_IFACE (stylable);
+
+  if (iface->get_attribute)
+    return iface->get_attribute (stylable, name);
+
+  /* look up a generic gobject property */
+  pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (stylable), name);
+
+  /* if no such property exists, return NULL */
+  if (pspec == NULL)
+    return NULL;
+
+  g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+  g_object_get_property (G_OBJECT (stylable), name, &value);
+
+  g_value_init (&string_value, G_TYPE_STRING);
+  if (g_value_transform (&value, &string_value))
+    ret = g_strdup (g_value_get_string (&string_value));
+  else
+      ret = NULL;
+
+  g_value_unset (&value);
+  g_value_unset (&string_value);
+
+  return ret;
+}
+
+/**
+ * nbtk_stylable_get_viewport:
+ * @stylable: a #NbtkStylable
+ * @x: location to store X coordinate
+ * @y: location to store Y coordinate
+ * @width: location to store width
+ * @height: location to store height
+ *
+ * Obtain the position and dimensions of @stylable.
+ *
+ * Return value: true if the function succeeded
+ */
+gboolean
+nbtk_stylable_get_viewport (NbtkStylable *stylable,
+                            gint         *x,
+                            gint         *y,
+                            gint         *width,
+                            gint         *height)
+{
+  NbtkStylableIface *iface;
+
+  g_return_val_if_fail (NBTK_IS_STYLABLE (stylable), FALSE);
+
+  iface = NBTK_STYLABLE_GET_IFACE (stylable);
+  if (iface->get_viewport)
+    return iface->get_viewport (stylable, x, y, width, height);
+  else
+    return FALSE;
+}
+
+
+/**
+ * nbtk_stylable_changed:
+ * @stylable: A #NbtkStylable
+ *
+ * Emit the "stylable-changed" signal on @stylable
+ */
+void
+nbtk_stylable_changed (NbtkStylable *stylable)
+{
+  g_signal_emit (stylable, stylable_signals[CHANGED], 0, NULL);
+}
diff --git a/src/nbtk/nbtk-stylable.h b/src/nbtk/nbtk-stylable.h
new file mode 100644
index 0000000..9a8dd1e
--- /dev/null
+++ b/src/nbtk/nbtk-stylable.h
@@ -0,0 +1,123 @@
+/*
+ * nbtk-stylable.h: Interface for stylable objects
+ *
+ * Copyright 2008, 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Emmanuele Bassi <ebassi openedhand com>
+ *             Thomas Wood <thomas linux intel com>
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_STYLABLE_H__
+#define __NBTK_STYLABLE_H__
+
+#include <glib-object.h>
+#include <nbtk/nbtk-style.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_STYLABLE              (nbtk_stylable_get_type ())
+#define NBTK_STYLABLE(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_STYLABLE, NbtkStylable))
+#define NBTK_IS_STYLABLE(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_STYLABLE))
+#define NBTK_STYLABLE_IFACE(iface)      (G_TYPE_CHECK_CLASS_CAST ((iface), NBTK_TYPE_STYLABLE, NbtkStylableIface))
+#define NBTK_IS_STYLABLE_IFACE(iface)   (G_TYPE_CHECK_CLASS_TYPE ((iface), NBTK_TYPE_STYLABLE))
+#define NBTK_STYLABLE_GET_IFACE(obj)    (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NBTK_TYPE_STYLABLE, NbtkStylableIface))
+
+/* NbtkStylableIface is defined in nbtk-style.h */
+
+struct _NbtkStylableIface
+{
+  GTypeInterface g_iface;
+
+  /* virtual functions */
+  NbtkStyle *(* get_style) (NbtkStylable *stylable);
+  void       (* set_style) (NbtkStylable *stylable,
+                            NbtkStyle    *style);
+
+  /* context virtual functions */
+  NbtkStylable *(*get_container)    (NbtkStylable *stylable);
+  NbtkStylable *(*get_base_style)   (NbtkStylable *stylable);
+  const gchar  *(*get_style_id)     (NbtkStylable *stylable);
+  const gchar  *(*get_style_type)   (NbtkStylable *stylable);
+  const gchar  *(*get_style_class)  (NbtkStylable *stylable);
+  const gchar  *(*get_pseudo_class) (NbtkStylable *stylable);
+  gchar        *(*get_attribute)    (NbtkStylable *stylable,
+                                     const gchar  *name);
+  gboolean      (*get_viewport)     (NbtkStylable *stylable,
+                                     gint         *x,
+                                     gint         *y,
+                                     gint         *width,
+                                     gint         *height);
+
+  /* signals, not vfuncs */
+  void (* style_notify)     (NbtkStylable *stylable,
+                             GParamSpec   *pspec);
+  void (* style_changed)    (NbtkStylable *stylable);
+
+  void (* stylable_changed) (NbtkStylable *stylable);
+};
+
+GType        nbtk_stylable_get_type               (void) G_GNUC_CONST;
+
+void         nbtk_stylable_iface_install_property (NbtkStylableIface *iface,
+                                                   GType              owner_type,
+                                                   GParamSpec        *pspec);
+
+void         nbtk_stylable_freeze_notify          (NbtkStylable      *stylable);
+void         nbtk_stylable_notify                 (NbtkStylable      *stylable,
+                                                   const gchar       *property_name);
+void         nbtk_stylable_thaw_notify            (NbtkStylable      *stylable);
+GParamSpec **nbtk_stylable_list_properties        (NbtkStylable      *stylable,
+                                                   guint             *n_props);
+GParamSpec * nbtk_stylable_find_property          (NbtkStylable      *stylable,
+                                                   const gchar       *property_name);
+void         nbtk_stylable_set_style              (NbtkStylable      *stylable,
+                                                   NbtkStyle         *style);
+NbtkStyle *  nbtk_stylable_get_style              (NbtkStylable      *stylable);
+
+void         nbtk_stylable_get                    (NbtkStylable      *stylable,
+                                                   const gchar       *first_property_name,
+                                                   ...) G_GNUC_NULL_TERMINATED;
+void         nbtk_stylable_get_property           (NbtkStylable      *stylable,
+                                                   const gchar       *property_name,
+                                                   GValue            *value);
+gboolean     nbtk_stylable_get_default_value      (NbtkStylable      *stylable,
+                                                   const gchar       *property_name,
+                                                   GValue            *value_out);
+
+NbtkStylable* nbtk_stylable_get_container     (NbtkStylable *stylable);
+NbtkStylable* nbtk_stylable_get_base_style    (NbtkStylable *stylable);
+const gchar*  nbtk_stylable_get_style_id      (NbtkStylable *stylable);
+const gchar*  nbtk_stylable_get_style_type    (NbtkStylable *stylable);
+const gchar*  nbtk_stylable_get_style_class   (NbtkStylable *stylable);
+const gchar*  nbtk_stylable_get_pseudo_class  (NbtkStylable *stylable);
+gchar*        nbtk_stylable_get_attribute     (NbtkStylable *stylable,
+                                               const gchar  *name);
+gboolean      nbtk_stylable_get_viewport      (NbtkStylable *stylable,
+                                               gint         *x,
+                                               gint         *y,
+                                               gint         *width,
+                                               gint         *height);
+
+void nbtk_stylable_changed (NbtkStylable *stylable);
+G_END_DECLS
+
+#endif /* __NBTK_STYLABLE_H__ */
diff --git a/src/nbtk/nbtk-style.c b/src/nbtk/nbtk-style.c
new file mode 100644
index 0000000..f5eeb3b
--- /dev/null
+++ b/src/nbtk/nbtk-style.c
@@ -0,0 +1,742 @@
+/*
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/**
+ * SECTION:nbtk-style
+ * @short_description: a data store for style properties
+ *
+ * #NbtkStyle is a property data store that can read properties from a style
+ * sheet. It is queried with objects that implement the NbtkStylable
+ * interface.
+ */
+
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib-object.h>
+#include <gobject/gvaluecollector.h>
+#include <glib/gi18n-lib.h>
+
+#include <clutter/clutter.h>
+
+#include <ccss/ccss.h>
+
+#include "nbtk-stylable.h"
+#include "nbtk-style.h"
+#include "nbtk-types.h"
+#include "nbtk-marshal.h"
+#include "nbtk-widget.h"
+
+enum
+{
+  CHANGED,
+
+  LAST_SIGNAL
+};
+
+#define NBTK_STYLE_GET_PRIVATE(obj) \
+        (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NBTK_TYPE_STYLE, NbtkStylePrivate))
+
+#define NBTK_STYLE_ERROR g_style_error_quark ()
+
+typedef struct {
+  GType value_type;
+  gchar *value_name;
+  GValue value;
+} StyleProperty;
+
+struct _NbtkStylePrivate
+{
+  ccss_stylesheet_t *stylesheet;
+  GList *image_paths;
+
+  GHashTable *style_hash;
+  GHashTable *node_hash;
+};
+
+typedef struct {
+  ccss_node_t parent;
+  NbtkStylable *stylable;
+  NbtkStylableIface *iface;
+} nbtk_style_node_t;
+
+static ccss_function_t const * peek_css_functions (void);
+
+static ccss_node_class_t * peek_node_class (void);
+
+static guint style_signals[LAST_SIGNAL] = { 0, };
+
+static NbtkStyle *default_style = NULL;
+
+G_DEFINE_TYPE (NbtkStyle, nbtk_style, G_TYPE_OBJECT);
+
+static GQuark
+g_style_error_quark (void)
+{
+  return g_quark_from_static_string ("nbtk-style-error-quark");
+}
+
+static gboolean
+nbtk_style_real_load_from_file (NbtkStyle    *style,
+                                const gchar  *filename,
+                                GError      **error,
+                                gint          priority)
+{
+  NbtkStylePrivate *priv;
+  ccss_grammar_t *grammar;
+  GError *internal_error;
+  gchar *path;
+  GList *l;
+
+  g_return_val_if_fail (NBTK_IS_STYLE (style), FALSE);
+  g_return_val_if_fail (filename != NULL, FALSE);
+
+  priv = NBTK_STYLE (style)->priv;
+
+  if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR))
+    {
+      internal_error = g_error_new (NBTK_STYLE_ERROR,
+                                    NBTK_STYLE_ERROR_INVALID_FILE,
+                                    _("Invalid theme file '%s'"), filename);
+      g_propagate_error (error, internal_error);
+      return FALSE;
+    }
+
+
+  /* add the path of the stylesheet to the search path */
+  path = g_path_get_dirname (filename);
+
+  /* make sure path is valid */
+  if (!path)
+    return TRUE;
+
+  for (l = priv->image_paths; l; l = l->next)
+    {
+      if (g_str_equal ((gchar *)l->data, path))
+        {
+          /* we have this path already */
+          g_free (path);
+          path = NULL;
+        }
+    }
+
+  /* Add the new path */
+  if (path)
+    priv->image_paths = g_list_append (priv->image_paths, path);
+
+  /* now load the stylesheet */
+  if (!priv->stylesheet)
+    {
+      grammar = ccss_grammar_create_css ();
+      ccss_grammar_add_functions (grammar, peek_css_functions ());
+      priv->stylesheet = ccss_grammar_create_stylesheet_from_file (grammar,
+                                                                   filename,
+                                                                   path);
+      ccss_grammar_destroy (grammar);
+    }
+  else
+    {
+      ccss_stylesheet_add_from_file (priv->stylesheet, filename, priority, path);
+    }
+
+  g_signal_emit (style, style_signals[CHANGED], 0, NULL);
+
+  return TRUE;
+}
+
+/**
+ * nbtk_style_load_from_file:
+ * @style: a #NbtkStyle
+ * @filename: filename of the style sheet to load
+ * @error: a #GError or #NULL
+ *
+ * Load style information from the specified file.
+ *
+ * returns: TRUE if the style information was loaded successfully. Returns
+ * FALSE on error.
+ */
+gboolean
+nbtk_style_load_from_file (NbtkStyle    *style,
+                           const gchar  *filename,
+                           GError      **error)
+{
+  return nbtk_style_real_load_from_file (style, filename, error,
+                                         CCSS_STYLESHEET_AUTHOR);
+}
+
+static void
+nbtk_style_finalize (GObject *gobject)
+{
+  NbtkStylePrivate *priv = ((NbtkStyle *)gobject)->priv;
+  GList *l;
+
+  for (l = priv->image_paths; l; l = g_list_delete_link (l, l))
+  {
+    g_free (l->data);
+  }
+
+  G_OBJECT_CLASS (nbtk_style_parent_class)->finalize (gobject);
+}
+
+static void
+nbtk_style_class_init (NbtkStyleClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (NbtkStylePrivate));
+
+  gobject_class->finalize = nbtk_style_finalize;
+
+  /**
+   * NbtkStyle::changed:
+   *
+   * Indicates that the style data has changed in some way. For example, a new
+   * stylesheet may have been loaded.
+   */
+
+  style_signals[CHANGED] =
+    g_signal_new ("changed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (NbtkStyleClass, changed),
+                  NULL, NULL,
+                  _nbtk_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+}
+
+/* url loader for libccss */
+static char *
+ccss_url (GSList const  *args,
+          void          *user_data)
+{
+  const gchar *given_path, *filename;
+  gchar *test_path;
+
+  g_return_val_if_fail (args, NULL);
+
+  given_path = (char const *) args->data;
+
+  /* we can only deal with local paths */
+  if (!g_str_has_prefix (given_path, "file://"))
+    return NULL;
+  filename = &given_path[7];
+
+  /*
+   * Handle absolute paths correctly
+   */
+  if (*filename == '/')
+    return strdup (filename);
+
+  /* first try looking in the theme dir */
+  test_path = g_build_filename (g_get_user_config_dir (),
+                                "nbtk",
+                                filename,
+                                NULL);
+  if (g_file_test (test_path, G_FILE_TEST_IS_REGULAR))
+    return test_path;
+  g_free (test_path);
+
+  if (user_data)
+    {
+      test_path = g_build_filename ((gchar *) user_data, filename, NULL);
+
+      if (g_file_test (test_path, G_FILE_TEST_IS_REGULAR))
+        return test_path;
+
+      g_free (test_path);
+    }
+  else
+    {
+      g_warning ("No path available css url resolver!");
+    }
+
+  /* couldn't find the image anywhere, so just return the filename */
+  return strdup (given_path);
+}
+
+static ccss_function_t const *
+peek_css_functions (void)
+{
+  static ccss_function_t const ccss_functions[] =
+  {
+    { "url", ccss_url },
+    { NULL }
+  };
+
+  return ccss_functions;
+}
+
+
+static void
+nbtk_style_init (NbtkStyle *style)
+{
+  NbtkStylePrivate *priv;
+
+  style->priv = priv = NBTK_STYLE_GET_PRIVATE (style);
+
+  /* create a hash table to look up pointer keys and values */
+  style->priv->node_hash = g_hash_table_new_full (NULL, NULL,
+                                                  NULL, g_free);
+  style->priv->style_hash = g_hash_table_new_full (NULL, NULL,
+                                                   NULL, (GDestroyNotify) ccss_style_destroy);
+
+}
+
+/**
+ * nbtk_style_new:
+ *
+ * Creates a new #NbtkStyle object. This must be freed using #g_object_unref
+ * when no longer required.
+ *
+ * Returns: a newly allocated #NbtkStyle
+ */
+NbtkStyle *
+nbtk_style_new (void)
+{
+  return g_object_new (NBTK_TYPE_STYLE, NULL);
+}
+
+/**
+ * nbtk_style_get_default:
+ *
+ * Return the default NbtkStyle object. This includes the current theme (if
+ * any).
+ *
+ * Returns: a #NbtkStyle object. This must not be freed or unref'd by
+ * applications
+ */
+NbtkStyle *
+nbtk_style_get_default (void)
+{
+  if (G_LIKELY (default_style))
+    return default_style;
+
+  default_style = g_object_new (NBTK_TYPE_STYLE, NULL);
+
+  return default_style;
+}
+
+/* functions for ccss */
+
+static nbtk_style_node_t *
+get_container (nbtk_style_node_t *node)
+{
+  nbtk_style_node_t *container;
+  ClutterActor      *parent;
+
+  g_return_val_if_fail (node, NULL);
+  g_return_val_if_fail (node->iface, NULL);
+  g_return_val_if_fail (node->stylable, NULL);
+
+  parent = clutter_actor_get_parent (CLUTTER_ACTOR (node->stylable));
+  while (parent && !NBTK_IS_WIDGET (parent))
+    parent = clutter_actor_get_parent (CLUTTER_ACTOR (parent));
+
+  if (!parent)
+    return NULL;
+
+  container = g_new0 (nbtk_style_node_t, 1);
+  ccss_node_init ((ccss_node_t*) container, peek_node_class ());
+  container->iface = node->iface;
+  container->stylable = NBTK_STYLABLE (parent);
+
+  return container;
+}
+
+static const gchar*
+get_style_id (nbtk_style_node_t *node)
+{
+  return nbtk_stylable_get_style_id (node->stylable);
+}
+
+static const gchar*
+get_style_type (nbtk_style_node_t *node)
+{
+  return nbtk_stylable_get_style_type (node->stylable);
+}
+
+static const gchar*
+get_style_class (nbtk_style_node_t *node)
+{
+  return nbtk_stylable_get_style_class (node->stylable);
+}
+
+static const gchar*
+get_pseudo_class (nbtk_style_node_t *node)
+{
+  return nbtk_stylable_get_pseudo_class (node->stylable);
+}
+
+static const gchar*
+get_attribute (nbtk_style_node_t *node, const char *name)
+{
+  return nbtk_stylable_get_attribute (node->stylable, name);
+}
+
+static void
+release (nbtk_style_node_t *node)
+{
+  g_return_if_fail (node);
+
+  g_free (node);
+}
+
+static ccss_node_class_t *
+peek_node_class (void)
+{
+  static ccss_node_class_t _node_class = {
+    .is_a             = NULL,
+    .get_container    = (ccss_node_get_container_f) get_container,
+    .get_id           = (ccss_node_get_id_f) get_style_id,
+    .get_type         = (ccss_node_get_type_f) get_style_type,
+    .get_class        = (ccss_node_get_class_f) get_style_class,
+    .get_pseudo_class = (ccss_node_get_pseudo_class_f) get_pseudo_class,
+    .get_viewport     = NULL,// (ccss_node_get_viewport_f) get_viewport,
+    .get_attribute    = (ccss_node_get_attribute_f) get_attribute,
+    .release          = (ccss_node_release_f) release
+  };
+
+  return &_node_class;
+}
+
+static void
+nbtk_style_fetch_ccss_property (ccss_style_t  *ccss_style,
+                                GParamSpec    *pspec,
+                                GValue        *value)
+{
+  gboolean value_set = FALSE;
+
+  g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+
+  if (G_PARAM_SPEC_VALUE_TYPE (pspec))
+    {
+      double number;
+
+      if (G_IS_PARAM_SPEC_INT (pspec))
+        {
+          if (ccss_style_get_double (ccss_style, pspec->name, &number))
+            {
+              g_value_set_int (value, (gint) number);
+              value_set = TRUE;
+            }
+        }
+      else if (G_IS_PARAM_SPEC_UINT (pspec))
+        {
+          if (ccss_style_get_double (ccss_style, pspec->name, &number))
+            {
+              g_value_set_uint (value, (guint) number);
+              value_set = TRUE;
+            }
+        }
+      else if (G_PARAM_SPEC_VALUE_TYPE (pspec) == NBTK_TYPE_BORDER_IMAGE &&
+               !g_strcmp0 ("border-image", pspec->name))
+        {
+              ccss_border_image_t const *border_image;
+
+          if (ccss_style_get_property (ccss_style,
+                                        "border-image",
+                                        (ccss_property_base_t const **) &border_image))
+            {
+              if (border_image &&
+                  border_image->base.state == CCSS_PROPERTY_STATE_SET)
+                {
+                  g_value_set_boxed (value, border_image);
+                  value_set = TRUE;
+                }
+            }
+        }
+      else if (NBTK_TYPE_PADDING == G_PARAM_SPEC_VALUE_TYPE (pspec) &&
+                0 == g_strcmp0 ("padding", pspec->name))
+        {
+          NbtkPadding padding = { 0, };
+          gboolean padding_set = 0;
+
+          if (ccss_style_get_double (ccss_style, "padding-top", &number))
+            {
+              padding.top = number;
+              padding_set = TRUE;
+            }
+
+          if (ccss_style_get_double (ccss_style, "padding-right", &number))
+            {
+              padding.right = number;
+              padding_set = TRUE;
+            }
+
+          if (ccss_style_get_double (ccss_style, "padding-bottom", &number))
+            {
+              padding.bottom = number;
+              padding_set = TRUE;
+            }
+
+          if (ccss_style_get_double (ccss_style, "padding-left", &number))
+            {
+              padding.left = number;
+              padding_set = TRUE;
+            }
+
+          if (padding_set)
+            {
+              g_value_set_boxed (value, &padding);
+              value_set = TRUE;
+            }
+        }
+      else
+        {
+          gchar *string = NULL;
+
+          ccss_style_get_string (ccss_style, pspec->name, &string);
+
+          if (string)
+            {
+              if (CLUTTER_IS_PARAM_SPEC_COLOR (pspec))
+                {
+                  ClutterColor color = { 0, };
+
+                  clutter_color_from_string (&color, string);
+                  clutter_value_set_color (value, &color);
+
+                  value_set = TRUE;
+                }
+              else
+                if (G_IS_PARAM_SPEC_STRING (pspec))
+                  {
+                    g_value_set_string (value, string);
+                    value_set = TRUE;
+                  }
+              g_free (string);
+            }
+        }
+    }
+
+  /* no value was found in css, so copy in the default value */
+  if (!value_set)
+    g_param_value_set_default (pspec, value);
+}
+
+static ccss_style_t*
+nbtk_style_get_ccss_query (NbtkStyle         *style,
+                           NbtkStylable      *stylable)
+{
+  NbtkStylableIface *iface = NBTK_STYLABLE_GET_IFACE (stylable);
+  ccss_style_t *ccss_style;
+  nbtk_style_node_t *ccss_node;
+
+  ccss_node = g_hash_table_lookup (style->priv->node_hash, stylable);
+
+  if (!ccss_node)
+    {
+      ccss_node = g_new0 (nbtk_style_node_t, 1);
+      ccss_node_init ((ccss_node_t*) ccss_node, peek_node_class ());
+      ccss_node->iface = iface;
+      ccss_node->stylable = stylable;
+
+      g_hash_table_insert (style->priv->node_hash, stylable, ccss_node);
+      g_signal_connect_swapped (stylable, "stylable-changed",
+                                G_CALLBACK (g_hash_table_remove),
+                                style->priv->node_hash);
+
+
+      g_object_weak_ref ((GObject*) stylable,
+                        (GWeakNotify) g_hash_table_remove, style->priv->node_hash);
+    }
+
+
+  ccss_style = g_hash_table_lookup (style->priv->style_hash, stylable);
+
+  if (!ccss_style)
+    {
+      ccss_style = ccss_stylesheet_query (style->priv->stylesheet,
+                                          (ccss_node_t *) ccss_node);
+
+      g_hash_table_insert (style->priv->style_hash, stylable, ccss_style);
+
+      /* remove the cache if the stylable changes */
+      g_signal_connect_swapped (stylable, "stylable-changed",
+                                G_CALLBACK (g_hash_table_remove),
+                                style->priv->style_hash);
+
+      g_object_weak_ref ((GObject*) stylable,
+                         (GWeakNotify) g_hash_table_remove, style->priv->style_hash);
+    }
+
+  return ccss_style;
+
+}
+
+
+/**
+ * nbtk_style_get_property:
+ * @style: the style data store object
+ * @stylable: a stylable to retreive the data for
+ * @pspec: a #GParamSpec describing the property required
+ * @value: a #GValue to place the return value in
+ *
+ * Requests the property described in @pspec for the specified stylable
+ */
+
+void
+nbtk_style_get_property (NbtkStyle    *style,
+                         NbtkStylable *stylable,
+                         GParamSpec   *pspec,
+                         GValue       *value)
+{
+  NbtkStylePrivate *priv;
+  gboolean value_set = FALSE;
+
+  g_return_if_fail (NBTK_IS_STYLE (style));
+  g_return_if_fail (NBTK_IS_STYLABLE (stylable));
+  g_return_if_fail (pspec != NULL);
+  g_return_if_fail (value != NULL);
+
+  priv = style->priv;
+
+  /* look up the property in the css */
+  if (priv->stylesheet)
+    {
+      ccss_style_t *ccss_style;
+
+      ccss_style = nbtk_style_get_ccss_query (style, stylable);
+      if (ccss_style)
+        {
+          nbtk_style_fetch_ccss_property (ccss_style, pspec, value);
+          value_set = TRUE;
+        }
+    }
+
+  /* no value was found in css, so copy in the default value */
+  if (!value_set)
+    {
+      g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+      g_param_value_set_default (pspec, value);
+    }
+}
+
+/**
+ * nbtk_style_get_valist:
+ * @style: a #NbtkStyle
+ * @stylable: a #NbtkStylable
+ * @first_property_name: name of the first property to get
+ * @va_args: return location for the first property, followed optionally
+ *   by more name/return location pairs, followed by %NULL
+ *
+ * Gets the style properties for @stylable from @style.
+ *
+ * Please refer to nbtk_style_get() for further information.
+ */
+void
+nbtk_style_get_valist (NbtkStyle     *style,
+                       NbtkStylable  *stylable,
+                       const gchar   *first_property_name,
+                       va_list        va_args)
+{
+  NbtkStylePrivate *priv;
+  const gchar *name = first_property_name;
+  gboolean values_set = FALSE;
+
+  g_return_if_fail (NBTK_IS_STYLE (style));
+  g_return_if_fail (NBTK_IS_STYLABLE (stylable));
+  g_return_if_fail (style->priv != NULL);
+
+  priv = style->priv;
+
+  /* look up the property in the css */
+  if (priv->stylesheet)
+    {
+      ccss_style_t *ccss_style;
+
+      ccss_style = nbtk_style_get_ccss_query (style, stylable);
+
+      if (ccss_style)
+        {
+          while (name)
+            {
+              GValue value = { 0, };
+              gchar *error = NULL;
+              GParamSpec *pspec = nbtk_stylable_find_property (stylable, name);
+              nbtk_style_fetch_ccss_property (ccss_style, pspec, &value);
+              G_VALUE_LCOPY (&value, va_args, 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 (va_args, gchar*);
+            }
+          values_set = TRUE;
+        }
+    }
+
+  if (!values_set)
+    {
+      /* Set the remaining properties to their default values
+       * even if broken out of the above loop. */
+      while (name)
+        {
+          GValue value = { 0, };
+          gchar *error = NULL;
+          nbtk_stylable_get_default_value (stylable, name, &value);
+          G_VALUE_LCOPY (&value, va_args, 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 (va_args, gchar*);
+        }
+    }
+}
+
+/**
+ * nbtk_style_get:
+ * @style: a #NbtkStyle
+ * @stylable: a #NbtkStylable
+ * @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 from @style.
+ *
+ * In general, a copy is made of the property contents and the caller
+ * is responsible for freeing the memory in the appropriate manner for
+ * the property type.
+ */
+void
+nbtk_style_get (NbtkStyle     *style,
+                NbtkStylable  *stylable,
+                const gchar   *first_property_name,
+                ...)
+{
+  va_list va_args;
+
+  g_return_if_fail (NBTK_IS_STYLE (style));
+  g_return_if_fail (first_property_name != NULL);
+
+  va_start (va_args, first_property_name);
+  nbtk_style_get_valist (style, stylable, first_property_name, va_args);
+  va_end (va_args);
+}
diff --git a/src/nbtk/nbtk-style.h b/src/nbtk/nbtk-style.h
new file mode 100644
index 0000000..35efc12
--- /dev/null
+++ b/src/nbtk/nbtk-style.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_STYLE_H__
+#define __NBTK_STYLE_H__
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_STYLE                 (nbtk_style_get_type ())
+#define NBTK_STYLE(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_STYLE, NbtkStyle))
+#define NBTK_IS_STYLE(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_STYLE))
+#define NBTK_STYLE_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), NBTK_TYPE_STYLE, NbtkStyleClass))
+#define NBTK_IS_STYLE_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), NBTK_TYPE_STYLE))
+#define NBTK_STYLE_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), NBTK_TYPE_STYLE, NbtkStyleClass))
+
+typedef struct _NbtkStyle               NbtkStyle;
+typedef struct _NbtkStylePrivate        NbtkStylePrivate;
+typedef struct _NbtkStyleClass          NbtkStyleClass;
+
+/* forward declaration */
+typedef struct _NbtkStylable            NbtkStylable; /* dummy typedef */
+typedef struct _NbtkStylableIface       NbtkStylableIface;
+
+typedef enum { /*< prefix=NBTK_STYLE_ERROR >*/
+  NBTK_STYLE_ERROR_INVALID_FILE
+} NbtkStyleError;
+
+/**
+ * NbtkStyle:
+ *
+ * The contents of this structure is private and should only be accessed using
+ * the provided API.
+ */
+struct _NbtkStyle
+{
+  /*< private >*/
+  GObject parent_instance;
+
+  NbtkStylePrivate *priv;
+};
+
+struct _NbtkStyleClass
+{
+  GObjectClass parent_class;
+
+  void (* changed) (NbtkStyle *style);
+};
+
+GType            nbtk_style_get_type     (void) G_GNUC_CONST;
+
+NbtkStyle *      nbtk_style_get_default  (void);
+NbtkStyle *      nbtk_style_new          (void);
+
+gboolean         nbtk_style_load_from_file (NbtkStyle     *style,
+                                            const gchar   *filename,
+                                            GError       **error);
+void             nbtk_style_get_property   (NbtkStyle     *style,
+                                            NbtkStylable  *stylable,
+                                            GParamSpec    *pspec,
+                                            GValue        *value);
+void             nbtk_style_get            (NbtkStyle     *style,
+                                            NbtkStylable  *stylable,
+                                            const gchar   *first_property_name,
+                                            ...) G_GNUC_NULL_TERMINATED;
+void             nbtk_style_get_valist     (NbtkStyle     *style,
+                                            NbtkStylable  *stylable,
+                                            const gchar   *first_property_name,
+                                            va_list        va_args);
+
+G_END_DECLS
+
+#endif /* __NBTK_STYLE_H__ */
diff --git a/src/nbtk/nbtk-subtexture.c b/src/nbtk/nbtk-subtexture.c
new file mode 100644
index 0000000..4841dc8
--- /dev/null
+++ b/src/nbtk/nbtk-subtexture.c
@@ -0,0 +1,575 @@
+/*
+ * nbtk-subtexture.h: Class to wrap a texture and "subframe" it.
+ * based on
+ * nbtk-texture-frame.c: Expandible texture actor
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cogl/cogl.h>
+#include <clutter/clutter.h>
+
+#include "nbtk-subtexture.h"
+
+enum
+{
+  PROP_0,
+
+  PROP_PARENT_TEXTURE,
+
+  PROP_TOP,
+  PROP_LEFT,
+  PROP_WIDTH,
+  PROP_HEIGHT
+};
+
+G_DEFINE_TYPE (NbtkSubtexture, nbtk_subtexture, CLUTTER_TYPE_ACTOR);
+
+#define NBTK_SUBTEXTURE_GET_PRIVATE(obj)     (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NBTK_TYPE_SUBTEXTURE, NbtkSubtexturePrivate))
+
+struct _NbtkSubtexturePrivate
+{
+  ClutterTexture *parent_texture;
+
+  int left;
+  int top;
+  int width;
+  int height;
+
+  CoglHandle material;
+};
+
+static void
+nbtk_subtexture_get_preferred_width (ClutterActor *self,
+                                        gfloat   for_height,
+                                        gfloat  *min_width_p,
+                                        gfloat  *natural_width_p)
+{
+  NbtkSubtexturePrivate *priv = NBTK_SUBTEXTURE (self)->priv;
+
+  if (G_UNLIKELY (priv->parent_texture == NULL))
+    {
+      if (min_width_p)
+        *min_width_p = 0;
+
+      if (natural_width_p)
+        *natural_width_p = 0;
+    }
+  else
+    {
+	if (min_width_p)
+		*min_width_p = priv->width;
+	if (natural_width_p)
+		*natural_width_p = priv->width;
+    }
+}
+
+static void
+nbtk_subtexture_get_preferred_height (ClutterActor *self,
+                                         gfloat   for_width,
+                                         gfloat  *min_height_p,
+                                         gfloat  *natural_height_p)
+{
+  NbtkSubtexturePrivate *priv = NBTK_SUBTEXTURE (self)->priv;
+
+  if (G_UNLIKELY (priv->parent_texture == NULL))
+    {
+      if (min_height_p)
+        *min_height_p = 0;
+
+      if (natural_height_p)
+        *natural_height_p = 0;
+    }
+  else
+    {
+	if (min_height_p)
+		*min_height_p = priv->height;
+	if (natural_height_p)
+		*natural_height_p = priv->height;
+    }
+}
+
+static void
+nbtk_subtexture_realize (ClutterActor *self)
+{
+  NbtkSubtexturePrivate *priv = NBTK_SUBTEXTURE (self)->priv;
+
+  if (priv->material != COGL_INVALID_HANDLE)
+    return;
+
+  priv->material = cogl_material_new ();
+
+  CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
+}
+
+static void
+nbtk_subtexture_unrealize (ClutterActor *self)
+{
+  NbtkSubtexturePrivate *priv = NBTK_SUBTEXTURE (self)->priv;
+
+  if (priv->material == COGL_INVALID_HANDLE)
+    return;
+
+  cogl_material_unref (priv->material);
+  priv->material = COGL_INVALID_HANDLE;
+
+  CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
+}
+
+static void
+nbtk_subtexture_paint (ClutterActor *self)
+{
+  NbtkSubtexturePrivate *priv = NBTK_SUBTEXTURE (self)->priv;
+  CoglHandle cogl_texture = COGL_INVALID_HANDLE;
+  ClutterActorBox box = { 0, 0, 0, 0 };
+  gfloat tx1, ty1, tx2, ty2, tex_width, tex_height, width, height;
+  guint8 opacity;
+
+  /* no need to paint stuff if we don't have a texture */
+  if (G_UNLIKELY (priv->parent_texture == NULL))
+    return;
+
+  /* parent texture may have been hidden, so need to make sure it gets
+   * realized
+   */
+  if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent_texture))
+    clutter_actor_realize (CLUTTER_ACTOR (priv->parent_texture));
+
+  cogl_texture = clutter_texture_get_cogl_texture (priv->parent_texture);
+  if (cogl_texture == COGL_INVALID_HANDLE)
+    return;
+
+  tex_width  = cogl_texture_get_width (cogl_texture);
+  tex_height = cogl_texture_get_height (cogl_texture);
+
+  clutter_actor_get_allocation_box (self, &box);
+
+  width = box.x2 - box.x1;
+  height = box.y2 - box.y1;
+
+  tx1 = 1.0 *  priv->left / tex_width;
+  ty1 = 1.0 * priv->top / tex_height;
+
+  tx2 = 1.0 * (priv->left + priv->width) / tex_width;
+  ty2 = 1.0 * (priv->top + priv->height) / tex_height;
+
+
+  opacity = clutter_actor_get_paint_opacity (self);
+
+  g_assert (priv->material != COGL_INVALID_HANDLE);
+
+  /* set the source material using the parent texture's COGL handle */
+  cogl_material_set_color4ub (priv->material, 255, 255, 255, opacity);
+  cogl_material_set_layer (priv->material, 0, cogl_texture);
+  cogl_set_source (priv->material);
+
+  cogl_rectangle_with_texture_coords (0,0, (float)width, (float)height,
+		tx1, ty1, tx2, ty2);
+}
+
+static inline void
+nbtk_subtexture_set_frame_internal (NbtkSubtexture *frame,
+                                       int            left,
+                                       int            top,
+                                       int            width,
+                                       int            height)
+{
+  NbtkSubtexturePrivate *priv = frame->priv;
+  GObject *gobject = G_OBJECT (frame);
+  gboolean changed = FALSE;
+
+  g_object_freeze_notify (gobject);
+
+  if (priv->top != top)
+    {
+      priv->top = top;
+      g_object_notify (gobject, "top");
+      changed = TRUE;
+    }
+
+  if (priv->left != left)
+    {
+      priv->left = left;
+      g_object_notify (gobject, "left");
+      changed = TRUE;
+    }
+
+  if (priv->width != width)
+    {
+      priv->width = width;
+      g_object_notify (gobject, "width");
+      changed = TRUE;
+    }
+
+  if (priv->height != height)
+    {
+      priv->height = height;
+      g_object_notify (gobject, "height");
+      changed = TRUE;
+    }
+
+  if (changed && CLUTTER_ACTOR_IS_VISIBLE (frame))
+    clutter_actor_queue_redraw (CLUTTER_ACTOR (frame));
+
+  g_object_thaw_notify (gobject);
+}
+
+static void
+nbtk_subtexture_set_property (GObject      *gobject,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  NbtkSubtexture *frame = NBTK_SUBTEXTURE (gobject);
+  NbtkSubtexturePrivate *priv = frame->priv;
+
+  switch (prop_id)
+    {
+    case PROP_PARENT_TEXTURE:
+      nbtk_subtexture_set_parent_texture (frame,
+                                             g_value_get_object (value));
+      break;
+
+    case PROP_TOP:
+      nbtk_subtexture_set_frame_internal (frame,
+                                             priv->left,
+                                             g_value_get_int (value),
+                                             priv->width,
+                                             priv->height);
+      break;
+
+    case PROP_LEFT:
+      nbtk_subtexture_set_frame_internal (frame,
+                                             g_value_get_int (value),
+                                             priv->top,
+                                             priv->width,
+                                             priv->height);
+      break;
+
+    case PROP_WIDTH:
+      nbtk_subtexture_set_frame_internal (frame,
+                                             priv->left,
+                                             priv->top,
+                                             g_value_get_int (value),
+                                             priv->height);
+      break;
+
+    case PROP_HEIGHT:
+      nbtk_subtexture_set_frame_internal (frame,
+                                             priv->left,
+                                             priv->top,
+                                             priv->width,
+                                             g_value_get_int (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+nbtk_subtexture_get_property (GObject    *gobject,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  NbtkSubtexturePrivate *priv = NBTK_SUBTEXTURE (gobject)->priv;
+
+  switch (prop_id)
+    {
+    case PROP_PARENT_TEXTURE:
+      g_value_set_object (value, priv->parent_texture);
+      break;
+
+    case PROP_LEFT:
+      g_value_set_int (value, priv->left);
+      break;
+
+    case PROP_TOP:
+      g_value_set_int (value, priv->top);
+      break;
+
+    case PROP_WIDTH:
+      g_value_set_int (value, priv->width);
+      break;
+
+    case PROP_HEIGHT:
+      g_value_set_int (value, priv->height);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+nbtk_subtexture_dispose (GObject *gobject)
+{
+  NbtkSubtexturePrivate *priv = NBTK_SUBTEXTURE (gobject)->priv;
+
+  if (priv->parent_texture)
+    {
+      g_object_unref (priv->parent_texture);
+      priv->parent_texture = NULL;
+    }
+
+  if (priv->material)
+    {
+      cogl_material_unref (priv->material);
+      priv->material = COGL_INVALID_HANDLE;
+    }
+
+  G_OBJECT_CLASS (nbtk_subtexture_parent_class)->dispose (gobject);
+}
+
+static void
+nbtk_subtexture_class_init (NbtkSubtextureClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+  GParamSpec *pspec;
+
+  g_type_class_add_private (gobject_class, sizeof (NbtkSubtexturePrivate));
+
+  actor_class->get_preferred_width =
+    nbtk_subtexture_get_preferred_width;
+  actor_class->get_preferred_height =
+    nbtk_subtexture_get_preferred_height;
+  actor_class->realize = nbtk_subtexture_realize;
+  actor_class->unrealize = nbtk_subtexture_unrealize;
+  actor_class->paint = nbtk_subtexture_paint;
+
+  gobject_class->set_property = nbtk_subtexture_set_property;
+  gobject_class->get_property = nbtk_subtexture_get_property;
+  gobject_class->dispose = nbtk_subtexture_dispose;
+
+  pspec = g_param_spec_object ("parent-texture",
+                               "Parent Texture",
+                               "The parent ClutterTexture",
+                               CLUTTER_TYPE_TEXTURE,
+                               G_PARAM_READWRITE |
+                               G_PARAM_CONSTRUCT);
+  g_object_class_install_property (gobject_class, PROP_PARENT_TEXTURE, pspec);
+
+  pspec = g_param_spec_int ("left",
+                              "Left",
+                              "Left offset",
+			      0, G_MAXINT,
+                              0,
+                              G_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_LEFT, pspec);
+
+  pspec = g_param_spec_int ("top",
+                              "Top",
+                              "Top offset",
+                              0, G_MAXINT,
+                              0,
+                              G_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_TOP, pspec);
+
+  pspec = g_param_spec_int ("width",
+                              "Width",
+                              "Width",
+                              0, G_MAXINT,
+                              0,
+                              G_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_WIDTH, pspec);
+
+  pspec = g_param_spec_int ("height",
+                              "Height",
+                              "Height",
+                              0, G_MAXINT,
+                              0,
+                              G_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_HEIGHT, pspec);
+}
+
+static void
+nbtk_subtexture_init (NbtkSubtexture *self)
+{
+  NbtkSubtexturePrivate *priv;
+
+  self->priv = priv = NBTK_SUBTEXTURE_GET_PRIVATE (self);
+
+  priv->material = COGL_INVALID_HANDLE;
+}
+
+/**
+ * nbtk_subtexture_new:
+ * @texture: a #ClutterTexture or %NULL
+ * @left: left
+ * @top: top
+ * @width: width
+ * @height: height
+ *
+ * A #NbtkSubtexture is a specialized texture that efficiently clones
+ * an area of the given @texture while keeping preserving portions of the
+ * same texture.
+ *
+ * A #NbtkSubtexture can be used to make a rectangular texture fit a
+ * given size without stretching its borders.
+ *
+ * Return value: the newly created #NbtkSubtexture
+ */
+ClutterActor*
+nbtk_subtexture_new (ClutterTexture *texture,
+			gint          left,
+			gint          top,
+			gint          width,
+			gint          height)
+{
+  g_return_val_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture), NULL);
+
+  return g_object_new (NBTK_TYPE_SUBTEXTURE,
+		       "parent-texture", texture,
+		       "top", top,
+		       "left", left,
+		       "width", width,
+		       "height", height,
+		       NULL);
+}
+
+/**
+ * nbtk_subtexture_get_parent_texture:
+ * @frame: A #NbtkSubtexture
+ *
+ * Return the texture used by the #NbtkSubtexture
+ *
+ * Returns: a #ClutterTexture owned by the #NbtkSubtexture
+ */
+ClutterTexture *
+nbtk_subtexture_get_parent_texture (NbtkSubtexture *frame)
+{
+  g_return_val_if_fail (NBTK_IS_SUBTEXTURE (frame), NULL);
+
+  return frame->priv->parent_texture;
+}
+
+/**
+ * nbtk_subtexture_set_parent_texture:
+ * @frame: A #NbtkSubtexture
+ * @texture: A #ClutterTexture
+ *
+ * Set the #ClutterTexture used by this #NbtkSubtexture
+ *
+ */
+void
+nbtk_subtexture_set_parent_texture (NbtkSubtexture *frame,
+                                       ClutterTexture   *texture)
+{
+  NbtkSubtexturePrivate *priv;
+  gboolean was_visible;
+
+  g_return_if_fail (NBTK_IS_SUBTEXTURE (frame));
+  g_return_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture));
+
+  priv = frame->priv;
+
+  was_visible = CLUTTER_ACTOR_IS_VISIBLE (frame);
+
+  if (priv->parent_texture == texture)
+    return;
+
+  if (priv->parent_texture)
+    {
+      g_object_unref (priv->parent_texture);
+      priv->parent_texture = NULL;
+
+      if (was_visible)
+        clutter_actor_hide (CLUTTER_ACTOR (frame));
+    }
+
+  if (texture)
+    {
+      priv->parent_texture = g_object_ref (texture);
+
+      if (was_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->parent_texture))
+        clutter_actor_show (CLUTTER_ACTOR (frame));
+    }
+
+  clutter_actor_queue_relayout (CLUTTER_ACTOR (frame));
+
+  g_object_notify (G_OBJECT (frame), "parent-texture");
+}
+
+/**
+ * nbtk_subtexture_set_frame:
+ * @frame: A #NbtkSubtexture
+ * @left: left
+ * @top: top
+ * @width: width
+ * @height: height
+ *
+ * Set the frame of the subtexture
+ *
+ */
+void
+nbtk_subtexture_set_frame (NbtkSubtexture *frame,
+                              gint            left,
+                              gint            top,
+                              gint            width,
+                              gint            height)
+{
+  g_return_if_fail (NBTK_IS_SUBTEXTURE (frame));
+
+  nbtk_subtexture_set_frame_internal (frame, left, top, width, height);
+}
+
+/**
+ * nbtk_subtexture_get_frame:
+ * @frame: A #NbtkSubtexture
+ * @left: left
+ * @top: top
+ * @width: width
+ * @height: height
+ *
+ * Retrieve the current frame.
+ *
+ */
+void
+nbtk_subtexture_get_frame (NbtkSubtexture *frame,
+                              gint           *left,
+                              gint           *top,
+                              gint           *width,
+                              gint           *height)
+{
+  NbtkSubtexturePrivate *priv;
+
+  g_return_if_fail (NBTK_IS_SUBTEXTURE (frame));
+
+  priv = frame->priv;
+
+  if (top)
+    *top = priv->top;
+
+  if (left)
+    *left = priv->left;
+
+  if (width)
+    *width = priv->width;
+
+  if (height)
+    *height = priv->height;
+}
diff --git a/src/nbtk/nbtk-subtexture.h b/src/nbtk/nbtk-subtexture.h
new file mode 100644
index 0000000..33a4b72
--- /dev/null
+++ b/src/nbtk/nbtk-subtexture.h
@@ -0,0 +1,92 @@
+/*
+ * nbtk-subtexture.h: Class to wrap a texture and "subframe" it.
+ *
+ * Based on
+ * nbtk-texture-frame.h: Expandible texture actor
+ *
+ * Copyright 2007, 2008 OpenedHand Ltd
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef __NBTK_SUBTEXTURE_H__
+#define __NBTK_SUBTEXTURE_H__
+
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_SUBTEXTURE                 (nbtk_subtexture_get_type ())
+#define NBTK_SUBTEXTURE(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_SUBTEXTURE, NbtkSubtexture))
+#define NBTK_SUBTEXTURE_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), NBTK_TYPE_SUBTEXTURE, NbtkSubtextureClass))
+#define NBTK_IS_SUBTEXTURE(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_SUBTEXTURE))
+#define NBTK_IS_SUBTEXTURE_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), NBTK_TYPE_SUBTEXTURE))
+#define NBTK_SUBTEXTURE_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), NBTK_TYPE_SUBTEXTURE, NbtkSubtextureClass))
+
+typedef struct _NbtkSubtexture                NbtkSubtexture;
+typedef struct _NbtkSubtexturePrivate         NbtkSubtexturePrivate;
+typedef struct _NbtkSubtextureClass           NbtkSubtextureClass;
+
+/**
+ * NbtkSubtexture:
+ *
+ * The contents of this structure are private and should only be accessed
+ * through the public API.
+ */
+struct _NbtkSubtexture
+{
+  /*< private >*/
+  ClutterActor parent_instance;
+
+  NbtkSubtexturePrivate    *priv;
+};
+
+struct _NbtkSubtextureClass
+{
+  ClutterActorClass parent_class;
+
+  /* padding for future expansion */
+  void (*_nbtk_box_1) (void);
+  void (*_nbtk_box_2) (void);
+  void (*_nbtk_box_3) (void);
+  void (*_nbtk_box_4) (void);
+};
+
+GType           nbtk_subtexture_get_type           (void) G_GNUC_CONST;
+ClutterActor *  nbtk_subtexture_new                (ClutterTexture   *texture,
+                                                       gint            top,
+                                                       gint            left,
+                                                       gint            width,
+                                                       gint            height);
+void            nbtk_subtexture_set_parent_texture (NbtkSubtexture *frame,
+                                                       ClutterTexture   *texture);
+ClutterTexture *nbtk_subtexture_get_parent_texture (NbtkSubtexture *frame);
+void            nbtk_subtexture_set_frame          (NbtkSubtexture *frame,
+                                                       gint            top,
+                                                       gint            left,
+                                                       gint            width,
+                                                       gint            height);
+void            nbtk_subtexture_get_frame          (NbtkSubtexture *frame,
+                                                       gint           *top,
+                                                       gint           *left,
+                                                       gint           *width,
+                                                       gint           *height);
+
+G_END_DECLS
+
+#endif /* __NBTK_SUBTEXTURE_H__ */
diff --git a/src/nbtk/nbtk-texture-cache.c b/src/nbtk/nbtk-texture-cache.c
new file mode 100644
index 0000000..10499bd
--- /dev/null
+++ b/src/nbtk/nbtk-texture-cache.c
@@ -0,0 +1,456 @@
+/*
+ * nbtk-widget.h: Base class for Nbtk actors
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+/**
+ * SECTION:nbtk-texture-cache
+ * @short_description: A per-process store to cache textures
+ *
+ * #NbtkTextureCache allows an application to re-use an previously loaded
+ * textures.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <string.h>
+
+#include "nbtk-texture-cache.h"
+#include "nbtk-marshal.h"
+#include "nbtk-private.h"
+#include "nbtk-subtexture.h"
+G_DEFINE_TYPE (NbtkTextureCache, nbtk_texture_cache, G_TYPE_OBJECT)
+
+#define TEXTURE_CACHE_PRIVATE(o) \
+  (G_TYPE_INSTANCE_GET_PRIVATE ((o), NBTK_TYPE_TEXTURE_CACHE, NbtkTextureCachePrivate))
+
+typedef struct _NbtkTextureCachePrivate NbtkTextureCachePrivate;
+
+struct _NbtkTextureCachePrivate
+{
+  GHashTable  *cache;
+};
+
+typedef struct FinalizedClosure
+{
+  gchar             *path;
+  NbtkTextureCache  *cache;
+} FinalizedClosure;
+
+enum
+{
+  PROP_0,
+};
+
+static NbtkTextureCache* __cache_singleton = NULL;
+
+/*
+ * Convention: posX with a value of -1 indicates whole texture
+ */
+typedef struct NbtkTextureCacheItem {
+  char filename[256];
+  int  width, height;
+  int  posX, posY;
+  ClutterActor *ptr;
+} NbtkTextureCacheItem;
+
+static NbtkTextureCacheItem *
+nbtk_texture_cache_item_new (void)
+{
+  return g_slice_new0 (NbtkTextureCacheItem);
+}
+
+static void
+nbtk_texture_cache_item_free (NbtkTextureCacheItem *item)
+{
+  g_slice_free (NbtkTextureCacheItem, item);
+}
+
+static void
+nbtk_texture_cache_set_property (GObject      *object,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+nbtk_texture_cache_get_property (GObject    *object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+nbtk_texture_cache_dispose (GObject *object)
+{
+  if (G_OBJECT_CLASS (nbtk_texture_cache_parent_class)->dispose)
+    G_OBJECT_CLASS (nbtk_texture_cache_parent_class)->dispose (object);
+}
+
+static void
+nbtk_texture_cache_finalize (GObject *object)
+{
+  NbtkTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(object);
+
+  if (priv->cache)
+    {
+      g_hash_table_unref (priv->cache);
+      priv->cache = NULL;
+    }
+
+  G_OBJECT_CLASS (nbtk_texture_cache_parent_class)->finalize (object);
+}
+
+static void
+nbtk_texture_cache_class_init (NbtkTextureCacheClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (NbtkTextureCachePrivate));
+
+  object_class->get_property = nbtk_texture_cache_get_property;
+  object_class->set_property = nbtk_texture_cache_set_property;
+  object_class->dispose = nbtk_texture_cache_dispose;
+  object_class->finalize = nbtk_texture_cache_finalize;
+
+}
+
+static void
+nbtk_texture_cache_init (NbtkTextureCache *self)
+{
+  NbtkTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
+
+  priv->cache = g_hash_table_new_full (g_str_hash,
+                                       g_str_equal,
+                                       g_free,
+                                       NULL);
+
+}
+
+/**
+ * nbtk_texture_cache_get_default:
+ *
+ * Returns the default texture cache. This is owned by Nbtk and should not be
+ * unreferenced or freed.
+ *
+ * Returns: a NbtkTextureCache
+ */
+NbtkTextureCache*
+nbtk_texture_cache_get_default (void)
+{
+  if (G_UNLIKELY (__cache_singleton == NULL))
+    __cache_singleton = g_object_new (NBTK_TYPE_TEXTURE_CACHE, NULL);
+
+  return  __cache_singleton;
+}
+
+#if 0
+static void
+on_texure_finalized (gpointer data,
+		     GObject *where_the_object_was)
+{
+  FinalizedClosure *closure = (FinalizedClosure *)data;
+  NbtkTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(closure->cache);
+
+  g_hash_table_remove (priv->cache, closure->path);
+
+  g_free(closure->path);
+  g_free(closure);
+}
+#endif
+
+/**
+ * nbtk_texture_cache_get_size:
+ * @self: A #NbtkTextureCache
+ *
+ * Returns the number of items in the texture cache
+ *
+ * Returns: the current size of the cache
+ */
+gint
+nbtk_texture_cache_get_size (NbtkTextureCache *self)
+{
+  NbtkTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
+
+  return g_hash_table_size (priv->cache);
+}
+
+static void
+add_texture_to_cache (NbtkTextureCache     *self,
+                      const gchar          *path,
+                      NbtkTextureCacheItem *item)
+{
+  /*  FinalizedClosure        *closure; */
+  NbtkTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
+
+  g_hash_table_insert (priv->cache, g_strdup (path), item);
+
+#if 0
+  /* Make sure we can remove from hash */
+  closure = g_new0 (FinalizedClosure, 1);
+  closure->path = g_strdup (path);
+  closure->cache = self;
+
+  g_object_weak_ref (G_OBJECT (res), on_texure_finalized, closure);
+#endif
+}
+
+/* NOTE: you should unref the returned texture when not needed */
+
+/**
+ * nbtk_texture_cache_get_texture:
+ * @self: A #NbtkTextureCache
+ * @path: A path to a image file
+ * @want_clone: ignored
+ *
+ * Create a new ClutterTexture with the specified image. Adds the image to the
+ * cache if the image had not been previously loaded. Subsequent calls with
+ * the same image path will return a new ClutterTexture with the previously
+ * loaded image.
+ *
+ * Returns: a newly created ClutterTexture
+ */
+ClutterTexture*
+nbtk_texture_cache_get_texture (NbtkTextureCache *self,
+                                const gchar      *path,
+                                gboolean          want_clone)
+{
+  ClutterActor *texture;
+  CoglHandle *handle;
+  NbtkTextureCachePrivate *priv;
+  NbtkTextureCacheItem *item;
+
+  g_return_val_if_fail (NBTK_IS_TEXTURE_CACHE (self), NULL);
+  g_return_val_if_fail (path != NULL, NULL);
+
+
+  priv = TEXTURE_CACHE_PRIVATE (self);
+
+  if (want_clone)
+    g_warning ("The want_clone parameter of %s is now ignored. This function "
+               "always returns a new ClutterTexture", __FUNCTION__);
+
+  item = g_hash_table_lookup (priv->cache, path);
+
+  if (item && item->posX != -1)
+    {
+      GError *err = NULL;
+      /*
+       * We have a cache hit, but it's for a partial texture. The only
+       * sane option is to read it from disk and just don't cache it
+       * at all.
+       */
+      return CLUTTER_TEXTURE(clutter_texture_new_from_file(path, &err));
+    }
+  if (!item)
+    {
+      GError *err = NULL;
+
+      item = nbtk_texture_cache_item_new ();
+      item->posX = -1;
+      item->posY = -1;
+      item->ptr = clutter_texture_new_from_file (path, &err);
+      clutter_texture_get_base_size (CLUTTER_TEXTURE (item->ptr),
+                                     &item->width, &item->height);
+
+      if (!item->ptr)
+        {
+          if (err)
+            {
+              g_warning ("Error loading image: %s", err->message);
+              g_error_free (err);
+            }
+
+          return NULL;
+        }
+
+      add_texture_to_cache (self, path, item);
+    }
+
+  texture = clutter_texture_new ();
+  handle = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (item->ptr));
+  clutter_texture_set_cogl_texture ((ClutterTexture*) texture, handle);
+
+  return (ClutterTexture*) texture;
+}
+
+
+/**
+ * nbtk_texture_cache_get_actor:
+ * @self: A #NbtkTextureCache
+ * @path: A path to a image file
+ *
+ * Create a new ClutterSubTexture with the specified image. Adds the image to the
+ * cache if the image had not been previously loaded. Subsequent calls with
+ * the same image path will return a new ClutterTexture with the previously
+ * loaded image.
+ *
+ * Use this function if all you need is an actor for drawing.
+ *
+ * Returns: a newly created ClutterTexture
+ */
+ClutterActor*
+nbtk_texture_cache_get_actor (NbtkTextureCache *self,
+                              const gchar      *path)
+{
+  NbtkTextureCachePrivate *priv;
+  NbtkTextureCacheItem *item;
+  GError *err = NULL;
+
+  g_return_val_if_fail (NBTK_IS_TEXTURE_CACHE (self), NULL);
+  g_return_val_if_fail (path != NULL, NULL);
+
+  priv = TEXTURE_CACHE_PRIVATE (self);
+
+
+  item = g_hash_table_lookup (priv->cache, path);
+
+  if (item)
+    {
+      int posX = item->posX;
+      int posY = item->posY;
+      if (posX == -1)
+          posX = 0;
+      if (posY == -1)
+          posY = 0;
+      return nbtk_subtexture_new (CLUTTER_TEXTURE (item->ptr), posX, posY,
+                                  item->width, item->height);
+    }
+
+  item = nbtk_texture_cache_item_new ();
+  item->posX = -1;
+  item->posY = -1;
+  item->ptr = clutter_texture_new_from_file (path, &err);
+  clutter_texture_get_base_size (CLUTTER_TEXTURE (item->ptr),
+                                 &item->width, &item->height);
+
+  if (!item->ptr)
+    {
+      if (err)
+        {
+          g_warning ("Error loading image: %s", err->message);
+          g_error_free (err);
+        }
+
+      return NULL;
+    }
+
+  add_texture_to_cache (self, path, item);
+
+  return nbtk_subtexture_new (CLUTTER_TEXTURE (item->ptr), 0, 0, item->width,
+                              item->height);
+}
+
+void
+nbtk_texture_cache_load_cache (NbtkTextureCache *self,
+                               const gchar      *filename)
+{
+   FILE *file;
+   NbtkTextureCacheItem *element, head;
+   int ret;
+   ClutterActor *actor;
+   GError *error = NULL;
+   NbtkTextureCachePrivate *priv;
+
+   g_return_if_fail (NBTK_IS_TEXTURE_CACHE (self));
+   g_return_if_fail (filename != NULL);
+
+   priv = TEXTURE_CACHE_PRIVATE (self);
+
+   file = fopen(filename, "rm");
+   if (!file)
+      return;
+
+   ret = fread (&head, sizeof(NbtkTextureCacheItem), 1, file);
+   if (ret < 0)
+     {
+        fclose (file);
+        return;
+     }
+
+   /* check if we already if this texture in the cache */
+   if (g_hash_table_lookup (priv->cache, head.filename))
+     {
+        /* skip it, we're done */
+        fclose (file);
+        return;
+     }
+
+   actor = clutter_texture_new_from_file (head.filename, &error);
+
+   if (error)
+   {
+     g_critical (G_STRLOC ": Error opening cache image file: %s",
+                 error->message);
+     g_clear_error (&error);
+     fclose (file);
+     return;
+   }
+
+   element = nbtk_texture_cache_item_new ();
+   element->posX = -1;
+   element->posY = -1;
+   element->ptr = actor;
+   strncpy (element->filename, head.filename, 256);
+   clutter_texture_get_base_size (CLUTTER_TEXTURE (element->ptr),
+                                  &element->width, &element->height);
+   g_hash_table_insert (priv->cache, element->filename, element);
+
+   while (!feof (file))
+     {
+        element = nbtk_texture_cache_item_new ();
+        ret = fread (element, sizeof (NbtkTextureCacheItem), 1, file);
+        if (ret < 1)
+          {
+             /* end of file */
+             nbtk_texture_cache_item_free (element);
+             break;
+          }
+
+        element->ptr = actor;
+
+        if (g_hash_table_lookup (priv->cache, element->filename))
+          {
+            /* file is already in the cache.... */
+            nbtk_texture_cache_item_free (element);
+          } else {
+            g_hash_table_insert (priv->cache, element->filename, element);
+          }
+     }
+}
diff --git a/src/nbtk/nbtk-texture-cache.h b/src/nbtk/nbtk-texture-cache.h
new file mode 100644
index 0000000..1b2e17d
--- /dev/null
+++ b/src/nbtk/nbtk-texture-cache.h
@@ -0,0 +1,94 @@
+/*
+ * nbtk-texture-cache.h: Cached textures object
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef _NBTK_TEXTURE_CACHE
+#define _NBTK_TEXTURE_CACHE
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_TEXTURE_CACHE nbtk_texture_cache_get_type()
+
+#define NBTK_TEXTURE_CACHE(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+  NBTK_TYPE_TEXTURE_CACHE, NbtkTextureCache))
+
+#define NBTK_TEXTURE_CACHE_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), \
+  NBTK_TYPE_TEXTURE_CACHE, NbtkTextureCacheClass))
+
+#define NBTK_IS_TEXTURE_CACHE(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+  NBTK_TYPE_TEXTURE_CACHE))
+
+#define NBTK_IS_TEXTURE_CACHE_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+  NBTK_TYPE_TEXTURE_CACHE))
+
+#define NBTK_TEXTURE_CACHE_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+  NBTK_TYPE_TEXTURE_CACHE, NbtkTextureCacheClass))
+
+/**
+ * NbtkTextureCache:
+ *
+ * The contents of this structure are private and should only be accessed
+ * through the public API.
+ */
+typedef struct {
+    /*< private >*/
+    GObject parent;
+} NbtkTextureCache;
+
+typedef struct {
+  GObjectClass parent_class;
+
+  void (* loaded)        (NbtkTextureCache *self,
+                          const gchar      *path,
+                          ClutterTexture   *texture);
+
+  void (* error_loading) (NbtkTextureCache *self,
+                          GError           *error);
+} NbtkTextureCacheClass;
+
+GType nbtk_texture_cache_get_type (void);
+
+NbtkTextureCache* nbtk_texture_cache_get_default (void);
+ClutterTexture*   nbtk_texture_cache_get_texture (NbtkTextureCache *self,
+                                                  const gchar *path,
+                                                  gboolean want_clone);
+ClutterActor*     nbtk_texture_cache_get_actor (NbtkTextureCache *self,
+                                                  const gchar *path);
+
+gint              nbtk_texture_cache_get_size    (NbtkTextureCache *self);
+
+void nbtk_texture_cache_load_cache(NbtkTextureCache *self, const char *filename);
+
+G_END_DECLS
+
+#endif /* _NBTK_TEXTURE_CACHE */
diff --git a/src/nbtk/nbtk-texture-frame.c b/src/nbtk/nbtk-texture-frame.c
new file mode 100644
index 0000000..6734cba
--- /dev/null
+++ b/src/nbtk/nbtk-texture-frame.c
@@ -0,0 +1,620 @@
+/*
+ * nbtk-texture-frame.h: Expandible texture actor
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+/**
+ * SECTION:nbtk-texture-frame
+ * @short_description: Stretch a texture to fit the entire allocation
+ *
+ * #NbtkTextureFrame
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cogl/cogl.h>
+
+#include "nbtk-texture-frame.h"
+#include "nbtk-private.h"
+
+enum
+{
+  PROP_0,
+
+  PROP_PARENT_TEXTURE,
+
+  PROP_TOP,
+  PROP_RIGHT,
+  PROP_BOTTOM,
+  PROP_LEFT
+};
+
+G_DEFINE_TYPE (NbtkTextureFrame, nbtk_texture_frame, CLUTTER_TYPE_ACTOR);
+
+#define NBTK_TEXTURE_FRAME_GET_PRIVATE(obj)     (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NBTK_TYPE_TEXTURE_FRAME, NbtkTextureFramePrivate))
+
+struct _NbtkTextureFramePrivate
+{
+  ClutterTexture *parent_texture;
+
+  gfloat top;
+  gfloat right;
+  gfloat bottom;
+  gfloat left;
+};
+
+static void
+nbtk_texture_frame_get_preferred_width (ClutterActor *self,
+                                        gfloat        for_height,
+                                        gfloat       *min_width_p,
+                                        gfloat       *natural_width_p)
+{
+  NbtkTextureFramePrivate *priv = NBTK_TEXTURE_FRAME (self)->priv;
+
+  if (G_UNLIKELY (priv->parent_texture == NULL))
+    {
+      if (min_width_p)
+        *min_width_p = 0;
+
+      if (natural_width_p)
+        *natural_width_p = 0;
+    }
+  else
+    {
+      ClutterActorClass *klass;
+
+      /* by directly querying the parent texture's class implementation
+       * we are going around any override mechanism the parent texture
+       * might have in place, and we ask directly for the original
+       * preferred width
+       */
+      klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture);
+      klass->get_preferred_width (CLUTTER_ACTOR (priv->parent_texture),
+                                  for_height,
+                                  min_width_p,
+                                  natural_width_p);
+    }
+}
+
+static void
+nbtk_texture_frame_get_preferred_height (ClutterActor *self,
+                                         gfloat        for_width,
+                                         gfloat       *min_height_p,
+                                         gfloat       *natural_height_p)
+{
+  NbtkTextureFramePrivate *priv = NBTK_TEXTURE_FRAME (self)->priv;
+
+  if (G_UNLIKELY (priv->parent_texture == NULL))
+    {
+      if (min_height_p)
+        *min_height_p = 0;
+
+      if (natural_height_p)
+        *natural_height_p = 0;
+    }
+  else
+    {
+      ClutterActorClass *klass;
+
+      /* by directly querying the parent texture's class implementation
+       * we are going around any override mechanism the parent texture
+       * might have in place, and we ask directly for the original
+       * preferred height
+       */
+      klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture);
+      klass->get_preferred_height (CLUTTER_ACTOR (priv->parent_texture),
+                                   for_width,
+                                   min_height_p,
+                                   natural_height_p);
+    }
+}
+
+static void
+nbtk_texture_frame_paint (ClutterActor *self)
+{
+  NbtkTextureFramePrivate *priv = NBTK_TEXTURE_FRAME (self)->priv;
+  CoglHandle cogl_texture = COGL_INVALID_HANDLE;
+  CoglHandle cogl_material = COGL_INVALID_HANDLE;
+  ClutterActorBox box = { 0, };
+  gfloat width, height;
+  gfloat tex_width, tex_height;
+  gfloat ex, ey;
+  gfloat tx1, ty1, tx2, ty2;
+  guint8 opacity;
+
+  /* no need to paint stuff if we don't have a texture */
+  if (G_UNLIKELY (priv->parent_texture == NULL))
+    return;
+
+  /* parent texture may have been hidden, so need to make sure it gets
+   * realized
+   */
+  if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent_texture))
+    clutter_actor_realize (CLUTTER_ACTOR (priv->parent_texture));
+
+  cogl_texture = clutter_texture_get_cogl_texture (priv->parent_texture);
+  if (cogl_texture == COGL_INVALID_HANDLE)
+    return;
+  cogl_material = clutter_texture_get_cogl_material (priv->parent_texture);
+  if (cogl_material == COGL_INVALID_HANDLE)
+    return;
+
+  tex_width  = cogl_texture_get_width (cogl_texture);
+  tex_height = cogl_texture_get_height (cogl_texture);
+
+  clutter_actor_get_allocation_box (self, &box);
+  width = box.x2 - box.x1;
+  height = box.y2 - box.y1;
+
+  tx1 = priv->left / tex_width;
+  tx2 = (tex_width - priv->right) / tex_width;
+  ty1 = priv->top / tex_height;
+  ty2 = (tex_height - priv->bottom) / tex_height;
+
+  ex = width - priv->right;
+  if (ex < 0)
+    ex = priv->right; 		/* FIXME ? */
+
+  ey = height - priv->bottom;
+  if (ey < 0)
+    ey = priv->bottom; 		/* FIXME ? */
+
+  opacity = clutter_actor_get_paint_opacity (self);
+
+  /* Paint using the parent texture's material. It should already have
+     the cogl texture set as the first layer */
+  /* NB: for correct blending we need set a preumultiplied color here: */
+  cogl_material_set_color4ub (cogl_material,
+                              opacity, opacity, opacity, opacity);
+  cogl_set_source (cogl_material);
+
+  {
+    GLfloat rectangles[] =
+      {
+        /* top left corner */
+        0, 0, priv->left, priv->top,
+        0.0, 0.0,
+        tx1, ty1,
+
+        /* top middle */
+        priv->left, 0, ex, priv->top,
+        tx1, 0.0,
+        tx2, ty1,
+
+        /* top right */
+        ex, 0, width, priv->top,
+        tx2, 0.0,
+        1.0, ty1,
+
+        /* mid left */
+        0, priv->top, priv->left, ey,
+        0.0, ty1,
+        tx1, ty2,
+
+        /* center */
+        priv->left, priv->top, ex, ey,
+        tx1, ty1,
+        tx2, ty2,
+
+        /* mid right */
+        ex, priv->top, width, ey,
+        tx2, ty1,
+        1.0, ty2,
+
+        /* bottom left */
+        0, ey, priv->left, height,
+        0.0, ty2,
+        tx1, 1.0,
+
+        /* bottom center */
+        priv->left, ey, ex, height,
+        tx1, ty2,
+        tx2, 1.0,
+
+        /* bottom right */
+        ex, ey, width, height,
+        tx2, ty2,
+        1.0, 1.0
+      };
+
+    cogl_rectangles_with_texture_coords (rectangles, 9);
+  }
+}
+
+static inline void
+nbtk_texture_frame_set_frame_internal (NbtkTextureFrame *frame,
+                                       gfloat            top,
+                                       gfloat            right,
+                                       gfloat            bottom,
+                                       gfloat            left)
+{
+  NbtkTextureFramePrivate *priv = frame->priv;
+  GObject *gobject = G_OBJECT (frame);
+  gboolean changed = FALSE;
+
+  g_object_freeze_notify (gobject);
+
+  if (priv->top != top)
+    {
+      priv->top = top;
+      g_object_notify (gobject, "top");
+      changed = TRUE;
+    }
+
+  if (priv->right != right)
+    {
+      priv->right = right;
+      g_object_notify (gobject, "right");
+      changed = TRUE;
+    }
+
+  if (priv->bottom != bottom)
+    {
+      priv->bottom = bottom;
+      g_object_notify (gobject, "bottom");
+      changed = TRUE;
+    }
+
+  if (priv->left != left)
+    {
+      priv->left = left;
+      g_object_notify (gobject, "left");
+      changed = TRUE;
+    }
+
+  if (changed && CLUTTER_ACTOR_IS_VISIBLE (frame))
+    clutter_actor_queue_redraw (CLUTTER_ACTOR (frame));
+
+  g_object_thaw_notify (gobject);
+}
+
+static void
+nbtk_texture_frame_set_property (GObject      *gobject,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  NbtkTextureFrame *frame = NBTK_TEXTURE_FRAME (gobject);
+  NbtkTextureFramePrivate *priv = frame->priv;
+
+  switch (prop_id)
+    {
+    case PROP_PARENT_TEXTURE:
+      nbtk_texture_frame_set_parent_texture (frame,
+                                             g_value_get_object (value));
+      break;
+
+    case PROP_TOP:
+      nbtk_texture_frame_set_frame_internal (frame,
+                                             g_value_get_float (value),
+                                             priv->right,
+                                             priv->bottom,
+                                             priv->left);
+      break;
+
+    case PROP_RIGHT:
+      nbtk_texture_frame_set_frame_internal (frame,
+                                             priv->top,
+                                             g_value_get_float (value),
+                                             priv->bottom,
+                                             priv->left);
+      break;
+
+    case PROP_BOTTOM:
+      nbtk_texture_frame_set_frame_internal (frame,
+                                             priv->top,
+                                             priv->right,
+                                             g_value_get_float (value),
+                                             priv->left);
+      break;
+
+    case PROP_LEFT:
+      nbtk_texture_frame_set_frame_internal (frame,
+                                             priv->top,
+                                             priv->right,
+                                             priv->bottom,
+                                             g_value_get_float (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+nbtk_texture_frame_get_property (GObject    *gobject,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  NbtkTextureFramePrivate *priv = NBTK_TEXTURE_FRAME (gobject)->priv;
+
+  switch (prop_id)
+    {
+    case PROP_PARENT_TEXTURE:
+      g_value_set_object (value, priv->parent_texture);
+      break;
+
+    case PROP_LEFT:
+      g_value_set_float (value, priv->left);
+      break;
+
+    case PROP_TOP:
+      g_value_set_float (value, priv->top);
+      break;
+
+    case PROP_RIGHT:
+      g_value_set_float (value, priv->right);
+      break;
+
+    case PROP_BOTTOM:
+      g_value_set_float (value, priv->bottom);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+nbtk_texture_frame_dispose (GObject *gobject)
+{
+  NbtkTextureFramePrivate *priv = NBTK_TEXTURE_FRAME (gobject)->priv;
+
+  if (priv->parent_texture)
+    {
+      g_object_unref (priv->parent_texture);
+      priv->parent_texture = NULL;
+    }
+
+  G_OBJECT_CLASS (nbtk_texture_frame_parent_class)->dispose (gobject);
+}
+
+static void
+nbtk_texture_frame_class_init (NbtkTextureFrameClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+  GParamSpec *pspec;
+
+  g_type_class_add_private (gobject_class, sizeof (NbtkTextureFramePrivate));
+
+  actor_class->get_preferred_width =
+    nbtk_texture_frame_get_preferred_width;
+  actor_class->get_preferred_height =
+    nbtk_texture_frame_get_preferred_height;
+  actor_class->paint = nbtk_texture_frame_paint;
+
+  gobject_class->set_property = nbtk_texture_frame_set_property;
+  gobject_class->get_property = nbtk_texture_frame_get_property;
+  gobject_class->dispose = nbtk_texture_frame_dispose;
+
+  pspec = g_param_spec_object ("parent-texture",
+                               "Parent Texture",
+                               "The parent ClutterTexture",
+                               CLUTTER_TYPE_TEXTURE,
+                               NBTK_PARAM_READWRITE |
+                               G_PARAM_CONSTRUCT);
+  g_object_class_install_property (gobject_class, PROP_PARENT_TEXTURE, pspec);
+
+  pspec = g_param_spec_float ("left",
+                              "Left",
+                              "Left offset",
+			      0, G_MAXFLOAT,
+                              0,
+                              NBTK_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_LEFT, pspec);
+
+  pspec = g_param_spec_float ("top",
+                              "Top",
+                              "Top offset",
+                              0, G_MAXFLOAT,
+                              0,
+                              NBTK_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_TOP, pspec);
+
+  pspec = g_param_spec_float ("bottom",
+                              "Bottom",
+                              "Bottom offset",
+                              0, G_MAXFLOAT,
+                              0,
+                              NBTK_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_BOTTOM, pspec);
+
+  pspec = g_param_spec_float ("right",
+                              "Right",
+                              "Right offset",
+                              0, G_MAXFLOAT,
+                              0,
+                              NBTK_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_RIGHT, pspec);
+}
+
+static void
+nbtk_texture_frame_init (NbtkTextureFrame *self)
+{
+  NbtkTextureFramePrivate *priv;
+
+  self->priv = priv = NBTK_TEXTURE_FRAME_GET_PRIVATE (self);
+}
+
+/**
+ * nbtk_texture_frame_new:
+ * @texture: a #ClutterTexture or %NULL
+ * @left: left margin preserving its content
+ * @top: top margin preserving its content
+ * @right: right margin preserving its content
+ * @bottom: bottom margin preserving its content
+ *
+ * A #NbtkTextureFrame is a specialized texture that efficiently clones
+ * an area of the given @texture while keeping preserving portions of the
+ * same texture.
+ *
+ * A #NbtkTextureFrame can be used to make a rectangular texture fit a
+ * given size without stretching its borders.
+ *
+ * Return value: the newly created #NbtkTextureFrame
+ */
+ClutterActor*
+nbtk_texture_frame_new (ClutterTexture *texture,
+			gfloat          top,
+			gfloat          right,
+			gfloat          bottom,
+			gfloat          left)
+{
+  g_return_val_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture), NULL);
+
+  return g_object_new (NBTK_TYPE_TEXTURE_FRAME,
+		       "parent-texture", texture,
+		       "top", top,
+		       "right", right,
+		       "bottom", bottom,
+		       "left", left,
+		       NULL);
+}
+
+/**
+ * nbtk_texture_frame_get_parent_texture:
+ * @frame: A #NbtkTextureFrame
+ *
+ * Return the texture used by the #NbtkTextureFrame
+ *
+ * Returns: a #ClutterTexture owned by the #NbtkTextureFrame
+ */
+ClutterTexture *
+nbtk_texture_frame_get_parent_texture (NbtkTextureFrame *frame)
+{
+  g_return_val_if_fail (NBTK_IS_TEXTURE_FRAME (frame), NULL);
+
+  return frame->priv->parent_texture;
+}
+
+/**
+ * nbtk_texture_frame_set_parent_texture:
+ * @frame: A #NbtkTextureFrame
+ * @texture: A #ClutterTexture
+ *
+ * Set the #ClutterTexture used by this #NbtkTextureFrame
+ *
+ */
+void
+nbtk_texture_frame_set_parent_texture (NbtkTextureFrame *frame,
+                                       ClutterTexture   *texture)
+{
+  NbtkTextureFramePrivate *priv;
+  gboolean was_visible;
+
+  g_return_if_fail (NBTK_IS_TEXTURE_FRAME (frame));
+  g_return_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture));
+
+  priv = frame->priv;
+
+  was_visible = CLUTTER_ACTOR_IS_VISIBLE (frame);
+
+  if (priv->parent_texture == texture)
+    return;
+
+  if (priv->parent_texture)
+    {
+      g_object_unref (priv->parent_texture);
+      priv->parent_texture = NULL;
+
+      if (was_visible)
+        clutter_actor_hide (CLUTTER_ACTOR (frame));
+    }
+
+  if (texture)
+    {
+      priv->parent_texture = g_object_ref_sink (texture);
+
+      if (was_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->parent_texture))
+        clutter_actor_show (CLUTTER_ACTOR (frame));
+    }
+
+  clutter_actor_queue_relayout (CLUTTER_ACTOR (frame));
+
+  g_object_notify (G_OBJECT (frame), "parent-texture");
+}
+
+/**
+ * nbtk_texture_frame_set_frame:
+ * @frame: A #NbtkTextureFrame
+ * @top: width of the top slice
+ * @right: width of the right slice
+ * @bottom: width of the bottom slice
+ * @left: width of the left slice
+ *
+ * Set the slice lines of the specified frame. The slices are calculated as
+ * widths from the edge of the frame.
+ *
+ */
+void
+nbtk_texture_frame_set_frame (NbtkTextureFrame *frame,
+                              gfloat            top,
+                              gfloat            right,
+                              gfloat            bottom,
+                              gfloat            left)
+{
+  g_return_if_fail (NBTK_IS_TEXTURE_FRAME (frame));
+
+  nbtk_texture_frame_set_frame_internal (frame, top, right, bottom, left);
+}
+
+/**
+ * nbtk_texture_frame_get_frame:
+ * @frame: A #NbtkTextureFrame
+ * @top: width of the top slice
+ * @right: width of the right slice
+ * @bottom: width of the bottom slice
+ * @left: width of the left slice
+ *
+ * Retrieve the current slice lines from the specified frame.
+ *
+ */
+void
+nbtk_texture_frame_get_frame (NbtkTextureFrame *frame,
+                              gfloat           *top,
+                              gfloat           *right,
+                              gfloat           *bottom,
+                              gfloat           *left)
+{
+  NbtkTextureFramePrivate *priv;
+
+  g_return_if_fail (NBTK_IS_TEXTURE_FRAME (frame));
+
+  priv = frame->priv;
+
+  if (top)
+    *top = priv->top;
+
+  if (right)
+    *right = priv->right;
+
+  if (bottom)
+    *bottom = priv->bottom;
+
+  if (left)
+    *left = priv->left;
+}
diff --git a/src/nbtk/nbtk-texture-frame.h b/src/nbtk/nbtk-texture-frame.h
new file mode 100644
index 0000000..710c10b
--- /dev/null
+++ b/src/nbtk/nbtk-texture-frame.h
@@ -0,0 +1,92 @@
+/*
+ * nbtk-texture-frame.h: Expandible texture actor
+ *
+ * Copyright 2007, 2008 OpenedHand Ltd
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_TEXTURE_FRAME_H__
+#define __NBTK_TEXTURE_FRAME_H__
+
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_TEXTURE_FRAME                 (nbtk_texture_frame_get_type ())
+#define NBTK_TEXTURE_FRAME(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_TEXTURE_FRAME, NbtkTextureFrame))
+#define NBTK_TEXTURE_FRAME_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), NBTK_TYPE_TEXTURE_FRAME, NbtkTextureFrameClass))
+#define NBTK_IS_TEXTURE_FRAME(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_TEXTURE_FRAME))
+#define NBTK_IS_TEXTURE_FRAME_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), NBTK_TYPE_TEXTURE_FRAME))
+#define NBTK_TEXTURE_FRAME_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), NBTK_TYPE_TEXTURE_FRAME, NbtkTextureFrameClass))
+
+typedef struct _NbtkTextureFrame                NbtkTextureFrame;
+typedef struct _NbtkTextureFramePrivate         NbtkTextureFramePrivate;
+typedef struct _NbtkTextureFrameClass           NbtkTextureFrameClass;
+
+/**
+ * NbtkTextureFrame:
+ *
+ * The contents of this structure are private and should only be accessed
+ * through the public API.
+ */
+struct _NbtkTextureFrame
+{
+  /*< private >*/
+  ClutterActor parent_instance;
+
+  NbtkTextureFramePrivate    *priv;
+};
+
+struct _NbtkTextureFrameClass
+{
+  ClutterActorClass parent_class;
+
+  /* padding for future expansion */
+  void (*_clutter_box_1) (void);
+  void (*_clutter_box_2) (void);
+  void (*_clutter_box_3) (void);
+  void (*_clutter_box_4) (void);
+};
+
+GType           nbtk_texture_frame_get_type           (void) G_GNUC_CONST;
+ClutterActor *  nbtk_texture_frame_new                (ClutterTexture   *texture,
+                                                       gfloat            top,
+                                                       gfloat            right,
+                                                       gfloat            bottom,
+                                                       gfloat            left);
+void            nbtk_texture_frame_set_parent_texture (NbtkTextureFrame *frame,
+                                                       ClutterTexture   *texture);
+ClutterTexture *nbtk_texture_frame_get_parent_texture (NbtkTextureFrame *frame);
+void            nbtk_texture_frame_set_frame          (NbtkTextureFrame *frame,
+                                                       gfloat            top,
+                                                       gfloat            right,
+                                                       gfloat            bottom,
+                                                       gfloat            left);
+void            nbtk_texture_frame_get_frame          (NbtkTextureFrame *frame,
+                                                       gfloat           *top,
+                                                       gfloat           *right,
+                                                       gfloat           *bottom,
+                                                       gfloat           *left);
+
+G_END_DECLS
+
+#endif /* __NBTK_TEXTURE_FRAME_H__ */
diff --git a/src/nbtk/nbtk-tooltip.c b/src/nbtk/nbtk-tooltip.c
new file mode 100644
index 0000000..4bae527
--- /dev/null
+++ b/src/nbtk/nbtk-tooltip.c
@@ -0,0 +1,710 @@
+/*
+ * nbtk-tooltip.c: Plain tooltip actor
+ *
+ * Copyright 2008, 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Thomas Wood <thomas linux intel com>
+ *
+ */
+
+/**
+ * SECTION:nbtk-tooltip
+ * @short_description: A tooltip widget
+ *
+ * #NbtkTooltip implements a single tooltip. It should not normally be created
+ * by the application but by the widget implementing tooltip capabilities, for
+ * example, #nbtk_button_set_tooltip().
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <clutter/clutter.h>
+
+#include "nbtk-tooltip.h"
+
+#include "nbtk-widget.h"
+#include "nbtk-stylable.h"
+#include "nbtk-private.h"
+
+enum
+{
+  PROP_0,
+
+  PROP_LABEL,
+  PROP_TIP_AREA
+};
+
+#define NBTK_TOOLTIP_GET_PRIVATE(obj)    \
+        (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NBTK_TYPE_TOOLTIP, NbtkTooltipPrivate))
+
+struct _NbtkTooltipPrivate
+{
+  ClutterActor *label;
+
+  gfloat        arrow_offset;
+  gboolean      actor_below;
+
+  ClutterGeometry *tip_area;
+};
+
+G_DEFINE_TYPE (NbtkTooltip, nbtk_tooltip, NBTK_TYPE_WIDGET);
+
+static void
+nbtk_tooltip_set_property (GObject      *gobject,
+                           guint         prop_id,
+                           const GValue *value,
+                           GParamSpec   *pspec)
+{
+  NbtkTooltip *tooltip = NBTK_TOOLTIP (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_LABEL:
+      nbtk_tooltip_set_label (tooltip, g_value_get_string (value));
+      break;
+
+    case PROP_TIP_AREA:
+      nbtk_tooltip_set_tip_area (tooltip, g_value_get_boxed (value));
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+nbtk_tooltip_get_property (GObject    *gobject,
+                           guint       prop_id,
+                           GValue     *value,
+                           GParamSpec *pspec)
+{
+  NbtkTooltipPrivate *priv = NBTK_TOOLTIP (gobject)->priv;
+
+  switch (prop_id)
+    {
+    case PROP_LABEL:
+      g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->label)));
+      break;
+
+    case PROP_TIP_AREA:
+      g_value_set_boxed (value, priv->tip_area);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+nbtk_tooltip_style_changed (NbtkWidget *self)
+{
+  ClutterColor *color = NULL;
+  NbtkTooltipPrivate *priv;
+  gchar *font_name;
+  gchar *font_string;
+  gint font_size;
+
+  priv = NBTK_TOOLTIP (self)->priv;
+
+  nbtk_stylable_get (NBTK_STYLABLE (self),
+                     "color", &color,
+                     "font-family", &font_name,
+                     "font-size", &font_size,
+                     NULL);
+
+  if (color)
+    {
+      clutter_text_set_color (CLUTTER_TEXT (priv->label), color);
+      clutter_color_free (color);
+    }
+
+  if (font_name || font_size)
+    {
+      if (font_name && font_size)
+        {
+          font_string = g_strdup_printf ("%s %dpx", font_name, font_size);
+          g_free (font_name);
+        }
+      else
+        if (font_size)
+          font_string = g_strdup_printf ("%dpx", font_size);
+        else
+          font_string = font_name;
+
+      clutter_text_set_font_name (CLUTTER_TEXT (priv->label), font_string);
+
+      g_free (font_string);
+    }
+
+}
+
+static void
+nbtk_tooltip_get_preferred_width (ClutterActor *self,
+                                  gfloat       for_height,
+                                  gfloat      *min_width_p,
+                                  gfloat      *natural_width_p)
+{
+  NbtkTooltipPrivate *priv = NBTK_TOOLTIP (self)->priv;
+  gfloat min_label_w, natural_label_w;
+  gfloat label_height, arrow_height;
+  ClutterActor *arrow_image;
+  NbtkPadding padding;
+
+  nbtk_widget_get_padding (NBTK_WIDGET (self), &padding);
+
+  arrow_image = nbtk_widget_get_background_image (NBTK_WIDGET (self));
+  if (arrow_image)
+    {
+      clutter_actor_get_preferred_height (arrow_image,
+                                          -1,
+                                          NULL,
+                                          &arrow_height);
+    }
+  else
+    {
+      arrow_height = 0;
+    }
+
+  if (for_height > -1)
+    {
+      label_height = for_height - arrow_height - padding.top - padding.bottom;
+    }
+  else
+    {
+      label_height = -1;
+    }
+
+  if (priv->label)
+    {
+      clutter_actor_get_preferred_width (priv->label,
+                                         label_height,
+                                         &min_label_w,
+                                         &natural_label_w);
+    }
+  else
+    {
+      min_label_w = 0;
+      natural_label_w = 0;
+    }
+
+
+  if (min_width_p)
+    {
+      *min_width_p = padding.left + padding.right + min_label_w;
+    }
+
+  if (natural_width_p)
+    {
+      *natural_width_p = padding.left + padding.right + natural_label_w;
+    }
+}
+
+static void
+nbtk_tooltip_get_preferred_height (ClutterActor *self,
+                                   gfloat        for_width,
+                                   gfloat       *min_height_p,
+                                   gfloat       *natural_height_p)
+{
+  NbtkTooltipPrivate *priv = NBTK_TOOLTIP (self)->priv;
+  gfloat arrow_height;
+  gfloat min_label_h, natural_label_h;
+  gfloat label_width;
+  ClutterActor *arrow_image;
+  NbtkPadding padding;
+
+  arrow_image = nbtk_widget_get_background_image (NBTK_WIDGET (self));
+
+  if (arrow_image && !priv->actor_below)
+    {
+      clutter_actor_get_preferred_height (arrow_image,
+                                          -1,
+                                          NULL,
+                                          &arrow_height);
+    }
+  else
+    {
+      arrow_height = 0;
+    }
+  nbtk_widget_get_padding (NBTK_WIDGET (self), &padding);
+
+  if (for_width > -1)
+    {
+      label_width = for_width - padding.left - padding.right;
+    }
+  else
+    {
+      label_width = -1;
+    }
+
+  if (priv->label)
+    {
+      clutter_actor_get_preferred_height (priv->label,
+                                          label_width,
+                                          &min_label_h,
+                                          &natural_label_h);
+    }
+  else
+    {
+      min_label_h = 0;
+      natural_label_h = 0;
+    }
+
+  if (min_height_p)
+    {
+      *min_height_p = padding.top + padding.bottom
+                      + arrow_height + min_label_h;
+    }
+
+  if (natural_height_p)
+    {
+      *natural_height_p = padding.top + padding.bottom
+                          + arrow_height + natural_label_h;
+    }
+}
+
+static void
+nbtk_tooltip_allocate (ClutterActor          *self,
+                       const ClutterActorBox *box,
+                       ClutterAllocationFlags flags)
+{
+  NbtkTooltipPrivate *priv = NBTK_TOOLTIP (self)->priv;
+  ClutterActorBox child_box, arrow_box;
+  gfloat arrow_height, arrow_width;
+  ClutterActor *border_image, *arrow_image;
+  NbtkPadding padding;
+
+  CLUTTER_ACTOR_CLASS (nbtk_tooltip_parent_class)->allocate (self,
+                                                             box,
+                                                             flags);
+
+  nbtk_widget_get_padding (NBTK_WIDGET (self), &padding);
+
+  arrow_image = nbtk_widget_get_background_image (NBTK_WIDGET (self));
+
+  if (arrow_image && !priv->actor_below)
+    {
+      clutter_actor_get_preferred_height (arrow_image, -1, NULL, &arrow_height);
+      clutter_actor_get_preferred_width (arrow_image, -1, NULL, &arrow_width);
+
+      arrow_box.x1 = (float) (priv->arrow_offset) - (int) (arrow_width / 2);
+      arrow_box.y1 = 0;
+      arrow_box.x2 = arrow_box.x1 + arrow_width;
+      arrow_box.y2 = arrow_box.y1 + arrow_height;
+
+      clutter_actor_allocate (arrow_image, &arrow_box, flags);
+    }
+  else
+    {
+      arrow_height = 0;
+      arrow_width = 0;
+    }
+
+  child_box.x1 = child_box.y1 = 0;
+  child_box.x2 = (box->x2 - box->x1);
+  child_box.y2 = (box->y2 - box->y1);
+
+  /* remove the space that is used by the arrow */
+  child_box.y1 += arrow_height;
+
+  border_image = nbtk_widget_get_border_image (NBTK_WIDGET (self));
+  if (border_image)
+    clutter_actor_allocate (border_image, &child_box, flags);
+
+  if (priv->label)
+    {
+      /* now remove the padding */
+      child_box.y1 += padding.top;
+      child_box.x1 += padding.left;
+      child_box.x2 -= padding.right;
+      child_box.y2 -= padding.bottom;
+
+      clutter_actor_allocate (priv->label, &child_box, flags);
+    }
+}
+
+
+static void
+nbtk_tooltip_paint (ClutterActor *self)
+{
+  ClutterActor *border_image, *arrow_image;
+  NbtkTooltipPrivate *priv = NBTK_TOOLTIP (self)->priv;
+
+  border_image = nbtk_widget_get_border_image (NBTK_WIDGET (self));
+  if (border_image)
+    clutter_actor_paint (border_image);
+
+  arrow_image = nbtk_widget_get_background_image (NBTK_WIDGET (self));
+  if (arrow_image && !priv->actor_below)
+    clutter_actor_paint (arrow_image);
+
+  clutter_actor_paint (priv->label);
+}
+
+static void
+nbtk_tooltip_map (ClutterActor *self)
+{
+  NbtkTooltipPrivate *priv = NBTK_TOOLTIP (self)->priv;
+  ClutterActor *border_image, *arrow_image;
+
+  CLUTTER_ACTOR_CLASS (nbtk_tooltip_parent_class)->map (self);
+
+  border_image = nbtk_widget_get_border_image (NBTK_WIDGET (self));
+  if (border_image)
+    clutter_actor_map (border_image);
+
+  arrow_image = nbtk_widget_get_background_image (NBTK_WIDGET (self));
+  if (arrow_image)
+    clutter_actor_map (arrow_image);
+
+  clutter_actor_map (priv->label);
+}
+
+static void
+nbtk_tooltip_unmap (ClutterActor *self)
+{
+  NbtkTooltipPrivate *priv = NBTK_TOOLTIP (self)->priv;
+  ClutterActor *border_image, *arrow_image;
+
+  CLUTTER_ACTOR_CLASS (nbtk_tooltip_parent_class)->unmap (self);
+
+  border_image = nbtk_widget_get_border_image (NBTK_WIDGET (self));
+  if (border_image)
+    clutter_actor_unmap (border_image);
+
+  arrow_image = nbtk_widget_get_background_image (NBTK_WIDGET (self));
+  if (arrow_image)
+    clutter_actor_unmap (arrow_image);
+
+  clutter_actor_unmap (priv->label);
+}
+
+static void
+nbtk_tooltip_class_init (NbtkTooltipClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+  GParamSpec *pspec;
+
+  g_type_class_add_private (klass, sizeof (NbtkTooltipPrivate));
+
+  gobject_class->set_property = nbtk_tooltip_set_property;
+  gobject_class->get_property = nbtk_tooltip_get_property;
+
+  actor_class->get_preferred_width = nbtk_tooltip_get_preferred_width;
+  actor_class->get_preferred_height = nbtk_tooltip_get_preferred_height;
+  actor_class->allocate = nbtk_tooltip_allocate;
+  actor_class->paint = nbtk_tooltip_paint;
+  actor_class->map = nbtk_tooltip_map;
+  actor_class->unmap = nbtk_tooltip_unmap;
+
+  pspec = g_param_spec_string ("label",
+                               "Label",
+                               "Label of the tooltip",
+                               NULL, G_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_LABEL, pspec);
+
+  pspec = g_param_spec_boxed ("tip-area",
+                              "Tip Area",
+                              "Area on the stage the tooltip applies to",
+                              CLUTTER_TYPE_GEOMETRY,
+                              NBTK_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_TIP_AREA, pspec);
+}
+
+static void
+nbtk_tooltip_init (NbtkTooltip *tooltip)
+{
+  tooltip->priv = NBTK_TOOLTIP_GET_PRIVATE (tooltip);
+
+  tooltip->priv->label = g_object_new (CLUTTER_TYPE_TEXT,
+                                       "line-alignment", PANGO_ALIGN_CENTER,
+                                       "ellipsize", PANGO_ELLIPSIZE_END,
+                                       "use-markup", TRUE,
+                                       NULL);
+
+  tooltip->priv->tip_area = NULL;
+
+  clutter_actor_set_parent (CLUTTER_ACTOR (tooltip->priv->label),
+                            CLUTTER_ACTOR (tooltip));
+
+  g_object_set (tooltip, "show-on-set-parent", FALSE, NULL);
+
+  clutter_actor_set_reactive (CLUTTER_ACTOR (tooltip), FALSE);
+
+  g_signal_connect (tooltip, "style-changed",
+                    G_CALLBACK (nbtk_tooltip_style_changed), NULL);
+}
+
+static void
+nbtk_tooltip_update_position (NbtkTooltip *tooltip)
+{
+  NbtkTooltipPrivate *priv = tooltip->priv;
+  ClutterGeometry *tip_area = tooltip->priv->tip_area;
+  gfloat tooltip_w, tooltip_h, tooltip_x, tooltip_y;
+  gfloat stage_w, stage_h;
+  ClutterActor *stage;
+
+  /* ensure the tooltip with is not fixed size */
+  clutter_actor_set_size ((ClutterActor*) tooltip, -1, -1);
+
+  /* if no area set, just position ourselves top left */
+  if (!priv->tip_area)
+    {
+      clutter_actor_set_position ((ClutterActor*) tooltip, 0, 0);
+      return;
+    }
+
+  /* we need to have a style in case there are padding values to take into
+   * account when calculating width/height */
+  nbtk_widget_ensure_style ((NbtkWidget *) tooltip);
+
+  /* find out the tooltip's size */
+  clutter_actor_get_size ((ClutterActor*) tooltip, &tooltip_w, &tooltip_h);
+
+  /* attempt to place the tooltip */
+  tooltip_x = (int) (tip_area->x + (tip_area->width / 2) - (tooltip_w / 2));
+  tooltip_y = (int) (tip_area->y + tip_area->height);
+
+  stage = clutter_actor_get_stage ((ClutterActor *) tooltip);
+  if (!stage)
+    {
+      return;
+    }
+  clutter_actor_get_size (stage, &stage_w, &stage_h);
+
+  /* make sure the tooltip is not off screen vertically */
+  if (tooltip_w > stage_w)
+    {
+      tooltip_x = 0;
+      clutter_actor_set_width ((ClutterActor*) tooltip, stage_w);
+    }
+  else if (tooltip_x < 0)
+    {
+      tooltip_x = 0;
+    }
+  else if (tooltip_x + tooltip_w > stage_w)
+    {
+      tooltip_x = (int) (stage_w) - tooltip_w;
+    }
+
+  /* make sure the tooltip is not off screen horizontally */
+  if (tooltip_y + tooltip_h > stage_h)
+    {
+      priv->actor_below = TRUE;
+
+      /* re-query size as may have changed */
+      clutter_actor_get_preferred_height ((ClutterActor*) tooltip,
+                                          -1, NULL, &tooltip_h);
+      tooltip_y = tip_area->y - tooltip_h;
+    }
+  else
+    {
+      priv->actor_below = FALSE;
+    }
+
+  /* calculate the arrow offset */
+  priv->arrow_offset = tip_area->x + tip_area->width / 2 - tooltip_x;
+
+  clutter_actor_set_position ((ClutterActor*) tooltip, tooltip_x, tooltip_y);
+}
+
+/**
+ * nbtk_tooltip_get_label:
+ * @tooltip: a #NbtkTooltip
+ *
+ * Get the text displayed on the tooltip
+ *
+ * Returns: the text for the tooltip. This must not be freed by the application
+ */
+G_CONST_RETURN gchar *
+nbtk_tooltip_get_label (NbtkTooltip *tooltip)
+{
+  g_return_val_if_fail (NBTK_IS_TOOLTIP (tooltip), NULL);
+
+  return clutter_text_get_text (CLUTTER_TEXT (tooltip->priv->label));
+}
+
+/**
+ * nbtk_tooltip_set_label:
+ * @tooltip: a #NbtkTooltip
+ * @text: text to set the label to
+ *
+ * Sets the text displayed on the tooltip
+ */
+void
+nbtk_tooltip_set_label (NbtkTooltip *tooltip,
+                        const gchar *text)
+{
+  NbtkTooltipPrivate *priv;
+
+  g_return_if_fail (NBTK_IS_TOOLTIP (tooltip));
+
+  priv = tooltip->priv;
+
+  clutter_text_set_text (CLUTTER_TEXT (priv->label), text);
+
+  g_object_notify (G_OBJECT (tooltip), "label");
+}
+
+/**
+ * nbtk_tooltip_show:
+ * @tooltip: a #NbtkTooltip
+ *
+ * Show the tooltip relative to the associated widget.
+ */
+void
+nbtk_tooltip_show (NbtkTooltip *tooltip)
+{
+  NbtkTooltipPrivate *priv;
+  ClutterActor *parent;
+  ClutterActor *stage;
+  ClutterActor *self = CLUTTER_ACTOR (tooltip);
+  ClutterAnimation *animation;
+
+  /* make sure we're not currently already animating (e.g. hiding) */
+  animation = clutter_actor_get_animation (CLUTTER_ACTOR (tooltip));
+  if (animation)
+    clutter_animation_completed (animation);
+
+  priv = tooltip->priv;
+  parent = clutter_actor_get_parent (self);
+  stage = clutter_actor_get_stage (self);
+
+  if (!stage)
+    {
+      g_warning ("NbtkTooltip is not on any stage.");
+      return;
+    }
+
+  /* make sure we're parented on the stage */
+  if (G_UNLIKELY (parent != stage))
+    {
+      g_object_ref (self);
+      clutter_actor_unparent (self);
+      clutter_actor_set_parent (self, stage);
+      g_object_unref (self);
+      parent = stage;
+    }
+
+  /* raise the tooltip to the top */
+  clutter_container_raise_child (CLUTTER_CONTAINER (stage),
+                                 CLUTTER_ACTOR (tooltip),
+                                 NULL);
+
+  nbtk_tooltip_update_position (tooltip);
+
+  /* finally show the tooltip... */
+  CLUTTER_ACTOR_CLASS (nbtk_tooltip_parent_class)->show (self);
+
+  /* and give it some bounce! */
+  g_object_set (G_OBJECT (self),
+                "scale-center-x", priv->arrow_offset,
+                "scale-center-y", (priv->actor_below) ? clutter_actor_get_height (self) : 0,
+                NULL);
+  clutter_actor_set_scale (self, 0.0, 0.0);
+  clutter_actor_animate (self, CLUTTER_EASE_OUT_ELASTIC,
+                         500,
+                         "scale-x", 1.0,
+                         "scale-y", 1.0,
+                         NULL);
+}
+
+static void
+nbtk_tooltip_hide_complete (ClutterAnimation *animation,
+                            ClutterActor     *actor)
+{
+  CLUTTER_ACTOR_CLASS (nbtk_tooltip_parent_class)->hide (actor);
+  g_signal_handlers_disconnect_by_func (actor,
+                                        nbtk_tooltip_hide_complete,
+                                        actor);
+}
+
+/**
+ * nbtk_tooltip_hide:
+ * @tooltip: a #NbtkTooltip
+ *
+ * Hide the tooltip
+ */
+void
+nbtk_tooltip_hide (NbtkTooltip *tooltip)
+{
+  ClutterAnimation *animation;
+
+  g_return_if_fail (NBTK_TOOLTIP (tooltip));
+
+  /* make sure we're not currently already animating (e.g. hiding) */
+  animation = clutter_actor_get_animation (CLUTTER_ACTOR (tooltip));
+  if (animation)
+    clutter_animation_completed (animation);
+
+  g_object_set (G_OBJECT (tooltip),
+                "scale-center-x", tooltip->priv->arrow_offset,
+                NULL);
+  animation =
+    clutter_actor_animate (CLUTTER_ACTOR (tooltip), CLUTTER_EASE_IN_SINE,
+                           150,
+                           "scale-x", 0.0,
+                           "scale-y", 0.0,
+                           NULL);
+  g_signal_connect (animation, "completed",
+                    G_CALLBACK (nbtk_tooltip_hide_complete), tooltip);
+}
+
+/**
+ * nbtk_tooltip_set_tip_area:
+ * @tooltip: A #NbtkTooltip
+ * @area: A #ClutterGeometry
+ *
+ * Set the area on the stage that the tooltip applies to.
+ */
+void
+nbtk_tooltip_set_tip_area (NbtkTooltip           *tooltip,
+                           const ClutterGeometry *area)
+{
+  g_return_if_fail (NBTK_IS_TOOLTIP (tooltip));
+
+  if (tooltip->priv->tip_area)
+    g_boxed_free (CLUTTER_TYPE_GEOMETRY, tooltip->priv->tip_area);
+  tooltip->priv->tip_area = g_boxed_copy (CLUTTER_TYPE_GEOMETRY, area);
+
+  nbtk_tooltip_update_position (tooltip);
+}
+
+/**
+ * nbtk_tooltip_get_tip_area:
+ * @tooltip: A #NbtkTooltip
+ *
+ * Retrieve the area on the stage that the tooltip currently applies to
+ *
+ * Returns: the #ClutterGeometry, owned by the tooltip which must not be freed
+ * by the application.
+ */
+G_CONST_RETURN ClutterGeometry*
+nbtk_tooltip_get_tip_area (NbtkTooltip *tooltip)
+{
+  g_return_val_if_fail (NBTK_IS_TOOLTIP (tooltip), NULL);
+
+  return tooltip->priv->tip_area;
+}
diff --git a/src/nbtk/nbtk-tooltip.h b/src/nbtk/nbtk-tooltip.h
new file mode 100644
index 0000000..8f6b435
--- /dev/null
+++ b/src/nbtk/nbtk-tooltip.h
@@ -0,0 +1,79 @@
+/*
+ * nbtk-tooltip.h: Plain tooltip actor
+ *
+ * Copyright 2008, 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Thomas Wood <thomas linux intel com>
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_TOOLTIP_H__
+#define __NBTK_TOOLTIP_H__
+
+G_BEGIN_DECLS
+
+#include <nbtk/nbtk-bin.h>
+
+#define NBTK_TYPE_TOOLTIP                (nbtk_tooltip_get_type ())
+#define NBTK_TOOLTIP(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_TOOLTIP, NbtkTooltip))
+#define NBTK_IS_TOOLTIP(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_TOOLTIP))
+#define NBTK_TOOLTIP_CLASS(klass)        (G_TYPE_CHECK_CLASS_CAST ((klass), NBTK_TYPE_TOOLTIP, NbtkTooltipClass))
+#define NBTK_IS_TOOLTIP_CLASS(klass)     (G_TYPE_CHECK_CLASS_TYPE ((klass), NBTK_TYPE_TOOLTIP))
+#define NBTK_TOOLTIP_GET_CLASS(obj)      (G_TYPE_INSTANCE_GET_CLASS ((obj), NBTK_TYPE_TOOLTIP, NbtkTooltipClass))
+
+typedef struct _NbtkTooltip              NbtkTooltip;
+typedef struct _NbtkTooltipPrivate       NbtkTooltipPrivate;
+typedef struct _NbtkTooltipClass         NbtkTooltipClass;
+
+/**
+ * NbtkTooltip:
+ *
+ * The contents of this structure is private and should only be accessed using
+ * the provided API.
+ */
+struct _NbtkTooltip
+{
+  /*< private >*/
+  NbtkBin parent_instance;
+
+  NbtkTooltipPrivate *priv;
+};
+
+struct _NbtkTooltipClass
+{
+  NbtkBinClass parent_class;
+};
+
+GType nbtk_tooltip_get_type (void) G_GNUC_CONST;
+
+G_CONST_RETURN gchar *nbtk_tooltip_get_label (NbtkTooltip *tooltip);
+void                  nbtk_tooltip_set_label (NbtkTooltip *tooltip,
+                                              const gchar *text);
+void                  nbtk_tooltip_show      (NbtkTooltip *tooltip);
+void                  nbtk_tooltip_hide      (NbtkTooltip *tooltip);
+
+void                  nbtk_tooltip_set_tip_area (NbtkTooltip *tooltip, const ClutterGeometry *area);
+G_CONST_RETURN ClutterGeometry* nbtk_tooltip_get_tip_area (NbtkTooltip *tooltip);
+
+G_END_DECLS
+
+#endif /* __NBTK_TOOLTIP_H__ */
diff --git a/src/nbtk/nbtk-types.h b/src/nbtk/nbtk-types.h
new file mode 100644
index 0000000..77acfad
--- /dev/null
+++ b/src/nbtk/nbtk-types.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_TYPES_H__
+#define __NBTK_TYPES_H__
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_BORDER_IMAGE          (nbtk_border_image_get_type ())
+#define NBTK_TYPE_PADDING               (nbtk_padding_get_type ())
+
+typedef struct _NbtkPadding             NbtkPadding;
+
+GType nbtk_border_image_get_type (void) G_GNUC_CONST;
+
+/**
+ * NbtkPadding:
+ * @top: padding from the top
+ * @right: padding from the right
+ * @bottom: padding from the bottom
+ * @left: padding from the left
+ *
+ * The padding from the internal border of the parent container.
+ */
+struct _NbtkPadding
+{
+  gfloat top;
+  gfloat right;
+  gfloat bottom;
+  gfloat left;
+};
+
+GType nbtk_padding_get_type (void) G_GNUC_CONST;
+
+/**
+ * NbtkAlignment:
+ * @NBTK_ALIGN_TOP: align to the top (vertically)
+ * @NBTK_ALIGN_RIGHT: align to the right (horizontally)
+ * @NBTK_ALIGN_BOTTOM: align to the bottom (vertically)
+ * @NBTK_ALIGN_LEFT: align to the left (horizontally)
+ * @NBTK_ALIGN_CENTER: align to the center (horizontally or vertically)
+ *
+ * The alignment values for a #NbtkBin.
+ */
+typedef enum {
+  NBTK_ALIGN_TOP,
+  NBTK_ALIGN_RIGHT,
+  NBTK_ALIGN_BOTTOM,
+  NBTK_ALIGN_LEFT,
+  NBTK_ALIGN_CENTER
+} NbtkAlignment;
+
+typedef enum {
+  NBTK_ALIGN_START,
+  NBTK_ALIGN_MIDDLE,
+  NBTK_ALIGN_END
+} NbtkAlign;
+
+G_END_DECLS
+
+#endif /* __NBTK_TYPES_H__ */
diff --git a/src/nbtk/nbtk-widget.c b/src/nbtk/nbtk-widget.c
new file mode 100644
index 0000000..1542cec
--- /dev/null
+++ b/src/nbtk/nbtk-widget.c
@@ -0,0 +1,1354 @@
+/*
+ * nbtk-widget.c: Base class for Nbtk actors
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2008, 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Written by: Emmanuele Bassi <ebassi openedhand com>
+ *             Thomas Wood <thomas linux intel com>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <clutter/clutter.h>
+#include <ccss/ccss.h>
+
+#include "nbtk-widget.h"
+
+#include "nbtk-marshal.h"
+#include "nbtk-private.h"
+#include "nbtk-stylable.h"
+#include "nbtk-texture-cache.h"
+#include "nbtk-texture-frame.h"
+#include "nbtk-tooltip.h"
+
+typedef ccss_border_image_t             NbtkBorderImage;
+
+/*
+ * Forward declaration for sake of NbtkWidgetChild
+ */
+struct _NbtkWidgetPrivate
+{
+  NbtkPadding border;
+  NbtkPadding padding;
+
+  NbtkStyle *style;
+  gchar *pseudo_class;
+  gchar *style_class;
+
+  ClutterActor *border_image;
+  ClutterActor *background_image;
+  ClutterColor *bg_color;
+
+  gboolean is_stylable : 1;
+  gboolean has_tooltip : 1;
+  gboolean is_style_dirty : 1;
+
+  NbtkTooltip *tooltip;
+};
+
+/**
+ * SECTION:nbtk-widget
+ * @short_description: Base class for stylable actors
+ *
+ * #NbtkWidget is a simple abstract class on top of #ClutterActor. It
+ * provides basic themeing properties.
+ *
+ * Actors in the Nbtk library should subclass #NbtkWidget if they plan
+ * to obey to a certain #NbtkStyle.
+ */
+
+enum
+{
+  PROP_0,
+
+  PROP_STYLE,
+  PROP_PSEUDO_CLASS,
+  PROP_STYLE_CLASS,
+
+  PROP_STYLABLE,
+
+  PROP_HAS_TOOLTIP,
+  PROP_TOOLTIP_TEXT
+};
+
+static void nbtk_stylable_iface_init (NbtkStylableIface *iface);
+
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NbtkWidget, nbtk_widget, CLUTTER_TYPE_ACTOR,
+                                  G_IMPLEMENT_INTERFACE (NBTK_TYPE_STYLABLE,
+                                                         nbtk_stylable_iface_init));
+
+#define NBTK_WIDGET_GET_PRIVATE(obj)    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NBTK_TYPE_WIDGET, NbtkWidgetPrivate))
+
+static void
+nbtk_widget_set_property (GObject      *gobject,
+                          guint         prop_id,
+                          const GValue *value,
+                          GParamSpec   *pspec)
+{
+  NbtkWidget *actor = NBTK_WIDGET (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_STYLE:
+      nbtk_stylable_set_style (NBTK_STYLABLE (actor),
+                               g_value_get_object (value));
+      break;
+
+    case PROP_PSEUDO_CLASS:
+      nbtk_widget_set_style_pseudo_class (actor, g_value_get_string (value));
+      break;
+
+    case PROP_STYLE_CLASS:
+      nbtk_widget_set_style_class_name (actor, g_value_get_string (value));
+      break;
+
+    case PROP_STYLABLE:
+      if (actor->priv->is_stylable != g_value_get_boolean (value))
+        {
+          actor->priv->is_stylable = g_value_get_boolean (value);
+          clutter_actor_queue_relayout ((ClutterActor *)gobject);
+        }
+      break;
+
+    case PROP_HAS_TOOLTIP:
+      nbtk_widget_set_has_tooltip (actor, g_value_get_boolean (value));
+      break;
+
+    case PROP_TOOLTIP_TEXT:
+      nbtk_widget_set_tooltip_text (actor, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+nbtk_widget_get_property (GObject    *gobject,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  NbtkWidget *actor = NBTK_WIDGET (gobject);
+  NbtkWidgetPrivate *priv = actor->priv;
+
+  switch (prop_id)
+    {
+    case PROP_STYLE:
+      g_value_set_object (value, priv->style);
+      break;
+
+    case PROP_PSEUDO_CLASS:
+      g_value_set_string (value, priv->pseudo_class);
+      break;
+
+    case PROP_STYLE_CLASS:
+      g_value_set_string (value, priv->style_class);
+      break;
+
+    case PROP_STYLABLE:
+      g_value_set_boolean (value, priv->is_stylable);
+      break;
+
+    case PROP_HAS_TOOLTIP:
+      g_value_set_boolean (value, priv->has_tooltip);
+      break;
+
+    case PROP_TOOLTIP_TEXT:
+      g_value_set_string (value, nbtk_tooltip_get_label (priv->tooltip));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+nbtk_widget_dispose (GObject *gobject)
+{
+  NbtkWidget *actor = NBTK_WIDGET (gobject);
+  NbtkWidgetPrivate *priv = NBTK_WIDGET (actor)->priv;
+
+  if (priv->style)
+    {
+      g_object_unref (priv->style);
+      priv->style = NULL;
+    }
+
+  if (priv->border_image)
+    {
+      clutter_actor_unparent (priv->border_image);
+      priv->border_image = NULL;
+    }
+
+  if (priv->bg_color)
+    {
+      clutter_color_free (priv->bg_color);
+      priv->bg_color = NULL;
+    }
+
+  if (priv->tooltip)
+    {
+      ClutterContainer *parent;
+      ClutterActor *tooltip = CLUTTER_ACTOR (priv->tooltip);
+
+      /* this is just a little bit awkward because the tooltip is parented
+       * on the stage, but we still want to "own" it */
+      parent = CLUTTER_CONTAINER (clutter_actor_get_parent (tooltip));
+
+      if (parent)
+        clutter_container_remove_actor (parent, tooltip);
+
+      priv->tooltip = NULL;
+    }
+
+  G_OBJECT_CLASS (nbtk_widget_parent_class)->dispose (gobject);
+}
+
+static void
+nbtk_widget_finalize (GObject *gobject)
+{
+  NbtkWidgetPrivate *priv = NBTK_WIDGET (gobject)->priv;
+
+  g_free (priv->style_class);
+  g_free (priv->pseudo_class);
+
+  G_OBJECT_CLASS (nbtk_widget_parent_class)->finalize (gobject);
+}
+
+static void
+nbtk_widget_allocate (ClutterActor          *actor,
+                      const ClutterActorBox *box,
+                      ClutterAllocationFlags flags)
+{
+  NbtkWidgetPrivate *priv = NBTK_WIDGET (actor)->priv;
+  ClutterActorClass *klass;
+  ClutterGeometry area;
+  ClutterVertex in_v, out_v;
+
+  klass = CLUTTER_ACTOR_CLASS (nbtk_widget_parent_class);
+  klass->allocate (actor, box, flags);
+
+  /* update tooltip position */
+  if (priv->tooltip)
+    {
+      in_v.x = in_v.y = in_v.z = 0;
+      clutter_actor_apply_transform_to_point (actor, &in_v, &out_v);
+      area.x = out_v.x;
+      area.y = out_v.y;
+
+      in_v.x = box->x2 - box->x1;
+      in_v.y = box->y2 - box->y1;
+      clutter_actor_apply_transform_to_point (actor, &in_v, &out_v);
+      area.width = out_v.x - area.x;
+      area.height = out_v.y - area.y;
+
+      nbtk_tooltip_set_tip_area (priv->tooltip, &area);
+    }
+
+
+
+  if (priv->border_image)
+    {
+      ClutterActorBox frame_box = {
+        0,
+        0,
+        box->x2 - box->x1,
+        box->y2 - box->y1
+      };
+
+      clutter_actor_allocate (CLUTTER_ACTOR (priv->border_image),
+                              &frame_box,
+                              flags);
+    }
+
+  if (priv->background_image)
+    {
+      ClutterActorBox frame_box = {
+          0, 0, box->x2 - box->x1, box->y2 - box->y1
+      };
+      gfloat w, h;
+
+      clutter_actor_get_size (CLUTTER_ACTOR (priv->background_image), &w, &h);
+
+      /* scale the background into the allocated bounds */
+      if (w > frame_box.x2 || h > frame_box.y2)
+        {
+          gint new_h, new_w, offset;
+          gint box_w, box_h;
+
+          box_w = (int) frame_box.x2;
+          box_h = (int) frame_box.y2;
+
+          /* scale to fit */
+          new_h = (int) ((h / w) * ((gfloat) box_w));
+          new_w = (int) ((w / h) * ((gfloat) box_h));
+
+          if (new_h > box_h)
+            {
+              /* center for new width */
+              offset = ((box_w) - new_w) * 0.5;
+              frame_box.x1 = offset;
+              frame_box.x2 = offset + new_w;
+
+              frame_box.y2 = box_h;
+            }
+          else
+            {
+              /* center for new height */
+              offset = ((box_h) - new_h) * 0.5;
+              frame_box.y1 = offset;
+              frame_box.y2 = offset + new_h;
+
+              frame_box.x2 = box_w;
+            }
+
+        }
+      else
+        {
+          /* center the background on the widget */
+          frame_box.x1 = (int) (((box->x2 - box->x1) / 2) - (w / 2));
+          frame_box.y1 = (int) (((box->y2 - box->y1) / 2) - (h / 2));
+          frame_box.x2 = frame_box.x1 + w;
+          frame_box.y2 = frame_box.y1 + h;
+        }
+
+      clutter_actor_allocate (CLUTTER_ACTOR (priv->background_image),
+                              &frame_box,
+                              flags);
+    }
+}
+
+static void
+nbtk_widget_real_draw_background (NbtkWidget         *self,
+                                  ClutterActor       *background,
+                                  const ClutterColor *color)
+{
+  /* Default implementation just draws the background
+   * colour and the image on top
+   */
+  if (color && color->alpha != 0)
+    {
+      ClutterActor *actor = CLUTTER_ACTOR (self);
+      ClutterActorBox allocation = { 0, };
+      ClutterColor bg_color = *color;
+      gfloat w, h;
+
+      bg_color.alpha = clutter_actor_get_paint_opacity (actor)
+                     * bg_color.alpha
+                     / 255;
+
+      clutter_actor_get_allocation_box (actor, &allocation);
+
+      w = allocation.x2 - allocation.x1;
+      h = allocation.y2 - allocation.y1;
+
+      cogl_set_source_color4ub (bg_color.red,
+                                bg_color.green,
+                                bg_color.blue,
+                                bg_color.alpha);
+      cogl_rectangle (0, 0, w, h);
+    }
+
+  if (background)
+    clutter_actor_paint (background);
+}
+
+static void
+nbtk_widget_paint (ClutterActor *self)
+{
+  NbtkWidgetPrivate *priv = NBTK_WIDGET (self)->priv;
+  NbtkWidgetClass *klass = NBTK_WIDGET_GET_CLASS (self);
+
+  klass->draw_background (NBTK_WIDGET (self),
+                          priv->border_image,
+                          priv->bg_color);
+
+  if (priv->background_image != NULL)
+    clutter_actor_paint (priv->background_image);
+}
+
+static void
+nbtk_widget_parent_set (ClutterActor *widget,
+                        ClutterActor *old_parent)
+{
+  ClutterActorClass *parent_class;
+  ClutterActor *new_parent;
+
+  parent_class = CLUTTER_ACTOR_CLASS (nbtk_widget_parent_class);
+  if (parent_class->parent_set)
+    parent_class->parent_set (widget, old_parent);
+
+  new_parent = clutter_actor_get_parent (widget);
+
+  /* don't send the style changed signal if we no longer have a parent actor */
+  if (new_parent)
+    nbtk_stylable_changed ((NbtkStylable*) widget);
+}
+
+static void
+nbtk_widget_map (ClutterActor *actor)
+{
+  NbtkWidgetPrivate *priv = NBTK_WIDGET (actor)->priv;
+
+  CLUTTER_ACTOR_CLASS (nbtk_widget_parent_class)->map (actor);
+
+  nbtk_widget_ensure_style ((NbtkWidget*) actor);
+
+  if (priv->border_image)
+    clutter_actor_map (priv->border_image);
+
+  if (priv->background_image)
+    clutter_actor_map (priv->background_image);
+
+  if (priv->tooltip)
+    clutter_actor_map ((ClutterActor *) priv->tooltip);
+}
+
+static void
+nbtk_widget_unmap (ClutterActor *actor)
+{
+  NbtkWidgetPrivate *priv = NBTK_WIDGET (actor)->priv;
+
+  CLUTTER_ACTOR_CLASS (nbtk_widget_parent_class)->unmap (actor);
+
+  if (priv->border_image)
+    clutter_actor_unmap (priv->border_image);
+
+  if (priv->background_image)
+    clutter_actor_unmap (priv->background_image);
+
+  if (priv->tooltip)
+    clutter_actor_unmap ((ClutterActor *) priv->tooltip);
+}
+
+static void
+nbtk_widget_style_changed (NbtkStylable *self)
+{
+  NbtkWidgetPrivate *priv = NBTK_WIDGET (self)->priv;
+  NbtkBorderImage *border_image = NULL;
+  NbtkTextureCache *texture_cache;
+  ClutterTexture *texture;
+  gchar *bg_file = NULL;
+  NbtkPadding *padding = NULL;
+  gboolean relayout_needed = FALSE;
+  gboolean has_changed = FALSE;
+  ClutterColor *color;
+
+  /* application has request this widget is not stylable */
+  if (!priv->is_stylable)
+    return;
+
+  /* cache these values for use in the paint function */
+  nbtk_stylable_get (self,
+                    "background-color", &color,
+                    "background-image", &bg_file,
+                    "border-image", &border_image,
+                    "padding", &padding,
+                    NULL);
+
+  if (color)
+    {
+      if (priv->bg_color && clutter_color_equal (color, priv->bg_color))
+        {
+          /* color is the same ... */
+          clutter_color_free (color);
+        }
+      else
+        {
+          clutter_color_free (priv->bg_color);
+          priv->bg_color = color;
+          has_changed = TRUE;
+        }
+    }
+  else
+    if (priv->bg_color)
+      {
+        clutter_color_free (priv->bg_color);
+        priv->bg_color = NULL;
+        has_changed = TRUE;
+      }
+
+
+
+  if (padding)
+    {
+      if (priv->padding.top != padding->top ||
+          priv->padding.left != padding->left ||
+          priv->padding.right != padding->right ||
+          priv->padding.bottom != padding->bottom)
+      {
+        /* Padding changed. Need to relayout. */
+        has_changed = TRUE;
+        relayout_needed = TRUE;
+      }
+
+      priv->padding = *padding;
+      g_boxed_free (NBTK_TYPE_PADDING, padding);
+    }
+
+  if (priv->border_image)
+    {
+       clutter_actor_unparent (priv->border_image);
+       priv->border_image = NULL;
+    }
+
+  if (priv->background_image)
+    {
+      clutter_actor_unparent (priv->background_image);
+      priv->background_image = NULL;
+    }
+
+  texture_cache = nbtk_texture_cache_get_default ();
+
+  /* Check if the URL is actually present, not garbage in the property */
+  if (border_image && border_image->uri)
+    {
+      gint border_left, border_right, border_top, border_bottom;
+      gint width, height;
+
+      /* `border-image' takes precedence over `background-image'.
+       * Firefox lets the background-image shine thru when border-image has
+       * alpha an channel, maybe that would be an option for the future. */
+      texture = nbtk_texture_cache_get_texture (texture_cache,
+                                                border_image->uri,
+                                                FALSE);
+
+      clutter_texture_get_base_size (CLUTTER_TEXTURE (texture),
+                                     &width, &height);
+
+      border_left = ccss_position_get_size (&border_image->left, width);
+      border_top = ccss_position_get_size (&border_image->top, height);
+      border_right = ccss_position_get_size (&border_image->right, width);
+      border_bottom = ccss_position_get_size (&border_image->bottom, height);
+
+      priv->border_image = nbtk_texture_frame_new (texture,
+                                                   border_top,
+                                                   border_right,
+                                                   border_bottom,
+                                                   border_left);
+      clutter_actor_set_parent (priv->border_image, CLUTTER_ACTOR (self));
+      g_boxed_free (NBTK_TYPE_BORDER_IMAGE, border_image);
+
+      has_changed = TRUE;
+      relayout_needed = TRUE;
+    }
+
+  if (bg_file != NULL &&
+      strcmp (bg_file, "none"))
+    {
+      texture = nbtk_texture_cache_get_texture (texture_cache,
+                                                bg_file,
+                                                FALSE);
+      priv->background_image = (ClutterActor*) texture;
+
+      if (priv->background_image != NULL)
+        {
+          clutter_actor_set_parent (priv->background_image,
+                                    CLUTTER_ACTOR (self));
+        }
+      else
+        g_warning ("Could not load %s", bg_file);
+
+      has_changed = TRUE;
+      relayout_needed = TRUE;
+    }
+    g_free (bg_file);
+
+  /* If there are any properties above that need to cause a relayout thay
+   * should set this flag.
+   */
+  if (has_changed)
+    {
+      if (relayout_needed)
+        clutter_actor_queue_relayout ((ClutterActor *) self);
+      else
+        clutter_actor_queue_redraw ((ClutterActor *) self);
+    }
+
+  priv->is_style_dirty = FALSE;
+}
+
+static void
+nbtk_widget_stylable_child_notify (ClutterActor *actor,
+                                   gpointer      user_data)
+{
+  if (NBTK_IS_STYLABLE (actor))
+    nbtk_stylable_changed ((NbtkStylable*) actor);
+}
+
+static void
+nbtk_widget_stylable_changed (NbtkStylable *stylable)
+{
+
+  NBTK_WIDGET (stylable)->priv->is_style_dirty = TRUE;
+
+  /* update the style only if we are mapped */
+  if (!CLUTTER_ACTOR_IS_MAPPED ((ClutterActor *) stylable))
+    return;
+
+  g_signal_emit_by_name (stylable, "style-changed", 0);
+
+
+  if (CLUTTER_IS_CONTAINER (stylable))
+    {
+      /* notify our children that their parent stylable has changed */
+      clutter_container_foreach ((ClutterContainer *) stylable,
+                                 nbtk_widget_stylable_child_notify,
+                                 NULL);
+    }
+}
+
+static gboolean
+nbtk_widget_enter (ClutterActor         *actor,
+                   ClutterCrossingEvent *event)
+{
+  NbtkWidgetPrivate *priv = NBTK_WIDGET (actor)->priv;
+
+
+  if (priv->has_tooltip)
+      nbtk_widget_show_tooltip ((NbtkWidget*) actor);
+
+  if (CLUTTER_ACTOR_CLASS (nbtk_widget_parent_class)->enter_event)
+    return CLUTTER_ACTOR_CLASS (nbtk_widget_parent_class)->enter_event (actor, event);
+  else
+    return FALSE;
+}
+
+static gboolean
+nbtk_widget_leave (ClutterActor         *actor,
+                   ClutterCrossingEvent *event)
+{
+  NbtkWidgetPrivate *priv = NBTK_WIDGET (actor)->priv;
+
+  if (priv->has_tooltip)
+      nbtk_tooltip_hide (priv->tooltip);
+
+  if (CLUTTER_ACTOR_CLASS (nbtk_widget_parent_class)->leave_event)
+    return CLUTTER_ACTOR_CLASS (nbtk_widget_parent_class)->leave_event (actor, event);
+  else
+    return FALSE;
+}
+
+static void
+nbtk_widget_hide (ClutterActor *actor)
+{
+  NbtkWidget *widget = (NbtkWidget *) actor;
+
+  /* hide the tooltip, if there is one */
+  if (widget->priv->tooltip)
+    nbtk_tooltip_hide (NBTK_TOOLTIP (widget->priv->tooltip));
+
+  CLUTTER_ACTOR_CLASS (nbtk_widget_parent_class)->hide (actor);
+}
+
+
+
+static void
+nbtk_widget_class_init (NbtkWidgetClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+  GParamSpec *pspec;
+
+  g_type_class_add_private (klass, sizeof (NbtkWidgetPrivate));
+
+  gobject_class->set_property = nbtk_widget_set_property;
+  gobject_class->get_property = nbtk_widget_get_property;
+  gobject_class->dispose = nbtk_widget_dispose;
+  gobject_class->finalize = nbtk_widget_finalize;
+
+  actor_class->allocate = nbtk_widget_allocate;
+  actor_class->paint = nbtk_widget_paint;
+  actor_class->parent_set = nbtk_widget_parent_set;
+  actor_class->map = nbtk_widget_map;
+  actor_class->unmap = nbtk_widget_unmap;
+
+  actor_class->enter_event = nbtk_widget_enter;
+  actor_class->leave_event = nbtk_widget_leave;
+  actor_class->hide = nbtk_widget_hide;
+
+  klass->draw_background = nbtk_widget_real_draw_background;
+
+  /**
+   * NbtkWidget:pseudo-class:
+   *
+   * The pseudo-class of the actor. Typical values include "hover", "active",
+   * "focus".
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_PSEUDO_CLASS,
+                                   g_param_spec_string ("pseudo-class",
+                                                        "Pseudo Class",
+                                                        "Pseudo class for styling",
+                                                        "",
+                                                        NBTK_PARAM_READWRITE));
+  /**
+   * NbtkWidget:style-class:
+   *
+   * The style-class of the actor for use in styling.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_STYLE_CLASS,
+                                   g_param_spec_string ("style-class",
+                                                        "Style Class",
+                                                        "Style class for styling",
+                                                        "",
+                                                        NBTK_PARAM_READWRITE));
+
+  g_object_class_override_property (gobject_class, PROP_STYLE, "style");
+
+  /**
+   * NbtkWidget:stylable:
+   *
+   * Enable or disable styling of the widget
+   */
+  pspec = g_param_spec_boolean ("stylable",
+                                "Stylable",
+                                "Whether the table should be styled",
+                                TRUE,
+                                NBTK_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class,
+                                   PROP_STYLABLE,
+                                   pspec);
+
+  /**
+   * NbtkWidget:has-tooltip:
+   *
+   * Determines whether the widget has a tooltip. If set to TRUE, causes the
+   * widget to monitor enter and leave events (i.e. sets the widget reactive).
+   */
+  pspec = g_param_spec_boolean ("has-tooltip",
+                                "Has Tooltip",
+                                "Determines whether the widget has a tooltip",
+                                FALSE,
+                                NBTK_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class,
+                                   PROP_HAS_TOOLTIP,
+                                   pspec);
+
+
+  /**
+   * NbtkWidget:tooltip-text:
+   *
+   * text displayed on the tooltip
+   */
+  pspec = g_param_spec_string ("tooltip-text",
+                               "Tooltip Text",
+                               "Text displayed on the tooltip",
+                               "",
+                               NBTK_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_TOOLTIP_TEXT, pspec);
+
+}
+
+static NbtkStyle *
+nbtk_widget_get_style (NbtkStylable *stylable)
+{
+  NbtkWidgetPrivate *priv = NBTK_WIDGET (stylable)->priv;
+
+  return priv->style;
+}
+
+static void
+nbtk_style_changed_cb (NbtkStyle    *style,
+                       NbtkStylable *stylable)
+{
+  nbtk_stylable_changed (stylable);
+}
+
+
+static void
+nbtk_widget_set_style (NbtkStylable *stylable,
+                       NbtkStyle    *style)
+{
+  NbtkWidgetPrivate *priv = NBTK_WIDGET (stylable)->priv;
+
+  if (priv->style)
+    g_object_unref (priv->style);
+
+  priv->style = g_object_ref_sink (style);
+
+  g_signal_connect (priv->style,
+                    "changed",
+                    G_CALLBACK (nbtk_style_changed_cb),
+                    stylable);
+}
+
+static NbtkStylable*
+nbtk_widget_get_container (NbtkStylable *stylable)
+{
+  ClutterActor *parent;
+
+  g_return_val_if_fail (NBTK_IS_WIDGET (stylable), NULL);
+
+  parent = clutter_actor_get_parent (CLUTTER_ACTOR (stylable));
+
+  if (NBTK_IS_STYLABLE (parent))
+    return NBTK_STYLABLE (parent);
+  else
+    return NULL;
+}
+
+static NbtkStylable*
+nbtk_widget_get_base_style (NbtkStylable *stylable)
+{
+  return NULL;
+}
+
+static const gchar*
+nbtk_widget_get_style_id (NbtkStylable *stylable)
+{
+  g_return_val_if_fail (NBTK_IS_WIDGET (stylable), NULL);
+
+  return clutter_actor_get_name (CLUTTER_ACTOR (stylable));
+}
+
+static const gchar*
+nbtk_widget_get_style_type (NbtkStylable *stylable)
+{
+  return G_OBJECT_TYPE_NAME (stylable);
+}
+
+static const gchar*
+nbtk_widget_get_style_class (NbtkStylable *stylable)
+{
+  g_return_val_if_fail (NBTK_IS_WIDGET (stylable), NULL);
+
+  return NBTK_WIDGET (stylable)->priv->style_class;
+}
+
+static const gchar*
+nbtk_widget_get_pseudo_class (NbtkStylable *stylable)
+{
+  g_return_val_if_fail (NBTK_IS_WIDGET (stylable), NULL);
+
+  return NBTK_WIDGET (stylable)->priv->pseudo_class;
+}
+
+static gboolean
+nbtk_widget_get_viewport (NbtkStylable *stylable,
+                          gint *x,
+                          gint *y,
+                          gint *width,
+                          gint *height)
+{
+  g_return_val_if_fail (NBTK_IS_WIDGET (stylable), FALSE);
+
+  *x = 0;
+  *y = 0;
+
+  *width = clutter_actor_get_width (CLUTTER_ACTOR (stylable));
+  *height = clutter_actor_get_height (CLUTTER_ACTOR (stylable));
+
+  return TRUE;
+}
+
+/**
+ * nbtk_widget_set_style_class_name:
+ * @actor: a #NbtkWidget
+ * @style_class: a new style class string
+ *
+ * Set the style class name
+ */
+void
+nbtk_widget_set_style_class_name (NbtkWidget  *actor,
+                                  const gchar *style_class)
+{
+  NbtkWidgetPrivate *priv = actor->priv;
+
+  g_return_if_fail (NBTK_WIDGET (actor));
+
+  priv = actor->priv;
+
+  if (g_strcmp0 (style_class, priv->style_class))
+    {
+      g_free (priv->style_class);
+      priv->style_class = g_strdup (style_class);
+
+      nbtk_stylable_changed ((NbtkStylable*) actor);
+
+      g_object_notify (G_OBJECT (actor), "style-class");
+    }
+}
+
+
+/**
+ * nbtk_widget_get_style_class_name:
+ * @actor: a #NbtkWidget
+ *
+ * Get the current style class name
+ *
+ * Returns: the class name string. The string is owned by the #NbtkWidget and
+ * should not be modified or freed.
+ */
+const gchar*
+nbtk_widget_get_style_class_name (NbtkWidget *actor)
+{
+  g_return_val_if_fail (NBTK_WIDGET (actor), NULL);
+
+  return actor->priv->style_class;
+}
+
+/**
+ * nbtk_widget_get_style_pseudo_class:
+ * @actor: a #NbtkWidget
+ *
+ * Get the current style pseudo class
+ *
+ * Returns: the pseudo class string. The string is owned by the #NbtkWidget and
+ * should not be modified or freed.
+ */
+const gchar*
+nbtk_widget_get_style_pseudo_class (NbtkWidget *actor)
+{
+  g_return_val_if_fail (NBTK_WIDGET (actor), NULL);
+
+  return actor->priv->pseudo_class;
+}
+
+/**
+ * nbtk_widget_set_style_pseudo_class:
+ * @actor: a #NbtkWidget
+ * @pseudo_class: a new pseudo class string
+ *
+ * Set the style pseudo class
+ */
+void
+nbtk_widget_set_style_pseudo_class (NbtkWidget  *actor,
+                                    const gchar *pseudo_class)
+{
+  NbtkWidgetPrivate *priv;
+
+  g_return_if_fail (NBTK_WIDGET (actor));
+
+  priv = actor->priv;
+
+  if (g_strcmp0 (pseudo_class, priv->pseudo_class))
+    {
+      g_free (priv->pseudo_class);
+      priv->pseudo_class = g_strdup (pseudo_class);
+
+      nbtk_stylable_changed ((NbtkStylable*) actor);
+
+      g_object_notify (G_OBJECT (actor), "pseudo-class");
+    }
+}
+
+
+static void
+nbtk_stylable_iface_init (NbtkStylableIface *iface)
+{
+  static gboolean is_initialized = FALSE;
+
+  if (!is_initialized)
+    {
+      GParamSpec *pspec;
+      ClutterColor color = { 0x00, 0x00, 0x00, 0xff };
+      ClutterColor bg_color = { 0xff, 0xff, 0xff, 0x00 };
+
+      is_initialized = TRUE;
+
+      pspec = clutter_param_spec_color ("background-color",
+                                  "Background Color",
+                                  "The background color of an actor",
+                                  &bg_color,
+                                  G_PARAM_READWRITE);
+      nbtk_stylable_iface_install_property (iface, NBTK_TYPE_WIDGET, pspec);
+
+      pspec = clutter_param_spec_color ("color",
+                                  "Text Color",
+                                  "The color of the text of an actor",
+                                  &color,
+                                  G_PARAM_READWRITE);
+      nbtk_stylable_iface_install_property (iface, NBTK_TYPE_WIDGET, pspec);
+
+      pspec = g_param_spec_string ("background-image",
+                                   "Background Image",
+                                   "Background image filename",
+                                   NULL,
+                                   G_PARAM_READWRITE);
+      nbtk_stylable_iface_install_property (iface, NBTK_TYPE_WIDGET, pspec);
+
+      pspec = g_param_spec_string ("font-family",
+                                   "Font Family",
+                                   "Name of the font to use",
+                                   "Sans",
+                                   G_PARAM_READWRITE);
+      nbtk_stylable_iface_install_property (iface, NBTK_TYPE_WIDGET, pspec);
+
+      pspec = g_param_spec_int ("font-size",
+                                "Font Size",
+                                "Size of the font to use in pixels",
+                                0, G_MAXINT, 12,
+                                G_PARAM_READWRITE);
+      nbtk_stylable_iface_install_property (iface, NBTK_TYPE_WIDGET, pspec);
+
+      pspec = g_param_spec_boxed ("border-image",
+                                  "Border image",
+                                  "9-slice image to use for drawing borders and background",
+                                  NBTK_TYPE_BORDER_IMAGE,
+                                  G_PARAM_READWRITE);
+      nbtk_stylable_iface_install_property (iface, NBTK_TYPE_WIDGET, pspec);
+
+      pspec = g_param_spec_boxed ("padding",
+                                  "Padding",
+                                  "Padding between the widget's borders "
+                                  "and its content",
+                                  NBTK_TYPE_PADDING,
+                                  G_PARAM_READWRITE);
+      nbtk_stylable_iface_install_property (iface, NBTK_TYPE_WIDGET, pspec);
+
+      iface->style_changed = nbtk_widget_style_changed;
+      iface->stylable_changed = nbtk_widget_stylable_changed;
+
+      iface->get_style = nbtk_widget_get_style;
+      iface->set_style = nbtk_widget_set_style;
+      iface->get_base_style = nbtk_widget_get_base_style;
+      iface->get_container = nbtk_widget_get_container;
+      iface->get_style_id = nbtk_widget_get_style_id;
+      iface->get_style_type = nbtk_widget_get_style_type;
+      iface->get_style_class = nbtk_widget_get_style_class;
+      iface->get_pseudo_class = nbtk_widget_get_pseudo_class;
+      /* iface->get_attribute = nbtk_widget_get_attribute; */
+      iface->get_viewport = nbtk_widget_get_viewport;
+    }
+}
+
+
+static void
+nbtk_widget_name_notify (NbtkWidget *widget,
+                         GParamSpec *pspec,
+                         gpointer data)
+{
+  nbtk_stylable_changed ((NbtkStylable*) widget);
+}
+
+static void
+nbtk_widget_init (NbtkWidget *actor)
+{
+  NbtkWidgetPrivate *priv;
+
+  actor->priv = priv = NBTK_WIDGET_GET_PRIVATE (actor);
+  priv->is_stylable = TRUE;
+
+  /* connect style changed */
+  g_signal_connect (actor, "notify::name", G_CALLBACK (nbtk_widget_name_notify), NULL);
+
+  /* set the default style */
+  nbtk_widget_set_style (NBTK_STYLABLE (actor), nbtk_style_get_default ());
+
+}
+
+static NbtkBorderImage *
+nbtk_border_image_copy (const NbtkBorderImage *border_image)
+{
+  NbtkBorderImage *copy;
+
+  g_return_val_if_fail (border_image != NULL, NULL);
+
+  copy = g_slice_new (NbtkBorderImage);
+  *copy = *border_image;
+
+  return copy;
+}
+
+static void
+nbtk_border_image_free (NbtkBorderImage *border_image)
+{
+  if (G_LIKELY (border_image))
+    g_slice_free (NbtkBorderImage, border_image);
+}
+
+GType
+nbtk_border_image_get_type (void)
+{
+  static GType our_type = 0;
+
+  if (G_UNLIKELY (our_type == 0))
+    our_type =
+      g_boxed_type_register_static (I_("NbtkBorderImage"),
+                                    (GBoxedCopyFunc) nbtk_border_image_copy,
+                                    (GBoxedFreeFunc) nbtk_border_image_free);
+
+  return our_type;
+}
+
+/**
+ * nbtk_widget_ensure_style:
+ * @widget: A #NbtkWidget
+ *
+ * Ensures that @widget has read its style information.
+ *
+ */
+void
+nbtk_widget_ensure_style (NbtkWidget *widget)
+{
+  g_return_if_fail (NBTK_IS_WIDGET (widget));
+
+  if (widget->priv->is_style_dirty)
+    {
+      g_signal_emit_by_name (widget, "style-changed", 0);
+    }
+}
+
+
+/**
+ * nbtk_widget_get_border_image:
+ * @actor: A #NbtkWidget
+ *
+ * Get the texture used as the border image. This is set using the
+ * "border-image" CSS property. This function should normally only be used
+ * by subclasses.
+ *
+ * Returns: #ClutterActor
+ */
+ClutterActor *
+nbtk_widget_get_border_image (NbtkWidget *actor)
+{
+  NbtkWidgetPrivate *priv = NBTK_WIDGET (actor)->priv;
+  return priv->border_image;
+}
+
+/**
+ * nbtk_widget_get_background_image:
+ * @actor: A #NbtkWidget
+ *
+ * Get the texture used as the background image. This is set using the
+ * "background-image" CSS property. This function should normally only be used
+ * by subclasses.
+ *
+ * Returns: a #ClutterActor
+ */
+ClutterActor *
+nbtk_widget_get_background_image (NbtkWidget *actor)
+{
+  NbtkWidgetPrivate *priv = NBTK_WIDGET (actor)->priv;
+  return priv->background_image;
+}
+
+/**
+ * nbtk_widget_get_padding:
+ * @widget: A #NbtkWidget
+ * @padding: A pointer to an #NbtkPadding to fill
+ *
+ * Gets the padding of the widget, set using the "padding" CSS property. This
+ * function should normally only be used by subclasses.
+ *
+ */
+void
+nbtk_widget_get_padding (NbtkWidget *widget,
+                         NbtkPadding *padding)
+{
+  g_return_if_fail (NBTK_IS_WIDGET (widget));
+  g_return_if_fail (padding != NULL);
+
+  *padding = widget->priv->padding;
+}
+
+/**
+ * nbtk_widget_set_has_tooltip:
+ * @widget: A #NbtkWidget
+ * @has_tooltip: #TRUE if the widget should display a tooltip
+ *
+ * Enables tooltip support on the #NbtkWidget.
+ *
+ * Note that setting has-tooltip to #TRUE will cause the widget to be set
+ * reactive. If you no longer need tooltip support and do not need the widget
+ * to be reactive, you need to set ClutterActor::reactive to FALSE.
+ *
+ */
+void
+nbtk_widget_set_has_tooltip (NbtkWidget *widget,
+                             gboolean    has_tooltip)
+{
+  NbtkWidgetPrivate *priv;
+
+  g_return_if_fail (NBTK_IS_WIDGET (widget));
+
+  priv = widget->priv;
+
+  priv->has_tooltip = has_tooltip;
+
+  if (has_tooltip)
+    {
+      clutter_actor_set_reactive ((ClutterActor*) widget, TRUE);
+
+      if (!priv->tooltip)
+        {
+          priv->tooltip = g_object_new (NBTK_TYPE_TOOLTIP, NULL);
+          clutter_actor_set_parent ((ClutterActor *) priv->tooltip,
+                                    (ClutterActor *) widget);
+        }
+    }
+  else
+    {
+      if (priv->tooltip)
+        {
+          clutter_actor_unparent (CLUTTER_ACTOR (priv->tooltip));
+          priv->tooltip = NULL;
+        }
+    }
+}
+
+/**
+ * nbtk_widget_get_has_tooltip:
+ * @widget: A #NbtkWidget
+ *
+ * Returns the current value of the has-tooltip property. See
+ * nbtk_tooltip_set_has_tooltip() for more information.
+ *
+ * Returns: current value of has-tooltip on @widget
+ */
+gboolean
+nbtk_widget_get_has_tooltip (NbtkWidget *widget)
+{
+  g_return_val_if_fail (NBTK_IS_WIDGET (widget), FALSE);
+
+  return widget->priv->has_tooltip;
+}
+
+/**
+ * nbtk_widget_set_tooltip_text:
+ * @widget: A #NbtkWidget
+ * @text: text to set as the tooltip
+ *
+ * Set the tooltip text of the widget. This will set NbtkWidget::has-tooltip to
+ * #TRUE. A value of #NULL will unset the tooltip and set has-tooltip to #FALSE.
+ *
+ */
+void
+nbtk_widget_set_tooltip_text (NbtkWidget  *widget,
+                              const gchar *text)
+{
+  NbtkWidgetPrivate *priv;
+
+  g_return_if_fail (NBTK_IS_WIDGET (widget));
+
+  priv = widget->priv;
+
+  if (text == NULL)
+    nbtk_widget_set_has_tooltip (widget, FALSE);
+  else
+    nbtk_widget_set_has_tooltip (widget, TRUE);
+
+  nbtk_tooltip_set_label (priv->tooltip, text);
+}
+
+/**
+ * nbtk_widget_get_tooltip_text:
+ * @widget: A #NbtkWidget
+ *
+ * Get the current tooltip string
+ *
+ * Returns: The current tooltip string, owned by the #NbtkWidget
+ */
+const gchar*
+nbtk_widget_get_tooltip_text (NbtkWidget *widget)
+{
+  g_return_val_if_fail (NBTK_IS_WIDGET (widget), NULL);
+
+  return nbtk_tooltip_get_label (widget->priv->tooltip);
+}
+
+/**
+ * nbtk_widget_show_tooltip:
+ * @widget: A #NbtkWidget
+ *
+ * Show the tooltip for @widget
+ *
+ */
+void
+nbtk_widget_show_tooltip (NbtkWidget *widget)
+{
+  gfloat x, y, width, height;
+  ClutterGeometry area;
+
+  g_return_if_fail (NBTK_IS_WIDGET (widget));
+
+  /* XXX not necceary, but first allocate transform is wrong */
+
+  clutter_actor_get_transformed_position ((ClutterActor*) widget,
+                                          &x, &y);
+
+  clutter_actor_get_size ((ClutterActor*) widget, &width, &height);
+
+  area.x = x;
+  area.y = y;
+  area.width = width;
+  area.height = height;
+
+
+  if (widget->priv->tooltip)
+    {
+      nbtk_tooltip_set_tip_area (widget->priv->tooltip, &area);
+      nbtk_tooltip_show (widget->priv->tooltip);
+    }
+}
+
+/**
+ * nbtk_widget_hide_tooltip:
+ * @widget: A #NbtkWidget
+ *
+ * Hide the tooltip for @widget
+ *
+ */
+void
+nbtk_widget_hide_tooltip (NbtkWidget *widget)
+{
+  g_return_if_fail (NBTK_IS_WIDGET (widget));
+
+  if (widget->priv->tooltip)
+    nbtk_tooltip_hide (widget->priv->tooltip);
+}
+
+/**
+ * nbtk_widget_draw_background:
+ * @widget: a #NbtkWidget
+ *
+ * Invokes #NbtkWidget::draw_background() using the default background
+ * image and/or color from the @widget style
+ *
+ * This function should be used by subclasses of #NbtkWidget that override
+ * the paint() virtual function and cannot chain up
+ */
+void
+nbtk_widget_draw_background (NbtkWidget *self)
+{
+  NbtkWidgetPrivate *priv;
+  NbtkWidgetClass *klass;
+
+  g_return_if_fail (NBTK_IS_WIDGET (self));
+
+  priv = self->priv;
+
+  klass = NBTK_WIDGET_GET_CLASS (self);
+  klass->draw_background (NBTK_WIDGET (self),
+                          priv->border_image,
+                          priv->bg_color);
+
+}
diff --git a/src/nbtk/nbtk-widget.h b/src/nbtk/nbtk-widget.h
new file mode 100644
index 0000000..1f5fe66
--- /dev/null
+++ b/src/nbtk/nbtk-widget.h
@@ -0,0 +1,107 @@
+/*
+ * nbtk-widget.h: Base class for Nbtk actors
+ *
+ * Copyright 2007 OpenedHand
+ * Copyright 2008, 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#if !defined(NBTK_H_INSIDE) && !defined(NBTK_COMPILATION)
+#error "Only <nbtk/nbtk.h> can be included directly.h"
+#endif
+
+#ifndef __NBTK_WIDGET_H__
+#define __NBTK_WIDGET_H__
+
+#include <clutter/clutter.h>
+#include <nbtk/nbtk-types.h>
+
+G_BEGIN_DECLS
+
+#define NBTK_TYPE_WIDGET                 (nbtk_widget_get_type ())
+#define NBTK_WIDGET(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), NBTK_TYPE_WIDGET, NbtkWidget))
+#define NBTK_IS_WIDGET(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NBTK_TYPE_WIDGET))
+#define NBTK_WIDGET_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), NBTK_TYPE_WIDGET, NbtkWidgetClass))
+#define NBTK_IS_WIDGET_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), NBTK_TYPE_WIDGET))
+#define NBTK_WIDGET_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), NBTK_TYPE_WIDGET, NbtkWidgetClass))
+
+typedef struct _NbtkWidget               NbtkWidget;
+typedef struct _NbtkWidgetPrivate        NbtkWidgetPrivate;
+typedef struct _NbtkWidgetClass          NbtkWidgetClass;
+
+/**
+ * NbtkWidget:
+ *
+ * Base class for stylable actors. The contents of the #NbtkWidget
+ * structure are private and should only be accessed through the
+ * public API.
+ */
+struct _NbtkWidget
+{
+  /*< private >*/
+  ClutterActor parent_instance;
+
+  NbtkWidgetPrivate *priv;
+};
+
+/**
+ * NbtkWidgetClass:
+ *
+ * Base class for stylable actors.
+ */
+struct _NbtkWidgetClass
+{
+  /*< private >*/
+  ClutterActorClass parent_class;
+
+  /* vfuncs */
+  void (* draw_background) (NbtkWidget         *self,
+                            ClutterActor       *background,
+                            const ClutterColor *color);
+};
+
+GType nbtk_widget_get_type (void) G_GNUC_CONST;
+
+void                  nbtk_widget_set_style_pseudo_class (NbtkWidget   *actor,
+                                                          const gchar  *pseudo_class);
+G_CONST_RETURN gchar *nbtk_widget_get_style_pseudo_class (NbtkWidget   *actor);
+void                  nbtk_widget_set_style_class_name   (NbtkWidget   *actor,
+                                                          const gchar  *style_class);
+G_CONST_RETURN gchar *nbtk_widget_get_style_class_name   (NbtkWidget   *actor);
+
+
+void     nbtk_widget_set_has_tooltip (NbtkWidget *widget, gboolean has_tooltip);
+gboolean nbtk_widget_get_has_tooltip (NbtkWidget *widget);
+void     nbtk_widget_set_tooltip_text (NbtkWidget *widget, const gchar *text);
+const gchar* nbtk_widget_get_tooltip_text (NbtkWidget *widget);
+
+void nbtk_widget_show_tooltip (NbtkWidget *widget);
+void nbtk_widget_hide_tooltip (NbtkWidget *widget);
+
+void nbtk_widget_ensure_style (NbtkWidget *widget);
+
+
+/* Only to be used by sub-classes of NbtkWidget */
+ClutterActor *nbtk_widget_get_background_image (NbtkWidget  *actor);
+ClutterActor *nbtk_widget_get_border_image     (NbtkWidget  *actor);
+void          nbtk_widget_get_padding          (NbtkWidget  *widget,
+                                                NbtkPadding *padding);
+void          nbtk_widget_draw_background      (NbtkWidget  *widget);
+
+G_END_DECLS
+
+#endif /* __NBTK_WIDGET_H__ */



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