gnome-shell r103 - in trunk: . src src/big



Author: walters
Date: Mon Dec  1 23:01:52 2008
New Revision: 103
URL: http://svn.gnome.org/viewvc/gnome-shell?rev=103&view=rev

Log:
Import Big

We want this for the BigBox primarily; rounded corners, etc.

http://bugzilla.gnome.org/show_bug.cgi?id=562923

Added:
   trunk/src/Makefile-big.am
   trunk/src/big/
   trunk/src/big/big-enum-types.c.in
   trunk/src/big/big-enum-types.h.in
   trunk/src/big/box.c
   trunk/src/big/box.h
   trunk/src/big/rectangle.c
   trunk/src/big/rectangle.h
   trunk/src/big/theme-image.c
   trunk/src/big/theme-image.h
Modified:
   trunk/configure.ac
   trunk/src/Makefile-tidy.am
   trunk/src/Makefile.am

Modified: trunk/configure.ac
==============================================================================
--- trunk/configure.ac	(original)
+++ trunk/configure.ac	Mon Dec  1 23:01:52 2008
@@ -18,6 +18,7 @@
 
 PKG_CHECK_MODULES(MUTTER_PLUGIN, gtk+-2.0 metacity-plugins gjs-gi-1.0)
 PKG_CHECK_MODULES(TIDY, clutter-0.8)
+PKG_CHECK_MODULES(BIG, clutter-cairo-0.8 gtk+-2.0 librsvg-2.0)
 PKG_CHECK_MODULES(TRAY, gtk+-2.0)
 
 # Sets GLIB_GENMARSHAL and GLIB_MKENUMS

Added: trunk/src/Makefile-big.am
==============================================================================
--- (empty file)
+++ trunk/src/Makefile-big.am	Mon Dec  1 23:01:52 2008
@@ -0,0 +1,67 @@
+big_cflags =					\
+	-I$(top_srcdir)/src			\
+	-DPREFIX=\""$(prefix)"\"		\
+	-DLIBDIR=\""$(libdir)"\"		\
+	-DG_DISABLE_DEPRECATED			\
+	-DG_LOG_DOMAIN=\"Big\"			\
+	$(BIG_CFLAGS)			\
+	$(NULL)
+
+big_built_sources = \
+	big-enum-types.h 	\
+	big-enum-types.c 	\
+	$(NULL)
+
+BUILT_SOURCES += $(big_built_sources)
+
+BIG_STAMP_FILES = stamp-big-marshal.h stamp-big-enum-types.h
+
+# please, keep this sorted alphabetically
+big_source_h =					\
+	big/box.h				\
+	big/rectangle.h			\
+	big/theme-image.h		\
+	$(NULL)
+
+# please, keep this sorted alphabetically
+big_source_c =					\
+	big/box.c			\
+	big/rectangle.c			\
+	big/theme-image.c			\
+	$(NULL)
+
+big-enum-types.h: stamp-big-enum-types.h Makefile
+	@true
+stamp-big-enum-types.h: $(big_source_h) big/big-enum-types.h.in
+	( cd $(srcdir) && \
+	  $(GLIB_MKENUMS) \
+	    --template $(srcdir)/big/big-enum-types.h.in \
+	  $(big_source_h) ) >> xgen-teth && \
+	(cmp xgen-teth big-enum-types.h || cp xgen-teth big-enum-types.h) && \
+	rm -f xgen-teth && \
+	echo timestamp > $(@F)
+
+big-enum-types.c: stamp-big-enum-types.h big/big-enum-types.c.in
+	( cd $(srcdir) && \
+	  $(GLIB_MKENUMS) \
+	    --template $(srcdir)/big/big-enum-types.c.in \
+	  $(big_source_h) ) >> xgen-tetc && \
+	cp xgen-tetc big-enum-types.c && \
+	rm -f xgen-tetc
+
+noinst_LTLIBRARIES += libbig-1.0.la
+
+libbig_1_0_la_LIBADD = $(BIG_LIBS)
+libbig_1_0_la_SOURCES = \
+	$(big_source_c) \
+	$(big_source_h) \
+	$(big_built_sources) \
+	$(NULL)
+libbig_1_0_la_CPPFLAGS = $(big_cflags)
+libbig_1_0_la_LDFLAGS = $(LDADD)
+
+CLEANFILES += $(BIG_STAMP_FILES) $(BUILT_SOURCES)
+
+EXTRA_DIST +=					\
+	big/big-enum-types.h.in		\
+	big/big-enum-types.c.in

Modified: trunk/src/Makefile-tidy.am
==============================================================================
--- trunk/src/Makefile-tidy.am	(original)
+++ trunk/src/Makefile-tidy.am	Mon Dec  1 23:01:52 2008
@@ -1,5 +1,3 @@
-NULL =
-
 tidy_cflags =					\
 	-I$(top_srcdir)/src			\
 	-DPREFIX=\""$(prefix)"\"		\
@@ -85,7 +83,7 @@
 	cp xgen-tetc tidy-enum-types.c && \
 	rm -f xgen-tetc
 
-noinst_LTLIBRARIES = libtidy-1.0.la
+noinst_LTLIBRARIES += libtidy-1.0.la
 
 libtidy_1_0_la_LIBADD = $(TIDY_LIBS)
 libtidy_1_0_la_SOURCES = \
@@ -99,7 +97,7 @@
 
 CLEANFILES += $(TIDY_STAMP_FILES) $(BUILT_SOURCES)
 
-EXTRA_DIST =					\
+EXTRA_DIST +=					\
 	tidy/tidy-enum-types.h.in		\
 	tidy/tidy-enum-types.c.in		\
 	tidy/tidy-private.h			\

Modified: trunk/src/Makefile.am
==============================================================================
--- trunk/src/Makefile.am	(original)
+++ trunk/src/Makefile.am	Mon Dec  1 23:01:52 2008
@@ -1,7 +1,11 @@
+NULL = 
 BUILT_SOURCES =
 CLEANFILES =
+EXTRA_DIST = 
+noinst_LTLIBRARIES = 
 
 include Makefile-tidy.am
+include Makefile-big.am
 include Makefile-tray.am
 
 gnome_shell_cflags =				\
@@ -60,12 +64,13 @@
 libgnome_shell_la_LDFLAGS = -avoid-version -module
 libgnome_shell_la_LIBADD =	\
 	$(MUTTER_PLUGIN_LIBS)	\
+	libbig-1.0.la		\
 	libtidy-1.0.la		\
 	libtray.la
 libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
 
 typelibdir = $(pkglibdir)
-typelib_DATA = Shell-0.1.typelib Tidy-1.0.typelib
+typelib_DATA = Shell-0.1.typelib Tidy-1.0.typelib Big-1.0.typelib
 
 Shell-0.1.gir: $(metacity) $(G_IR_SCANNER) libgnome-shell.la Makefile
 	$(G_IR_SCANNER)		\
@@ -103,3 +108,20 @@
 Tidy-1.0.typelib: libtidy-1.0.la Tidy-1.0.gir
 	LD_LIBRARY_PATH=$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}. g-ir-compiler Tidy-1.0.gir -o $@
 CLEANFILES += Tidy-1.0.typelib
+
+Big-1.0.gir: $(metacity) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la Makefile
+	$(G_IR_SCANNER)					\
+		--namespace=Big			\
+		--nsversion=1.0				\
+		--include=ClutterCairo-0.8			\
+		--program=metacity			\
+	        --program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
+		$(big_source_h)			\
+		$(big_source_c)			\
+		$(big_cflags)				\
+		-o $@
+CLEANFILES += Big-1.0.gir
+
+Big-1.0.typelib: libbig-1.0.la Big-1.0.gir
+	LD_LIBRARY_PATH=$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}. g-ir-compiler Big-1.0.gir -o $@
+CLEANFILES += Big-1.0.typelib

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

Added: trunk/src/big/big-enum-types.h.in
==============================================================================
--- (empty file)
+++ trunk/src/big/big-enum-types.h.in	Mon Dec  1 23:01:52 2008
@@ -0,0 +1,25 @@
+/*** BEGIN file-header ***/
+#ifndef __BIG_ENUM_TYPES_H__
+#define __BIG_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 /* !__BIG_ENUM_TYPES_H__ */
+/*** END file-tail ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name _get_type (void) G_GNUC_CONST;
+#define BIG_TYPE_ ENUMSHORT@ (@enum_name _get_type())
+
+/*** END value-header ***/

