diff --git a/clutter/Makefile.am b/clutter/Makefile.am
index a1ef46e3b..37d1f39c5 100644
--- a/clutter/Makefile.am
+++ b/clutter/Makefile.am
@@ -133,6 +133,7 @@ source_h = \
$(srcdir)/clutter-stage-manager.h \
$(srcdir)/clutter-stage-window.h \
$(srcdir)/clutter-state.h \
+ $(srcdir)/clutter-table-layout.h \
$(srcdir)/clutter-texture.h \
$(srcdir)/clutter-text.h \
$(srcdir)/clutter-timeline.h \
@@ -227,6 +228,7 @@ source_c = \
$(srcdir)/clutter-stage-manager.c \
$(srcdir)/clutter-stage-window.c \
$(srcdir)/clutter-state.c \
+ $(srcdir)/clutter-table-layout.c \
$(srcdir)/clutter-texture.c \
$(srcdir)/clutter-text.c \
$(srcdir)/clutter-timeline.c \
diff --git a/clutter/clutter-table-layout.c b/clutter/clutter-table-layout.c
new file mode 100644
index 000000000..5c8595af4
--- /dev/null
+++ b/clutter/clutter-table-layout.c
@@ -0,0 +1,2642 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2009 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ *
+ * Author:
+ * Jose Dapena Paz
+ *
+ * Based on the MX MxTable actor by:
+ * Thomas Wood
+ * and ClutterBoxLayout by:
+ * Emmanuele Bassi
+ */
+
+/**
+ * SECTION:clutter-table-layout
+ * @title: ClutterTableLayout
+ * @short_description: A layout manager arranging children in rows
+ * and columns
+ *
+ * The #ClutterTableLayout is a #ClutterLayoutManager implementing the
+ * following layout policy:
+ *
+ *
+ * children are arranged in a table
+ * each child specifies the specific row and column
+ * cell to appear;
+ * a child can also set a span, and this way, take
+ * more than one cell both horizontally and vertically;
+ * each child will be allocated to its natural
+ * size or, if set to expand, the available size;
+ * if a child is set to fill on either (or both)
+ * axis, its allocation will match all the available size; the
+ * fill layout property only makes sense if the expand property is
+ * also set;
+ * if a child is set to expand but not to fill then
+ * it is possible to control the alignment using the horizontal and
+ * vertical alignment layout properties.
+ *
+ *
+ * It is possible to control the spacing between children of a
+ * #ClutterTableLayout by using clutter_table_layout_set_row_spacing()
+ * and clutter_table_layout_set_column_spacing().
+ *
+ * In order to set the layout properties when packing an actor inside a
+ * #ClutterTableLayout you should use the clutter_table_layout_pack()
+ * function.
+ *
+ * A #ClutterTableLayout can use animations to transition between different
+ * values of the layout management properties; the easing mode and duration
+ * used for the animations are controlled by the
+ * #ClutterTableLayout:easing-mode and #ClutterTableLayout:easing-duration
+ * properties and their accessor functions.
+ *
+ * #ClutterTableLayout is available since Clutter 1.4
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include
+
+#include "clutter-table-layout.h"
+
+#include "clutter-debug.h"
+#include "clutter-enum-types.h"
+#include "clutter-layout-meta.h"
+#include "clutter-private.h"
+#include "clutter-types.h"
+
+#define CLUTTER_TYPE_TABLE_CHILD (clutter_table_child_get_type ())
+#define CLUTTER_TABLE_CHILD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_TABLE_CHILD, ClutterTableChild))
+#define CLUTTER_IS_TABLE_CHILD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_TABLE_CHILD))
+
+#define CLUTTER_TABLE_LAYOUT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TABLE_LAYOUT, ClutterTableLayoutPrivate))
+
+typedef struct _ClutterTableChild ClutterTableChild;
+typedef struct _ClutterLayoutMetaClass ClutterTableChildClass;
+
+typedef struct _DimensionData {
+ gfloat min_size;
+ gfloat pref_size;
+ gfloat final_size;
+
+ guint expand : 1;
+ guint shrink : 1;
+ guint visible : 1;
+} DimensionData;
+
+struct _ClutterTableLayoutPrivate
+{
+ ClutterContainer *container;
+
+ guint col_spacing;
+ guint row_spacing;
+
+ gint n_rows;
+ gint n_cols;
+ gint active_row;
+ gint active_col;
+ gint visible_rows;
+ gint visible_cols;
+
+ GArray *columns;
+ GArray *rows;
+
+ gulong easing_mode;
+ guint easing_duration;
+
+ guint is_animating : 1;
+ guint use_animations : 1;
+};
+
+struct _ClutterTableChild
+{
+ ClutterLayoutMeta parent_instance;
+
+ /* the last stable allocation before an animation; it is
+ * used as the initial ActorBox when interpolating
+ */
+ ClutterActorBox *last_allocation;
+
+ gint col;
+ gint row;
+
+ gint col_span;
+ gint row_span;
+
+ ClutterTableAlignment x_align;
+ ClutterTableAlignment y_align;
+
+ guint x_expand : 1;
+ guint y_expand : 1;
+ guint x_fill : 1;
+ guint y_fill : 1;
+};
+
+enum
+{
+ PROP_CHILD_0,
+
+ PROP_CHILD_ROW,
+ PROP_CHILD_COLUMN,
+ PROP_CHILD_ROW_SPAN,
+ PROP_CHILD_COLUMN_SPAN,
+ PROP_CHILD_X_ALIGN,
+ PROP_CHILD_Y_ALIGN,
+ PROP_CHILD_X_FILL,
+ PROP_CHILD_Y_FILL,
+ PROP_CHILD_X_EXPAND,
+ PROP_CHILD_Y_EXPAND
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_ROW_SPACING,
+ PROP_COLUMN_SPACING,
+ PROP_USE_ANIMATIONS,
+ PROP_EASING_MODE,
+ PROP_EASING_DURATION
+};
+
+G_DEFINE_TYPE (ClutterTableChild,
+ clutter_table_child,
+ CLUTTER_TYPE_LAYOUT_META);
+
+G_DEFINE_TYPE (ClutterTableLayout,
+ clutter_table_layout,
+ CLUTTER_TYPE_LAYOUT_MANAGER);
+
+/*
+ * ClutterBoxChild
+ */
+
+static void
+table_child_set_position (ClutterTableChild *self,
+ gint row,
+ gint col)
+{
+ gboolean row_changed = FALSE, col_changed = FALSE;
+
+ if (self->row != row)
+ {
+ self->row = row;
+
+ row_changed = TRUE;
+ }
+
+ if (self->col != col)
+ {
+ self->col = col;
+
+ col_changed = TRUE;
+ }
+
+ if (row_changed || col_changed)
+ {
+ ClutterLayoutManager *layout;
+ ClutterTableLayoutPrivate *priv;
+
+ layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self));
+ priv = CLUTTER_TABLE_LAYOUT (layout)->priv;
+
+ g_object_freeze_notify (G_OBJECT (self));
+
+ if (priv->use_animations)
+ {
+ clutter_layout_manager_begin_animation (layout,
+ priv->easing_duration,
+ priv->easing_mode);
+ }
+ else
+ clutter_layout_manager_layout_changed (layout);
+
+ if (row_changed)
+ g_object_notify (G_OBJECT (self), "row");
+
+ if (col_changed)
+ g_object_notify (G_OBJECT (self), "column");
+
+ g_object_thaw_notify (G_OBJECT (self));
+ }
+}
+
+static void
+table_child_set_span (ClutterTableChild *self,
+ gint row_span,
+ gint col_span)
+{
+ gboolean row_changed = FALSE, col_changed = FALSE;
+
+ if (self->row_span != row_span)
+ {
+ self->row_span = row_span;
+
+ row_changed = TRUE;
+ }
+
+ if (self->col_span != col_span)
+ {
+ self->col_span = col_span;
+
+ col_changed = TRUE;
+ }
+
+ if (row_changed || col_changed)
+ {
+ ClutterLayoutManager *layout;
+ ClutterTableLayout *table;
+
+ layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self));
+ table = CLUTTER_TABLE_LAYOUT (layout);
+
+ if (table->priv->use_animations)
+ {
+ clutter_layout_manager_begin_animation (layout,
+ table->priv->easing_duration,
+ table->priv->easing_mode);
+ }
+ else
+ clutter_layout_manager_layout_changed (layout);
+
+ if (row_changed)
+ g_object_notify (G_OBJECT (self), "row-span");
+
+ if (col_changed)
+ g_object_notify (G_OBJECT (self), "column-span");
+ }
+}
+
+static void
+table_child_set_align (ClutterTableChild *self,
+ ClutterTableAlignment x_align,
+ ClutterTableAlignment y_align)
+{
+ gboolean x_changed = FALSE, y_changed = FALSE;
+
+ if (self->x_align != x_align)
+ {
+ self->x_align = x_align;
+
+ x_changed = TRUE;
+ }
+
+ if (self->y_align != y_align)
+ {
+ self->y_align = y_align;
+
+ y_changed = TRUE;
+ }
+
+ if (x_changed || y_changed)
+ {
+ ClutterLayoutManager *layout;
+ ClutterTableLayout *table;
+
+ layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self));
+ table = CLUTTER_TABLE_LAYOUT (layout);
+
+ if (table->priv->use_animations)
+ {
+ clutter_layout_manager_begin_animation (layout,
+ table->priv->easing_duration,
+ table->priv->easing_mode);
+ }
+ else
+ clutter_layout_manager_layout_changed (layout);
+
+ if (x_changed)
+ g_object_notify (G_OBJECT (self), "x-align");
+
+ if (y_changed)
+ g_object_notify (G_OBJECT (self), "y-align");
+ }
+}
+
+static void
+table_child_set_fill (ClutterTableChild *self,
+ gboolean x_fill,
+ gboolean y_fill)
+{
+ gboolean x_changed = FALSE, y_changed = FALSE;
+
+ x_fill = !!x_fill;
+ y_fill = !!y_fill;
+
+ if (self->x_fill != x_fill)
+ {
+ self->x_fill = x_fill;
+
+ x_changed = TRUE;
+ }
+
+ if (self->y_fill != y_fill)
+ {
+ self->y_fill = y_fill;
+
+ y_changed = TRUE;
+ }
+
+ if (x_changed || y_changed)
+ {
+ ClutterLayoutManager *layout;
+ ClutterTableLayoutPrivate *priv;
+
+ layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self));
+ priv = CLUTTER_TABLE_LAYOUT (layout)->priv;
+
+ g_object_freeze_notify (G_OBJECT (self));
+
+ if (priv->use_animations)
+ {
+ clutter_layout_manager_begin_animation (layout,
+ priv->easing_duration,
+ priv->easing_mode);
+ }
+ else
+ clutter_layout_manager_layout_changed (layout);
+
+ if (x_changed)
+ g_object_notify (G_OBJECT (self), "x-fill");
+
+ if (y_changed)
+ g_object_notify (G_OBJECT (self), "y-fill");
+
+ g_object_thaw_notify (G_OBJECT (self));
+ }
+}
+
+static void
+table_child_set_expand (ClutterTableChild *self,
+ gboolean x_expand,
+ gboolean y_expand)
+{
+ gboolean x_changed = FALSE, y_changed = FALSE;
+
+ x_expand = !!x_expand;
+ y_expand = !!y_expand;
+
+ if (self->x_expand != x_expand)
+ {
+ self->x_expand = x_expand;
+
+ x_changed = TRUE;
+ }
+
+ if (self->y_expand != y_expand)
+ {
+ self->y_expand = y_expand;
+
+ y_changed = TRUE;
+ }
+
+ if (x_changed || y_changed)
+ {
+ ClutterLayoutManager *layout;
+ ClutterTableLayoutPrivate *priv;
+
+ layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self));
+ priv = CLUTTER_TABLE_LAYOUT (layout)->priv;
+
+ g_object_freeze_notify (G_OBJECT (self));
+
+ if (priv->use_animations)
+ {
+ clutter_layout_manager_begin_animation (layout,
+ priv->easing_duration,
+ priv->easing_mode);
+ }
+ else
+ clutter_layout_manager_layout_changed (layout);
+
+ if (x_changed)
+ g_object_notify (G_OBJECT (self), "x-expand");
+
+ if (y_changed)
+ g_object_notify (G_OBJECT (self), "y-expand");
+
+ g_object_thaw_notify (G_OBJECT (self));
+ }
+}
+
+static void
+clutter_table_child_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterTableChild *self = CLUTTER_TABLE_CHILD (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_CHILD_COLUMN:
+ table_child_set_position (self,
+ self->row,
+ g_value_get_int (value));
+ break;
+
+ case PROP_CHILD_ROW:
+ table_child_set_position (self,
+ g_value_get_int (value),
+ self->col);
+ break;
+
+ case PROP_CHILD_COLUMN_SPAN:
+ table_child_set_span (self,
+ self->row_span,
+ g_value_get_int (value));
+ break;
+
+ case PROP_CHILD_ROW_SPAN:
+ table_child_set_span (self,
+ g_value_get_int (value),
+ self->col_span);
+ break;
+
+ case PROP_CHILD_X_ALIGN:
+ table_child_set_align (self,
+ g_value_get_enum (value),
+ self->y_align);
+ break;
+
+ case PROP_CHILD_Y_ALIGN:
+ table_child_set_align (self,
+ self->x_align,
+ g_value_get_enum (value));
+ break;
+
+ case PROP_CHILD_X_FILL:
+ table_child_set_fill (self,
+ g_value_get_boolean (value),
+ self->y_fill);
+ break;
+
+ case PROP_CHILD_Y_FILL:
+ table_child_set_fill (self,
+ self->x_fill,
+ g_value_get_boolean (value));
+ break;
+
+ case PROP_CHILD_X_EXPAND:
+ table_child_set_expand (self,
+ g_value_get_boolean (value),
+ self->y_expand);
+ break;
+
+ case PROP_CHILD_Y_EXPAND:
+ table_child_set_expand (self,
+ self->x_expand,
+ g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clutter_table_child_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterTableChild *self = CLUTTER_TABLE_CHILD (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_CHILD_ROW:
+ g_value_set_int (value, self->row);
+ break;
+
+ case PROP_CHILD_COLUMN:
+ g_value_set_int (value, self->col);
+ break;
+
+ case PROP_CHILD_ROW_SPAN:
+ g_value_set_int (value, self->row_span);
+ break;
+
+ case PROP_CHILD_COLUMN_SPAN:
+ g_value_set_int (value, self->col_span);
+ break;
+
+ case PROP_CHILD_X_ALIGN:
+ g_value_set_enum (value, self->x_align);
+ break;
+
+ case PROP_CHILD_Y_ALIGN:
+ g_value_set_enum (value, self->y_align);
+ break;
+
+ case PROP_CHILD_X_FILL:
+ g_value_set_boolean (value, self->x_fill);
+ break;
+
+ case PROP_CHILD_Y_FILL:
+ g_value_set_boolean (value, self->y_fill);
+ break;
+
+ case PROP_CHILD_X_EXPAND:
+ g_value_set_boolean (value, self->x_expand);
+ break;
+
+ case PROP_CHILD_Y_EXPAND:
+ g_value_set_boolean (value, self->y_expand);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clutter_table_child_finalize (GObject *gobject)
+{
+ ClutterTableChild *self = CLUTTER_TABLE_CHILD (gobject);
+
+ clutter_actor_box_free (self->last_allocation);
+
+ G_OBJECT_CLASS (clutter_table_child_parent_class)->finalize (gobject);
+}
+
+static void
+clutter_table_child_class_init (ClutterTableChildClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GParamSpec *pspec;
+
+ gobject_class->set_property = clutter_table_child_set_property;
+ gobject_class->get_property = clutter_table_child_get_property;
+ gobject_class->finalize = clutter_table_child_finalize;
+
+ pspec = g_param_spec_int ("column",
+ P_("Column Number"),
+ P_("The column the widget resides in"),
+ 0, G_MAXINT,
+ 0,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_CHILD_COLUMN, pspec);
+
+ pspec = g_param_spec_int ("row",
+ P_("Row Number"),
+ P_("The row the widget resides in"),
+ 0, G_MAXINT,
+ 0,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_CHILD_ROW, pspec);
+
+ pspec = g_param_spec_int ("column-span",
+ P_("Column Span"),
+ P_("The number of columns the widget should span"),
+ 1, G_MAXINT,
+ 1,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_CHILD_COLUMN_SPAN, pspec);
+
+ pspec = g_param_spec_int ("row-span",
+ P_("Row Span"),
+ P_("The number of rows the widget should span"),
+ 1, G_MAXINT,
+ 1,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_CHILD_ROW_SPAN, pspec);
+
+ pspec = g_param_spec_boolean ("x-expand",
+ P_("Horizontal Expand"),
+ P_("Allocate extra space for the child in horizontal axis"),
+ FALSE,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_CHILD_X_EXPAND, pspec);
+
+ pspec = g_param_spec_boolean ("y-expand",
+ P_("Vertical Expand"),
+ P_("Allocate extra space for the child in vertical axis"),
+ FALSE,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_CHILD_Y_EXPAND, pspec);
+
+ pspec = g_param_spec_boolean ("x-fill",
+ P_("Horizontal Fill"),
+ P_("Whether the child should receive priority when the container is allocating spare space on the horizontal axis"),
+ FALSE,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_CHILD_X_FILL, pspec);
+
+ pspec = g_param_spec_boolean ("y-fill",
+ P_("Vertical Fill"),
+ P_("Whether the child should receive priority when the container is allocating spare space on the vertical axis"),
+ FALSE,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_CHILD_Y_FILL, pspec);
+
+ /**
+ * ClutterTableLayout:x-align:
+ *
+ * The horizontal alignment of the actor within the cell
+ *
+ * Since: 1.4
+ */
+ pspec = g_param_spec_enum ("x-align",
+ P_("Horizontal Alignment"),
+ P_("Horizontal alignment of the actor within the cell"),
+ CLUTTER_TYPE_TABLE_ALIGNMENT,
+ CLUTTER_TABLE_ALIGNMENT_CENTER,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_CHILD_X_ALIGN, pspec);
+
+ /**
+ * ClutterTableLayout:y-align:
+ *
+ * The vertical alignment of the actor within the cell
+ *
+ * Since: 1.4
+ */
+ pspec = g_param_spec_enum ("y-align",
+ P_("Vertical Alignment"),
+ P_("Vertical alignment of the actor within the cell"),
+ CLUTTER_TYPE_TABLE_ALIGNMENT,
+ CLUTTER_TABLE_ALIGNMENT_CENTER,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_CHILD_Y_ALIGN, pspec);
+}
+
+static void
+clutter_table_child_init (ClutterTableChild *self)
+{
+ self->col_span = 1;
+ self->row_span = 1;
+
+ self->x_align = CLUTTER_TABLE_ALIGNMENT_CENTER;
+ self->y_align = CLUTTER_TABLE_ALIGNMENT_CENTER;
+
+ self->x_expand = TRUE;
+ self->y_expand = TRUE;
+
+ self->x_fill = TRUE;
+ self->y_fill = TRUE;
+
+ self->last_allocation = NULL;
+}
+
+static GType
+clutter_table_layout_get_child_meta_type (ClutterLayoutManager *manager)
+{
+ return CLUTTER_TYPE_TABLE_CHILD;
+}
+
+static void
+clutter_table_layout_set_container (ClutterLayoutManager *layout,
+ ClutterContainer *container)
+{
+ ClutterTableLayoutPrivate *priv = CLUTTER_TABLE_LAYOUT (layout)->priv;
+
+ priv->container = container;
+}
+
+
+static void
+update_row_col (ClutterTableLayout *layout,
+ ClutterContainer *container)
+{
+ ClutterTableLayoutPrivate *priv = layout->priv;
+ ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (layout);
+ GList *children, *l;
+ gint n_cols, n_rows;
+
+ n_cols = n_rows = 0;
+ children = container ? clutter_container_get_children (container) : NULL;
+
+ for (l = children; l; l = g_list_next (l))
+ {
+ ClutterActor *child = l->data;
+ ClutterTableChild *meta;
+
+ meta = CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (manager, container, child));
+
+ n_cols = MAX (n_cols, meta->col + meta->col_span);
+ n_rows = MAX (n_rows, meta->row + meta->row_span);
+ }
+
+ g_list_free (children);
+
+ priv->n_cols = n_cols;
+ priv->n_rows = n_rows;
+
+}
+
+static void
+calculate_col_widths (ClutterTableLayout *self,
+ ClutterContainer *container,
+ gint for_width)
+{
+ ClutterTableLayoutPrivate *priv = self->priv;
+ ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (self);
+ gint i;
+ DimensionData *columns;
+ GList *l, *children;
+
+ update_row_col (self, container);
+ g_array_set_size (priv->columns, 0);
+ g_array_set_size (priv->columns, priv->n_cols);
+ columns = (DimensionData *) priv->columns->data;
+
+ /* reset the visibility of all columns */
+ priv->visible_cols = 0;
+ for (i = 0; i < priv->n_cols; i++)
+ columns[i].visible = FALSE;
+
+ children = clutter_container_get_children (container);
+
+ /* STAGE ONE: calculate column widths for non-spanned children */
+ for (l = children; l; l = g_list_next (l))
+ {
+ ClutterActor *child = l->data;
+ ClutterTableChild *meta;
+ DimensionData *col;
+ gfloat c_min, c_pref;
+
+ if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+ continue;
+
+ meta = (ClutterTableChild *)
+ clutter_layout_manager_get_child_meta (manager, container, child);
+
+ if (meta->col_span > 1)
+ continue;
+
+ col = &columns[meta->col];
+
+ if (!col->visible)
+ {
+ col->visible = TRUE;
+ priv->visible_cols += 1;
+ }
+
+ clutter_actor_get_preferred_width (child, -1, &c_min, &c_pref);
+
+ col->min_size = MAX (col->min_size, c_min);
+ col->pref_size = MAX (col->pref_size, c_pref);
+ col->expand = MAX (col->expand, meta->x_expand);
+ }
+
+ /* STAGE TWO: take spanning children into account */
+ for (l = children; l; l = g_list_next (l))
+ {
+ ClutterActor *child = l->data;
+ ClutterTableChild *meta;
+ DimensionData *col;
+ gfloat c_min, c_pref;
+ gfloat min_width, pref_width;
+ gint start_col, end_col;
+ gint n_expand;
+
+ if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+ continue;
+
+ meta = (ClutterTableChild *)
+ clutter_layout_manager_get_child_meta (manager, container, child);
+
+ if (meta->col_span < 2)
+ continue;
+
+ col = &columns[meta->col];
+ start_col = meta->col;
+ end_col = meta->col + meta->col_span - 1;
+
+ clutter_actor_get_preferred_width (child, -1, &c_min, &c_pref);
+
+ /* check there is enough room for this actor */
+ min_width = 0;
+ pref_width = 0;
+ n_expand = 0;
+ for (i = start_col; i <= end_col; i++)
+ {
+ min_width += columns[i].min_size;
+ pref_width += columns[i].pref_size;
+
+ if (columns[i].expand)
+ n_expand++;
+
+ if (!col->visible)
+ {
+ col->visible = TRUE;
+ priv->visible_cols += 1;
+ }
+ }
+ min_width += priv->col_spacing * (meta->col_span - 1);
+ pref_width += priv->col_spacing * (meta->col_span - 1);
+
+ /* see calculate_row_heights() for comments */
+ /* (1) */
+ if (c_min > min_width)
+ {
+
+ /* (2) */
+ /* we can start from preferred width and decrease */
+ if (pref_width > c_min)
+ {
+ for (i = start_col; i <= end_col; i++)
+ columns[i].final_size = columns[i].pref_size;
+
+ while (pref_width > c_min)
+ {
+ for (i = start_col; i <= end_col; i++)
+ {
+ if (columns[i].final_size > columns[i].min_size)
+ {
+ columns[i].final_size--;
+ pref_width--;
+ }
+ }
+ }
+
+ for (i = start_col; i <= end_col; i++)
+ columns[i].min_size = columns[i].final_size;
+ }
+ else
+ {
+ /* (3) */
+ /* we can expand from preferred size */
+ gfloat expand_by;
+
+ expand_by = c_pref - pref_width;
+
+ for (i = start_col; i <= end_col; i++)
+ {
+ if (n_expand)
+ {
+ if (columns[i].expand)
+ columns[i].min_size = columns[i].pref_size
+ + expand_by / n_expand;
+ }
+ else
+ columns[i].min_size = columns[i].pref_size
+ + expand_by / meta->col_span;
+ }
+ }
+ }
+
+
+ }
+ g_list_free (children);
+
+ /* calculate final widths */
+ if (for_width >= 0)
+ {
+ gfloat min_width, pref_width;
+ gint n_expand;
+
+ min_width = 0;
+ pref_width = 0;
+ n_expand = 0;
+ for (i = 0; i < self->priv->n_cols; i++)
+ {
+ pref_width += columns[i].pref_size;
+ min_width += columns[i].min_size;
+ if (columns[i].expand)
+ n_expand++;
+ }
+ pref_width += priv->col_spacing * (priv->n_cols - 1);
+ min_width += priv->col_spacing * (priv->n_cols - 1);
+
+ if (for_width <= min_width)
+ {
+ /* erk, we can't shrink this! */
+ for (i = 0; i < priv->n_cols; i++)
+ columns[i].final_size = columns[i].min_size;
+
+ return;
+ }
+
+ if (for_width == pref_width)
+ {
+ /* perfect! */
+ for (i = 0; i < self->priv->n_cols; i++)
+ columns[i].final_size = columns[i].pref_size;
+
+ return;
+ }
+
+ /* for_width is between min_width and pref_width */
+ if (for_width < pref_width && for_width > min_width)
+ {
+ gfloat width;
+
+ /* shrink columns until they reach min_width */
+
+ /* start with all columns at preferred size */
+ for (i = 0; i < self->priv->n_cols; i++)
+ columns[i].final_size = columns[i].pref_size;
+
+ width = pref_width;
+
+ while (width > for_width)
+ {
+ for (i = 0; i < self->priv->n_cols; i++)
+ {
+ if (columns[i].final_size > columns[i].min_size)
+ {
+ columns[i].final_size--;
+ width--;
+ }
+ }
+ }
+
+ return;
+ }
+
+ /* expand columns */
+ if (for_width > pref_width)
+ {
+ gfloat extra_width = for_width - pref_width;
+ gint remaining;
+
+ if (n_expand)
+ remaining = (gint) extra_width % n_expand;
+ else
+ remaining = (gint) extra_width % priv->n_cols;
+
+ for (i = 0; i < self->priv->n_cols; i++)
+ {
+ if (columns[i].expand)
+ {
+ if (n_expand)
+ columns[i].final_size = columns[i].pref_size
+ + (extra_width / n_expand);
+ else
+ columns[i].final_size = columns[i].pref_size
+ + (extra_width / priv->n_cols);
+ }
+ else
+ columns[i].final_size = columns[i].pref_size;
+ }
+
+ /* distribute the remainder among children */
+ i = 0;
+ while (remaining)
+ {
+ columns[i].final_size++;
+ i++;
+ remaining--;
+ }
+ }
+ }
+
+}
+
+static void
+calculate_row_heights (ClutterTableLayout *self,
+ ClutterContainer *container,
+ gint for_height)
+{
+ ClutterTableLayoutPrivate *priv = self->priv;
+ ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (self);
+ GList *l, *children;
+ gint i;
+ DimensionData *rows, *columns;
+
+ update_row_col (self, container);
+ g_array_set_size (priv->rows, 0);
+ g_array_set_size (priv->rows, self->priv->n_rows);
+
+ rows = (DimensionData*) priv->rows->data;
+ columns = (DimensionData*) priv->columns->data;
+
+ /* reset the visibility of all rows */
+ priv->visible_rows = 0;
+ for (i = 0; i < priv->n_rows; i++)
+ rows[i].visible = FALSE;
+
+ children = clutter_container_get_children (container);
+ /* STAGE ONE: calculate row heights for non-spanned children */
+ for (l = children; l; l = g_list_next (l))
+ {
+ ClutterActor *child = l->data;
+ ClutterTableChild *meta;
+ DimensionData *row;
+ gfloat c_min, c_pref;
+
+ if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+ continue;
+
+ meta = (ClutterTableChild *)
+ clutter_layout_manager_get_child_meta (manager, container, child);
+
+ if (meta->row_span > 1)
+ continue;
+
+ row = &rows[meta->row];
+
+ if (!row->visible)
+ {
+ row->visible = TRUE;
+ priv->visible_rows += 1;
+ }
+
+ clutter_actor_get_preferred_height (child, columns[meta->col].final_size,
+ &c_min, &c_pref);
+
+ row->min_size = MAX (row->min_size, c_min);
+ row->pref_size = MAX (row->pref_size, c_pref);
+ row->expand = MAX (row->expand, meta->y_expand);
+ }
+
+
+
+ /* STAGE TWO: take spanning children into account */
+ for (l = children; l; l = g_list_next (l))
+ {
+ ClutterActor *child = l->data;
+ ClutterTableChild *meta;
+ DimensionData *row;
+ gfloat c_min, c_pref;
+ gfloat min_height, pref_height;
+ gint start_row, end_row;
+ gint n_expand;
+
+ if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+ continue;
+
+ meta = (ClutterTableChild *)
+ clutter_layout_manager_get_child_meta (manager, container, child);
+
+ if (meta->row_span < 2)
+ continue;
+
+ row = &rows[meta->row];
+ start_row = meta->row;
+ end_row = meta->row + meta->row_span - 1;
+
+ clutter_actor_get_preferred_height (child, columns[meta->col].final_size,
+ &c_min, &c_pref);
+
+
+ /* check there is enough room for this actor */
+ min_height = 0;
+ pref_height = 0;
+ n_expand = 0;
+ for (i = start_row; i <= end_row; i++)
+ {
+ min_height += rows[i].min_size;
+ pref_height += rows[i].pref_size;
+
+ if (rows[i].expand)
+ n_expand++;
+
+ if (!rows[i].visible)
+ {
+ rows[i].visible = TRUE;
+ priv->visible_rows += 1;
+ }
+ }
+ min_height += priv->row_spacing * (meta->row_span - 1);
+ pref_height += priv->row_spacing * (meta->row_span - 1);
+
+ /* 1) If the minimum height of the rows spanned is less than the
+ * minimum height of the child that is spanning them, then we
+ * must increase the minimum height of the rows spanned.
+ *
+ * 2) If the preferred height of the spanned rows is more than
+ * the minimum height of the spanning child, then we can start
+ * at this size and decrease each row evenly.
+ *
+ * 3) If the preferred height of the rows is more than the minimum
+ * height of the spanned child, then we can start at the preferred
+ * height and expand.
+ */
+
+ /* (1) */
+ if (c_min > min_height)
+ {
+
+ /* (2) */
+ /* we can start from preferred height and decrease */
+ if (pref_height > c_min)
+ {
+ for (i = start_row; i <= end_row; i++)
+ rows[i].final_size = rows[i].pref_size;
+
+ while (pref_height > c_min)
+ {
+ for (i = start_row; i <= end_row; i++)
+ {
+ if (rows[i].final_size > rows[i].min_size)
+ {
+ rows[i].final_size--;
+ pref_height--;
+ }
+ }
+ }
+
+ for (i = start_row; i <= end_row; i++)
+ rows[i].min_size = rows[i].final_size;
+ }
+ else
+ {
+ /* (3) */
+ /* we can expand from preferred size */
+ gfloat expand_by = c_pref - pref_height;
+
+ for (i = start_row; i <= end_row; i++)
+ {
+ if (n_expand)
+ {
+ if (rows[i].expand)
+ rows[i].min_size = rows[i].pref_size
+ + expand_by / n_expand;
+ }
+ else
+ rows[i].min_size = rows[i].pref_size
+ + expand_by / meta->row_span;
+ }
+ }
+ }
+
+ }
+
+ g_list_free (children);
+
+ /* calculate final heights */
+ if (for_height >= 0)
+ {
+ gfloat min_height, pref_height;
+ gint n_expand;
+
+ min_height = 0;
+ pref_height = 0;
+ n_expand = 0;
+ for (i = 0; i < self->priv->n_rows; i++)
+ {
+ pref_height += rows[i].pref_size;
+ min_height += rows[i].min_size;
+ if (rows[i].expand)
+ n_expand++;
+ }
+ pref_height += priv->row_spacing * (priv->n_rows - 1);
+ min_height += priv->row_spacing * (priv->n_rows - 1);
+
+ if (for_height <= min_height)
+ {
+ /* erk, we can't shrink this! */
+ for (i = 0; i < self->priv->n_rows; i++)
+ rows[i].final_size = rows[i].min_size;
+
+ return;
+ }
+
+ if (for_height == pref_height)
+ {
+ /* perfect! */
+ for (i = 0; i < self->priv->n_rows; i++)
+ rows[i].final_size = rows[i].pref_size;
+
+ return;
+ }
+
+ /* for_height is between min_height and pref_height */
+ if (for_height < pref_height && for_height > min_height)
+ {
+ gfloat height;
+
+ /* shrink rows until they reach min_height */
+
+ /* start with all rows at preferred size */
+ for (i = 0; i < self->priv->n_rows; i++)
+ rows[i].final_size = rows[i].pref_size;
+
+ height = pref_height;
+
+ while (height > for_height)
+ {
+ for (i = 0; i < priv->n_rows; i++)
+ {
+ if (rows[i].final_size > rows[i].min_size)
+ {
+ rows[i].final_size--;
+ height--;
+ }
+ }
+ }
+
+ return;
+ }
+
+ /* expand rows */
+ if (for_height > pref_height)
+ {
+ gfloat extra_height = for_height - pref_height;
+ gint remaining;
+
+ if (n_expand)
+ remaining = (gint) extra_height % n_expand;
+ else
+ remaining = (gint) extra_height % self->priv->n_rows;
+
+ for (i = 0; i < self->priv->n_rows; i++)
+ {
+ if (rows[i].expand)
+ {
+ if (n_expand)
+ rows[i].final_size = rows[i].pref_size
+ + (extra_height / n_expand);
+ else
+ rows[i].final_size = rows[i].pref_size
+ + (extra_height / priv->n_rows);
+ }
+ else
+ rows[i].final_size = rows[i].pref_size;
+ }
+
+ /* distribute the remainder among children */
+ i = 0;
+ while (remaining)
+ {
+ rows[i].final_size++;
+ i++;
+ remaining--;
+ }
+ }
+ }
+
+}
+
+static void
+calculate_table_dimensions (ClutterTableLayout *self,
+ ClutterContainer *container,
+ gfloat for_width,
+ gfloat for_height)
+{
+ calculate_col_widths (self, container, for_width);
+ calculate_row_heights (self, container, for_height);
+}
+
+static void
+clutter_table_layout_get_preferred_width (ClutterLayoutManager *layout,
+ ClutterContainer *container,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *natural_width_p)
+{
+ ClutterTableLayout *self = CLUTTER_TABLE_LAYOUT (layout);
+ ClutterTableLayoutPrivate *priv = self->priv;
+ gfloat total_min_width, total_pref_width;
+ DimensionData *columns;
+ gint i;
+
+ update_row_col (self, container);
+ if (priv->n_cols < 1)
+ {
+ *min_width_p = 0;
+ *natural_width_p = 0;
+ return;
+ }
+
+ calculate_table_dimensions (self, container, -1, for_height);
+ columns = (DimensionData *) priv->columns->data;
+
+ total_min_width = (priv->visible_cols - 1) * (float) priv->col_spacing;
+ total_pref_width = total_min_width;
+
+ for (i = 0; i < priv->n_cols; i++)
+ {
+ total_min_width += columns[i].min_size;
+ total_pref_width += columns[i].pref_size;
+ }
+
+ if (min_width_p)
+ *min_width_p = total_min_width;
+
+ if (natural_width_p)
+ *natural_width_p = total_pref_width;
+}
+
+static void
+clutter_table_layout_get_preferred_height (ClutterLayoutManager *layout,
+ ClutterContainer *container,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *natural_height_p)
+{
+ ClutterTableLayout *self = CLUTTER_TABLE_LAYOUT (layout);
+ ClutterTableLayoutPrivate *priv = self->priv;
+ gfloat total_min_height, total_pref_height;
+ DimensionData *rows;
+ gint i;
+
+ update_row_col (self, container);
+ if (priv->n_rows < 1)
+ {
+ *min_height_p = 0;
+ *natural_height_p = 0;
+ return;
+ }
+
+ calculate_table_dimensions (self, container, for_width, -1);
+ rows = (DimensionData *) priv->rows->data;
+
+ total_min_height = (priv->visible_rows - 1) * (float) priv->row_spacing;
+ total_pref_height = total_min_height;
+
+ for (i = 0; i < self->priv->n_rows; i++)
+ {
+ total_min_height += rows[i].min_size;
+ total_pref_height += rows[i].pref_size;
+ }
+
+ if (min_height_p)
+ *min_height_p = total_min_height;
+
+ if (natural_height_p)
+ *natural_height_p = total_pref_height;
+}
+
+static gdouble
+get_table_alignment_factor (ClutterTableAlignment alignment)
+{
+ switch (alignment)
+ {
+ case CLUTTER_TABLE_ALIGNMENT_START:
+ return 0.0;
+
+ case CLUTTER_TABLE_ALIGNMENT_CENTER:
+ return 0.5;
+
+ case CLUTTER_TABLE_ALIGNMENT_END:
+ return 1.0;
+ }
+
+ return 0.0;
+}
+
+static void
+clutter_table_layout_allocate (ClutterLayoutManager *layout,
+ ClutterContainer *container,
+ const ClutterActorBox *box,
+ ClutterAllocationFlags flags)
+{
+ ClutterTableLayout *self = CLUTTER_TABLE_LAYOUT (layout);
+ ClutterTableLayoutPrivate *priv = self->priv;
+ GList *list, *children;
+ gint row_spacing, col_spacing;
+ gint i, table_width, table_height;
+ DimensionData *rows, *columns;
+
+ update_row_col (self, container);
+ if (priv->n_cols < 1 || priv->n_rows < 1)
+ return;
+
+ children = clutter_container_get_children (container);
+ if (children == NULL)
+ return;
+
+ col_spacing = (priv->col_spacing);
+ row_spacing = (priv->row_spacing);
+
+
+ table_height = (int)(box->y2 - box->y1);
+ table_width = (int)(box->x2 - box->x1);
+
+ calculate_table_dimensions (self, container, box->x2 - box->x1, box->y2 - box->y1);
+
+ rows = (DimensionData *) priv->rows->data;
+ columns = (DimensionData *) priv->columns->data;
+
+ for (list = children; list; list = g_list_next (list))
+ {
+ ClutterActor *child = list->data;
+ gint row, col, row_span, col_span;
+ gint col_width, row_height;
+ ClutterTableChild *meta;
+ ClutterActorBox childbox;
+ gint child_x, child_y;
+ gdouble x_align, y_align;
+ gboolean x_fill, y_fill;
+
+ if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+ continue;
+
+ meta = (ClutterTableChild *)
+ clutter_layout_manager_get_child_meta (layout, priv->container, child);
+
+ /* get child properties */
+ col = meta->col;
+ row = meta->row;
+ row_span = meta->row_span;
+ col_span = meta->col_span;
+ x_align = get_table_alignment_factor (meta->x_align);
+ y_align = get_table_alignment_factor (meta->y_align);
+ x_fill = meta->x_fill;
+ y_fill = meta->y_fill;
+
+ /* initialise the width and height */
+ col_width = columns[col].final_size;
+ row_height = rows[row].final_size;
+
+ /* Add the widths of the spanned columns:
+ *
+ * First check that we have a non-zero span. Then we loop over each of
+ * the columns that we're spanning but we stop short if we go past the
+ * number of columns in the table. This is necessary to avoid accessing
+ * uninitialised memory. We add the spacing in here too since we only
+ * want to add as much spacing as times we successfully span.
+ */
+ if (col + col_span > priv->n_cols)
+ g_warning (G_STRLOC ": column-span exceeds number of columns");
+ if (row + row_span > priv->n_rows)
+ g_warning (G_STRLOC ": row-span exceeds number of rows");
+
+ if (col_span > 1)
+ {
+ for (i = col + 1; i < col + col_span && i < priv->n_cols; i++)
+ {
+ col_width += columns[i].final_size;
+ col_width += col_spacing;
+ }
+ }
+
+ /* add the height of the spanned rows */
+ if (row_span > 1)
+ {
+ for (i = row + 1; i < row + row_span && i < priv->n_rows; i++)
+ {
+ row_height += rows[i].final_size;
+ row_height += row_spacing;
+ }
+ }
+
+ /* calculate child x */
+ child_x = 0.0f;
+ for (i = 0; i < col; i++)
+ {
+ if (columns[i].visible)
+ {
+ child_x += columns[i].final_size;
+ child_x += col_spacing;
+ }
+ }
+
+ /* calculate child y */
+ child_y = 0.0f;
+ for (i = 0; i < row; i++)
+ {
+ if (rows[i].visible)
+ {
+ child_y += rows[i].final_size;
+ child_y += row_spacing;
+ }
+ }
+
+ /* set up childbox */
+ childbox.x1 = (float) child_x;
+ childbox.x2 = (float) MAX (0, child_x + col_width);
+
+ childbox.y1 = (float) child_y;
+ childbox.y2 = (float) MAX (0, child_y + row_height);
+
+ clutter_actor_allocate_align_fill (child, &childbox,
+ x_align, y_align,
+ x_fill, y_fill,
+ flags);
+
+ /* since we call this after allocate_align_fill(), this is
+ * just a cheap copy
+ */
+ clutter_actor_get_allocation_box (child, &childbox);
+
+ if (priv->use_animations && priv->is_animating)
+ {
+ ClutterActorBox *start = NULL;
+ ClutterActorBox end = { 0, };
+ gdouble p;
+
+ p = clutter_layout_manager_get_animation_progress (layout);
+
+ start = meta->last_allocation;
+ if (start == NULL)
+ {
+ /* if there is no allocation available then the child has just
+ * been added to the container; we put it in the final state
+ * and store its allocation for later
+ */
+ meta->last_allocation = clutter_actor_box_copy (&childbox);
+
+ goto do_allocate;
+ }
+
+ end = childbox;
+
+ /* interpolate between the initial and final values */
+ clutter_actor_box_interpolate (start, &end, p, &childbox);
+
+ CLUTTER_NOTE (ANIMATION,
+ "Animate { %.1f, %.1f, %.1f, %.1f }\t"
+ "%.3f * { %.1f, %.1f, %.1f, %.1f }\t"
+ "-> { %.1f, %.1f, %.1f, %.1f }",
+ start->x1, start->y1,
+ start->x2, start->y2,
+ p,
+ childbox.x1, childbox.y1,
+ childbox.x2, childbox.y2,
+ end.x1, end.y1,
+ end.x2, end.y2);
+ }
+ else
+ {
+ /* store the allocation for later animations */
+ meta->last_allocation = clutter_actor_box_copy (&childbox);
+ }
+
+ do_allocate:
+ clutter_actor_allocate (child, &childbox, flags);
+ }
+
+ g_list_free (children);
+}
+
+static ClutterAlpha *
+clutter_table_layout_begin_animation (ClutterLayoutManager *manager,
+ guint duration,
+ gulong easing)
+{
+ ClutterTableLayoutPrivate *priv = CLUTTER_TABLE_LAYOUT (manager)->priv;
+ ClutterLayoutManagerClass *parent_class;
+
+ priv->is_animating = TRUE;
+
+ /* we want the default implementation */
+ parent_class = CLUTTER_LAYOUT_MANAGER_CLASS (clutter_table_layout_parent_class);
+
+ return parent_class->begin_animation (manager, duration, easing);
+}
+
+static void
+clutter_table_layout_end_animation (ClutterLayoutManager *manager)
+{
+ ClutterTableLayoutPrivate *priv = CLUTTER_TABLE_LAYOUT (manager)->priv;
+ ClutterLayoutManagerClass *parent_class;
+
+ priv->is_animating = FALSE;
+
+ /* we want the default implementation */
+ parent_class = CLUTTER_LAYOUT_MANAGER_CLASS (clutter_table_layout_parent_class);
+ parent_class->end_animation (manager);
+}
+
+static void
+clutter_table_layout_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterTableLayout *self = CLUTTER_TABLE_LAYOUT (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_COLUMN_SPACING:
+ clutter_table_layout_set_column_spacing (self, g_value_get_uint (value));
+ break;
+
+ case PROP_ROW_SPACING:
+ clutter_table_layout_set_row_spacing (self, g_value_get_uint (value));
+ break;
+
+ case PROP_USE_ANIMATIONS:
+ clutter_table_layout_set_use_animations (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_EASING_MODE:
+ clutter_table_layout_set_easing_mode (self, g_value_get_ulong (value));
+ break;
+
+ case PROP_EASING_DURATION:
+ clutter_table_layout_set_easing_duration (self, g_value_get_uint (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clutter_table_layout_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterTableLayoutPrivate *priv = CLUTTER_TABLE_LAYOUT (gobject)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_ROW_SPACING:
+ g_value_set_uint (value, priv->row_spacing);
+ break;
+
+ case PROP_COLUMN_SPACING:
+ g_value_set_uint (value, priv->col_spacing);
+ break;
+
+ case PROP_USE_ANIMATIONS:
+ g_value_set_boolean (value, priv->use_animations);
+ break;
+
+ case PROP_EASING_MODE:
+ g_value_set_ulong (value, priv->easing_mode);
+ break;
+
+ case PROP_EASING_DURATION:
+ g_value_set_uint (value, priv->easing_duration);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clutter_table_layout_finalize (GObject *gobject)
+{
+ ClutterTableLayoutPrivate *priv = CLUTTER_TABLE_LAYOUT (gobject)->priv;
+
+ g_array_free (priv->columns, TRUE);
+ g_array_free (priv->rows, TRUE);
+
+ G_OBJECT_CLASS (clutter_table_layout_parent_class)->finalize (gobject);
+}
+
+static void
+clutter_table_layout_class_init (ClutterTableLayoutClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterLayoutManagerClass *layout_class;
+ GParamSpec *pspec;
+
+ layout_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass);
+
+ gobject_class->set_property = clutter_table_layout_set_property;
+ gobject_class->get_property = clutter_table_layout_get_property;
+ gobject_class->finalize = clutter_table_layout_finalize;
+
+ layout_class->get_preferred_width =
+ clutter_table_layout_get_preferred_width;
+ layout_class->get_preferred_height =
+ clutter_table_layout_get_preferred_height;
+ layout_class->allocate = clutter_table_layout_allocate;
+ layout_class->set_container = clutter_table_layout_set_container;
+ layout_class->get_child_meta_type =
+ clutter_table_layout_get_child_meta_type;
+ layout_class->begin_animation = clutter_table_layout_begin_animation;
+ layout_class->end_animation = clutter_table_layout_end_animation;
+
+ g_type_class_add_private (klass, sizeof (ClutterTableLayoutPrivate));
+
+ /**
+ * ClutterTableLayout:column-spacing:
+ *
+ * The spacing between columns of the #ClutterTableLayout, in pixels
+ *
+ * Since: 1.4
+ */
+ pspec = g_param_spec_uint ("column-spacing",
+ "Column Spacing",
+ "Spacing between columns",
+ 0, G_MAXUINT, 0,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_COLUMN_SPACING, pspec);
+
+ /**
+ * ClutterTableLayout:row-spacing:
+ *
+ * The spacing between rows of the #ClutterTableLayout, in pixels
+ *
+ * Since: 1.4
+ */
+ pspec = g_param_spec_uint ("row-spacing",
+ "Row Spacing",
+ "Spacing between rows",
+ 0, G_MAXUINT, 0,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_ROW_SPACING, pspec);
+
+ /**
+ * ClutterTableLayout:use-animations:
+ *
+ * Whether the #ClutterTableLayout should animate changes in the
+ * layout properties
+ *
+ * Since: 1.4
+ */
+ pspec = g_param_spec_boolean ("use-animations",
+ "Use Animations",
+ "Whether layout changes should be animated",
+ FALSE,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_USE_ANIMATIONS, pspec);
+
+ /**
+ * ClutterTableLayout:easing-mode:
+ *
+ * The easing mode for the animations, in case
+ * #ClutterTableLayout:use-animations is set to %TRUE
+ *
+ * The easing mode has the same semantics of #ClutterAnimation:mode: it can
+ * either be a value from the #ClutterAnimationMode enumeration, like
+ * %CLUTTER_EASE_OUT_CUBIC, or a logical id as returned by
+ * clutter_alpha_register_func()
+ *
+ * The default value is %CLUTTER_EASE_OUT_CUBIC
+ *
+ * Since: 1.4
+ */
+ pspec = g_param_spec_ulong ("easing-mode",
+ "Easing Mode",
+ "The easing mode of the animations",
+ 0, G_MAXULONG,
+ CLUTTER_EASE_OUT_CUBIC,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_EASING_MODE, pspec);
+
+ /**
+ * ClutterTableLayout:easing-duration:
+ *
+ * The duration of the animations, in case #ClutterTableLayout:use-animations
+ * is set to %TRUE
+ *
+ * The duration is expressed in milliseconds
+ *
+ * Since: 1.4
+ */
+ pspec = g_param_spec_uint ("easing-duration",
+ "Easing Duration",
+ "The duration of the animations",
+ 0, G_MAXUINT,
+ 500,
+ CLUTTER_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_EASING_DURATION, pspec);
+}
+
+static void
+clutter_table_layout_init (ClutterTableLayout *layout)
+{
+ ClutterTableLayoutPrivate *priv;
+
+ layout->priv = priv = CLUTTER_TABLE_LAYOUT_GET_PRIVATE (layout);
+
+ priv->row_spacing = 0;
+ priv->col_spacing = 0;
+
+ priv->use_animations = FALSE;
+ priv->easing_mode = CLUTTER_EASE_OUT_CUBIC;
+ priv->easing_duration = 500;
+
+ priv->columns = g_array_new (FALSE, TRUE, sizeof (DimensionData));
+ priv->rows = g_array_new (FALSE, TRUE, sizeof (DimensionData));
+}
+
+/**
+ * clutter_table_layout_new:
+ *
+ * Creates a new #ClutterTableLayout layout manager
+ *
+ * Return value: the newly created #ClutterTableLayout
+ *
+ * Since: 1.4
+ */
+ClutterLayoutManager *
+clutter_table_layout_new (void)
+{
+ return g_object_new (CLUTTER_TYPE_TABLE_LAYOUT, NULL);
+}
+
+/**
+ * clutter_table_layout_set_column_spacing:
+ * @layout: a #ClutterTableLayout
+ * @spacing: the spacing between columns of the layout, in pixels
+ *
+ * Sets the spacing between columns of @layout
+ *
+ * Since: 1.4
+ */
+void
+clutter_table_layout_set_column_spacing (ClutterTableLayout *layout,
+ guint spacing)
+{
+ ClutterTableLayoutPrivate *priv;
+
+ g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
+
+ priv = layout->priv;
+
+ if (priv->col_spacing != spacing)
+ {
+ ClutterLayoutManager *manager;
+
+ priv->col_spacing = spacing;
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+
+ if (priv->use_animations)
+ {
+ clutter_layout_manager_begin_animation (manager,
+ priv->easing_duration,
+ priv->easing_mode);
+ }
+ else
+ clutter_layout_manager_layout_changed (manager);
+
+ g_object_notify (G_OBJECT (layout), "column-spacing");
+ }
+}
+
+/**
+ * clutter_table_layout_get_column_spacing:
+ * @layout: a #ClutterTableLayout
+ *
+ * Retrieves the spacing set using clutter_table_layout_set_column_spacing()
+ *
+ * Return value: the spacing between columns of the #ClutterTableLayout
+ *
+ * Since: 1.4
+ */
+guint
+clutter_table_layout_get_column_spacing (ClutterTableLayout *layout)
+{
+ g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout), 0);
+
+ return layout->priv->col_spacing;
+}
+
+/**
+ * clutter_table_layout_set_row_spacing:
+ * @layout: a #ClutterTableLayout
+ * @spacing: the spacing between rows of the layout, in pixels
+ *
+ * Sets the spacing between rows of @layout
+ *
+ * Since: 1.4
+ */
+void
+clutter_table_layout_set_row_spacing (ClutterTableLayout *layout,
+ guint spacing)
+{
+ ClutterTableLayoutPrivate *priv;
+
+ g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
+
+ priv = layout->priv;
+
+ if (priv->row_spacing != spacing)
+ {
+ ClutterLayoutManager *manager;
+
+ priv->row_spacing = spacing;
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+
+ if (priv->use_animations)
+ {
+ clutter_layout_manager_begin_animation (manager,
+ priv->easing_duration,
+ priv->easing_mode);
+ }
+ else
+ clutter_layout_manager_layout_changed (manager);
+
+ g_object_notify (G_OBJECT (layout), "row-spacing");
+ }
+}
+
+/**
+ * clutter_table_layout_get_row_spacing:
+ * @layout: a #ClutterTableLayout
+ *
+ * Retrieves the spacing set using clutter_table_layout_set_row_spacing()
+ *
+ * Return value: the spacing between rows of the #ClutterTableLayout
+ *
+ * Since: 1.4
+ */
+guint
+clutter_table_layout_get_row_spacing (ClutterTableLayout *layout)
+{
+ g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout), 0);
+
+ return layout->priv->row_spacing;
+}
+
+/**
+ * clutter_table_layout_pack:
+ * @layout: a #ClutterTableLayout
+ * @actor: a #ClutterActor
+ * @row: the row the @actor should be put, or -1 to append
+ * @column: the column the @actor should be put, or -1 to append
+ *
+ * Packs @actor inside the #ClutterContainer associated to @layout
+ * at the given row and column.
+ *
+ * Since: 1.4
+ */
+void
+clutter_table_layout_pack (ClutterTableLayout *layout,
+ ClutterActor *actor,
+ gint row,
+ gint column)
+{
+ ClutterTableLayoutPrivate *priv;
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+
+ g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ priv = layout->priv;
+
+ if (priv->container == NULL)
+ {
+ g_warning ("The layout of type '%s' must be associated to "
+ "a ClutterContainer before adding children",
+ G_OBJECT_TYPE_NAME (layout));
+ return;
+ }
+
+ update_row_col (CLUTTER_TABLE_LAYOUT (layout), priv->container);
+
+ clutter_container_add_actor (priv->container, actor);
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ meta = clutter_layout_manager_get_child_meta (manager,
+ priv->container,
+ actor);
+ g_assert (CLUTTER_IS_TABLE_CHILD (meta));
+
+ if (row < 0)
+ row = priv->n_rows + 1;
+
+ if (column < 0)
+ column = priv->n_cols + 1;
+
+ table_child_set_position (CLUTTER_TABLE_CHILD (meta), row, column);
+}
+
+/**
+ * clutter_table_layout_set_span:
+ * @layout: a #ClutterTableLayout
+ * @actor: a #ClutterActor child of @layout
+ * @row_span: Row span for @actor
+ * @column_span: Column span for @actor
+ *
+ * Sets the row and column span for @actor
+ * inside @layout
+ *
+ * Since: 1.4
+ */
+void
+clutter_table_layout_set_span (ClutterTableLayout *layout,
+ ClutterActor *actor,
+ gint row_span,
+ gint column_span)
+{
+ ClutterTableLayoutPrivate *priv;
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+
+ g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ priv = layout->priv;
+
+ if (priv->container == NULL)
+ {
+ g_warning ("The layout of type '%s' must be associated to "
+ "a ClutterContainer before querying layout "
+ "properties",
+ G_OBJECT_TYPE_NAME (layout));
+ return;
+ }
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ meta = clutter_layout_manager_get_child_meta (manager,
+ priv->container,
+ actor);
+ if (meta == NULL)
+ {
+ g_warning ("No layout meta found for the child of type '%s' "
+ "inside the layout manager of type '%s'",
+ G_OBJECT_TYPE_NAME (actor),
+ G_OBJECT_TYPE_NAME (manager));
+ return;
+ }
+
+ g_assert (CLUTTER_IS_TABLE_CHILD (meta));
+
+ table_child_set_span (CLUTTER_TABLE_CHILD (meta), row_span, column_span);
+}
+
+/**
+ * clutter_table_layout_get_span:
+ * @layout: a #ClutterTableLayout
+ * @actor: a #ClutterActor child of @layout
+ * @row_span: (out): return location for the row span
+ * @column_span: (out): return location for the col span
+ *
+ * Retrieves the row and column span for @actor as set using
+ * clutter_table_layout_pack() or clutter_table_layout_set_span()
+ *
+ * Since: 1.4
+ */
+void
+clutter_table_layout_get_span (ClutterTableLayout *layout,
+ ClutterActor *actor,
+ gint *row_span,
+ gint *column_span)
+{
+ ClutterTableLayoutPrivate *priv;
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+
+ g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ priv = layout->priv;
+
+ if (priv->container == NULL)
+ {
+ g_warning ("The layout of type '%s' must be associated to "
+ "a ClutterContainer before querying layout "
+ "properties",
+ G_OBJECT_TYPE_NAME (layout));
+ return;
+ }
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ meta = clutter_layout_manager_get_child_meta (manager,
+ priv->container,
+ actor);
+ if (meta == NULL)
+ {
+ g_warning ("No layout meta found for the child of type '%s' "
+ "inside the layout manager of type '%s'",
+ G_OBJECT_TYPE_NAME (actor),
+ G_OBJECT_TYPE_NAME (manager));
+ return;
+ }
+
+ g_assert (CLUTTER_IS_TABLE_CHILD (meta));
+
+ if (row_span)
+ *row_span = CLUTTER_TABLE_CHILD (meta)->row_span;
+
+ if (column_span)
+ *column_span = CLUTTER_TABLE_CHILD (meta)->col_span;
+}
+
+/**
+ * clutter_table_layout_set_alignment:
+ * @layout: a #ClutterTableLayout
+ * @actor: a #ClutterActor child of @layout
+ * @x_align: Horizontal alignment policy for @actor
+ * @y_align: Vertical alignment policy for @actor
+ *
+ * Sets the horizontal and vertical alignment policies for @actor
+ * inside @layout
+ *
+ * Since: 1.4
+ */
+void
+clutter_table_layout_set_alignment (ClutterTableLayout *layout,
+ ClutterActor *actor,
+ ClutterTableAlignment x_align,
+ ClutterTableAlignment y_align)
+{
+ ClutterTableLayoutPrivate *priv;
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+
+ g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ priv = layout->priv;
+
+ if (priv->container == NULL)
+ {
+ g_warning ("The layout of type '%s' must be associated to "
+ "a ClutterContainer before querying layout "
+ "properties",
+ G_OBJECT_TYPE_NAME (layout));
+ return;
+ }
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ meta = clutter_layout_manager_get_child_meta (manager,
+ priv->container,
+ actor);
+ if (meta == NULL)
+ {
+ g_warning ("No layout meta found for the child of type '%s' "
+ "inside the layout manager of type '%s'",
+ G_OBJECT_TYPE_NAME (actor),
+ G_OBJECT_TYPE_NAME (manager));
+ return;
+ }
+
+ g_assert (CLUTTER_IS_TABLE_CHILD (meta));
+
+ table_child_set_align (CLUTTER_TABLE_CHILD (meta), x_align, y_align);
+}
+
+/**
+ * clutter_table_layout_get_alignment:
+ * @layout: a #ClutterTableLayout
+ * @actor: a #ClutterActor child of @layout
+ * @x_align: (out): return location for the horizontal alignment policy
+ * @y_align: (out): return location for the vertical alignment policy
+ *
+ * Retrieves the horizontal and vertical alignment policies for @actor
+ * as set using clutter_table_layout_pack() or
+ * clutter_table_layout_set_alignment().
+ *
+ * Since: 1.4
+ */
+void
+clutter_table_layout_get_alignment (ClutterTableLayout *layout,
+ ClutterActor *actor,
+ ClutterTableAlignment *x_align,
+ ClutterTableAlignment *y_align)
+{
+ ClutterTableLayoutPrivate *priv;
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+
+ g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ priv = layout->priv;
+
+ if (priv->container == NULL)
+ {
+ g_warning ("The layout of type '%s' must be associated to "
+ "a ClutterContainer before querying layout "
+ "properties",
+ G_OBJECT_TYPE_NAME (layout));
+ return;
+ }
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ meta = clutter_layout_manager_get_child_meta (manager,
+ priv->container,
+ actor);
+ if (meta == NULL)
+ {
+ g_warning ("No layout meta found for the child of type '%s' "
+ "inside the layout manager of type '%s'",
+ G_OBJECT_TYPE_NAME (actor),
+ G_OBJECT_TYPE_NAME (manager));
+ return;
+ }
+
+ g_assert (CLUTTER_IS_TABLE_CHILD (meta));
+
+ if (x_align)
+ *x_align = CLUTTER_TABLE_CHILD (meta)->x_align;
+
+ if (y_align)
+ *y_align = CLUTTER_TABLE_CHILD (meta)->y_align;
+}
+
+/**
+ * clutter_table_layout_set_fill:
+ * @layout: a #ClutterTableLayout
+ * @actor: a #ClutterActor child of @layout
+ * @x_fill: whether @actor should fill horizontally the allocated space
+ * @y_fill: whether @actor should fill vertically the allocated space
+ *
+ * Sets the horizontal and vertical fill policies for @actor
+ * inside @layout
+ *
+ * Since: 1.4
+ */
+void
+clutter_table_layout_set_fill (ClutterTableLayout *layout,
+ ClutterActor *actor,
+ gboolean x_fill,
+ gboolean y_fill)
+{
+ ClutterTableLayoutPrivate *priv;
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+
+ g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ priv = layout->priv;
+
+ if (priv->container == NULL)
+ {
+ g_warning ("The layout of type '%s' must be associated to "
+ "a ClutterContainer before querying layout "
+ "properties",
+ G_OBJECT_TYPE_NAME (layout));
+ return;
+ }
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ meta = clutter_layout_manager_get_child_meta (manager,
+ priv->container,
+ actor);
+ if (meta == NULL)
+ {
+ g_warning ("No layout meta found for the child of type '%s' "
+ "inside the layout manager of type '%s'",
+ G_OBJECT_TYPE_NAME (actor),
+ G_OBJECT_TYPE_NAME (manager));
+ return;
+ }
+
+ g_assert (CLUTTER_IS_TABLE_CHILD (meta));
+
+ table_child_set_fill (CLUTTER_TABLE_CHILD (meta), x_fill, y_fill);
+}
+
+/**
+ * clutter_table_layout_get_fill:
+ * @layout: a #ClutterTableLayout
+ * @actor: a #ClutterActor child of @layout
+ * @x_fill: (out): return location for the horizontal fill policy
+ * @y_fill: (out): return location for the vertical fill policy
+ *
+ * Retrieves the horizontal and vertical fill policies for @actor
+ * as set using clutter_table_layout_pack() or clutter_table_layout_set_fill()
+ *
+ * Since: 1.4
+ */
+void
+clutter_table_layout_get_fill (ClutterTableLayout *layout,
+ ClutterActor *actor,
+ gboolean *x_fill,
+ gboolean *y_fill)
+{
+ ClutterTableLayoutPrivate *priv;
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+
+ g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ priv = layout->priv;
+
+ if (priv->container == NULL)
+ {
+ g_warning ("The layout of type '%s' must be associated to "
+ "a ClutterContainer before querying layout "
+ "properties",
+ G_OBJECT_TYPE_NAME (layout));
+ return;
+ }
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ meta = clutter_layout_manager_get_child_meta (manager,
+ priv->container,
+ actor);
+ if (meta == NULL)
+ {
+ g_warning ("No layout meta found for the child of type '%s' "
+ "inside the layout manager of type '%s'",
+ G_OBJECT_TYPE_NAME (actor),
+ G_OBJECT_TYPE_NAME (manager));
+ return;
+ }
+
+ g_assert (CLUTTER_IS_TABLE_CHILD (meta));
+
+ if (x_fill)
+ *x_fill = CLUTTER_TABLE_CHILD (meta)->x_fill;
+
+ if (y_fill)
+ *y_fill = CLUTTER_TABLE_CHILD (meta)->y_fill;
+}
+
+
+/**
+ * clutter_table_layout_set_expand:
+ * @layout: a #ClutterTableLayout
+ * @actor: a #ClutterActor child of @layout
+ * @x_expand: whether @actor should allocate extra space horizontally
+ * @y_expand: whether @actor should allocate extra space vertically
+ *
+ * Sets the horizontal and vertical expand policies for @actor
+ * inside @layout
+ *
+ * Since: 1.4
+ */
+void
+clutter_table_layout_set_expand (ClutterTableLayout *layout,
+ ClutterActor *actor,
+ gboolean x_expand,
+ gboolean y_expand)
+{
+ ClutterTableLayoutPrivate *priv;
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+
+ g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ priv = layout->priv;
+
+ if (priv->container == NULL)
+ {
+ g_warning ("The layout of type '%s' must be associated to "
+ "a ClutterContainer before querying layout "
+ "properties",
+ G_OBJECT_TYPE_NAME (layout));
+ return;
+ }
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ meta = clutter_layout_manager_get_child_meta (manager,
+ priv->container,
+ actor);
+ if (meta == NULL)
+ {
+ g_warning ("No layout meta found for the child of type '%s' "
+ "inside the layout manager of type '%s'",
+ G_OBJECT_TYPE_NAME (actor),
+ G_OBJECT_TYPE_NAME (manager));
+ return;
+ }
+
+ g_assert (CLUTTER_IS_TABLE_CHILD (meta));
+
+ table_child_set_expand (CLUTTER_TABLE_CHILD (meta), x_expand, y_expand);
+}
+
+/**
+ * clutter_table_layout_get_expand:
+ * @layout: a #ClutterTableLayout
+ * @actor: a #ClutterActor child of @layout
+ * @x_expand: (out): return location for the horizontal expand policy
+ * @y_expand: (out): return location for the vertical expand policy
+ *
+ * Retrieves the horizontal and vertical expand policies for @actor
+ * as set using clutter_table_layout_pack() or clutter_table_layout_set_expand()
+ *
+ * Since: 1.4
+ */
+void
+clutter_table_layout_get_expand (ClutterTableLayout *layout,
+ ClutterActor *actor,
+ gboolean *x_expand,
+ gboolean *y_expand)
+{
+ ClutterTableLayoutPrivate *priv;
+ ClutterLayoutManager *manager;
+ ClutterLayoutMeta *meta;
+
+ g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ priv = layout->priv;
+
+ if (priv->container == NULL)
+ {
+ g_warning ("The layout of type '%s' must be associated to "
+ "a ClutterContainer before querying layout "
+ "properties",
+ G_OBJECT_TYPE_NAME (layout));
+ return;
+ }
+
+ manager = CLUTTER_LAYOUT_MANAGER (layout);
+ meta = clutter_layout_manager_get_child_meta (manager,
+ priv->container,
+ actor);
+ if (meta == NULL)
+ {
+ g_warning ("No layout meta found for the child of type '%s' "
+ "inside the layout manager of type '%s'",
+ G_OBJECT_TYPE_NAME (actor),
+ G_OBJECT_TYPE_NAME (manager));
+ return;
+ }
+
+ g_assert (CLUTTER_IS_TABLE_CHILD (meta));
+
+ if (x_expand)
+ *x_expand = CLUTTER_TABLE_CHILD (meta)->x_expand;
+
+ if (y_expand)
+ *y_expand = CLUTTER_TABLE_CHILD (meta)->y_expand;
+}
+
+/**
+ * clutter_table_layout_set_use_animations:
+ * @layout: a #ClutterTableLayout
+ * @animate: %TRUE if the @layout should use animations
+ *
+ * Sets whether @layout should animate changes in the layout properties
+ *
+ * The duration of the animations is controlled by
+ * clutter_table_layout_set_easing_duration(); the easing mode to be used
+ * by the animations is controlled by clutter_table_layout_set_easing_mode()
+ *
+ * Since: 1.4
+ */
+void
+clutter_table_layout_set_use_animations (ClutterTableLayout *layout,
+ gboolean animate)
+{
+ ClutterTableLayoutPrivate *priv;
+
+ g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
+
+ priv = layout->priv;
+
+ animate = !!animate;
+ if (priv->use_animations != animate)
+ {
+ priv->use_animations = animate;
+
+ g_object_notify (G_OBJECT (layout), "use-animations");
+ }
+}
+
+/**
+ * clutter_table_layout_get_use_animations:
+ * @layout: a #ClutterTableLayout
+ *
+ * Retrieves whether @layout should animate changes in the layout properties
+ *
+ * Since clutter_table_layout_set_use_animations()
+ *
+ * Return value: %TRUE if the animations should be used, %FALSE otherwise
+ *
+ * Since: 1.4
+ */
+gboolean
+clutter_table_layout_get_use_animations (ClutterTableLayout *layout)
+{
+ g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout), FALSE);
+
+ return layout->priv->use_animations;
+}
+
+/**
+ * clutter_table_layout_set_easing_mode:
+ * @layout: a #ClutterTableLayout
+ * @mode: an easing mode, either from #ClutterAnimationMode or a logical id
+ * from clutter_alpha_register_func()
+ *
+ * Sets the easing mode to be used by @layout when animating changes in layout
+ * properties
+ *
+ * Use clutter_table_layout_set_use_animations() to enable and disable the
+ * animations
+ *
+ * Since: 1.4
+ */
+void
+clutter_table_layout_set_easing_mode (ClutterTableLayout *layout,
+ gulong mode)
+{
+ ClutterTableLayoutPrivate *priv;
+
+ g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
+
+ priv = layout->priv;
+
+ if (priv->easing_mode != mode)
+ {
+ priv->easing_mode = mode;
+
+ g_object_notify (G_OBJECT (layout), "easing-mode");
+ }
+}
+
+/**
+ * clutter_table_layout_get_easing_mode:
+ * @layout: a #ClutterTableLayout
+ *
+ * Retrieves the easing mode set using clutter_table_layout_set_easing_mode()
+ *
+ * Return value: an easing mode
+ *
+ * Since: 1.4
+ */
+gulong
+clutter_table_layout_get_easing_mode (ClutterTableLayout *layout)
+{
+ g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout),
+ CLUTTER_EASE_OUT_CUBIC);
+
+ return layout->priv->easing_mode;
+}
+
+/**
+ * clutter_table_layout_set_easing_duration:
+ * @layout: a #ClutterTableLayout
+ * @msecs: the duration of the animations, in milliseconds
+ *
+ * Sets the duration of the animations used by @layout when animating changes
+ * in the layout properties
+ *
+ * Use clutter_table_layout_set_use_animations() to enable and disable the
+ * animations
+ *
+ * Since: 1.4
+ */
+void
+clutter_table_layout_set_easing_duration (ClutterTableLayout *layout,
+ guint msecs)
+{
+ ClutterTableLayoutPrivate *priv;
+
+ g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
+
+ priv = layout->priv;
+
+ if (priv->easing_duration != msecs)
+ {
+ priv->easing_duration = msecs;
+
+ g_object_notify (G_OBJECT (layout), "easing-duration");
+ }
+}
+
+/**
+ * clutter_table_layout_get_easing_duration:
+ * @layout: a #ClutterTableLayout
+ *
+ * Retrieves the duration set using clutter_table_layout_set_easing_duration()
+ *
+ * Return value: the duration of the animations, in milliseconds
+ *
+ * Since: 1.4
+ */
+guint
+clutter_table_layout_get_easing_duration (ClutterTableLayout *layout)
+{
+ g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout), 500);
+
+ return layout->priv->easing_duration;
+}
+
+
+/**
+ * clutter_table_layout_get_row_count:
+ * @layout: A #ClutterTableLayout
+ *
+ * Retrieve the current number rows in the @layout
+ *
+ * Returns: the number of rows
+ *
+ * Since: 1.4
+ */
+gint
+clutter_table_layout_get_row_count (ClutterTableLayout *layout)
+{
+ g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout), -1);
+
+ update_row_col (layout, layout->priv->container);
+ return CLUTTER_TABLE_LAYOUT (layout)->priv->n_rows;
+}
+
+/**
+ * clutter_table_layout_get_column_count:
+ * @layout: A #ClutterTableLayout
+ *
+ * Retrieve the current number of columns in @layout
+ *
+ * Returns: the number of columns
+ *
+ * Since: 1.4
+ */
+gint
+clutter_table_layout_get_column_count (ClutterTableLayout *layout)
+{
+ g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout), -1);
+
+ update_row_col (layout, layout->priv->container);
+ return CLUTTER_TABLE_LAYOUT (layout)->priv->n_cols;
+}
diff --git a/clutter/clutter-table-layout.h b/clutter/clutter-table-layout.h
new file mode 100644
index 000000000..bf7006abc
--- /dev/null
+++ b/clutter/clutter-table-layout.h
@@ -0,0 +1,163 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2010 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ *
+ * Author:
+ * Jose Dapena Paz
+ *
+ * Based on the MX MxTable actor by:
+ * Thomas Wood
+ */
+
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#ifndef __CLUTTER_TABLE_LAYOUT_H__
+#define __CLUTTER_TABLE_LAYOUT_H__
+
+#include
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_TABLE_LAYOUT (clutter_table_layout_get_type ())
+#define CLUTTER_TABLE_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_TABLE_LAYOUT, ClutterTableLayout))
+#define CLUTTER_IS_TABLE_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_TABLE_LAYOUT))
+#define CLUTTER_TABLE_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_TABLE_LAYOUT, ClutterTableLayoutClass))
+#define CLUTTER_IS_TABLE_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_TABLE_LAYOUT))
+#define CLUTTER_TABLE_LAYOUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_TABLE_LAYOUT, ClutterTableLayoutClass))
+
+typedef struct _ClutterTableLayout ClutterTableLayout;
+typedef struct _ClutterTableLayoutPrivate ClutterTableLayoutPrivate;
+typedef struct _ClutterTableLayoutClass ClutterTableLayoutClass;
+
+/**
+ * ClutterTableAlignment:
+ * @CLUTTER_TABLE_ALIGNMENT_START: Align the child to the top or to the
+ * left of a cell in the table, depending on the axis
+ * @CLUTTER_TABLE_ALIGNMENT_CENTER: Align the child to the center of
+ * a cell in the table
+ * @CLUTTER_TABLE_ALIGNMENT_END: Align the child to the bottom or to the
+ * right of a cell in the table, depending on the axis
+ *
+ * The alignment policies available on each axis of the #ClutterTableLayout
+ *
+ * Since: 1.4
+ */
+typedef enum {
+ CLUTTER_TABLE_ALIGNMENT_START,
+ CLUTTER_TABLE_ALIGNMENT_CENTER,
+ CLUTTER_TABLE_ALIGNMENT_END
+} ClutterTableAlignment;
+
+/**
+ * ClutterTableLayout:
+ *
+ * The #ClutterTableLayout structure contains only private data
+ * and should be accessed using the provided API
+ *
+ * Since: 1.4
+ */
+struct _ClutterTableLayout
+{
+ /*< private >*/
+ ClutterLayoutManager parent_instance;
+
+ ClutterTableLayoutPrivate *priv;
+};
+
+/**
+ * ClutterTableLayoutClass:
+ *
+ * The #ClutterTableLayoutClass structure contains only private
+ * data and should be accessed using the provided API
+ *
+ * Since: 1.4
+ */
+struct _ClutterTableLayoutClass
+{
+ /*< private >*/
+ ClutterLayoutManagerClass parent_class;
+};
+
+GType clutter_table_layout_get_type (void) G_GNUC_CONST;
+
+ClutterLayoutManager *clutter_table_layout_new (void);
+
+void clutter_table_layout_pack (ClutterTableLayout *layout,
+ ClutterActor *actor,
+ gint row,
+ gint column);
+
+void clutter_table_layout_set_column_spacing (ClutterTableLayout *layout,
+ guint spacing);
+void clutter_table_layout_set_row_spacing (ClutterTableLayout *layout,
+ guint spacing);
+guint clutter_table_layout_get_column_spacing (ClutterTableLayout *layout);
+guint clutter_table_layout_get_row_spacing (ClutterTableLayout *layout);
+
+void clutter_table_layout_set_span (ClutterTableLayout *layout,
+ ClutterActor *actor,
+ gint column_span,
+ gint row_span);
+void clutter_table_layout_get_span (ClutterTableLayout *layout,
+ ClutterActor *actor,
+ gint *column_span,
+ gint *row_span);
+void clutter_table_layout_set_alignment (ClutterTableLayout *layout,
+ ClutterActor *actor,
+ ClutterTableAlignment x_align,
+ ClutterTableAlignment y_align);
+void clutter_table_layout_get_alignment (ClutterTableLayout *layout,
+ ClutterActor *actor,
+ ClutterTableAlignment *x_align,
+ ClutterTableAlignment *y_align);
+void clutter_table_layout_set_fill (ClutterTableLayout *layout,
+ ClutterActor *actor,
+ gboolean x_fill,
+ gboolean y_fill);
+void clutter_table_layout_get_fill (ClutterTableLayout *layout,
+ ClutterActor *actor,
+ gboolean *x_fill,
+ gboolean *y_fill);
+void clutter_table_layout_set_expand (ClutterTableLayout *layout,
+ ClutterActor *actor,
+ gboolean x_expand,
+ gboolean y_expand);
+void clutter_table_layout_get_expand (ClutterTableLayout *layout,
+ ClutterActor *actor,
+ gboolean *x_expand,
+ gboolean *y_expand);
+
+gint clutter_table_layout_get_row_count (ClutterTableLayout *layout);
+gint clutter_table_layout_get_column_count (ClutterTableLayout *layout);
+
+void clutter_table_layout_set_use_animations (ClutterTableLayout *layout,
+ gboolean animate);
+gboolean clutter_table_layout_get_use_animations (ClutterTableLayout *layout);
+void clutter_table_layout_set_easing_mode (ClutterTableLayout *layout,
+ gulong mode);
+gulong clutter_table_layout_get_easing_mode (ClutterTableLayout *layout);
+void clutter_table_layout_set_easing_duration (ClutterTableLayout *layout,
+ guint msecs);
+guint clutter_table_layout_get_easing_duration (ClutterTableLayout *layout);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_TABLE_LAYOUT_H__ */
diff --git a/clutter/clutter.h b/clutter/clutter.h
index 7fae0cd6c..18145472f 100644
--- a/clutter/clutter.h
+++ b/clutter/clutter.h
@@ -95,6 +95,7 @@
#include "clutter-stage-manager.h"
#include "clutter-stage-window.h"
#include "clutter-state.h"
+#include "clutter-table-layout.h"
#include "clutter-texture.h"
#include "clutter-text.h"
#include "clutter-timeline.h"
diff --git a/doc/reference/clutter/clutter-docs.xml.in b/doc/reference/clutter/clutter-docs.xml.in
index 22f9d21e0..fd6ea0cc0 100644
--- a/doc/reference/clutter/clutter-docs.xml.in
+++ b/doc/reference/clutter/clutter-docs.xml.in
@@ -90,6 +90,7 @@
+
diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt
index d92816525..e7859011c 100644
--- a/doc/reference/clutter/clutter-sections.txt
+++ b/doc/reference/clutter/clutter-sections.txt
@@ -2068,6 +2068,53 @@ ClutterBoxLayoutPrivate
clutter_box_layout_get_type
+
+clutter-table-layout
+ClutterTableAlignment
+ClutterTableLayout
+ClutterTableLayoutClass
+clutter_table_layout_new
+clutter_table_layout_set_row_spacing
+clutter_table_layout_get_row_spacing
+clutter_table_layout_set_column_spacing
+clutter_table_layout_get_column_spacing
+clutter_table_layout_get_row_count
+clutter_table_layout_get_column_count
+
+
+clutter_table_layout_pack
+
+
+clutter_table_layout_set_alignment
+clutter_table_layout_get_alignment
+clutter_table_layout_set_expand
+clutter_table_layout_get_expand
+clutter_table_layout_set_fill
+clutter_table_layout_get_fill
+clutter_table_layout_get_span
+clutter_table_layout_set_span
+
+
+clutter_table_layout_set_use_animations
+clutter_table_layout_get_use_animations
+clutter_table_layout_set_easing_duration
+clutter_table_layout_get_easing_duration
+clutter_table_layout_set_easing_mode
+clutter_table_layout_get_easing_mode
+
+
+CLUTTER_TYPE_TABLE_LAYOUT
+CLUTTER_TABLE_LAYOUT
+CLUTTER_TABLE_LAYOUT_CLASS
+CLUTTER_IS_TABLE_LAYOUT
+CLUTTER_IS_TABLE_LAYOUT_CLASS
+CLUTTER_TABLE_LAYOUT_GET_CLASS
+
+
+ClutterTableLayoutPrivate
+clutter_table_layout_get_type
+
+
clutter-animator
ClutterAnimator
diff --git a/doc/reference/clutter/clutter.types b/doc/reference/clutter/clutter.types
index 0541f969f..86b1c698e 100644
--- a/doc/reference/clutter/clutter.types
+++ b/doc/reference/clutter/clutter.types
@@ -56,6 +56,7 @@ clutter_shader_effect_get_type
clutter_stage_get_type
clutter_stage_manager_get_type
clutter_state_get_type
+clutter_table_layout_get_type
clutter_text_get_type
clutter_texture_get_type
clutter_timeline_get_type
diff --git a/tests/interactive/.gitignore b/tests/interactive/.gitignore
index fa5269a33..ea21db2f1 100644
--- a/tests/interactive/.gitignore
+++ b/tests/interactive/.gitignore
@@ -71,3 +71,4 @@
/test-scrolling
/test-bind
/test-cogl-point-sprites
+/test-table-layout
diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am
index 190436feb..58eeae56e 100644
--- a/tests/interactive/Makefile.am
+++ b/tests/interactive/Makefile.am
@@ -56,7 +56,8 @@ UNIT_TESTS = \
test-constraints.c \
test-scrolling.c \
test-bind.c \
- test-cogl-point-sprites.c
+ test-cogl-point-sprites.c \
+ test-table-layout.c
if X11_TESTS
UNIT_TESTS += test-pixmap.c
diff --git a/tests/interactive/test-table-layout.c b/tests/interactive/test-table-layout.c
new file mode 100644
index 000000000..fe39bc339
--- /dev/null
+++ b/tests/interactive/test-table-layout.c
@@ -0,0 +1,275 @@
+#include
+#include
+
+#include
+
+#include
+#include
+#include "pango/cogl-pango.h"
+
+#define FONT "Sans 12"
+
+static void
+set_text (ClutterActor *actor, const gchar *text)
+{
+ GList *children, *l;
+
+ children = clutter_container_get_children (CLUTTER_CONTAINER (actor));
+ for (l = children; l; l = g_list_next (l)) {
+ if (CLUTTER_IS_TEXT (l->data)) {
+ clutter_text_set_text (CLUTTER_TEXT (l->data), text);
+ break;
+ }
+ }
+ g_list_free (children);
+}
+
+static void
+toggle_expand (ClutterActor *actor, ClutterEvent *event, ClutterBox *box)
+{
+ gboolean x_expand;
+ gchar *label;
+ ClutterLayoutManager *layout = clutter_box_get_layout_manager (box);
+
+
+ clutter_layout_manager_child_get (layout, CLUTTER_CONTAINER (box), actor,
+ "x-expand", &x_expand,
+ NULL);
+
+ x_expand = !x_expand;
+
+ clutter_layout_manager_child_set (layout, CLUTTER_CONTAINER (box), actor,
+ "x-expand", x_expand,
+ "y-expand", x_expand,
+ NULL);
+
+ label = g_strdup_printf ("Expand = %d", x_expand);
+ set_text (actor, label);
+
+ g_free (label);
+}
+
+static const gchar *
+get_alignment_name (ClutterTableAlignment alignment)
+{
+ switch (alignment)
+ {
+ case CLUTTER_TABLE_ALIGNMENT_START:
+ return "start";
+
+ case CLUTTER_TABLE_ALIGNMENT_CENTER:
+ return "center";
+
+ case CLUTTER_TABLE_ALIGNMENT_END:
+ return "end";
+ }
+
+ return "undefined";
+}
+
+static void
+randomise_align (ClutterActor *actor, ClutterEvent *event, ClutterBox *box)
+{
+ ClutterTableAlignment x_align, y_align;
+ gchar *label;
+ ClutterLayoutManager *layout;
+
+ layout = clutter_box_get_layout_manager (box);
+
+ x_align = (ClutterTableAlignment) g_random_int_range (0, 3);
+ y_align = (ClutterTableAlignment) g_random_int_range (0, 3);
+
+ clutter_layout_manager_child_set (layout, CLUTTER_CONTAINER (box), actor,
+ "x-align", x_align,
+ "y-align", y_align,
+ NULL);
+
+ label = g_strdup_printf ("Align (%s, %s)",
+ get_alignment_name (x_align),
+ get_alignment_name (y_align));
+ set_text (actor, label);
+ g_free (label);
+}
+
+static void
+toggle_visible (ClutterActor *actor, ClutterEvent *event, gpointer userdata)
+{
+ clutter_actor_hide (actor);
+}
+
+gboolean drag = FALSE;
+
+static ClutterActor *
+create_cell (ClutterActor *actor, const gchar *color_str)
+{
+ ClutterActor *result;
+ ClutterActor *rectangle;
+ ClutterColor color;
+
+ result =
+ clutter_box_new (clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_FILL,
+ CLUTTER_BIN_ALIGNMENT_FILL));
+
+ rectangle = clutter_rectangle_new ();
+ clutter_color_from_string (&color, color_str);
+ clutter_rectangle_set_color (CLUTTER_RECTANGLE (rectangle), (const ClutterColor *) &color);
+ clutter_color_from_string (&color, "#000f");
+ clutter_rectangle_set_border_color (CLUTTER_RECTANGLE (rectangle), (const ClutterColor *) &color);
+ clutter_rectangle_set_border_width (CLUTTER_RECTANGLE (rectangle), 2);
+
+ clutter_actor_show (rectangle);
+ clutter_actor_set_reactive (result, TRUE);
+ clutter_container_add_actor (CLUTTER_CONTAINER (result), rectangle);
+ clutter_box_pack (CLUTTER_BOX (result), actor,
+ "x-align", CLUTTER_BIN_ALIGNMENT_CENTER,
+ "y-align", CLUTTER_BIN_ALIGNMENT_CENTER,
+ NULL);
+
+ return result;
+}
+
+static ClutterActor *
+create_text (const gchar *label, const gchar *color)
+{
+ ClutterActor *text;
+ ClutterActor *result;
+
+ text = clutter_text_new_with_text (FONT, label);
+ clutter_actor_show (text);
+
+ result = create_cell (text, color);
+ clutter_actor_show (result);
+
+ return result;
+}
+
+static ClutterActor *
+create_image (const gchar *file, const gchar *color)
+{
+ ClutterActor *texture;
+ ClutterActor *result;
+
+ texture = clutter_texture_new_from_file (file, NULL);
+ g_object_set (G_OBJECT (texture), "keep-aspect-ratio", TRUE, NULL);
+ clutter_actor_show (texture);
+
+ result = create_cell (texture, color);
+ clutter_actor_show (result);
+
+ return result;
+}
+
+G_MODULE_EXPORT int
+test_table_layout_main (int argc, char *argv[])
+{
+ ClutterActor *stage;
+ ClutterLayoutManager *layout;
+ ClutterActor *actor1, *actor2, *actor3, *actor4, *actor5, *actor6, *actor7, *actor8, *actor9, *actor10;
+ ClutterActor *box;
+ gchar *file;
+
+ clutter_init (&argc, &argv);
+
+ stage = clutter_stage_get_default ();
+ clutter_stage_set_title (CLUTTER_STAGE (stage), "Table Layout");
+ clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE);
+ clutter_actor_set_size (stage, 640, 480);
+
+ layout = clutter_table_layout_new ();
+ clutter_table_layout_set_column_spacing (CLUTTER_TABLE_LAYOUT (layout), 10);
+ clutter_table_layout_set_row_spacing (CLUTTER_TABLE_LAYOUT (layout), 10);
+
+ box = clutter_box_new (layout);
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), box);
+ clutter_actor_add_constraint (box, clutter_bind_constraint_new (stage, CLUTTER_BIND_WIDTH, -10.0));
+ clutter_actor_add_constraint (box, clutter_bind_constraint_new (stage, CLUTTER_BIND_HEIGHT, -10.0));
+
+ actor1 = create_text ("label 1", "#f66f");
+ file = g_build_filename (TESTS_DATADIR, "redhand.png", NULL);
+ actor2 = create_image (file, "#bbcf");
+ g_free (file);
+ actor3 = create_text ("label 3", "#6f6f");
+ actor4 = create_text ("Expand = 1", "#66ff");
+ actor5 = create_text ("label 5", "#f6ff");
+ actor6 = create_text ("label 6", "#6fff");
+ actor7 = create_text ("Align (center, center)", "#66ff");
+ actor8 = create_text ("label 8", "#ffff");
+ actor9 = create_text ("label 9", "#666f");
+ actor10 = create_text ("label 10", "#aaaf");
+
+ clutter_table_layout_pack (CLUTTER_TABLE_LAYOUT (layout), actor1, 0, 0);
+ clutter_table_layout_pack (CLUTTER_TABLE_LAYOUT (layout), actor2, 0, 1);
+ clutter_table_layout_pack (CLUTTER_TABLE_LAYOUT (layout), actor3, 1, 1);
+ clutter_table_layout_pack (CLUTTER_TABLE_LAYOUT (layout), actor4, 2, 0);
+ clutter_table_layout_pack (CLUTTER_TABLE_LAYOUT (layout), actor5, 3, 0);
+ clutter_table_layout_pack (CLUTTER_TABLE_LAYOUT (layout), actor6, 3, 1);
+ clutter_table_layout_pack (CLUTTER_TABLE_LAYOUT (layout), actor7, 4, 1);
+ clutter_table_layout_pack (CLUTTER_TABLE_LAYOUT (layout), actor8, 4, 0);
+ clutter_table_layout_pack (CLUTTER_TABLE_LAYOUT (layout), actor9, 5, 0);
+ clutter_table_layout_pack (CLUTTER_TABLE_LAYOUT (layout), actor10, -1, 0);
+ clutter_table_layout_set_span (CLUTTER_TABLE_LAYOUT (layout), actor1, 2, 1);
+ clutter_table_layout_set_span (CLUTTER_TABLE_LAYOUT (layout), actor7, 2, 1);
+ clutter_table_layout_set_span (CLUTTER_TABLE_LAYOUT (layout), actor4, 1, 2);
+
+ clutter_actor_set_size (actor1, 100, 100);
+ clutter_actor_set_width (actor4, 250);
+
+ clutter_layout_manager_child_set (CLUTTER_LAYOUT_MANAGER (layout),
+ CLUTTER_CONTAINER (box),
+ actor1,
+ "x-expand", FALSE, "y-expand", FALSE,
+ NULL);
+ clutter_layout_manager_child_set (CLUTTER_LAYOUT_MANAGER (layout),
+ CLUTTER_CONTAINER (box),
+ actor4,
+ "x-expand", TRUE, "y-expand", TRUE,
+ "x-fill", TRUE, "y-fill", TRUE,
+ NULL);
+ clutter_layout_manager_child_set (CLUTTER_LAYOUT_MANAGER (layout),
+ CLUTTER_CONTAINER (box),
+ actor7,
+ "x-expand", TRUE, "y-expand", TRUE,
+ "x-fill", FALSE, "y-fill", FALSE,
+ NULL);
+ clutter_layout_manager_child_set (CLUTTER_LAYOUT_MANAGER (layout),
+ CLUTTER_CONTAINER (box),
+ actor8,
+ "x-expand", FALSE, "y-expand", FALSE,
+ NULL);
+ clutter_layout_manager_child_set (CLUTTER_LAYOUT_MANAGER (layout),
+ CLUTTER_CONTAINER (box),
+ actor9,
+ "x-expand", FALSE, "y-expand", FALSE,
+ NULL);
+
+ clutter_layout_manager_child_set (CLUTTER_LAYOUT_MANAGER (layout),
+ CLUTTER_CONTAINER (box),
+ actor2,
+ "y-fill", FALSE,
+ "x-fill", FALSE,
+ NULL);
+
+ clutter_actor_set_position (box, 5, 5);
+
+ g_signal_connect (actor4, "button-release-event", G_CALLBACK (toggle_expand), box);
+ g_signal_connect (actor7, "button-release-event", G_CALLBACK (randomise_align), box);
+ g_signal_connect (actor10, "button-release-event", G_CALLBACK (toggle_visible), NULL);
+
+ /* g_signal_connect (stage, "button-press-event", G_CALLBACK (button_press), */
+ /* box); */
+ /* g_signal_connect (stage, "motion-event", G_CALLBACK (motion_event), */
+ /* box); */
+ /* g_signal_connect (stage, "button-release-event", G_CALLBACK (button_release), */
+ /* box); */
+
+ clutter_actor_show (stage);
+
+ g_debug ("table row count = %d",
+ clutter_table_layout_get_row_count (CLUTTER_TABLE_LAYOUT (layout)));
+ g_debug ("table column count = %d",
+ clutter_table_layout_get_column_count (CLUTTER_TABLE_LAYOUT (layout)));
+
+ clutter_main ();
+
+ return EXIT_SUCCESS;
+}