[egg-list-box] Add EggFlowBox



commit ee1e2fdbeee18d6d2115cf724fb44ff7a0a074fc
Author: William Jon McCann <jmccann redhat com>
Date:   Fri Feb 8 17:07:15 2013 -0500

    Add EggFlowBox
    
    Derived from EggWrapBox originally written by
    Tristan Van Berkom <tristanvb openismus com>

 Makefile.am     |   21 +-
 egg-flow-box.c  | 2248 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 egg-flow-box.h  |   90 +++
 test-flow-box.c |  514 +++++++++++++
 4 files changed, 2871 insertions(+), 2 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 2efaf09..21f7960 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -10,7 +10,10 @@ AM_VALAFLAGS = \
 	--vapidir=. \
 	$(NULL)
 
-noinst_LTLIBRARIES = libegglistbox.la
+noinst_LTLIBRARIES = \
+	libegglistbox.la \
+	libeggflowbox.la \
+	$(NULL)
 
 libegglistbox_la_SOURCES = \
 	egg-list-box.c egg-list-box.h \
@@ -18,7 +21,12 @@ libegglistbox_la_SOURCES = \
 
 libegglistbox_la_LIBADD = $(LISTBOX_LIBS)
 
-noinst_PROGRAMS = test-list test-scrolled test-focus test-sel
+libeggflowbox_la_SOURCES = \
+	egg-flow-box.c egg-flow-box.h
+
+libeggflowbox_la_LIBADD = $(LISTBOX_LIBS)
+
+noinst_PROGRAMS = test-list test-scrolled test-focus test-sel test-flow-box
 
 test_sel_SOURCES = \
 	test-sel.c \
@@ -66,6 +74,15 @@ test_focus_LDADD = \
 	libegglistbox.la \
 	$(NULL)
 
+test_flow_box_SOURCES = \
+	test-flow-box.c \
+	$(NULL)
+
+test_flow_box_LDADD = \
+	$(LISTBOX_LIBS) \
+	libeggflowbox.la \
+	$(NULL)
+
 CLEANFILES = \
 	$(test_list_SOURCES:.vala=.c) \
 	$(test_scrolled_SOURCES:.vala=.c) \