Added: trunk/src/big/box.c
==============================================================================
--- (empty file)
+++ trunk/src/big/box.c	Mon Dec  1 23:01:52 2008
@@ -0,0 +1,3050 @@
+/* -*- mode: C; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
+/* big-box.c: Box container.
+
+   Copyright (C) 2006-2008 Red Hat, Inc.
+   Copyright (C) 2008 litl, LLC.
+
+   The libbigwidgets-lgpl is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The libbigwidgets-lgpl is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the libbigwidgets-lgpl; see the file COPYING.LIB.
+   If not, write to the Free Software Foundation, Inc., 59 Temple Place -
+   Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <glib.h>
+
+#include <clutter/clutter-units.h>
+#include <clutter/clutter-actor.h>
+#include <clutter/clutter-container.h>
+#include <cogl/cogl.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "big-enum-types.h"
+
+#include "box.h"
+#include "theme-image.h"
+#include "rectangle.h"
+
+static void clutter_container_iface_init (ClutterContainerIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (BigBox,
+                         big_box,
+                         CLUTTER_TYPE_ACTOR,
+                         G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
+                                                clutter_container_iface_init));
+
+#define BIG_BOX_GET_PRIVATE(obj)    \
+(G_TYPE_INSTANCE_GET_PRIVATE ((obj), BIG_TYPE_BOX, BigBoxPrivate))
+
+#define BOX_CHILD_IS_VISIBLE(c)    \
+(CLUTTER_ACTOR_IS_VISIBLE (c->actor))
+
+#define BOX_CHILD_IN_LAYOUT(c)    \
+(!c->fixed && (BOX_CHILD_IS_VISIBLE (c) || c->if_hidden))
+
+enum
+{
+  PROP_0,
+
+  PROP_ORIENTATION,
+  PROP_SPACING,
+  PROP_X_ALIGN,
+  PROP_Y_ALIGN,
+  PROP_PADDING,
+  PROP_PADDING_TOP,
+  PROP_PADDING_BOTTOM,
+  PROP_PADDING_LEFT,
+  PROP_PADDING_RIGHT,
+  PROP_BORDER,
+  PROP_BORDER_TOP,
+  PROP_BORDER_BOTTOM,
+  PROP_BORDER_LEFT,
+  PROP_BORDER_RIGHT,
+  PROP_CORNER_RADIUS,
+  PROP_BACKGROUND_BORDER_TOP,
+  PROP_BACKGROUND_BORDER_BOTTOM,
+  PROP_BACKGROUND_BORDER_LEFT,
+  PROP_BACKGROUND_BORDER_RIGHT,
+  PROP_BACKGROUND_COLOR,
+  PROP_BACKGROUND_FILENAME,
+  PROP_BACKGROUND_PIXBUF,
+  PROP_BACKGROUND_TEXTURE,
+  PROP_BACKGROUND_RECTANGLE,
+  PROP_BACKGROUND_REPEAT,
+  PROP_BACKGROUND_X_ALIGN,
+  PROP_BACKGROUND_Y_ALIGN,
+  PROP_BORDER_COLOR,
+  PROP_DEBUG
+};
+
+struct _BigBoxPrivate
+{
+  GList                   *children;
+  BigBoxOrientation        orientation;
+  BigBoxAlignment          x_align;
+  BigBoxAlignment          y_align;
+  ClutterUnit              spacing;
+  ClutterUnit              padding_top;
+  ClutterUnit              padding_bottom;
+  ClutterUnit              padding_left;
+  ClutterUnit              padding_right;
+  ClutterUnit              border_top;
+  ClutterUnit              border_bottom;
+  ClutterUnit              border_left;
+  ClutterUnit              border_right;
+  ClutterUnit              corner_radius;
+  ClutterColor             border_color;
+  guint                    background_border_top;
+  guint                    background_border_bottom;
+  guint                    background_border_left;
+  guint                    background_border_right;
+  ClutterColor             background_color;
+  ClutterActor            *background_texture;
+  BigBoxBackgroundRepeat   background_repeat;
+  BigBoxAlignment          background_x_align;
+  BigBoxAlignment          background_y_align;
+  ClutterActor            *background_rectangle;
+
+  guint                    draw_rounded_corner : 1;
+  guint                    debug               : 1;
+};
+
+typedef struct
+{
+  ClutterActor *actor;
+
+  guint         expand    : 1;
+  guint         end       : 1;
+  guint         if_fits   : 1;
+  guint         fixed     : 1;
+  guint         if_hidden : 1;
+
+  /* BigBoxAlignment applies if fixed=true */
+  guint         fixed_x_align : 3;
+  guint         fixed_y_align : 3;
+} BigBoxChild;
+
+typedef struct
+{
+  ClutterUnit  minimum;
+  ClutterUnit  natural;
+  ClutterUnit  adjustment;
+  guint        does_not_fit : 1;
+} BigBoxAdjustInfo;
+
+static gboolean
+box_child_set_flags (BigBoxChild     *c,
+                     BigBoxPackFlags  flags)
+{
+  BigBoxPackFlags old;
+
+  old = 0;
+
+  if (c->end)
+    old |= BIG_BOX_PACK_END;
+  if (c->expand)
+    old |= BIG_BOX_PACK_EXPAND;
+  if (c->if_fits)
+    old |= BIG_BOX_PACK_IF_FITS;
+  if (c->fixed)
+    old |= BIG_BOX_PACK_FIXED;
+  if (c->if_hidden)
+    old |= BIG_BOX_PACK_ALLOCATE_WHEN_HIDDEN;
+
+  if (old == flags)
+    return FALSE; /* no change */
+
+  c->expand    = (flags & BIG_BOX_PACK_EXPAND) != 0;
+  c->end       = (flags & BIG_BOX_PACK_END) != 0;
+  c->if_fits   = (flags & BIG_BOX_PACK_IF_FITS) != 0;
+  c->fixed     = (flags & BIG_BOX_PACK_FIXED) != 0;
+  c->if_hidden = (flags & BIG_BOX_PACK_ALLOCATE_WHEN_HIDDEN) != 0;
+
+  return TRUE;
+}
+
+static gboolean
+box_child_set_align (BigBoxChild     *c,
+                     BigBoxAlignment  fixed_x_align,
+                     BigBoxAlignment  fixed_y_align)
+{
+  if (fixed_x_align == c->fixed_x_align &&
+      fixed_y_align == c->fixed_y_align)
+    return FALSE;
+
+  c->fixed_x_align = fixed_x_align;
+  c->fixed_y_align = fixed_y_align;
+
+  return TRUE;
+}
+
+static BigBoxChild *
+box_child_find (BigBox       *box,
+                ClutterActor *actor)
+{
+  GList *c;
+
+  for (c = box->priv->children; c != NULL; c = c->next)
+    {
+      BigBoxChild *child = c->data;
+
+      if (child->actor == actor)
+        return (BigBoxChild *) child;
+    }
+
+  return NULL;
+}
+
+static BigBoxChild *
+box_child_new_from_actor (ClutterActor    *child,
+                          BigBoxPackFlags  flags)
+{
+  BigBoxChild *c;
+
+  c = g_new0 (BigBoxChild, 1);
+
+  g_object_ref (child);
+  c->actor = child;
+
+  box_child_set_flags (c, flags);
+
+  return c;
+}
+
+static void
+box_child_free (BigBoxChild *c)
+{
+  g_object_unref (c->actor);
+
+  g_free (c);
+}
+
+static void
+box_child_remove (BigBox *box, BigBoxChild *child)
+{
+  BigBoxPrivate *priv = box->priv;
+
+  priv->children = g_list_remove (priv->children, child);
+
+  clutter_actor_unparent (child->actor);
+
+  /* at this point, the actor passed to the "actor-removed" signal
+   * handlers is not parented anymore to the container but since we
+   * are holding a reference on it, it's still valid
+   */
+  g_signal_emit_by_name (box, "actor-removed", child->actor);
+
+  box_child_free (child);
+}
+
+static void
+big_box_real_add (ClutterContainer *container,
+                  ClutterActor     *child)
+{
+  big_box_append (BIG_BOX (container), child, BIG_BOX_PACK_FIXED);
+}
+
+static void
+big_box_real_remove (ClutterContainer *container,
+                     ClutterActor     *child)
+{
+  BigBox *box = BIG_BOX (container);
+  BigBoxChild *c;
+
+  g_object_ref (child);
+
+  c = box_child_find (box, child);
+
+  if (c != NULL)
+    {
+      box_child_remove (box, c);
+
+      clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+    }
+
+  g_object_unref (child);
+}
+
+static void
+big_box_real_foreach (ClutterContainer *container,
+                      ClutterCallback   callback,
+                      gpointer          user_data)
+{
+  BigBox *group = BIG_BOX (container);
+  BigBoxPrivate *priv = group->priv;
+  GList *l;
+
+  for (l = priv->children; l; l = l->next)
+   {
+     BigBoxChild *c = (BigBoxChild *) l->data;
+
+     (* callback) (c->actor, user_data);
+   }
+}
+
+static void
+big_box_real_raise (ClutterContainer *container,
+                    ClutterActor     *child,
+                    ClutterActor     *sibling)
+{
+  BigBox *box = BIG_BOX (container);
+  BigBoxPrivate *priv = box->priv;
+  BigBoxChild *c;
+
+  c = box_child_find (box, child);
+
+  /* Child not found */
+  if (c == NULL)
+    return;
+
+  /* We only do raise for children not in the layout */
+  if (!c->fixed)
+    return;
+
+  priv->children = g_list_remove (priv->children, c);
+
+  /* Raise at the top */
+  if (!sibling)
+    {
+      GList *last_item;
+
+      last_item = g_list_last (priv->children);
+
+      if (last_item)
+        {
+          BigBoxChild *sibling_child = last_item->data;
+
+          sibling = sibling_child->actor;
+        }
+
+      priv->children = g_list_append (priv->children, c);
+    }
+  else
+    {
+      BigBoxChild *sibling_child;
+      gint pos;
+
+      sibling_child = box_child_find (box, sibling);
+
+      pos = g_list_index (priv->children, sibling_child) + 1;
+
+      priv->children = g_list_insert (priv->children, c, pos);
+    }
+
+  if (sibling &&
+      clutter_actor_get_depth (sibling) != clutter_actor_get_depth (child))
+    {
+      clutter_actor_set_depth (child, clutter_actor_get_depth (sibling));
+    }
+}
+
+static void
+big_box_real_lower (ClutterContainer *container,
+                    ClutterActor     *child,
+                    ClutterActor     *sibling)
+{
+  BigBox *box = BIG_BOX (container);
+  BigBoxPrivate *priv = box->priv;
+  BigBoxChild *c;
+
+  c = box_child_find (box, child);
+
+  /* Child not found */
+  if (c == NULL)
+    return;
+
+  /* We only do lower for children not in the layout */
+  if (!c->fixed)
+    return;
+
+  priv->children = g_list_remove (priv->children, c);
+
+  /* Push to bottom */
+  if (!sibling)
+    {
+      GList *first_item;
+
+      first_item = g_list_first (priv->children);
+
+      if (first_item)
+        {
+          BigBoxChild *sibling_child = first_item->data;
+
+          sibling = sibling_child->actor;
+        }
+
+      priv->children = g_list_prepend (priv->children, c);
+    }
+  else
+    {
+      BigBoxChild *sibling_child;
+      gint pos;
+
+      sibling_child = box_child_find (box, sibling);
+
+      pos = g_list_index (priv->children, sibling_child);
+
+      priv->children = g_list_insert (priv->children, c, pos);
+    }
+
+  if (sibling &&
+      clutter_actor_get_depth (sibling) != clutter_actor_get_depth (child))
+    {
+      clutter_actor_set_depth (child, clutter_actor_get_depth (sibling));
+    }
+}
+
+static int
+sort_z_order (gconstpointer a,
+              gconstpointer b)
+{
+  const BigBoxChild *child_a = a;
+  const BigBoxChild *child_b = b;
+  int depth_a, depth_b;
+
+  /* Depth of non-fixed children is ignored in stacking/painting order and they
+   * are considered to be at depth=0. Though when the children are painting
+   * themselves the depth translation is applied.
+   */
+
+  depth_a = child_a->fixed ? clutter_actor_get_depth (child_a->actor) : 0;
+  depth_b = child_b->fixed ? clutter_actor_get_depth (child_b->actor) : 0;
+
+  return depth_a - depth_b;
+}
+
+static void
+big_box_real_sort_depth_order (ClutterContainer *container)
+{
+  BigBox *box = BIG_BOX (container);
+  BigBoxPrivate *priv = box->priv;
+
+  priv->children = g_list_sort (priv->children, sort_z_order);
+
+  if (CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (box)))
+    clutter_actor_queue_redraw (CLUTTER_ACTOR (box));
+}
+
+static void
+clutter_container_iface_init (ClutterContainerIface *iface)
+{
+  iface->add = big_box_real_add;
+  iface->remove = big_box_real_remove;
+  iface->foreach = big_box_real_foreach;
+  iface->raise = big_box_real_raise;
+  iface->lower = big_box_real_lower;
+  iface->sort_depth_order = big_box_real_sort_depth_order;
+}
+
+static void
+big_box_get_bg_texture_allocation (ClutterActor          *self,
+                                   ClutterUnit            allocated_width,
+                                   ClutterUnit            allocated_height,
+                                   ClutterActorBox       *bg_box)
+{
+  BigBoxPrivate *priv;
+  ClutterUnit bg_width, bg_height;
+  ClutterUnit min_x1, min_y1, max_x2, max_y2;
+
+  g_return_if_fail (BIG_IS_BOX (self));
+
+  priv = BIG_BOX (self)->priv;
+
+  clutter_actor_get_preferred_width (priv->background_texture,
+                                     -1,
+                                     NULL,
+                                     &bg_width);
+  clutter_actor_get_preferred_height (priv->background_texture,
+                                      bg_width,
+                                      NULL,
+                                      &bg_height);
+
+  min_x1 = priv->border_left;
+  max_x2 = allocated_width - priv->border_right;
+
+  switch (priv->background_x_align)
+    {
+    case BIG_BOX_ALIGNMENT_FIXED:
+      g_warning("Must specify a real alignment for background, not FIXED");
+      break;
+    case BIG_BOX_ALIGNMENT_FILL:
+      bg_box->x1 = min_x1;
+      bg_box->x2 = max_x2;
+      break;
+
+    case BIG_BOX_ALIGNMENT_START:
+      bg_box->x1 = min_x1;
+      bg_box->x2 = MIN (bg_box->x1 + bg_width, max_x2);
+      break;
+
+    case BIG_BOX_ALIGNMENT_END:
+      bg_box->x1 = MAX (min_x1, max_x2 - bg_width);
+      bg_box->x2 = max_x2;
+      break;
+
+    case BIG_BOX_ALIGNMENT_CENTER:
+      bg_box->x1 = MAX (min_x1, priv->border_left + (allocated_width - bg_width) / 2);
+      bg_box->x2 = MIN (bg_box->x1 + bg_width, max_x2);
+      break;
+    }
+
+  min_y1 = priv->border_top;
+  max_y2 = allocated_height - priv->border_bottom;
+
+  switch (priv->background_y_align)
+    {
+    case BIG_BOX_ALIGNMENT_FIXED:
+      g_warning("Must specify a real alignment for background, not FIXED");
+      break;
+    case BIG_BOX_ALIGNMENT_FILL:
+      bg_box->y1 = min_y1;
+      bg_box->y2 = max_y2;
+      break;
+
+    case BIG_BOX_ALIGNMENT_START:
+      bg_box->y1 = min_y1;
+      bg_box->y2 = MIN (bg_box->y1 + bg_height, max_y2);
+      break;
+
+    case BIG_BOX_ALIGNMENT_END:
+      bg_box->y1 = MAX (min_y1, max_y2 - bg_height);
+      bg_box->y2 = max_y2;
+      break;
+
+    case BIG_BOX_ALIGNMENT_CENTER:
+      bg_box->y1 = MAX (min_y1, priv->border_top + (allocated_height - bg_height) / 2);
+      bg_box->y2 = MIN (bg_box->y1 + bg_height, max_y2);
+      break;
+    }
+}
+
+static void
+big_box_update_background_border (BigBox *box)
+{
+  BigBoxPrivate *priv;
+
+  g_return_if_fail (BIG_IS_BOX (box));
+
+  priv = box->priv;
+
+  if (priv->background_texture)
+    {
+      guint border_top = 0, border_bottom = 0, border_left = 0, border_right = 0;
+
+      if ((priv->background_x_align == BIG_BOX_ALIGNMENT_FILL) &&
+          /* No repeat on the horizontal axis */
+         ((priv->background_repeat == BIG_BOX_BACKGROUND_REPEAT_NONE) ||
+          (priv->background_repeat == BIG_BOX_BACKGROUND_REPEAT_Y)))
+        {
+          border_left = priv->background_border_left;
+          border_right = priv->background_border_right;
+        }
+
+      if ((priv->background_y_align == BIG_BOX_ALIGNMENT_FILL) &&
+          /* No repeat on the vertical axis */
+         ((priv->background_repeat == BIG_BOX_BACKGROUND_REPEAT_NONE) ||
+          (priv->background_repeat == BIG_BOX_BACKGROUND_REPEAT_X)))
+        {
+          border_top = priv->background_border_top;
+          border_bottom = priv->background_border_bottom;
+        }
+
+      g_object_set (priv->background_texture,
+                    "border-left", border_left,
+                    "border-right", border_right,
+                    "border-top", border_top,
+                    "border-bottom", border_bottom,
+                    NULL);
+    }
+}
+
+/* Update whether or not we draw rounded corners.
+ * If different sizes are set for different border segments,
+ * we ignore the radius.
+ */
+static void
+big_box_update_draw_rounded_corner (BigBox *box)
+{
+  BigBoxPrivate *priv;
+
+  priv = box->priv;
+
+  priv->draw_rounded_corner = (priv->border_top == priv->border_left) &&
+                              (priv->border_top == priv->border_right) &&
+                              (priv->border_top == priv->border_bottom) &&
+                              (priv->corner_radius != 0);
+
+  if (!priv->draw_rounded_corner &&
+      priv->background_rectangle)
+    {
+      clutter_actor_unparent(priv->background_rectangle);
+      priv->background_rectangle = NULL;
+    }
+
+  if (priv->draw_rounded_corner &&
+      !priv->background_rectangle)
+    {
+      priv->background_rectangle = g_object_new (BIG_TYPE_RECTANGLE, NULL);
+      clutter_actor_set_parent (priv->background_rectangle, CLUTTER_ACTOR (box));
+      clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+    }
+
+  if (priv->draw_rounded_corner)
+    {
+      g_object_set (priv->background_rectangle,
+                    "color", &priv->background_color,
+                    "border-color", &priv->border_color,
+                    "border-width", CLUTTER_UNITS_TO_DEVICE (priv->border_top),
+                    "corner-radius", CLUTTER_UNITS_TO_DEVICE (priv->corner_radius),
+                    NULL);
+    }
+}
+
+static void
+big_box_set_background_repeat (BigBox *box,
+                               BigBoxBackgroundRepeat background_repeat)
+{
+  BigBoxPrivate *priv;
+
+  priv = box->priv;
+
+  priv->background_repeat = background_repeat;
+
+  if (priv->background_texture)
+    {
+      gboolean repeat_x = FALSE, repeat_y = FALSE;
+
+      switch (priv->background_repeat)
+        {
+        case BIG_BOX_BACKGROUND_REPEAT_NONE:
+          break;
+
+        case BIG_BOX_BACKGROUND_REPEAT_X:
+          repeat_x = TRUE;
+          break;
+
+        case BIG_BOX_BACKGROUND_REPEAT_Y:
+          repeat_y = TRUE;
+          break;
+
+        case BIG_BOX_BACKGROUND_REPEAT_BOTH:
+          repeat_x = TRUE;
+          repeat_y = TRUE;
+          break;
+        }
+
+      g_object_set (G_OBJECT (priv->background_texture),
+                    "repeat-x", repeat_x,
+                    "repeat-y", repeat_y,
+                    NULL);
+    }
+}
+
+static void
+big_box_set_background_pixbuf (BigBox *box, GdkPixbuf *pixbuf)
+{
+  BigBoxPrivate *priv;
+  ClutterActor *background_texture;
+
+  g_return_if_fail (BIG_IS_BOX (box));
+
+  priv = box->priv;
+
+  if (priv->background_texture)
+    {
+      clutter_actor_unparent (priv->background_texture);
+      priv->background_texture = NULL;
+    }
+
+  /* This means we just want to remove current background texture  */
+  if (pixbuf == NULL)
+    return;
+
+  /* The border values are set depending on the background-repeat
+   * and background alignment settings in
+   * big_box_update_background_border */
+  background_texture = big_theme_image_new_from_pixbuf (pixbuf,
+                                                        0, 0, 0, 0);
+
+  if (background_texture)
+    {
+      clutter_actor_set_parent (background_texture, CLUTTER_ACTOR (box));
+      priv->background_texture = background_texture;
+      big_box_set_background_repeat (box, priv->background_repeat);
+    }
+}
+
+static void
+big_box_set_background_filename (BigBox *box, const char *filename)
+{
+  BigBoxPrivate *priv;
+  ClutterActor *background_texture;
+
+  g_return_if_fail (BIG_IS_BOX (box));
+  g_return_if_fail (filename != NULL);
+
+  priv = box->priv;
+
+  if (priv->background_texture)
+    {
+      clutter_actor_unparent (priv->background_texture);
+      priv->background_texture = NULL;
+    }
+
+  /* This means we just want to remove current background texture  */
+  if (filename == NULL)
+    return;
+
+  /* The border values are set depending on the background-repeat
+   * and background alignment settings in
+   * big_box_update_background_border */
+  background_texture = big_theme_image_new_from_file (filename,
+                                                      0, 0, 0, 0);
+
+  if (background_texture)
+    {
+      clutter_actor_set_parent (background_texture, CLUTTER_ACTOR (box));
+      priv->background_texture = background_texture;
+      big_box_set_background_repeat (box, priv->background_repeat);
+    }
+}
+
+static void
+big_box_set_property (GObject      *gobject,
+                       guint         prop_id,
+                       const GValue *value,
+                       GParamSpec   *pspec)
+{
+  BigBoxPrivate *priv = BIG_BOX (gobject)->priv;
+  gboolean need_repaint = TRUE;
+  gboolean need_resize = TRUE;
+
+  switch (prop_id)
+    {
+    case PROP_ORIENTATION:
+      priv->orientation = g_value_get_enum (value);
+      break;
+
+    case PROP_SPACING:
+      priv->spacing = CLUTTER_UNITS_FROM_DEVICE (g_value_get_int (value));
+      break;
+
+    case PROP_X_ALIGN:
+      priv->x_align = g_value_get_enum (value);
+      break;
+
+    case PROP_Y_ALIGN:
+      priv->y_align = g_value_get_enum (value);
+      break;
+
+    case PROP_PADDING:
+      big_box_set_padding(BIG_BOX(gobject),
+                          g_value_get_int(value));
+      break;
+
+    case PROP_PADDING_TOP:
+      priv->padding_top = CLUTTER_UNITS_FROM_DEVICE (g_value_get_int (value));
+      break;
+
+    case PROP_PADDING_BOTTOM:
+      priv->padding_bottom = CLUTTER_UNITS_FROM_DEVICE (g_value_get_int (value));
+      break;
+
+    case PROP_PADDING_LEFT:
+      priv->padding_left = CLUTTER_UNITS_FROM_DEVICE(g_value_get_int (value));
+      break;
+
+    case PROP_PADDING_RIGHT:
+      priv->padding_right = CLUTTER_UNITS_FROM_DEVICE(g_value_get_int (value));
+      break;
+
+    case PROP_BORDER:
+      big_box_set_border_width(BIG_BOX(gobject),
+                               g_value_get_int(value));
+      break;
+
+    case PROP_BORDER_TOP:
+      priv->border_top = CLUTTER_UNITS_FROM_DEVICE(g_value_get_int (value));
+      big_box_update_draw_rounded_corner (BIG_BOX (gobject));
+      break;
+
+    case PROP_BORDER_BOTTOM:
+      priv->border_bottom = CLUTTER_UNITS_FROM_DEVICE(g_value_get_int (value));
+      big_box_update_draw_rounded_corner (BIG_BOX (gobject));
+      break;
+
+    case PROP_BORDER_LEFT:
+      priv->border_left = CLUTTER_UNITS_FROM_DEVICE(g_value_get_int (value));
+      big_box_update_draw_rounded_corner (BIG_BOX (gobject));
+      break;
+
+    case PROP_BORDER_RIGHT:
+      priv->border_right = CLUTTER_UNITS_FROM_DEVICE(g_value_get_int (value));
+      big_box_update_draw_rounded_corner (BIG_BOX (gobject));
+      break;
+
+    case PROP_CORNER_RADIUS:
+      priv->corner_radius = CLUTTER_UNITS_FROM_DEVICE(g_value_get_uint (value));
+      big_box_update_draw_rounded_corner (BIG_BOX (gobject));
+      break;
+
+    case PROP_BACKGROUND_BORDER_TOP:
+      priv->background_border_top = g_value_get_uint (value);
+      need_resize = FALSE;
+      big_box_update_background_border (BIG_BOX (gobject));
+      break;
+
+    case PROP_BACKGROUND_BORDER_BOTTOM:
+      priv->background_border_bottom = g_value_get_uint (value);
+      need_resize = FALSE;
+      big_box_update_background_border (BIG_BOX (gobject));
+      break;
+
+    case PROP_BACKGROUND_BORDER_LEFT:
+      priv->background_border_left = g_value_get_uint (value);
+      need_resize = FALSE;
+      big_box_update_background_border (BIG_BOX (gobject));
+      break;
+
+    case PROP_BACKGROUND_BORDER_RIGHT:
+      priv->background_border_right = g_value_get_uint (value);
+      need_resize = FALSE;
+      big_box_update_background_border (BIG_BOX (gobject));
+      break;
+
+    case PROP_BACKGROUND_COLOR:
+      {
+        ClutterColor *color;
+        color = g_value_get_boxed (value);
+        if (color) {
+          priv->background_color = *color;
+        } else {
+          /* null = default (black and transparent) */
+          priv->background_color.red = 0;
+          priv->background_color.green = 0;
+          priv->background_color.blue = 0;
+          priv->background_color.alpha = 0;
+        }
+
+        if (priv->background_rectangle)
+          {
+            g_object_set (priv->background_rectangle,
+                          "color", &priv->background_color,
+                          NULL);
+          }
+
+        need_resize = FALSE;
+      }
+      break;
+
+    case PROP_BACKGROUND_FILENAME:
+      big_box_set_background_filename (BIG_BOX (gobject),
+                                       g_value_get_string (value));
+      big_box_update_background_border (BIG_BOX (gobject));
+      break;
+
+    case PROP_BACKGROUND_PIXBUF:
+      big_box_set_background_pixbuf (BIG_BOX (gobject),
+                                     g_value_get_object (value));
+      big_box_update_background_border (BIG_BOX (gobject));
+      break;
+
+    case PROP_BACKGROUND_REPEAT:
+      big_box_set_background_repeat (BIG_BOX (gobject),
+                                     g_value_get_enum (value));
+      big_box_update_background_border (BIG_BOX (gobject));
+      break;
+
+    case PROP_BACKGROUND_X_ALIGN:
+      priv->background_x_align = g_value_get_enum (value);
+      big_box_update_background_border (BIG_BOX (gobject));
+      break;
+
+    case PROP_BACKGROUND_Y_ALIGN:
+      priv->background_y_align = g_value_get_enum (value);
+      big_box_update_background_border (BIG_BOX (gobject));
+      break;
+
+    case PROP_BORDER_COLOR:
+      {
+        ClutterColor *color;
+        color = g_value_get_boxed (value);
+        if (color) {
+          priv->border_color = *color;
+        } else {
+          /* null = default (black and transparent) */
+          priv->border_color.red = 0;
+          priv->border_color.green = 0;
+          priv->border_color.blue = 0;
+          priv->border_color.alpha = 0;
+        }
+
+        if (priv->background_rectangle)
+          {
+            g_object_set (priv->background_rectangle,
+                          "border-color", &priv->border_color,
+                          NULL);
+          }
+
+        need_resize = FALSE;
+      }
+      break;
+
+    case PROP_DEBUG:
+      priv->debug = g_value_get_boolean (value);
+
+      need_repaint = FALSE;
+      need_resize = FALSE;
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+
+  if (need_resize)
+    clutter_actor_queue_relayout (CLUTTER_ACTOR (gobject));
+  else if (need_repaint)
+    clutter_actor_queue_redraw (CLUTTER_ACTOR (gobject));
+}
+
+static void
+big_box_get_property (GObject    *gobject,
+                       guint       prop_id,
+                       GValue     *value,
+                       GParamSpec *pspec)
+{
+  BigBoxPrivate *priv = BIG_BOX (gobject)->priv;
+
+  switch (prop_id)
+    {
+    case PROP_ORIENTATION:
+      g_value_set_enum (value, priv->orientation);
+      break;
+
+    case PROP_SPACING:
+      g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE(priv->spacing));
+      break;
+
+    case PROP_X_ALIGN:
+      g_value_set_enum (value, priv->x_align);
+      break;
+
+    case PROP_Y_ALIGN:
+      g_value_set_enum (value, priv->y_align);
+      break;
+
+    case PROP_PADDING_TOP:
+      g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE(priv->padding_top));
+      break;
+
+    case PROP_PADDING_BOTTOM:
+      g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE(priv->padding_bottom));
+      break;
+
+    case PROP_PADDING_LEFT:
+      g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE(priv->padding_left));
+      break;
+
+    case PROP_PADDING_RIGHT:
+      g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE(priv->padding_right));
+      break;
+
+    case PROP_BORDER_TOP:
+      g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE(priv->border_top));
+      break;
+
+    case PROP_BORDER_BOTTOM:
+      g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE(priv->border_bottom));
+      break;
+
+    case PROP_BORDER_LEFT:
+      g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE(priv->border_left));
+      break;
+
+    case PROP_BORDER_RIGHT:
+      g_value_set_int (value,CLUTTER_UNITS_TO_DEVICE(priv->border_right));
+      break;
+
+    case PROP_CORNER_RADIUS:
+      g_value_set_uint (value,CLUTTER_UNITS_TO_DEVICE (priv->corner_radius));
+      break;
+
+    case PROP_BACKGROUND_TEXTURE:
+      g_value_set_object (value, priv->background_texture);
+      break;
+
+    case PROP_BACKGROUND_RECTANGLE:
+      g_value_set_object (value, priv->background_rectangle);
+      break;
+
+    case PROP_BACKGROUND_BORDER_TOP:
+      g_value_set_uint (value, priv->background_border_top);
+      break;
+
+    case PROP_BACKGROUND_BORDER_BOTTOM:
+      g_value_set_uint (value, priv->background_border_bottom);
+      break;
+
+    case PROP_BACKGROUND_BORDER_LEFT:
+      g_value_set_uint (value, priv->background_border_left);
+      break;
+
+    case PROP_BACKGROUND_BORDER_RIGHT:
+      g_value_set_uint (value, priv->background_border_right);
+      break;
+
+    case PROP_BACKGROUND_COLOR:
+      g_value_set_boxed (value, &priv->background_color);
+      break;
+
+    case PROP_BACKGROUND_REPEAT:
+      g_value_set_enum (value, priv->background_repeat);
+      break;
+
+    case PROP_BACKGROUND_X_ALIGN:
+      g_value_set_enum (value, priv->background_x_align);
+      break;
+
+    case PROP_BACKGROUND_Y_ALIGN:
+      g_value_set_enum (value, priv->background_y_align);
+      break;
+
+    case PROP_BORDER_COLOR:
+      g_value_set_boxed (value, &priv->border_color);
+      break;
+
+    case PROP_DEBUG:
+      g_value_set_boolean (value, priv->debug);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+big_box_finalize (GObject *gobject)
+{
+  G_OBJECT_CLASS (big_box_parent_class)->finalize (gobject);
+}
+
+static void
+big_box_dispose (GObject *gobject)
+{
+  BigBox *self = BIG_BOX (gobject);
+  BigBoxPrivate *priv = self->priv;
+
+  if (priv->background_texture)
+    {
+      clutter_actor_unparent (priv->background_texture);
+      priv->background_texture = NULL;
+    }
+
+  if (priv->background_rectangle)
+    {
+      clutter_actor_unparent (priv->background_rectangle);
+      priv->background_rectangle = NULL;
+    }
+
+  while (priv->children)
+    {
+      clutter_actor_destroy(((BigBoxChild*)priv->children->data)->actor);
+    }
+
+  G_OBJECT_CLASS (big_box_parent_class)->dispose (gobject);
+}
+
+static void
+big_box_get_content_width_request (ClutterActor  *self,
+                                   ClutterUnit   *min_width_p,
+                                   ClutterUnit   *natural_width_p)
+{
+  BigBoxPrivate *priv;
+  ClutterUnit total_min;
+  ClutterUnit total_natural;
+  gint n_children_in_min;
+  gint n_children_in_natural;
+  GList *c;
+
+  priv = BIG_BOX (self)->priv;
+
+  total_min = 0;
+  total_natural = 0;
+  n_children_in_min = 0;
+  n_children_in_natural = 0;
+
+  for (c = priv->children; c != NULL; c = c->next)
+    {
+      BigBoxChild *child = (BigBoxChild *) c->data;
+      ClutterUnit min_width;
+      ClutterUnit natural_width;
+
+      if (!BOX_CHILD_IN_LAYOUT (child))
+        continue;
+
+      /* PACK_IF_FITS children are do not contribute to the min size
+       * of the whole box, but do contribute to natural size, and
+       * will be hidden entirely if their width request does not
+       * fit.
+       */
+      clutter_actor_get_preferred_width (child->actor,
+                                         -1,
+                                         &min_width,
+                                         &natural_width);
+
+      if (priv->debug)
+        g_debug ("Child %p min width %d natural %d",
+                 child->actor,
+                 CLUTTER_UNITS_TO_DEVICE (min_width),
+                 CLUTTER_UNITS_TO_DEVICE (natural_width));
+
+      n_children_in_natural += 1;
+
+      /* children with if fits flag won't appear at our min width if
+       * we are horizontal. If we're vertical, always request enough
+       * width for all if_fits children. Children with 0 min size won't
+       * themselves appear but they will get spacing around them, so
+       * they count in n_children_in_min.
+       */
+      if (priv->orientation == BIG_BOX_ORIENTATION_VERTICAL)
+        {
+          total_min = MAX (total_min, min_width);
+          n_children_in_min += 1;
+
+          total_natural = MAX (total_natural, natural_width);
+        }
+      else
+        {
+          if (!child->if_fits)
+            {
+              total_min += min_width;
+              n_children_in_min += 1;
+            }
+
+          total_natural += natural_width;
+        }
+    }
+
+  if (priv->orientation == BIG_BOX_ORIENTATION_HORIZONTAL &&
+      n_children_in_min > 1)
+    {
+      total_min += priv->spacing * (n_children_in_min - 1);
+    }
+
+  if (priv->orientation == BIG_BOX_ORIENTATION_HORIZONTAL &&
+      n_children_in_natural > 1)
+    {
+      total_natural += priv->spacing * (n_children_in_natural - 1);
+    }
+
+  if (min_width_p)
+    *min_width_p = total_min;
+  if (natural_width_p)
+    *natural_width_p = total_natural;
+}
+
+static void
+big_box_get_preferred_width (ClutterActor  *self,
+                             ClutterUnit    for_height,
+                             ClutterUnit   *min_width_p,
+                             ClutterUnit   *natural_width_p)
+{
+  BigBoxPrivate *priv;
+  ClutterUnit content_min_width;
+  ClutterUnit content_natural_width;
+  ClutterUnit outside;
+
+  priv = BIG_BOX (self)->priv;
+
+  big_box_get_content_width_request (self,
+                                     &content_min_width,
+                                     &content_natural_width);
+
+  outside = priv->padding_left + priv->padding_right +
+            priv->border_left + priv->border_right;
+
+  if (min_width_p)
+    *min_width_p = content_min_width + outside;
+  if (natural_width_p)
+    *natural_width_p = content_natural_width + outside;
+
+  if (priv->debug)
+    {
+      if (min_width_p)
+        g_debug ("Computed minimum width as %d", CLUTTER_UNITS_TO_DEVICE (*min_width_p));
+      if (natural_width_p)
+        g_debug ("Computed natural width as %d", CLUTTER_UNITS_TO_DEVICE (*natural_width_p));
+    }
+}
+
+static void
+big_box_get_content_area_horizontal (ClutterActor  *self,
+                                     ClutterUnit    requested_content_width,
+                                     ClutterUnit    natural_content_width,
+                                     ClutterUnit    allocated_box_width,
+                                     ClutterUnit   *x_p,
+                                     ClutterUnit   *width_p)
+{
+  BigBoxPrivate *priv;
+  ClutterUnit left;
+  ClutterUnit right;
+  ClutterUnit unpadded_box_width;
+  ClutterUnit content_width;
+
+  priv = BIG_BOX (self)->priv;
+
+  left = priv->border_left + priv->padding_left;
+  right = priv->border_right + priv->padding_right;
+
+  g_return_if_fail (requested_content_width >= 0);
+
+  if (natural_content_width < allocated_box_width)
+    content_width = natural_content_width;
+  else
+    content_width = MAX (requested_content_width, allocated_box_width);
+
+  unpadded_box_width = allocated_box_width - left - right;
+
+  switch (priv->x_align)
+    {
+    case BIG_BOX_ALIGNMENT_FIXED:
+      g_warning("Must specify a real alignment for content, not FIXED");
+      break;
+    case BIG_BOX_ALIGNMENT_FILL:
+      if (x_p)
+        *x_p = left;
+      if (width_p)
+        *width_p = unpadded_box_width;
+      break;
+    case BIG_BOX_ALIGNMENT_START:
+      if (x_p)
+        *x_p = left;
+      if (width_p)
+        *width_p = content_width;
+      break;
+    case BIG_BOX_ALIGNMENT_END:
+      if (x_p)
+        *x_p = allocated_box_width - right - content_width;
+      if (width_p)
+        *width_p = content_width;
+      break;
+    case BIG_BOX_ALIGNMENT_CENTER:
+      if (x_p)
+        *x_p = left + (unpadded_box_width - content_width) / 2;
+      if (width_p)
+        *width_p = content_width;
+      break;
+    }
+}
+
+static void
+big_box_get_content_area_vertical (ClutterActor   *self,
+                                   ClutterUnit     requested_content_height,
+                                   ClutterUnit     natural_content_height,
+                                   ClutterUnit     allocated_box_height,
+                                   ClutterUnit    *y_p,
+                                   ClutterUnit    *height_p)
+{
+  BigBoxPrivate *priv;
+  ClutterUnit top;
+  ClutterUnit bottom;
+  ClutterUnit unpadded_box_height;
+  ClutterUnit content_height;
+
+  priv = BIG_BOX (self)->priv;
+
+  top = priv->border_top + priv->padding_top;
+  bottom = priv->border_bottom + priv->padding_bottom;
+
+  g_return_if_fail (requested_content_height >= 0);
+
+  if (natural_content_height < allocated_box_height)
+    content_height = natural_content_height;
+  else
+    content_height = MAX (requested_content_height, allocated_box_height);
+
+  unpadded_box_height = allocated_box_height - top - bottom;
+
+  switch (priv->y_align)
+    {
+    case BIG_BOX_ALIGNMENT_FIXED:
+      g_warning("Must specify a real alignment for content, not FIXED");
+      break;
+    case BIG_BOX_ALIGNMENT_FILL:
+      if (y_p)
+        *y_p = top;
+      if (height_p)
+        *height_p = unpadded_box_height;
+      break;
+    case BIG_BOX_ALIGNMENT_START:
+      if (y_p)
+        *y_p = top;
+      if (height_p)
+        *height_p = content_height;
+      break;
+    case BIG_BOX_ALIGNMENT_END:
+      if (y_p)
+        *y_p = allocated_box_height - bottom - content_height;
+      if (height_p)
+        *height_p = content_height;
+      break;
+    case BIG_BOX_ALIGNMENT_CENTER:
+      if (y_p)
+        *y_p = top + (unpadded_box_height - content_height) / 2;
+      if (height_p)
+        *height_p = content_height;
+      break;
+    }
+}
+static BigBoxAdjustInfo *
+big_box_adjust_infos_new (BigBox      *box,
+                          ClutterUnit  for_content_width)
+{
+  BigBoxPrivate *priv = box->priv;
+  BigBoxAdjustInfo *adjusts = g_new0 (BigBoxAdjustInfo, g_list_length (priv->children));
+  GList *c;
+  gint i = 0;
+
+  for (c = priv->children; c != NULL; c = c->next)
+    {
+      BigBoxChild *child = (BigBoxChild *) c->data;
+
+      if (!BOX_CHILD_IN_LAYOUT (child))
+        {
+          adjusts[i].minimum = adjusts[i].natural = 0;
+        }
+      else if (priv->orientation == BIG_BOX_ORIENTATION_VERTICAL)
+        {
+          clutter_actor_get_preferred_height (child->actor, for_content_width,
+                                              &adjusts[i].minimum, &adjusts[i].natural);
+        }
+      else
+        {
+          clutter_actor_get_preferred_width (child->actor, -1, &adjusts[i].minimum,
+                                             &adjusts[i].natural);
+        }
+
+      i++;
+    }
+
+  return adjusts;
+}
+
+static void
+big_box_adjust_if_fits_as_not_fitting (GList            *children,
+                                       BigBoxAdjustInfo *adjusts)
+{
+    GList *c;
+    gint i;
+
+    i = 0;
+
+    for (c = children; c != NULL; c = c->next)
+      {
+        BigBoxChild *child = (BigBoxChild *) c->data;
+
+        if (child->if_fits)
+          {
+            adjusts[i].adjustment -= adjusts[i].minimum;
+            adjusts[i].does_not_fit = TRUE;
+          }
+
+        ++i;
+      }
+}
+
+static gboolean
+big_box_adjust_up_to_natural_size (GList            *children,
+                                   ClutterUnit      *remaining_extra_space_p,
+                                   BigBoxAdjustInfo *adjusts,
+                                   gboolean          if_fits)
+{
+  ClutterUnit smallest_increase;
+  ClutterUnit space_to_distribute;
+  GList *c;
+  gint n_needing_increase;
+  gint i;
+
+  g_assert (*remaining_extra_space_p >= 0);
+
+  if (*remaining_extra_space_p == 0)
+    return FALSE;
+
+  smallest_increase = CLUTTER_MAXUNIT;
+  n_needing_increase = 0;
+
+  i = 0;
+
+  for (c = children; c != NULL; c = c->next)
+    {
+      BigBoxChild *child = (BigBoxChild *) c->data;
+
+      if (BOX_CHILD_IN_LAYOUT (child) &&
+          ((!child->if_fits && !if_fits) ||
+           (child->if_fits && if_fits && !adjusts[i].does_not_fit)))
+        {
+          ClutterUnit needed_increase;
+
+          g_assert (adjusts[i].adjustment >= 0);
+
+          /* Guaranteed to be >= 0 */
+          needed_increase = adjusts[i].natural - adjusts[i].minimum;
+
+          g_assert (needed_increase >= 0);
+
+          needed_increase -= adjusts[i].adjustment; /* see how much we've already increased */
+
+          if (needed_increase > 0)
+            {
+              n_needing_increase += 1;
+              smallest_increase = MIN (smallest_increase, needed_increase);
+            }
+        }
+
+      ++i;
+    }
+
+  if (n_needing_increase == 0)
+    return FALSE;
+
+  g_assert (smallest_increase < G_MAXINT);
+
+  space_to_distribute = MIN (*remaining_extra_space_p,
+                             smallest_increase * n_needing_increase);
+
+  g_assert(space_to_distribute >= 0);
+  g_assert(space_to_distribute <= *remaining_extra_space_p);
+
+  *remaining_extra_space_p -= space_to_distribute;
+
+  i = 0;
+
+  for (c = children; c != NULL; c = c->next)
+    {
+      BigBoxChild *child = (BigBoxChild *) c->data;
+
+      if (BOX_CHILD_IN_LAYOUT (child) &&
+          ((!child->if_fits && !if_fits) ||
+           (child->if_fits && if_fits && !adjusts[i].does_not_fit)))
+        {
+          ClutterUnit needed_increase;
+
+          g_assert (adjusts[i].adjustment >= 0);
+
+          /* Guaranteed to be >= 0 */
+          needed_increase = adjusts[i].natural - adjusts[i].minimum;
+
+          g_assert(needed_increase >= 0);
+
+          needed_increase -= adjusts[i].adjustment; /* see how much we've already increased */
+
+          if (needed_increase > 0)
+            {
+              ClutterUnit extra;
+
+              extra = (space_to_distribute / n_needing_increase);
+
+              n_needing_increase -= 1;
+              space_to_distribute -= extra;
+              adjusts[i].adjustment += extra;
+            }
+        }
+
+      ++i;
+    }
+
+  g_assert (n_needing_increase == 0);
+  g_assert (space_to_distribute == 0);
+
+  return TRUE;
+}
+
+static gboolean
+big_box_adjust_one_if_fits (GList             *children,
+                            ClutterUnit        spacing,
+                            ClutterUnit       *remaining_extra_space_p,
+                            BigBoxAdjustInfo  *adjusts)
+{
+  GList *c;
+  ClutterUnit spacing_delta;
+  gint i;
+  gboolean visible_children = FALSE;
+
+  if (*remaining_extra_space_p == 0)
+    return FALSE;
+
+  /* if there are no currently visible children, then adding a child won't
+   * add another spacing
+   */
+  i = 0;
+
+  for (c = children; c != NULL; c = c->next) {
+      BigBoxChild *child = (BigBoxChild *) c->data;
+
+      if (BOX_CHILD_IN_LAYOUT (child) &&
+          (!child->if_fits || !adjusts[i].does_not_fit))
+        {
+          visible_children = TRUE;
+          break;
+        }
+
+      i++;
+  }
+
+  spacing_delta = visible_children ? spacing : 0;
+
+  i = 0;
+
+  for (c = children; c != NULL; c = c->next)
+    {
+      if (adjusts[i].does_not_fit)
+        {
+          /* This child was adjusted downward, see if we can pop it visible
+           * (picking the smallest instead of first if-fits child on each pass
+           * might be nice, but for now it's the first that fits)
+           */
+          if ((adjusts[i].minimum + spacing_delta) <= *remaining_extra_space_p)
+            {
+              adjusts[i].adjustment += adjusts[i].minimum;
+
+              g_assert (adjusts[i].adjustment >= 0);
+
+              adjusts[i].does_not_fit = FALSE;
+              *remaining_extra_space_p -= (adjusts[i].minimum + spacing_delta);
+
+              g_assert (*remaining_extra_space_p >= 0);
+
+              return TRUE;
+            }
+        }
+
+      ++i;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+box_child_is_expandable (BigBoxChild *child, BigBoxAdjustInfo *adjust)
+{
+  return (BOX_CHILD_IS_VISIBLE (child) || child->if_hidden) && child->expand &&
+         (!child->if_fits || (adjust && !(adjust->does_not_fit)));
+}
+
+static int
+big_box_count_expandable_children (GList           *children,
+                                   BigBoxAdjustInfo *adjusts)
+{
+  GList *c;
+  gint count;
+  gint i;
+
+  count = 0;
+  i = 0;
+
+  for (c = children; c != NULL; c = c->next)
+    {
+      BigBoxChild *child = (BigBoxChild *) c->data;
+
+      /* We assume here that we've prevented via g_warning
+       * any floats/fixed from having expand=TRUE
+       */
+      if (box_child_is_expandable (child, adjusts ? &(adjusts[i]) : NULL))
+        ++count;
+
+      ++i;
+    }
+
+  return count;
+}
+
+static void
+big_box_adjust_for_expandable (GList            *children,
+                               ClutterUnit      *remaining_extra_space_p,
+                               BigBoxAdjustInfo *adjusts)
+{
+  GList *c;
+  ClutterUnit expand_space;
+  gint expand_count;
+  gint i;
+
+  if (*remaining_extra_space_p == 0)
+    return;
+
+  expand_space = *remaining_extra_space_p;
+  expand_count = big_box_count_expandable_children (children, adjusts);
+
+  if (expand_count == 0)
+    return;
+
+  i = 0;
+
+  for (c = children; c != NULL; c = c->next)
+    {
+      BigBoxChild *child = (BigBoxChild *) c->data;
+
+      if (box_child_is_expandable (child, &(adjusts[i])) &&
+          !adjusts[i].does_not_fit)
+        {
+          ClutterUnit extra;
+
+          extra = (expand_space / expand_count);
+
+          expand_count -= 1;
+          expand_space -= extra;
+          adjusts[i].adjustment += extra;
+        }
+
+      ++i;
+    }
+
+  /* if we had anything to expand, then we will have used up all space */
+  g_assert (expand_space == 0);
+  g_assert (expand_count == 0);
+
+  *remaining_extra_space_p = 0;
+}
+static void
+big_box_compute_adjusts (GList             *children,
+                         BigBoxAdjustInfo  *adjusts,
+                         ClutterUnit        spacing,
+                         ClutterUnit        alloc_request_delta)
+{
+  ClutterUnit remaining_extra_space;
+
+  if (children == NULL)
+    return;
+
+  /* Go ahead and cram all PACK_IF_FITS children to zero width,
+   * we'll expand them again if we can.
+   */
+  big_box_adjust_if_fits_as_not_fitting (children, adjusts);
+
+  /* Make no adjustments if we got too little or just right space.
+   * (FIXME handle too little space better)
+   */
+  if (alloc_request_delta <= 0) {
+      return;
+  }
+
+  remaining_extra_space = alloc_request_delta;
+
+  /* Adjust non-PACK_IF_FITS up to natural size */
+  while (big_box_adjust_up_to_natural_size (children,
+                                            &remaining_extra_space, adjusts,
+                                            FALSE))
+      ;
+
+  /* See if any PACK_IF_FITS can get their minimum size */
+  while (big_box_adjust_one_if_fits (children,
+                                     spacing, &remaining_extra_space, adjusts))
+      ;
+
+  /* If so, then see if they can also get a natural size */
+  while (big_box_adjust_up_to_natural_size(children,
+                                           &remaining_extra_space, adjusts,
+                                           TRUE))
+      ;
+
+  /* And finally we can expand to fill empty space */
+  big_box_adjust_for_expandable (children, &remaining_extra_space, adjusts);
+
+  /* remaining_extra_space need not be 0, if we had no expandable children */
+}
+
+static ClutterUnit
+big_box_get_adjusted_size (BigBoxAdjustInfo  *adjust)
+{
+  return adjust->minimum + adjust->adjustment;
+}
+
+static void
+big_box_get_hbox_height_request (ClutterActor  *self,
+                                 ClutterUnit    for_width,
+                                 ClutterUnit   *min_height_p,
+                                 ClutterUnit   *natural_height_p)
+{
+  BigBoxPrivate *priv;
+  ClutterUnit total_min;
+  ClutterUnit total_natural;
+  ClutterUnit requested_content_width;
+  ClutterUnit natural_content_width;
+  ClutterUnit allocated_content_width;
+  BigBoxAdjustInfo *width_adjusts;
+  GList *c;
+  gint i;
+
+  priv = BIG_BOX (self)->priv;
+
+  total_min = 0;
+  total_natural = 0;
+
+  big_box_get_content_width_request (self, &requested_content_width,
+                                     &natural_content_width);
+
+  big_box_get_content_area_horizontal (self,
+                                       requested_content_width,
+                                       natural_content_width,
+                                       for_width, NULL,
+                                       &allocated_content_width);
+
+  width_adjusts = big_box_adjust_infos_new (BIG_BOX (self), for_width);
+
+  big_box_compute_adjusts (priv->children,
+                           width_adjusts,
+                           priv->spacing,
+                           allocated_content_width - requested_content_width);
+
+  i = 0;
+
+  for (c = priv->children; c != NULL; c = c->next)
+    {
+      BigBoxChild *child = c->data;
+      ClutterUnit min_height, natural_height;
+      ClutterUnit req = 0;
+
+      if (!BOX_CHILD_IN_LAYOUT (child))
+        {
+          ++i;
+          continue;
+        }
+
+      req = big_box_get_adjusted_size (&width_adjusts[i]);
+
+      clutter_actor_get_preferred_height (child->actor, req,
+                                          &min_height, &natural_height);
+
+      if (priv->debug)
+        g_debug ("H - Child %p min height %d natural %d",
+                 child->actor,
+                 CLUTTER_UNITS_TO_DEVICE (min_height),
+                 CLUTTER_UNITS_TO_DEVICE (natural_height));
+
+      total_min = MAX (total_min, min_height);
+      total_natural = MAX (total_natural, natural_height);
+
+      ++i;
+    }
+
+  g_free (width_adjusts);
+
+  if (min_height_p)
+    *min_height_p = total_min;
+  if (natural_height_p)
+    *natural_height_p = total_natural;
+}
+
+static void
+big_box_get_vbox_height_request (ClutterActor  *self,
+                                 ClutterUnit    for_width,
+                                 ClutterUnit   *min_height_p,
+                                 ClutterUnit   *natural_height_p)
+{
+  BigBoxPrivate *priv;
+  ClutterUnit total_min;
+  ClutterUnit total_natural;
+  gint n_children_in_min;
+  gint n_children_in_natural;
+  GList *c;
+
+  priv = BIG_BOX (self)->priv;
+
+  total_min = 0;
+  total_natural = 0;
+  n_children_in_min = 0;
+  n_children_in_natural = 0;
+
+  for (c = priv->children; c != NULL; c = c->next)
+    {
+      BigBoxChild *child = (BigBoxChild *) c->data;
+      ClutterUnit min_height;
+      ClutterUnit natural_height;
+
+      if (!BOX_CHILD_IN_LAYOUT (child))
+        continue;
+
+      clutter_actor_get_preferred_height (child->actor, for_width,
+                                          &min_height, &natural_height);
+
+      if (priv->debug)
+        g_debug ("V - Child %p min height %d natural %d",
+                 child->actor,
+                 CLUTTER_UNITS_TO_DEVICE (min_height),
+                 CLUTTER_UNITS_TO_DEVICE (natural_height));
+
+      n_children_in_natural += 1;
+      total_natural += natural_height;
+
+      if (!child->if_fits)
+        {
+          n_children_in_min += 1;
+          total_min += min_height;
+        }
+    }
+
+  if (n_children_in_min > 1)
+    total_min += priv->spacing * (n_children_in_min - 1);
+  if (n_children_in_natural > 1)
+    total_natural += priv->spacing * (n_children_in_natural - 1);
+
+  if (min_height_p)
+    *min_height_p = total_min;
+  if (natural_height_p)
+    *natural_height_p = total_natural;
+}
+
+static void
+big_box_get_content_height_request (ClutterActor  *self,
+                                    ClutterUnit    for_width,
+                                    ClutterUnit   *min_height_p,
+                                    ClutterUnit   *natural_height_p)
+{
+  BigBoxPrivate *priv;
+
+  priv = BIG_BOX (self)->priv;
+
+  if (priv->orientation == BIG_BOX_ORIENTATION_VERTICAL)
+    big_box_get_vbox_height_request (self, for_width,
+                                     min_height_p, natural_height_p);
+  else
+    big_box_get_hbox_height_request (self, for_width,
+                                     min_height_p, natural_height_p);
+}
+
+static void
+big_box_get_preferred_height (ClutterActor  *self,
+                              ClutterUnit    for_width,
+                              ClutterUnit   *min_height_p,
+                              ClutterUnit   *natural_height_p)
+{
+  BigBoxPrivate *priv;
+  ClutterUnit content_min_height, content_natural_height;
+  ClutterUnit content_for_width;
+  ClutterUnit outside;
+
+  priv = BIG_BOX (self)->priv;
+
+  content_for_width = for_width
+      - priv->padding_left - priv->padding_right
+      - priv->border_left - priv->border_right;
+
+  /* We need to call this even if just returning the box-height prop,
+   * so that children can rely on getting the full request, allocate
+   * cycle in order every time, and so we compute the cached requests.
+   */
+  big_box_get_content_height_request (self,
+                                      content_for_width,
+                                      &content_min_height,
+                                      &content_natural_height);
+
+  outside = priv->padding_top + priv->padding_bottom +
+            priv->border_top + priv->border_bottom;
+
+  if (min_height_p)
+      *min_height_p = content_min_height + outside;
+  if (natural_height_p)
+      *natural_height_p = content_natural_height + outside;
+
+
+  if (priv->debug)
+    {
+      if (min_height_p)
+        g_debug ("Computed minimum height for width=%d as %d",
+                 CLUTTER_UNITS_TO_DEVICE (for_width), CLUTTER_UNITS_TO_DEVICE (*min_height_p));
+      if (natural_height_p)
+        g_debug ("Computed natural height for width=%d as %d",
+                 CLUTTER_UNITS_TO_DEVICE (for_width), CLUTTER_UNITS_TO_DEVICE (*natural_height_p));
+    }
+}
+
+static void
+big_box_layout (ClutterActor    *self,
+                ClutterUnit      content_x,
+                ClutterUnit      content_y,
+                ClutterUnit      allocated_content_width,
+                ClutterUnit      allocated_content_height,
+                ClutterUnit      requested_content_width,
+                ClutterUnit      requested_content_height,
+                gboolean         absolute_origin_changed)
+{
+  BigBoxPrivate *priv;
+  BigBoxAdjustInfo *adjusts;
+  ClutterActorBox child_box;
+  ClutterUnit allocated_size, requested_size;
+  ClutterUnit start;
+  ClutterUnit end;
+  GList *c;
+  gint i;
+
+  priv = BIG_BOX (self)->priv;
+
+  if (priv->orientation == BIG_BOX_ORIENTATION_VERTICAL)
+    {
+      allocated_size = allocated_content_height;
+      requested_size = requested_content_height;
+      start = content_y;
+    }
+  else
+    {
+      allocated_size = allocated_content_width;
+      requested_size = requested_content_width;
+      start = content_x;
+    }
+
+  end = start + allocated_size;
+
+  adjusts = big_box_adjust_infos_new (BIG_BOX (self), allocated_content_width);
+
+  big_box_compute_adjusts (priv->children,
+                           adjusts,
+                           priv->spacing,
+                           allocated_size - requested_size);
+
+  i = 0;
+
+  for (c = priv->children; c != NULL; c = c->next)
+   {
+      BigBoxChild *child = (BigBoxChild *) c->data;
+      ClutterUnit req;
+
+      if (!BOX_CHILD_IN_LAYOUT (child))
+        {
+          ++i;
+          continue;
+        }
+
+      if (priv->orientation == BIG_BOX_ORIENTATION_VERTICAL)
+        {
+          req = big_box_get_adjusted_size (&adjusts[i]);
+
+          child_box.x1 = content_x;
+          child_box.y1 = child->end ? end - req : start;
+          child_box.x2 = child_box.x1 + allocated_content_width;
+          child_box.y2 = child_box.y1 + req;
+
+          if (priv->debug)
+            g_debug ("V - Child %p %s being allocated: %d, %d, %d, %d w: %d h: %d",
+                     child->actor,
+                     g_type_name_from_instance((GTypeInstance*) child->actor),
+                     CLUTTER_UNITS_TO_DEVICE (child_box.x1),
+                     CLUTTER_UNITS_TO_DEVICE (child_box.y1),
+                     CLUTTER_UNITS_TO_DEVICE (child_box.x2),
+                     CLUTTER_UNITS_TO_DEVICE (child_box.y2),
+                     CLUTTER_UNITS_TO_DEVICE (child_box.x2 - child_box.x1),
+                     CLUTTER_UNITS_TO_DEVICE (child_box.y2 - child_box.y1));
+
+          clutter_actor_allocate (child->actor, &child_box,
+                                  absolute_origin_changed);
+        }
+      else
+        {
+          req = big_box_get_adjusted_size (&adjusts[i]);
+
+          child_box.x1 = child->end ? end - req : start;
+          child_box.y1 = content_y;
+          child_box.x2 = child_box.x1 + req;
+          child_box.y2 = child_box.y1 + allocated_content_height;
+
+          if (priv->debug)
+            g_debug ("H - Child %p %s being allocated: %d, %d, %d, %d  w: %d h: %d",
+                     child->actor,
+                     g_type_name_from_instance((GTypeInstance*) child->actor),
+                     CLUTTER_UNITS_TO_DEVICE (child_box.x1),
+                     CLUTTER_UNITS_TO_DEVICE (child_box.y1),
+                     CLUTTER_UNITS_TO_DEVICE (child_box.x2),
+                     CLUTTER_UNITS_TO_DEVICE (child_box.y2),
+                     CLUTTER_UNITS_TO_DEVICE (child_box.x2 - child_box.x1),
+                     CLUTTER_UNITS_TO_DEVICE (child_box.y2 - child_box.y1));
+
+          clutter_actor_allocate (child->actor, &child_box,
+                                  absolute_origin_changed);
+        }
+
+      if (req <= 0)
+        {
+          /* Child was adjusted out of existence, act like it's
+           * !visible
+           */
+          child_box.x1 = 0;
+          child_box.y1 = 0;
+          child_box.x2 = 0;
+          child_box.y2 = 0;
+
+          clutter_actor_allocate (child->actor, &child_box,
+                                  absolute_origin_changed);
+        }
+
+      /* Children with req == 0 still get spacing unless they are IF_FITS.
+       * The handling of spacing could use improvement (spaces should probably
+       * act like items with min width 0 and natural width of spacing) but
+       * it's pretty hard to get right without rearranging the code a lot.
+       */
+      if (!adjusts[i].does_not_fit)
+        {
+          if (child->end)
+            end -= (req + priv->spacing);
+          else
+            start += (req + priv->spacing);
+        }
+
+      ++i;
+    }
+
+  g_free (adjusts);
+}
+
+static void
+big_box_allocate (ClutterActor          *self,
+                  const ClutterActorBox *box,
+                  gboolean               absolute_origin_changed)
+{
+  BigBoxPrivate *priv;
+  ClutterUnit requested_content_width;
+  ClutterUnit requested_content_height;
+  ClutterUnit natural_content_width;
+  ClutterUnit natural_content_height;
+  ClutterUnit allocated_content_width;
+  ClutterUnit allocated_content_height = 0;
+  ClutterUnit content_x, content_y = 0;
+  GList *c;
+
+  priv = BIG_BOX (self)->priv;
+
+  if (priv->debug)
+    g_debug ("Entire box %p being allocated: %d, %d, %d, %d",
+             self,
+             CLUTTER_UNITS_TO_DEVICE (box->x1),
+             CLUTTER_UNITS_TO_DEVICE (box->y1),
+             CLUTTER_UNITS_TO_DEVICE (box->x2),
+             CLUTTER_UNITS_TO_DEVICE (box->y2));
+
+  CLUTTER_ACTOR_CLASS (big_box_parent_class)->allocate (self, box, absolute_origin_changed);
+
+  big_box_get_content_width_request (self,
+                                     &requested_content_width,
+                                     &natural_content_width);
+
+  big_box_get_content_area_horizontal (self, requested_content_width,
+                                       natural_content_width, box->x2 - box->x1,
+                                       &content_x, &allocated_content_width);
+
+  big_box_get_content_height_request (self,
+                                      allocated_content_width,
+                                      &requested_content_height,
+                                      &natural_content_height);
+
+  big_box_get_content_area_vertical (self, requested_content_height,
+                                     natural_content_height, box->y2 - box->y1,
+                                     &content_y, &allocated_content_height);
+
+  if (priv->debug)
+    {
+      if (allocated_content_height < requested_content_height)
+        g_debug ("Box %p allocated height %d but requested %d",
+                 self,
+                 CLUTTER_UNITS_TO_DEVICE (allocated_content_height),
+                 CLUTTER_UNITS_TO_DEVICE (requested_content_height));
+      if (allocated_content_width < requested_content_width)
+        g_debug ("Box %p allocated width %d but requested %d",
+                 self,
+                 CLUTTER_UNITS_TO_DEVICE (allocated_content_width),
+                 CLUTTER_UNITS_TO_DEVICE (requested_content_width));
+    }
+
+  if (priv->background_texture)
+    {
+      ClutterActorBox bg_box;
+
+      big_box_get_bg_texture_allocation (self,
+                                         box->x2 - box->x1,
+                                         box->y2 - box->y1,
+                                         &bg_box);
+
+      if (priv->debug)
+        {
+          g_debug ("Box %p texture allocated width %d and height %d",
+                   self,
+                   CLUTTER_UNITS_TO_DEVICE (bg_box.x2 - bg_box.x1),
+                   CLUTTER_UNITS_TO_DEVICE (bg_box.y2 - bg_box.y1));
+        }
+
+      clutter_actor_allocate (priv->background_texture, &bg_box,
+                              absolute_origin_changed);
+    }
+
+  if (priv->background_rectangle)
+    {
+      ClutterActorBox rectangle_box;
+
+      rectangle_box.x1 = 0;
+      rectangle_box.y1 = 0;
+      rectangle_box.x2 = box->x2 - box->x1;
+      rectangle_box.y2 = box->y2 - box->y1;
+
+      clutter_actor_allocate (priv->background_rectangle,
+                              &rectangle_box,
+                              absolute_origin_changed);
+    }
+
+  for (c = priv->children; c != NULL; c = c->next)
+    {
+      BigBoxChild *child = (BigBoxChild *) c->data;
+      ClutterActorBox child_box;
+
+      if (!(BOX_CHILD_IS_VISIBLE (child) || child->if_hidden))
+        {
+          child_box.x1 = 0;
+          child_box.y1 = 0;
+          child_box.x2 = 0;
+          child_box.y2 = 0;
+
+          clutter_actor_allocate (child->actor, &child_box, FALSE);
+        }
+      else if (child->fixed)
+        {
+          ClutterUnit x, y, width, height;
+
+          clutter_actor_get_positionu (child->actor, &x, &y);
+          clutter_actor_get_preferred_width(child->actor, -1, NULL, &width);
+          clutter_actor_get_preferred_height(child->actor, width, NULL, &height);
+
+          switch (child->fixed_x_align)
+            {
+            case BIG_BOX_ALIGNMENT_FIXED:
+              /* honor child forced x,y instead of aligning automatically */
+              child_box.x1 = x;
+              child_box.x2 = x + width;
+              break;
+            case BIG_BOX_ALIGNMENT_START:
+              child_box.x1 = content_x;
+              child_box.x2 = child_box.x1 + width;
+              break;
+            case BIG_BOX_ALIGNMENT_END:
+              child_box.x2 = content_x + allocated_content_width;
+              child_box.x1 = child_box.x2 - width;
+              break;
+            case BIG_BOX_ALIGNMENT_CENTER:
+              child_box.x1 = content_x + (allocated_content_width - width) / 2;
+              child_box.x2 = child_box.x1 + width;
+              break;
+            case BIG_BOX_ALIGNMENT_FILL:
+              child_box.x1 = content_x;
+              child_box.x2 = content_x + allocated_content_width;
+              break;
+            }
+
+          switch (child->fixed_y_align)
+            {
+            case BIG_BOX_ALIGNMENT_FIXED:
+              /* honor child forced x,y instead of aligning automatically */
+              child_box.y1 = y;
+              child_box.y2 = y + height;
+              break;
+            case BIG_BOX_ALIGNMENT_START:
+              child_box.y1 = content_y;
+              child_box.y2 = child_box.y1 + height;
+              break;
+            case BIG_BOX_ALIGNMENT_END:
+              child_box.y2 = content_y + allocated_content_height;
+              child_box.y1 = child_box.y2 - height;
+              break;
+            case BIG_BOX_ALIGNMENT_CENTER:
+              child_box.y1 = content_y + (allocated_content_height - height) / 2;
+              child_box.y2 = child_box.y1 + height;
+              break;
+            case BIG_BOX_ALIGNMENT_FILL:
+              child_box.y1 = content_y;
+              child_box.y2 = content_y + allocated_content_height;
+              break;
+            }
+
+          if (priv->debug)
+            g_debug ("Fixed Child being allocated: %d, %d, %d, %d",
+                     CLUTTER_UNITS_TO_DEVICE (child_box.x1),
+                     CLUTTER_UNITS_TO_DEVICE (child_box.y1),
+                     CLUTTER_UNITS_TO_DEVICE (child_box.x2),
+                     CLUTTER_UNITS_TO_DEVICE (child_box.y2));
+
+          clutter_actor_allocate(child->actor, &child_box,
+                                 absolute_origin_changed);
+        }
+    }
+
+  big_box_layout (self, content_x, content_y,
+                  allocated_content_width, allocated_content_height,
+                  requested_content_width, requested_content_height,
+                  absolute_origin_changed);
+}
+
+static void
+big_box_paint (ClutterActor *actor)
+{
+  BigBox *self = BIG_BOX (actor);
+  GList *c;
+  ClutterColor color;
+  guint8 actor_opacity;
+  ClutterGeometry allocation;
+  ClutterGeometry bg_rect;
+  int border_top, border_bottom, border_left, border_right;
+  int padding_top, padding_bottom, padding_left, padding_right;
+
+  /* First, convert everything to pixels */
+  border_top = CLUTTER_UNITS_TO_DEVICE (self->priv->border_top);
+  border_bottom = CLUTTER_UNITS_TO_DEVICE (self->priv->border_bottom);
+  border_left = CLUTTER_UNITS_TO_DEVICE (self->priv->border_left);
+  border_right = CLUTTER_UNITS_TO_DEVICE (self->priv->border_right);
+  padding_top = CLUTTER_UNITS_TO_DEVICE (self->priv->padding_top);
+  padding_bottom = CLUTTER_UNITS_TO_DEVICE (self->priv->padding_bottom);
+  padding_left = CLUTTER_UNITS_TO_DEVICE (self->priv->padding_left);
+  padding_right = CLUTTER_UNITS_TO_DEVICE (self->priv->padding_right);
+
+  actor_opacity = clutter_actor_get_paint_opacity (actor);
+
+  /* Remember allocation.x and .y don't matter since we are
+   * theoretically already translated to 0,0
+   */
+  clutter_actor_get_allocation_geometry (actor, &allocation);
+
+  cogl_enable(0); /* defeat cogl GL cache in case GL used directly */
+
+  cogl_push_matrix ();
+
+  /* Background */
+
+  color = self->priv->background_color;
+  color.alpha = (color.alpha * actor_opacity) / 0xff;
+
+  bg_rect.x = border_left;
+  bg_rect.y = border_top;
+  bg_rect.width = allocation.width - border_left - border_right;
+  bg_rect.height = allocation.height - border_top - border_bottom;
+
+  /* Border */
+
+  if (self->priv->draw_rounded_corner)
+    {
+      /* we delegate drawing of the background to BigRectangle.
+       * Ideally we would always do that but currently BigRectangle
+       * does not support border segments with different size
+       */
+      clutter_actor_paint (self->priv->background_rectangle);
+    }
+  else
+    {
+      if (color.alpha != 0)
+        {
+          cogl_color (&color);
+          cogl_rectangle (bg_rect.x, bg_rect.y, bg_rect.width, bg_rect.height);
+        }
+
+      color = self->priv->border_color;
+      color.alpha = (color.alpha * actor_opacity) / 0xff;
+
+      if (color.alpha != 0)
+        {
+          cogl_color (&color);
+
+          /* top */
+          cogl_rectangle (0, 0,
+                          allocation.width,
+                          border_top);
+
+          /* left */
+          cogl_rectangle (0, border_top,
+                          border_left,
+                          allocation.height - border_top - border_bottom);
+
+          /* right */
+          cogl_rectangle (allocation.width - border_right,
+                          border_top,
+                          border_right,
+                          allocation.height - border_top - border_bottom);
+
+          /* bottom */
+          cogl_rectangle (0, allocation.height - border_bottom,
+                          allocation.width,
+                          border_bottom);
+        }
+    }
+
+  cogl_enable(0); /* defeat cogl GL cache in case GL used directly */
+
+  if (self->priv->background_texture &&
+      CLUTTER_ACTOR_IS_VISIBLE(self->priv->background_texture)) {
+    clutter_actor_paint (CLUTTER_ACTOR (self->priv->background_texture));
+    cogl_enable(0); /* defeat cogl GL cache in case GL used directly */
+  }
+
+  /* Children */
+
+  for (c = self->priv->children;
+       c != NULL;
+       c = c->next)
+    {
+      BigBoxChild *child = (BigBoxChild *) c->data;
+
+      g_assert (child != NULL);
+
+      if (BOX_CHILD_IS_VISIBLE (child)) {
+        clutter_actor_paint (child->actor);
+        cogl_enable(0); /* defeat cogl GL cache in case GL used directly */
+      }
+    }
+
+  cogl_pop_matrix();
+}
+
+static void
+big_box_pick (ClutterActor       *actor,
+              const ClutterColor *color)
+{
+  BigBox *self = BIG_BOX (actor);
+  GList *c;
+
+  CLUTTER_ACTOR_CLASS (big_box_parent_class)->pick (actor, color);
+
+  for (c = self->priv->children;
+       c != NULL;
+       c = c->next)
+    {
+      BigBoxChild *child = (BigBoxChild *) c->data;
+
+      g_assert (child != NULL);
+
+      if (BOX_CHILD_IS_VISIBLE (child))
+        clutter_actor_paint (child->actor);
+    }
+}
+
+static void
+big_box_class_init (BigBoxClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (BigBoxPrivate));
+
+  gobject_class->set_property = big_box_set_property;
+  gobject_class->get_property = big_box_get_property;
+  gobject_class->dispose = big_box_dispose;
+  gobject_class->finalize = big_box_finalize;
+
+  actor_class->get_preferred_width = big_box_get_preferred_width;
+  actor_class->get_preferred_height = big_box_get_preferred_height;
+  actor_class->allocate = big_box_allocate;
+  actor_class->paint = big_box_paint;
+  actor_class->pick  = big_box_pick;
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_ORIENTATION,
+                  g_param_spec_enum ("orientation",
+                                     "Orientation",
+                                     "Orientation of the box",
+                                     BIG_TYPE_BOX_ORIENTATION,
+                                     BIG_BOX_ORIENTATION_VERTICAL,
+                                     G_PARAM_READWRITE));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_SPACING,
+                  g_param_spec_int ("spacing",
+                                    "Spacing",
+                                    "Spacing between items of the box",
+                                    G_MININT,
+                                    G_MAXINT,
+                                    0,
+                                    G_PARAM_READWRITE));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_X_ALIGN,
+                  g_param_spec_enum ("x-align",
+                                     "X alignment",
+                                     "X alignment",
+                                     BIG_TYPE_BOX_ALIGNMENT,
+                                     BIG_BOX_ALIGNMENT_START,
+                                     G_PARAM_READWRITE));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_Y_ALIGN,
+                  g_param_spec_enum ("y-align",
+                                     "Y alignment",
+                                     "Y alignment",
+                                     BIG_TYPE_BOX_ALIGNMENT,
+                                     BIG_BOX_ALIGNMENT_START,
+                                    G_PARAM_READWRITE));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_PADDING,
+                  g_param_spec_int ("padding",
+                                    "Padding",
+                                    "Padding (set all paddings at once)",
+                                    G_MININT,
+                                    G_MAXINT,
+                                    0,
+                                    G_PARAM_WRITABLE));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_PADDING_TOP,
+                  g_param_spec_int ("padding-top",
+                                    "Padding on the top",
+                                    "Padding on the top",
+                                    G_MININT,
+                                    G_MAXINT,
+                                    0,
+                                    G_PARAM_READWRITE));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_PADDING_BOTTOM,
+                  g_param_spec_int ("padding-bottom",
+                                    "Padding on the bottom",
+                                    "Padding on the bottom",
+                                    G_MININT,
+                                    G_MAXINT,
+                                    0,
+                                    G_PARAM_READWRITE));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_PADDING_LEFT,
+                  g_param_spec_int ("padding-left",
+                                    "Padding on the left",
+                                    "Padding on the left",
+                                    G_MININT,
+                                    G_MAXINT,
+                                    0,
+                                    G_PARAM_READWRITE));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_PADDING_RIGHT,
+                  g_param_spec_int ("padding-right",
+                                    "Padding on the right",
+                                    "Padding on the right",
+                                    G_MININT,
+                                    G_MAXINT,
+                                    0,
+                                    G_PARAM_READWRITE));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_BORDER,
+                  g_param_spec_int ("border",
+                                    "Border",
+                                    "Border (set all borders at once)",
+                                    G_MININT,
+                                    G_MAXINT,
+                                    0,
+                                    G_PARAM_WRITABLE));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_BORDER_TOP,
+                  g_param_spec_int ("border-top",
+                                    "Border on the top",
+                                    "Border on the top",
+                                    G_MININT,
+                                    G_MAXINT,
+                                    0,
+                                    G_PARAM_READWRITE));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_BORDER_BOTTOM,
+                  g_param_spec_int ("border-bottom",
+                                    "Border on the bottom",
+                                    "Border on the bottom",
+                                    G_MININT,
+                                    G_MAXINT,
+                                    0,
+                                    G_PARAM_READWRITE));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_BORDER_LEFT,
+                  g_param_spec_int ("border-left",
+                                    "Border on the left",
+                                    "Border on the left",
+                                    G_MININT,
+                                    G_MAXINT,
+                                    0,
+                                    G_PARAM_READWRITE));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_BORDER_RIGHT,
+                  g_param_spec_int ("border-right",
+                                    "Border on the right",
+                                    "Border on the right",
+                                    G_MININT,
+                                    G_MAXINT,
+                                    0,
+                                    G_PARAM_READWRITE));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_CORNER_RADIUS,
+                  g_param_spec_uint ("corner-radius",
+                                     "Corner radius",
+                                     "Radius of the rounded corner "
+                                     "(ignored with border segments of "
+                                     "different sizes)",
+                                     0,
+                                     G_MAXUINT,
+                                     0,
+                                     G_PARAM_READWRITE));
+
+  /**
+   * BigBox:background-border-top:
+   *
+   * Specifies a border on the top of the image which should not be
+   * stretched when the image is set to fill the box vertically
+   * (background-y-align set to FILL, background-repeat set to
+   * not repeat vertically. Useful for images with rounded corners.
+   */
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_BACKGROUND_BORDER_TOP,
+                  g_param_spec_uint ("background-border-top",
+                                     "Background border on the top",
+                                     "Background border on the top",
+                                     0,
+                                     G_MAXUINT,
+                                     0,
+                                     G_PARAM_READWRITE));
+
+  /**
+   * BigBox:background-border-bottom:
+   *
+   * Specifies a border on the bottom of the image which should not be
+   * stretched when the image is set to fill the box vertically
+   * (background-y-align set to FILL, background-repeat set to
+   * not repeat vertically. Useful for images with rounded corners.
+   */
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_BACKGROUND_BORDER_BOTTOM,
+                  g_param_spec_uint ("background-border-bottom",
+                                     "Background border on the bottom",
+                                     "Background border on the bottom",
+                                     0,
+                                     G_MAXUINT,
+                                     0,
+                                     G_PARAM_READWRITE));
+
+  /**
+   * BigBox:background-border-left:
+   *
+   * Specifies a border on the left of the image which should not be
+   * stretched when the image is set to fill the box horizontally
+   * (background-x-align set to FILL, background-repeat set to
+   * not repeat horizontally. Useful for images with rounded corners.
+   */
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_BACKGROUND_BORDER_LEFT,
+                  g_param_spec_uint ("background-border-left",
+                                     "Background border on the left",
+                                     "Background border on the left",
+                                     0,
+                                     G_MAXUINT,
+                                     0,
+                                     G_PARAM_READWRITE));
+
+  /**
+   * BigBox:background-border-right:
+   *
+   * Specifies a border on the right of the image which should not be
+   * stretched when the image is set to fill the box horizontally
+   * (background-x-align set to FILL, background-repeat set to
+   * not repeat horizontally. Useful for images with rounded corners.
+   */
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_BACKGROUND_BORDER_RIGHT,
+                  g_param_spec_uint ("background-border-right",
+                                     "Background border on the right",
+                                     "Background border on the right",
+                                     0,
+                                     G_MAXUINT,
+                                     0,
+                                     G_PARAM_READWRITE));
+
+  /**
+   * BigBox:background-color:
+   *
+   * Background color, covers padding but not border.
+   */
+  g_object_class_install_property
+    (gobject_class,
+     PROP_BACKGROUND_COLOR,
+     g_param_spec_boxed ("background-color",
+                         "Background Color",
+                         "The color of the background",
+                         CLUTTER_TYPE_COLOR,
+                         G_PARAM_READWRITE));
+  /**
+   * BigBox:background-filename:
+   *
+   * Background filename, covers padding but not border.
+   */
+  g_object_class_install_property
+    (gobject_class,
+     PROP_BACKGROUND_FILENAME,
+     g_param_spec_string ("background-filename",
+                          "Background Filename",
+                          "The image filename of the background",
+                          NULL,
+                          G_PARAM_WRITABLE));
+  /**
+   * BigBox:background-pixbuf:
+   *
+   * Background pixbuf, covers padding but not border.
+   */
+  g_object_class_install_property
+    (gobject_class,
+     PROP_BACKGROUND_PIXBUF,
+     g_param_spec_object ("background-pixbuf",
+                          "Background Pixbuf",
+                          "The image pixbuf of the background",
+                          GDK_TYPE_PIXBUF,
+                          G_PARAM_WRITABLE));
+  /**
+   * BigBox:background-texture:
+   *
+   * Background texture, covers padding but not border.
+   */
+  g_object_class_install_property
+    (gobject_class,
+     PROP_BACKGROUND_TEXTURE,
+     g_param_spec_object ("background-texture",
+                          "Background Texture",
+                          "The texture of the background",
+                          CLUTTER_TYPE_TEXTURE,
+                          G_PARAM_READABLE));
+
+  /**
+   * BigBox:background-rectangle:
+   *
+   * Background rectangle, covers the complete box allocation.
+   */
+  g_object_class_install_property
+    (gobject_class,
+     PROP_BACKGROUND_RECTANGLE,
+     g_param_spec_object ("background-rectangle",
+                          "Background Rectangle",
+                          "The rectangle forming the box border and "
+                          "background color",
+                          BIG_TYPE_RECTANGLE,
+                          G_PARAM_READABLE));
+  /**
+   * BigBox:background-repeat:
+   *
+   * Sets if/how a background image will be repeated.
+   */
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_BACKGROUND_REPEAT,
+                  g_param_spec_enum ("background-repeat",
+                                     "Background Repeat",
+                                     "Background Repeat",
+                                     BIG_TYPE_BOX_BACKGROUND_REPEAT,
+                                     BIG_BOX_BACKGROUND_REPEAT_NONE,
+                                     G_PARAM_READWRITE));
+  /**
+   * BigBox:background-x-align:
+   *
+   * Sets the horizontal alignment of the background texture.
+   */
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_BACKGROUND_X_ALIGN,
+                  g_param_spec_enum ("background-x-align",
+                                     "Background X alignment",
+                                     "Background X alignment",
+                                     BIG_TYPE_BOX_ALIGNMENT,
+                                     BIG_BOX_ALIGNMENT_FILL,
+                                     G_PARAM_READWRITE));
+  /**
+   * BigBox:background-y-align:
+   *
+   * Sets the vertical alignment of the background texture.
+   */
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_BACKGROUND_Y_ALIGN,
+                  g_param_spec_enum ("background-y-align",
+                                     "Background Y alignment",
+                                     "Background Y alignment",
+                                     BIG_TYPE_BOX_ALIGNMENT,
+                                     BIG_BOX_ALIGNMENT_FILL,
+                                     G_PARAM_READWRITE));
+  /**
+   * BigBox:border-color:
+   *
+   * Border color, color of border if any. Make the border transparent
+   * to use the border purely for spacing with no visible border.
+   *
+   */
+  g_object_class_install_property
+    (gobject_class,
+     PROP_BORDER_COLOR,
+     g_param_spec_boxed ("border-color",
+                         "Border Color",
+                         "The color of the border of the rectangle",
+                         CLUTTER_TYPE_COLOR,
+                         G_PARAM_READWRITE));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_DEBUG,
+                  g_param_spec_boolean ("debug",
+                                        "Debug",
+                                        "Whether debug is activated or not",
+                                        FALSE,
+                                        G_PARAM_READWRITE));
+}
+
+static void
+big_box_init (BigBox *box)
+{
+  box->priv = BIG_BOX_GET_PRIVATE (box);
+
+  box->priv->orientation = BIG_BOX_ORIENTATION_VERTICAL;
+  box->priv->x_align = BIG_BOX_ALIGNMENT_FILL;
+  box->priv->y_align = BIG_BOX_ALIGNMENT_FILL;
+  box->priv->spacing = 0;
+  box->priv->padding_top = 0;
+  box->priv->padding_bottom = 0;
+  box->priv->padding_left = 0;
+  box->priv->padding_right = 0;
+  box->priv->border_top = 0;
+  box->priv->border_bottom = 0;
+  box->priv->border_left = 0;
+  box->priv->border_right = 0;
+
+  box->priv->background_texture = NULL;
+  box->priv->background_repeat = BIG_BOX_BACKGROUND_REPEAT_NONE;
+  box->priv->background_x_align = BIG_BOX_ALIGNMENT_FILL;
+  box->priv->background_y_align = BIG_BOX_ALIGNMENT_FILL;
+
+  box->priv->background_rectangle = NULL;
+  box->priv->draw_rounded_corner = FALSE;
+
+  /* both bg and border colors default to black and transparent (all bits 0) */
+}
+
+ClutterActor *
+big_box_new (BigBoxOrientation orientation)
+{
+  return g_object_new (BIG_TYPE_BOX,
+                       "orientation", orientation,
+                       NULL);
+}
+
+void
+big_box_prepend (BigBox          *box,
+                 ClutterActor    *child,
+                 BigBoxPackFlags  flags)
+{
+  BigBoxPrivate *priv;
+  BigBoxChild *c;
+
+  g_return_if_fail (BIG_IS_BOX (box));
+  g_return_if_fail (CLUTTER_IS_ACTOR (child));
+
+  priv = box->priv;
+
+  g_object_ref (child);
+
+  c = box_child_new_from_actor (child, flags);
+
+  priv->children = g_list_prepend (priv->children, c);
+
+  clutter_actor_set_parent (child, CLUTTER_ACTOR (box));
+
+  g_signal_emit_by_name (box, "actor-added", child);
+
+  clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+
+  g_object_unref (child);
+}
+
+void
+big_box_append (BigBox          *box,
+                ClutterActor    *child,
+                BigBoxPackFlags  flags)
+{
+  BigBoxPrivate *priv;
+  BigBoxChild *c;
+
+  g_return_if_fail (BIG_IS_BOX (box));
+  g_return_if_fail (CLUTTER_IS_ACTOR (child));
+
+  priv = box->priv;
+
+  g_object_ref (child);
+
+  c = box_child_new_from_actor (child, flags);
+
+  priv->children = g_list_append (priv->children, c);
+
+  clutter_actor_set_parent (child, CLUTTER_ACTOR (box));
+
+  g_signal_emit_by_name (box, "actor-added", child);
+
+  big_box_real_sort_depth_order (CLUTTER_CONTAINER (box));
+
+  clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+
+  g_object_unref (child);
+}
+
+gboolean
+big_box_is_empty (BigBox *box)
+{
+  g_return_val_if_fail (BIG_IS_BOX (box), TRUE);
+
+  return (box->priv->children == NULL);
+}
+
+void
+big_box_remove_all (BigBox *box)
+{
+  BigBoxPrivate *priv;
+
+  g_return_if_fail (BIG_IS_BOX (box));
+
+  priv = box->priv;
+
+  while (priv->children != NULL) {
+    BigBoxChild *child = priv->children->data;
+
+    box_child_remove (box, child);
+  }
+
+  clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+}
+
+void
+big_box_insert_after (BigBox          *box,
+                      ClutterActor    *child,
+                      ClutterActor    *ref_child,
+                      BigBoxPackFlags  flags)
+{
+  BigBoxPrivate *priv;
+  BigBoxChild *c, *ref_c;
+  gint position;
+
+  g_return_if_fail (BIG_IS_BOX (box));
+  g_return_if_fail (CLUTTER_IS_ACTOR (child));
+  g_return_if_fail (CLUTTER_IS_ACTOR (ref_child));
+
+  priv = box->priv;
+
+  g_object_ref (child);
+
+  ref_c = box_child_find (box, ref_child);
+
+  if (ref_c != NULL)
+    {
+      c = box_child_new_from_actor (child, flags);
+
+      position = g_list_index (priv->children, ref_c);
+      priv->children = g_list_insert (priv->children, c, ++position);
+
+      clutter_actor_set_parent (child, CLUTTER_ACTOR (box));
+
+      g_signal_emit_by_name (box, "actor-added", child);
+
+      clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+    }
+
+  g_object_unref (child);
+}
+
+void
+big_box_insert_before (BigBox          *box,
+                       ClutterActor    *child,
+                       ClutterActor    *ref_child,
+                       BigBoxPackFlags  flags)
+{
+  BigBoxPrivate *priv;
+  BigBoxChild *c, *ref_c;
+  gint position;
+
+  g_return_if_fail (BIG_IS_BOX (box));
+  g_return_if_fail (CLUTTER_IS_ACTOR (child));
+
+  priv = box->priv;
+
+  g_object_ref (child);
+
+  ref_c = box_child_find (box, ref_child);
+
+  if (ref_c != NULL)
+    {
+      c = box_child_new_from_actor (child, flags);
+
+      position = g_list_index (priv->children, ref_c);
+      priv->children = g_list_insert (priv->children, c, position);
+
+      clutter_actor_set_parent (child, CLUTTER_ACTOR (box));
+
+      g_signal_emit_by_name (box, "actor-added", child);
+
+      clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+    }
+
+  g_object_unref (child);
+}
+
+void
+big_box_set_child_packing (BigBox          *box,
+                           ClutterActor    *child,
+                           BigBoxPackFlags  flags)
+{
+  BigBoxChild *c;
+
+  g_return_if_fail (BIG_IS_BOX (box));
+  g_return_if_fail (CLUTTER_IS_ACTOR (child));
+
+  g_object_ref (child);
+
+  c = box_child_find (box, child);
+
+  if (c != NULL && box_child_set_flags (c, flags))
+    clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+
+  g_object_unref (child);
+}
+
+void
+big_box_set_child_align(BigBox              *box,
+                        ClutterActor        *child,
+                        BigBoxAlignment      fixed_x_align,
+                        BigBoxAlignment      fixed_y_align)
+{
+  BigBoxChild *c;
+
+  g_return_if_fail (BIG_IS_BOX (box));
+  g_return_if_fail (CLUTTER_IS_ACTOR (child));
+
+  g_object_ref (child);
+
+  c = box_child_find (box, child);
+
+  if (c != NULL && box_child_set_align (c, fixed_x_align, fixed_y_align))
+    clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+
+  g_object_unref (child);
+}
+
+void
+big_box_set_padding (BigBox *box, int padding)
+{
+  BigBoxPrivate *priv;
+  ClutterUnit padding_in_units;
+  gboolean padding_changed;
+
+  g_return_if_fail (BIG_IS_BOX (box));
+  g_return_if_fail (padding >= 0);
+
+  priv = box->priv;
+
+  padding_in_units = CLUTTER_UNITS_FROM_DEVICE (padding);
+
+  padding_changed = (priv->padding_top    != padding_in_units ||
+                     priv->padding_bottom != padding_in_units ||
+                     priv->padding_left   != padding_in_units ||
+                     priv->padding_right  != padding_in_units);
+
+  if (padding_changed)
+    {
+      g_object_freeze_notify (G_OBJECT (box));
+
+      if (box->priv->padding_top != padding_in_units)
+        g_object_notify (G_OBJECT (box), "padding-top");
+      box->priv->padding_top = padding_in_units;
+
+      if (box->priv->padding_bottom != padding_in_units)
+        g_object_notify (G_OBJECT (box), "padding-bottom");
+      box->priv->padding_bottom = padding_in_units;
+
+      if (box->priv->padding_left != padding_in_units)
+        g_object_notify (G_OBJECT (box), "padding-left");
+      box->priv->padding_left = padding_in_units;
+
+      if (box->priv->padding_right != padding_in_units)
+        g_object_notify (G_OBJECT (box), "padding-right");
+      box->priv->padding_right = padding_in_units;
+
+      g_object_thaw_notify (G_OBJECT (box));
+
+      clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+    }
+}
+
+void
+big_box_set_border_width (BigBox *box, int border_width)
+{
+  BigBoxPrivate *priv;
+  ClutterUnit border_in_units;
+  gboolean border_changed;
+
+  g_return_if_fail (BIG_IS_BOX (box));
+  g_return_if_fail (border_width >= 0);
+
+  priv = box->priv;
+
+  border_in_units = CLUTTER_UNITS_FROM_DEVICE (border_width);
+
+  border_changed = (priv->border_top    != border_in_units ||
+                    priv->border_bottom != border_in_units ||
+                    priv->border_left   != border_in_units ||
+                    priv->border_right  != border_in_units);
+
+  if (border_changed)
+    {
+      g_object_freeze_notify (G_OBJECT (box));
+
+      if (box->priv->border_top != border_in_units)
+        g_object_notify (G_OBJECT (box), "border-top");
+      box->priv->border_top = border_in_units;
+
+      if (box->priv->border_bottom != border_in_units)
+        g_object_notify (G_OBJECT (box), "border-bottom");
+      box->priv->border_bottom = border_in_units;
+
+      if (box->priv->border_left != border_in_units)
+        g_object_notify (G_OBJECT (box), "border-left");
+      box->priv->border_left = border_in_units;
+
+      if (box->priv->border_right != border_in_units)
+        g_object_notify (G_OBJECT (box), "border-right");
+      box->priv->border_right = border_in_units;
+
+      g_object_thaw_notify (G_OBJECT (box));
+
+      clutter_actor_queue_relayout (CLUTTER_ACTOR (box));
+
+      big_box_update_draw_rounded_corner (box);
+    }
+}

