[clutter] Add ClutterGridLayout



commit 1eb869ec8f893cfc9cd03576335bfac173f8fa3c
Author: Bastian Winkler <buz netbuz org>
Date:   Sun Jun 3 05:02:31 2012 +0200

    Add ClutterGridLayout
    
    ClutterGridLayout is port of GtkGrid to a Clutter layout manager. All
    the logic is taken from gtkgrid.c, so all the credits should go to
    Matthias Clasen for writing this nice piece of code.
    
    ClutterGridLayout supports adding children with it's own methods
    GridLayout.attach() and GridLayout.attach_next_to() as well as
    Actor.add_child() and friends. The latter adds children in a similar
    fashion to ClutterBoxLayout
    
    https://bugzilla.gnome.org/show_bug.cgi?id=677372

 clutter/Makefile.am                        |    2 +
 clutter/clutter-enums.h                    |   18 +
 clutter/clutter-grid-layout.c              | 2199 ++++++++++++++++++++++++++++
 clutter/clutter-grid-layout.h              |  161 ++
 clutter/clutter.h                          |    1 +
 clutter/clutter.symbols                    |   18 +
 doc/reference/clutter/clutter-docs.xml.in  |    1 +
 doc/reference/clutter/clutter-sections.txt |   39 +
 doc/reference/clutter/clutter.types        |    1 +
 9 files changed, 2440 insertions(+), 0 deletions(-)
---
diff --git a/clutter/Makefile.am b/clutter/Makefile.am
index 12b6ca1..660a33d 100644
--- a/clutter/Makefile.am
+++ b/clutter/Makefile.am
@@ -86,6 +86,7 @@ source_h =					\
 	$(srcdir)/clutter-fixed-layout.h	\
 	$(srcdir)/clutter-flow-layout.h		\
 	$(srcdir)/clutter-gesture-action.h 	\
+	$(srcdir)/clutter-grid-layout.h 	\
 	$(srcdir)/clutter-group.h 		\
 	$(srcdir)/clutter-image.h		\
 	$(srcdir)/clutter-input-device.h	\
@@ -164,6 +165,7 @@ source_c = \
 	$(srcdir)/clutter-flatten-effect.c	\
 	$(srcdir)/clutter-flow-layout.c		\
 	$(srcdir)/clutter-gesture-action.c 	\
+	$(srcdir)/clutter-grid-layout.c 	\
 	$(srcdir)/clutter-image.c		\
 	$(srcdir)/clutter-input-device.c	\
 	$(srcdir)/clutter-interval.c            \
diff --git a/clutter/clutter-enums.h b/clutter/clutter-enums.h
index 62ea3bc..7faf2d0 100644
--- a/clutter/clutter-enums.h
+++ b/clutter/clutter-enums.h
@@ -1237,6 +1237,24 @@ typedef enum { /*< prefix=CLUTTER_SCROLL >*/
   CLUTTER_SCROLL_BOTH         = CLUTTER_SCROLL_HORIZONTALLY | CLUTTER_SCROLL_VERTICALLY
 } ClutterScrollMode;
 
+/**
+ * ClutterGridPosition:
+ * @CLUTTER_GRID_POSITION_LEFT: left position
+ * @CLUTTER_GRID_POSITION_RIGHT: right position
+ * @CLUTTER_GRID_POSITION_TOP: top position
+ * @CLUTTER_GRID_POSITION_BOTTOM: bottom position
+ *
+ * Grid position modes.
+ *
+ * Since: 1.12
+ */
+typedef enum {
+  CLUTTER_GRID_POSITION_LEFT,
+  CLUTTER_GRID_POSITION_RIGHT,
+  CLUTTER_GRID_POSITION_TOP,
+  CLUTTER_GRID_POSITION_BOTTOM
+} ClutterGridPosition;
+
 G_END_DECLS
 
 #endif /* __CLUTTER_ENUMS_H__ */