diff --git a/egg-flow-box.c b/egg-flow-box.c
new file mode 100644
index 0000000..4832a78
--- /dev/null
+++ b/egg-flow-box.c
@@ -0,0 +1,2248 @@
+/*
+ * Copyright (C) 2007-2010 Openismus GmbH
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * 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:eggflowbox
+ * @Short_Description: A container that allows reflowing its children
+ * @Title: EggFlowBox
+ *
+ * #EggFlowBox positions child widgets in sequence according to its
+ * orientation. For instance, with the horizontal orientation, the widgets
+ * will be arranged from left to right, starting a new row under the
+ * previous row when necessary. Reducing the width in this case will
+ * require more rows, so a larger height will be requested.
+ *
+ * Likewise, with the vertical orientation, the widgets will be arranged
+ * from top to bottom, starting a new column to the right when necessary.
+ * Reducing the height will require more columns, so a larger width will be
+ * requested.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include "egg-flow-box.h"
+
+#define P_(msgid) (msgid)
+
+enum {
+  CHILD_ACTIVATED,
+  LAST_SIGNAL
+};
+
+enum {
+  PROP_0,
+  PROP_ORIENTATION,
+  PROP_HOMOGENEOUS,
+  PROP_HALIGN_POLICY,
+  PROP_VALIGN_POLICY,
+  PROP_COLUMN_SPACING,
+  PROP_ROW_SPACING,
+  PROP_MIN_CHILDREN_PER_LINE,
+  PROP_MAX_CHILDREN_PER_LINE,
+  PROP_ACTIVATE_ON_SINGLE_CLICK
+};
+
+typedef struct _EggFlowBoxChildInfo EggFlowBoxChildInfo;
+
+struct _EggFlowBoxPrivate {
+  GtkOrientation orientation;
+  GtkAlign       halign_policy;
+  GtkAlign       valign_policy;
+  guint          homogeneous : 1;
+  guint          activate_on_single_click : 1;
+
+  guint          row_spacing;
+  guint          column_spacing;
+
+  gboolean       active_child_active;
+  EggFlowBoxChildInfo *active_child;
+
+  guint16        min_children_per_line;
+  guint16        max_children_per_line;
+
+  GSequence     *children;
+  GHashTable    *child_hash;
+};
+
+struct _EggFlowBoxChildInfo
+{
+  GSequenceIter *iter;
+  GtkWidget *widget;
+  gint x;
+  gint y;
+  gint width;
+  gint height;
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE_WITH_CODE (EggFlowBox, egg_flow_box, GTK_TYPE_CONTAINER,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
+
+
+#define ORIENTATION_ALIGN_POLICY(box)                                   \
+  (((EggFlowBox *)(box))->priv->orientation == GTK_ORIENTATION_HORIZONTAL ? \
+   ((EggFlowBox *)(box))->priv->halign_policy :                         \
+   ((EggFlowBox *)(box))->priv->valign_policy)
+
+#define OPPOSING_ORIENTATION_ALIGN_POLICY(box)                          \
+  (((EggFlowBox *)(box))->priv->orientation == GTK_ORIENTATION_HORIZONTAL ? \
+   ((EggFlowBox *)(box))->priv->valign_policy :                         \
+   ((EggFlowBox *)(box))->priv->halign_policy)
+
+static EggFlowBoxChildInfo*
+egg_flow_box_child_info_new (GtkWidget *widget)
+{
+  EggFlowBoxChildInfo *info;
+
+  info = g_new0 (EggFlowBoxChildInfo, 1);
+  info->widget = g_object_ref (widget);
+  return info;
+}
+
+static void
+egg_flow_box_child_info_free (EggFlowBoxChildInfo *info)
+{
+  g_clear_object (&info->widget);
+  g_free (info);
+}
+
+static EggFlowBoxChildInfo*
+egg_flow_box_lookup_info (EggFlowBox *flow_box, GtkWidget* child)
+{
+  EggFlowBoxPrivate *priv = flow_box->priv;
+
+  return g_hash_table_lookup (priv->child_hash, child);
+}
+
+/**
+ * egg_flow_box_get_homogeneous:
+ * @box: a #EggFlowBox
+ *
+ * Returns whether the box is homogeneous (all children are the
+ * same size). See gtk_box_set_homogeneous().
+ *
+ * Return value: %TRUE if the box is homogeneous.
+ **/
+gboolean
+egg_flow_box_get_homogeneous (EggFlowBox *box)
+{
+  g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
+
+  return box->priv->homogeneous;
+}
+
+/**
+ * egg_flow_box_set_homogeneous:
+ * @box: a #EggFlowBox
+ * @homogeneous: a boolean value, %TRUE to create equal allotments,
+ *   %FALSE for variable allotments
+ *
+ * Sets the #EggFlowBox:homogeneous property of @box, controlling
+ * whether or not all children of @box are given equal space
+ * in the box.
+ */
+void
+egg_flow_box_set_homogeneous (EggFlowBox *box,
+                              gboolean    homogeneous)
+{
+  EggFlowBoxPrivate *priv;
+
+  g_return_if_fail (EGG_IS_FLOW_BOX (box));
+
+  priv = box->priv;
+
+  if ((homogeneous ? TRUE : FALSE) != priv->homogeneous)
+    {
+      priv->homogeneous = homogeneous ? TRUE : FALSE;
+      g_object_notify (G_OBJECT (box), "homogeneous");
+      gtk_widget_queue_resize (GTK_WIDGET (box));
+    }
+}
+
+static gint
+get_visible_children (EggFlowBox *box)
+{
+  EggFlowBoxPrivate *priv = box->priv;
+  GSequenceIter *iter;
+  gint i = 0;
+
+  for (iter = g_sequence_get_begin_iter (priv->children);
+       !g_sequence_iter_is_end (iter);
+       iter = g_sequence_iter_next (iter))
+    {
+      EggFlowBoxChildInfo *child_info;
+      GtkWidget *child;
+
+      child_info = g_sequence_get (iter);
+      child = child_info->widget;
+
+      if (!gtk_widget_get_visible (child))
+        continue;
+
+      i++;
+    }
+
+  return i;
+}
+
+/* Used in columned modes where all items share at least their
+ * equal widths or heights
+ */
+static void
+get_average_item_size (EggFlowBox    *box,
+                       GtkOrientation orientation,
+                       gint          *min_size,
+                       gint          *nat_size)
+{
+  EggFlowBoxPrivate *priv = box->priv;
+  GSequenceIter *iter;
+  gint max_min_size = 0;
+  gint max_nat_size = 0;
+
+  for (iter = g_sequence_get_begin_iter (priv->children);
+       !g_sequence_iter_is_end (iter);
+       iter = g_sequence_iter_next (iter))
+    {
+      EggFlowBoxChildInfo *child_info;
+      GtkWidget *child;
+      gint child_min, child_nat;
+
+      child_info = g_sequence_get (iter);
+      child = child_info->widget;
+
+      if (!gtk_widget_get_visible (child))
+        continue;
+
+      if (orientation == GTK_ORIENTATION_HORIZONTAL)
+        gtk_widget_get_preferred_width (child, &child_min, &child_nat);
+      else
+        gtk_widget_get_preferred_height (child, &child_min, &child_nat);
+
+      max_min_size = MAX (max_min_size, child_min);
+      max_nat_size = MAX (max_nat_size, child_nat);
+    }
+
+  if (min_size)
+    *min_size = max_min_size;
+
+  if (nat_size)
+    *nat_size = max_nat_size;
+}
+
+
+/* Gets the largest minimum/natural size for a given size
+ * (used to get the largest item heights for a fixed item width and the opposite) */
+static void
+get_largest_size_for_opposing_orientation (EggFlowBox    *box,
+                                           GtkOrientation orientation,
+                                           gint           item_size,
+                                           gint          *min_item_size,
+                                           gint          *nat_item_size)
+{
+  EggFlowBoxPrivate *priv = box->priv;
+  GSequenceIter *iter;
+  gint max_min_size = 0;
+  gint max_nat_size = 0;
+
+  for (iter = g_sequence_get_begin_iter (priv->children);
+       !g_sequence_iter_is_end (iter);
+       iter = g_sequence_iter_next (iter))
+    {
+      EggFlowBoxChildInfo *child_info;
+      GtkWidget *child;
+      gint       child_min, child_nat;
+
+      child_info = g_sequence_get (iter);
+      child = child_info->widget;
+
+      if (!gtk_widget_get_visible (child))
+        continue;
+
+      if (orientation == GTK_ORIENTATION_HORIZONTAL)
+        gtk_widget_get_preferred_height_for_width (child,
+                                                   item_size,
+                                                   &child_min, &child_nat);
+      else
+        gtk_widget_get_preferred_width_for_height (child,
+                                                   item_size,
+                                                   &child_min, &child_nat);
+
+      max_min_size = MAX (max_min_size, child_min);
+      max_nat_size = MAX (max_nat_size, child_nat);
+    }
+
+  if (min_item_size)
+    *min_item_size = max_min_size;
+
+  if (nat_item_size)
+    *nat_item_size = max_nat_size;
+}
+
+
+/* Gets the largest minimum/natural size on a single line for a given size
+ * (used to get the largest line heights for a fixed item width and the opposite
+ * while itterating over a list of children, note the new index is returned) */
+static GSequenceIter *
+get_largest_size_for_line_in_opposing_orientation (EggFlowBox       *box,
+                                                   GtkOrientation    orientation,
+                                                   GSequenceIter    *cursor,
+                                                   gint              line_length,
+                                                   GtkRequestedSize *item_sizes,
+                                                   gint              extra_pixels,
+                                                   gint             *min_item_size,
+                                                   gint             *nat_item_size)
+{
+  GSequenceIter *iter;
+  gint max_min_size = 0;
+  gint max_nat_size = 0;
+  gint i;
+
+  i = 0;
+  for (iter = cursor;
+       !g_sequence_iter_is_end (iter) && i < line_length;
+       iter = g_sequence_iter_next (iter))
+    {
+      GtkWidget *child;
+      EggFlowBoxChildInfo *child_info;
+      gint child_min, child_nat, this_item_size;
+
+      child_info = g_sequence_get (iter);
+      child = child_info->widget;
+
+      if (!gtk_widget_get_visible (child))
+        continue;
+
+      /* Distribute the extra pixels to the first children in the line
+       * (could be fancier and spread them out more evenly) */
+      this_item_size = item_sizes[i].minimum_size;
+      if (extra_pixels > 0 && ORIENTATION_ALIGN_POLICY (box) == GTK_ALIGN_FILL)
+        {
+          this_item_size++;
+          extra_pixels--;
+        }
+
+      if (orientation == GTK_ORIENTATION_HORIZONTAL)
+        gtk_widget_get_preferred_height_for_width (child,
+                                                   this_item_size,
+                                                   &child_min, &child_nat);
+      else
+        gtk_widget_get_preferred_width_for_height (child,
+                                                   this_item_size,
+                                                   &child_min, &child_nat);
+
+      max_min_size = MAX (max_min_size, child_min);
+      max_nat_size = MAX (max_nat_size, child_nat);
+
+      i++;
+    }
+
+  if (min_item_size)
+    *min_item_size = max_min_size;
+
+  if (nat_item_size)
+    *nat_item_size = max_nat_size;
+
+  /* Return next item in the list */
+  return iter;
+}
+
+/* fit_aligned_item_requests() helper */
+static gint
+gather_aligned_item_requests (EggFlowBox       *box,
+                              GtkOrientation    orientation,
+                              gint              line_length,
+                              gint              item_spacing,
+                              gint              n_children,
+                              GtkRequestedSize *item_sizes)
+{
+  EggFlowBoxPrivate *priv   = box->priv;
+  GSequenceIter *iter;
+  gint i;
+  gint extra_items, natural_line_size = 0;
+
+  extra_items = n_children % line_length;
+
+  i = 0;
+  for (iter = g_sequence_get_begin_iter (priv->children);
+       !g_sequence_iter_is_end (iter);
+       iter = g_sequence_iter_next (iter), i++)
+    {
+      EggFlowBoxChildInfo *child_info;
+      GtkWidget *child;
+      GtkAlign item_align;
+      gint child_min, child_nat;
+      gint position;
+
+      child_info = g_sequence_get (iter);
+      child = child_info->widget;
+
+      if (!gtk_widget_get_visible (child))
+        continue;
+
+      if (orientation == GTK_ORIENTATION_HORIZONTAL)
+        gtk_widget_get_preferred_width (child,
+                                        &child_min, &child_nat);
+      else
+        gtk_widget_get_preferred_height (child,
+                                         &child_min, &child_nat);
+
+      /* Get the index and push it over for the last line when spreading to the end */
+      position = i % line_length;
+
+      item_align = ORIENTATION_ALIGN_POLICY (box);
+      if (item_align == GTK_ALIGN_END && i >= n_children - extra_items)
+        position += line_length - extra_items;
+
+      /* Round up the size of every column/row */
+      item_sizes[position].minimum_size = MAX (item_sizes[position].minimum_size, child_min);
+      item_sizes[position].natural_size = MAX (item_sizes[position].natural_size, child_nat);
+    }
+
+  for (i = 0; i < line_length; i++)
+    natural_line_size += item_sizes[i].natural_size;
+
+  natural_line_size += (line_length - 1) * item_spacing;
+
+  return natural_line_size;
+}
+
+static GtkRequestedSize *
+fit_aligned_item_requests (EggFlowBox     *box,
+                           GtkOrientation  orientation,
+                           gint            avail_size,
+                           gint            item_spacing,
+                           gint           *line_length, /* in-out */
+                           gint            items_per_line,
+                           gint            n_children)
+{
+  GtkRequestedSize *sizes, *try_sizes;
+  gint try_line_size, try_length;
+
+  sizes = g_new0 (GtkRequestedSize, *line_length);
+
+  /* get the sizes for the initial guess */
+  try_line_size = gather_aligned_item_requests (box,
+                                                orientation,
+                                                *line_length,
+                                                item_spacing,
+                                                n_children,
+                                                sizes);
+
+  /* Try columnizing the whole thing and adding an item to the end of the line;
+   * try to fit as many columns into the available size as possible */
+  for (try_length = *line_length + 1; try_line_size < avail_size; try_length++)
+    {
+      try_sizes = g_new0 (GtkRequestedSize, try_length);
+      try_line_size = gather_aligned_item_requests (box,
+                                                    orientation,
+                                                    try_length,
+                                                    item_spacing,
+                                                    n_children,
+                                                    try_sizes);
+
+      if (try_line_size <= avail_size
+          && items_per_line >= try_length)
+        {
+          *line_length = try_length;
+
+          g_free (sizes);
+          sizes = try_sizes;
+        }
+      else
+        {
+          /* oops, this one failed; stick to the last size that fit and then return */
+          g_free (try_sizes);
+          break;
+        }
+    }
+
+  return sizes;
+}
+
+typedef struct {
+  GArray *requested;
+  gint    extra_pixels;
+} AllocatedLine;
+
+static gint
+get_offset_pixels (GtkAlign align,
+                   gint     pixels)
+{
+  gint offset;
+
+  switch (align) {
+  case GTK_ALIGN_START:
+  case GTK_ALIGN_FILL:
+    offset = 0;
+    break;
+  case GTK_ALIGN_CENTER:
+    offset = pixels / 2;
+    break;
+  case GTK_ALIGN_END:
+    offset = pixels;
+    break;
+  default:
+    g_assert_not_reached ();
+    break;
+  }
+
+  return offset;
+}
+
+static void
+egg_flow_box_real_size_allocate (GtkWidget     *widget,
+                                 GtkAllocation *allocation)
+{
+  EggFlowBox *box = EGG_FLOW_BOX (widget);
+  EggFlowBoxPrivate  *priv = box->priv;
+  GtkAllocation child_allocation;
+  gint avail_size, avail_other_size, min_items, item_spacing, line_spacing;
+  GtkAlign item_align;
+  GtkAlign line_align;
+  GdkWindow *window;
+  GtkRequestedSize *line_sizes = NULL;
+  GtkRequestedSize *item_sizes = NULL;
+  gint min_item_size, nat_item_size;
+  gint line_length;
+  gint item_size = 0;
+  gint line_size = 0, min_fixed_line_size = 0, nat_fixed_line_size = 0;
+  gint line_offset, item_offset, n_children, n_lines, line_count;
+  gint extra_pixels = 0, extra_per_item = 0, extra_extra = 0;
+  gint extra_line_pixels = 0, extra_per_line = 0, extra_line_extra = 0;
+  gint i, this_line_size;
+  GSequenceIter *iter;
+  GtkStyleContext *context;
+  gint focus_width;
+  gint focus_pad;
+
+  child_allocation.x = 0;
+  child_allocation.y = 0;
+  child_allocation.width = 0;
+  child_allocation.height = 0;
+
+  gtk_widget_set_allocation (widget, allocation);
+  window = gtk_widget_get_window (widget);
+  if (window != NULL)
+    gdk_window_move_resize (window,
+                            allocation->x, allocation->y,
+                            allocation->width, allocation->height);
+
+  context = gtk_widget_get_style_context (GTK_WIDGET (box));
+  gtk_style_context_get_style (context,
+                               "focus-line-width", &focus_width,
+                               "focus-padding", &focus_pad,
+                               NULL);
+  child_allocation.x = 0 + focus_width + focus_pad;
+  child_allocation.y = 0;
+  child_allocation.width = allocation->width - 2 * (focus_width + focus_pad);
+
+  min_items = MAX (1, priv->min_children_per_line);
+
+  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      avail_size = allocation->width;
+      avail_other_size = allocation->height;
+      item_spacing = priv->column_spacing;
+      line_spacing = priv->row_spacing;
+    }
+  else /* GTK_ORIENTATION_VERTICAL */
+    {
+      avail_size = allocation->height;
+      avail_other_size = allocation->width;
+      item_spacing = priv->row_spacing;
+      line_spacing = priv->column_spacing;
+    }
+
+  item_align = ORIENTATION_ALIGN_POLICY (box);
+  line_align = OPPOSING_ORIENTATION_ALIGN_POLICY (box);
+
+
+  /*
+   * Deal with ALIGNED/HOMOGENEOUS modes first, start with
+   * initial guesses at item/line sizes
+   */
+  get_average_item_size (box, priv->orientation, &min_item_size, &nat_item_size);
+
+  /* By default flow at the natural item width */
+  line_length = avail_size / (nat_item_size + item_spacing);
+
+  /* After the above aproximation, check if we cant fit one more on the line */
+  if (line_length * item_spacing + (line_length + 1) * nat_item_size <= avail_size)
+    line_length++;
+
+  /* Its possible we were allocated just less than the natural width of the
+   * minimum item flow length */
+  line_length = MAX (min_items, line_length);
+  line_length = MIN (line_length, priv->max_children_per_line);
+
+  /* Get how many lines we'll be needing to flow */
+  n_children = get_visible_children (box);
+
+  /* Here we just use the largest height-for-width and use that for the height
+   * of all lines */
+  if (priv->homogeneous)
+    {
+      n_lines = n_children / line_length;
+      if ((n_children % line_length) > 0)
+        n_lines++;
+
+      n_lines = MAX (n_lines, 1);
+
+      /* Now we need the real item allocation size */
+      item_size = (avail_size - (line_length - 1) * item_spacing) / line_length;
+
+      /* Cut out the expand space if we're not distributing any */
+      if (item_align != GTK_ALIGN_FILL)
+        item_size = MIN (item_size, nat_item_size);
+
+      get_largest_size_for_opposing_orientation (box,
+                                                 priv->orientation,
+                                                 item_size,
+                                                 &min_fixed_line_size,
+                                                 &nat_fixed_line_size);
+
+      /* resolve a fixed 'line_size' */
+      line_size = (avail_other_size - (n_lines - 1) * line_spacing) / n_lines;
+
+      if (line_align != GTK_ALIGN_FILL)
+        line_size = MIN (line_size, nat_fixed_line_size);
+
+      /* Get the real extra pixels incase of GTK_ALIGN_START lines */
+      extra_pixels      = avail_size       - (line_length - 1) * item_spacing - item_size * line_length;
+      extra_line_pixels = avail_other_size - (n_lines - 1)     * line_spacing - line_size * n_lines;
+    }
+  else
+    {
+      gboolean first_line = TRUE;
+
+      /* Find the amount of columns that can fit aligned into the available space
+       * and collect their requests.
+       */
+      item_sizes = fit_aligned_item_requests (box,
+                                              priv->orientation,
+                                              avail_size,
+                                              item_spacing,
+                                              &line_length,
+                                              priv->max_children_per_line,
+                                              n_children);
+
+      /* Calculate the number of lines after determining the final line_length */
+      n_lines = n_children / line_length;
+      if ((n_children % line_length) > 0)
+        n_lines++;
+
+      n_lines = MAX (n_lines, 1);
+      line_sizes = g_new0 (GtkRequestedSize, n_lines);
+
+      /* Get the available remaining size */
+      avail_size -= (line_length - 1) * item_spacing;
+      for (i = 0; i < line_length; i++)
+        avail_size -= item_sizes[i].minimum_size;
+
+      /* Perform a natural allocation on the columnized items and get the remaining pixels */
+      if (avail_size > 0)
+        extra_pixels = gtk_distribute_natural_allocation (avail_size, line_length, item_sizes);
+
+      /* Now that we have the size of each column of items find the size of each individual
+       * line based on the aligned item sizes.
+       */
+
+      for (i = 0, iter = g_sequence_get_begin_iter (priv->children);
+           !g_sequence_iter_is_end (iter);
+           i++)
+        {
+
+          iter = get_largest_size_for_line_in_opposing_orientation (box,
+                                                                    priv->orientation,
+                                                                    iter,
+                                                                    line_length,
+                                                                    item_sizes,
+                                                                    extra_pixels,
+                                                                    &line_sizes[i].minimum_size,
+                                                                    &line_sizes[i].natural_size);
+
+
+          /* Its possible a line is made of completely invisible children */
+          if (line_sizes[i].natural_size > 0)
+            {
+              if (first_line)
+                first_line = FALSE;
+              else
+                avail_other_size -= line_spacing;
+
+              avail_other_size -= line_sizes[i].minimum_size;
+
+              line_sizes[i].data = GINT_TO_POINTER (i);
+            }
+        }
+
+      /* Distribute space among lines naturally */
+      if (avail_other_size > 0)
+        extra_line_pixels = gtk_distribute_natural_allocation (avail_other_size, n_lines, line_sizes);
+    }
+
+  /*
+   * Initial sizes of items/lines guessed at this point,
+   * go on to distribute expand space if needed.
+   */
+
+  /* FIXME: This portion needs to consider which columns
+   * and rows asked for expand space and distribute those
+   * accordingly for the case of ALIGNED allocation.
+   *
+   * If at least one child in a column/row asked for expand;
+   * we should make that row/column expand entirely.
+   */
+
+  /* Calculate expand space per item */
+  if (item_align == GTK_ALIGN_FILL)
+    {
+      extra_per_item = extra_pixels / line_length;
+      extra_extra    = extra_pixels % line_length;
+    }
+
+  /* Calculate expand space per line */
+  if (line_align == GTK_ALIGN_FILL)
+    {
+      extra_per_line   = extra_line_pixels / n_lines;
+      extra_line_extra = extra_line_pixels % n_lines;
+    }
+
+  /*
+   * Prepare item/line initial offsets and jump into the
+   * real allocation loop.
+   */
+  line_offset = item_offset = 0;
+
+  /* prepend extra space to item_offset/line_offset for SPREAD_END */
+  item_offset += get_offset_pixels (item_align, extra_pixels);
+  line_offset += get_offset_pixels (line_align, extra_line_pixels);
+
+  /* Get the allocation size for the first line */
+  if (priv->homogeneous)
+    this_line_size = line_size;
+  else
+    {
+      this_line_size  = line_sizes[0].minimum_size;
+
+      if (line_align == GTK_ALIGN_FILL)
+        {
+          this_line_size += extra_per_line;
+
+          if (extra_line_extra > 0)
+            this_line_size++;
+        }
+    }
+
+  i = 0;
+  line_count = 0;
+  for (iter = g_sequence_get_begin_iter (priv->children);
+       !g_sequence_iter_is_end (iter);
+       iter = g_sequence_iter_next (iter))
+    {
+      EggFlowBoxChildInfo *child_info;
+      GtkWidget *child;
+      gint position;
+      gint this_item_size;
+
+      child_info = g_sequence_get (iter);
+      child = child_info->widget;
+
+      if (!gtk_widget_get_visible (child))
+        {
+          child_info->x = child_allocation.x;
+          child_info->y = child_allocation.y;
+          child_info->width = 0;
+          child_info->height = 0;
+          continue;
+        }
+
+      /* Get item position */
+      position = i % line_length;
+
+      /* adjust the line_offset/count at the beginning of each new line */
+      if (i > 0 && position == 0)
+        {
+          /* Push the line_offset */
+          line_offset += this_line_size + line_spacing;
+
+          line_count++;
+
+          /* Get the new line size */
+          if (priv->homogeneous)
+            this_line_size = line_size;
+          else
+            {
+              this_line_size = line_sizes[line_count].minimum_size;
+
+              if (line_align == GTK_ALIGN_FILL)
+                {
+                  this_line_size += extra_per_line;
+
+                  if (line_count < extra_line_extra)
+                    this_line_size++;
+                }
+            }
+
+          item_offset = 0;
+
+          if (item_align == GTK_ALIGN_CENTER)
+            {
+              item_offset += get_offset_pixels (item_align, extra_pixels);
+            }
+          else if (item_align == GTK_ALIGN_END)
+            {
+              item_offset += get_offset_pixels (item_align, extra_pixels);
+
+              /* If we're on the last line, prepend the space for
+               * any leading items */
+              if (line_count == n_lines -1)
+                {
+                  gint extra_items = n_children % line_length;
+
+                  if (priv->homogeneous)
+                    {
+                      item_offset += item_size * (line_length - extra_items);
+                      item_offset += item_spacing * (line_length - extra_items);
+                    }
+                  else
+                    {
+                      gint j;
+
+                      for (j = 0; j < (line_length - extra_items); j++)
+                        {
+                          item_offset += item_sizes[j].minimum_size;
+                          item_offset += item_spacing;
+                        }
+                    }
+                }
+            }
+        }
+
+      /* Push the index along for the last line when spreading to the end */
+      if (item_align == GTK_ALIGN_END && line_count == n_lines -1)
+        {
+          gint extra_items = n_children % line_length;
+
+          position += line_length - extra_items;
+        }
+
+      if (priv->homogeneous)
+        this_item_size = item_size;
+      else
+        this_item_size = item_sizes[position].minimum_size;
+
+      if (item_align == GTK_ALIGN_FILL)
+        {
+          this_item_size += extra_per_item;
+
+          if (position < extra_extra)
+            this_item_size++;
+        }
+
+      /* Do the actual allocation */
+      if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+        {
+          child_allocation.x = allocation->x + item_offset;
+          child_allocation.y = allocation->y + line_offset;
+          child_allocation.width = this_item_size;
+          child_allocation.height = this_line_size;
+        }
+      else /* GTK_ORIENTATION_VERTICAL */
+        {
+          child_allocation.x = allocation->x + line_offset;
+          child_allocation.y = allocation->y + item_offset;
+          child_allocation.width = this_line_size;
+          child_allocation.height = this_item_size;
+        }
+
+      child_info->x = child_allocation.x;
+      child_info->y = child_allocation.y;
+      child_info->width = child_allocation.width;
+      child_info->height = child_allocation.height;
+      gtk_widget_size_allocate (child, &child_allocation);
+
+      item_offset += this_item_size;
+      item_offset += item_spacing;
+
+      i++;
+    }
+
+  g_free (item_sizes);
+  g_free (line_sizes);
+}
+
+static void
+egg_flow_box_real_add (GtkContainer *container,
+                       GtkWidget    *child)
+{
+  EggFlowBox *box = EGG_FLOW_BOX (container);
+  EggFlowBoxPrivate *priv = box->priv;
+  EggFlowBoxChildInfo *info;
+  GSequenceIter *iter = NULL;
+
+  g_return_if_fail (EGG_IS_FLOW_BOX (box));
+  g_return_if_fail (GTK_IS_WIDGET (child));
+
+  info = egg_flow_box_child_info_new (child);
+  g_hash_table_insert (priv->child_hash, child, info);
+  iter = g_sequence_append (priv->children, info);
+  info->iter = iter;
+
+  gtk_widget_set_parent (child, GTK_WIDGET (box));
+}
+
+static void
+egg_flow_box_real_remove (GtkContainer *container,
+                          GtkWidget    *child)
+{
+  EggFlowBox *box = EGG_FLOW_BOX (container);
+  EggFlowBoxPrivate *priv = box->priv;
+  gboolean was_visible;
+  EggFlowBoxChildInfo *info;
+
+  g_return_if_fail (child != NULL);
+
+  was_visible = gtk_widget_get_visible (child);
+
+  info = egg_flow_box_lookup_info (box, child);
+  if (info == NULL)
+    {
+      g_warning ("Tried to remove non-child %p\n", child);
+      return;
+    }
+
+  gtk_widget_unparent (child);
+  g_hash_table_remove (priv->child_hash, child);
+  g_sequence_remove (info->iter);
+
+  if (was_visible && gtk_widget_get_visible (GTK_WIDGET (box)))
+    gtk_widget_queue_resize (GTK_WIDGET (box));
+}
+
+static void
+egg_flow_box_real_forall (GtkContainer *container,
+                          gboolean      include_internals,
+                          GtkCallback   callback,
+                          gpointer      callback_target)
+{
+  EggFlowBox *box = EGG_FLOW_BOX (container);
+  EggFlowBoxPrivate *priv   = box->priv;
+  GSequenceIter *iter;
+  EggFlowBoxChildInfo *child_info;
+
+  iter = g_sequence_get_begin_iter (priv->children);
+  while (!g_sequence_iter_is_end (iter))
+    {
+      child_info = g_sequence_get (iter);
+      iter = g_sequence_iter_next (iter);
+      callback (child_info->widget, callback_target);
+    }
+}
+
+static GType
+egg_flow_box_real_child_type (GtkContainer *container)
+{
+  return GTK_TYPE_WIDGET;
+}
+
+static GtkSizeRequestMode
+egg_flow_box_real_get_request_mode (GtkWidget *widget)
+{
+  EggFlowBox *box = EGG_FLOW_BOX (widget);
+  EggFlowBoxPrivate *priv   = box->priv;
+
+  return (priv->orientation == GTK_ORIENTATION_HORIZONTAL) ?
+    GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH : GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
+}
+
+/* Gets the largest minimum and natural length of
+ * 'line_length' consecutive items when aligned into rows/columns */
+static void
+get_largest_aligned_line_length (EggFlowBox     *box,
+                                 GtkOrientation  orientation,
+                                 gint            line_length,
+                                 gint           *min_size,
+                                 gint           *nat_size)
+{
+  EggFlowBoxPrivate *priv = box->priv;
+  GSequenceIter *iter;
+  gint max_min_size = 0;
+  gint max_nat_size = 0;
+  gint spacing, i;
+  GtkRequestedSize *aligned_item_sizes;
+
+  if (orientation == GTK_ORIENTATION_HORIZONTAL)
+    spacing = priv->column_spacing;
+  else
+    spacing = priv->row_spacing;
+
+  aligned_item_sizes = g_new0 (GtkRequestedSize, line_length);
+
+  /* Get the largest sizes of each index in the line.
+   */
+  i = 0;
+  for (iter = g_sequence_get_begin_iter (priv->children);
+       !g_sequence_iter_is_end (iter);
+       iter = g_sequence_iter_next (iter))
+    {
+      EggFlowBoxChildInfo *child_info;
+      GtkWidget *child;
+      gint child_min, child_nat;
+
+      child_info = g_sequence_get (iter);
+      child = child_info->widget;
+
+      if (!gtk_widget_get_visible (child))
+        continue;
+
+      if (orientation == GTK_ORIENTATION_HORIZONTAL)
+        gtk_widget_get_preferred_width (child,
+                                        &child_min, &child_nat);
+      else /* GTK_ORIENTATION_VERTICAL */
+        gtk_widget_get_preferred_height (child,
+                                         &child_min, &child_nat);
+
+      aligned_item_sizes[i % line_length].minimum_size =
+        MAX (aligned_item_sizes[i % line_length].minimum_size, child_min);
+
+      aligned_item_sizes[i % line_length].natural_size =
+        MAX (aligned_item_sizes[i % line_length].natural_size, child_nat);
+
+      i++;
+    }
+
+  /* Add up the largest indexes */
+  for (i = 0; i < line_length; i++)
+    {
+      max_min_size += aligned_item_sizes[i].minimum_size;
+      max_nat_size += aligned_item_sizes[i].natural_size;
+    }
+
+  g_free (aligned_item_sizes);
+
+  max_min_size += (line_length - 1) * spacing;
+  max_nat_size += (line_length - 1) * spacing;
+
+  if (min_size)
+    *min_size = max_min_size;
+
+  if (nat_size)
+    *nat_size = max_nat_size;
+}
+
+
+static void
+egg_flow_box_real_get_preferred_width (GtkWidget *widget,
+                                       gint      *minimum_size,
+                                       gint      *natural_size)
+{
+  EggFlowBox *box  = EGG_FLOW_BOX (widget);
+  EggFlowBoxPrivate *priv = box->priv;
+  gint min_item_width, nat_item_width;
+  gint min_items, nat_items;
+  gint min_width, nat_width;
+
+  min_items = MAX (1, priv->min_children_per_line);
+  nat_items = MAX (min_items, priv->max_children_per_line);
+
+  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      min_width = nat_width = 0;
+
+      if (! priv->homogeneous)
+        {
+          /* When not homogeneous; horizontally oriented boxes
+           * need enough width for the widest row */
+          if (min_items == 1)
+            {
+              get_average_item_size (box,
+                                     GTK_ORIENTATION_HORIZONTAL,
+                                     &min_item_width,
+                                     &nat_item_width);
+
+              min_width += min_item_width;
+              nat_width += nat_item_width;
+            }
+          else
+            {
+              gint min_line_length, nat_line_length;
+
+              get_largest_aligned_line_length (box,
+                                               GTK_ORIENTATION_HORIZONTAL,
+                                               min_items,
+                                               &min_line_length,
+                                               &nat_line_length);
+
+              if (nat_items > min_items)
+                get_largest_aligned_line_length (box,
+                                                 GTK_ORIENTATION_HORIZONTAL,
+                                                 nat_items,
+                                                 NULL,
+                                                 &nat_line_length);
+
+              min_width += min_line_length;
+              nat_width += nat_line_length;
+            }
+        }
+      else /* In homogeneous mode; horizontally oriented boxs
+            * give the same width to all children */
+        {
+          get_average_item_size (box, GTK_ORIENTATION_HORIZONTAL,
+                                 &min_item_width, &nat_item_width);
+
+          min_width += min_item_width * min_items;
+          min_width += (min_items -1) * priv->column_spacing;
+
+          nat_width += nat_item_width * nat_items;
+          nat_width += (nat_items -1) * priv->column_spacing;
+        }
+    }
+  else /* GTK_ORIENTATION_VERTICAL */
+    {
+      /* Return the width for the 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);
+
+    }
+
+  if (minimum_size)
+    *minimum_size = min_width;
+
+  if (natural_size)
+    *natural_size = nat_width;
+}
+
+static void
+egg_flow_box_real_get_preferred_height (GtkWidget *widget,
+                                        gint      *minimum_size,
+                                        gint      *natural_size)
+{
+  EggFlowBox *box  = EGG_FLOW_BOX (widget);
+  EggFlowBoxPrivate *priv = box->priv;
+  gint min_item_height, nat_item_height;
+  gint min_items, nat_items;
+  gint min_height, nat_height;
+
+  min_items = MAX (1, priv->min_children_per_line);
+  nat_items = MAX (min_items, priv->max_children_per_line);
+
+  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      /* 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);
+    }
+  else /* GTK_ORIENTATION_VERTICAL */
+    {
+      min_height   = nat_height = 0;
+
+      if (! priv->homogeneous)
+        {
+          /* When not homogeneous; vertically oriented boxes
+           * need enough height for the tallest column */
+          if (min_items == 1)
+            {
+              get_average_item_size (box, GTK_ORIENTATION_VERTICAL,
+                                     &min_item_height, &nat_item_height);
+
+              min_height += min_item_height;
+              nat_height += nat_item_height;
+            }
+          else
+            {
+              gint min_line_length, nat_line_length;
+
+              get_largest_aligned_line_length (box,
+                                               GTK_ORIENTATION_VERTICAL,
+                                               min_items,
+                                               &min_line_length,
+                                               &nat_line_length);
+
+              if (nat_items > min_items)
+                get_largest_aligned_line_length (box,
+                                                 GTK_ORIENTATION_VERTICAL,
+                                                 nat_items,
+                                                 NULL,
+                                                 &nat_line_length);
+
+              min_height += min_line_length;
+              nat_height += nat_line_length;
+            }
+
+        }
+      else /* In homogeneous mode; vertically oriented boxs
+            * give the same height to all children */
+        {
+          get_average_item_size (box,
+                                 GTK_ORIENTATION_VERTICAL,
+                                 &min_item_height,
+                                 &nat_item_height);
+
+          min_height += min_item_height * min_items;
+          min_height += (min_items -1) * priv->row_spacing;
+
+          nat_height += nat_item_height * nat_items;
+          nat_height += (nat_items -1) * priv->row_spacing;
+        }
+    }
+
+  if (minimum_size)
+    *minimum_size = min_height;
+
+  if (natural_size)
+    *natural_size = nat_height;
+}
+
+static void
+egg_flow_box_real_get_preferred_height_for_width (GtkWidget *widget,
+                                                  gint       width,
+                                                  gint      *minimum_height,
+                                                  gint      *natural_height)
+{
+  EggFlowBox *box = EGG_FLOW_BOX (widget);
+  EggFlowBoxPrivate *priv   = box->priv;
+  gint min_item_width, nat_item_width;
+  gint min_items;
+  gint min_height, nat_height;
+  gint avail_size, n_children;
+
+  min_items = MAX (1, priv->min_children_per_line);
+
+  min_height = 0;
+  nat_height = 0;
+
+  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      gint min_width;
+      gint line_length;
+      gint item_size, extra_pixels;
+
+      n_children = get_visible_children (box);
+
+      /* Make sure its no smaller than the minimum */
+      GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, NULL);
+
+      avail_size  = MAX (width, min_width);
+
+      get_average_item_size (box, GTK_ORIENTATION_HORIZONTAL, &min_item_width, &nat_item_width);
+
+      /* By default flow at the natural item width */
+      line_length = avail_size / (nat_item_width + priv->column_spacing);
+
+      /* After the above aproximation, check if we cant fit one more on the line */
+      if (line_length * priv->column_spacing + (line_length + 1) * nat_item_width <= avail_size)
+        line_length++;
+
+      /* Its possible we were allocated just less than the natural width of the
+       * minimum item flow length */
+      line_length = MAX (min_items, line_length);
+      line_length = MIN (line_length, priv->max_children_per_line);
+
+      /* Now we need the real item allocation size */
+      item_size = (avail_size - (line_length - 1) * priv->column_spacing) / line_length;
+
+      /* Cut out the expand space if we're not distributing any */
+      if (priv->halign_policy != GTK_ALIGN_FILL)
+        {
+          item_size    = MIN (item_size, nat_item_width);
+          extra_pixels = 0;
+        }
+      else
+        /* Collect the extra pixels for expand children */
+        extra_pixels = (avail_size - (line_length - 1) * priv->column_spacing) % line_length;
+
+      if (priv->homogeneous)
+        {
+          gint min_item_height, nat_item_height;
+          gint lines;
+
+          /* Here we just use the largest height-for-width and
+           * add up the size accordingly */
+          get_largest_size_for_opposing_orientation (box,
+                                                     GTK_ORIENTATION_HORIZONTAL,
+                                                     item_size,
+                                                     &min_item_height,
+                                                     &nat_item_height);
+
+          /* Round up how many lines we need to allocate for */
+          lines = n_children / line_length;
+          if ((n_children % line_length) > 0)
+            lines++;
+
+          min_height = min_item_height * lines;
+          nat_height = nat_item_height * lines;
+
+          min_height += (lines - 1) * priv->row_spacing;
+          nat_height += (lines - 1) * priv->row_spacing;
+        }
+      else
+        {
+          gint min_line_height, nat_line_height, i;
+          gboolean first_line = TRUE;
+          GtkRequestedSize *item_sizes;
+          GSequenceIter *iter;
+
+          /* First get the size each set of items take to span the line
+           * when aligning the items above and below after flowping.
+           */
+          item_sizes = fit_aligned_item_requests (box,
+                                                  priv->orientation,
+                                                  avail_size,
+                                                  priv->column_spacing,
+                                                  &line_length,
+                                                  priv->max_children_per_line,
+                                                  n_children);
+
+          /* Get the available remaining size */
+          avail_size -= (line_length - 1) * priv->column_spacing;
+          for (i = 0; i < line_length; i++)
+            avail_size -= item_sizes[i].minimum_size;
+
+          if (avail_size > 0)
+            extra_pixels = gtk_distribute_natural_allocation (avail_size, line_length, item_sizes);
+
+          for (iter = g_sequence_get_begin_iter (priv->children);
+               !g_sequence_iter_is_end (iter);
+               iter = g_sequence_iter_next (iter))
+            {
+              iter = get_largest_size_for_line_in_opposing_orientation (box,
+                                                                        GTK_ORIENTATION_HORIZONTAL,
+                                                                        iter,
+                                                                        line_length,
+                                                                        item_sizes,
+                                                                        extra_pixels,
+                                                                        &min_line_height,
+                                                                        &nat_line_height);
+
+              /* Its possible the line only had invisible widgets */
+              if (nat_line_height > 0)
+                {
+                  if (first_line)
+                    first_line = FALSE;
+                  else
+                    {
+                      min_height += priv->row_spacing;
+                      nat_height += priv->row_spacing;
+                    }
+
+                  min_height += min_line_height;
+                  nat_height += nat_line_height;
+                }
+            }
+
+          g_free (item_sizes);
+        }
+    }
+  else /* GTK_ORIENTATION_VERTICAL */
+    {
+      /* Return the minimum height */
+      GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, &min_height, &nat_height);
+    }
+
+  if (minimum_height)
+    *minimum_height = min_height;
+
+  if (natural_height)
+    *natural_height = nat_height;
+}
+
+static void
+egg_flow_box_real_get_preferred_width_for_height (GtkWidget *widget,
+                                                  gint       height,
+                                                  gint      *minimum_width,
+                                                  gint      *natural_width)
+{
+  EggFlowBox *box = EGG_FLOW_BOX (widget);
+  EggFlowBoxPrivate *priv   = box->priv;
+  gint min_item_height, nat_item_height;
+  gint min_items;
+  gint min_width, nat_width;
+  gint avail_size, n_children;
+
+  min_items = MAX (1, priv->min_children_per_line);
+
+  min_width = 0;
+  nat_width = 0;
+
+  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      /* Return the minimum width */
+      GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, &nat_width);
+    }
+  else /* GTK_ORIENTATION_VERTICAL */
+    {
+      gint min_height;
+      gint line_length;
+      gint item_size, extra_pixels;
+
+      n_children = get_visible_children (box);
+
+      /* Make sure its no smaller than the minimum */
+      GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, &min_height, NULL);
+
+      avail_size = MAX (height, min_height);
+
+      get_average_item_size (box, GTK_ORIENTATION_VERTICAL, &min_item_height, &nat_item_height);
+
+      /* By default flow at the natural item width */
+      line_length = avail_size / (nat_item_height + priv->row_spacing);
+
+      /* After the above aproximation, check if we cant fit one more on the line */
+      if (line_length * priv->row_spacing + (line_length + 1) * nat_item_height <= avail_size)
+        line_length++;
+
+      /* Its possible we were allocated just less than the natural width of the
+       * minimum item flow length */
+      line_length = MAX (min_items, line_length);
+      line_length = MIN (line_length, priv->max_children_per_line);
+
+      /* Now we need the real item allocation size */
+      item_size = (avail_size - (line_length - 1) * priv->row_spacing) / line_length;
+
+      /* Cut out the expand space if we're not distributing any */
+      if (priv->valign_policy != GTK_ALIGN_FILL)
+        {
+          item_size    = MIN (item_size, nat_item_height);
+          extra_pixels = 0;
+        }
+      else
+        /* Collect the extra pixels for expand children */
+        extra_pixels = (avail_size - (line_length - 1) * priv->row_spacing) % line_length;
+
+      if (priv->homogeneous)
+        {
+          gint min_item_width, nat_item_width;
+          gint lines;
+
+          /* Here we just use the largest height-for-width and
+           * add up the size accordingly */
+          get_largest_size_for_opposing_orientation (box,
+                                                     GTK_ORIENTATION_VERTICAL,
+                                                     item_size,
+                                                     &min_item_width,
+                                                     &nat_item_width);
+
+          /* Round up how many lines we need to allocate for */
+          n_children = get_visible_children (box);
+          lines = n_children / line_length;
+          if ((n_children % line_length) > 0)
+            lines++;
+
+          min_width = min_item_width * lines;
+          nat_width = nat_item_width * lines;
+
+          min_width += (lines - 1) * priv->column_spacing;
+          nat_width += (lines - 1) * priv->column_spacing;
+        }
+      else
+        {
+          gint min_line_width, nat_line_width, i;
+          gboolean first_line = TRUE;
+          GtkRequestedSize *item_sizes;
+          GSequenceIter *iter;
+
+          /* First get the size each set of items take to span the line
+           * when aligning the items above and below after flowping.
+           */
+          item_sizes = fit_aligned_item_requests (box,
+                                                  priv->orientation,
+                                                  avail_size,
+                                                  priv->row_spacing,
+                                                  &line_length,
+                                                  priv->max_children_per_line,
+                                                  n_children);
+
+          /* Get the available remaining size */
+          avail_size -= (line_length - 1) * priv->column_spacing;
+          for (i = 0; i < line_length; i++)
+            avail_size -= item_sizes[i].minimum_size;
+
+          if (avail_size > 0)
+            extra_pixels = gtk_distribute_natural_allocation (avail_size, line_length, item_sizes);
+
+          for (iter = g_sequence_get_begin_iter (priv->children);
+               !g_sequence_iter_is_end (iter);
+               iter = g_sequence_iter_next (iter))
+            {
+              iter = get_largest_size_for_line_in_opposing_orientation (box,
+                                                                        GTK_ORIENTATION_VERTICAL,
+                                                                        iter,
+                                                                        line_length,
+                                                                        item_sizes,
+                                                                        extra_pixels,
+                                                                        &min_line_width,
+                                                                        &nat_line_width);
+
+              /* Its possible the last line only had invisible widgets */
+              if (nat_line_width > 0)
+                {
+                  if (first_line)
+                    first_line = FALSE;
+                  else
+                    {
+                      min_width += priv->column_spacing;
+                      nat_width += priv->column_spacing;
+                    }
+
+                  min_width += min_line_width;
+                  nat_width += nat_line_width;
+                }
+            }
+          g_free (item_sizes);
+        }
+    }
+
+  if (minimum_width)
+    *minimum_width = min_width;
+
+  if (natural_width)
+    *natural_width = nat_width;
+}
+
+/**
+ * egg_flow_box_set_halign_policy:
+ * @box: An #EggFlowBox
+ * @align: The #GtkAlign to use.
+ *
+ * Sets the horizontal align policy for @box's children.
+ */
+void
+egg_flow_box_set_halign_policy (EggFlowBox *box,
+                                GtkAlign    align)
+{
+  EggFlowBoxPrivate *priv;
+
+  g_return_if_fail (EGG_IS_FLOW_BOX (box));
+
+  priv = box->priv;
+
+  if (priv->halign_policy != align)
+    {
+      priv->halign_policy = align;
+
+      gtk_widget_queue_resize (GTK_WIDGET (box));
+
+      g_object_notify (G_OBJECT (box), "halign-policy");
+    }
+}
+
+/**
+ * egg_flow_box_get_halign_policy:
+ * @box: An #EggFlowBox
+ *
+ * Gets the horizontal alignment policy.
+ *
+ * Returns: The horizontal #GtkAlign for @box.
+ */
+GtkAlign
+egg_flow_box_get_halign_policy (EggFlowBox *box)
+{
+  g_return_val_if_fail (EGG_IS_FLOW_BOX (box), FALSE);
+
+  return box->priv->halign_policy;
+}
+
+
+/**
+ * egg_flow_box_set_valign_policy:
+ * @box: An #EggFlowBox
+ * @align: The #GtkAlign to use.
+ *
+ * Sets the vertical alignment policy for @box's children.
+ */
+void
+egg_flow_box_set_valign_policy (EggFlowBox *box,
+                                GtkAlign    align)
+{
+  EggFlowBoxPrivate *priv;
+
+  g_return_if_fail (EGG_IS_FLOW_BOX (box));
+
+  priv = box->priv;
+
+  if (priv->valign_policy != align)
+    {
+      priv->valign_policy = align;
+
+      gtk_widget_queue_resize (GTK_WIDGET (box));
+
+      g_object_notify (G_OBJECT (box), "valign-policy");
+    }
+}
+
+/**
+ * egg_flow_box_get_valign_policy:
+ * @box: An #EggFlowBox
+ *
+ * Gets the vertical alignment policy.
+ *
+ * Returns: The vertical #GtkAlign for @box.
+ */
+GtkAlign
+egg_flow_box_get_valign_policy (EggFlowBox *box)
+{
+  g_return_val_if_fail (EGG_IS_FLOW_BOX (box), FALSE);
+
+  return box->priv->valign_policy;
+}
+
+
+/**
+ * egg_flow_box_set_row_spacing:
+ * @box: An #EggFlowBox
+ * @spacing: The spacing to use.
+ *
+ * Sets the vertical space to add between children.
+ */
+void
+egg_flow_box_set_row_spacing  (EggFlowBox *box,
+                               guint       spacing)
+{
+  EggFlowBoxPrivate *priv;
+
+  g_return_if_fail (EGG_IS_FLOW_BOX (box));
+
+  priv = box->priv;
+
+  if (priv->row_spacing != spacing)
+    {
+      priv->row_spacing = spacing;
+
+      gtk_widget_queue_resize (GTK_WIDGET (box));
+
+      g_object_notify (G_OBJECT (box), "vertical-spacing");
+    }
+}
+
+/**
+ * egg_flow_box_get_row_spacing:
+ * @box: An #EggFlowBox
+ *
+ * Gets the vertical spacing.
+ *
+ * Returns: The vertical spacing.
+ */
+guint
+egg_flow_box_get_row_spacing  (EggFlowBox *box)
+{
+  g_return_val_if_fail (EGG_IS_FLOW_BOX (box), FALSE);
+
+  return box->priv->row_spacing;
+}
+
+/**
+ * egg_flow_box_set_column_spacing:
+ * @box: An #EggFlowBox
+ * @spacing: The spacing to use.
+ *
+ * Sets the horizontal space to add between children.
+ */
+void
+egg_flow_box_set_column_spacing (EggFlowBox    *box,
+                                 guint          spacing)
+{
+  EggFlowBoxPrivate *priv;
+
+  g_return_if_fail (EGG_IS_FLOW_BOX (box));
+
+  priv = box->priv;
+
+  if (priv->column_spacing != spacing)
+    {
+      priv->column_spacing = spacing;
+
+      gtk_widget_queue_resize (GTK_WIDGET (box));
+
+      g_object_notify (G_OBJECT (box), "horizontal-spacing");
+    }
+}
+
+/**
+ * egg_flow_box_get_column_spacing:
+ * @box: An #EggFlowBox
+ *
+ * Gets the horizontal spacing.
+ *
+ * Returns: The horizontal spacing.
+ */
+guint
+egg_flow_box_get_column_spacing (EggFlowBox *box)
+{
+  g_return_val_if_fail (EGG_IS_FLOW_BOX (box), FALSE);
+
+  return box->priv->column_spacing;
+}
+
+/**
+ * egg_flow_box_set_min_children_per_line:
+ * @box: An #EggFlowBox
+ * @n_children: The minimum amount of children per line.
+ *
+ * Sets the minimum amount of children to line up
+ * in @box's orientation before flowping.
+ */
+void
+egg_flow_box_set_min_children_per_line (EggFlowBox *box,
+                                        guint       n_children)
+{
+  EggFlowBoxPrivate *priv;
+
+  g_return_if_fail (EGG_IS_FLOW_BOX (box));
+
+  priv = box->priv;
+
+  if (priv->min_children_per_line != n_children)
+    {
+      priv->min_children_per_line = n_children;
+
+      gtk_widget_queue_resize (GTK_WIDGET (box));
+
+      g_object_notify (G_OBJECT (box), "min-children-per-line");
+    }
+}
+
+/**
+ * egg_flow_box_get_min_children_per_line:
+ * @box: An #EggFlowBox
+ *
+ * Gets the minimum amount of children per line.
+ *
+ * Returns: The minimum amount of children per line.
+ */
+guint
+egg_flow_box_get_min_children_per_line (EggFlowBox *box)
+{
+  g_return_val_if_fail (EGG_IS_FLOW_BOX (box), FALSE);
+
+  return box->priv->min_children_per_line;
+}
+
+/**
+ * egg_flow_box_set_max_children_per_line:
+ * @box: An #EggFlowBox
+ * @n_children: The natural amount of children per line.
+ *
+ * Sets the natural length of items to request and
+ * allocate space for in @box's orientation.
+ *
+ * Setting the natural amount of children per line
+ * limits the overall natural size request to be no more
+ * than @n_children items long in the given orientation.
+ */
+void
+egg_flow_box_set_max_children_per_line (EggFlowBox *box,
+                                        guint       n_children)
+{
+  EggFlowBoxPrivate *priv;
+
+  g_return_if_fail (EGG_IS_FLOW_BOX (box));
+
+  priv = box->priv;
+
+  if (priv->max_children_per_line != n_children)
+    {
+      priv->max_children_per_line = n_children;
+
+      gtk_widget_queue_resize (GTK_WIDGET (box));
+
+      g_object_notify (G_OBJECT (box), "max-children-per-line");
+    }
+}
+
+/**
+ * egg_flow_box_get_max_children_per_line:
+ * @box: An #EggFlowBox
+ *
+ * Gets the natural amount of children per line.
+ *
+ * Returns: The natural amount of children per line.
+ */
+guint
+egg_flow_box_get_max_children_per_line (EggFlowBox *box)
+{
+  g_return_val_if_fail (EGG_IS_FLOW_BOX (box), FALSE);
+
+  return box->priv->max_children_per_line;
+}
+
+/**
+ * egg_flow_box_set_activate_on_single_click:
+ * @box: An #EggFlowBox
+ * @single: %TRUE to emit child-activated on a single click
+ *
+ * Causes the #EggFlowBox::child-activated signal to be emitted on
+ * a single click instead of a double click.
+ **/
+void
+egg_flow_box_set_activate_on_single_click (EggFlowBox *box,
+                                           gboolean    single)
+{
+  g_return_if_fail (EGG_IS_FLOW_BOX (box));
+
+  single = single != FALSE;
+
+  if (box->priv->activate_on_single_click == single)
+    return;
+
+  box->priv->activate_on_single_click = single;
+  g_object_notify (G_OBJECT (box), "activate-on-single-click");
+}
+
+/**
+ * egg_flow_box_get_activate_on_single_click:
+ * @box: An #EggFlowBox
+ *
+ * Gets the setting set by egg_flow_box_set_activate_on_single_click().
+ *
+ * Return value: %TRUE if child-activated will be emitted on a single click
+ **/
+gboolean
+egg_flow_box_get_activate_on_single_click (EggFlowBox *box)
+{
+  g_return_val_if_fail (EGG_IS_FLOW_BOX (box), FALSE);
+
+  return box->priv->activate_on_single_click;
+}
+
+static void
+egg_flow_box_get_property (GObject      *object,
+                           guint         prop_id,
+                           GValue       *value,
+                           GParamSpec   *pspec)
+{
+  EggFlowBox *box  = EGG_FLOW_BOX (object);
+  EggFlowBoxPrivate *priv = box->priv;
+
+  switch (prop_id)
+    {
+    case PROP_ORIENTATION:
+      g_value_set_enum (value, priv->orientation);
+      break;
+    case PROP_HOMOGENEOUS:
+      g_value_set_boolean (value, priv->homogeneous);
+      break;
+    case PROP_HALIGN_POLICY:
+      g_value_set_enum (value, priv->halign_policy);
+      break;
+    case PROP_VALIGN_POLICY:
+      g_value_set_enum (value, priv->valign_policy);
+      break;
+    case PROP_COLUMN_SPACING:
+      g_value_set_uint (value, priv->column_spacing);
+      break;
+    case PROP_ROW_SPACING:
+      g_value_set_uint (value, priv->row_spacing);
+      break;
+    case PROP_MIN_CHILDREN_PER_LINE:
+      g_value_set_uint (value, priv->min_children_per_line);
+      break;
+    case PROP_MAX_CHILDREN_PER_LINE:
+      g_value_set_uint (value, priv->max_children_per_line);
+      break;
+    case PROP_ACTIVATE_ON_SINGLE_CLICK:
+      g_value_set_boolean (value, priv->activate_on_single_click);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+egg_flow_box_set_property (GObject      *object,
+                           guint         prop_id,
+                           const GValue *value,
+                           GParamSpec   *pspec)
+{
+  EggFlowBox *box = EGG_FLOW_BOX (object);
+  EggFlowBoxPrivate *priv   = box->priv;
+
+  switch (prop_id)
+    {
+    case PROP_ORIENTATION:
+      priv->orientation = g_value_get_enum (value);
+
+      /* Re-box the children in the new orientation */
+      gtk_widget_queue_resize (GTK_WIDGET (box));
+      break;
+    case PROP_HOMOGENEOUS:
+      egg_flow_box_set_homogeneous (box, g_value_get_boolean (value));
+      break;
+    case PROP_HALIGN_POLICY:
+      egg_flow_box_set_halign_policy (box, g_value_get_enum (value));
+      break;
+    case PROP_VALIGN_POLICY:
+      egg_flow_box_set_valign_policy (box, g_value_get_enum (value));
+      break;
+    case PROP_COLUMN_SPACING:
+      egg_flow_box_set_column_spacing (box, g_value_get_uint (value));
+      break;
+    case PROP_ROW_SPACING:
+      egg_flow_box_set_row_spacing (box, g_value_get_uint (value));
+      break;
+    case PROP_MIN_CHILDREN_PER_LINE:
+      egg_flow_box_set_min_children_per_line (box, g_value_get_uint (value));
+      break;
+    case PROP_MAX_CHILDREN_PER_LINE:
+      egg_flow_box_set_max_children_per_line (box, g_value_get_uint (value));
+      break;
+    case PROP_ACTIVATE_ON_SINGLE_CLICK:
+      egg_flow_box_set_activate_on_single_click (box, g_value_get_boolean (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static EggFlowBoxChildInfo *
+egg_flow_box_find_child_at_pos (EggFlowBox *box,
+                                gint        x,
+                                gint        y)
+{
+  EggFlowBoxPrivate *priv = box->priv;
+  EggFlowBoxChildInfo *child_info;
+  GSequenceIter *iter;
+  EggFlowBoxChildInfo *info;
+
+  child_info = NULL;
+  for (iter = g_sequence_get_begin_iter (priv->children);
+       !g_sequence_iter_is_end (iter);
+       iter = g_sequence_iter_next (iter))
+    {
+      info = (EggFlowBoxChildInfo *) g_sequence_get (iter);
+      if (x >= info->x && x < (info->x + info->width)
+          && y >= info->y && y < (info->y + info->height))
+        {
+          child_info = info;
+          break;
+        }
+    }
+
+  return child_info;
+}
+
+static gboolean
+egg_flow_box_real_button_press_event (GtkWidget      *widget,
+                                      GdkEventButton *event)
+{
+  EggFlowBox *box = EGG_FLOW_BOX (widget);
+  EggFlowBoxPrivate *priv = box->priv;
+
+  if (event->button == 1)
+    {
+      EggFlowBoxChildInfo *child;
+      child = egg_flow_box_find_child_at_pos (box, event->x, event->y);
+      if (child != NULL)
+        {
+          priv->active_child = child;
+          priv->active_child_active = TRUE;
+          if (event->type == GDK_2BUTTON_PRESS &&
+              !priv->activate_on_single_click)
+            g_signal_emit (box,
+                           signals[CHILD_ACTIVATED], 0,
+                           child->widget);
+        }
+    }
+
+  return FALSE;
+}
+
+static void
+egg_flow_box_select_and_activate (EggFlowBox          *box,
+                                  EggFlowBoxChildInfo *child)
+{
+  GtkWidget *w = NULL;
+
+  if (child != NULL)
+    w = child->widget;
+
+  if (w != NULL)
+    g_signal_emit (box, signals[CHILD_ACTIVATED], 0, w);
+}
+
+static gboolean
+egg_flow_box_real_button_release_event (GtkWidget      *widget,
+                                        GdkEventButton *event)
+{
+  EggFlowBox *box = EGG_FLOW_BOX (widget);
+  EggFlowBoxPrivate *priv = box->priv;
+
+  if (event->button == 1)
+    {
+      if (priv->active_child != NULL &&
+          priv->active_child_active)
+        {
+          if (priv->activate_on_single_click)
+            egg_flow_box_select_and_activate (box, priv->active_child);
+        }
+      priv->active_child = NULL;
+      priv->active_child_active = FALSE;
+      gtk_widget_queue_draw (GTK_WIDGET (box));
+  }
+
+  return FALSE;
+}
+
+static void
+egg_flow_box_real_realize (GtkWidget *widget)
+{
+  EggFlowBox *box = EGG_FLOW_BOX (widget);
+  GtkAllocation allocation;
+  GdkWindowAttr attributes = {0};
+  GdkWindow *window;
+
+  gtk_widget_get_allocation (GTK_WIDGET (box), &allocation);
+  gtk_widget_set_realized (GTK_WIDGET (box), TRUE);
+
+  attributes.x = allocation.x;
+  attributes.y = allocation.y;
+  attributes.width = allocation.width;
+  attributes.height = allocation.height;
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.event_mask = gtk_widget_get_events (GTK_WIDGET (box)) |
+    GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK |
+    GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
+  attributes.wclass = GDK_INPUT_OUTPUT;
+
+  window = gdk_window_new (gtk_widget_get_parent_window (GTK_WIDGET (box)),
+                           &attributes, GDK_WA_X | GDK_WA_Y);
+  gtk_style_context_set_background (gtk_widget_get_style_context (GTK_WIDGET (box)), window);
+  gdk_window_set_user_data (window, (GObject*) box);
+  gtk_widget_set_window (GTK_WIDGET (box), window); /* Passes ownership */
+}
+
+static void
+egg_flow_box_finalize (GObject *obj)
+{
+  EggFlowBox *flow_box = EGG_FLOW_BOX (obj);
+  EggFlowBoxPrivate *priv = flow_box->priv;
+
+  g_sequence_free (priv->children);
+  g_hash_table_unref (priv->child_hash);
+
+  G_OBJECT_CLASS (egg_flow_box_parent_class)->finalize (obj);
+}
+
+static void
+egg_flow_box_class_init (EggFlowBoxClass *class)
+{
+  GObjectClass      *object_class = G_OBJECT_CLASS (class);
+  GtkWidgetClass    *widget_class = GTK_WIDGET_CLASS (class);
+  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
+
+  object_class->finalize = egg_flow_box_finalize;
+  object_class->get_property = egg_flow_box_get_property;
+  object_class->set_property = egg_flow_box_set_property;
+
+  widget_class->size_allocate = egg_flow_box_real_size_allocate;
+  widget_class->realize = egg_flow_box_real_realize;
+  widget_class->button_press_event = egg_flow_box_real_button_press_event;
+  widget_class->button_release_event = egg_flow_box_real_button_release_event;
+  widget_class->get_request_mode = egg_flow_box_real_get_request_mode;
+  widget_class->get_preferred_width = egg_flow_box_real_get_preferred_width;
+  widget_class->get_preferred_height = egg_flow_box_real_get_preferred_height;
+  widget_class->get_preferred_height_for_width = egg_flow_box_real_get_preferred_height_for_width;
+  widget_class->get_preferred_width_for_height = egg_flow_box_real_get_preferred_width_for_height;
+
+  container_class->add = egg_flow_box_real_add;
+  container_class->remove = egg_flow_box_real_remove;
+  container_class->forall = egg_flow_box_real_forall;
+  container_class->child_type = egg_flow_box_real_child_type;
+  gtk_container_class_handle_border_width (container_class);
+
+  /* GObjectClass properties */
+  g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation");
+
+  g_object_class_install_property (object_class,
+                                   PROP_ACTIVATE_ON_SINGLE_CLICK,
+                                   g_param_spec_boolean ("activate-on-single-click",
+                                                         P_("Activate on Single Click"),
+                                                         P_("Activate row on a single click"),
+                                                         TRUE,
+                                                         G_PARAM_READWRITE));
+
+
+  g_object_class_install_property (object_class,
+                                   PROP_HOMOGENEOUS,
+                                   g_param_spec_boolean ("homogeneous",
+                                                         P_("Homogeneous"),
+                                                         P_("Whether the children should all be the same size"),
+                                                         FALSE,
+                                                         G_PARAM_READWRITE));
+
+  /**
+   * EggFlowBox:halign-policy:
+   *
+   * The #GtkAlign to used to define what is done with extra
+   * space in a given orientation.
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_HALIGN_POLICY,
+                                   g_param_spec_enum ("halign-policy",
+                                                      P_("Horizontal align policy"),
+                                                      P_("The align policy horizontally"),
+                                                      GTK_TYPE_ALIGN,
+                                                      GTK_ALIGN_FILL,
+                                                      G_PARAM_READWRITE));
+  /**
+   * EggFlowBox:valign-policy:
+   *
+   * The #GtkAlign to used to define what is done with extra
+   * space in a given orientation.
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_VALIGN_POLICY,
+                                   g_param_spec_enum ("valign-policy",
+                                                      P_("Vertical align policy"),
+                                                      P_("The align policy vertically"),
+                                                      GTK_TYPE_ALIGN,
+                                                      GTK_ALIGN_START,
+                                                      G_PARAM_READWRITE));
+
+  /**
+   * EggFlowBox:min-children-per-line:
+   *
+   * The minimum number of children to allocate consecutively in the given orientation.
+   *
+   * <note><para>Setting the minimum children per line ensures
+   * that a reasonably small height will be requested
+   * for the overall minimum width of the box.</para></note>
+   *
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_MIN_CHILDREN_PER_LINE,
+                                   g_param_spec_uint ("min-children-per-line",
+                                                      P_("Minimum Children Per Line"),
+                                                      P_("The minimum number of children to allocate "
+                                                         "consecutively in the given orientation."),
+                                                      0,
+                                                      65535,
+                                                      0,
+                                                      G_PARAM_READWRITE));
+
+  /**
+   * EggFlowBox:max-children-per-line:
+   *
+   * The maximum amount of children to request space for consecutively in the given orientation.
+   *
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_MAX_CHILDREN_PER_LINE,
+                                   g_param_spec_uint ("max-children-per-line",
+                                                      P_("Maximum Children Per Line"),
+                                                      P_("The maximum amount of children to request space for "
+                                                         "consecutively in the given orientation."),
+                                                      0,
+                                                      65535,
+                                                      0,
+                                                      G_PARAM_READWRITE));
+
+  /**
+   * EggFlowBox:vertical-spacing:
+   *
+   * The amount of vertical space between two children.
+   *
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_ROW_SPACING,
+                                   g_param_spec_uint ("vertical-spacing",
+                                                      P_("Vertical spacing"),
+                                                      P_("The amount of vertical space between two children"),
+                                                      0,
+                                                      65535,
+                                                      0,
+                                                      G_PARAM_READWRITE));
+
+  /**
+   * EggFlowBox:horizontal-spacing:
+   *
+   * The amount of horizontal space between two children.
+   *
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_COLUMN_SPACING,
+                                   g_param_spec_uint ("horizontal-spacing",
+                                                      P_("Horizontal spacing"),
+                                                      P_("The amount of horizontal space between two children"),
+                                                      0,
+                                                      65535,
+                                                      0,
+                                                      G_PARAM_READWRITE));
+
+  signals[CHILD_ACTIVATED] = g_signal_new ("child-activated",
+                                           EGG_TYPE_FLOW_BOX,
+                                           G_SIGNAL_RUN_LAST,
+                                           G_STRUCT_OFFSET (EggFlowBoxClass, child_activated),
+                                           NULL, NULL,
+                                           g_cclosure_marshal_VOID__OBJECT,
+                                           G_TYPE_NONE, 1,
+                                           GTK_TYPE_WIDGET);
+
+  g_type_class_add_private (class, sizeof (EggFlowBoxPrivate));
+}
+
+static void
+egg_flow_box_init (EggFlowBox *box)
+{
+  EggFlowBoxPrivate *priv;
+
+  box->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (box, EGG_TYPE_FLOW_BOX, EggFlowBoxPrivate);
+
+  priv->orientation = GTK_ORIENTATION_HORIZONTAL;
+  priv->halign_policy = GTK_ALIGN_FILL;
+  priv->valign_policy = GTK_ALIGN_START;
+  priv->column_spacing = 0;
+  priv->row_spacing = 0;
+  priv->children = g_sequence_new ((GDestroyNotify)egg_flow_box_child_info_free);
+  priv->child_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
+  priv->activate_on_single_click = TRUE;
+
+  gtk_widget_set_has_window (GTK_WIDGET (box), TRUE);
+}
+
+/**
+ * egg_flow_box_new:
+ *
+ * Creates an #EggFlowBox.
+ *
+ * Returns: A new #EggFlowBox container
+ */
+GtkWidget *
+egg_flow_box_new (void)
+{
+  return (GtkWidget *)g_object_new (EGG_TYPE_FLOW_BOX, NULL);
+}
diff --git a/egg-flow-box.h b/egg-flow-box.h
new file mode 100644
index 0000000..4418415
--- /dev/null
+++ b/egg-flow-box.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * 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_FLOW_BOX_H__
+#define __EGG_FLOW_BOX_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+
+#define EGG_TYPE_FLOW_BOX                  (egg_flow_box_get_type ())
+#define EGG_FLOW_BOX(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_FLOW_BOX, EggFlowBox))
+#define EGG_FLOW_BOX_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_FLOW_BOX, EggFlowBoxClass))
+#define EGG_IS_FLOW_BOX(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_FLOW_BOX))
+#define EGG_IS_FLOW_BOX_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_FLOW_BOX))
+#define EGG_FLOW_BOX_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_FLOW_BOX, EggFlowBoxClass))
+
+typedef struct _EggFlowBox            EggFlowBox;
+typedef struct _EggFlowBoxPrivate     EggFlowBoxPrivate;
+typedef struct _EggFlowBoxClass       EggFlowBoxClass;
+
+struct _EggFlowBox
+{
+  GtkContainer container;
+
+  /*< private >*/
+  EggFlowBoxPrivate *priv;
+};
+
+struct _EggFlowBoxClass
+{
+  GtkContainerClass parent_class;
+
+  void (* child_activated) (EggFlowBox *self, GtkWidget *child);
+};
+
+GType                 egg_flow_box_get_type                  (void) G_GNUC_CONST;
+
+GtkWidget            *egg_flow_box_new                       (void);
+
+void                  egg_flow_box_set_homogeneous           (EggFlowBox           *box,
+                                                              gboolean              homogeneous);
+gboolean              egg_flow_box_get_homogeneous           (EggFlowBox           *box);
+void                  egg_flow_box_set_halign_policy         (EggFlowBox           *box,
+                                                              GtkAlign              align);
+GtkAlign              egg_flow_box_get_halign_policy         (EggFlowBox           *box);
+void                  egg_flow_box_set_valign_policy         (EggFlowBox           *box,
+                                                              GtkAlign              align);
+GtkAlign              egg_flow_box_get_valign_policy         (EggFlowBox           *box);
+void                  egg_flow_box_set_row_spacing           (EggFlowBox           *box,
+                                                              guint                 spacing);
+guint                 egg_flow_box_get_row_spacing           (EggFlowBox           *box);
+
+void                  egg_flow_box_set_column_spacing        (EggFlowBox           *box,
+                                                              guint                 spacing);
+guint                 egg_flow_box_get_column_spacing        (EggFlowBox           *box);
+
+void                  egg_flow_box_set_min_children_per_line (EggFlowBox           *box,
+                                                              guint                 n_children);
+guint                 egg_flow_box_get_min_children_per_line (EggFlowBox           *box);
+
+void                  egg_flow_box_set_max_children_per_line (EggFlowBox           *box,
+                                                              guint                 n_children);
+guint                 egg_flow_box_get_max_children_per_line (EggFlowBox           *box);
+
+gboolean              egg_flow_box_get_activate_on_single_click (EggFlowBox        *box);
+void                  egg_flow_box_set_activate_on_single_click (EggFlowBox        *box,
+                                                                 gboolean           single);
+
+G_END_DECLS
+
+
+#endif /* __EGG_FLOW_BOX_H__ */
diff --git a/test-flow-box.c b/test-flow-box.c
new file mode 100644
index 0000000..ff7b16c
--- /dev/null
+++ b/test-flow-box.c
@@ -0,0 +1,514 @@
+/*
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * 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 <gtk/gtk.h>
+#include "egg-flow-box.h"
+
+enum {
+  SIMPLE_ITEMS = 0,
+  WRAPPY_ITEMS,
+  STOCK_ITEMS,
+  IMAGE_ITEMS
+};
+
+#define INITIAL_HALIGN          GTK_ALIGN_FILL
+#define INITIAL_VALIGN          GTK_ALIGN_START
+#define INITIAL_MINIMUM_LENGTH  3
+#define INITIAL_MAXIMUM_LENGTH  6
+#define INITIAL_CSPACING        2
+#define INITIAL_RSPACING        2
+#define N_ITEMS 4000
+
+static EggFlowBox    *the_flowbox       = NULL;
+static gint           items_type       = SIMPLE_ITEMS;
+static GtkOrientation text_orientation = GTK_ORIENTATION_HORIZONTAL;
+
+static void
+populate_flowbox_simple (EggFlowBox *flowbox)
+{
+  GtkWidget *widget, *frame;
+  gint i;
+
+  for (i = 0; i < N_ITEMS; i++)
+    {
+      gchar *text = g_strdup_printf ("Item %02d", i);
+
+      widget = gtk_label_new (text);
+      frame  = gtk_frame_new (NULL);
+      gtk_widget_show (widget);
+      gtk_widget_show (frame);
+
+      gtk_container_add (GTK_CONTAINER (frame), widget);
+
+      if (text_orientation == GTK_ORIENTATION_VERTICAL)
+        gtk_label_set_angle (GTK_LABEL (widget), 90);
+      g_object_set_data_full (G_OBJECT (frame), "id", (gpointer)g_strdup (text), g_free);
+      gtk_container_add (GTK_CONTAINER (flowbox), frame);
+
+      g_free (text);
+    }
+}
+
+static void
+populate_flowbox_wrappy (EggFlowBox *flowbox)
+{
+  GtkWidget *widget, *frame;
+  gint 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."
+  };
+
+  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);
+
+      if (text_orientation == GTK_ORIENTATION_VERTICAL)
+        gtk_label_set_angle (GTK_LABEL (widget), 90);
+
+      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);
+      g_object_set_data_full (G_OBJECT (frame), "id", (gpointer)g_strdup (strings[i]), g_free);
+
+      gtk_container_add (GTK_CONTAINER (flowbox), frame);
+    }
+}
+
+static void
+populate_flowbox_stock (EggFlowBox *flowbox)
+{
+  GtkWidget *widget;
+  static GSList *stock_ids = NULL;
+  GSList *l;
+  gint i;
+
+  if (!stock_ids)
+    stock_ids = gtk_stock_list_ids ();
+
+  for (i = 0, l = stock_ids; i < 30 && l != NULL; i++, l = l->next)
+    {
+      gchar *stock_id = l->data;
+      gchar *text = g_strdup_printf ("Item %02d", i);
+
+      widget = gtk_button_new_from_stock (stock_id);
+      gtk_widget_show (widget);
+
+      g_object_set_data_full (G_OBJECT (widget), "id", (gpointer)g_strdup (text), g_free);
+      gtk_container_add (GTK_CONTAINER (flowbox), widget);
+    }
+}
+
+static void
+populate_flowbox_images (EggFlowBox *flowbox)
+{
+  GtkWidget *widget, *image, *label;
+  gint i;
+
+  for (i = 0; i < N_ITEMS; i++)
+    {
+      gchar *text = g_strdup_printf ("Item %02d", i);
+
+      widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+      gtk_widget_set_hexpand (widget, TRUE);
+
+      image = gtk_image_new_from_icon_name ("face-sad", GTK_ICON_SIZE_DIALOG);
+      gtk_widget_set_hexpand (image, TRUE);
+      gtk_image_set_pixel_size (GTK_IMAGE (image), 256);
+
+      label = gtk_label_new (text);
+
+      gtk_container_add (GTK_CONTAINER (widget), image);
+      gtk_container_add (GTK_CONTAINER (widget), label);
+      gtk_widget_show_all (widget);
+
+      if (text_orientation == GTK_ORIENTATION_VERTICAL)
+        gtk_label_set_angle (GTK_LABEL (widget), 90);
+
+      g_object_set_data_full (G_OBJECT (widget), "id", (gpointer)g_strdup (text), g_free);
+      gtk_container_add (GTK_CONTAINER (flowbox), widget);
+
+      g_free (text);
+    }
+}
+
+static void
+populate_items (EggFlowBox *flowbox)
+{
+  GList *children, *l;
+
+  /* Remove all children first */
+  children = gtk_container_get_children (GTK_CONTAINER (flowbox));
+  for (l = children; l; l = l->next)
+    {
+      GtkWidget *child = l->data;
+
+      gtk_container_remove (GTK_CONTAINER (flowbox), child);
+    }
+  g_list_free (children);
+
+  if (items_type == SIMPLE_ITEMS)
+    populate_flowbox_simple (flowbox);
+  else if (items_type == WRAPPY_ITEMS)
+    populate_flowbox_wrappy (flowbox);
+  else if (items_type == STOCK_ITEMS)
+    populate_flowbox_stock (flowbox);
+  else if (items_type == IMAGE_ITEMS)
+    populate_flowbox_images (flowbox);
+}
+
+static void
+horizontal_alignment_changed (GtkComboBox   *box,
+                              EggFlowBox    *flowbox)
+{
+  GtkAlign alignment = gtk_combo_box_get_active (box);
+
+  egg_flow_box_set_halign_policy (flowbox, alignment);
+}
+
+static void
+vertical_alignment_changed (GtkComboBox   *box,
+                            EggFlowBox    *flowbox)
+{
+  GtkAlign alignment = gtk_combo_box_get_active (box);
+
+  egg_flow_box_set_valign_policy (flowbox, alignment);
+}
+
+static void
+orientation_changed (GtkComboBox   *box,
+                     EggFlowBox *flowbox)
+{
+  GtkOrientation orientation = gtk_combo_box_get_active (box);
+
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (flowbox), orientation);
+}
+
+static void
+line_length_changed (GtkSpinButton *spin,
+                     EggFlowBox *flowbox)
+{
+  gint length = gtk_spin_button_get_value_as_int (spin);
+
+  egg_flow_box_set_min_children_per_line (flowbox, length);
+}
+
+static void
+max_line_length_changed (GtkSpinButton *spin,
+                         EggFlowBox *flowbox)
+{
+  gint length = gtk_spin_button_get_value_as_int (spin);
+
+  egg_flow_box_set_max_children_per_line (flowbox, length);
+}
+
+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_flow_box_set_column_spacing (the_flowbox, state);
+  else
+    egg_flow_box_set_row_spacing (the_flowbox, state);
+}
+
+static void
+items_changed (GtkComboBox   *box,
+               EggFlowBox *flowbox)
+{
+  items_type = gtk_combo_box_get_active (box);
+
+  populate_items (flowbox);
+}
+
+static void
+text_orientation_changed (GtkComboBox   *box,
+                          EggFlowBox *flowbox)
+{
+  text_orientation = gtk_combo_box_get_active (box);
+
+  populate_items (flowbox);
+}
+
+static void
+homogeneous_toggled (GtkToggleButton *button,
+                     EggFlowBox      *flowbox)
+{
+  gboolean state = gtk_toggle_button_get_active (button);
+
+  egg_flow_box_set_homogeneous (flowbox, state);
+}
+
+static void
+on_child_activated (EggFlowBox *self,
+                    GtkWidget  *child)
+{
+  const char *id;
+  id = g_object_get_data (G_OBJECT (child), "id");
+  g_message ("Child activated %p: %s", child, id);
+}
+
+static GtkWidget *
+create_window (void)
+{
+  GtkWidget *window, *hbox, *vbox, *flowbox_cntl, *items_cntl;
+  GtkWidget *flowbox, *widget, *expander, *swindow;
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  hbox   = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+  vbox   = gtk_box_new (GTK_ORIENTATION_VERTICAL, 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);
+
+  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_box_pack_start (GTK_BOX (hbox), swindow, TRUE, TRUE, 0);
+
+  flowbox = egg_flow_box_new ();
+  the_flowbox = (EggFlowBox *)flowbox;
+  egg_flow_box_set_halign_policy (EGG_FLOW_BOX (flowbox), INITIAL_HALIGN);
+  egg_flow_box_set_valign_policy (EGG_FLOW_BOX (flowbox), INITIAL_VALIGN);
+  egg_flow_box_set_column_spacing (EGG_FLOW_BOX (flowbox), INITIAL_CSPACING);
+  egg_flow_box_set_row_spacing (EGG_FLOW_BOX (flowbox), INITIAL_RSPACING);
+  egg_flow_box_set_min_children_per_line (EGG_FLOW_BOX (flowbox), INITIAL_MINIMUM_LENGTH);
+  egg_flow_box_set_max_children_per_line (EGG_FLOW_BOX (flowbox), INITIAL_MAXIMUM_LENGTH);
+  gtk_widget_show (flowbox);
+  gtk_container_add (GTK_CONTAINER (swindow), flowbox);
+
+  g_signal_connect (flowbox, "child-activated", G_CALLBACK (on_child_activated), NULL);
+
+  /* Add Flowbox test control frame */
+  expander = gtk_expander_new ("Flow Box controls");
+  gtk_expander_set_expanded (GTK_EXPANDER (expander), TRUE);
+  flowbox_cntl = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+  gtk_widget_show (flowbox_cntl);
+  gtk_widget_show (expander);
+  gtk_container_add (GTK_CONTAINER (expander), flowbox_cntl);
+  gtk_box_pack_start (GTK_BOX (vbox), expander, FALSE, FALSE, 0);
+
+  widget = gtk_check_button_new_with_label ("Homogeneous");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), FALSE);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set whether the items should be displayed at the same size");
+  gtk_box_pack_start (GTK_BOX (flowbox_cntl), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "toggled",
+                    G_CALLBACK (homogeneous_toggled), flowbox);
+
+  /* Add alignment controls */
+  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 horizontal alignment policy");
+  gtk_box_pack_start (GTK_BOX (flowbox_cntl), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+                    G_CALLBACK (horizontal_alignment_changed), flowbox);
+
+  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_VALIGN);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set the vertical alignment policy");
+  gtk_box_pack_start (GTK_BOX (flowbox_cntl), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+                    G_CALLBACK (vertical_alignment_changed), flowbox);
+
+  /* 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), 0);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set the flowbox orientation");
+  gtk_box_pack_start (GTK_BOX (flowbox_cntl), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+                    G_CALLBACK (orientation_changed), flowbox);
+
+  /* Add minimum line length in items control */
+  widget = gtk_spin_button_new_with_range (1, 10, 1);
+  gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), INITIAL_MINIMUM_LENGTH);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set the minimum amount of items per line before wrapping");
+  gtk_box_pack_start (GTK_BOX (flowbox_cntl), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+                    G_CALLBACK (line_length_changed), flowbox);
+  g_signal_connect (G_OBJECT (widget), "value-changed",
+                    G_CALLBACK (line_length_changed), flowbox);
+
+  /* Add natural line length in items control */
+  widget = gtk_spin_button_new_with_range (1, 10, 1);
+  gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), INITIAL_MAXIMUM_LENGTH);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set the natural amount of items per line ");
+  gtk_box_pack_start (GTK_BOX (flowbox_cntl), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+                    G_CALLBACK (max_line_length_changed), flowbox);
+  g_signal_connect (G_OBJECT (widget), "value-changed",
+                    G_CALLBACK (max_line_length_changed), flowbox);
+
+  /* Add horizontal/vertical spacing controls */
+  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 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_CSPACING);
+  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 (flowbox_cntl), hbox, FALSE, FALSE, 0);
+
+  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 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_RSPACING);
+  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 (flowbox_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_box_new (GTK_ORIENTATION_VERTICAL, 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 Items control */
+  widget = gtk_combo_box_text_new ();
+  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Simple");
+  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Wrappy");
+  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Stock");
+  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), "Images");
+  gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set the item set to use");
+  gtk_box_pack_start (GTK_BOX (items_cntl), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+                    G_CALLBACK (items_changed), flowbox);
+
+
+  /* Add Text 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), 0);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set the item's text orientation (cant be done for stock buttons)");
+  gtk_box_pack_start (GTK_BOX (items_cntl), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+                    G_CALLBACK (text_orientation_changed), flowbox);
+
+  populate_items (EGG_FLOW_BOX (flowbox));
+
+  /* This line was added only for the convenience of reproducing
+   * a height-for-width inside GtkScrolledWindow bug (bug 629778).
+   *   -Tristan
+   */
+  gtk_window_set_default_size (GTK_WINDOW (window), 390, -1);
+
+  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]