Added: trunk/src/big/box.h
==============================================================================
--- (empty file)
+++ trunk/src/big/box.h	Mon Dec  1 23:01:52 2008
@@ -0,0 +1,129 @@
+/* -*- mode: C; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
+/* big-box.h: Box container.
+
+   Copyright (C) 2006-2008 Red Hat, Inc.
+   Copyright (C) 2008 litl, LLC.
+
+   The libbigwidgets-lgpl is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The libbigwidgets-lgpl is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the libbigwidgets-lgpl; see the file COPYING.LIB.
+   If not, write to the Free Software Foundation, Inc., 59 Temple Place -
+   Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __BIG_BOX_H__
+#define __BIG_BOX_H__
+
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define BIG_TYPE_BOX            (big_box_get_type ())
+#define BIG_BOX(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), BIG_TYPE_BOX, BigBox))
+#define BIG_IS_BOX(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BIG_TYPE_BOX))
+#define BIG_BOX_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  BIG_TYPE_BOX, BigBoxClass))
+#define BIG_IS_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  BIG_TYPE_BOX))
+#define BIG_BOX_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  BIG_TYPE_BOX, BigBoxClass))
+
+typedef struct _BigBox          BigBox;
+typedef struct _BigBoxPrivate   BigBoxPrivate;
+typedef struct _BigBoxClass     BigBoxClass;
+
+typedef enum
+{
+    BIG_BOX_PACK_NONE                 = 0,
+    BIG_BOX_PACK_EXPAND               = 1 << 0,
+    BIG_BOX_PACK_END                  = 1 << 1,
+    BIG_BOX_PACK_IF_FITS              = 1 << 2,
+    BIG_BOX_PACK_FIXED                = 1 << 3,
+    BIG_BOX_PACK_ALLOCATE_WHEN_HIDDEN = 1 << 4
+} BigBoxPackFlags;
+
+typedef enum
+{
+  BIG_BOX_ALIGNMENT_FIXED  = 0,
+  BIG_BOX_ALIGNMENT_FILL   = 1,
+  BIG_BOX_ALIGNMENT_START  = 2,
+  BIG_BOX_ALIGNMENT_END    = 3,
+  BIG_BOX_ALIGNMENT_CENTER = 4
+} BigBoxAlignment;
+
+typedef enum
+{
+  BIG_BOX_ORIENTATION_VERTICAL   = 1,
+  BIG_BOX_ORIENTATION_HORIZONTAL = 2
+} BigBoxOrientation;
+
+typedef enum
+{
+  BIG_BOX_BACKGROUND_REPEAT_NONE = 0,
+  BIG_BOX_BACKGROUND_REPEAT_X    = 1,
+  BIG_BOX_BACKGROUND_REPEAT_Y    = 2,
+  BIG_BOX_BACKGROUND_REPEAT_BOTH = 3,
+} BigBoxBackgroundRepeat;
+
+struct _BigBox
+{
+  ClutterActor parent_instance;
+
+  BigBoxPrivate *priv;
+};
+
+struct _BigBoxClass
+{
+  ClutterActorClass parent_class;
+};
+
+GType          big_box_get_type          (void) G_GNUC_CONST;
+
+ClutterActor  *big_box_new               (BigBoxOrientation    orientation);
+
+void           big_box_prepend           (BigBox              *box,
+                                          ClutterActor        *child,
+                                          BigBoxPackFlags      flags);
+
+void           big_box_append            (BigBox              *box,
+                                          ClutterActor        *child,
+                                          BigBoxPackFlags      flags);
+
+gboolean       big_box_is_empty          (BigBox              *box);
+
+void           big_box_remove_all        (BigBox              *box);
+
+void           big_box_insert_after      (BigBox              *box,
+                                          ClutterActor        *child,
+                                          ClutterActor        *ref_child,
+                                          BigBoxPackFlags      flags);
+
+void           big_box_insert_before     (BigBox              *box,
+                                          ClutterActor        *child,
+                                          ClutterActor        *ref_child,
+                                          BigBoxPackFlags      flags);
+
+void           big_box_set_child_packing (BigBox              *box,
+                                          ClutterActor        *child,
+                                          BigBoxPackFlags      flags);
+
+void           big_box_set_child_align   (BigBox              *box,
+                                          ClutterActor        *child,
+                                          BigBoxAlignment      fixed_x_align,
+                                          BigBoxAlignment      fixed_y_align);
+
+void           big_box_set_padding       (BigBox              *box,
+                                          int                  padding);
+
+void           big_box_set_border_width  (BigBox              *box,
+                                          int                  border_width);
+
+G_END_DECLS
+
+#endif /* __BIG_BOX_H__ */

