[libegg] Add spreadtable



commit 6881bab42266316e7085a0c1a807cea2f584c5eb
Author: David King <davidk openismus com>
Date:   Thu Oct 28 15:45:19 2010 +0200

    Add spreadtable
    
    EggSpreadTable is a dump of the GtkSpreadTable code from the
    spread-table branch of gtk+. It modifies configure.in to add a check for
    GTK+ 3, and enables the spreadtable subdirectory if GTK+ 3 is installed.

 configure.in                         |    8 +
 libegg/Makefile.am                   |    8 +-
 libegg/spreadtable/Makefile.am       |   30 +
 libegg/spreadtable/eggspreadtable.c  | 1126 ++++++++++++++++++++++++++++++++++
 libegg/spreadtable/eggspreadtable.h  |   89 +++
 libegg/spreadtable/testspreadtable.c |  393 ++++++++++++
 6 files changed, 1653 insertions(+), 1 deletions(-)
---
diff --git a/configure.in b/configure.in
index fd180f1..5a107e4 100644
--- a/configure.in
+++ b/configure.in
@@ -106,6 +106,13 @@ AM_CONDITIONAL(HAVE_GTK210, $PKG_CONFIG --atleast-version=2.10.0 gtk+-2.0)
 ## check if we have gtk+ 2.15.x
 AM_CONDITIONAL(HAVE_GTK215, $PKG_CONFIG --atleast-version=2.15.0 gtk+-2.0)
 
+## check if we have gtk+ 3
+AM_CONDITIONAL(HAVE_GTK30, $PKG_CONFIG --atleast-version=2.91.1 gtk+-3.0)
+
+AM_COND_IF(HAVE_GTK30,
+  [PKG_CHECK_MODULES(EGG_30, gtk+-3.0)
+  AC_CONFIG_FILES()])
+
 ## check if we have the X11 gtk+ backend
 AM_CONDITIONAL(HAVE_GTK_TARGET_X11, test `$PKG_CONFIG --variable=target gtk+-2.0` = x11)
 
@@ -144,6 +151,7 @@ libegg/pixbufthumbnail/Makefile
 libegg/thumbnailpreview/Makefile
 libegg/iconchooser/Makefile
 libegg/smclient/Makefile
+libegg/spreadtable/Makefile
 libegg/fileformatchooser/Makefile
 doc/Makefile
 ])
diff --git a/libegg/Makefile.am b/libegg/Makefile.am
index 149a447..cc05c4f 100644
--- a/libegg/Makefile.am
+++ b/libegg/Makefile.am
@@ -1,3 +1,8 @@
+if HAVE_GTK30
+spreadtable_SUBDIRS = \
+  spreadtable
+endif
+
 if HAVE_GTK25
 thumbnail_SUBDIRS = \
   pixbufthumbnail \
@@ -28,7 +33,8 @@ SUBDIRS = util column-chooser treeviewutils \
   dock datetime $(thumbnail_SUBDIRS) \
   smclient \
   $(gtk210_SUBDIRS) \
-  $(gtk215_SUBDIRS)
+  $(gtk215_SUBDIRS) \
+  $(gtk30_SUBDIRS)
 
 # Don't compile broken code
 #$(iconchooser_SUBDIRS)
