[gtk+] Added initial revision of GtkWrapBox container widget and test case.



commit deaa351630d73c04d22007ff8ca7f1f7a46bbe06
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date:   Sat Aug 28 15:59:23 2010 +0900

    Added initial revision of GtkWrapBox container widget and test case.

 gtk/Makefile.am     |    4 +-
 gtk/gtk.h           |    1 +
 gtk/gtk.symbols     |   23 +
 gtk/gtkenums.h      |   46 +
 gtk/gtkwrapbox.c    | 2403 +++++++++++++++++++++++++++++++++++++++++++++++++++
 gtk/gtkwrapbox.h    |  101 +++
 tests/Makefile.am   |    8 +-
 tests/testwrapbox.c |  547 ++++++++++++
 8 files changed, 3131 insertions(+), 2 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 6a791e0..8fdc747 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -335,7 +335,8 @@ gtk_public_h_sources =          \
 	gtkvscrollbar.h		\
 	gtkvseparator.h		\
 	gtkwidget.h		\
-	gtkwindow.h
+	gtkwindow.h		\
+	gtkwrapbox.h
 
 if OS_UNIX
 gtk_unix_print_public_h_sources =    \
@@ -619,6 +620,7 @@ gtk_base_c_sources =            \
 	gtkwidget.c		\
 	gtkwindow-decorate.c    \
 	gtkwindow.c		\
+	gtkwrapbox.c		\
 	$(gtk_clipboard_dnd_c_sources)
 
 gtk_c_sources = $(gtk_base_c_sources)
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 1ffec2f..48f1c29 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -218,6 +218,7 @@
 #include <gtk/gtkvseparator.h>
 #include <gtk/gtkwidget.h>
 #include <gtk/gtkwindow.h>
+#include <gtk/gtkwrapbox.h>
 
 #undef __GTK_H_INSIDE__
 
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index 44e0b0a..01ed52b 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -4629,6 +4629,29 @@ gtk_window_unstick
 #endif
 #endif
 
+
+#if IN_HEADER(__GTK_WRAP_BOX_H__)
+#if IN_FILE(__GTK_WRAP_BOX_C__)
+gtk_wrap_box_get_allocation_mode
+gtk_wrap_box_get_horizontal_spacing
+gtk_wrap_box_get_minimum_line_children
+gtk_wrap_box_get_natural_line_children
+gtk_wrap_box_get_spreading
+gtk_wrap_box_get_type G_GNUC_CONST
+gtk_wrap_box_get_vertical_spacing
+gtk_wrap_box_insert_child
+gtk_wrap_box_new
+gtk_wrap_box_reorder_child
+gtk_wrap_box_set_allocation_mode
+gtk_wrap_box_set_horizontal_spacing
+gtk_wrap_box_set_minimum_line_children
+gtk_wrap_box_set_natural_line_children
+gtk_wrap_box_set_spreading
+gtk_wrap_box_set_vertical_spacing
+#endif
+#endif
+
+
 #if IN_HEADER(__GTK_WIN32_EMBED_WIDGET_H__)
 #if IN_FILE(__GTK_WIN32_EMBED_WIDGET_C__)
 #ifdef G_OS_WIN32
diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h
index fd02139..f2c931e 100644
--- a/gtk/gtkenums.h
+++ b/gtk/gtkenums.h
@@ -560,6 +560,52 @@ typedef enum
 } GtkSizeRequestMode;
 
 
+/**
+ * GtkWrapAllocationMode:
+ * @GTK_WRAP_ALLOCATE_FREE:        Items wrap freely in the box's orientation
+ * @GTK_WRAP_ALLOCATE_ALIGNED:     Items are aligned into rows and columns
+ * @GTK_WRAP_ALLOCATE_HOMOGENEOUS: Items are all allocated the same size
+ *
+ * Describes how an #GtkWrapBox positions its children.
+ */
+typedef enum {
+  GTK_WRAP_ALLOCATE_FREE = 0,
+  GTK_WRAP_ALLOCATE_ALIGNED,
+  GTK_WRAP_ALLOCATE_HOMOGENEOUS
+} GtkWrapAllocationMode;
+
+/**
+ * GtkWrapBoxSpreading:
+ * @GTK_WRAP_BOX_SPREAD_BEGIN:  Items are allocated no more than their natural size
+ *                              in the layout's orientation and any extra space is left trailing at 
+ *                              the end of each line.
+ * @GTK_WRAP_BOX_SPREAD_END:    Items are allocated no more than their natural size
+ *                              in the layout's orientation and any extra space skipped at the beginning
+ *                              of each line.
+ * @GTK_WRAP_BOX_SPREAD_EVEN:   Items are allocated no more than their natural size
+ *                              in the layout's orientation and any extra space is evenly distributed
+ *                              between children.
+ * @GTK_WRAP_BOX_SPREAD_EXPAND: Items share the extra space evenly (or among children that 'expand' when
+ *                              in %GTK_WRAP_ALLOCATE_FREE mode.
+ *
+ * Describes how an #GtkWrapBox deals with extra space when allocating children.
+ *
+ * The box always tries to fit as many children at their natural size 
+ * in the given orentation as possible with the exception of fitting "minimum-line-children"
+ * items into the available size. When the available size is larger than
+ * the size needed to fit a given number of children at their natural size
+ * then extra space is available to distribute among children. The
+ * #GtkWrapBoxSpreading option describes what to do with this space.
+ *
+ */
+typedef enum {
+  GTK_WRAP_BOX_SPREAD_BEGIN = 0,
+  GTK_WRAP_BOX_SPREAD_END,
+  GTK_WRAP_BOX_SPREAD_EVEN,
+  GTK_WRAP_BOX_SPREAD_EXPAND
+} GtkWrapBoxSpreading;
+
+
 G_END_DECLS
 
 #endif /* __GTK_ENUMS_H__ */