Added: trunk/src/big/rectangle.c
==============================================================================
--- (empty file)
+++ trunk/src/big/rectangle.c	Mon Dec  1 23:01:52 2008
@@ -0,0 +1,537 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* rectangle.c: Rounded rectangle.
+
+   Copyright (C) 2008 litl, LLC.
+
+   The libbigwidgets-lgpl is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The libbigwidgets-lgpl is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the libbigwidgets-lgpl; see the file COPYING.LIB.
+   If not, write to the Free Software Foundation, Inc., 59 Temple Place -
+   Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <string.h>
+#include <math.h>
+
+#include <glib.h>
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <cairo/cairo.h>
+
+#include "rectangle.h"
+
+typedef struct {
+    gint         ref_count;
+
+    ClutterColor color;
+    ClutterColor border_color;
+    int          radius;
+    int          border_width;
+
+    CoglHandle   texture;
+    guint8      *data;
+} Corner;
+
+struct BigRectangle {
+    ClutterRectangle parent_instance;
+    ClutterUnit      radius;
+    Corner          *corner;
+    gboolean         corners_dirty;
+};
+
+/* map of { radius, border_width, border_color, color } to Corner textures */
+static GHashTable *all_corners = NULL;
+
+struct BigRectangleClass {
+    ClutterRectangleClass parent_class;
+};
+
+G_DEFINE_TYPE(BigRectangle, big_rectangle, CLUTTER_TYPE_RECTANGLE)
+
+enum
+{
+    PROP_0,
+
+    PROP_CORNER_RADIUS
+};
+
+static gboolean
+corner_equal(gconstpointer a,
+             gconstpointer b)
+{
+    const Corner *corner_a;
+    const Corner *corner_b;
+
+    corner_a = a;
+    corner_b = b;
+
+    return *((guint32 *)&corner_a->color) == *((guint32 *)&corner_b->color) &&
+           *((guint32 *)&corner_a->border_color) == *((guint32 *)&corner_b->border_color) &&
+           corner_a->border_width == corner_b->border_width &&
+           corner_a->radius == corner_b->radius;
+}
+
+static guint
+corner_hash(gconstpointer key)
+{
+    const Corner *corner;
+    guint hashed[4];
+
+    corner = key;
+
+    hashed[0] = *((guint *)&(corner->color));
+    hashed[1] = *((guint *)&(corner->border_color));
+    hashed[2] = *((guint *)&(corner->border_width));
+    hashed[3] = *((guint *)&(corner->radius));
+
+    return hashed[0] ^ hashed[1] ^ hashed[2] ^ hashed[3];
+}
+
+static Corner *
+create_corner_texture(Corner *src)
+{
+    Corner *corner;
+    CoglHandle texture;
+    cairo_t *cr;
+    cairo_surface_t *surface;
+    guint x, y;
+    guint rowstride;
+    guint8 *data;
+    guint32 *src_p;
+    guint8 *dst_p;
+    guint size;
+
+    corner = g_memdup(src, sizeof(Corner));
+
+    size = 2 * MAX(corner->border_width, corner->radius);
+    rowstride = size * 4;
+    data = g_new0(guint8, size * rowstride);
+
+    surface = cairo_image_surface_create_for_data(data,
+                                                  CAIRO_FORMAT_ARGB32,
+                                                  size, size,
+                                                  rowstride);
+    cr = cairo_create(surface);
+    cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+    cairo_scale(cr, size, size);
+
+    if (corner->border_width < corner->radius) {
+        double internal_radius = 0.5 * (1.0 - (double) corner->border_width / corner->radius);
+
+        if (corner->border_width != 0) {
+            cairo_set_source_rgba(cr,
+                                  (double)corner->border_color.red / G_MAXUINT8,
+                                  (double)corner->border_color.green / G_MAXUINT8,
+                                  (double)corner->border_color.blue / G_MAXUINT8,
+                                  (double)corner->border_color.alpha / G_MAXUINT8);
+
+            cairo_arc(cr, 0.5, 0.5, 0.5, 0, 2 * M_PI);
+            cairo_fill(cr);
+        }
+
+        cairo_set_source_rgba(cr,
+                              (double)corner->color.red / G_MAXUINT8,
+                              (double)corner->color.green / G_MAXUINT8,
+                              (double)corner->color.blue / G_MAXUINT8,
+                              (double)corner->color.alpha / G_MAXUINT8);
+        cairo_arc(cr, 0.5, 0.5, internal_radius, 0, 2 * M_PI);
+        cairo_fill(cr);
+
+    } else {
+        double radius;
+
+        radius = (gdouble)corner->radius / corner->border_width;
+
+        cairo_set_source_rgba(cr,
+                              (double)corner->border_color.red / G_MAXUINT8,
+                              (double)corner->border_color.green / G_MAXUINT8,
+                              (double)corner->border_color.blue / G_MAXUINT8,
+                              (double)corner->border_color.alpha / G_MAXUINT8);
+
+        cairo_arc(cr, radius, radius, radius, M_PI, 3 * M_PI / 2);
+        cairo_line_to(cr, 1.0 - radius, 0.0);
+        cairo_arc(cr, 1.0 - radius, radius, radius, 3 * M_PI / 2, 2*M_PI);
+        cairo_line_to(cr, 1.0, 1.0 - radius);
+        cairo_arc(cr, 1.0 - radius, 1.0 - radius, radius, 0, M_PI / 2);
+        cairo_line_to(cr, radius, 1.0);
+        cairo_arc(cr, radius, 1.0 - radius, radius, M_PI / 2, M_PI);
+        cairo_fill(cr);
+    }
+    cairo_destroy(cr);
+
+    cairo_surface_destroy(surface);
+
+    corner->data = g_new0(guint8, size * rowstride);
+
+    /* cogl doesn't seem to support the conversion, do it manually */
+    /* borrowed from clutter-cairo, conversion from ARGB pre-multiplied
+     * to RGBA */
+    for (y = 0; y < size; y++) {
+        src_p = (guint32 *) (data + y * rowstride);
+        dst_p = corner->data + y * rowstride;
+
+        for (x = 0; x < size; x++) {
+            guint8 alpha = (*src_p >> 24) & 0xff;
+
+            if (alpha == 0) {
+                dst_p[0] = dst_p[1] = dst_p[2] = dst_p[3] = alpha;
+            } else {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+                dst_p[0] = (((*src_p >> 16) & 0xff) * 255 ) / alpha;
+                dst_p[1] = (((*src_p >> 8) & 0xff) * 255 ) / alpha;
+                dst_p[2] = (((*src_p >> 0) & 0xff) * 255 ) / alpha;
+                dst_p[3] = alpha;
+#elif G_BYTE_ORDER == G_BIG_ENDIAN
+                dst_p[0] = alpha;
+                dst_p[1] = (((*src_p >> 0) & 0xff) * 255 ) / alpha;
+                dst_p[2] = (((*src_p >> 8) & 0xff) * 255 ) / alpha;
+                dst_p[3] = (((*src_p >> 16) & 0xff) * 255 ) / alpha;
+#else /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */
+#error unknown ENDIAN type
+#endif /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */
+            }
+            dst_p += 4;
+            src_p++;
+        }
+    }
+
+    g_free(data);
+
+    texture = cogl_texture_new_from_data(size, size,
+                                         0,
+                                         FALSE,
+                                         COGL_PIXEL_FORMAT_RGBA_8888,
+                                         COGL_PIXEL_FORMAT_ANY,
+                                         rowstride,
+                                         corner->data);
+    g_assert(texture != COGL_INVALID_HANDLE);
+
+    corner->ref_count = 1;
+    corner->texture = texture;
+
+    g_hash_table_insert(all_corners, corner, corner);
+
+    return corner;
+}
+
+static void
+corner_unref(Corner *corner)
+{
+    corner->ref_count --;
+
+    if (corner->ref_count == 0) {
+        g_hash_table_remove(all_corners, corner);
+
+        cogl_texture_unref(corner->texture);
+        g_free(corner->data);
+        g_free(corner);
+    }
+}
+
+static Corner *
+corner_get(guint         radius,
+           ClutterColor *color,
+           guint         border_width,
+           ClutterColor *border_color)
+{
+    Corner key;
+    Corner *corner;
+
+    if (all_corners == NULL) {
+        all_corners = g_hash_table_new(corner_hash, corner_equal);
+    }
+
+    key.radius = radius;
+    key.color = *color;
+    key.border_color = *border_color;
+    key.border_width = border_width;
+
+    corner = g_hash_table_lookup(all_corners, &key);
+
+    if (!corner) {
+        corner = create_corner_texture(&key);
+    } else {
+        corner->ref_count ++;
+    }
+
+    return corner;
+}
+
+static void
+big_rectangle_update_corners(BigRectangle *rectangle)
+{
+    Corner *corner;
+
+    corner = NULL;
+
+    if (rectangle->radius != 0) {
+        ClutterColor *color;
+        ClutterColor *border_color;
+        guint border_width;
+
+        g_object_get(rectangle,
+                     "border-color", &border_color,
+                     "border-width", &border_width,
+                     "color", &color,
+                     NULL);
+
+        corner = corner_get(CLUTTER_UNITS_TO_DEVICE(rectangle->radius),
+                            color,
+                            border_width,
+                            border_color);
+
+        clutter_color_free(border_color);
+        clutter_color_free(color);
+    }
+
+    if (rectangle->corner) {
+        corner_unref(rectangle->corner);
+    }
+
+    rectangle->corner = corner;
+
+    rectangle->corners_dirty = FALSE;
+}
+
+static void
+big_rectangle_paint(ClutterActor *actor)
+{
+    BigRectangle *rectangle;
+    ClutterColor *color;
+    ClutterColor *border_color;
+    guint border_width;
+    ClutterActorBox box;
+    ClutterFixed radiusx;
+    ClutterFixed widthx;
+    ClutterFixed heightx;
+    ClutterFixed border_widthx;
+    ClutterFixed max;
+    ClutterColor tmp_col;
+
+    rectangle = BIG_RECTANGLE(actor);
+
+    if (rectangle->radius == 0) {
+        /* In that case we are no different than our parent class,
+         * so don't bother */
+        CLUTTER_ACTOR_CLASS(big_rectangle_parent_class)->paint(actor);
+        return;
+    }
+
+    if (rectangle->corners_dirty)
+        big_rectangle_update_corners(rectangle);
+
+    g_object_get(rectangle,
+                 "border-color", &border_color,
+                 "border-width", &border_width,
+                 "color", &color,
+                 NULL);
+
+    clutter_actor_get_allocation_box(actor, &box);
+
+    /* translation was already done */
+    box.x2 -= box.x1;
+    box.y2 -= box.y1;
+
+    widthx = CLUTTER_UNITS_TO_FIXED(box.x2);
+    heightx = CLUTTER_UNITS_TO_FIXED(box.y2);
+
+    radiusx = CLUTTER_UNITS_TO_FIXED(rectangle->radius);
+
+    border_widthx = CLUTTER_INT_TO_FIXED(border_width);
+    max = MAX(border_widthx, radiusx);
+
+    if (radiusx != 0) {
+        tmp_col.red = 0xFF;
+        tmp_col.blue = 0xFF;
+        tmp_col.green = 0xFF;
+        tmp_col.alpha = clutter_actor_get_paint_opacity(actor);
+
+        cogl_color(&tmp_col);
+
+        /* NW */
+        cogl_texture_rectangle(rectangle->corner->texture,
+                               0, 0,
+                               max, max,
+                               0, 0,
+                               CFX_HALF, CFX_HALF);
+
+        /* NE */
+        cogl_texture_rectangle(rectangle->corner->texture,
+                               widthx - max, 0,
+                               widthx, max,
+                               CFX_HALF, 0,
+                               CFX_ONE, CFX_HALF);
+
+        /* SW */
+        cogl_texture_rectangle(rectangle->corner->texture,
+                               0, heightx - max,
+                               max, heightx,
+                               0, CFX_HALF,
+                               CFX_HALF, CFX_ONE);
+
+        /* SE */
+        cogl_texture_rectangle(rectangle->corner->texture,
+                               widthx - max, heightx - max,
+                               widthx, heightx,
+                               CFX_HALF, CFX_HALF,
+                               CFX_ONE, CFX_ONE);
+
+    }
+
+    if (border_width != 0) {
+        tmp_col = *border_color;
+
+        tmp_col.alpha = clutter_actor_get_paint_opacity(actor) * tmp_col.alpha / 255;
+
+        cogl_color(&tmp_col);
+
+        /* NORTH */
+        cogl_rectanglex(max, 0,
+                        widthx - 2 * max, border_widthx);
+
+        /* EAST */
+        cogl_rectanglex(widthx - border_widthx, max,
+                        border_widthx, heightx - 2 * max);
+
+        /* SOUTH */
+        cogl_rectanglex(max, heightx - border_widthx,
+                        widthx - 2 * max, border_widthx);
+
+        /* WEST */
+        cogl_rectanglex(0, max,
+                        border_widthx, heightx - 2 * max);
+
+    }
+
+    tmp_col = *color;
+    tmp_col.alpha = clutter_actor_get_paint_opacity(actor) * tmp_col.alpha / 255;
+
+    cogl_color(&tmp_col);
+
+    if (radiusx > border_widthx) {
+        cogl_rectanglex(radiusx, border_widthx,
+                        widthx - radiusx - radiusx, radiusx - border_widthx);
+        cogl_rectanglex(radiusx, heightx - radiusx,
+                        widthx - 2 * radiusx, radiusx - border_widthx);
+    }
+
+    cogl_rectanglex(border_widthx, MAX(radiusx, border_widthx),
+                    widthx - 2 * border_widthx, heightx - 2 * MAX(radiusx, border_widthx));
+
+    clutter_color_free(border_color);
+    clutter_color_free(color);
+}
+
+static void
+big_rectangle_notify(GObject    *object,
+                     GParamSpec *pspec)
+{
+    BigRectangle *rectangle;
+
+    rectangle = BIG_RECTANGLE(object);
+
+    if (g_str_equal(pspec->name, "border-width") ||
+        g_str_equal(pspec->name, "color") ||
+        g_str_equal(pspec->name, "border-color")) {
+        rectangle->corners_dirty = TRUE;
+    }
+}
+
+static void
+big_rectangle_set_property(GObject      *object,
+                           guint         prop_id,
+                           const GValue *value,
+                           GParamSpec   *pspec)
+{
+    BigRectangle *rectangle;
+
+    rectangle = BIG_RECTANGLE(object);
+
+    switch (prop_id) {
+    case PROP_CORNER_RADIUS:
+        rectangle->radius = CLUTTER_UNITS_FROM_DEVICE(g_value_get_uint(value));
+        rectangle->corners_dirty = TRUE;
+        break;
+
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+
+    }
+}
+
+static void
+big_rectangle_get_property(GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+    BigRectangle *rectangle;
+
+    rectangle = BIG_RECTANGLE(object);
+
+    switch (prop_id) {
+    case PROP_CORNER_RADIUS:
+        g_value_set_uint(value, CLUTTER_UNITS_TO_DEVICE(rectangle->radius));
+        break;
+
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+big_rectangle_dispose(GObject *object)
+{
+    BigRectangle *rectangle;
+
+    rectangle = BIG_RECTANGLE(object);
+
+    rectangle->radius = 0;
+    big_rectangle_update_corners(rectangle);
+
+    if (G_OBJECT_CLASS(big_rectangle_parent_class)->dispose)
+        G_OBJECT_CLASS(big_rectangle_parent_class)->dispose(object);
+
+}
+
+
+static void
+big_rectangle_class_init(BigRectangleClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+  gobject_class->dispose = big_rectangle_dispose;
+  gobject_class->set_property = big_rectangle_set_property;
+  gobject_class->get_property = big_rectangle_get_property;
+  gobject_class->notify = big_rectangle_notify;
+
+  actor_class->paint = big_rectangle_paint;
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_CORNER_RADIUS,
+                  g_param_spec_uint("corner-radius",
+                                    "Corner radius",
+                                    "Radius of the rectangle rounded corner",
+                                    0, G_MAXUINT,
+                                    0,
+                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+}
+
+static void
+big_rectangle_init(BigRectangle *rectangle)
+{
+}

Added: trunk/src/big/rectangle.h
==============================================================================
--- (empty file)
+++ trunk/src/big/rectangle.h	Mon Dec  1 23:01:52 2008
@@ -0,0 +1,42 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+   Copyright (C) 2008 litl, LLC.
+
+   The libbigwidgets-lgpl is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The libbigwidgets-lgpl is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the libbigwidgets-lgpl; see the file COPYING.LIB.
+   If not, write to the Free Software Foundation, Inc., 59 Temple Place -
+   Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef __BIG_RECTANGLE_H__
+#define __BIG_RECTANGLE_H__
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define BIG_TYPE_RECTANGLE            (big_rectangle_get_type ())
+#define BIG_RECTANGLE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), BIG_TYPE_RECTANGLE, BigRectangle))
+#define BIG_RECTANGLE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  BIG_TYPE_RECTANGLE, BigRectangleClass))
+#define BIG_IS_RECTANGLE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BIG_TYPE_RECTANGLE))
+#define BIG_IS_RECTANGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  BIG_TYPE_RECTANGLE))
+#define BIG_RECTANGLE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  BIG_TYPE_RECTANGLE, BigRectangleClass))
+
+typedef struct BigRectangle      BigRectangle;
+typedef struct BigRectangleClass BigRectangleClass;
+
+GType          big_rectangle_get_type           (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif  /* __BIG_RECTANGLE_H__ */

Added: trunk/src/big/theme-image.c
==============================================================================
--- (empty file)
+++ trunk/src/big/theme-image.c	Mon Dec  1 23:01:52 2008
@@ -0,0 +1,795 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* theme-image.c: Stretched image.
+
+   Copyright (C) 2005-2008 Red Hat, Inc.
+   Copyright (C) 2008 litl, LLC.
+
+   The libbigwidgets-lgpl is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The libbigwidgets-lgpl is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the libbigwidgets-lgpl; see the file COPYING.LIB.
+   If not, write to the Free Software Foundation, Inc., 59 Temple Place -
+   Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <glib.h>
+
+#include <clutter/clutter-units.h>
+#include <clutter/clutter-actor.h>
+#include <clutter-cairo/clutter-cairo.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include <librsvg/rsvg.h>
+#include <librsvg/rsvg-cairo.h>
+
+#include "theme-image.h"
+
+typedef enum {
+    BIG_THEME_IMAGE_UNSET,
+    BIG_THEME_IMAGE_SVG,
+    BIG_THEME_IMAGE_SURFACE
+
+} BigThemeImageType;
+
+struct BigThemeImage {
+    ClutterCairo parent_instance;
+
+    guint border_top;
+    guint border_bottom;
+    guint border_left;
+    guint border_right;
+
+    BigThemeImageType type;
+
+    union {
+        RsvgHandle      *svg_handle;
+        cairo_surface_t *surface;
+    } u;
+
+    guint render_idle;
+    guint needs_render : 1;
+};
+
+struct BigThemeImageClass {
+    ClutterCairoClass parent_class;
+};
+
+G_DEFINE_TYPE(BigThemeImage, big_theme_image, CLUTTER_TYPE_CAIRO)
+
+enum
+{
+    PROP_0,
+
+    PROP_BORDER_TOP,
+    PROP_BORDER_BOTTOM,
+    PROP_BORDER_LEFT,
+    PROP_BORDER_RIGHT,
+
+    PROP_FILENAME,
+    PROP_PIXBUF
+};
+
+static void
+big_theme_image_render(BigThemeImage *image)
+{
+    int source_width = 0;
+    int source_height = 0;
+    int source_x1 = 0, source_x2 = 0, source_y1 = 0, source_y2 = 0;
+    int dest_x1 = 0, dest_x2 = 0, dest_y1 = 0, dest_y2 = 0;
+    int i, j;
+    int dest_width;
+    int dest_height;
+    ClutterGeometry geometry;
+    cairo_t *cr;
+
+    image->needs_render = FALSE;
+
+    if (image->render_idle) {
+        g_source_remove(image->render_idle);
+        image->render_idle = 0;
+    }
+
+
+    /* To draw a theme image, we divide the source and destination into 9
+     * pieces and draw each piece separately. (Some pieces may not exist
+     * if we have 0-width borders, in which case they'll be skipped)
+     *
+     *                         i=0               i=1              i=2
+     *                     border_left                        border_right
+     *                    +------------+--------------------+--------------+
+     * j=0: border_top    |            |                    |              |
+     *                    +------------+--------------------+--------------+
+     * j=1                |            |                    |              |
+     *                    +------------+--------------------+--------------+
+     * j=2: border_bottom |            |                    |              |
+     *                    +------------+--------------------+--------------+
+     *
+     */
+
+    switch (image->type) {
+    case BIG_THEME_IMAGE_SURFACE:
+        if (!image->u.surface)
+            return;
+
+        source_width = cairo_image_surface_get_width(image->u.surface);
+        source_height = cairo_image_surface_get_height(image->u.surface);
+        break;
+    case BIG_THEME_IMAGE_SVG:
+        {
+            RsvgDimensionData dimensions;
+
+            if (!image->u.svg_handle)
+                return;
+
+            rsvg_handle_get_dimensions(image->u.svg_handle, &dimensions);
+            source_width = dimensions.width;
+            source_height = dimensions.height;
+            break;
+        }
+    default:
+        return;
+    }
+
+    clutter_actor_get_allocation_geometry(CLUTTER_ACTOR(image), &geometry);
+
+    dest_width = geometry.width;
+    dest_height = geometry.height;
+
+    cr = clutter_cairo_create(CLUTTER_CAIRO(image));
+
+    for (j = 0; j < 3; j++) {
+        switch (j) {
+        case 0:
+            source_y1 = 0;
+            source_y2 = image->border_top;
+            dest_y1 = 0;
+            dest_y2 = image->border_top;
+            break;
+        case 1:
+            source_y1 = image->border_top;
+            source_y2 = source_height - image->border_bottom;
+            dest_y1 = image->border_top;
+            dest_y2 = dest_height - image->border_bottom;
+            break;
+        case 2:
+            source_y1 = source_height - image->border_bottom;
+            source_y2 = source_height;
+            dest_y1 = dest_height - image->border_bottom;
+            dest_y2 = dest_height;
+            break;
+        }
+
+        if (dest_y2 <= dest_y1)
+            continue;
+
+        /* pixbuf-theme-engine has a nice interpretation of source_y2 == source_y1,
+         * dest_y2 != dest_y1, which is to linearly interpolate between the surrounding
+         * areas. We could do that for the surface case by setting
+         *
+         *   source_y1 == y - 0.5
+         *   source_y2 == y + 0.5
+         *
+         * but it's hard for the SVG case. source_y2 < source_y1 is pathological ... someone
+         * specified borders that sum up larger than the image.
+         */
+        if (source_y2 <= source_y1)
+            continue;
+
+        for (i = 0; i < 3; i++) {
+            switch (i) {
+            case 0:
+                source_x1 = 0;
+                source_x2 = image->border_left;
+                dest_x1 = 0;
+                dest_x2 = image->border_left;
+                break;
+            case 1:
+                source_x1 = image->border_left;
+                source_x2 = source_width - image->border_right;
+                dest_x1 = image->border_left;
+                dest_x2 = dest_width - image->border_right;
+                break;
+            case 2:
+                source_x1 = source_width - image->border_right;
+                source_x2 = source_width;
+                dest_x1 = dest_width - image->border_right;
+                dest_x2 = dest_width;
+                break;
+            }
+
+            if (dest_x2 <= dest_x1)
+                continue;
+
+            if (source_x2 <= source_x1)
+                continue;
+
+            cairo_save(cr);
+
+            cairo_rectangle(cr, dest_x1, dest_y1, dest_x2 - dest_x1, dest_y2 - dest_y1);
+            cairo_clip(cr);
+
+            cairo_translate(cr, dest_x1, dest_y1);
+            cairo_scale(cr,
+                        (double)(dest_x2 - dest_x1) / (source_x2 - source_x1),
+                        (double)(dest_y2 - dest_y1) / (source_y2 - source_y1));
+
+            switch (image->type) {
+            case BIG_THEME_IMAGE_SURFACE:
+                cairo_set_source_surface(cr, image->u.surface, - source_x1, - source_y1);
+                cairo_paint(cr);
+                break;
+            case BIG_THEME_IMAGE_SVG:
+                cairo_translate(cr, - source_x1, - source_y1);
+                rsvg_handle_render_cairo(image->u.svg_handle, cr);
+                break;
+            default:
+                break;
+            }
+
+            cairo_restore(cr);
+        }
+    }
+
+    /* This will cause the surface content to be uploaded as
+     * new texture content */
+    cairo_destroy(cr);
+}
+
+static gboolean
+big_theme_image_render_idle(gpointer data)
+{
+    BigThemeImage *image;
+
+    image = data;
+    big_theme_image_render(image);
+
+    return FALSE;
+}
+
+static void
+big_theme_image_queue_render(BigThemeImage *image)
+{
+    image->needs_render = TRUE;
+    if (!image->render_idle)
+        image->render_idle = g_idle_add(big_theme_image_render_idle,
+                                        image);
+}
+
+static void
+big_theme_image_paint(ClutterActor *actor)
+{
+    BigThemeImage *image;
+
+    image = BIG_THEME_IMAGE(actor);
+
+    if (image->needs_render)
+        big_theme_image_render(image);
+
+    if (CLUTTER_ACTOR_CLASS(big_theme_image_parent_class)->paint)
+        CLUTTER_ACTOR_CLASS(big_theme_image_parent_class)->paint(actor);
+}
+
+static void
+big_theme_image_allocate(ClutterActor          *actor,
+                         const ClutterActorBox *box,
+                         gboolean               absolute_origin_changed)
+{
+    BigThemeImage *image;
+    guint old_width;
+    guint old_height;
+    guint width;
+    guint height;
+
+    image = BIG_THEME_IMAGE(actor);
+
+    width = ABS(CLUTTER_UNITS_TO_DEVICE(box->x2 - box->x1));
+    height = ABS(CLUTTER_UNITS_TO_DEVICE(box->y2 - box->y1));
+
+    g_object_get(actor,
+                 "surface-width", &old_width,
+                 "surface-height", &old_height,
+                 NULL);
+
+    if (width != old_width || height != old_height) {
+
+        clutter_cairo_surface_resize(CLUTTER_CAIRO(actor), width, height);
+
+        big_theme_image_queue_render(image);
+    }
+
+    if (CLUTTER_ACTOR_CLASS(big_theme_image_parent_class))
+        CLUTTER_ACTOR_CLASS(big_theme_image_parent_class)->allocate(actor,
+                                                                    box,
+                                                                    absolute_origin_changed);
+}
+
+static void
+big_theme_image_get_preferred_height(ClutterActor *actor,
+                                     ClutterUnit   for_width,
+                                     ClutterUnit  *min_height_p,
+                                     ClutterUnit  *natural_height_p)
+{
+    BigThemeImage *image;
+
+    image = BIG_THEME_IMAGE(actor);
+
+    if (min_height_p)
+        *min_height_p = 0;
+
+    if (!natural_height_p)
+        return;
+
+    *natural_height_p = 0;
+
+    switch (image->type) {
+    case BIG_THEME_IMAGE_SURFACE:
+        if (!image->u.surface)
+            break;
+
+        *natural_height_p = CLUTTER_UNITS_FROM_DEVICE(
+                   cairo_image_surface_get_height(image->u.surface));
+        break;
+    case BIG_THEME_IMAGE_SVG:
+        {
+            RsvgDimensionData dimensions;
+
+            if (!image->u.svg_handle)
+                return;
+
+            rsvg_handle_get_dimensions(image->u.svg_handle, &dimensions);
+            *natural_height_p =
+                CLUTTER_UNITS_FROM_DEVICE(dimensions.height);
+            break;
+        }
+    default:
+        break;
+    }
+}
+
+static void
+big_theme_image_get_preferred_width(ClutterActor *actor,
+                                    ClutterUnit   for_height,
+                                    ClutterUnit  *min_width_p,
+                                    ClutterUnit  *natural_width_p)
+{
+    BigThemeImage *image;
+
+    image = BIG_THEME_IMAGE(actor);
+
+    if (min_width_p)
+        *min_width_p = 0;
+
+    if (!natural_width_p)
+        return;
+
+    *natural_width_p = 0;
+
+    switch (image->type) {
+    case BIG_THEME_IMAGE_SURFACE:
+        if (!image->u.surface)
+            break;
+
+        *natural_width_p = CLUTTER_UNITS_FROM_DEVICE(cairo_image_surface_get_width(image->u.surface));
+        break;
+    case BIG_THEME_IMAGE_SVG:
+        {
+            RsvgDimensionData dimensions;
+
+            if (!image->u.svg_handle)
+                return;
+
+            rsvg_handle_get_dimensions(image->u.svg_handle, &dimensions);
+            *natural_width_p = CLUTTER_UNITS_FROM_DEVICE(dimensions.width);
+            break;
+        }
+    default:
+        break;
+    }
+
+}
+
+static void
+big_theme_image_set_border_value(BigThemeImage *image, guint *old_value, const GValue *new_value)
+{
+    guint border_value;
+
+    border_value = g_value_get_uint(new_value);
+
+    if (*old_value != border_value) {
+        *old_value = border_value;
+
+        big_theme_image_queue_render(image);
+    }
+}
+
+static void
+big_theme_image_set_filename(BigThemeImage *image, const char *filename)
+{
+    if (!filename)
+        return;
+
+    if (g_str_has_suffix(filename, ".png") ||
+        g_str_has_suffix(filename, ".PNG")) {
+
+        image->type = BIG_THEME_IMAGE_SURFACE;
+
+        image->u.surface = cairo_image_surface_create_from_png(filename);
+
+        if (image->u.surface == NULL) {
+            g_warning("Error when loading PNG from file %s", filename);
+        }
+    } else if (g_str_has_suffix(filename, ".svg") ||
+               g_str_has_suffix(filename, ".SVG")) {
+
+        GError *error;
+
+        error = NULL;
+        image->u.svg_handle = rsvg_handle_new_from_file(filename, &error);
+
+        if (image->u.svg_handle == NULL) {
+            g_warning("Error when loading SVG from file %s: %s", filename,
+                      error?error->message:"Error not set by RSVG");
+            if (error)
+                g_error_free(error);
+
+            return;
+        }
+
+        image->type = BIG_THEME_IMAGE_SVG;
+    } else {
+        g_warning("%s: Unsupported file type", filename);
+        return;
+    }
+
+    big_theme_image_queue_render(image);
+}
+
+static cairo_surface_t *
+create_surface_from_pixbuf(const GdkPixbuf *pixbuf)
+{
+    gint width = gdk_pixbuf_get_width (pixbuf);
+    gint height = gdk_pixbuf_get_height (pixbuf);
+    guchar *gdk_pixels = gdk_pixbuf_get_pixels (pixbuf);
+    int gdk_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+    int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
+
+    guchar *cairo_pixels;
+    cairo_format_t format;
+    cairo_surface_t *surface;
+    static const cairo_user_data_key_t key;
+
+    int j;
+
+    if (n_channels == 3)
+        format = CAIRO_FORMAT_RGB24;
+    else
+        format = CAIRO_FORMAT_ARGB32;
+
+    cairo_pixels = g_malloc (4 * width * height);
+
+    surface = cairo_image_surface_create_for_data((unsigned char *)cairo_pixels,
+                                                   format,
+                                                   width, height, 4 * width);
+
+    cairo_surface_set_user_data(surface, &key,
+                                cairo_pixels, (cairo_destroy_func_t)g_free);
+
+    for (j = height; j; j--) {
+        guchar *p = gdk_pixels;
+        guchar *q = cairo_pixels;
+
+        if (n_channels == 3) {
+            guchar *end = p + 3 * width;
+
+            while (p < end) {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+                q[0] = p[2];
+                q[1] = p[1];
+                q[2] = p[0];
+#else
+                q[1] = p[0];
+                q[2] = p[1];
+                q[3] = p[2];
+#endif
+                p += 3;
+                q += 4;
+            }
+        } else {
+            guchar *end = p + 4 * width;
+            guint t1,t2,t3;
+
+#define MULT(d,c,a,t) G_STMT_START { t = c * a + 0x7f; d = ((t >> 8) + t) >> 8; } G_STMT_END
+
+            while (p < end) {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+                MULT(q[0], p[2], p[3], t1);
+                MULT(q[1], p[1], p[3], t2);
+                MULT(q[2], p[0], p[3], t3);
+                q[3] = p[3];
+#else
+                q[0] = p[3];
+                MULT(q[1], p[0], p[3], t1);
+                MULT(q[2], p[1], p[3], t2);
+                MULT(q[3], p[2], p[3], t3);
+#endif
+
+                p += 4;
+                q += 4;
+            }
+#undef MULT
+        }
+
+        gdk_pixels += gdk_rowstride;
+        cairo_pixels += 4 * width;
+    }
+
+    return surface;
+}
+
+static void
+big_theme_image_set_pixbuf(BigThemeImage *image, GdkPixbuf *pixbuf)
+{
+    if (!pixbuf)
+        return;
+
+    image->type = BIG_THEME_IMAGE_SURFACE;
+
+    image->u.surface = create_surface_from_pixbuf(pixbuf);
+
+    g_assert(image->u.surface != NULL);
+
+    big_theme_image_queue_render(image);
+}
+
+static void
+big_theme_image_set_property(GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+    BigThemeImage *image;
+
+    image = BIG_THEME_IMAGE(object);
+
+    switch (prop_id) {
+    case PROP_BORDER_TOP:
+        big_theme_image_set_border_value(image, &image->border_top, value);
+        break;
+
+    case PROP_BORDER_BOTTOM:
+        big_theme_image_set_border_value(image, &image->border_bottom, value);
+        break;
+
+    case PROP_BORDER_LEFT:
+        big_theme_image_set_border_value(image, &image->border_left, value);
+        break;
+
+    case PROP_BORDER_RIGHT:
+        big_theme_image_set_border_value(image, &image->border_right, value);
+        break;
+
+    case PROP_FILENAME:
+        big_theme_image_set_filename(image, g_value_get_string(value));
+        break;
+
+    case PROP_PIXBUF:
+        big_theme_image_set_pixbuf(image, g_value_get_object(value));
+        break;
+
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+
+    }
+}
+
+static void
+big_theme_image_get_property(GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+    BigThemeImage *image;
+
+    image = BIG_THEME_IMAGE(object);
+
+    switch (prop_id) {
+    case PROP_BORDER_TOP:
+        g_value_set_uint(value, image->border_top);
+        break;
+
+    case PROP_BORDER_BOTTOM:
+        g_value_set_uint(value, image->border_bottom);
+        break;
+
+    case PROP_BORDER_LEFT:
+        g_value_set_uint(value, image->border_left);
+        break;
+
+    case PROP_BORDER_RIGHT:
+        g_value_set_uint(value, image->border_right);
+        break;
+
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+big_theme_image_dispose(GObject *object)
+{
+    BigThemeImage *image;
+
+    image = BIG_THEME_IMAGE(object);
+
+    if (image->render_idle) {
+        g_source_remove(image->render_idle);
+        image->render_idle = 0;
+    }
+
+    switch (image->type) {
+    case BIG_THEME_IMAGE_SVG:
+        if (image->u.svg_handle) {
+            g_object_unref(image->u.svg_handle);
+            image->u.svg_handle = NULL;
+        }
+        break;
+    case BIG_THEME_IMAGE_SURFACE:
+        if (image->u.surface) {
+            cairo_surface_destroy(image->u.surface);
+            image->u.surface = NULL;
+        }
+        break;
+    default:
+        break;
+    }
+
+    if (G_OBJECT_CLASS(big_theme_image_parent_class)->dispose)
+        G_OBJECT_CLASS(big_theme_image_parent_class)->dispose(object);
+
+}
+
+
+static void
+big_theme_image_class_init(BigThemeImageClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+  gobject_class->dispose = big_theme_image_dispose;
+  gobject_class->set_property = big_theme_image_set_property;
+  gobject_class->get_property = big_theme_image_get_property;
+
+  actor_class->allocate = big_theme_image_allocate;
+  actor_class->get_preferred_width = big_theme_image_get_preferred_width;
+  actor_class->get_preferred_height = big_theme_image_get_preferred_height;
+  actor_class->paint = big_theme_image_paint;
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_BORDER_TOP,
+                  g_param_spec_uint("border-top",
+                                    "Border top",
+                                    "Top dimension of the image border "
+                                    "(none-scaled part)",
+                                    0, G_MAXUINT,
+                                    0,
+                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_BORDER_BOTTOM,
+                  g_param_spec_uint("border-bottom",
+                                    "Border bottom",
+                                    "Bottom dimension of the image border "
+                                    "(none-scaled part)",
+                                    0, G_MAXUINT,
+                                    0,
+                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_BORDER_LEFT,
+                  g_param_spec_uint("border-left",
+                                    "Border left",
+                                    "Left dimension of the image border "
+                                    "(none-scaled part)",
+                                    0, G_MAXUINT,
+                                    0,
+                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_BORDER_RIGHT,
+                  g_param_spec_uint("border-right",
+                                    "Border right",
+                                    "Right dimension of the image border "
+                                    "(none-scaled part)",
+                                    0, G_MAXUINT,
+                                    0,
+                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_FILENAME,
+                  g_param_spec_string("filename",
+                                      "Filename",
+                                      "Name of the file",
+                                      NULL,
+                                      G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property
+                 (gobject_class,
+                  PROP_PIXBUF,
+                  g_param_spec_object("pixbuf",
+                                      "Pixbuf",
+                                      "Pixbuf of the image",
+                                      GDK_TYPE_PIXBUF,
+                                      G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
+}
+
+static void
+big_theme_image_init(BigThemeImage *image)
+{
+}
+
+ClutterActor *
+big_theme_image_new_from_file(const char *filename,
+                              guint       border_top,
+                              guint       border_bottom,
+                              guint       border_left,
+                              guint       border_right)
+{
+    ClutterActor *actor;
+
+    actor = g_object_new(BIG_TYPE_THEME_IMAGE,
+                         /* FIXME ClutterCairo requires creating a bogus
+                          * surface with nonzero size
+                          */
+                         "surface-width", 1,
+                         "surface-height", 1,
+                         "filename", filename,
+                         "border-top", border_top,
+                         "border-bottom", border_bottom,
+                         "border-left", border_left,
+                         "border-right", border_right,
+                         NULL);
+
+    return actor;
+}
+
+ClutterActor *
+big_theme_image_new_from_pixbuf(GdkPixbuf  *pixbuf,
+                                guint       border_top,
+                                guint       border_bottom,
+                                guint       border_left,
+                                guint       border_right)
+{
+    ClutterActor *actor;
+
+    actor = g_object_new(BIG_TYPE_THEME_IMAGE,
+                         /* FIXME ClutterCairo requires creating a bogus
+                          * surface with nonzero size
+                          */
+                         "surface-width", 1,
+                         "surface-height", 1,
+                         "pixbuf", pixbuf,
+                         "border-top", border_top,
+                         "border-bottom", border_bottom,
+                         "border-left", border_left,
+                         "border-right", border_right,
+                         NULL);
+
+    return actor;
+}
+

Added: trunk/src/big/theme-image.h
==============================================================================
--- (empty file)
+++ trunk/src/big/theme-image.h	Mon Dec  1 23:01:52 2008
@@ -0,0 +1,39 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* Copyright 2008 litl, LLC. All Rights Reserved. */
+
+#ifndef __BIG_THEME_IMAGE_H__
+#define __BIG_THEME_IMAGE_H__
+
+#include <glib-object.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define BIG_TYPE_THEME_IMAGE            (big_theme_image_get_type ())
+#define BIG_THEME_IMAGE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), BIG_TYPE_THEME_IMAGE, BigThemeImage))
+#define BIG_THEME_IMAGE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  BIG_TYPE_THEME_IMAGE, BigThemeImageClass))
+#define BIG_IS_THEME_IMAGE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BIG_TYPE_THEME_IMAGE))
+#define BIG_IS_THEME_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  BIG_TYPE_THEME_IMAGE))
+#define BIG_THEME_IMAGE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  BIG_TYPE_THEME_IMAGE, BigThemeImageClass))
+
+typedef struct BigThemeImage      BigThemeImage;
+typedef struct BigThemeImageClass BigThemeImageClass;
+
+GType          big_theme_image_get_type           (void) G_GNUC_CONST;
+
+ClutterActor * big_theme_image_new_from_file      (const gchar *filename,
+                                                   guint        border_top,
+                                                   guint        border_bottom,
+                                                   guint        border_left,
+                                                   guint        border_right);
+
+ClutterActor * big_theme_image_new_from_pixbuf    (GdkPixbuf   *pixbuf,
+                                                   guint        border_top,
+                                                   guint        border_bottom,
+                                                   guint        border_left,
+                                                   guint        border_right);
+
+G_END_DECLS
+
+#endif  /* __BIG_THEME_IMAGE_H__ */



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