diff --git a/clutter/clutter-grid-layout.c b/clutter/clutter-grid-layout.c
new file mode 100644
index 0000000..b339156
--- /dev/null
+++ b/clutter/clutter-grid-layout.c
@@ -0,0 +1,2199 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ * Copyright (C) 2012 Bastian Winkler <buz netbuz org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Bastian Winkler <buz netbuz org>
+ *
+ * Based on GtkGrid widget by:
+ *   Matthias Clasen
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <math.h>
+
+#include "clutter-grid-layout.h"
+
+#include "clutter-actor-private.h"
+#include "clutter-container.h"
+#include "clutter-debug.h"
+#include "clutter-enum-types.h"
+#include "clutter-layout-meta.h"
+#include "clutter-private.h"
+
+/**
+ * SECTION:clutter-grid-layout
+ * @Short_description: Pack widgets in a rows and columns
+ * @Title: ClutterGridLayout
+ * @See_also: #ClutterTableLayout, #ClutterBoxLayout
+ *
+ * #ClutterGridLayout is a layout manager which arranges its child widgets in
+ * rows and columns. It is a very similar to #ClutterTableLayout and
+ * #ClutterBoxLayout, but it consistently uses #ClutterActor's
+ * alignment and expansion flags instead of custom child properties.
+ *
+ * Children are added using clutter_grid_layout_attach(). They can span
+ * multiple rows or columns. It is also possible to add a child next to an
+ * existing child, using clutter_grid_layout_attach_next_to(). The behaviour of
+ * #ClutterGridLayout when several children occupy the same grid cell is undefined.
+ *
+ * #ClutterGridLayout can be used like a #ClutterBoxLayout by just using
+ * clutter_actor_add_child(), which will place children next to each other in
+ * the direction determined by the #ClutterGridLayout:orientation property.
+ */
+
+#define CLUTTER_TYPE_GRID_CHILD          (clutter_grid_child_get_type ())
+#define CLUTTER_GRID_CHILD(obj)          (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_GRID_CHILD, ClutterGridChild))
+#define CLUTTER_IS_GRID_CHILD(obj)       (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_GRID_CHILD))
+#define CLUTTER_GRID_LAYOUT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_GRID_LAYOUT, ClutterGridLayoutPrivate))
+
+typedef struct _ClutterGridChild        ClutterGridChild;
+typedef struct _ClutterLayoutMetaClass  ClutterGridChildClass;
+
+typedef struct _ClutterGridAttach       ClutterGridAttach;
+typedef struct _ClutterGridLine         ClutterGridLine;
+typedef struct _ClutterGridLines        ClutterGridLines;
+typedef struct _ClutterGridLineData     ClutterGridLineData;
+typedef struct _ClutterGridRequest      ClutterGridRequest;
+
+
+struct _ClutterGridAttach
+{
+  gint pos;
+  gint span;
+};
+
+struct _ClutterGridChild
+{
+  ClutterLayoutMeta parent_instance;
+
+  ClutterGridAttach attach[2];
+};
+
+#define CHILD_LEFT(child)    ((child)->attach[CLUTTER_ORIENTATION_HORIZONTAL].pos)
+#define CHILD_WIDTH(child)   ((child)->attach[CLUTTER_ORIENTATION_HORIZONTAL].span)
+#define CHILD_TOP(child)     ((child)->attach[CLUTTER_ORIENTATION_VERTICAL].pos)
+#define CHILD_HEIGHT(child)  ((child)->attach[CLUTTER_ORIENTATION_VERTICAL].span)
+
+/* A ClutterGridLineData struct contains row/column specific parts
+ * of the grid.
+ */
+struct _ClutterGridLineData
+{
+  gfloat spacing;
+  guint homogeneous : 1;
+};
+
+struct _ClutterGridLayoutPrivate
+{
+  ClutterContainer *container;
+  ClutterOrientation orientation;
+
+  ClutterGridLineData linedata[2];
+};
+
+#define ROWS(priv)    (&(priv)->linedata[CLUTTER_ORIENTATION_HORIZONTAL])
+#define COLUMNS(priv) (&(priv)->linedata[CLUTTER_ORIENTATION_VERTICAL])
+
+/* A ClutterGridLine struct represents a single row or column
+ * during size requests
+ */
+struct _ClutterGridLine
+{
+  gfloat minimum;
+  gfloat natural;
+  gfloat position;
+  gfloat allocation;
+
+  guint need_expand : 1;
+  guint expand      : 1;
+  guint empty       : 1;
+};
+
+struct _ClutterGridLines
+{
+  ClutterGridLine *lines;
+  gint min, max;
+};
+
+struct _ClutterGridRequest
+{
+  ClutterGridLayout *grid;
+  ClutterGridLines lines[2];
+};
+
+enum
+{
+  PROP_0,
+
+  PROP_ORIENTATION,
+  PROP_ROW_SPACING,
+  PROP_COLUMN_SPACING,
+  PROP_ROW_HOMOGENEOUS,
+  PROP_COLUMN_HOMOGENEOUS,
+
+  PROP_LAST
+};
+static GParamSpec *obj_props[PROP_LAST];
+
+enum
+{
+  PROP_CHILD_0,
+
+  PROP_CHILD_LEFT_ATTACH,
+  PROP_CHILD_TOP_ATTACH,
+  PROP_CHILD_WIDTH,
+  PROP_CHILD_HEIGHT,
+
+  PROP_CHILD_LAST
+};
+static GParamSpec *child_props[PROP_CHILD_LAST];
+
+GType clutter_grid_child_get_type (void);
+
+G_DEFINE_TYPE (ClutterGridChild, clutter_grid_child,
+               CLUTTER_TYPE_LAYOUT_META);
+G_DEFINE_TYPE (ClutterGridLayout, clutter_grid_layout,
+               CLUTTER_TYPE_LAYOUT_MANAGER);
+
+
+#define GET_GRID_CHILD(grid, child) \
+  (CLUTTER_GRID_CHILD(clutter_layout_manager_get_child_meta \
+   (CLUTTER_LAYOUT_MANAGER((grid)),\
+    CLUTTER_GRID_LAYOUT((grid))->priv->container,(child))))
+
+static void
+grid_attach (ClutterGridLayout *self,
+             ClutterActor      *actor,
+             gint               left,
+             gint               top,
+             gint               width,
+             gint               height)
+{
+  ClutterGridChild *grid_child;
+
+  grid_child = GET_GRID_CHILD (self, actor);
+
+  CHILD_LEFT (grid_child) = left;
+  CHILD_TOP (grid_child) = top;
+  CHILD_WIDTH (grid_child) = width;
+  CHILD_HEIGHT (grid_child) = height;
+}
+
+/* Find the position 'touching' existing
+ * children. @orientation and @max determine
+ * from which direction to approach (horizontal
+ * + max = right, vertical + !max = top, etc).
+ * @op_pos, @op_span determine the rows/columns
+ * in which the touching has to happen.
+ */
+static gint
+find_attach_position (ClutterGridLayout  *self,
+                      ClutterOrientation  orientation,
+                      gint                op_pos,
+                      gint                op_span,
+                      gboolean            max)
+{
+  ClutterGridLayoutPrivate *priv = self->priv;
+  ClutterGridChild *grid_child;
+  ClutterGridAttach *attach;
+  ClutterGridAttach *opposite;
+  ClutterActorIter iter;
+  ClutterActor *child;
+  gint pos;
+  gboolean hit;
+
+  if (max)
+    pos = -G_MAXINT;
+  else
+    pos = G_MAXINT;
+
+  hit = FALSE;
+
+  if (!priv->container)
+    return -1;
+
+  clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
+  while (clutter_actor_iter_next (&iter, &child))
+    {
+      grid_child = GET_GRID_CHILD (self, child);
+
+      attach = &grid_child->attach[orientation];
+      opposite = &grid_child->attach[1 - orientation];
+
+      /* check if the ranges overlap */
+      if (opposite->pos <= op_pos + op_span && op_pos <= opposite->pos + opposite->span)
+        {
+          hit = TRUE;
+
+          if (max)
+            pos = MAX (pos, attach->pos + attach->span);
+          else
+            pos = MIN (pos, attach->pos);
+        }
+     }
+
+  if (!hit)
+    pos = 0;
+
+  return pos;
+}
+static void
+grid_attach_next_to (ClutterGridLayout   *layout,
+                     ClutterActor        *child,
+                     ClutterActor        *sibling,
+                     ClutterGridPosition  side,
+                     gint                 width,
+                     gint                 height)
+{
+  ClutterGridChild *grid_sibling;
+  gint left, top;
+
+  if (sibling)
+    {
+      grid_sibling = GET_GRID_CHILD (layout, sibling);
+
+      switch (side)
+        {
+        case CLUTTER_GRID_POSITION_LEFT:
+          left = CHILD_LEFT (grid_sibling) - width;
+          top = CHILD_TOP (grid_sibling);
+          break;
+
+        case CLUTTER_GRID_POSITION_RIGHT:
+          left = CHILD_LEFT (grid_sibling) + CHILD_WIDTH (grid_sibling);
+          top = CHILD_TOP (grid_sibling);
+          break;
+
+        case CLUTTER_GRID_POSITION_TOP:
+          left = CHILD_LEFT (grid_sibling);
+          top = CHILD_TOP (grid_sibling) - height;
+          break;
+
+        case CLUTTER_GRID_POSITION_BOTTOM:
+          left = CHILD_LEFT (grid_sibling);
+          top = CHILD_TOP (grid_sibling) + CHILD_HEIGHT (grid_sibling);
+          break;
+
+        default:
+          g_assert_not_reached ();
+        }
+    }
+  else
+    {
+      switch (side)
+        {
+        case CLUTTER_GRID_POSITION_LEFT:
+          left = find_attach_position (layout, CLUTTER_ORIENTATION_HORIZONTAL,
+                                       0, height, FALSE);
+          left -= width;
+          top = 0;
+          break;
+
+        case CLUTTER_GRID_POSITION_RIGHT:
+          left = find_attach_position (layout, CLUTTER_ORIENTATION_HORIZONTAL,
+                                       0, height, TRUE);
+          top = 0;
+          break;
+
+        case CLUTTER_GRID_POSITION_TOP:
+          left = 0;
+          top = find_attach_position (layout, CLUTTER_ORIENTATION_VERTICAL,
+                                      0, width, FALSE);
+          top -= height;
+          break;
+
+        case CLUTTER_GRID_POSITION_BOTTOM:
+          left = 0;
+          top = find_attach_position (layout, CLUTTER_ORIENTATION_VERTICAL,
+                                      0, width, TRUE);
+          break;
+
+        default:
+          g_assert_not_reached ();
+        }
+    }
+
+  grid_attach (layout, child, left, top, width, height);
+}
+
+static void
+clutter_grid_request_update_child_attach (ClutterGridRequest *request,
+                                          ClutterActor       *actor)
+{
+  ClutterGridLayoutPrivate *priv = request->grid->priv;
+  ClutterGridChild *grid_child;
+
+  grid_child = GET_GRID_CHILD (request->grid, actor);
+
+  if (CHILD_LEFT (grid_child) == -1 || CHILD_TOP (grid_child) == -1)
+    {
+      ClutterGridPosition side;
+      ClutterActor *sibling;
+
+      if (priv->orientation == CLUTTER_ORIENTATION_HORIZONTAL)
+        {
+          ClutterTextDirection td;
+          gboolean rtl;
+          ClutterActor *container = CLUTTER_ACTOR (priv->container);
+
+          td = clutter_actor_get_text_direction (container);
+          rtl = (td == CLUTTER_TEXT_DIRECTION_RTL) ? TRUE : FALSE;
+          side = rtl ? CLUTTER_GRID_POSITION_RIGHT : CLUTTER_GRID_POSITION_LEFT;
+        }
+      else
+        {
+          /* XXX: maybe we should also add a :pack-start property to modify
+           * this */
+          side = CLUTTER_GRID_POSITION_BOTTOM;
+        }
+
+      sibling = clutter_actor_get_previous_sibling (actor);
+      grid_attach_next_to (request->grid, actor, sibling, side,
+                           CHILD_WIDTH (grid_child),
+                           CHILD_HEIGHT (grid_child));
+    }
+}
+
+static void
+clutter_grid_request_update_attach (ClutterGridRequest *request)
+{
+  ClutterGridLayoutPrivate *priv = request->grid->priv;
+  ClutterActorIter iter;
+  ClutterActor *child;
+
+  clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
+  while (clutter_actor_iter_next (&iter, &child))
+    clutter_grid_request_update_child_attach (request, child);
+}
+
+/* Calculates the min and max numbers for both orientations.
+ */
+static void
+clutter_grid_request_count_lines (ClutterGridRequest *request)
+{
+  ClutterGridLayoutPrivate *priv = request->grid->priv;
+  ClutterGridChild *grid_child;
+  ClutterGridAttach *attach;
+  ClutterActorIter iter;
+  ClutterActor *child;
+  gint min[2];
+  gint max[2];
+
+  min[0] = min[1] = G_MAXINT;
+  max[0] = max[1] = G_MININT;
+
+  clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
+  while (clutter_actor_iter_next (&iter, &child))
+    {
+      grid_child = GET_GRID_CHILD (request->grid, child);
+      attach = grid_child->attach;
+
+      min[0] = MIN (min[0], attach[0].pos);
+      max[0] = MAX (max[0], attach[0].pos + attach[0].span);
+      min[1] = MIN (min[1], attach[1].pos);
+      max[1] = MAX (max[1], attach[1].pos + attach[1].span);
+    }
+
+  request->lines[0].min = min[0];
+  request->lines[0].max = max[0];
+  request->lines[1].min = min[1];
+  request->lines[1].max = max[1];
+}
+
+/* Sets line sizes to 0 and marks lines as expand
+ * if they have a non-spanning expanding child.
+ */
+static void
+clutter_grid_request_init (ClutterGridRequest *request,
+                           ClutterOrientation  orientation)
+{
+  ClutterGridLayoutPrivate *priv = request->grid->priv;
+  ClutterGridChild *grid_child;
+  ClutterGridAttach *attach;
+  ClutterGridLines *lines;
+  ClutterActorIter iter;
+  ClutterActor *child;
+  gint i;
+
+  lines = &request->lines[orientation];
+
+  for (i = 0; i < lines->max - lines->min; i++)
+    {
+      lines->lines[i].minimum = 0;
+      lines->lines[i].natural = 0;
+      lines->lines[i].expand = FALSE;
+    }
+
+  clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
+  while (clutter_actor_iter_next (&iter, &child))
+    {
+      grid_child = GET_GRID_CHILD (request->grid, child);
+      attach = &grid_child->attach[orientation];
+      if (attach->span == 1 && clutter_actor_needs_expand (child, orientation))
+        lines->lines[attach->pos - lines->min].expand = TRUE;
+    }
+}
+
+/* Sums allocations for lines spanned by child and their spacing.
+ */
+static gfloat
+compute_allocation_for_child (ClutterGridRequest *request,
+                              ClutterActor       *child,
+                              ClutterOrientation  orientation)
+{
+  ClutterGridLayoutPrivate *priv = request->grid->priv;
+  ClutterGridChild *grid_child;
+  ClutterGridLineData *linedata;
+  ClutterGridLines *lines;
+  ClutterGridLine *line;
+  ClutterGridAttach *attach;
+  gfloat size;
+  gint i;
+
+  grid_child = GET_GRID_CHILD (request->grid, child);
+  linedata = &priv->linedata[orientation];
+  lines = &request->lines[orientation];
+  attach = &grid_child->attach[orientation];
+
+  size = (attach->span - 1) * linedata->spacing;
+  for (i = 0; i < attach->span; i++)
+    {
+      line = &lines->lines[attach->pos - lines->min + i];
+      size += line->allocation;
+    }
+
+  return size;
+}
+
+static void
+compute_request_for_child (ClutterGridRequest *request,
+                           ClutterActor       *child,
+                           ClutterOrientation  orientation,
+                           gboolean            contextual,
+                           gfloat             *minimum,
+                           gfloat             *natural)
+{
+  if (contextual)
+    {
+      gfloat size;
+
+      size = compute_allocation_for_child (request, child, 1 - orientation);
+      if (orientation == CLUTTER_ORIENTATION_HORIZONTAL)
+        clutter_actor_get_preferred_width (child, size, minimum, natural);
+      else
+        clutter_actor_get_preferred_height (child, size, minimum, natural);
+    }
+  else
+    {
+      if (orientation == CLUTTER_ORIENTATION_VERTICAL)
+        clutter_actor_get_preferred_width (child, -1, minimum, natural);
+      else
+        clutter_actor_get_preferred_height (child, -1, minimum, natural);
+    }
+}
+
+/* Sets requisition to max. of non-spanning children.
+ * If contextual is TRUE, requires allocations of
+ * lines in the opposite orientation to be set.
+ */
+static void
+clutter_grid_request_non_spanning (ClutterGridRequest *request,
+                                   ClutterOrientation  orientation,
+                                   gboolean            contextual)
+{
+  ClutterGridLayoutPrivate *priv = request->grid->priv;
+  ClutterGridChild *grid_child;
+  ClutterGridAttach *attach;
+  ClutterGridLines *lines;
+  ClutterGridLine *line;
+  ClutterActorIter iter;
+  ClutterActor *child;
+  gfloat minimum;
+  gfloat natural;
+
+  lines = &request->lines[orientation];
+
+  clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
+  while (clutter_actor_iter_next (&iter, &child))
+    {
+      if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+        continue;
+
+      grid_child = GET_GRID_CHILD (request->grid, child);
+
+      attach = &grid_child->attach[orientation];
+      if (attach->span != 1)
+        continue;
+
+      compute_request_for_child (request, child, orientation, contextual, &minimum, &natural);
+
+      line = &lines->lines[attach->pos - lines->min];
+      line->minimum = MAX (line->minimum, minimum);
+      line->natural = MAX (line->natural, natural);
+    }
+}
+
+/* Enforce homogeneous sizes.
+ */
+static void
+clutter_grid_request_homogeneous (ClutterGridRequest *request,
+                                  ClutterOrientation  orientation)
+{
+  ClutterGridLayoutPrivate *priv = request->grid->priv;
+  ClutterGridLineData *linedata;
+  ClutterGridLines *lines;
+  gfloat minimum, natural;
+  gint i;
+
+  linedata = &priv->linedata[orientation];
+  lines = &request->lines[orientation];
+
+  if (!linedata->homogeneous)
+    return;
+
+  minimum = 0.0f;
+  natural = 0.0f;
+
+  for (i = 0; i < lines->max - lines->min; i++)
+    {
+      minimum = MAX (minimum, lines->lines[i].minimum);
+      natural = MAX (natural, lines->lines[i].natural);
+    }
+
+  for (i = 0; i < lines->max - lines->min; i++)
+    {
+      lines->lines[i].minimum = minimum;
+      lines->lines[i].natural = natural;
+    }
+}
+
+/* Deals with spanning children.
+ * Requires expand fields of lines to be set for
+ * non-spanning children.
+ */
+static void
+clutter_grid_request_spanning (ClutterGridRequest *request,
+                               ClutterOrientation  orientation,
+                               gboolean            contextual)
+{
+  ClutterGridLayoutPrivate *priv = request->grid->priv;
+  ClutterGridChild *grid_child;
+  ClutterActor *child;
+  ClutterActorIter iter;
+  ClutterGridAttach *attach;
+  ClutterGridLineData *linedata;
+  ClutterGridLines *lines;
+  ClutterGridLine *line;
+  gfloat minimum;
+  gfloat natural;
+  gint span_minimum;
+  gint span_natural;
+  gint span_expand;
+  gboolean force_expand;
+  gint extra;
+  gint expand;
+  gint line_extra;
+  gint i;
+
+  linedata = &priv->linedata[orientation];
+  lines = &request->lines[orientation];
+
+  clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
+  while (clutter_actor_iter_next (&iter, &child))
+    {
+      if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+        continue;
+
+      grid_child = GET_GRID_CHILD (request->grid, child);
+
+      attach = &grid_child->attach[orientation];
+      if (attach->span == 1)
+        continue;
+
+      compute_request_for_child (request, child, orientation, contextual,
+                                 &minimum, &natural);
+
+      span_minimum = (attach->span - 1) * linedata->spacing;
+      span_natural = (attach->span - 1) * linedata->spacing;
+      span_expand = 0;
+      force_expand = FALSE;
+      for (i = 0; i < attach->span; i++)
+        {
+          line = &lines->lines[attach->pos - lines->min + i];
+          span_minimum += line->minimum;
+          span_natural += line->natural;
+          if (line->expand)
+            span_expand += 1;
+        }
+      if (span_expand == 0)
+        {
+          span_expand = attach->span;
+          force_expand = TRUE;
+        }
+
+      /* If we need to request more space for this child to fill
+       * its requisition, then divide up the needed space amongst the
+       * lines it spans, favoring expandable lines if any.
+       *
+       * When doing homogeneous allocation though, try to keep the
+       * line allocations even, since we're going to force them to
+       * be the same anyway, and we don't want to introduce unnecessary
+       * extra space.
+       */
+      if (span_minimum < minimum)
+        {
+          if (linedata->homogeneous)
+            {
+              gint total, m;
+
+              total = minimum - (attach->span - 1) * linedata->spacing;
+              m = total / attach->span + (total % attach->span ? 1 : 0);
+              for (i = 0; i < attach->span; i++)
+                {
+                  line = &lines->lines[attach->pos - lines->min + i];
+                  line->minimum = MAX(line->minimum, m);
+                }
+            }
+          else
+            {
+              extra = minimum - span_minimum;
+              expand = span_expand;
+              for (i = 0; i < attach->span; i++)
+                {
+                  line = &lines->lines[attach->pos - lines->min + i];
+                  if (force_expand || line->expand)
+                    {
+                      line_extra = extra / expand;
+                      line->minimum += line_extra;
+                      extra -= line_extra;
+                      expand -= 1;
+                    }
+                }
+            }
+        }
+
+      if (span_natural < natural)
+        {
+          if (linedata->homogeneous)
+            {
+              gint total, n;
+
+              total = natural - (attach->span - 1) * linedata->spacing;
+              n = total / attach->span + (total % attach->span ? 1 : 0);
+              for (i = 0; i < attach->span; i++)
+                {
+                  line = &lines->lines[attach->pos - lines->min + i];
+                  line->natural = MAX(line->natural, n);
+                }
+            }
+          else
+            {
+              extra = natural - span_natural;
+              expand = span_expand;
+              for (i = 0; i < attach->span; i++)
+                {
+                  line = &lines->lines[attach->pos - lines->min + i];
+                  if (force_expand || line->expand)
+                    {
+                      line_extra = extra / expand;
+                      line->natural += line_extra;
+                      extra -= line_extra;
+                      expand -= 1;
+                    }
+                }
+            }
+        }
+    }
+}
+
+/* Marks empty and expanding lines and counts them.
+ */
+static void
+clutter_grid_request_compute_expand (ClutterGridRequest *request,
+                                     ClutterOrientation  orientation,
+                                     gint               *nonempty_lines,
+                                     gint               *expand_lines)
+{
+  ClutterGridLayoutPrivate *priv = request->grid->priv;
+  ClutterGridChild *grid_child;
+  ClutterGridAttach *attach;
+  ClutterActorIter iter;
+  ClutterActor *child;
+  gint i;
+  ClutterGridLines *lines;
+  ClutterGridLine *line;
+  gboolean has_expand;
+  gint expand;
+  gint empty;
+
+  lines = &request->lines[orientation];
+
+  for (i = 0; i < lines->max - lines->min; i++)
+    {
+      lines->lines[i].need_expand = FALSE;
+      lines->lines[i].expand = FALSE;
+      lines->lines[i].empty = TRUE;
+    }
+
+  clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
+  while (clutter_actor_iter_next (&iter, &child))
+    {
+      if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+        continue;
+
+      grid_child = GET_GRID_CHILD (request->grid, child);
+
+      attach = &grid_child->attach[orientation];
+      if (attach->span != 1)
+        continue;
+
+      line = &lines->lines[attach->pos - lines->min];
+      line->empty = FALSE;
+      if (clutter_actor_needs_expand (child, orientation))
+        line->expand = TRUE;
+    }
+
+
+  clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
+  while (clutter_actor_iter_next (&iter, &child))
+    {
+      if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+        continue;
+
+      grid_child = GET_GRID_CHILD (request->grid, child);
+
+      attach = &grid_child->attach[orientation];
+      if (attach->span == 1)
+        continue;
+
+      has_expand = FALSE;
+      for (i = 0; i < attach->span; i++)
+        {
+          line = &lines->lines[attach->pos - lines->min + i];
+          line->empty = FALSE;
+          if (line->expand)
+            has_expand = TRUE;
+        }
+
+      if (!has_expand && clutter_actor_needs_expand (child, orientation))
+        {
+          for (i = 0; i < attach->span; i++)
+            {
+              line = &lines->lines[attach->pos - lines->min + i];
+              line->need_expand = TRUE;
+            }
+        }
+    }
+
+  empty = 0;
+  expand = 0;
+  for (i = 0; i < lines->max - lines->min; i++)
+    {
+      line = &lines->lines[i];
+
+      if (line->need_expand)
+        line->expand = TRUE;
+
+      if (line->empty)
+        empty += 1;
+
+      if (line->expand)
+        expand += 1;
+    }
+
+  if (nonempty_lines)
+    *nonempty_lines = lines->max - lines->min - empty;
+
+  if (expand_lines)
+    *expand_lines = expand;
+}
+
+/* Sums the minimum and natural fields of lines and their spacing.
+ */
+static void
+clutter_grid_request_sum (ClutterGridRequest *request,
+                          ClutterOrientation  orientation,
+                          gfloat             *minimum,
+                          gfloat             *natural)
+{
+  ClutterGridLayoutPrivate *priv = request->grid->priv;
+  ClutterGridLineData *linedata;
+  ClutterGridLines *lines;
+  gint i;
+  gfloat min, nat;
+  gint nonempty;
+
+  clutter_grid_request_compute_expand (request, orientation, &nonempty, NULL);
+
+  linedata = &priv->linedata[orientation];
+  lines = &request->lines[orientation];
+
+  min = 0;
+  nat = 0;
+  if (nonempty > 0)
+    {
+      min = (nonempty - 1) * linedata->spacing;
+      nat = (nonempty - 1) * linedata->spacing;
+    }
+
+  for (i = 0; i < lines->max - lines->min; i++)
+    {
+      min += lines->lines[i].minimum;
+      nat += lines->lines[i].natural;
+    }
+
+  if (minimum)
+    *minimum = min;
+
+  if (natural)
+    *natural = nat;
+}
+
+/* Computes minimum and natural fields of lines.
+ * When contextual is TRUE, requires allocation of
+ * lines in the opposite orientation to be set.
+ */
+static void
+clutter_grid_request_run (ClutterGridRequest *request,
+                          ClutterOrientation  orientation,
+                          gboolean            contextual)
+{
+  clutter_grid_request_init (request, orientation);
+  clutter_grid_request_non_spanning (request, orientation, contextual);
+  clutter_grid_request_homogeneous (request, orientation);
+  clutter_grid_request_spanning (request, orientation, contextual);
+  clutter_grid_request_homogeneous (request, orientation);
+}
+
+typedef struct _RequestedSize
+{
+  gpointer data;
+
+  gfloat minimum_size;
+  gfloat natural_size;
+} RequestedSize;
+
+
+/* Pulled from gtksizerequest.c from Gtk+ */
+static gint
+compare_gap (gconstpointer p1,
+             gconstpointer p2,
+             gpointer      data)
+{
+  RequestedSize *sizes = data;
+  const guint *c1 = p1;
+  const guint *c2 = p2;
+
+  const gint d1 = MAX (sizes[*c1].natural_size -
+                       sizes[*c1].minimum_size,
+                       0);
+  const gint d2 = MAX (sizes[*c2].natural_size -
+                       sizes[*c2].minimum_size,
+                       0);
+
+  gint delta = (d2 - d1);
+
+  if (0 == delta)
+    delta = (*c2 - *c1);
+
+  return delta;
+}
+
+/*
+ * distribute_natural_allocation:
+ * @extra_space: Extra space to redistribute among children after subtracting
+ *   minimum sizes and any child padding from the overall allocation
+ * @n_requested_sizes: Number of requests to fit into the allocation
+ * @sizes: An array of structs with a client pointer and a minimum/natural size
+ *   in the orientation of the allocation.
+ *
+ * Distributes @extra_space to child @sizes by bringing smaller
+ * children up to natural size first.
+ *
+ * The remaining space will be added to the @minimum_size member of the
+ * RequestedSize struct. If all sizes reach their natural size then
+ * the remaining space is returned.
+ *
+ * Returns: The remainder of @extra_space after redistributing space
+ * to @sizes.
+ *
+ * Pulled from gtksizerequest.c from Gtk+
+ */
+static gint
+distribute_natural_allocation (gint           extra_space,
+                               guint          n_requested_sizes,
+                               RequestedSize *sizes)
+{
+  guint *spreading;
+  gint   i;
+
+  g_return_val_if_fail (extra_space >= 0, 0);
+
+  spreading = g_newa (guint, n_requested_sizes);
+
+  for (i = 0; i < n_requested_sizes; i++)
+    spreading[i] = i;
+
+  /* Distribute the container's extra space c_gap. We want to assign
+   * this space such that the sum of extra space assigned to children
+   * (c^i_gap) is equal to c_cap. The case that there's not enough
+   * space for all children to take their natural size needs some
+   * attention. The goals we want to achieve are:
+   *
+   *   a) Maximize number of children taking their natural size.
+   *   b) The allocated size of children should be a continuous
+   *   function of c_gap.  That is, increasing the container size by
+   *   one pixel should never make drastic changes in the distribution.
+   *   c) If child i takes its natural size and child j doesn't,
+   *   child j should have received at least as much gap as child i.
+   *
+   * The following code distributes the additional space by following
+   * these rules.
+   */
+
+  /* Sort descending by gap and position. */
+  g_qsort_with_data (spreading,
+                     n_requested_sizes, sizeof (guint),
+                     compare_gap, sizes);
+
+  /* Distribute available space.
+   * This master piece of a loop was conceived by Behdad Esfahbod.
+   */
+  for (i = n_requested_sizes - 1; extra_space > 0 && i >= 0; --i)
+    {
+      /* Divide remaining space by number of remaining children.
+       * Sort order and reducing remaining space by assigned space
+       * ensures that space is distributed equally.
+       */
+      gint glue = (extra_space + i) / (i + 1);
+      gint gap = sizes[(spreading[i])].natural_size
+               - sizes[(spreading[i])].minimum_size;
+
+      gint extra = MIN (glue, gap);
+
+      sizes[spreading[i]].minimum_size += extra;
+
+      extra_space -= extra;
+    }
+
+  return extra_space;
+}
+
+/* Requires that the minimum and natural fields of lines
+ * have been set, computes the allocation field of lines
+ * by distributing total_size among lines.
+ */
+static void
+clutter_grid_request_allocate (ClutterGridRequest *request,
+                               ClutterOrientation  orientation,
+                               gfloat              total_size)
+{
+  ClutterGridLayoutPrivate *priv = request->grid->priv;
+  ClutterGridLineData *linedata;
+  ClutterGridLines *lines;
+  ClutterGridLine *line;
+  gint nonempty;
+  gint expand;
+  gint i, j;
+  RequestedSize *sizes;
+  gint extra;
+  gint rest;
+  gint size;
+
+  clutter_grid_request_compute_expand (request, orientation, &nonempty, &expand);
+
+  if (nonempty == 0)
+    return;
+
+  linedata = &priv->linedata[orientation];
+  lines = &request->lines[orientation];
+
+  size = total_size - (nonempty - 1) * linedata->spacing;
+
+  if (linedata->homogeneous)
+    {
+      extra = size / nonempty;
+      rest = size % nonempty;
+
+      for (i = 0; i < lines->max - lines->min; i++)
+        {
+          line = &lines->lines[i];
+          if (line->empty)
+            continue;
+
+          line->allocation = extra;
+          if (rest > 0)
+            {
+              line->allocation += 1;
+              rest -= 1;
+            }
+        }
+    }
+  else
+    {
+      sizes = g_newa (RequestedSize, nonempty);
+
+      j = 0;
+      for (i = 0; i < lines->max - lines->min; i++)
+        {
+          line = &lines->lines[i];
+          if (line->empty)
+            continue;
+
+          size -= line->minimum;
+
+          sizes[j].minimum_size = line->minimum;
+          sizes[j].natural_size = line->natural;
+          sizes[j].data = line;
+          j++;
+        }
+
+      size = distribute_natural_allocation (MAX (0, size), nonempty, sizes);
+
+      if (expand > 0)
+        {
+          extra = size / expand;
+          rest = size % expand;
+        }
+      else
+        {
+          extra = 0;
+          rest = 0;
+        }
+
+      j = 0;
+      for (i = 0; i < lines->max - lines->min; i++)
+        {
+          line = &lines->lines[i];
+          if (line->empty)
+            continue;
+
+          g_assert (line == sizes[j].data);
+
+          line->allocation = sizes[j].minimum_size;
+          if (line->expand)
+            {
+              line->allocation += extra;
+              if (rest > 0)
+                {
+                  line->allocation += 1;
+                  rest -= 1;
+                }
+            }
+
+          j++;
+        }
+    }
+}
+
+/* Computes the position fields from allocation and spacing.
+ */
+static void
+clutter_grid_request_position (ClutterGridRequest *request,
+                               ClutterOrientation  orientation)
+{
+  ClutterGridLayoutPrivate *priv = request->grid->priv;
+  ClutterGridLineData *linedata;
+  ClutterGridLines *lines;
+  ClutterGridLine *line;
+  gfloat position;
+  gint i;
+
+  linedata = &priv->linedata[orientation];
+  lines = &request->lines[orientation];
+
+  position = 0.f;
+  for (i = 0; i < lines->max - lines->min; i++)
+    {
+      line = &lines->lines[i];
+      if (!line->empty)
+        {
+          line->position = position;
+          position += line->allocation + linedata->spacing;
+        }
+    }
+}
+
+static void
+clutter_grid_child_set_property (GObject      *gobject,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  ClutterGridChild *grid_child = CLUTTER_GRID_CHILD (gobject);
+  ClutterLayoutManager *manager;
+
+  manager = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (gobject));
+
+  switch (prop_id)
+    {
+    case PROP_CHILD_LEFT_ATTACH:
+      CHILD_LEFT (grid_child) = g_value_get_int (value);
+      clutter_layout_manager_layout_changed (manager);
+      break;
+
+    case PROP_CHILD_TOP_ATTACH:
+      CHILD_TOP (grid_child) = g_value_get_int (value);
+      clutter_layout_manager_layout_changed (manager);
+      break;
+
+    case PROP_CHILD_WIDTH:
+      CHILD_WIDTH (grid_child) = g_value_get_int (value);
+      clutter_layout_manager_layout_changed (manager);
+      break;
+
+    case PROP_CHILD_HEIGHT:
+      CHILD_HEIGHT (grid_child) = g_value_get_int (value);
+      clutter_layout_manager_layout_changed (manager);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+clutter_grid_child_get_property (GObject    *gobject,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  ClutterGridChild *grid_child = CLUTTER_GRID_CHILD (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_CHILD_LEFT_ATTACH:
+      g_value_set_int (value, CHILD_LEFT (grid_child));
+      break;
+
+    case PROP_CHILD_TOP_ATTACH:
+      g_value_set_int (value, CHILD_TOP (grid_child));
+      break;
+
+    case PROP_CHILD_WIDTH:
+      g_value_set_int (value, CHILD_WIDTH (grid_child));
+      break;
+
+    case PROP_CHILD_HEIGHT:
+      g_value_set_int (value, CHILD_HEIGHT (grid_child));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+clutter_grid_child_class_init (ClutterGridChildClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->set_property = clutter_grid_child_set_property;
+  gobject_class->get_property = clutter_grid_child_get_property;
+
+  child_props[PROP_CHILD_LEFT_ATTACH] =
+    g_param_spec_int ("left-attach",
+                      P_("Left attachment"),
+                      P_("The column number to attach the left side of the "
+                         "child to"),
+                      -G_MAXINT, G_MAXINT, 0,
+                      G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+
+  child_props[PROP_CHILD_TOP_ATTACH] =
+    g_param_spec_int ("top-attach",
+                      P_("Top attachment"),
+                      P_("The row number to attach the top side of a child "
+                         "widget to"),
+                      -G_MAXINT, G_MAXINT, 0,
+                      G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+
+  child_props[PROP_CHILD_WIDTH] =
+    g_param_spec_int ("width",
+                      P_("Width"),
+                      P_("The number of columns that a child spans"),
+                      -G_MAXINT, G_MAXINT, 1,
+                      G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+
+  child_props[PROP_CHILD_HEIGHT] =
+    g_param_spec_int ("height",
+                      P_("Height"),
+                      P_("The number of rows that a child spans"),
+                      -G_MAXINT, G_MAXINT, 1,
+                      G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+
+  g_object_class_install_properties (gobject_class, PROP_CHILD_LAST,
+                                     child_props);
+}
+
+static void
+clutter_grid_child_init (ClutterGridChild *self)
+{
+  CHILD_LEFT (self) = -1;
+  CHILD_TOP (self) = -1;
+  CHILD_WIDTH (self) = 1;
+  CHILD_HEIGHT (self) = 1;
+}
+
+static void
+clutter_grid_layout_set_container (ClutterLayoutManager *self,
+                                   ClutterContainer     *container)
+{
+  ClutterGridLayoutPrivate *priv = CLUTTER_GRID_LAYOUT (self)->priv;
+  ClutterLayoutManagerClass *parent_class;
+
+  priv->container = container;
+
+  if (priv->container != NULL)
+    {
+      ClutterRequestMode request_mode;
+
+      /* we need to change the :request-mode of the container
+       * to match the orientation
+       */
+      request_mode = priv->orientation == CLUTTER_ORIENTATION_VERTICAL
+                   ? CLUTTER_REQUEST_HEIGHT_FOR_WIDTH
+                   : CLUTTER_REQUEST_WIDTH_FOR_HEIGHT;
+      clutter_actor_set_request_mode (CLUTTER_ACTOR (priv->container),
+                                      request_mode);
+    }
+
+  parent_class = CLUTTER_LAYOUT_MANAGER_CLASS (clutter_grid_layout_parent_class);
+  parent_class->set_container (self, container);
+}
+
+static void
+clutter_grid_layout_get_preferred_width (ClutterLayoutManager *self,
+                                         ClutterContainer     *container,
+                                         gfloat                for_height,
+                                         gfloat               *min_width_p,
+                                         gfloat               *nat_width_p)
+{
+  ClutterGridLayoutPrivate *priv = CLUTTER_GRID_LAYOUT (self)->priv;
+  ClutterGridRequest request;
+  ClutterGridLines *lines;
+
+  if (min_width_p)
+    *min_width_p = 0.0f;
+  if (nat_width_p)
+    *nat_width_p = 0.0f;
+
+  request.grid = CLUTTER_GRID_LAYOUT (self);
+  clutter_grid_request_update_attach (&request);
+  clutter_grid_request_count_lines (&request);
+  lines = &request.lines[priv->orientation];
+  lines->lines = g_newa (ClutterGridLine, lines->max - lines->min);
+  memset (lines->lines, 0, (lines->max - lines->min) * sizeof (ClutterGridLine));
+
+  clutter_grid_request_run (&request, priv->orientation, FALSE);
+  clutter_grid_request_sum (&request, priv->orientation,
+                            min_width_p, nat_width_p);
+}
+
+static void
+clutter_grid_layout_get_preferred_height (ClutterLayoutManager *self,
+                                          ClutterContainer     *container,
+                                          gfloat                for_width,
+                                          gfloat               *min_height_p,
+                                          gfloat               *nat_height_p)
+{
+  ClutterGridLayoutPrivate *priv = CLUTTER_GRID_LAYOUT (self)->priv;
+  ClutterGridRequest request;
+  ClutterGridLines *lines;
+
+  if (min_height_p)
+    *min_height_p = 0.0f;
+  if (nat_height_p)
+    *nat_height_p = 0.0f;
+
+  request.grid = CLUTTER_GRID_LAYOUT (self);
+  clutter_grid_request_update_attach (&request);
+  clutter_grid_request_count_lines (&request);
+  lines = &request.lines[priv->orientation];
+  lines->lines = g_newa (ClutterGridLine, lines->max - lines->min);
+  memset (lines->lines, 0, (lines->max - lines->min) * sizeof (ClutterGridLine));
+
+  clutter_grid_request_run (&request, priv->orientation, FALSE);
+  clutter_grid_request_sum (&request, priv->orientation,
+                            min_height_p, nat_height_p);
+}
+
+static void
+allocate_child (ClutterGridRequest *request,
+                ClutterOrientation  orientation,
+                ClutterGridChild   *child,
+                gfloat             *position,
+                gfloat             *size)
+{
+  ClutterGridLayoutPrivate *priv = request->grid->priv;
+  ClutterGridLineData *linedata;
+  ClutterGridLines *lines;
+  ClutterGridLine *line;
+  ClutterGridAttach *attach;
+  gint i;
+
+  linedata = &priv->linedata[orientation];
+  lines = &request->lines[orientation];
+  attach = &child->attach[orientation];
+
+  *position = lines->lines[attach->pos - lines->min].position;
+
+  *size = (attach->span - 1) * linedata->spacing;
+  for (i = 0; i < attach->span; i++)
+    {
+      line = &lines->lines[attach->pos - lines->min + i];
+      *size += line->allocation;
+    }
+}
+
+#define GET_SIZE(allocation, orientation) \
+  (orientation == CLUTTER_ORIENTATION_HORIZONTAL \
+   ? clutter_actor_box_get_width ((allocation)) \
+   : clutter_actor_box_get_height ((allocation)))
+
+static void
+clutter_grid_layout_allocate (ClutterLayoutManager   *layout,
+                              ClutterContainer       *container,
+                              const ClutterActorBox  *allocation,
+                              ClutterAllocationFlags  flags)
+{
+  ClutterGridLayout *self = CLUTTER_GRID_LAYOUT (layout);
+  ClutterGridLayoutPrivate *priv = self->priv;
+  ClutterGridRequest request;
+  ClutterGridLines *lines;
+  ClutterActorIter iter;
+  ClutterActor *child;
+  gboolean use_animations;
+  ClutterAnimationMode mode;
+  guint duration, delay;
+
+  request.grid = self;
+
+  clutter_grid_request_update_attach (&request);
+  clutter_grid_request_count_lines (&request);
+  lines = &request.lines[0];
+  lines->lines = g_newa (ClutterGridLine, lines->max - lines->min);
+  memset (lines->lines, 0, (lines->max - lines->min) * sizeof (ClutterGridLine));
+  lines = &request.lines[1];
+  lines->lines = g_newa (ClutterGridLine, lines->max - lines->min);
+  memset (lines->lines, 0, (lines->max - lines->min) * sizeof (ClutterGridLine));
+
+  clutter_grid_request_run (&request, 1 - priv->orientation, FALSE);
+  clutter_grid_request_allocate (&request, 1 - priv->orientation, GET_SIZE (allocation, 1 - priv->orientation));
+  clutter_grid_request_run (&request, priv->orientation, TRUE);
+  clutter_grid_request_allocate (&request, priv->orientation, GET_SIZE (allocation, priv->orientation));
+
+  clutter_grid_request_position (&request, 0);
+  clutter_grid_request_position (&request, 1);
+
+  use_animations = clutter_layout_manager_get_easing_state (layout,
+                                                            &mode,
+                                                            &duration,
+                                                            &delay);
+
+  clutter_actor_iter_init (&iter, CLUTTER_ACTOR (container));
+  while (clutter_actor_iter_next (&iter, &child))
+    {
+      ClutterActorBox child_allocation;
+      gfloat x, y, width, height;
+      ClutterGridChild *grid_child;
+
+      if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+        continue;
+
+      grid_child = GET_GRID_CHILD (self, child);
+      allocate_child (&request, CLUTTER_ORIENTATION_HORIZONTAL, grid_child,
+                      &x, &width);
+      allocate_child (&request, CLUTTER_ORIENTATION_VERTICAL, grid_child,
+                      &y, &height);
+      x += allocation->x1;
+      y += allocation->y1;
+      clutter_actor_box_set_origin (&child_allocation, x, y);
+      clutter_actor_box_set_size (&child_allocation, width, height);
+
+      CLUTTER_NOTE (LAYOUT, "Allocation for %s { %.2f, %.2f - %.2f x %.2f }",
+                    _clutter_actor_get_debug_name (child),
+                    x, y, width, height);
+
+      if (use_animations)
+        {
+          clutter_actor_save_easing_state (child);
+          clutter_actor_set_easing_mode (child, mode);
+          clutter_actor_set_easing_duration (child, duration);
+          clutter_actor_set_easing_delay (child, delay);
+        }
+
+      clutter_actor_allocate (child, &child_allocation, flags);
+
+      if (use_animations)
+        clutter_actor_restore_easing_state (child);
+    }
+}
+
+static GType
+clutter_grid_layout_get_child_meta_type (ClutterLayoutManager *self)
+{
+  return CLUTTER_TYPE_GRID_CHILD;
+}
+
+static void
+clutter_grid_layout_set_property (GObject      *gobject,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  ClutterGridLayout *self = CLUTTER_GRID_LAYOUT (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_ORIENTATION:
+      clutter_grid_layout_set_orientation (self, g_value_get_enum (value));
+      break;
+
+    case PROP_ROW_SPACING:
+      clutter_grid_layout_set_row_spacing (self, g_value_get_uint (value));
+      break;
+
+    case PROP_COLUMN_SPACING:
+      clutter_grid_layout_set_column_spacing (self, g_value_get_uint (value));
+      break;
+
+    case PROP_ROW_HOMOGENEOUS:
+      clutter_grid_layout_set_row_homogeneous (self,
+                                               g_value_get_boolean (value));
+      break;
+
+    case PROP_COLUMN_HOMOGENEOUS:
+      clutter_grid_layout_set_column_homogeneous (self,
+                                                  g_value_get_boolean (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+clutter_grid_layout_get_property (GObject    *gobject,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  ClutterGridLayoutPrivate *priv = CLUTTER_GRID_LAYOUT (gobject)->priv;
+
+  switch (prop_id)
+    {
+    case PROP_ORIENTATION:
+      g_value_set_enum (value, priv->orientation);
+      break;
+
+    case PROP_ROW_SPACING:
+      g_value_set_uint (value, COLUMNS (priv)->spacing);
+      break;
+
+    case PROP_COLUMN_SPACING:
+      g_value_set_uint (value, ROWS (priv)->spacing);
+      break;
+
+    case PROP_ROW_HOMOGENEOUS:
+      g_value_set_boolean (value, COLUMNS (priv)->homogeneous);
+      break;
+
+    case PROP_COLUMN_HOMOGENEOUS:
+      g_value_set_boolean (value, ROWS (priv)->homogeneous);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+clutter_grid_layout_class_init (ClutterGridLayoutClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  ClutterLayoutManagerClass *layout_class;
+
+  layout_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (ClutterGridLayoutPrivate));
+
+  object_class->set_property = clutter_grid_layout_set_property;
+  object_class->get_property = clutter_grid_layout_get_property;
+
+  layout_class->set_container = clutter_grid_layout_set_container;
+  layout_class->get_preferred_width = clutter_grid_layout_get_preferred_width;
+  layout_class->get_preferred_height = clutter_grid_layout_get_preferred_height;
+  layout_class->allocate = clutter_grid_layout_allocate;
+  layout_class->get_child_meta_type = clutter_grid_layout_get_child_meta_type;
+
+  /**
+   * ClutterGridLayout:orientation:
+   *
+   * The orientation of the layout, either horizontal or vertical
+   *
+   * Since: 1.12
+   */
+  obj_props[PROP_ORIENTATION] =
+    g_param_spec_enum ("orientation",
+                       P_("Orientation"),
+                       P_("The orientation of the layout"),
+                       CLUTTER_TYPE_ORIENTATION,
+                       CLUTTER_ORIENTATION_HORIZONTAL,
+                       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+
+  /**
+   * ClutterGridLayout:row-spacing:
+   *
+   * The amount of space in pixels between two consecutive rows
+   *
+   * Since: 1.12
+   */
+  obj_props[PROP_ROW_SPACING] =
+    g_param_spec_uint ("row-spacing",
+                       P_("Row spacing"),
+                       P_("The amount of space between two consecutive rows"),
+                       0, G_MAXUINT, 0,
+                       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+
+  /**
+   * ClutterGridLayout:column-spacing:
+   *
+   * The amount of space in pixels between two consecutive columns
+   *
+   * Since: 1.12
+   */
+  obj_props[PROP_COLUMN_SPACING] =
+    g_param_spec_uint ("column-spacing",
+                       P_("Column spacing"),
+                       P_("The amount of space between two consecutive "
+                          "columns"),
+                       0, G_MAXUINT, 0,
+                       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+
+  /**
+   * ClutterGridLayout:row-homogeneous:
+   *
+   * Whether all rows of the layout should have the same height
+   *
+   * Since: 1.12
+   */
+  obj_props[PROP_ROW_HOMOGENEOUS] =
+    g_param_spec_boolean ("row-homogeneous",
+                          P_("Row Homogeneous"),
+                          P_("If TRUE, the rows are all the same height"),
+                          FALSE,
+                          G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+
+  /**
+   * ClutterGridLayout:column-homogeneous:
+   *
+   * Whether all columns of the layout should have the same width
+   *
+   * Since: 1.12
+   */
+  obj_props[PROP_COLUMN_HOMOGENEOUS] =
+    g_param_spec_boolean ("column-homogeneous",
+                          P_("Column Homogeneous"),
+                          P_("If TRUE, the columns are all the same width"),
+                          FALSE,
+                          G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+
+  g_object_class_install_properties (object_class, PROP_LAST, obj_props);
+}
+
+static void
+clutter_grid_layout_init (ClutterGridLayout *self)
+{
+  ClutterGridLayoutPrivate *priv;
+
+  self->priv = priv = CLUTTER_GRID_LAYOUT_GET_PRIVATE (self);
+
+  priv->orientation = CLUTTER_ORIENTATION_HORIZONTAL;
+
+  priv->linedata[0].spacing = 0;
+  priv->linedata[1].spacing = 0;
+
+  priv->linedata[0].homogeneous = FALSE;
+  priv->linedata[1].homogeneous = FALSE;
+}
+
+/**
+ * clutter_grid_layout_new:
+ *
+ * Creates a new #ClutterGridLayout
+ *
+ * Return value: the new #ClutterGridLayout
+ */
+ClutterLayoutManager *
+clutter_grid_layout_new (void)
+{
+  return g_object_new (CLUTTER_TYPE_GRID_LAYOUT, NULL);
+}
+
+/**
+ * clutter_grid_layout_attach:
+ * @layout: a #ClutterGridLayout
+ * @child: the #ClutterActor to add
+ * @left: the column number to attach the left side of @child to
+ * @top: the row number to attach the top side of @child to
+ * @width: the number of columns that @child will span
+ * @height: the number of rows that @child will span
+ *
+ * Adds a widget to the grid.
+ *
+ * The position of @child is determined by @left and @top. The
+ * number of 'cells' that @child will occupy is determined by
+ * @width and @height.
+ *
+ * Since: 1.12
+ */
+void
+clutter_grid_layout_attach (ClutterGridLayout *layout,
+                            ClutterActor      *child,
+                            gint               left,
+                            gint               top,
+                            gint               width,
+                            gint               height)
+{
+  ClutterGridLayoutPrivate *priv;
+
+  g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
+
+  priv = layout->priv;
+
+  if (!priv->container)
+    return;
+
+  grid_attach (layout, child, left, top, width, height);
+  clutter_actor_add_child (CLUTTER_ACTOR (priv->container), child);
+}
+
+/**
+ * clutter_grid_layout_attach_next_to:
+ * @layout: a #ClutterGridLayout
+ * @child: the actor to add
+ * @sibling: (allow-none): the child of @layout that @child will be placed
+ *     next to, or %NULL to place @child at the beginning or end
+ * @side: the side of @sibling that @child is positioned next to
+ * @width: the number of columns that @child will span
+ * @height: the number of rows that @child will span
+ *
+ * Adds a actor to the grid.
+ *
+ * The actor is placed next to @sibling, on the side determined by
+ * @side. When @sibling is %NULL, the actor is placed in row (for
+ * left or right placement) or column 0 (for top or bottom placement),
+ * at the end indicated by @side.
+ *
+ * Attaching widgets labeled [1], [2], [3] with @sibling == %NULL and
+ * @side == %CLUTTER_GRID_POSITION_LEFT yields a layout of [3][2][1].
+ *
+ * Since: 1.12
+ */
+void
+clutter_grid_layout_attach_next_to (ClutterGridLayout   *layout,
+                                    ClutterActor        *child,
+                                    ClutterActor        *sibling,
+                                    ClutterGridPosition  side,
+                                    gint                 width,
+                                    gint                 height)
+{
+  ClutterGridLayoutPrivate *priv;
+
+  g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
+  g_return_if_fail (CLUTTER_IS_ACTOR (child));
+  g_return_if_fail (clutter_actor_get_parent (child) == NULL);
+  g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling));
+  g_return_if_fail (width > 0);
+  g_return_if_fail (height > 0);
+
+  priv = layout->priv;
+
+  if (!priv->container)
+    return;
+
+  grid_attach_next_to (layout, child, sibling, side, width, height);
+  clutter_actor_add_child (CLUTTER_ACTOR (priv->container), child);
+}
+
+/**
+ * clutter_grid_layout_set_orientation:
+ * @layout: a #ClutterGridLayout
+ * @orientation: the orientation of the #ClutterGridLayout
+ *
+ * Sets the orientation of the @layout
+ *
+ * Since: 1.12
+ */
+void
+clutter_grid_layout_set_orientation (ClutterGridLayout *layout,
+                                     ClutterOrientation orientation)
+{
+  ClutterGridLayoutPrivate *priv;
+
+  g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
+
+  priv = layout->priv;
+
+  if (priv->orientation != orientation)
+    {
+      priv->orientation = orientation;
+
+      clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
+      g_object_notify_by_pspec (G_OBJECT (layout), obj_props[PROP_ORIENTATION]);
+    }
+}
+
+/**
+ * clutter_grid_layout_get_child_at:
+ * @layout: a #ClutterGridLayout
+ * @left: the left edge of the cell
+ * @top: the top edge of the cell
+ *
+ * Gets the child of @layout whose area covers the grid
+ * cell whose upper left corner is at @left, @top.
+ *
+ * Returns: (transfer none): the child at the given position, or %NULL
+ *
+ * Since: 1.12
+ */
+ClutterActor *
+clutter_grid_layout_get_child_at (ClutterGridLayout *layout,
+                                  gint               left,
+                                  gint               top)
+{
+  ClutterGridLayoutPrivate *priv;
+  ClutterGridChild *grid_child;
+  ClutterActorIter iter;
+  ClutterActor *child;
+
+  g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), NULL);
+
+  priv = layout->priv;
+
+  if (!priv->container)
+    return NULL;
+
+  clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
+  while (clutter_actor_iter_next (&iter, &child))
+    {
+      grid_child = GET_GRID_CHILD (layout, child);
+
+      if (CHILD_LEFT (grid_child) <= left &&
+          CHILD_LEFT (grid_child) + CHILD_WIDTH (grid_child) > left &&
+          CHILD_TOP (grid_child) <= top &&
+          CHILD_TOP (grid_child) + CHILD_HEIGHT (grid_child) > top)
+        return child;
+    }
+
+  return NULL;
+}
+
+/**
+ * clutter_grid_layout_insert_row:
+ * @layout: a #ClutterGridLayout
+ * @position: the position to insert the row at
+ *
+ * Inserts a row at the specified position.
+ *
+ * Children which are attached at or below this position
+ * are moved one row down. Children which span across this
+ * position are grown to span the new row.
+ *
+ * Since: 1.12
+ */
+void
+clutter_grid_layout_insert_row (ClutterGridLayout *layout,
+                                gint               position)
+{
+  ClutterGridLayoutPrivate *priv;
+  ClutterGridChild *grid_child;
+  ClutterActorIter iter;
+  ClutterActor *child;
+  gint top, height;
+
+  g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
+
+  priv = layout->priv;
+
+  if (!priv->container)
+    return;
+
+  clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
+  while (clutter_actor_iter_next (&iter, &child))
+    {
+      grid_child = GET_GRID_CHILD (layout, child);
+
+      top = CHILD_TOP (grid_child);
+      height = CHILD_HEIGHT (grid_child);
+
+      if (top >= position)
+        {
+          CHILD_TOP (grid_child) = top + 1;
+          g_object_notify_by_pspec (G_OBJECT (grid_child),
+                                    child_props[PROP_CHILD_TOP_ATTACH]);
+        }
+      else if (top + height > position)
+        {
+          CHILD_HEIGHT (grid_child) = height + 1;
+          g_object_notify_by_pspec (G_OBJECT (grid_child),
+                                    child_props[PROP_CHILD_HEIGHT]);
+        }
+    }
+  clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
+}
+
+/**
+ * clutter_grid_layout_insert_column:
+ * @layout: a #ClutterGridLayout
+ * @position: the position to insert the column at
+ *
+ * Inserts a column at the specified position.
+ *
+ * Children which are attached at or to the right of this position
+ * are moved one column to the right. Children which span across this
+ * position are grown to span the new column.
+ *
+ * Since: 1.12
+ */
+void
+clutter_grid_layout_insert_column (ClutterGridLayout *layout,
+                                   gint               position)
+{
+  ClutterGridLayoutPrivate *priv;
+  ClutterGridChild *grid_child;
+  ClutterActorIter iter;
+  ClutterActor *child;
+  gint left, width;
+
+  g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
+
+  priv = layout->priv;
+
+  if (!priv->container)
+    return;
+
+  clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
+  while (clutter_actor_iter_next (&iter, &child))
+    {
+      grid_child = GET_GRID_CHILD (layout, child);
+
+      left = CHILD_LEFT (grid_child);
+      width = CHILD_WIDTH (grid_child);
+
+      if (left >= position)
+        {
+          CHILD_LEFT (grid_child) = left + 1;
+          g_object_notify_by_pspec (G_OBJECT (grid_child),
+                                    child_props[PROP_CHILD_LEFT_ATTACH]);
+        }
+      else if (left + width > position)
+        {
+          CHILD_WIDTH (grid_child) = width + 1;
+          g_object_notify_by_pspec (G_OBJECT (grid_child),
+                                    child_props[PROP_CHILD_WIDTH]);
+        }
+    }
+  clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
+}
+
+/**
+ * clutter_grid_layout_insert_next_to:
+ * @layout: a #ClutterGridLayout
+ * @sibling: the child of @layout that the new row or column will be
+ *     placed next to
+ * @side: the side of @sibling that @child is positioned next to
+ *
+ * Inserts a row or column at the specified position.
+ *
+ * The new row or column is placed next to @sibling, on the side
+ * determined by @side. If @side is %CLUTTER_GRID_POSITION_LEFT or
+ * %CLUTTER_GRID_POSITION_BOTTOM, a row is inserted. If @side is
+ * %CLUTTER_GRID_POSITION_LEFT of %CLUTTER_GRID_POSITION_RIGHT,
+ * a column is inserted.
+ *
+ * Since: 1.12
+ */
+void
+clutter_grid_layout_insert_next_to (ClutterGridLayout   *layout,
+                                    ClutterActor        *sibling,
+                                    ClutterGridPosition  side)
+{
+  ClutterGridChild *grid_child;
+
+  g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
+  g_return_if_fail (CLUTTER_IS_ACTOR (sibling));
+
+  grid_child = GET_GRID_CHILD (layout, sibling);
+
+  switch (side)
+    {
+    case CLUTTER_GRID_POSITION_LEFT:
+      clutter_grid_layout_insert_column (layout, CHILD_LEFT (grid_child));
+      break;
+
+    case CLUTTER_GRID_POSITION_RIGHT:
+      clutter_grid_layout_insert_column (layout, CHILD_LEFT (grid_child) +
+                                         CHILD_WIDTH (grid_child));
+      break;
+
+    case CLUTTER_GRID_POSITION_TOP:
+      clutter_grid_layout_insert_row (layout, CHILD_TOP (grid_child));
+      break;
+
+    case CLUTTER_GRID_POSITION_BOTTOM:
+      clutter_grid_layout_insert_row (layout, CHILD_TOP (grid_child) +
+                                      CHILD_HEIGHT (grid_child));
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+/**
+ * clutter_grid_layout_get_orientation:
+ * @layout: a #ClutterGridLayout
+ *
+ * Retrieves the orientation of the @layout.
+ *
+ * Return value: the orientation of the layout
+ *
+ * Since: 1.12
+ */
+ClutterOrientation
+clutter_grid_layout_get_orientation (ClutterGridLayout *layout)
+{
+  g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout),
+                        CLUTTER_ORIENTATION_HORIZONTAL);
+
+  return layout->priv->orientation;
+}
+
+/**
+ * clutter_grid_layout_set_row_spacing:
+ * @layout: a #ClutterGridLayout
+ * @spacing: the spacing between rows of the layout, in pixels
+ *
+ * Sets the spacing between rows of @layout
+ *
+ * Since: 1.12
+ */
+void
+clutter_grid_layout_set_row_spacing (ClutterGridLayout *layout,
+                                     guint              spacing)
+{
+  ClutterGridLayoutPrivate *priv;
+
+  g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
+
+  priv = layout->priv;
+
+  if (COLUMNS (priv)->spacing != spacing)
+    {
+      COLUMNS (priv)->spacing = spacing;
+
+      clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
+      g_object_notify_by_pspec (G_OBJECT (layout),
+                                obj_props[PROP_ROW_SPACING]);
+    }
+}
+
+/**
+ * clutter_grid_layout_get_row_spacing:
+ * @layout: a #ClutterGridLayout
+ *
+ * Retrieves the spacing set using clutter_grid_layout_set_row_spacing()
+ *
+ * Return value: the spacing between rows of @layout
+ *
+ * Since: 1.12
+ */
+guint
+clutter_grid_layout_get_row_spacing (ClutterGridLayout *layout)
+{
+  ClutterGridLayoutPrivate *priv;
+
+  g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), 0);
+
+  priv = layout->priv;
+
+  return COLUMNS (priv)->spacing;
+}
+
+/**
+ * clutter_grid_layout_set_column_spacing:
+ * @layout: a #ClutterGridLayout
+ * @spacing: the spacing between columns of the layout, in pixels
+ *
+ * Sets the spacing between columns of @layout
+ *
+ * Since: 1.12
+ */
+void
+clutter_grid_layout_set_column_spacing (ClutterGridLayout *layout,
+                                        guint spacing)
+{
+  ClutterGridLayoutPrivate *priv;
+
+  g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
+
+  priv = layout->priv;
+
+  if (ROWS (priv)->spacing != spacing)
+    {
+      ROWS (priv)->spacing = spacing;
+
+      clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
+      g_object_notify_by_pspec (G_OBJECT (layout),
+                                obj_props[PROP_COLUMN_SPACING]);
+    }
+}
+
+/**
+ * clutter_grid_layout_get_column_spacing:
+ * @layout: a #ClutterGridLayout
+ *
+ * Retrieves the spacing set using clutter_grid_layout_set_column_spacing()
+ *
+ * Return value: the spacing between coluns of @layout
+ *
+ * Since: 1.12
+ */
+guint
+clutter_grid_layout_get_column_spacing (ClutterGridLayout *layout)
+{
+  ClutterGridLayoutPrivate *priv;
+
+  g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), 0);
+
+  priv = layout->priv;
+
+  return ROWS (priv)->spacing;
+}
+
+/**
+ * clutter_grid_layout_set_column_homogeneous:
+ * @layout: a #ClutterGridLayout
+ * @homogeneous: %TRUE to make columns homogeneous
+ *
+ * Sets whether all columns of @layout will have the same width.
+ *
+ * Since: 1.12
+ */
+void
+clutter_grid_layout_set_column_homogeneous (ClutterGridLayout *layout,
+                                            gboolean           homogeneous)
+{
+  ClutterGridLayoutPrivate *priv;
+
+  g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
+
+  priv = layout->priv;
+
+  if (ROWS (priv)->homogeneous != homogeneous)
+    {
+      ROWS (priv)->homogeneous = homogeneous;
+
+      clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
+      g_object_notify_by_pspec (G_OBJECT (layout),
+                                obj_props[PROP_COLUMN_HOMOGENEOUS]);
+    }
+}
+
+/**
+ * clutter_grid_layout_get_column_homogeneous:
+ * @layout: a #ClutterGridLayout
+ *
+ * Returns whether all columns of @layout have the same width.
+ *
+ * Returns: whether all columns of @layout have the same width.
+ */
+gboolean
+clutter_grid_layout_get_column_homogeneous (ClutterGridLayout *layout)
+{
+  ClutterGridLayoutPrivate *priv;
+
+  g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), FALSE);
+
+  priv = layout->priv;
+
+  return ROWS (priv)->homogeneous;
+}
+
+/**
+ * clutter_grid_layout_set_row_homogeneous:
+ * @layout: a #ClutterGridLayout
+ * @homogeneous: %TRUE to make rows homogeneous
+ *
+ * Sets whether all rows of @layout will have the same height.
+ *
+ * Since: 1.12
+ */
+void
+clutter_grid_layout_set_row_homogeneous (ClutterGridLayout *layout,
+                                         gboolean           homogeneous)
+{
+  ClutterGridLayoutPrivate *priv;
+
+  g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
+
+  priv = layout->priv;
+
+  if (COLUMNS (priv)->homogeneous != homogeneous)
+    {
+      COLUMNS (priv)->homogeneous = homogeneous;
+
+      clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
+      g_object_notify_by_pspec (G_OBJECT (layout),
+                                obj_props[PROP_ROW_HOMOGENEOUS]);
+    }
+}
+
+/**
+ * clutter_grid_layout_get_row_homogeneous:
+ * @layout: a #ClutterGridLayout
+ *
+ * Returns whether all rows of @layout have the same height.
+ *
+ * Returns: whether all rows of @layout have the same height.
+ *
+ * Since: 1.12
+ */
+gboolean
+clutter_grid_layout_get_row_homogeneous (ClutterGridLayout *layout)
+{
+  ClutterGridLayoutPrivate *priv;
+
+  g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), FALSE);
+
+  priv = layout->priv;
+
+  return COLUMNS (priv)->homogeneous;
+}
diff --git a/clutter/clutter-grid-layout.h b/clutter/clutter-grid-layout.h
new file mode 100644
index 0000000..1c7644a
--- /dev/null
+++ b/clutter/clutter-grid-layout.h
@@ -0,0 +1,161 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ * Copyright (C) 2012 Bastian Winkler <buz netbuz org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Bastian Winkler <buz netbuz org>
+ *
+ * Based on GtkGrid widget by:
+ *   Matthias Clasen
+ */
+
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only <clutter/clutter.h> can be included directly."
+#endif
+
+#ifndef __CLUTTER_GRID_LAYOUT_H__
+#define __CLUTTER_GRID_LAYOUT_H__
+
+#include <clutter/clutter-layout-manager.h>
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_GRID_LAYOUT                 (clutter_grid_layout_get_type ())
+#define CLUTTER_GRID_LAYOUT(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_GRID_LAYOUT, ClutterGridLayout))
+#define CLUTTER_IS_GRID_LAYOUT(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_GRID_LAYOUT))
+#define CLUTTER_GRID_LAYOUT_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_GRID_LAYOUT, ClutterGridLayoutClass))
+#define CLUTTER_IS_GRID_LAYOUT_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_GRID_LAYOUT))
+#define CLUTTER_GRID_LAYOUT_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_GRID_LAYOUT, ClutterGridLayoutClass))
+
+typedef struct _ClutterGridLayout                ClutterGridLayout;
+typedef struct _ClutterGridLayoutPrivate         ClutterGridLayoutPrivate;
+typedef struct _ClutterGridLayoutClass           ClutterGridLayoutClass;
+
+/**
+ * ClutterGridLayout:
+ *
+ * The #ClutterGridLayout structure contains only private data
+ * and should be accessed using the provided API
+ *
+ * Since: 1.12
+ */
+struct _ClutterGridLayout
+{
+  /*< private >*/
+  ClutterLayoutManager parent_instance;
+
+  ClutterGridLayoutPrivate *priv;
+};
+
+/**
+ * ClutterGridLayoutClass:
+ *
+ * The #ClutterGridLayoutClass structure contains only private
+ * data and should be accessed using the provided API
+ *
+ * Since: 1.12
+ */
+struct _ClutterGridLayoutClass
+{
+  /*< private >*/
+  ClutterLayoutManagerClass parent_class;
+
+  gpointer _padding[8];
+};
+
+CLUTTER_AVAILABLE_IN_1_12
+GType clutter_grid_layout_get_type (void) G_GNUC_CONST;
+
+CLUTTER_AVAILABLE_IN_1_12
+ClutterLayoutManager *  clutter_grid_layout_new                 (void);
+
+CLUTTER_AVAILABLE_IN_1_12
+void                clutter_grid_layout_attach                      (ClutterGridLayout *layout,
+                                                                     ClutterActor      *child,
+                                                                     gint               left,
+                                                                     gint               top,
+                                                                     gint               width,
+                                                                     gint               height);
+
+CLUTTER_AVAILABLE_IN_1_12
+void                clutter_grid_layout_attach_next_to              (ClutterGridLayout   *layout,
+                                                                     ClutterActor        *child,
+                                                                     ClutterActor        *sibling,
+                                                                     ClutterGridPosition  side,
+                                                                     gint                 width,
+                                                                     gint                 height);
+
+CLUTTER_AVAILABLE_IN_1_12
+ClutterActor *      clutter_grid_layout_get_child_at                (ClutterGridLayout *layout,
+                                                                     gint               left,
+                                                                     gint               top);
+
+CLUTTER_AVAILABLE_IN_1_12
+void                clutter_grid_layout_insert_row                  (ClutterGridLayout *layout,
+                                                                     gint               position);
+
+CLUTTER_AVAILABLE_IN_1_12
+void                clutter_grid_layout_insert_column               (ClutterGridLayout *layout,
+                                                                     gint               position);
+
+CLUTTER_AVAILABLE_IN_1_12
+void                clutter_grid_layout_insert_next_to              (ClutterGridLayout   *layout,
+                                                                     ClutterActor        *sibling,
+                                                                     ClutterGridPosition  side);
+
+CLUTTER_AVAILABLE_IN_1_12
+void                clutter_grid_layout_set_orientation             (ClutterGridLayout *layout,
+                                                                     ClutterOrientation orientation);
+
+CLUTTER_AVAILABLE_IN_1_12
+ClutterOrientation  clutter_grid_layout_get_orientation             (ClutterGridLayout *layout);
+
+CLUTTER_AVAILABLE_IN_1_12
+void                clutter_grid_layout_set_column_spacing          (ClutterGridLayout *layout,
+                                                                     guint              spacing);
+
+CLUTTER_AVAILABLE_IN_1_12
+guint               clutter_grid_layout_get_column_spacing          (ClutterGridLayout *layout);
+
+CLUTTER_AVAILABLE_IN_1_12
+void                clutter_grid_layout_set_row_spacing             (ClutterGridLayout *layout,
+                                                                     guint              spacing);
+
+CLUTTER_AVAILABLE_IN_1_12
+guint               clutter_grid_layout_get_row_spacing             (ClutterGridLayout *layout);
+
+CLUTTER_AVAILABLE_IN_1_12
+void                clutter_grid_layout_set_column_homogeneous      (ClutterGridLayout *layout,
+                                                                     gboolean           homogeneous);
+
+CLUTTER_AVAILABLE_IN_1_12
+gboolean            clutter_grid_layout_get_column_homogeneous      (ClutterGridLayout *layout);
+
+
+CLUTTER_AVAILABLE_IN_1_12
+void                clutter_grid_layout_set_row_homogeneous         (ClutterGridLayout *layout,
+                                                                     gboolean           homogeneous);
+
+CLUTTER_AVAILABLE_IN_1_12
+gboolean            clutter_grid_layout_get_row_homogeneous         (ClutterGridLayout *layout);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_GRID_LAYOUT_H__ */
diff --git a/clutter/clutter.h b/clutter/clutter.h
index b4a624a..821a58b 100644
--- a/clutter/clutter.h
+++ b/clutter/clutter.h
@@ -66,6 +66,7 @@
 #include "clutter-fixed-layout.h"
 #include "clutter-flow-layout.h"
 #include "clutter-gesture-action.h"
+#include "clutter-grid-layout.h"
 #include "clutter-group.h"
 #include "clutter-image.h"
 #include "clutter-input-device.h"
diff --git a/clutter/clutter.symbols b/clutter/clutter.symbols
index c7c2578..ad6f0a4 100644
--- a/clutter/clutter.symbols
+++ b/clutter/clutter.symbols
@@ -745,6 +745,24 @@ clutter_grab_keyboard
 clutter_grab_pointer
 clutter_grab_pointer_for_device
 clutter_gravity_get_type
+clutter_grid_layout_get_type
+clutter_grid_layout_new
+clutter_grid_layout_attach
+clutter_grid_layout_attach_next_to
+clutter_grid_layout_get_child_at
+clutter_grid_layout_insert_row
+clutter_grid_layout_insert_column
+clutter_grid_layout_insert_next_to
+clutter_grid_layout_set_orientation
+clutter_grid_layout_get_orientation
+clutter_grid_layout_set_column_spacing
+clutter_grid_layout_get_column_spacing
+clutter_grid_layout_set_row_spacing
+clutter_grid_layout_get_row_spacing
+clutter_grid_layout_set_column_homogeneous
+clutter_grid_layout_get_column_homogeneous
+clutter_grid_layout_set_row_homogeneous
+clutter_grid_layout_get_row_homogeneous
 clutter_group_get_nth_child
 clutter_group_get_n_children
 clutter_group_get_type
diff --git a/doc/reference/clutter/clutter-docs.xml.in b/doc/reference/clutter/clutter-docs.xml.in
index 3540ad3..6869f73 100644
--- a/doc/reference/clutter/clutter-docs.xml.in
+++ b/doc/reference/clutter/clutter-docs.xml.in
@@ -86,6 +86,7 @@
       <xi:include href="xml/clutter-flow-layout.xml"/>
       <xi:include href="xml/clutter-box-layout.xml"/>
       <xi:include href="xml/clutter-table-layout.xml"/>
+      <xi:include href="xml/clutter-grid-layout.xml"/>
     </chapter>
 
     <chapter>
diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt
index 073442c..92991c3 100644
--- a/doc/reference/clutter/clutter-sections.txt
+++ b/doc/reference/clutter/clutter-sections.txt
@@ -2293,6 +2293,45 @@ clutter_flow_layout_get_type
 </SECTION>
 
 <SECTION>
+<FILE>clutter-grid-layout</FILE>
+<TITLE>ClutterGridLayout</TITLE>
+ClutterGridPosition
+ClutterGridLayout
+ClutterGridLayoutClass
+clutter_grid_layout_new
+clutter_grid_layout_attach
+clutter_grid_layout_attach_next_to
+clutter_grid_layout_get_child_at
+clutter_grid_layout_insert_column
+clutter_grid_layout_insert_row
+clutter_grid_layout_insert_next_to
+
+<SUBSECTION>
+clutter_grid_layout_set_orientation
+clutter_grid_layout_get_orientation
+clutter_grid_layout_set_column_homogeneous
+clutter_grid_layout_get_column_homogeneous
+clutter_grid_layout_set_row_homogeneous
+clutter_grid_layout_get_row_homogeneous
+clutter_grid_layout_set_column_spacing
+clutter_grid_layout_get_column_spacing
+clutter_grid_layout_set_row_spacing
+clutter_grid_layout_get_row_spacing
+
+<SUBSECTION Standard>
+CLUTTER_TYPE_GRID_LAYOUT
+CLUTTER_GRID_LAYOUT
+CLUTTER_GRID_LAYOUT_CLASS
+CLUTTER_GRID_LAYOUT_GET_CLASS
+CLUTTER_IS_GRID_LAYOUT
+CLUTTER_IS_GRID_LAYOUT_CLASS
+
+<SUBSECTION Private>
+ClutterGridLayoutPrivate
+clutter_grid_layout_get_type
+</SECTION>
+
+<SECTION>
 <TITLE>ClutterBoxLayout</TITLE>
 <FILE>clutter-box-layout</FILE>
 ClutterBoxAlignment
diff --git a/doc/reference/clutter/clutter.types b/doc/reference/clutter/clutter.types
index 533713f..5e4f281 100644
--- a/doc/reference/clutter/clutter.types
+++ b/doc/reference/clutter/clutter.types
@@ -40,6 +40,7 @@ clutter_effect_get_type
 clutter_fixed_layout_get_type
 clutter_flow_layout_get_type
 clutter_gesture_action_get_type
+clutter_grid_layout_get_type
 clutter_group_get_type
 clutter_input_device_get_type
 clutter_interval_get_type



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