diff --git a/gtk/gtkwrapbox.c b/gtk/gtkwrapbox.c
new file mode 100644
index 0000000..eba306f
--- /dev/null
+++ b/gtk/gtkwrapbox.c
@@ -0,0 +1,2403 @@
+/* gtkwrapbox.c
+ * Copyright (C) 2007-2010 Openismus GmbH
+ *
+ * Authors:
+ *      Tristan Van Berkom <tristanvb openismus com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+/**
+ * SECTION:gtkwrapbox
+ * @Short_Description: A container that wraps its children;
+ * @Title: GtkWrapBox
+ *
+ * #GtkWrapBox allocates space for an ordered list of children
+ * by wrapping them over in the box's orentation.
+ * 
+ */
+
+#include "config.h"
+#include "gtksizerequest.h"
+#include "gtkorientable.h"
+#include "gtkwrapbox.h"
+#include "gtkintl.h"
+
+
+typedef struct _GtkWrapBoxChild  GtkWrapBoxChild;
+
+enum {
+  PROP_0,
+  PROP_ORIENTATION,
+  PROP_ALLOCATION_MODE,
+  PROP_SPREADING,
+  PROP_HORIZONTAL_SPACING,
+  PROP_VERTICAL_SPACING,
+  PROP_MINIMUM_LINE_CHILDREN,
+  PROP_NATURAL_LINE_CHILDREN
+};
+
+enum {
+  CHILD_PROP_0,
+  CHILD_PROP_X_EXPAND,
+  CHILD_PROP_X_FILL,
+  CHILD_PROP_Y_EXPAND,
+  CHILD_PROP_Y_FILL,
+  CHILD_PROP_X_PADDING,
+  CHILD_PROP_Y_PADDING
+};
+
+struct _GtkWrapBoxPriv {
+  GtkOrientation        orientation;
+  GtkWrapAllocationMode mode;
+  GtkWrapBoxSpreading   spreading;
+
+  guint16               vertical_spacing;
+  guint16               horizontal_spacing;
+
+  guint16               minimum_line_children;
+  guint16               natural_line_children;
+
+  GList                *children;
+};
+
+struct _GtkWrapBoxChild {
+  GtkWidget *widget;
+
+  guint16    xpadding;
+  guint16    ypadding;
+
+  guint16    xexpand : 1;
+  guint16    xfill   : 1;
+  guint16    yexpand : 1;
+  guint16    yfill   : 1;
+};
+
+/* GObjectClass */
+static void gtk_wrap_box_get_property         (GObject             *object,
+					       guint                prop_id,
+					       GValue              *value,
+					       GParamSpec          *pspec);
+static void gtk_wrap_box_set_property         (GObject             *object,
+					       guint                prop_id,
+					       const GValue        *value,
+					       GParamSpec          *pspec);
+
+/* GtkWidgetClass */
+static void gtk_wrap_box_size_allocate        (GtkWidget	   *widget,
+					       GtkAllocation       *allocation);
+
+/* GtkContainerClass */
+static void gtk_wrap_box_add                  (GtkContainer        *container,
+					       GtkWidget	   *widget);
+static void gtk_wrap_box_remove               (GtkContainer        *container,
+					       GtkWidget           *widget);
+static void gtk_wrap_box_forall               (GtkContainer        *container,
+					       gboolean	            include_internals,
+					       GtkCallback          callback,
+					       gpointer	            callback_data);
+static void gtk_wrap_box_set_child_property   (GtkContainer        *container,
+					       GtkWidget           *child,
+					       guint                property_id,
+					       const GValue        *value,
+					       GParamSpec          *pspec);
+static void gtk_wrap_box_get_child_property   (GtkContainer        *container,
+					       GtkWidget           *child,
+					       guint                property_id,
+					       GValue              *value,
+					       GParamSpec          *pspec);
+static GType gtk_wrap_box_child_type          (GtkContainer        *container);
+
+
+/* GtkSizeRequest */
+static void gtk_wrap_box_size_request_init    (GtkSizeRequestIface *iface);
+static GtkSizeRequestMode gtk_wrap_box_get_request_mode (GtkSizeRequest *widget);
+static void gtk_wrap_box_get_width            (GtkSizeRequest      *widget,
+					       gint                *minimum_size,
+					       gint                *natural_size);
+static void gtk_wrap_box_get_height           (GtkSizeRequest      *widget,
+					       gint                *minimum_size,
+					       gint                *natural_size);
+static void gtk_wrap_box_get_height_for_width (GtkSizeRequest      *box,
+					       gint                 width,
+					       gint                *minimum_height,
+					       gint                *natural_height);
+static void gtk_wrap_box_get_width_for_height (GtkSizeRequest      *box,
+					       gint                 width,
+					       gint                *minimum_height,
+					       gint                *natural_height);
+
+
+G_DEFINE_TYPE_WITH_CODE (GtkWrapBox, gtk_wrap_box, GTK_TYPE_CONTAINER,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_SIZE_REQUEST,
+                                                gtk_wrap_box_size_request_init)
+			 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
+
+
+static void
+gtk_wrap_box_class_init (GtkWrapBoxClass *class)
+{
+  GObjectClass      *gobject_class    = G_OBJECT_CLASS (class);
+  GtkWidgetClass    *widget_class     = GTK_WIDGET_CLASS (class);
+  GtkContainerClass *container_class  = GTK_CONTAINER_CLASS (class);
+  
+  gobject_class->get_property         = gtk_wrap_box_get_property;
+  gobject_class->set_property         = gtk_wrap_box_set_property;
+  
+  widget_class->size_allocate         = gtk_wrap_box_size_allocate;
+  
+  container_class->add                = gtk_wrap_box_add;
+  container_class->remove             = gtk_wrap_box_remove;
+  container_class->forall             = gtk_wrap_box_forall;
+  container_class->child_type         = gtk_wrap_box_child_type;
+  container_class->set_child_property = gtk_wrap_box_set_child_property;
+  container_class->get_child_property = gtk_wrap_box_get_child_property;
+
+  /* GObjectClass properties */
+  g_object_class_override_property (gobject_class, PROP_ORIENTATION, "orientation");
+
+  /**
+   * GtkWrapBox:allocation-mode:
+   *
+   * The #GtkWrapAllocationMode to use.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_ALLOCATION_MODE,
+                                   g_param_spec_enum ("allocation-mode",
+						      P_("Allocation Mode"),
+						      P_("The allocation mode to use"),
+						      GTK_TYPE_WRAP_ALLOCATION_MODE,
+						      GTK_WRAP_ALLOCATE_FREE,
+						      G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+  /**
+   * GtkWrapBox:spreading:
+   *
+   * The #GtkWrapBoxSpreading to used to define what is done with extra
+   * space.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_SPREADING,
+                                   g_param_spec_enum ("spreading",
+						      P_("Spreading"),
+						      P_("The spreading mode to use"),
+						      GTK_TYPE_WRAP_BOX_SPREADING,
+						      GTK_WRAP_BOX_SPREAD_BEGIN,
+						      G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+
+  /**
+   * GtkWrapBox:minimum-line-children:
+   *
+   * 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 (gobject_class,
+                                   PROP_MINIMUM_LINE_CHILDREN,
+                                   g_param_spec_uint ("minimum-line-children",
+						      P_("Minimum Line Children"),
+						      P_("The minimum number of children to allocate "
+							"consecutively in the given orientation."),
+						      0,
+						      65535,
+						      0,
+						      G_PARAM_READABLE | G_PARAM_WRITABLE));
+  
+
+  /**
+   * GtkWrapBox:natural-line-children:
+   *
+   * The maximum amount of children to request space for consecutively in the given orientation.
+   *
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_NATURAL_LINE_CHILDREN,
+                                   g_param_spec_uint ("natural-line-children",
+						      P_("Natural Line Children"),
+						      P_("The maximum amount of children to request space for "
+							"consecutively in the given orientation."),
+						      0,
+						      65535,
+						      0,
+						      G_PARAM_READABLE | G_PARAM_WRITABLE));
+  
+  /**
+   * GtkWrapBox:vertical-spacing:
+   *
+   * The amount of vertical space between two children.
+   *
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_VERTICAL_SPACING,
+                                   g_param_spec_uint ("vertical-spacing",
+						     P_("Vertical spacing"),
+						     P_("The amount of vertical space between two children"),
+						     0,
+						     65535,
+						     0,
+						     G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+  /**
+   * GtkWrapBox:horizontal-spacing:
+   *
+   * The amount of horizontal space between two children.
+   *
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_HORIZONTAL_SPACING,
+                                   g_param_spec_uint ("horizontal-spacing",
+						     P_("Horizontal spacing"),
+						     P_("The amount of horizontal space between two children"),
+						     0,
+						     65535,
+						     0,
+						     G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+  /* GtkContainerClass child properties */
+
+  /**
+   * GtkWrapBox:x-expand:
+   *
+   * Whether the child expands horizontally.
+   *
+   */
+  gtk_container_class_install_child_property (container_class,
+					      CHILD_PROP_X_EXPAND,
+					      g_param_spec_boolean 
+					      ("x-expand", 
+					       P_("Horizontally expand"), 
+					       P_("Whether the child expands horizontally."),
+					       FALSE,
+					       G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+  /**
+   * GtkWrapBox:x-fill:
+   *
+   * Whether the child fills its allocated horizontal space.
+   *
+   */
+  gtk_container_class_install_child_property (container_class,
+					      CHILD_PROP_X_FILL,
+					      g_param_spec_boolean 
+					      ("x-fill", 
+					       P_("Horizontally fills"), 
+					       P_("Whether the child fills its allocated horizontal space."),
+					       FALSE,
+					       G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+  /**
+   * GtkWrapBox:y-expand:
+   *
+   * Whether the child expands vertically.
+   *
+   */
+  gtk_container_class_install_child_property (container_class,
+					      CHILD_PROP_Y_EXPAND,
+					      g_param_spec_boolean 
+					      ("y-expand", 
+					       P_("Vertically expand"), 
+					       P_("Whether the child expands vertically."),
+					       FALSE,
+					       G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+  /**
+   * GtkWrapBox:y-fill:
+   *
+   * Whether the child fills its allocated vertical space.
+   *
+   */
+  gtk_container_class_install_child_property (container_class,
+					      CHILD_PROP_Y_FILL,
+					      g_param_spec_boolean 
+					      ("y-fill", 
+					       P_("Vertically fills"), 
+					       P_("Whether the child fills its allocated vertical space."),
+					       FALSE,
+					       G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+  /**
+   * GtkWrapBox:x-padding:
+   *
+   * Extra space to put between the child and its left and right neighbors, in pixels.
+   *
+   */
+  gtk_container_class_install_child_property (container_class,
+					      CHILD_PROP_X_PADDING,
+					      g_param_spec_uint 
+					      ("x-padding", 
+					       P_("Horizontal padding"), 
+					       P_("Extra space to put between the child and "
+						  "its left and right neighbors, in pixels"),
+					       0, 65535, 0,
+					       G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+
+  /**
+   * GtkWrapBox:y-padding:
+   *
+   * Extra space to put between the child and its upper and lower neighbors, in pixels.
+   *
+   */
+  gtk_container_class_install_child_property (container_class,
+					      CHILD_PROP_Y_PADDING,
+					      g_param_spec_uint 
+					      ("y-padding", 
+					       P_("Vertical padding"), 
+					       P_("Extra space to put between the child and "
+						  "its upper and lower neighbors, in pixels"),
+					       0, 65535, 0,
+					       G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+  g_type_class_add_private (class, sizeof (GtkWrapBoxPriv));
+}
+
+static void
+gtk_wrap_box_init (GtkWrapBox *box)
+{
+  GtkWrapBoxPriv *priv; 
+  
+  box->priv = priv = 
+    G_TYPE_INSTANCE_GET_PRIVATE (box, GTK_TYPE_WRAP_BOX, GtkWrapBoxPriv);
+
+  priv->orientation        = GTK_ORIENTATION_HORIZONTAL;
+  priv->mode               = GTK_WRAP_ALLOCATE_FREE;
+  priv->spreading          = GTK_WRAP_BOX_SPREAD_BEGIN;
+  priv->vertical_spacing   = 0;
+  priv->horizontal_spacing = 0;
+  priv->children           = NULL;
+
+  gtk_widget_set_has_window (GTK_WIDGET (box), FALSE);
+}
+
+/*****************************************************
+ *                  GObectClass                      * 
+ *****************************************************/
+static void
+gtk_wrap_box_get_property (GObject      *object,
+			   guint         prop_id,
+			   GValue       *value,
+			   GParamSpec   *pspec)
+{
+  GtkWrapBox     *box  = GTK_WRAP_BOX (object);
+  GtkWrapBoxPriv *priv = box->priv;
+
+  switch (prop_id)
+    {
+    case PROP_ORIENTATION:
+      g_value_set_boolean (value, priv->orientation);
+      break;
+    case PROP_ALLOCATION_MODE:
+      g_value_set_enum (value, priv->mode);
+      break;
+    case PROP_SPREADING:
+      g_value_set_enum (value, priv->spreading);
+      break;
+    case PROP_VERTICAL_SPACING:
+      g_value_set_uint (value, priv->vertical_spacing);
+      break;
+    case PROP_HORIZONTAL_SPACING:
+      g_value_set_uint (value, priv->horizontal_spacing);
+      break;
+    case PROP_MINIMUM_LINE_CHILDREN:
+      g_value_set_uint (value, priv->minimum_line_children);
+      break;
+    case PROP_NATURAL_LINE_CHILDREN:
+      g_value_set_uint (value, priv->natural_line_children);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_wrap_box_set_property (GObject      *object,
+			   guint         prop_id,
+			   const GValue *value,
+			   GParamSpec   *pspec)
+{
+  GtkWrapBox     *box = GTK_WRAP_BOX (object);
+  GtkWrapBoxPriv *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_ALLOCATION_MODE:
+      gtk_wrap_box_set_allocation_mode (box, g_value_get_enum (value));
+      break;
+    case PROP_SPREADING:
+      gtk_wrap_box_set_spreading (box, g_value_get_enum (value));
+      break;
+    case PROP_VERTICAL_SPACING:
+      gtk_wrap_box_set_vertical_spacing (box, g_value_get_uint (value));
+      break;
+    case PROP_HORIZONTAL_SPACING:
+      gtk_wrap_box_set_horizontal_spacing (box, g_value_get_uint (value));
+      break;
+    case PROP_MINIMUM_LINE_CHILDREN:
+      gtk_wrap_box_set_minimum_line_children (box, g_value_get_uint (value));
+      break;
+    case PROP_NATURAL_LINE_CHILDREN:
+      gtk_wrap_box_set_natural_line_children (box, g_value_get_uint (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+/*****************************************************
+ *                 GtkWidgetClass                    * 
+ *****************************************************/
+
+static gint
+get_visible_children (GtkWrapBox  *box)
+{
+  GtkWrapBoxPriv  *priv = box->priv;
+  GList           *list;
+  gint             i = 0;
+
+  for (list = priv->children; list; list = list->next)
+    {
+      GtkWrapBoxChild *child = list->data;
+
+      if (!gtk_widget_get_visible (child->widget))
+	continue;
+
+      i++;
+    }
+
+  return i;
+}
+
+static gint
+get_visible_expand_children (GtkWrapBox     *box,
+			     GtkOrientation  orientation,
+			     GList          *cursor,
+			     gint            n_visible)
+{
+  GList *list;
+  gint   i, expand_children = 0;
+
+  for (i = 0, list = cursor; (n_visible > 0 ? i < n_visible : TRUE) && list; list = list->next)
+    {
+      GtkWrapBoxChild *child = list->data;
+
+      if (!gtk_widget_get_visible (child->widget))
+	continue;
+
+      if ((orientation == GTK_ORIENTATION_HORIZONTAL && child->xexpand) ||
+	  (orientation == GTK_ORIENTATION_VERTICAL   && child->yexpand))
+	expand_children++;
+
+      i++;
+    }
+
+  return expand_children;
+}
+
+/* Used in columned modes where all items share at least their
+ * equal widths or heights
+ */
+static void
+get_average_item_size (GtkWrapBox      *box,
+		       GtkOrientation   orientation,
+		       gint            *min_size,
+		       gint            *nat_size)
+{
+  GtkWrapBoxPriv  *priv = box->priv;
+  GList              *list;
+  gint                max_min_size = 0;
+  gint                max_nat_size = 0;
+
+  for (list = priv->children; list; list = list->next)
+    {
+      GtkWrapBoxChild *child = list->data;
+      gint             child_min, child_nat;
+
+      if (!gtk_widget_get_visible (child->widget))
+	continue;
+
+      if (orientation == GTK_ORIENTATION_HORIZONTAL)
+	{
+	  gtk_size_request_get_width (GTK_SIZE_REQUEST (child->widget),
+				      &child_min, &child_nat);
+
+	  child_min += child->xpadding * 2;
+	  child_nat += child->xpadding * 2;
+	}
+      else
+	{
+	  gtk_size_request_get_height (GTK_SIZE_REQUEST (child->widget),
+				       &child_min, &child_nat);
+
+	  child_min += child->ypadding * 2;
+	  child_nat += child->ypadding * 2;
+	}
+      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 (GtkWrapBox         *box,
+					   GtkOrientation      orientation,
+					   gint                item_size,
+					   gint               *min_item_size, 
+					   gint               *nat_item_size)
+{
+  GtkWrapBoxPriv *priv = box->priv;
+  GList          *list;
+  gint            max_min_size = 0;
+  gint            max_nat_size = 0;
+
+  for (list = priv->children; list; list = list->next)
+    {
+      GtkWrapBoxChild *child = list->data;
+      gint             child_min, child_nat;
+
+      if (!gtk_widget_get_visible (child->widget))
+	continue;
+
+      if (orientation == GTK_ORIENTATION_HORIZONTAL)
+	{
+	  gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (child->widget), 
+						 item_size - child->xpadding * 2,
+						 &child_min, &child_nat);
+	  child_min += child->ypadding * 2;
+	  child_nat += child->ypadding * 2;
+	}
+      else 
+	{
+	  gtk_size_request_get_width_for_height (GTK_SIZE_REQUEST (child->widget), 
+						 item_size - child->ypadding * 2,
+						 &child_min, &child_nat);
+	  child_min += child->xpadding * 2;
+	  child_nat += child->xpadding * 2;
+	}
+
+      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) */
+GList *
+get_largest_size_for_line_in_opposing_orientation (GtkWrapBox      *box,
+						   GtkOrientation   orientation,
+						   GList           *cursor,
+						   gint             line_length,
+						   gint             item_size,
+						   gint             extra_pixels,
+						   gint            *min_item_size, 
+						   gint            *nat_item_size)
+{
+  GtkWrapBoxPriv *priv   = box->priv;
+  GList          *list;
+  gint            max_min_size = 0;
+  gint            max_nat_size = 0;
+  gint            i;
+
+  for (list = cursor, i = 0; list && i < line_length; list = list->next)
+    {
+      GtkWrapBoxChild *child = list->data;
+      gint             child_min, child_nat, this_item_size;
+
+      if (!gtk_widget_get_visible (child->widget))
+	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_size;
+      if (extra_pixels > 0 && 
+	  priv->spreading == GTK_WRAP_BOX_SPREAD_EXPAND)
+	{
+	  this_item_size++;
+	  extra_pixels--;
+	}
+
+      if (orientation == GTK_ORIENTATION_HORIZONTAL)
+	{
+	  gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (child->widget), 
+						 this_item_size - child->xpadding * 2,
+						 &child_min, &child_nat);
+	  child_min += child->ypadding * 2;
+	  child_nat += child->ypadding * 2;
+	}
+      else 
+	{
+	  gtk_size_request_get_width_for_height (GTK_SIZE_REQUEST (child->widget), 
+						 this_item_size - child->ypadding * 2,
+						 &child_min, &child_nat);
+	  child_min += child->xpadding * 2;
+	  child_nat += child->xpadding * 2;
+	}
+
+      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 list;
+}
+
+
+/* Gets the largest minimum/natural size on a single line for a given allocated line size
+ * (used to get the largest line heights for a width in pixels and the opposite
+ * while itterating over a list of children, note the new index is returned) */
+GList *
+get_largest_size_for_free_line_in_opposing_orientation (GtkWrapBox      *box,
+							GtkOrientation   orientation,
+							GList           *cursor,
+							gint             min_items,
+							gint             avail_size,
+							gint            *min_item_size, 
+							gint            *nat_item_size,
+							gint            *extra_pixels,
+							GArray         **ret_array)
+{
+  GtkWrapBoxPriv     *priv = box->priv;
+  GtkRequestedSize   *sizes;
+  GList              *list;
+  GArray             *array;
+  gint                max_min_size = 0;
+  gint                max_nat_size = 0;
+  gint                i, size = avail_size;
+  gint                line_length, spacing;
+  gint                expand_children = 0;
+  gint                expand_per_child;
+  gint                expand_remainder;
+
+  if (orientation == GTK_ORIENTATION_HORIZONTAL)
+    spacing = priv->horizontal_spacing;
+  else
+    spacing = priv->vertical_spacing;
+
+  /* First determine the length of this line in items (how many items fit) */
+  for (i = 0, list = cursor; size > 0 && list; list = list->next)
+    {
+      GtkWrapBoxChild *child = list->data;
+      gint             child_size;
+
+      if (!gtk_widget_get_visible (child->widget))
+	continue;
+
+      if (orientation == GTK_ORIENTATION_HORIZONTAL)
+	{
+	  gtk_size_request_get_width (GTK_SIZE_REQUEST (child->widget), 
+				      NULL, &child_size);
+	  child_size += child->xpadding * 2;
+	}
+      else 
+	{
+	  gtk_size_request_get_height (GTK_SIZE_REQUEST (child->widget), 
+				       NULL, &child_size);
+	  child_size += child->ypadding * 2;
+	}
+
+      if (i > 0)
+	child_size += spacing;
+
+      if (size - child_size >= 0)
+	size -= child_size;
+      else 
+	break;
+
+      i++;
+    }
+
+  line_length = MAX (min_items, i);
+  size        = avail_size;
+
+  /* Collect the sizes of the items on this line */
+  array = g_array_new (0, TRUE, sizeof (GtkRequestedSize));
+
+  for (i = 0, list = cursor; i < line_length && list; list = list->next)
+    {
+      GtkWrapBoxChild  *child = list->data;
+      GtkRequestedSize  requested;
+
+      if (!gtk_widget_get_visible (child->widget))
+	continue;
+
+      requested.data = child;
+      if (orientation == GTK_ORIENTATION_HORIZONTAL)
+	{
+	  gtk_size_request_get_width (GTK_SIZE_REQUEST (child->widget), 
+				      &requested.minimum_size,
+				      &requested.natural_size);
+
+	  size -= child->xpadding * 2;
+	}
+      else
+	{
+	  gtk_size_request_get_height (GTK_SIZE_REQUEST (child->widget), 
+				       &requested.minimum_size,
+				       &requested.natural_size);
+
+	  size -= child->ypadding * 2;
+	}
+
+      if (i > 0)
+	size -= spacing;
+
+      size -= requested.minimum_size;
+
+      g_array_append_val (array, requested);
+
+      i++;
+    }
+
+  sizes = (GtkRequestedSize *)array->data;
+  size  = gtk_distribute_natural_allocation (size, array->len, sizes);
+
+  if (extra_pixels)
+    *extra_pixels = size;
+
+  /* Cut out any expand space if we're not distributing any */
+  if (priv->spreading != GTK_WRAP_BOX_SPREAD_EXPAND)
+    size = 0;
+
+  /* Count how many children are going to expand... */
+  expand_children = get_visible_expand_children (box, orientation,
+						 cursor, line_length);
+
+  /* If no child prefers to expand, they all get some expand space */
+  if (expand_children == 0)
+    {
+      expand_per_child = size / line_length;
+      expand_remainder = size % line_length;
+    }
+  else
+    {
+      expand_per_child = size / expand_children;
+      expand_remainder = size % expand_children;
+    }
+
+  /* Now add the remaining expand space and get the collective size of this line
+   * in the opposing orientation */
+  for (i = 0, list = cursor; i < line_length && list; list = list->next)
+    {
+      GtkWrapBoxChild *child = list->data;
+      gint child_min, child_nat;
+
+      if (!gtk_widget_get_visible (child->widget))
+	continue;
+
+      g_assert (child == sizes[i].data);
+
+      if ((orientation == GTK_ORIENTATION_HORIZONTAL && child->xexpand) ||
+	  (orientation == GTK_ORIENTATION_VERTICAL   && child->yexpand) ||
+	  expand_children == 0)
+	{
+	  sizes[i].minimum_size += expand_per_child;
+	  if (expand_remainder)
+	    {
+	      sizes[i].minimum_size++;
+	      expand_remainder--;
+	    }
+	}
+
+      if (orientation == GTK_ORIENTATION_HORIZONTAL)
+	{
+	  gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (child->widget),
+						 sizes[i].minimum_size,
+						 &child_min, &child_nat);
+	  child_min += child->ypadding * 2;
+	  child_nat += child->ypadding * 2;
+	}
+      else
+	{
+	  gtk_size_request_get_width_for_height (GTK_SIZE_REQUEST (child->widget),
+						 sizes[i].minimum_size,
+						 &child_min, &child_nat);
+	  child_min += child->xpadding * 2;
+	  child_nat += child->xpadding * 2;
+	}
+
+      max_min_size = MAX (max_min_size, child_min);
+      max_nat_size = MAX (max_nat_size, child_nat);
+
+      i++;
+    }
+
+  if (ret_array)
+    *ret_array = array;
+  else
+    g_array_free (array, TRUE);
+
+  if (min_item_size)
+    *min_item_size = max_min_size;
+
+  if (nat_item_size)
+    *nat_item_size = max_nat_size;
+
+  /* Return the next item */
+  return list;
+}
+
+static void
+allocate_child (GtkWrapBox      *box, 
+		GtkWrapBoxChild *child, 
+		gint             item_offset, 
+		gint             line_offset, 
+		gint             item_size, 
+		gint             line_size)
+{
+  GtkWrapBoxPriv     *priv   = box->priv;
+  GtkAllocation       widget_allocation;
+  GtkAllocation       child_allocation;
+  GtkSizeRequestMode  request_mode;
+
+  gtk_widget_get_allocation (GTK_WIDGET (box), &widget_allocation);
+
+  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      child_allocation.x      = widget_allocation.x + item_offset + child->xpadding;
+      child_allocation.y      = widget_allocation.y + line_offset + child->ypadding;
+      child_allocation.width  = item_size - child->xpadding * 2;
+      child_allocation.height = line_size - child->ypadding * 2;
+    } 
+  else /* GTK_ORIENTATION_VERTICAL */
+    {
+      child_allocation.x      = widget_allocation.x + line_offset + child->xpadding;
+      child_allocation.y      = widget_allocation.y + item_offset + child->ypadding;
+      child_allocation.width  = line_size - child->xpadding * 2;
+      child_allocation.height = item_size - child->ypadding * 2;
+    }
+
+  request_mode = gtk_size_request_get_request_mode (GTK_SIZE_REQUEST (child->widget));
+  if (!child->xfill)
+    {
+      gint width, height;
+
+      if (!child->yfill && request_mode == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
+	{
+	  gtk_size_request_get_height (GTK_SIZE_REQUEST (child->widget), NULL, &height);
+
+	  height = MIN (child_allocation.height, height);
+	}
+      else
+	height = child_allocation.height;
+      
+      if (request_mode == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
+	gtk_size_request_get_width_for_height (GTK_SIZE_REQUEST (child->widget), 
+					       height, NULL, &width);
+      else
+	gtk_size_request_get_width (GTK_SIZE_REQUEST (child->widget), NULL, &width);
+
+      width = MIN (child_allocation.width, width);
+      child_allocation.x     = child_allocation.x + (child_allocation.width - width) / 2;
+      child_allocation.width = width;
+    }
+
+  if (!child->yfill)
+    {
+      gint height;
+      
+      /* Note here child_allocation.width is already changed if (!child->xfill) */
+      if (request_mode == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
+	gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (child->widget), 
+					       child_allocation.width, NULL, &height);
+      else
+	gtk_size_request_get_height (GTK_SIZE_REQUEST (child->widget), NULL, &height);
+
+      height = MIN (child_allocation.height, height);
+      child_allocation.y      = child_allocation.y + (child_allocation.height - height) / 2;
+      child_allocation.height = height;
+    }
+
+  gtk_widget_size_allocate (child->widget, &child_allocation);
+}
+
+
+typedef struct {
+  GArray *requested;
+  gint    extra_pixels;
+} AllocatedLine;
+
+static void
+gtk_wrap_box_size_allocate (GtkWidget     *widget,
+			    GtkAllocation *allocation)
+{
+  GtkWrapBox         *box  = GTK_WRAP_BOX (widget);
+  GtkWrapBoxPriv     *priv = box->priv;
+  GtkRequestedSize   *sizes = NULL;
+  GArray             *array;
+  guint               border_width;
+  gint                avail_size, avail_other_size, min_items, item_spacing, line_spacing;
+
+  gtk_widget_set_allocation (widget, allocation);
+
+  border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
+  min_items    = MAX (1, priv->minimum_line_children);
+
+  /* Collect the line sizes for GTK_WRAP_ALLOCATE_FREE and 
+   * GTK_WRAP_ALLOCATE_ALIGNED modes */
+  array = g_array_new (0, TRUE, sizeof (GtkRequestedSize));
+
+  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      avail_size       = allocation->width  - border_width * 2;
+      avail_other_size = allocation->height - border_width * 2;
+      item_spacing     = priv->horizontal_spacing;
+      line_spacing     = priv->vertical_spacing;
+    }
+  else /* GTK_ORIENTATION_VERTICAL */
+    {
+      avail_size       = allocation->height - border_width * 2;
+      avail_other_size = allocation->width  - border_width * 2;
+      item_spacing     = priv->vertical_spacing;
+      line_spacing     = priv->horizontal_spacing;
+    }
+
+
+  if (priv->mode == GTK_WRAP_ALLOCATE_ALIGNED ||
+      priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
+    {
+      GList *list;
+      gint   min_item_size, nat_item_size;
+      gint   line_length;
+      gint   item_size;
+      gint   line_size = 0, min_fixed_line_size = 0, nat_fixed_line_size = 0;
+      gint   line_offset, n_children, n_lines, line_count;
+      gint   extra_pixels, extra_per_item, extra_extra;
+      gint   i;
+
+      get_average_item_size (box, priv->orientation, &min_item_size, &nat_item_size);
+
+      /* By default wrap 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 wrap length */
+      line_length = MAX (min_items, line_length);
+      
+      /* 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 (priv->spreading != GTK_WRAP_BOX_SPREAD_EXPAND)
+	item_size = MIN (item_size, nat_item_size);
+
+      /* Get the real extra pixels incase of GTK_WRAP_BOX_SPREAD_BEGIN lines */
+      extra_pixels   = avail_size - (line_length - 1) * item_spacing - item_size * line_length;
+      extra_per_item = extra_pixels / MAX (line_length -1, 1); 
+      extra_extra    = extra_pixels % MAX (line_length -1, 1); 
+
+      /* Get how many lines that wraps to */
+      n_children = get_visible_children (box);
+      n_lines    = n_children / line_length;
+      if ((n_children % line_length) > 0)
+	n_lines++;
+
+      n_lines = MAX (n_lines, 1);
+
+      /* Here we just use the largest height-for-width and use that for the height
+       * of all lines */
+      if (priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
+	{
+	  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;
+	  line_size = MIN (line_size, nat_fixed_line_size);
+	}
+      else /* GTK_WRAP_ALLOCATE_ALIGNED */
+	{
+	  /* spread out the available size in the opposing orientation into an array of
+	   * lines (and then allocate those lines naturally) */
+	  GList *list;
+	  gboolean first_line = TRUE;
+	  
+	  /* In ALIGNED mode, all items have the same size in the box's orientation except
+	   * individual lines may have a different size */
+	  for (i = 0, list = priv->children; list != NULL; i++)
+	    {
+	      GtkRequestedSize    requested;
+	      
+	      list = 
+		get_largest_size_for_line_in_opposing_orientation (box, priv->orientation, 
+								   list, line_length,
+								   item_size, extra_pixels,
+								   &requested.minimum_size, 
+								   &requested.natural_size);
+	      
+
+	      /* Its possible a line is made of completely invisible children */
+	      if (requested.natural_size > 0)
+		{
+		  if (first_line)
+		    first_line = FALSE;
+		  else
+		    avail_other_size -= line_spacing;	
+		  
+		  avail_other_size -= requested.minimum_size;
+
+		  requested.data = GINT_TO_POINTER (i);
+		  g_array_append_val (array, requested);
+		}
+	    }
+
+	  /* Distribute space among lines naturally */
+	  sizes            = (GtkRequestedSize *)array->data;
+	  avail_other_size = gtk_distribute_natural_allocation (avail_other_size, array->len, sizes);
+	}
+      
+      line_offset = border_width;
+
+      for (i = 0, line_count = 0, list = priv->children; list; list = list->next)
+	{
+	  GtkWrapBoxChild *child = list->data;
+	  gint                position, this_line_size, item_offset;
+	  gint                this_item_size;
+
+	  if (!gtk_widget_get_visible (child->widget))
+	    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)
+	    {
+	      if (priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
+		line_offset += line_size + line_spacing;
+	      else /* aligned mode */
+		line_offset += sizes[line_count].minimum_size + line_spacing;
+
+	      line_count++;
+	    }
+
+	  /* We could be smarter here and distribute the extra pixels more
+	   * evenly across the children */
+	  if (position < extra_pixels && priv->spreading == GTK_WRAP_BOX_SPREAD_EXPAND)
+	    this_item_size = item_size + 1;
+	  else
+	    this_item_size = item_size;
+
+	  /* Push the index along for the last line when spreading to the end */
+	  if (priv->spreading == GTK_WRAP_BOX_SPREAD_END &&
+	      line_count == n_lines -1)
+	    {
+	      gint extra_items = n_children % line_length;
+
+	      position += line_length - extra_items;
+	    }
+
+	  item_offset = border_width + (position * item_size) + (position * item_spacing);
+
+	  if (priv->spreading == GTK_WRAP_BOX_SPREAD_EVEN)
+	    {
+	      item_offset += position * extra_per_item;
+	      item_offset += MIN (position, extra_extra);
+	    }
+	  else if (priv->spreading == GTK_WRAP_BOX_SPREAD_END)
+	    item_offset += extra_pixels;
+	  else if (priv->spreading == GTK_WRAP_BOX_SPREAD_EXPAND)
+	    item_offset += MIN (position, extra_pixels);
+
+	  /* Get the allocation size for this line */
+	  if (priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
+	    this_line_size = line_size;
+	  else
+	    this_line_size = sizes[line_count].minimum_size;
+
+	  /* Do the actual allocation */
+	  allocate_child (box, child, item_offset, line_offset, this_item_size, this_line_size);
+
+	  i++;
+	}
+    }
+  else /* GTK_WRAP_ALLOCATE_FREE */
+    {
+      /* Here we just fit as many children as we can allocate their natural size to
+       * on each line and add the heights for each of them on each line */
+      GtkRequestedSize requested;
+      GList           *list = priv->children;
+      gboolean         first_line = TRUE;
+      gint             i, line_count = 0;
+      gint             line_offset, item_offset;
+      gint             extra_pixels;
+
+      while (list != NULL)
+	{
+	  GArray         *line_array;
+	  AllocatedLine  *line;
+
+	  list = 
+	    get_largest_size_for_free_line_in_opposing_orientation (box, priv->orientation, 
+								    list, min_items, avail_size,
+								    &requested.minimum_size, 
+								    &requested.natural_size,
+								    &extra_pixels,
+								    &line_array);
+	  
+	  /* Its possible a line is made of completely invisible children */
+	  if (requested.natural_size > 0)
+	    {
+	      if (first_line)
+		first_line = FALSE;
+	      else
+		avail_other_size -= line_spacing;	
+	      
+	      avail_other_size -= requested.minimum_size;
+	      
+	      line = g_slice_new0 (AllocatedLine);
+	      line->requested    = line_array;
+	      line->extra_pixels = extra_pixels;
+
+	      requested.data  = line;
+
+	      g_array_append_val (array, requested);
+	    }
+	}
+
+      /* Distribute space among lines naturally, dont give lines expand space just let them
+       * unwrap/wrap in and out of the allocated extra space */
+      sizes            = (GtkRequestedSize *)array->data;
+      avail_other_size = gtk_distribute_natural_allocation (avail_other_size, array->len, sizes);
+
+      for (line_offset = border_width, line_count = 0; line_count < array->len; line_count++)
+	{
+	  AllocatedLine    *line       = (AllocatedLine *)sizes[line_count].data;
+	  GArray           *line_array = line->requested;
+	  GtkRequestedSize *line_sizes = (GtkRequestedSize *)line_array->data;
+	  gint              line_size  = sizes[line_count].minimum_size;
+	  gint              extra_per_item = 0;
+	  gint              extra_extra = 0;
+
+	  /* Set line start offset */
+	  item_offset = border_width;
+
+	  if (priv->spreading == GTK_WRAP_BOX_SPREAD_END)
+	    item_offset += line->extra_pixels;
+	  else if (priv->spreading == GTK_WRAP_BOX_SPREAD_EVEN)
+	    {
+	      extra_per_item = line->extra_pixels / MAX (line_array->len -1, 1);
+	      extra_extra    = line->extra_pixels % MAX (line_array->len -1, 1);
+	    }
+
+	  for (i = 0; i < line_array->len; i++)
+	    {
+	      GtkWrapBoxChild *child     = line_sizes[i].data;
+	      gint                item_size = line_sizes[i].minimum_size;
+
+	      item_size += (priv->orientation == GTK_ORIENTATION_HORIZONTAL) ?
+		child->xpadding * 2 : child->ypadding * 2;
+
+	      /* Do the actual allocation */
+	      allocate_child (box, child, item_offset, line_offset, item_size, line_size);
+
+	      /* Add extra space evenly between children */
+	      if (priv->spreading == GTK_WRAP_BOX_SPREAD_EVEN)
+		{
+		  item_offset += extra_per_item;
+		  if (i < extra_extra)
+		    item_offset++;
+		}
+
+	      /* Move item cursor along for the next allocation */
+	      item_offset += item_spacing;
+	      item_offset += item_size;
+	    }
+
+	  /* New line, increment offset and reset item cursor */
+	  line_offset += line_spacing;
+	  line_offset += line_size;
+
+	  /* Free the array for this line now its not needed anymore */
+	  g_array_free (line_array, TRUE);
+	  g_slice_free (AllocatedLine, line);
+	}
+    }
+
+  g_array_free (array, TRUE);
+}
+
+/*****************************************************
+ *                GtkContainerClass                  * 
+ *****************************************************/
+static void
+gtk_wrap_box_add (GtkContainer *container,
+		     GtkWidget    *widget)
+{
+  gtk_wrap_box_insert_child (GTK_WRAP_BOX (container), widget, -1,
+			     0, 0, FALSE, FALSE, FALSE, FALSE);
+}
+
+static gint
+find_child_in_list (GtkWrapBoxChild *child_in_list,
+		    GtkWidget       *search)
+{
+  return (child_in_list->widget == search) ? 0 : -1;
+}
+
+static void
+gtk_wrap_box_remove (GtkContainer *container,
+		     GtkWidget    *widget)
+{
+  GtkWrapBox      *box = GTK_WRAP_BOX (container);
+  GtkWrapBoxPriv  *priv   = box->priv;
+  GList              *list;
+
+  list = g_list_find_custom (priv->children, widget, 
+			     (GCompareFunc)find_child_in_list);
+
+  if (list) 
+    {
+      GtkWrapBoxChild *child = list->data;
+      gboolean was_visible = gtk_widget_get_visible (widget);
+
+      gtk_widget_unparent (widget);
+
+      g_slice_free (GtkWrapBoxChild, child);
+      priv->children = g_list_delete_link (priv->children, list);
+
+      if (was_visible && gtk_widget_get_visible (GTK_WIDGET (container)))
+	gtk_widget_queue_resize (GTK_WIDGET (container));
+    }
+}
+
+static void
+gtk_wrap_box_forall (GtkContainer *container,
+		     gboolean      include_internals,
+		     GtkCallback   callback,
+		     gpointer      callback_data)
+{
+  GtkWrapBox      *box = GTK_WRAP_BOX (container);
+  GtkWrapBoxPriv  *priv   = box->priv;
+  GtkWrapBoxChild *child;
+  GList              *list;
+
+  for (list = priv->children; list; list = list->next)
+    {
+      child = list->data;
+
+      (* callback) (child->widget, callback_data);
+    }
+}
+
+static GType
+gtk_wrap_box_child_type (GtkContainer   *container)
+{
+  return GTK_TYPE_WIDGET;
+}
+
+static void
+gtk_wrap_box_set_child_property (GtkContainer    *container,
+				 GtkWidget       *widget,
+				 guint            property_id,
+				 const GValue    *value,
+				 GParamSpec      *pspec)
+{
+  GtkWrapBox      *box = GTK_WRAP_BOX (container);
+  GtkWrapBoxPriv  *priv   = box->priv;
+  GtkWrapBoxChild *child;
+  GList           *list;
+
+  list = g_list_find_custom (priv->children, widget, 
+			     (GCompareFunc)find_child_in_list);
+  g_return_if_fail (list != NULL);
+
+  child = list->data;
+
+  switch (property_id)
+    {
+    case CHILD_PROP_X_EXPAND:
+      child->xexpand = g_value_get_boolean (value);
+      break;
+    case CHILD_PROP_X_FILL:
+      child->xfill = g_value_get_boolean (value);
+      break;
+    case CHILD_PROP_Y_EXPAND:
+      child->yexpand = g_value_get_boolean (value);
+      break;
+    case CHILD_PROP_Y_FILL:
+      child->yfill = g_value_get_boolean (value);
+      break;
+    case CHILD_PROP_X_PADDING:
+      child->xpadding = g_value_get_uint (value);
+      break;
+    case CHILD_PROP_Y_PADDING:
+      child->ypadding = g_value_get_uint (value);
+      break;
+    default:
+      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
+      break;
+    }
+
+  if (gtk_widget_get_visible (widget) &&
+      gtk_widget_get_visible (GTK_WIDGET (box)))
+    gtk_widget_queue_resize (widget);
+}
+
+static void
+gtk_wrap_box_get_child_property (GtkContainer    *container,
+				 GtkWidget       *widget,
+				 guint            property_id,
+				 GValue          *value,
+				 GParamSpec      *pspec)
+{
+  GtkWrapBox      *box = GTK_WRAP_BOX (container);
+  GtkWrapBoxPriv  *priv   = box->priv;
+  GtkWrapBoxChild *child;
+  GList           *list;
+
+  list = g_list_find_custom (priv->children, widget, 
+			     (GCompareFunc)find_child_in_list);
+  g_return_if_fail (list != NULL);
+
+  child = list->data;
+
+  switch (property_id)
+    {
+    case CHILD_PROP_X_EXPAND:
+      g_value_set_boolean (value, child->xexpand);
+      break;
+    case CHILD_PROP_X_FILL:
+      g_value_set_boolean (value, child->xfill);
+      break;
+    case CHILD_PROP_Y_EXPAND:
+      g_value_set_boolean (value, child->yexpand);
+      break;
+    case CHILD_PROP_Y_FILL:
+      g_value_set_boolean (value, child->yfill);
+      break;
+    case CHILD_PROP_X_PADDING:
+      g_value_set_uint (value, child->xpadding);
+      break;
+    case CHILD_PROP_Y_PADDING:
+      g_value_set_uint (value, child->ypadding);
+      break;
+    default:
+      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
+      break;
+    }
+}
+
+/*****************************************************
+ *               GtkSizeRequestIface                 * 
+ *****************************************************/
+
+static void 
+gtk_wrap_box_size_request_init (GtkSizeRequestIface *iface)
+{
+  iface->get_request_mode     = gtk_wrap_box_get_request_mode;
+  iface->get_width            = gtk_wrap_box_get_width;
+  iface->get_height           = gtk_wrap_box_get_height;
+  iface->get_height_for_width = gtk_wrap_box_get_height_for_width;
+  iface->get_width_for_height = gtk_wrap_box_get_width_for_height;
+}
+
+static GtkSizeRequestMode 
+gtk_wrap_box_get_request_mode (GtkSizeRequest *widget)
+{
+  GtkWrapBox      *box = GTK_WRAP_BOX (widget);
+  GtkWrapBoxPriv  *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 */
+static void
+get_largest_line_length (GtkWrapBox      *box,
+			 GtkOrientation   orientation,
+			 gint             line_length,
+			 gint            *min_size,
+			 gint            *nat_size)
+{
+  GtkWrapBoxPriv  *priv = box->priv;
+  GList           *list;
+  gint             max_min_size = 0;
+  gint             max_nat_size = 0;
+  gint             spacing;
+
+  if (orientation == GTK_ORIENTATION_HORIZONTAL)
+    spacing = priv->horizontal_spacing;
+  else
+    spacing = priv->vertical_spacing;
+
+  /* Get the largest size of 'line_length' consecutive items in the list.
+   */
+  for (list = priv->children; list; list = list->next)
+    {
+      GList *l;
+      gint   line_min = 0;
+      gint   line_nat = 0;
+      gint   i;
+
+      for (l = list, i = 0; l && i < line_length; l = l->next)
+	{
+	  GtkWrapBoxChild *child = list->data;
+	  gint             child_min, child_nat;
+
+	  if (!gtk_widget_get_visible (child->widget))
+	    continue;
+
+	  if (orientation == GTK_ORIENTATION_HORIZONTAL)
+	    {
+	      gtk_size_request_get_width (GTK_SIZE_REQUEST (child->widget),
+					  &child_min, &child_nat);
+
+	      child_min += child->xpadding * 2;
+	      child_nat += child->xpadding * 2;
+	    }
+	  else /* GTK_ORIENTATION_VERTICAL */
+	    {
+	      gtk_size_request_get_height (GTK_SIZE_REQUEST (child->widget),
+					   &child_min, &child_nat);
+	      
+	      child_min += child->ypadding * 2;
+	      child_nat += child->ypadding * 2;
+	    }
+
+	  if (i > 0)
+	    {
+	      line_min += spacing;
+	      line_nat += spacing;
+	    }
+
+	  line_min += child_min;
+	  line_nat += child_nat;
+
+	  i++;
+	}
+
+      max_min_size = MAX (max_min_size, line_min);
+      max_nat_size = MAX (max_nat_size, line_nat);
+    }
+
+  if (min_size)
+    *min_size = max_min_size;
+
+  if (nat_size)
+    *nat_size = max_nat_size;
+}
+
+
+static void 
+gtk_wrap_box_get_width (GtkSizeRequest      *widget,
+			gint                *minimum_size,
+			gint                *natural_size)
+{
+  GtkWrapBox      *box  = GTK_WRAP_BOX (widget);
+  GtkWrapBoxPriv  *priv = box->priv;
+  guint            border_width;
+  gint             min_item_width, nat_item_width;
+  gint             min_items, nat_items;
+  gint             min_width, nat_width;
+
+  min_items = MAX (1,         priv->minimum_line_children);
+  nat_items = MAX (min_items, priv->natural_line_children);
+
+  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
+      min_width    = nat_width = border_width * 2;
+
+      if (priv->mode == GTK_WRAP_ALLOCATE_FREE)
+	{
+	  if (priv->minimum_line_children <= 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_line_length (box, GTK_ORIENTATION_HORIZONTAL, min_items,
+				       &min_line_length, &nat_line_length);
+	      
+	      if (nat_items > min_items)
+		get_largest_line_length (box, GTK_ORIENTATION_HORIZONTAL, nat_items,
+					 NULL, &nat_line_length);
+
+	      min_width += min_line_length;
+	      nat_width += nat_line_length;
+	    }
+	}
+      else /* In ALIGNED or HOMOGENEOUS modes; 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->horizontal_spacing;
+
+	  nat_width += nat_item_width * nat_items;
+	  nat_width += (nat_items -1) * priv->horizontal_spacing;
+	}
+    }
+  else /* GTK_ORIENTATION_VERTICAL */
+    {
+      /* Return the width for the minimum height */
+      gint min_height;
+
+      gtk_size_request_get_height (widget, &min_height, NULL);
+      gtk_size_request_get_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 
+gtk_wrap_box_get_height (GtkSizeRequest      *widget,
+			 gint                *minimum_size,
+			 gint                *natural_size)
+{
+  GtkWrapBox      *box  = GTK_WRAP_BOX (widget);
+  GtkWrapBoxPriv  *priv = box->priv;
+  guint            border_width;
+  gint             min_item_height, nat_item_height;
+  gint             min_items, nat_items;
+  gint             min_height, nat_height;
+
+  min_items = MAX (1,         priv->minimum_line_children);
+  nat_items = MAX (min_items, priv->natural_line_children);
+
+  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      /* Return the height for the minimum width */
+      gint min_width;
+
+      gtk_size_request_get_width (widget, &min_width, NULL);
+      gtk_size_request_get_height_for_width (widget, min_width, &min_height, &nat_height);
+    }
+  else /* GTK_ORIENTATION_VERTICAL */
+    {
+      border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
+      min_height   = nat_height = border_width * 2;
+
+      if (priv->mode == GTK_WRAP_ALLOCATE_FREE)
+	{
+	  if (priv->minimum_line_children <= 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_line_length (box, GTK_ORIENTATION_VERTICAL, min_items,
+				       &min_line_length, &nat_line_length);
+	      
+	      if (nat_items > min_items)
+		get_largest_line_length (box, GTK_ORIENTATION_VERTICAL, nat_items,
+					 NULL, &nat_line_length);
+
+	      min_height += min_line_length;
+	      nat_height += nat_line_length;
+	    }
+	}
+      else /* In ALIGNED or HOMOGENEOUS modes; horizontally oriented boxs
+	    * give the same width 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->vertical_spacing;
+
+	  nat_height += nat_item_height * nat_items;
+	  nat_height += (nat_items -1) * priv->vertical_spacing;
+	}
+    }
+
+  if (minimum_size)
+    *minimum_size = min_height;
+
+  if (natural_size)
+    *natural_size = nat_height;
+}
+
+static void 
+gtk_wrap_box_get_height_for_width (GtkSizeRequest      *widget,
+				   gint                 width,
+				   gint                *minimum_height,
+				   gint                *natural_height)
+{
+  GtkWrapBox      *box = GTK_WRAP_BOX (widget);
+  GtkWrapBoxPriv  *priv   = box->priv;
+  guint            border_width;
+  gint             min_item_width, nat_item_width;
+  gint             min_items;
+  gint             min_height, nat_height;
+  gint             avail_size;
+
+  min_items = MAX (1, priv->minimum_line_children);
+
+  min_height = 0;
+  nat_height = 0;
+
+  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+    { 
+      gint min_width;
+
+      /* Make sure its no smaller than the minimum */
+      gtk_size_request_get_width (widget, &min_width, NULL);
+
+      border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
+     
+      avail_size  = MAX (width, min_width);
+      avail_size -= border_width * 2;
+
+      if (priv->mode == GTK_WRAP_ALLOCATE_ALIGNED ||
+	  priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
+	{
+	  gint line_length;
+	  gint item_size, extra_pixels;
+
+	  get_average_item_size (box, GTK_ORIENTATION_HORIZONTAL, &min_item_width, &nat_item_width);
+
+	  /* By default wrap at the natural item width */
+	  line_length = avail_size / (nat_item_width + priv->horizontal_spacing);
+
+	  /* After the above aproximation, check if we cant fit one more on the line */
+	  if (line_length * priv->horizontal_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 wrap length */
+	  line_length = MAX (min_items, line_length);
+
+	  /* Now we need the real item allocation size */
+	  item_size = (avail_size - (line_length - 1) * priv->horizontal_spacing) / line_length;
+
+	  /* Cut out the expand space if we're not distributing any */
+	  if (priv->spreading != GTK_WRAP_BOX_SPREAD_EXPAND)
+	    {
+	      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->horizontal_spacing) % line_length;
+
+	  if (priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
+	    {
+	      gint min_item_height, nat_item_height;
+	      gint lines, n_children;
+
+	      /* 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 */
+	      n_children = get_visible_children (box);
+	      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->vertical_spacing;
+	      nat_height += (lines - 1) * priv->vertical_spacing;
+	    }
+	  else /* GTK_WRAP_ALLOCATE_ALIGNED */
+	    {
+	      GList *list = priv->children;
+	      gint min_line_height, nat_line_height;
+	      gboolean first_line = TRUE;
+
+	      /* In ALIGNED mode, all items have the same size in the box's orientation except
+	       * individual rows may have a different size */
+	      while (list != NULL)
+		{
+		  list = 
+		    get_largest_size_for_line_in_opposing_orientation (box, GTK_ORIENTATION_HORIZONTAL, 
+								       list, line_length,
+								       item_size, 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->vertical_spacing;
+			  nat_height += priv->vertical_spacing;
+			}
+
+		      min_height += min_line_height;
+		      nat_height += nat_line_height;
+		    }
+		}
+	    }
+	}
+      else /* GTK_WRAP_ALLOCATE_FREE */
+	{
+	  /* Here we just fit as many children as we can allocate their natural size to
+	   * on each line and add the heights for each of them on each line */
+	  GList *list = priv->children;
+	  gint min_line_height = 0, nat_line_height = 0;
+	  gboolean first_line = TRUE;
+
+	  while (list != NULL)
+	    {
+	      list = 
+		get_largest_size_for_free_line_in_opposing_orientation (box, GTK_ORIENTATION_HORIZONTAL, 
+									list, min_items, avail_size,
+									&min_line_height, &nat_line_height,
+									NULL, NULL);
+	      
+	      /* Its possible the last line only had invisible widgets */
+	      if (nat_line_height > 0)
+		{
+		  if (first_line)
+		    first_line = FALSE;
+		  else
+		    {
+		      min_height += priv->vertical_spacing;
+		      nat_height += priv->vertical_spacing;
+		    }
+	      
+		  min_height += min_line_height;
+		  nat_height += nat_line_height;
+		}
+	    }
+	}
+
+      min_height += border_width * 2;
+      nat_height += border_width * 2;
+    }
+  else /* GTK_ORIENTATION_VERTICAL */
+    {
+      /* Return the minimum height */
+      gtk_size_request_get_height (widget, &min_height, &nat_height);
+    }
+
+  if (minimum_height)
+    *minimum_height = min_height;
+
+  if (natural_height)
+    *natural_height = nat_height;
+}
+
+static void
+gtk_wrap_box_get_width_for_height (GtkSizeRequest      *widget,
+				   gint                 height,
+				   gint                *minimum_width,
+				   gint                *natural_width)
+{
+  GtkWrapBox      *box = GTK_WRAP_BOX (widget);
+  GtkWrapBoxPriv  *priv   = box->priv;
+  guint            border_width;
+  gint             min_item_height, nat_item_height;
+  gint             min_items;
+  gint             min_width, nat_width;
+  gint             avail_size;
+
+  min_items = MAX (1, priv->minimum_line_children);
+
+  min_width = 0;
+  nat_width = 0;
+
+  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+    { 
+      /* Return the minimum width */
+      gtk_size_request_get_width (widget, &min_width, &nat_width);
+    }
+  else /* GTK_ORIENTATION_VERTICAL */
+    {
+      gint min_height;
+
+      /* Make sure its no smaller than the minimum */
+      gtk_size_request_get_height (widget, &min_height, NULL);
+
+      border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
+     
+      avail_size  = MAX (height, min_height);
+      avail_size -= border_width * 2;
+
+      if (priv->mode == GTK_WRAP_ALLOCATE_ALIGNED ||
+	  priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
+	{
+	  gint line_length;
+	  gint item_size, extra_pixels;
+
+	  get_average_item_size (box, GTK_ORIENTATION_VERTICAL, &min_item_height, &nat_item_height);
+
+	  /* By default wrap at the natural item width */
+	  line_length = avail_size / (nat_item_height + priv->vertical_spacing);
+
+	  /* After the above aproximation, check if we cant fit one more on the line */
+	  if (line_length * priv->vertical_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 wrap length */
+	  line_length = MAX (min_items, line_length);
+
+	  /* Now we need the real item allocation size */
+	  item_size = (avail_size - (line_length - 1) * priv->vertical_spacing) / line_length;
+
+	  /* Cut out the expand space if we're not distributing any */
+	  if (priv->spreading != GTK_WRAP_BOX_SPREAD_EXPAND)
+	    {
+	      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->vertical_spacing) % line_length;
+
+	  if (priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
+	    {
+	      gint min_item_width, nat_item_width;
+	      gint lines, n_children;
+
+	      /* 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->horizontal_spacing;
+	      nat_width += (lines - 1) * priv->horizontal_spacing;
+	    }
+	  else /* GTK_WRAP_ALLOCATE_ALIGNED */
+	    {
+	      GList *list = priv->children;
+	      gint min_line_width, nat_line_width;
+	      gboolean first_line = TRUE;
+	      
+	      /* In ALIGNED mode, all items have the same size in the box's orientation except
+	       * individual rows may have a different size */
+	      while (list != NULL)
+		{
+		  list = 
+		    get_largest_size_for_line_in_opposing_orientation (box, GTK_ORIENTATION_VERTICAL, 
+								       list, line_length,
+								       item_size, 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->horizontal_spacing;
+			  nat_width += priv->horizontal_spacing;
+			}
+
+		      min_width += min_line_width;
+		      nat_width += nat_line_width;
+		    }
+		}
+	    }
+	}
+      else /* GTK_WRAP_ALLOCATE_FREE */
+	{
+	  /* Here we just fit as many children as we can allocate their natural size to
+	   * on each line and add the heights for each of them on each line */
+	  GList *list = priv->children;
+	  gint min_line_width = 0, nat_line_width = 0;
+	  gboolean first_line = TRUE;
+
+	  while (list != NULL)
+	    {
+	      list = 
+		get_largest_size_for_free_line_in_opposing_orientation (box, GTK_ORIENTATION_VERTICAL, 
+									list, min_items, avail_size,
+									&min_line_width, &nat_line_width,
+									NULL, NULL);
+	      	
+	      /* Its possible the last line only had invisible widgets */
+	      if (nat_line_width > 0)
+		{
+		  if (first_line)
+		    first_line = FALSE;
+		  else
+		    {
+		      min_width += priv->horizontal_spacing;
+		      nat_width += priv->horizontal_spacing;
+		    }
+	      
+		  min_width += min_line_width;
+		  nat_width += nat_line_width;
+		}
+	    }
+	}
+
+      min_width += border_width * 2;
+      nat_width += border_width * 2;
+    }
+
+  if (minimum_width)
+    *minimum_width = min_width;
+
+  if (natural_width)
+    *natural_width = nat_width;
+}
+
+/*****************************************************
+ *                       API                         * 
+ *****************************************************/
+
+/**
+ * gtk_wrap_box_new:
+ * @allocation_mode: The #GtkWrapAllocationMode to use
+ * @spreading: The #GtkWrapBoxSpreading policy to use
+ * @horizontal_spacing: The horizontal spacing to add between children
+ * @vertical_spacing: The vertical spacing to add between children
+ *
+ * Creates an #GtkWrapBox.
+ *
+ * Returns: A new #GtkWrapBox container
+ */
+GtkWidget *
+gtk_wrap_box_new (GtkWrapAllocationMode mode,
+		  GtkWrapBoxSpreading   spreading,
+		  guint                 horizontal_spacing,
+		  guint                 vertical_spacing)
+{
+  return (GtkWidget *)g_object_new (GTK_TYPE_WRAP_BOX,
+				    "allocation-mode", mode,
+				    "spreading", spreading,
+				    "vertical-spacing", vertical_spacing,
+				    "horizontal-spacing", horizontal_spacing,
+				    NULL);
+}
+
+/**
+ * gtk_wrap_box_set_allocation_mode:
+ * @box: An #GtkWrapBox
+ * @mode: The #GtkWrapAllocationMode to use.
+ *
+ * Sets the allocation mode for @box's children.
+ */
+void
+gtk_wrap_box_set_allocation_mode (GtkWrapBox           *box,
+				  GtkWrapAllocationMode mode)
+{
+  GtkWrapBoxPriv *priv;
+
+  g_return_if_fail (GTK_IS_WRAP_BOX (box));
+
+  priv = box->priv;
+
+  if (priv->mode != mode)
+    {
+      priv->mode = mode;
+
+      gtk_widget_queue_resize (GTK_WIDGET (box));
+
+      g_object_notify (G_OBJECT (box), "allocation-mode");
+    }
+}
+
+/**
+ * gtk_wrap_box_get_allocation_mode:
+ * @box: An #GtkWrapBox
+ *
+ * Gets the allocation mode.
+ *
+ * Returns: The #GtkWrapAllocationMode for @box.
+ */
+GtkWrapAllocationMode 
+gtk_wrap_box_get_allocation_mode (GtkWrapBox *box)
+{
+  g_return_val_if_fail (GTK_IS_WRAP_BOX (box), FALSE);
+
+  return box->priv->mode;
+}
+
+
+/**
+ * gtk_wrap_box_set_spreading:
+ * @box: An #GtkWrapBox
+ * @spreading: The #GtkWrapBoxSpreading to use.
+ *
+ * Sets the spreading mode for @box's children.
+ */
+void
+gtk_wrap_box_set_spreading (GtkWrapBox          *box,
+			    GtkWrapBoxSpreading  spreading)
+{
+  GtkWrapBoxPriv *priv;
+
+  g_return_if_fail (GTK_IS_WRAP_BOX (box));
+
+  priv = box->priv;
+
+  if (priv->spreading != spreading)
+    {
+      priv->spreading = spreading;
+
+      gtk_widget_queue_resize (GTK_WIDGET (box));
+
+      g_object_notify (G_OBJECT (box), "spreading");
+    }
+}
+
+/**
+ * gtk_wrap_box_get_spreading:
+ * @box: An #GtkWrapBox
+ *
+ * Gets the spreading mode.
+ *
+ * Returns: The #GtkWrapBoxSpreading for @box.
+ */
+GtkWrapBoxSpreading
+gtk_wrap_box_get_spreading (GtkWrapBox *box)
+{
+  g_return_val_if_fail (GTK_IS_WRAP_BOX (box), FALSE);
+
+  return box->priv->spreading;
+}
+
+
+/**
+ * gtk_wrap_box_set_vertical_spacing:
+ * @box: An #GtkWrapBox
+ * @spacing: The spacing to use.
+ *
+ * Sets the vertical space to add between children.
+ */
+void
+gtk_wrap_box_set_vertical_spacing  (GtkWrapBox    *box, 
+				    guint          spacing)
+{
+  GtkWrapBoxPriv *priv;
+
+  g_return_if_fail (GTK_IS_WRAP_BOX (box));
+
+  priv = box->priv;
+
+  if (priv->vertical_spacing != spacing)
+    {
+      priv->vertical_spacing = spacing;
+
+      gtk_widget_queue_resize (GTK_WIDGET (box));
+
+      g_object_notify (G_OBJECT (box), "vertical-spacing");
+    }
+}
+
+/**
+ * gtk_wrap_box_get_vertical_spacing:
+ * @box: An #GtkWrapBox
+ *
+ * Gets the vertical spacing.
+ *
+ * Returns: The vertical spacing.
+ */
+guint
+gtk_wrap_box_get_vertical_spacing  (GtkWrapBox *box)
+{
+  g_return_val_if_fail (GTK_IS_WRAP_BOX (box), FALSE);
+
+  return box->priv->vertical_spacing;
+}
+
+/**
+ * gtk_wrap_box_set_horizontal_spacing:
+ * @box: An #GtkWrapBox
+ * @spacing: The spacing to use.
+ *
+ * Sets the horizontal space to add between children.
+ */
+void
+gtk_wrap_box_set_horizontal_spacing (GtkWrapBox    *box, 
+				     guint          spacing)
+{
+  GtkWrapBoxPriv *priv;
+
+  g_return_if_fail (GTK_IS_WRAP_BOX (box));
+
+  priv = box->priv;
+
+  if (priv->horizontal_spacing != spacing)
+    {
+      priv->horizontal_spacing = spacing;
+
+      gtk_widget_queue_resize (GTK_WIDGET (box));
+
+      g_object_notify (G_OBJECT (box), "horizontal-spacing");
+    }
+}
+
+/**
+ * gtk_wrap_box_get_horizontal_spacing:
+ * @box: An #GtkWrapBox
+ *
+ * Gets the horizontal spacing.
+ *
+ * Returns: The horizontal spacing.
+ */
+guint
+gtk_wrap_box_get_horizontal_spacing (GtkWrapBox *box)
+{
+  g_return_val_if_fail (GTK_IS_WRAP_BOX (box), FALSE);
+
+  return box->priv->horizontal_spacing;
+}
+
+/**
+ * gtk_wrap_box_set_minimum_line_children:
+ * @box: An #GtkWrapBox
+ * @n_children: The minimum amount of children per line.
+ *
+ * Sets the minimum amount of children to line up
+ * in @box's orientation before wrapping.
+ */
+void
+gtk_wrap_box_set_minimum_line_children (GtkWrapBox *box, 
+					guint       n_children)
+{
+  GtkWrapBoxPriv *priv;
+
+  g_return_if_fail (GTK_IS_WRAP_BOX (box));
+
+  priv = box->priv;
+
+  if (priv->minimum_line_children != n_children)
+    {
+      priv->minimum_line_children = n_children;
+
+      gtk_widget_queue_resize (GTK_WIDGET (box));
+
+      g_object_notify (G_OBJECT (box), "minimum-line-children");
+    }
+}
+
+/**
+ * gtk_wrap_box_get_minimum_line_children:
+ * @box: An #GtkWrapBox
+ *
+ * Gets the minimum amount of children per line.
+ *
+ * Returns: The minimum amount of children per line.
+ */
+guint
+gtk_wrap_box_get_minimum_line_children (GtkWrapBox *box)
+{
+  g_return_val_if_fail (GTK_IS_WRAP_BOX (box), FALSE);
+
+  return box->priv->minimum_line_children;
+}
+
+/**
+ * gtk_wrap_box_set_natural_line_children:
+ * @box: An #GtkWrapBox
+ * @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
+gtk_wrap_box_set_natural_line_children (GtkWrapBox *box, 
+					guint       n_children)
+{
+  GtkWrapBoxPriv *priv;
+
+  g_return_if_fail (GTK_IS_WRAP_BOX (box));
+
+  priv = box->priv;
+
+  if (priv->natural_line_children != n_children)
+    {
+      priv->natural_line_children = n_children;
+
+      gtk_widget_queue_resize (GTK_WIDGET (box));
+
+      g_object_notify (G_OBJECT (box), "natural-line-children");
+    }
+}
+
+/**
+ * gtk_wrap_box_get_natural_line_children:
+ * @box: An #GtkWrapBox
+ *
+ * Gets the natural amount of children per line.
+ *
+ * Returns: The natural amount of children per line.
+ */
+guint
+gtk_wrap_box_get_natural_line_children (GtkWrapBox *box)
+{
+  g_return_val_if_fail (GTK_IS_WRAP_BOX (box), FALSE);
+
+  return box->priv->natural_line_children;
+}
+
+
+/**
+ * gtk_wrap_box_insert_child:
+ * @box: And #GtkWrapBox
+ * @widget: the child #GtkWidget to add
+ * @index: the position in the child list to insert, specify -1 to append to the list.
+ * @xpad: horizontal spacing for this child
+ * @ypad: vertical spacing for this child
+ * @xexpand: whether this child expands horizontally
+ * @yexpand: whether this child expands vertically
+ * @xfill: whether this child fills its horizontal allocation
+ * @yfill: whether this child fills its vertical allocation
+ *
+ * Adds a child to an #GtkWrapBox with its packing options set
+ *
+ */
+void
+gtk_wrap_box_insert_child (GtkWrapBox *box,
+			   GtkWidget  *widget,
+			   gint        index,
+			   guint       xpad,
+			   guint       ypad,
+			   gboolean    xexpand,
+			   gboolean    yexpand,
+			   gboolean    xfill,
+			   gboolean    yfill)
+{
+  GtkWrapBoxPriv  *priv;
+  GtkWrapBoxChild *child;
+  GList           *list;
+
+  g_return_if_fail (GTK_IS_WRAP_BOX (box));
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  priv = box->priv;
+
+  list = g_list_find_custom (priv->children, widget, 
+			     (GCompareFunc)find_child_in_list);
+  g_return_if_fail (list == NULL);
+
+  child           = g_slice_new0 (GtkWrapBoxChild);
+  child->widget   = widget;
+  child->xpadding = xpad;
+  child->ypadding = ypad;
+  child->xexpand  = xexpand;
+  child->yexpand  = yexpand;
+  child->xfill    = xfill;
+  child->yfill    = yfill;
+
+  priv->children = g_list_insert (priv->children, child, index);
+
+  gtk_widget_set_parent (widget, GTK_WIDGET (box));
+}
+
+/**
+ * gtk_wrap_box_reorder_child:
+ * @box: An #GtkWrapBox
+ * @widget: The child to reorder
+ * @index: The new child position
+ *
+ * Reorders the child @widget in @box's list of children.
+ */
+void
+gtk_wrap_box_reorder_child (GtkWrapBox *box,
+			    GtkWidget  *widget,
+			    guint       index)
+{
+  GtkWrapBoxPriv  *priv;
+  GtkWrapBoxChild *child;
+  GList           *list;
+
+  g_return_if_fail (GTK_IS_WRAP_BOX (box));
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  priv = box->priv;
+
+  list = g_list_find_custom (priv->children, widget, 
+			     (GCompareFunc)find_child_in_list);
+  g_return_if_fail (list != NULL);
+
+  if (g_list_position (priv->children, list) != index)
+    {
+      child = list->data;
+      priv->children = g_list_delete_link (priv->children, list);
+      priv->children = g_list_insert (priv->children, child, index);
+
+      gtk_widget_queue_resize (GTK_WIDGET (box));
+    }
+}
diff --git a/gtk/gtkwrapbox.h b/gtk/gtkwrapbox.h
new file mode 100644
index 0000000..76c6370
--- /dev/null
+++ b/gtk/gtkwrapbox.h
@@ -0,0 +1,101 @@
+/* 
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * Authors:
+ *      Tristan Van Berkom <tristanvb openismus com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_WRAP_BOX_H__
+#define __GTK_WRAP_BOX_H__
+
+#include <gtk/gtkcontainer.h>
+
+G_BEGIN_DECLS
+
+
+#define GTK_TYPE_WRAP_BOX                  (gtk_wrap_box_get_type ())
+#define GTK_WRAP_BOX(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_WRAP_BOX, GtkWrapBox))
+#define GTK_WRAP_BOX_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_WRAP_BOX, GtkWrapBoxClass))
+#define GTK_IS_WRAP_BOX(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_WRAP_BOX))
+#define GTK_IS_WRAP_BOX_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_WRAP_BOX))
+#define GTK_WRAP_BOX_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_WRAP_BOX, GtkWrapBoxClass))
+
+typedef struct _GtkWrapBox            GtkWrapBox;
+typedef struct _GtkWrapBoxPriv        GtkWrapBoxPriv;
+typedef struct _GtkWrapBoxClass       GtkWrapBoxClass;
+
+struct _GtkWrapBox
+{
+  GtkContainer container;
+
+  /*< private >*/
+  GtkWrapBoxPriv *priv;
+};
+
+struct _GtkWrapBoxClass
+{
+  GtkContainerClass parent_class;
+};
+
+GType                 gtk_wrap_box_get_type                  (void) G_GNUC_CONST;
+
+GtkWidget            *gtk_wrap_box_new                       (GtkWrapAllocationMode mode,
+							      GtkWrapBoxSpreading   spreading,
+							      guint                 horizontal_spacing,
+							      guint                 vertical_spacing);
+void                  gtk_wrap_box_set_allocation_mode       (GtkWrapBox           *layout,
+							      GtkWrapAllocationMode mode);
+GtkWrapAllocationMode gtk_wrap_box_get_allocation_mode       (GtkWrapBox           *layout);
+
+void                  gtk_wrap_box_set_spreading             (GtkWrapBox           *layout,
+							      GtkWrapBoxSpreading   spreading);
+GtkWrapBoxSpreading   gtk_wrap_box_get_spreading             (GtkWrapBox           *layout);
+
+void                  gtk_wrap_box_set_vertical_spacing      (GtkWrapBox           *layout, 
+							      guint                 spacing);
+guint                 gtk_wrap_box_get_vertical_spacing      (GtkWrapBox           *layout);
+
+void                  gtk_wrap_box_set_horizontal_spacing    (GtkWrapBox           *layout, 
+							      guint                 spacing);
+guint                 gtk_wrap_box_get_horizontal_spacing    (GtkWrapBox           *layout);
+
+void                  gtk_wrap_box_set_minimum_line_children (GtkWrapBox           *layout, 
+							      guint                 n_children);
+guint                 gtk_wrap_box_get_minimum_line_children (GtkWrapBox           *layout);
+
+void                  gtk_wrap_box_set_natural_line_children (GtkWrapBox           *layout, 
+							      guint                 n_children);
+guint                 gtk_wrap_box_get_natural_line_children (GtkWrapBox           *layout);
+
+void                  gtk_wrap_box_insert_child              (GtkWrapBox           *layout,
+							      GtkWidget            *widget,
+							      gint                  index,
+							      guint                 xpad,
+							      guint                 ypad,
+							      gboolean              xexpand,
+							      gboolean              yexpand,
+							      gboolean              xfill,
+							      gboolean              yfill);
+void                  gtk_wrap_box_reorder_child             (GtkWrapBox           *layout,
+							      GtkWidget            *widget,
+							      guint                 index);
+
+G_END_DECLS
+
+
+#endif /* __GTK_WRAP_BOX_H__ */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index a729c24..b7bc395 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -89,7 +89,8 @@ noinst_PROGRAMS =  $(TEST_PROGS)	\
 	testgrouping			\
 	testtooltips			\
 	testexpander			\
-	testvolumebutton
+	testvolumebutton		\
+	testwrapbox
 
 if USE_X11
 noinst_PROGRAMS += testapplication
@@ -170,6 +171,7 @@ testactions_DEPENDENCIES = $(TEST_DEPS)
 testgrouping_DEPENDENCIES = $(TEST_DEPS)
 testtooltips_DEPENDENCIES = $(TEST_DEPS)
 testvolumebutton_DEPENDENCIES = $(TEST_DEPS)
+testwrapbox_DEPENDENCIES = $(TEST_DEPS)
 testwindows_DEPENDENCIES = $(TEST_DEPS)
 testexpander_DEPENDENCIES = $(TEST_DEPS)
 
@@ -238,6 +240,7 @@ testactions_LDADD = $(LDADDS)
 testgrouping_LDADD = $(LDADDS)
 testtooltips_LDADD = $(LDADDS)
 testvolumebutton_LDADD = $(LDADDS)
+testwrapbox_LDADD = $(LDADDS)
 testwindows_LDADD = $(LDADDS)
 testexpander_LDADD = $(LDADDS)
 
@@ -337,6 +340,9 @@ testrecentchoosermenu_SOURCES =	\
 testvolumebutton_SOURCES =	\
 	testvolumebutton.c
 
+testwrapbox_SOURCES =	\
+	testwrapbox.c
+
 testoffscreen_SOURCES = 	\
 	gtkoffscreenbox.c	\
 	gtkoffscreenbox.h	\
diff --git a/tests/testwrapbox.c b/tests/testwrapbox.c
new file mode 100644
index 0000000..0cc2916
--- /dev/null
+++ b/tests/testwrapbox.c
@@ -0,0 +1,547 @@
+/* testwrapbox.c
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * Author:
+ *      Tristan Van Berkom <tristan van berkom gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gtk/gtk.h>
+
+enum {
+  SIMPLE_ITEMS = 0,
+  WRAPPY_ITEMS,
+  STOCK_ITEMS
+};
+
+#define INITIAL_ALLOCATION_MODE GTK_WRAP_ALLOCATE_HOMOGENEOUS
+#define INITIAL_SPREADING       GTK_WRAP_BOX_SPREAD_BEGIN
+#define INITIAL_MINIMUM_LENGTH  3
+#define INITIAL_HSPACING        2
+#define INITIAL_VSPACING        2
+
+static GtkWrapBox *the_wrapbox       = NULL;
+static gint           items_type       = SIMPLE_ITEMS;
+static GtkOrientation text_orientation = GTK_ORIENTATION_HORIZONTAL;
+static gboolean       items_xexpand    = TRUE;
+static gboolean       items_yexpand    = TRUE;
+static gboolean       items_xfill      = TRUE;
+static gboolean       items_yfill      = TRUE;
+static gint           items_xpad       = 0;
+static gint           items_ypad       = 0;
+
+
+static void
+populate_wrapbox_simple (GtkWrapBox *wrapbox)
+{
+  GtkWidget *widget, *frame;
+  gint i;
+
+  for (i = 0; i < 30; 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);
+
+      gtk_wrap_box_insert_child (GTK_WRAP_BOX (wrapbox), frame, -1,
+				    items_xpad, items_ypad, 
+				    items_xexpand, items_yexpand, items_xfill, items_yfill);
+      
+      g_free (text);
+    }
+}
+
+static void
+populate_wrapbox_wrappy (GtkWrapBox *wrapbox)
+{
+  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);
+
+      gtk_wrap_box_insert_child (GTK_WRAP_BOX (wrapbox), frame, -1,
+				    items_xpad, items_ypad, 
+				    items_xexpand, items_yexpand, items_xfill, items_yfill);
+    }
+}
+
+
+static void
+populate_wrapbox_stock (GtkWrapBox *wrapbox)
+{
+  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;
+
+      widget = gtk_button_new_from_stock (stock_id);
+      gtk_widget_show (widget);
+
+      gtk_wrap_box_insert_child (GTK_WRAP_BOX (wrapbox), widget, -1,
+				    items_xpad, items_ypad, 
+				    items_xexpand, items_yexpand, items_xfill, items_yfill);
+    }
+}
+
+static void 
+populate_items (GtkWrapBox *wrapbox)
+{
+  GList *children, *l;
+  
+  /* Remove all children first */
+  children = gtk_container_get_children (GTK_CONTAINER (wrapbox));
+  for (l = children; l; l = l->next)
+    {
+      GtkWidget *child = l->data;
+
+      gtk_container_remove (GTK_CONTAINER (wrapbox), child);
+    }
+  g_list_free (children);
+
+  if (items_type == SIMPLE_ITEMS)
+    populate_wrapbox_simple (wrapbox);
+  else if (items_type == WRAPPY_ITEMS)
+    populate_wrapbox_wrappy (wrapbox);
+  else if (items_type == STOCK_ITEMS)
+    populate_wrapbox_stock (wrapbox);
+}
+
+
+static void
+mode_changed (GtkComboBox   *box,
+	      GtkWrapBox    *wrapbox)
+{
+  GtkWrapAllocationMode mode = gtk_combo_box_get_active (box);
+  
+  gtk_wrap_box_set_allocation_mode (wrapbox, mode);
+}
+
+static void
+spreading_changed (GtkComboBox   *box,
+		   GtkWrapBox    *wrapbox)
+{
+  GtkWrapBoxSpreading spreading = gtk_combo_box_get_active (box);
+  
+  gtk_wrap_box_set_spreading (wrapbox, spreading);
+}
+
+static void
+orientation_changed (GtkComboBox   *box,
+		     GtkWrapBox *wrapbox)
+{
+  GtkOrientation orientation = gtk_combo_box_get_active (box);
+
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (wrapbox), orientation);
+}
+
+static void
+line_length_changed (GtkSpinButton *spin,
+		     GtkWrapBox *wrapbox)
+{
+  gint length = gtk_spin_button_get_value_as_int (spin);
+  
+  gtk_wrap_box_set_minimum_line_children (wrapbox, 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)
+    gtk_wrap_box_set_horizontal_spacing (the_wrapbox, state);
+  else
+    gtk_wrap_box_set_vertical_spacing (the_wrapbox, state);
+}
+
+
+static void
+items_changed (GtkComboBox   *box,
+	       GtkWrapBox *wrapbox)
+{
+  items_type = gtk_combo_box_get_active (box);
+
+  populate_items (wrapbox);
+}
+
+static void
+text_orientation_changed (GtkComboBox   *box,
+			  GtkWrapBox *wrapbox)
+{
+  text_orientation = gtk_combo_box_get_active (box);
+  
+  populate_items (wrapbox);
+}
+
+static void
+child_option_toggled (GtkToggleButton *button,
+		      gboolean        *state)
+{
+  *state = gtk_toggle_button_get_active (button);
+
+  populate_items (the_wrapbox);
+}
+
+static void
+child_padding_changed (GtkSpinButton *button,
+		       gint          *state)
+{
+  *state = gtk_spin_button_get_value_as_int (button);
+
+  populate_items (the_wrapbox);
+}
+
+static GtkWidget *
+create_window (void)
+{
+  GtkWidget *window, *hbox, *vbox, *frame, *wrapbox_cntl, *items_cntl;
+  GtkWidget *wrapbox, *widget, *expander, *swindow;
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  hbox   = gtk_hbox_new (FALSE, 2);
+  vbox   = gtk_vbox_new (FALSE, 6);
+
+  gtk_container_set_border_width (GTK_CONTAINER (window), 8);
+
+  gtk_widget_show (vbox);
+  gtk_widget_show (hbox);
+  gtk_container_add (GTK_CONTAINER (window), hbox);
+  gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+
+  frame = gtk_frame_new ("Wrap Box");
+  gtk_widget_show (frame);
+  gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+
+  swindow = gtk_scrolled_window_new (NULL, NULL);
+  gtk_widget_show (swindow);
+  gtk_container_add (GTK_CONTAINER (frame), swindow);
+
+  wrapbox = gtk_wrap_box_new (INITIAL_ALLOCATION_MODE, INITIAL_SPREADING, 
+			      INITIAL_HSPACING, INITIAL_VSPACING);
+  the_wrapbox = (GtkWrapBox *)wrapbox;
+  gtk_wrap_box_set_minimum_line_children (GTK_WRAP_BOX (wrapbox), INITIAL_MINIMUM_LENGTH);
+  gtk_widget_show (wrapbox);
+  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (swindow), wrapbox);
+
+  /* Add Wrapbox test control frame */
+  expander = gtk_expander_new ("Wrap Box controls");
+  gtk_expander_set_expanded (GTK_EXPANDER (expander), TRUE);
+  wrapbox_cntl = gtk_vbox_new (FALSE, 2);
+  gtk_widget_show (wrapbox_cntl);
+  gtk_widget_show (expander);
+  gtk_container_add (GTK_CONTAINER (expander), wrapbox_cntl);
+  gtk_box_pack_start (GTK_BOX (vbox), expander, FALSE, FALSE, 0);
+
+  /* Add Allocation mode control */
+  widget = gtk_combo_box_new_text ();
+  gtk_combo_box_append_text (GTK_COMBO_BOX (widget), "Wrap Freely");
+  gtk_combo_box_append_text (GTK_COMBO_BOX (widget), "Align items");
+  gtk_combo_box_append_text (GTK_COMBO_BOX (widget), "Homogeneous");
+  gtk_combo_box_set_active (GTK_COMBO_BOX (widget), INITIAL_ALLOCATION_MODE);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set the wrapbox allocation mode");
+  gtk_box_pack_start (GTK_BOX (wrapbox_cntl), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+		    G_CALLBACK (mode_changed), wrapbox);
+
+  /* Add Spreading control */
+  widget = gtk_combo_box_new_text ();
+  gtk_combo_box_append_text (GTK_COMBO_BOX (widget), "Spread Begin");
+  gtk_combo_box_append_text (GTK_COMBO_BOX (widget), "Spread End");
+  gtk_combo_box_append_text (GTK_COMBO_BOX (widget), "Spread Even");
+  gtk_combo_box_append_text (GTK_COMBO_BOX (widget), "Spread Expand");
+  gtk_combo_box_set_active (GTK_COMBO_BOX (widget), INITIAL_SPREADING);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set the wrapbox spread mode");
+  gtk_box_pack_start (GTK_BOX (wrapbox_cntl), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+		    G_CALLBACK (spreading_changed), wrapbox);
+
+  /* Add Orientation control */
+  widget = gtk_combo_box_new_text ();
+  gtk_combo_box_append_text (GTK_COMBO_BOX (widget), "Horizontal");
+  gtk_combo_box_append_text (GTK_COMBO_BOX (widget), "Vertical");
+  gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set the wrapbox orientation");
+  gtk_box_pack_start (GTK_BOX (wrapbox_cntl), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+		    G_CALLBACK (orientation_changed), wrapbox);
+
+  /* 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 (wrapbox_cntl), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+		    G_CALLBACK (line_length_changed), wrapbox);
+  g_signal_connect (G_OBJECT (widget), "value-changed",
+		    G_CALLBACK (line_length_changed), wrapbox);
+
+  /* Add horizontal/vertical spacing controls */
+  hbox = gtk_hbox_new (FALSE, 2);
+  gtk_widget_show (hbox);
+
+  widget = gtk_label_new ("H Spacing");
+  gtk_widget_show (widget);
+  gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
+
+  widget = gtk_spin_button_new_with_range (0, 30, 1);
+  gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), INITIAL_HSPACING);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set the horizontal spacing between children");
+  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+		    G_CALLBACK (spacing_changed), GINT_TO_POINTER (GTK_ORIENTATION_HORIZONTAL));
+  g_signal_connect (G_OBJECT (widget), "value-changed",
+		    G_CALLBACK (spacing_changed), GINT_TO_POINTER (GTK_ORIENTATION_HORIZONTAL));
+
+  gtk_box_pack_start (GTK_BOX (wrapbox_cntl), hbox, FALSE, FALSE, 0);
+
+  hbox = gtk_hbox_new (FALSE, 2);
+  gtk_widget_show (hbox);
+
+  widget = gtk_label_new ("V Spacing");
+  gtk_widget_show (widget);
+  gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
+
+  widget = gtk_spin_button_new_with_range (0, 30, 1);
+  gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), INITIAL_VSPACING);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set the vertical spacing between children");
+  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+		    G_CALLBACK (spacing_changed), GINT_TO_POINTER (GTK_ORIENTATION_VERTICAL));
+  g_signal_connect (G_OBJECT (widget), "value-changed",
+		    G_CALLBACK (spacing_changed), GINT_TO_POINTER (GTK_ORIENTATION_VERTICAL));
+
+  gtk_box_pack_start (GTK_BOX (wrapbox_cntl), hbox, FALSE, FALSE, 0);
+
+
+  /* Add test items control frame */
+  expander = gtk_expander_new ("Test item controls");
+  gtk_expander_set_expanded (GTK_EXPANDER (expander), TRUE);
+  items_cntl = gtk_vbox_new (FALSE, 2);
+  gtk_widget_show (items_cntl);
+  gtk_widget_show (expander);
+  gtk_container_add (GTK_CONTAINER (expander), items_cntl);
+  gtk_box_pack_start (GTK_BOX (vbox), expander, FALSE, FALSE, 0);
+
+  /* Add Items control */
+  widget = gtk_combo_box_new_text ();
+  gtk_combo_box_append_text (GTK_COMBO_BOX (widget), "Simple");
+  gtk_combo_box_append_text (GTK_COMBO_BOX (widget), "Wrappy");
+  gtk_combo_box_append_text (GTK_COMBO_BOX (widget), "Stock");
+  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), wrapbox);
+
+
+  /* Add Text Orientation control */
+  widget = gtk_combo_box_new_text ();
+  gtk_combo_box_append_text (GTK_COMBO_BOX (widget), "Horizontal");
+  gtk_combo_box_append_text (GTK_COMBO_BOX (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), wrapbox);
+
+
+  /* Add expand/fill options */
+  hbox = gtk_hbox_new (FALSE, 2);
+  gtk_widget_show (hbox);
+
+  widget = gtk_check_button_new_with_label ("X Expand");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set whether the items expand horizontally");
+  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "toggled",
+		    G_CALLBACK (child_option_toggled), &items_xexpand);
+
+
+  widget = gtk_check_button_new_with_label ("X Fill");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set whether the items fill their allotted size horizontally");
+  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "toggled",
+		    G_CALLBACK (child_option_toggled), &items_xfill);
+
+  gtk_box_pack_start (GTK_BOX (items_cntl), hbox, FALSE, FALSE, 0);
+
+
+  hbox = gtk_hbox_new (FALSE, 2);
+  gtk_widget_show (hbox);
+
+  widget = gtk_check_button_new_with_label ("Y Expand");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set whether the items expand vertically");
+  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "toggled",
+		    G_CALLBACK (child_option_toggled), &items_yexpand);
+
+
+  widget = gtk_check_button_new_with_label ("Y Fill");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+  gtk_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set whether the items fill their allotted size vertically");
+  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "toggled",
+		    G_CALLBACK (child_option_toggled), &items_yfill);
+
+  gtk_box_pack_start (GTK_BOX (items_cntl), hbox, FALSE, FALSE, 0);
+
+
+  /* Add x/y padding options */
+  hbox = gtk_hbox_new (FALSE, 2);
+  gtk_widget_show (hbox);
+
+  widget = gtk_label_new ("X Padding");
+  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_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set the horizontal padding values for children");
+  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+		    G_CALLBACK (child_padding_changed), &items_xpad);
+  g_signal_connect (G_OBJECT (widget), "value-changed",
+		    G_CALLBACK (child_padding_changed), &items_xpad);
+
+  gtk_box_pack_start (GTK_BOX (items_cntl), hbox, FALSE, FALSE, 0);
+
+
+  hbox = gtk_hbox_new (FALSE, 2);
+  gtk_widget_show (hbox);
+
+  widget = gtk_label_new ("Y Padding");
+  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_widget_show (widget);
+
+  gtk_widget_set_tooltip_text (widget, "Set the vertical padding values for children");
+  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+
+  g_signal_connect (G_OBJECT (widget), "changed",
+		    G_CALLBACK (child_padding_changed), &items_ypad);
+  g_signal_connect (G_OBJECT (widget), "value-changed",
+		    G_CALLBACK (child_padding_changed), &items_ypad);
+
+  gtk_box_pack_start (GTK_BOX (items_cntl), hbox, FALSE, FALSE, 0);
+
+  populate_items (GTK_WRAP_BOX (wrapbox));
+
+  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]