diff --git a/libegg/spreadtable/Makefile.am b/libegg/spreadtable/Makefile.am
new file mode 100644
index 0000000..5670e9d
--- /dev/null
+++ b/libegg/spreadtable/Makefile.am
@@ -0,0 +1,30 @@
+INCLUDES = \
+  $(EGG_30_CFLAGS) \
+  -DEGG_COMPILATION \
+  -DGTK_DISABLE_DEPRECATED \
+  -DGDK_DISABLE_DEPRECATED \
+  -DG_DISABLE_DEPRECATED \
+  -DGETTEXT_PACKAGE=\"libegg\"
+
+noinst_LTLIBRARIES = libegg-spreadtable.la
+
+libegg_spreadtable_la_LIBADD = $(EGG_30_LIBS)
+
+EGGSOURCES = \
+	eggspreadtable.c
+
+EGGHEADERS = \
+	eggspreadtable.h
+
+libegg_spreadtable_la_SOURCES = $(EGGSOURCES)
+
+noinst_HEADERS = $(EGG_HEADERS)
+
+#Test program
+
+noinst_PROGRAMS = testspreadtable
+
+testspreadtable_SOURCES = testspreadtable.c
+
+testspreadtable_LDADD = $(EGG_30_LIBS) \
+  $(top_builddir)/libegg/spreadtable/libegg-spreadtable.la
diff --git a/libegg/spreadtable/eggspreadtable.c b/libegg/spreadtable/eggspreadtable.c
new file mode 100644
index 0000000..fe20d64
--- /dev/null
+++ b/libegg/spreadtable/eggspreadtable.c
@@ -0,0 +1,1126 @@
+/* gtkspreadtable.c
+ * Copyright (C) 2007-2010 Openismus GmbH
+ *
+ * Authors:
+ *      Tristan Van Berkom <tristanvb openismus com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:gtkspreadtable
+ * @Short_Description: A container that distributes its children evenly across rows/columns.
+ * @Title: EggSpreadTable
+ *
+ * #EggSpreadTable positions its children by distributing them as
+ * evenly as possible across a fixed number of rows or columns.
+ *
+ * When oriented vertically the #EggSpreadTable will list its
+ * children in order from top to bottom in columns and request
+ * the smallest height as possible regardless of differences in
+ * child sizes.
+ */
+
+#include "config.h"
+#include <gtk/gtk.h>
+#include <string.h>
+#include "eggspreadtable.h"
+
+#define DEFAULT_LINES 2
+#define P_(msgid) (msgid)
+
+enum {
+  PROP_0,
+  PROP_ORIENTATION,
+  PROP_HORIZONTAL_SPACING,
+  PROP_VERTICAL_SPACING,
+  PROP_LINES
+};
+
+struct _EggSpreadTablePrivate {
+  GList         *children;
+
+  GtkOrientation orientation;
+
+  guint16        lines;
+  guint16        horizontal_spacing;
+  guint16        vertical_spacing;
+};
+
+/* GObjectClass */
+static void egg_spread_table_get_property         (GObject             *object,
+						   guint                prop_id,
+						   GValue              *value,
+						   GParamSpec          *pspec);
+static void egg_spread_table_set_property         (GObject             *object,
+						   guint                prop_id,
+						   const GValue        *value,
+						   GParamSpec          *pspec);
+
+/* GtkWidgetClass */
+
+static GtkSizeRequestMode egg_spread_table_get_request_mode (GtkWidget *widget);
+static void egg_spread_table_get_width            (GtkWidget           *widget,
+						   gint                *minimum_size,
+						   gint                *natural_size);
+static void egg_spread_table_get_height           (GtkWidget           *widget,
+						   gint                *minimum_size,
+						   gint                *natural_size);
+static void egg_spread_table_get_height_for_width (GtkWidget           *widget,
+						   gint                 width,
+						   gint                *minimum_height,
+						   gint                *natural_height);
+static void egg_spread_table_get_width_for_height (GtkWidget           *widget,
+						   gint                 width,
+						   gint                *minimum_height,
+						   gint                *natural_height);
+static void egg_spread_table_size_allocate        (GtkWidget           *widget,
+						   GtkAllocation       *allocation);
+
+/* GtkContainerClass */
+static void egg_spread_table_add                  (GtkContainer        *container,
+						   GtkWidget           *widget);
+static void egg_spread_table_remove               (GtkContainer        *container,
+						   GtkWidget           *widget);
+
+
+static void egg_spread_table_forall               (GtkContainer        *container,
+						   gboolean             include_internals,
+						   GtkCallback          callback,
+						   gpointer             callback_data); 
+static GType egg_spread_table_child_type          (GtkContainer        *container);
+
+
+G_DEFINE_TYPE_WITH_CODE (EggSpreadTable, egg_spread_table, GTK_TYPE_CONTAINER,
+			 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
+
+#define ITEM_SPACING(table)						\
+  (((EggSpreadTable *)(table))->priv->orientation == GTK_ORIENTATION_HORIZONTAL ? \
+   ((EggSpreadTable *)(table))->priv->horizontal_spacing :		\
+   ((EggSpreadTable *)(table))->priv->vertical_spacing)
+
+#define LINE_SPACING(table)						\
+  (((EggSpreadTable *)(table))->priv->orientation == GTK_ORIENTATION_HORIZONTAL ? \
+   ((EggSpreadTable *)(table))->priv->vertical_spacing :		\
+   ((EggSpreadTable *)(table))->priv->horizontal_spacing)
+
+#define OPPOSITE_ORIENTATION(table)					\
+  (((EggSpreadTable *)(table))->priv->orientation == GTK_ORIENTATION_HORIZONTAL ? \
+   GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL)
+
+static void
+egg_spread_table_class_init (EggSpreadTableClass *class)
+{
+  GObjectClass      *gobject_class    = G_OBJECT_CLASS (class);
+  GtkWidgetClass    *widget_class     = GTK_WIDGET_CLASS (class);
+  GtkContainerClass *container_class  = GTK_CONTAINER_CLASS (class);
+  
+  gobject_class->get_property         = egg_spread_table_get_property;
+  gobject_class->set_property         = egg_spread_table_set_property;
+
+  widget_class->get_request_mode               = egg_spread_table_get_request_mode;
+  widget_class->get_preferred_width            = egg_spread_table_get_width;
+  widget_class->get_preferred_height           = egg_spread_table_get_height;
+  widget_class->get_preferred_height_for_width = egg_spread_table_get_height_for_width;
+  widget_class->get_preferred_width_for_height = egg_spread_table_get_width_for_height;
+  widget_class->size_allocate                  = egg_spread_table_size_allocate;
+  
+  container_class->add                = egg_spread_table_add;
+  container_class->remove             = egg_spread_table_remove;
+  container_class->forall             = egg_spread_table_forall;
+  container_class->child_type         = egg_spread_table_child_type;
+
+  gtk_container_class_handle_border_width (container_class);
+
+  /* GObjectClass properties */
+  g_object_class_override_property (gobject_class, PROP_ORIENTATION, "orientation");
+
+  /**
+   * EggSpreadTable:lines:
+   *
+   * The number of lines (rows/columns) to evenly distribute children to.
+   *
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_LINES,
+                                   g_param_spec_uint ("lines",
+						      P_("Lines"),
+						      P_("The number of lines (rows/columns) to "
+							 "evenly distribute children to."),
+						      1,
+						      65535,
+						      DEFAULT_LINES,
+						      G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+
+  /**
+   * EggSpreadTable:vertical-spacing:
+   *
+   * The amount of vertical space between two children.
+   *
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_VERTICAL_SPACING,
+                                   g_param_spec_uint ("vertical-spacing",
+						      P_("Vertical spacing"),
+						      P_("The amount of vertical space between two children"),
+						      0,
+						      65535,
+						      0,
+						      G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+  /**
+   * EggSpreadTable:horizontal-spacing:
+   *
+   * The amount of horizontal space between two children.
+   *
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_HORIZONTAL_SPACING,
+                                   g_param_spec_uint ("horizontal-spacing",
+						      P_("Horizontal spacing"),
+						      P_("The amount of horizontal space between two children"),
+						      0,
+						      65535,
+						      0,
+						      G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+  g_type_class_add_private (class, sizeof (EggSpreadTablePrivate));
+}
+
+static void
+egg_spread_table_init (EggSpreadTable *spread_table)
+{
+  EggSpreadTablePrivate *priv; 
+  
+  spread_table->priv = priv = 
+    G_TYPE_INSTANCE_GET_PRIVATE (spread_table, EGG_TYPE_SPREAD_TABLE, EggSpreadTablePrivate);
+
+  /* We are vertical by default */
+  priv->orientation = GTK_ORIENTATION_VERTICAL;
+  priv->lines       = DEFAULT_LINES;
+
+  gtk_widget_set_has_window (GTK_WIDGET (spread_table), FALSE);
+}
+
+/*****************************************************
+ *                  GObectClass                      * 
+ *****************************************************/
+static void
+egg_spread_table_get_property (GObject      *object,
+			       guint         prop_id,
+			       GValue       *value,
+			       GParamSpec   *pspec)
+{
+  EggSpreadTable        *table = EGG_SPREAD_TABLE (object);
+  EggSpreadTablePrivate *priv  = table->priv;
+
+  switch (prop_id)
+    {
+    case PROP_ORIENTATION:
+      g_value_set_enum (value, priv->orientation);
+      break;
+    case PROP_LINES:
+      g_value_set_uint (value, egg_spread_table_get_lines (table));
+      break;
+    case PROP_VERTICAL_SPACING:
+      g_value_set_uint (value, egg_spread_table_get_vertical_spacing (table));
+      break;
+    case PROP_HORIZONTAL_SPACING:
+      g_value_set_uint (value, egg_spread_table_get_horizontal_spacing (table));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+egg_spread_table_set_property (GObject      *object,
+			       guint         prop_id,
+			       const GValue *value,
+			       GParamSpec   *pspec)
+{
+  EggSpreadTable        *table = EGG_SPREAD_TABLE (object);
+  EggSpreadTablePrivate *priv  = table->priv;
+
+  switch (prop_id)
+    {
+    case PROP_ORIENTATION:
+      priv->orientation = g_value_get_enum (value);
+
+      /* Re-spread_table the children in the new orientation */
+      gtk_widget_queue_resize (GTK_WIDGET (table));
+      break;
+    case PROP_LINES:
+      egg_spread_table_set_lines (table, g_value_get_uint (value));
+      break;
+    case PROP_HORIZONTAL_SPACING:
+      egg_spread_table_set_horizontal_spacing (table, g_value_get_uint (value));
+      break;
+    case PROP_VERTICAL_SPACING:
+      egg_spread_table_set_vertical_spacing (table, g_value_get_uint (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+
+
+/*****************************************************
+ *  Geometric helper functions for request/allocate  * 
+ *****************************************************/
+static void 
+get_widget_size (GtkWidget      *widget,
+		 GtkOrientation  orientation,
+		 gint            for_size,
+		 gint           *min_size,
+		 gint           *nat_size)
+{
+  if (orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      if (for_size < 0)
+	gtk_widget_get_preferred_width (widget, min_size, nat_size);
+      else
+	gtk_widget_get_preferred_width_for_height (widget, for_size, min_size, nat_size);
+    }
+  else
+    {
+      if (for_size < 0)
+	gtk_widget_get_preferred_height (widget, min_size, nat_size);
+      else
+	gtk_widget_get_preferred_height_for_width (widget, for_size, min_size, nat_size);
+    }
+}
+
+
+static GList *
+get_visible_children (EggSpreadTable *table)
+{
+  EggSpreadTablePrivate  *priv = table->priv;
+  GtkWidget              *child;
+  GList                  *list, *visible = NULL;
+
+  for (list = priv->children; list; list = list->next)
+    {
+      child = list->data;
+
+      if (!gtk_widget_get_visible (child))
+        continue;
+
+      visible = g_list_prepend (visible, child);
+    }
+
+  return g_list_reverse (visible);
+}
+
+/* This gets the widest child, it is used to reserve 
+ * enough space for (columns * widest_child) 
+ */
+static void
+get_largest_line_thickness (EggSpreadTable *table, 
+			    gint           *min_thickness,
+			    gint           *nat_thickness)
+{
+  EggSpreadTablePrivate  *priv = table->priv;
+  GList                  *list;
+  gint                    min_size = 0, nat_size = 0;
+  GtkOrientation          opposite_orientation;
+
+  opposite_orientation = OPPOSITE_ORIENTATION (table);
+
+  for (list = priv->children; list; list = list->next)
+    {
+      GtkWidget *child = list->data;
+      gint       child_min, child_nat;
+
+      if (!gtk_widget_get_visible (child))
+        continue;
+
+      get_widget_size (child, opposite_orientation, -1, &child_min, &child_nat);
+
+      min_size = MAX (min_size, child_min);
+      nat_size = MAX (nat_size, child_nat);
+    }
+
+  *min_thickness = min_size;
+  *nat_thickness = nat_size;
+}
+
+/* Gets the column width for a given width */
+static gint 
+get_line_thickness (EggSpreadTable *table,
+		    gint            for_thickness)
+{
+  EggSpreadTablePrivate  *priv = table->priv;
+  gint                    line_thickness;
+
+  /* Get the available size per line (when vertical, we are getting the column width here) */
+  line_thickness = for_thickness - (priv->lines -1) * LINE_SPACING (table);
+  line_thickness = line_thickness / priv->lines;
+
+  return line_thickness;
+}
+
+/* Gets the overall height of a column (length of a line segment) */
+static gint
+get_segment_length (EggSpreadTable *table,
+		    gint            line_thickness,
+		    GList          *seg_children)
+{
+  EggSpreadTablePrivate  *priv = table->priv;
+  GList                  *list;
+  gint                    size = 0, i = 0;
+  gint                    spacing;
+
+  spacing = ITEM_SPACING (table);
+
+  for (i = 0, list = seg_children; list; list = list->next)
+    {
+      GtkWidget *child = list->data;
+      gint       child_nat;
+
+      if (!gtk_widget_get_visible (child))
+        continue;
+
+      get_widget_size (child, priv->orientation, line_thickness, NULL, &child_nat);
+
+      size += child_nat;
+
+      if (i != 0)
+	size += spacing;
+
+      i++;
+    }
+
+  return size;
+}
+
+static gboolean
+children_fit_segment_size (EggSpreadTable *table, 
+			   GList          *children,
+			   gint            line_thickness,
+			   gint            size,
+			   gint           *segments,
+			   gint           *largest_segment_size)
+{
+  EggSpreadTablePrivate  *priv;
+  GList                  *l;
+  gint                    segment_size, i;
+  gint                    spacing;
+
+  priv    = table->priv;
+  spacing = ITEM_SPACING (table);
+
+  /* reset segments */
+  memset (segments, 0x0, sizeof (gint) * priv->lines);
+
+  for (l = children, i = 0; l && i < priv->lines; i++)
+    {
+      segment_size = 0;
+
+      /* While we still have children to place and
+       * there is space left on this line */
+      while (l && segment_size < size)
+	{
+	  GtkWidget *child = l->data;
+	  gint       widget_size;
+
+	  get_widget_size (child, priv->orientation, line_thickness, NULL, &widget_size);
+
+	  if (segment_size != 0)
+	    segment_size += spacing;
+
+	  segment_size += widget_size;
+
+	  /* Consume this widget in this line segment if it fits the size
+	   * or it is alone taller than the whole tested size */
+	  if (segment_size <= size || segments[i] == 0)
+	    {
+	      *largest_segment_size = MAX (*largest_segment_size, segment_size);
+	      segments[i]++;
+
+	      l = l->next;
+	    }
+	}
+    }
+
+  /* If we placed all the widgets in the target size, the size fits. */
+  return (l == NULL);
+}
+
+/* All purpose algorithm entry point, this function takes an allocated size
+ * to fit the columns (or rows) and then splits up the child list into 
+ * 'n' children per 'segment' in a way that it takes the least space as possible.
+ *
+ * If 'segments' is specified, it will be allocated the array of integers representing
+ * how many children are to be fit per line segment (and must be freed afterwards with g_free()).
+ *
+ * The function returns the required space (the required height for all columns).
+ */
+static gint
+segment_lines_for_size (EggSpreadTable *table, 
+			gint            for_size,
+			gint          **segments)
+{
+  EggSpreadTablePrivate  *priv;
+  GList                  *children;
+  gint                    line_thickness;
+  gint                   *segment_counts = NULL, *test_counts;
+  gint                    upper, lower, segment_size, largest_size = 0;
+  gint                    i, j;
+
+  priv           = table->priv;
+  line_thickness = get_line_thickness (table, for_size);
+
+  segment_counts = g_new0 (gint, priv->lines);
+  test_counts    = g_new0 (gint, priv->lines);
+
+  /* Start by getting the child list/total size/average size */
+  children = get_visible_children (table);
+  upper    = get_segment_length (table, line_thickness, children);
+  lower    = upper / priv->lines;
+
+  /* Handle a single line spread table as a special case */
+  if (priv->lines == 1)
+    {
+      segment_counts[0] = g_list_length (children);
+      largest_size      = upper;
+    }
+  else 
+    {
+      /* Start with half way between the average and total height */
+      segment_size = lower + (upper - lower) / 2;
+      
+      while (segment_size > lower && segment_size < upper)
+	{
+	  gint test_largest = 0;
+	  
+	  if (children_fit_segment_size (table, children, line_thickness,
+					 segment_size, test_counts, &test_largest))
+	    {
+	      upper         = segment_size;
+	      segment_size -= (segment_size - lower) / 2;
+	      
+	      /* Save the last arrangement that 'fit' */
+	      largest_size  = test_largest;
+	      memcpy (segment_counts, test_counts, sizeof (gint) * priv->lines);
+	    }
+	  else
+	    {
+	      lower         = segment_size;
+	      segment_size += (upper - segment_size) / 2;
+	    }
+	}
+      
+      /* Perform some corrections: fill in any trailing columns that are missing widgets */
+      for (i = 0; i < priv->lines; i++)
+	{
+	  /* If this column has no widgets... */
+	  if (!segment_counts[i])
+	    {
+	      /* rewind to the last column that had more than 1 widget */
+	      for (j = i - 1; j >= 0; j--)
+		{
+		  if (segment_counts[j] > 1)
+		    {
+		      /* put an available widget in the empty column */
+		      segment_counts[j]--;
+		      segment_counts[i]++;
+		      break;
+		    }
+		}
+	    }
+	}
+    }
+
+  if (segments)
+    *segments = segment_counts;
+  else
+    g_free (segment_counts);
+
+  g_free (test_counts);
+
+  return largest_size;
+}
+
+
+/*****************************************************
+ *                 GtkWidgetClass                    * 
+ *****************************************************/
+static GtkSizeRequestMode 
+egg_spread_table_get_request_mode (GtkWidget *widget)
+{
+  EggSpreadTable         *table = EGG_SPREAD_TABLE (widget);
+  EggSpreadTablePrivate  *priv  = table->priv;
+  
+  if (priv->orientation == GTK_ORIENTATION_VERTICAL)
+    return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
+  else
+    return GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
+}
+
+static void 
+egg_spread_table_get_width (GtkWidget           *widget,
+			    gint                *minimum_size,
+			    gint                *natural_size)
+{
+  EggSpreadTable         *table = EGG_SPREAD_TABLE (widget);
+  EggSpreadTablePrivate  *priv  = table->priv;
+  gint                    min_width, nat_width;
+
+  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      /* Get the width for minimum height */      
+      gint min_height;
+
+      GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, &min_height, NULL);
+      GTK_WIDGET_GET_CLASS (widget)->get_preferred_width_for_height (widget, min_height, 
+								     &min_width, &nat_width);
+    }
+  else /* GTK_ORIENTATION_VERTICAL */
+    {
+      gint min_thickness, nat_thickness;
+
+      get_largest_line_thickness (table, &min_thickness, &nat_thickness);
+
+      min_width = min_thickness * priv->lines + LINE_SPACING (table) * (priv->lines - 1);
+      nat_width = nat_thickness * priv->lines + LINE_SPACING (table) * (priv->lines - 1);
+    }
+
+#if 0
+  g_print ("get_width() called; returning min %d and nat %d\n",
+	   min_width, nat_width);
+#endif
+
+  if (minimum_size)
+    *minimum_size = min_width;
+
+  if (natural_size)
+    *natural_size = nat_width;
+}
+
+static void 
+egg_spread_table_get_height (GtkWidget           *widget,
+			     gint                *minimum_size,
+			     gint                *natural_size)
+{
+  EggSpreadTable         *table = EGG_SPREAD_TABLE (widget);
+  EggSpreadTablePrivate  *priv  = table->priv;
+  gint                    min_height, nat_height;
+
+  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      gint min_thickness, nat_thickness;
+
+      get_largest_line_thickness (table, &min_thickness, &nat_thickness);
+
+      min_height = min_thickness * priv->lines + LINE_SPACING (table) * (priv->lines - 1);
+      nat_height = nat_thickness * priv->lines + LINE_SPACING (table) * (priv->lines - 1);
+    }
+  else /* GTK_ORIENTATION_VERTICAL */
+    {
+      /* Return the height for the minimum width */
+      gint min_width;
+
+      GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, NULL);
+      GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, min_width, 
+								     &min_height, &nat_height);
+    }
+
+#if 0
+  g_print ("get_height() called; returning min %d and nat %d\n",
+	   min_height, nat_height);
+#endif
+
+  if (minimum_size)
+    *minimum_size = min_height;
+
+  if (natural_size)
+    *natural_size = nat_height;
+}
+
+static void 
+egg_spread_table_get_height_for_width (GtkWidget           *widget,
+				       gint                 width,
+				       gint                *minimum_height,
+				       gint                *natural_height)
+{
+  EggSpreadTable         *table = EGG_SPREAD_TABLE (widget);
+  EggSpreadTablePrivate  *priv  = table->priv;
+  gint                    min_height = 0, nat_height = 0;
+
+  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      /* Just return the minimum/natural height */
+      GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, &min_height, &nat_height);
+    }
+  else /* GTK_ORIENTATION_VERTICAL */
+    {
+      gint min_width;
+
+      /* Make sure its no smaller than the minimum */
+      GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, NULL);
+
+      /* This will segment the lines evenly and return the overall 
+       * lengths of the split segments */
+      nat_height = min_height = segment_lines_for_size (table, MAX (width, min_width), NULL);
+    }
+
+#if 0
+  g_print ("get_height_for_width() called for width %d; returning min %d and nat %d\n",
+	   width, min_height, nat_height);
+#endif
+
+  if (minimum_height)
+    *minimum_height = min_height;
+
+  if (natural_height)
+    *natural_height = nat_height;
+}
+
+static void
+egg_spread_table_get_width_for_height (GtkWidget           *widget,
+				       gint                 height,
+				       gint                *minimum_width,
+				       gint                *natural_width)
+{
+  EggSpreadTable         *table = EGG_SPREAD_TABLE (widget);
+  EggSpreadTablePrivate  *priv      = table->priv;
+  gint                    min_width = 0, nat_width = 0;
+
+  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      gint min_height;
+
+      /* Make sure its no smaller than the minimum */
+      GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, &min_height, NULL);
+
+      /* This will segment the lines evenly and return the overall 
+       * lengths of the split segments */
+      nat_width = min_width = segment_lines_for_size (table, MAX (height, min_height), NULL);
+    }
+  else /* GTK_ORIENTATION_VERTICAL */
+    {
+      /* Just return the minimum/natural height */
+      GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, &nat_width);
+    }
+
+#if 0
+  g_print ("get_width_for_height() called for height %d; returning min %d and nat %d\n",
+	   height, min_width, nat_width);
+#endif
+
+  if (minimum_width)
+    *minimum_width = min_width;
+
+  if (natural_width)
+    *natural_width = nat_width;
+}
+
+static void
+allocate_child (EggSpreadTable *table,
+                GtkWidget      *child,
+                gint            item_offset,
+                gint            line_offset,
+                gint            item_size,
+                gint            line_size)
+{
+  EggSpreadTablePrivate  *priv = table->priv;
+  GtkAllocation           widget_allocation;
+  GtkAllocation           child_allocation;
+
+  gtk_widget_get_allocation (GTK_WIDGET (table), &widget_allocation);
+
+  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      child_allocation.x      = widget_allocation.x + item_offset;
+      child_allocation.y      = widget_allocation.y + line_offset;
+      child_allocation.width  = item_size;
+      child_allocation.height = line_size;
+    }
+  else /* GTK_ORIENTATION_VERTICAL */
+    {
+      child_allocation.x      = widget_allocation.x + line_offset;
+      child_allocation.y      = widget_allocation.y + item_offset;
+      child_allocation.width  = line_size;
+      child_allocation.height = item_size;
+    }
+
+  gtk_widget_size_allocate (child, &child_allocation);
+}
+
+static void
+egg_spread_table_size_allocate (GtkWidget     *widget,
+				GtkAllocation *allocation)
+{
+  EggSpreadTable        *table = EGG_SPREAD_TABLE (widget);
+  EggSpreadTablePrivate *priv = table->priv;
+  GList                 *list;
+  gint                  *segments = NULL;
+  gint                   full_thickness;
+  gint                   i, j;
+  gint                   line_offset, item_offset;
+  gint                   line_thickness;
+  gint                   line_spacing;
+  gint                   item_spacing;
+  GtkOrientation         opposite_orientation;
+
+  gtk_widget_set_allocation (widget, allocation);
+
+  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+    full_thickness = allocation->height;
+  else
+    full_thickness = allocation->width;
+
+  line_thickness       = get_line_thickness (table, full_thickness);
+  line_spacing         = LINE_SPACING (table);
+  item_spacing         = ITEM_SPACING (table);
+  opposite_orientation = OPPOSITE_ORIENTATION (table);
+
+  segment_lines_for_size (table, full_thickness, &segments);
+
+  for (list = priv->children, line_offset = 0, i = 0; 
+       i < priv->lines; 
+       line_offset += line_thickness + line_spacing, i++)
+    {
+      for (j = 0, item_offset = 0; list && j < segments[i]; list = list->next)
+	{
+	  GtkWidget *child = list->data;
+	  gint       child_size;
+
+	  if (!gtk_widget_get_visible (child))
+	    continue;
+
+	  get_widget_size (child, priv->orientation,
+			   line_thickness, NULL, &child_size);
+
+	  allocate_child (table, child, item_offset, line_offset, child_size, line_thickness);
+
+	  item_offset += child_size + item_spacing;
+
+	  j++;
+	}
+    }
+
+  g_free (segments);
+}
+
+/*****************************************************
+ *                GtkContainerClass                  * 
+ *****************************************************/
+static void
+egg_spread_table_add (GtkContainer *container,
+		      GtkWidget    *widget)
+{
+  egg_spread_table_insert_child (EGG_SPREAD_TABLE (container), widget, -1);
+}
+
+
+static void
+egg_spread_table_remove (GtkContainer *container,
+			 GtkWidget    *widget)
+{
+  EggSpreadTable        *table = EGG_SPREAD_TABLE (container);
+  EggSpreadTablePrivate *priv = table->priv;
+  GList                 *list;
+
+  list = g_list_find (priv->children, widget);
+
+  if (list)
+    {
+      gboolean was_visible = gtk_widget_get_visible (widget);
+
+      gtk_widget_unparent (widget);
+
+      priv->children = g_list_delete_link (priv->children, list);
+
+      if (was_visible && gtk_widget_get_visible (GTK_WIDGET (container)))
+        gtk_widget_queue_resize (GTK_WIDGET (container));
+    }
+}
+
+
+static void
+egg_spread_table_forall (GtkContainer *container,
+			 gboolean      include_internals,
+			 GtkCallback   callback,
+			 gpointer      callback_data)
+{
+  EggSpreadTable        *table = EGG_SPREAD_TABLE (container);
+  EggSpreadTablePrivate *priv = table->priv;
+  GList                 *list;
+
+  for (list = priv->children; list; list = list->next)
+    (* callback) ((GtkWidget *)list->data, callback_data);
+}
+
+static GType
+egg_spread_table_child_type (G_GNUC_UNUSED GtkContainer   *container)
+{
+  return GTK_TYPE_WIDGET;
+}
+
+/*****************************************************
+ *                       API                         * 
+ *****************************************************/
+
+/**
+ * egg_spread_table_new:
+ * @orientation: The #GtkOrientation for the #EggSpreadTable
+ * @lines: The fixed amount of lines to distribute children to.
+ *
+ * Creates a #EggSpreadTable.
+ *
+ * Returns: A new #EggSpreadTable container
+ */
+GtkWidget *
+egg_spread_table_new (GtkOrientation orientation,
+		      guint          lines)
+{
+  return (GtkWidget *)g_object_new (EGG_TYPE_SPREAD_TABLE,
+				    "orientation", orientation,
+				    "lines", lines,
+				    NULL);
+}
+
+/**
+ * egg_spread_table_insert_child:
+ * @spread_table: An #EggSpreadTable
+ * @widget: the child #GtkWidget to add
+ * @index: the position in the child list to insert, specify -1 to append to the list.
+ *
+ * Adds a child to an #EggSpreadTable with its packing options set
+ */
+void
+egg_spread_table_insert_child (EggSpreadTable *table,
+			       GtkWidget      *child,
+			       gint            index)
+{
+  EggSpreadTablePrivate *priv;
+  GList                 *list;
+
+  g_return_if_fail (EGG_IS_SPREAD_TABLE (table));
+  g_return_if_fail (GTK_IS_WIDGET (child));
+
+  priv = table->priv;
+
+  list = g_list_find (priv->children, child);
+  g_return_if_fail (list == NULL);
+
+  priv->children = g_list_insert (priv->children, child, index);
+
+  gtk_widget_set_parent (child, GTK_WIDGET (table));
+}
+
+
+
+/**
+ * egg_spread_table_get_child_line:
+ * @table: A #EggSpreadTable
+ * @child: A Child of the @table.
+ * @size: A size in the opposing orientation of @table
+ *
+ * Gets the line index in which @child would be positioned 
+ * if @table were to be allocated @size in the opposing
+ * orientation of @table.
+ *
+ * For instance, if the @table is oriented vertically,
+ * this function will return @child's column if @table
+ * were to be allocated @size width.
+ *
+ * Returns: the line index @child would be positioned in
+ * for @size (starting from 0).
+ */
+guint
+egg_spread_table_get_child_line (EggSpreadTable *table,
+				 GtkWidget      *child,
+				 gint            size)
+
+{
+  EggSpreadTablePrivate *priv;
+  gint                  *segments = NULL;
+  gint                   i, child_count, child_idx = 0;
+  GList                 *l;
+
+  g_return_val_if_fail (EGG_IS_SPREAD_TABLE (table), 0);
+  g_return_val_if_fail (GTK_IS_WIDGET (child), 0);
+
+  priv = table->priv;
+
+  segment_lines_for_size (table, size, &segments);
+
+  /* Get child index in list */
+  l = g_list_find (priv->children, child);
+  g_return_val_if_fail (l != NULL, 0);
+
+  child_idx = g_list_position (priv->children, l);
+
+  for (i = 0, child_count = 0; i < priv->lines; i++)
+    {
+      child_count += segments[i];
+
+      if (child_idx < child_count)
+	break;
+    }
+
+  g_assert (i < priv->lines);
+
+  g_free (segments);
+
+  return i;
+}
+
+
+/**
+ * egg_spread_table_set_lines:
+ * @table: A #EggSpreadTable
+ * @lines: The amount of evenly allocated child segments.
+ *
+ * Sets the fixed amount of lines (rows or columns) to
+ * distribute children to.
+ *
+ * <note><para>Space will be allocated for all lines even
+ * if there are not enough children to be placed on every
+ * line, for instance if @lines is set to 4 and the table
+ * has only 3 children; then the last line will appear empty.</para></note>
+ */
+void
+egg_spread_table_set_lines (EggSpreadTable *table, 
+			    guint           lines)
+{
+  EggSpreadTablePrivate *priv;
+
+  g_return_if_fail (EGG_IS_SPREAD_TABLE (table));
+  g_return_if_fail (lines > 0);
+
+  priv = table->priv;
+
+  if (priv->lines != lines)
+    {
+      priv->lines = lines;
+
+      gtk_widget_queue_resize (GTK_WIDGET (table));
+
+      g_object_notify (G_OBJECT (table), "lines");
+    }
+}
+
+/**
+ * egg_spread_table_get_lines:
+ * @table: An #EggSpreadTable
+ *
+ * Gets the fixed amount of lines (rows or columns) to
+ * distribute children to.
+ *
+ * Returns: The amount of lines.
+ */
+guint
+egg_spread_table_get_lines (EggSpreadTable *table)
+{
+  g_return_val_if_fail (EGG_IS_SPREAD_TABLE (table), 0);
+
+  return table->priv->lines;
+}
+
+
+/**
+ * egg_spread_table_set_vertical_spacing:
+ * @table: An #EggSpreadTable
+ * @spacing: The spacing to use.
+ *
+ * Sets the vertical space to add between children.
+ */
+void
+egg_spread_table_set_vertical_spacing  (EggSpreadTable  *table,
+					guint            spacing)
+{
+  EggSpreadTablePrivate *priv;
+
+  g_return_if_fail (EGG_IS_SPREAD_TABLE (table));
+
+  priv = table->priv;
+
+  if (priv->vertical_spacing != spacing)
+    {
+      priv->vertical_spacing = spacing;
+
+      gtk_widget_queue_resize (GTK_WIDGET (table));
+
+      g_object_notify (G_OBJECT (table), "vertical-spacing");
+    }
+}
+
+/**
+ * egg_spread_table_get_vertical_spacing:
+ * @table: An #EggSpreadTable
+ *
+ * Gets the vertical spacing.
+ *
+ * Returns: The vertical spacing.
+ */
+guint
+egg_spread_table_get_vertical_spacing  (EggSpreadTable *table)
+{
+  g_return_val_if_fail (EGG_IS_SPREAD_TABLE (table), 0);
+
+  return table->priv->vertical_spacing;
+}
+
+/**
+ * egg_spread_table_set_horizontal_spacing:
+ * @table: A #EggSpreadTable
+ * @spacing: The spacing to use.
+ *
+ * Sets the horizontal space to add between children.
+ */
+void
+egg_spread_table_set_horizontal_spacing (EggSpreadTable  *table,
+					 guint            spacing)
+{
+  EggSpreadTablePrivate *priv;
+
+  g_return_if_fail (EGG_IS_SPREAD_TABLE (table));
+
+  priv = table->priv;
+
+  if (priv->horizontal_spacing != spacing)
+    {
+      priv->horizontal_spacing = spacing;
+
+      gtk_widget_queue_resize (GTK_WIDGET (table));
+
+      g_object_notify (G_OBJECT (table), "horizontal-spacing");
+    }
+}
+
+/**
+ * egg_spread_table_get_horizontal_spacing:
+ * @table: A #EggSpreadTable
+ *
+ * Gets the horizontal spacing.
+ *
+ * Returns: The horizontal spacing.
+ */
+guint
+egg_spread_table_get_horizontal_spacing (EggSpreadTable *table)
+{
+  g_return_val_if_fail (EGG_IS_SPREAD_TABLE (table), 0);
+
+  return table->priv->horizontal_spacing;
+}
diff --git a/libegg/spreadtable/eggspreadtable.h b/libegg/spreadtable/eggspreadtable.h
new file mode 100644
index 0000000..7531a80
--- /dev/null
+++ b/libegg/spreadtable/eggspreadtable.h
@@ -0,0 +1,89 @@
+/* 
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * Authors:
+ *      Tristan Van Berkom <tristanvb openismus com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EGG_SPREAD_TABLE_H__
+#define __EGG_SPREAD_TABLE_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+
+#define EGG_TYPE_SPREAD_TABLE                  (egg_spread_table_get_type ())
+#define EGG_SPREAD_TABLE(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SPREAD_TABLE, EggSpreadTable))
+#define EGG_SPREAD_TABLE_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SPREAD_TABLE, EggSpreadTableClass))
+#define EGG_IS_SPREAD_TABLE(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SPREAD_TABLE))
+#define EGG_IS_SPREAD_TABLE_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SPREAD_TABLE))
+#define EGG_SPREAD_TABLE_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SPREAD_TABLE, EggSpreadTableClass))
+
+typedef struct _EggSpreadTable            EggSpreadTable;
+typedef struct _EggSpreadTablePrivate     EggSpreadTablePrivate;
+typedef struct _EggSpreadTableClass       EggSpreadTableClass;
+
+
+struct _EggSpreadTable
+{
+  GtkContainer parent_instance;
+
+  /*< private >*/
+  EggSpreadTablePrivate *priv;
+};
+
+struct _EggSpreadTableClass
+{
+  GtkContainerClass parent_class;
+};
+
+
+GType                 egg_spread_table_get_type                  (void) G_GNUC_CONST;
+
+
+GtkWidget            *egg_spread_table_new                       (GtkOrientation  orientation,
+								  guint           lines);
+
+void                  egg_spread_table_insert_child              (EggSpreadTable *table,
+								  GtkWidget      *child,
+								  gint            index);
+
+guint                 egg_spread_table_get_child_line            (EggSpreadTable *table,
+								  GtkWidget      *child,
+								  gint            size);
+
+void                  egg_spread_table_set_lines                 (EggSpreadTable *table,
+								  guint           lines);
+guint                 egg_spread_table_get_lines                 (EggSpreadTable *table);
+
+void                  egg_spread_table_set_horizontal_spacing    (EggSpreadTable *table,
+								  guint           spacing);
+guint                 egg_spread_table_get_horizontal_spacing    (EggSpreadTable *table);
+
+void                  egg_spread_table_set_vertical_spacing      (EggSpreadTable *table,
+								  guint           spacing);
+guint                 egg_spread_table_get_vertical_spacing      (EggSpreadTable *table);
+
+
+
+
+G_END_DECLS
+
+
+#endif /* __EGG_SPREAD_TABLE_H__ */
diff --git a/libegg/spreadtable/testspreadtable.c b/libegg/spreadtable/testspreadtable.c
new file mode 100644
index 0000000..2da5073
--- /dev/null
+++ b/libegg/spreadtable/testspreadtable.c
@@ -0,0 +1,393 @@
+/* testspreadtable.c
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * Author:
+ *      Tristan Van Berkom <tristanvb openismus com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <eggspreadtable.h>
+
+enum {
+  IMAGE_NONE,
+  IMAGE_SMALL,
+  IMAGE_LARGE,
+  IMAGE_HUGE
+};
+
+#define INITIAL_HSPACING        2
+#define INITIAL_VSPACING        2
+#define INITIAL_LINES           3
+#define INITIAL_HALIGN          GTK_ALIGN_FILL
+#define INITIAL_IMAGE           IMAGE_NONE
+#define INITIAL_IMAGE_INDEX     10
+
+static GtkWidget *paper = NULL;
+static GtkAlign   child_halign     = INITIAL_HALIGN;
+static int        test_image       = INITIAL_IMAGE;
+static int        test_image_index = INITIAL_IMAGE_INDEX;
+
+static void
+populate_spread_table_wrappy (EggSpreadTable *spread_table)
+{
+  GList *children, *l;
+  GtkWidget *widget, *frame;
+  gsize i;
+
+  const gchar *strings[] = {
+    "These are", "some wrappy label", "texts", "of various", "lengths.",
+    "They should always be", "shown", "consecutively. Except it's",
+    "hard to say", "where exactly the", "label", "will wrap", "and where exactly",
+    "the actual", "container", "will wrap.", "This label is really really really long !", 
+    "Let's add some more", "labels to the",
+    "mix. Just to", "make sure we", "got something to work", "with here."
+  };
+
+  /* Remove all children first */
+  children = gtk_container_get_children (GTK_CONTAINER (paper));
+  for (l = children; l; l = l->next)
+    {
+      GtkWidget *child = l->data;
+
+      gtk_container_remove (GTK_CONTAINER (paper), child);
+    }
+  g_list_free (children);
+
+  for (i = 0; i < G_N_ELEMENTS (strings); i++)
+    {
+      widget = gtk_label_new (strings[i]);
+      frame  = gtk_frame_new (NULL);
+      gtk_widget_show (widget);
+      gtk_widget_show (frame);
+
+      gtk_container_add (GTK_CONTAINER (frame), widget);
+
+      gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+      gtk_label_set_line_wrap_mode (GTK_LABEL (widget), PANGO_WRAP_WORD);
+      gtk_label_set_width_chars (GTK_LABEL (widget), 10);
+
+      gtk_widget_set_halign (frame, child_halign);
+
+      egg_spread_table_insert_child (EGG_SPREAD_TABLE (spread_table), frame, -1);
+    }
+
+  /* Insert an image into the mix */
+  if (test_image)
+    {
+      widget = gtk_image_new_from_file ("apple-red.png");
+
+      switch (test_image)
+	{
+	case IMAGE_SMALL:
+	  gtk_widget_set_size_request (widget, 100, 100);
+	  break;
+	case IMAGE_LARGE:
+	  gtk_widget_set_size_request (widget, 150, 200);
+	  break;
+	case IMAGE_HUGE:
+	  gtk_widget_set_size_request (widget, 200, 300);
+	  break;
+	default:
+	  break;
+	}
+
+      frame  = gtk_frame_new (NULL);
+      gtk_widget_show (widget);
+      gtk_widget_show (frame);
+      
+      gtk_container_add (GTK_CONTAINER (frame), widget);
+      egg_spread_table_insert_child (EGG_SPREAD_TABLE (spread_table), frame, test_image_index);
+    }
+}
+
+static void
+orientation_changed (GtkComboBox   *box,
+                     EggSpreadTable  *paper)
+{
+  GtkOrientation orientation = gtk_combo_box_get_active (box);
+
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (paper), orientation);
+}
+
+static void
+lines_changed (GtkSpinButton *button,
+	       G_GNUC_UNUSED gpointer       data)
+{
+  gint lines = gtk_spin_button_get_value_as_int (button);
+
+  egg_spread_table_set_lines (EGG_SPREAD_TABLE (paper), lines);
+}
+
+static void
+spacing_changed (GtkSpinButton *button,
+                 gpointer       data)
+{
+  GtkOrientation orientation = GPOINTER_TO_INT (data);
+  gint           state = gtk_spin_button_get_value_as_int (button);
+
+  if (orientation == GTK_ORIENTATION_HORIZONTAL)
+    egg_spread_table_set_horizontal_spacing (EGG_SPREAD_TABLE (paper), state);
+  else
+    egg_spread_table_set_vertical_spacing (EGG_SPREAD_TABLE (paper), state);
+}
+
+
+
+static void
+halign_changed (GtkComboBox   *box,
+                     EggSpreadTable  *paper)
+{
+  child_halign = gtk_combo_box_get_active (box);
+
+  populate_spread_table_wrappy (EGG_SPREAD_TABLE (paper));
+}
+
+static void
+test_image_changed (GtkComboBox   *box,
+		    EggSpreadTable  *paper)
+{
+  test_image = gtk_combo_box_get_active (box);
+
+  populate_spread_table_wrappy (EGG_SPREAD_TABLE (paper));
+}
+
+
+
+static void
+test_image_index_changed (GtkSpinButton *button,
+			  G_GNUC_UNUSED gpointer       data)
+{
+  test_image_index = gtk_spin_button_get_value_as_int (button);
+
+  populate_spread_table_wrappy (EGG_SPREAD_TABLE (paper));
+}
+
+
+static GtkWidget *
+create_window (void)
+{
+  GtkWidget *window;
+  GtkWidget *hbox, *vbox, *widget;
+  GtkWidget *swindow, *frame, *expander;
+  GtkWidget *paper_cntl, *items_cntl;
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  hbox   = gtk_hbox_new (FALSE, 2);
+  vbox   = gtk_vbox_new (FALSE, 6);
+
+  gtk_container_set_border_width (GTK_CONTAINER (window), 8);
+
+  gtk_widget_show (vbox);
+  gtk_widget_show (hbox);
+  gtk_container_add (GTK_CONTAINER (window), hbox);
+  gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+
+  frame = gtk_frame_new ("SpreadTable");
+  gtk_widget_show (frame);
+  gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+
+  swindow = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
+                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+  
+  gtk_widget_show (swindow);
+  gtk_container_add (GTK_CONTAINER (frame), swindow);
+
+  paper = egg_spread_table_new (GTK_ORIENTATION_VERTICAL, INITIAL_LINES);
+  egg_spread_table_set_vertical_spacing (EGG_SPREAD_TABLE (paper), INITIAL_VSPACING);
+  egg_spread_table_set_horizontal_spacing (EGG_SPREAD_TABLE (paper), INITIAL_HSPACING);
+  gtk_widget_show (paper);
+
+  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (swindow), paper);
+
+  /* Add SpreadTable test control frame */
+  expander = gtk_expander_new ("SpreadTable controls");
+  gtk_expander_set_expanded (GTK_EXPANDER (expander), TRUE);
+  paper_cntl = gtk_vbox_new (FALSE, 2);
+  gtk_widget_show (paper_cntl);
+  gtk_widget_show (expander);
+  gtk_container_add (GTK_CONTAINER (expander), paper_cntl);
+  gtk_box_pack_start (GTK_BOX (vbox), expander, FALSE, FALSE, 0);
+
+  /* Add Orientation control */
+  widget = gtk_combo_box_text_new ();
+  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Horizontal");
+  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Vertical");
+  gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 1);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set the spread_table orientation");
+  gtk_box_pack_start (GTK_BOX (paper_cntl), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+                    G_CALLBACK (orientation_changed), paper);
+
+
+  /* Add horizontal/vertical spacing controls */
+  hbox = gtk_hbox_new (FALSE, 2);
+  gtk_widget_show (hbox);
+
+  widget = gtk_label_new ("H Spacing");
+  gtk_widget_show (widget);
+  gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
+
+  widget = gtk_spin_button_new_with_range (0, 30, 1);
+  gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), INITIAL_HSPACING);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set the horizontal spacing between children");
+  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+                    G_CALLBACK (spacing_changed), GINT_TO_POINTER (GTK_ORIENTATION_HORIZONTAL));
+  g_signal_connect (G_OBJECT (widget), "value-changed",
+                    G_CALLBACK (spacing_changed), GINT_TO_POINTER (GTK_ORIENTATION_HORIZONTAL));
+
+  gtk_box_pack_start (GTK_BOX (paper_cntl), hbox, FALSE, FALSE, 0);
+
+  hbox = gtk_hbox_new (FALSE, 2);
+  gtk_widget_show (hbox);
+
+  widget = gtk_label_new ("V Spacing");
+  gtk_widget_show (widget);
+  gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
+
+  widget = gtk_spin_button_new_with_range (0, 30, 1);
+  gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), INITIAL_VSPACING);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set the vertical spacing between children");
+  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+                    G_CALLBACK (spacing_changed), GINT_TO_POINTER (GTK_ORIENTATION_VERTICAL));
+  g_signal_connect (G_OBJECT (widget), "value-changed",
+                    G_CALLBACK (spacing_changed), GINT_TO_POINTER (GTK_ORIENTATION_VERTICAL));
+
+  gtk_box_pack_start (GTK_BOX (paper_cntl), hbox, FALSE, FALSE, 0);
+
+
+  /* Add lines controls */
+  hbox = gtk_hbox_new (FALSE, 2);
+  gtk_widget_show (hbox);
+
+  widget = gtk_label_new ("Lines");
+  gtk_widget_show (widget);
+  gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
+
+  widget = gtk_spin_button_new_with_range (1, 30, 1);
+  gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), INITIAL_LINES);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set the horizontal spacing between children");
+  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+                    G_CALLBACK (lines_changed), NULL);
+  g_signal_connect (G_OBJECT (widget), "value-changed",
+                    G_CALLBACK (lines_changed), NULL);
+
+  gtk_box_pack_start (GTK_BOX (paper_cntl), hbox, FALSE, FALSE, 0);
+
+
+  /* Add test items control frame */
+  expander = gtk_expander_new ("Test item controls");
+  gtk_expander_set_expanded (GTK_EXPANDER (expander), TRUE);
+  items_cntl = gtk_vbox_new (FALSE, 2);
+  gtk_widget_show (items_cntl);
+  gtk_widget_show (expander);
+  gtk_container_add (GTK_CONTAINER (expander), items_cntl);
+  gtk_box_pack_start (GTK_BOX (vbox), expander, FALSE, FALSE, 0);
+
+  /* Add child halign control */
+  widget = gtk_combo_box_text_new ();
+  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Fill");
+  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Start");
+  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "End");
+  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Center");
+  gtk_combo_box_set_active (GTK_COMBO_BOX (widget), INITIAL_HALIGN);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set the children's halign property");
+  gtk_box_pack_start (GTK_BOX (items_cntl), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+                    G_CALLBACK (halign_changed), paper);
+
+
+  /* Add image control */
+  widget = gtk_combo_box_text_new ();
+  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "None");
+  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Small");
+  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Large");
+  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Huge");
+  gtk_combo_box_set_active (GTK_COMBO_BOX (widget), INITIAL_IMAGE);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Use an image to test the container");
+  gtk_box_pack_start (GTK_BOX (items_cntl), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+                    G_CALLBACK (test_image_changed), paper);
+
+
+  /* Add horizontal/vertical spacing controls */
+  hbox = gtk_hbox_new (FALSE, 2);
+  gtk_widget_show (hbox);
+
+  widget = gtk_label_new ("Image index");
+  gtk_widget_show (widget);
+  gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
+
+  widget = gtk_spin_button_new_with_range (0, 25, 1);
+  gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), INITIAL_IMAGE_INDEX);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set the child list index for the optional test image");
+  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+  gtk_box_pack_start (GTK_BOX (items_cntl), hbox, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+                    G_CALLBACK (test_image_index_changed), GINT_TO_POINTER (GTK_ORIENTATION_HORIZONTAL));
+  g_signal_connect (G_OBJECT (widget), "value-changed",
+                    G_CALLBACK (test_image_index_changed), GINT_TO_POINTER (GTK_ORIENTATION_HORIZONTAL));
+
+  populate_spread_table_wrappy (EGG_SPREAD_TABLE (paper));
+
+  gtk_window_set_default_size (GTK_WINDOW (window), 500, 400);
+
+  return window;
+}
+
+int
+main (int argc, char *argv[])
+{
+  GtkWidget *window;
+
+  gtk_init (&argc, &argv);
+
+  window = create_window ();
+
+  g_signal_connect (window, "delete-event",
+                    G_CALLBACK (gtk_main_quit), window);
+
+  gtk_widget_show (window);
+
+  gtk_main ();
+
+  return 0;
+}



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