/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * GtkWrapBox: Wrapping box widget
 * Copyright (C) 1999 Tim Janik
 *
 * 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 "config.h"

#include "gtkwrapbox.h"
#include <gtk/gtk.h>


/* --- properties --- */
enum {
  PROP_0,
  PROP_HOMOGENEOUS,
  PROP_JUSTIFY,
  PROP_HSPACING,
  PROP_VSPACING,
  PROP_LINE_JUSTIFY,
  PROP_ASPECT_RATIO,
  PROP_CURRENT_RATIO,
  PROP_CHILD_LIMIT
};

enum {
  CHILD_PROP_0,
  CHILD_PROP_POSITION,
  CHILD_PROP_HEXPAND,
  CHILD_PROP_HFILL,
  CHILD_PROP_VEXPAND,
  CHILD_PROP_VFILL,
  CHILD_PROP_WRAPPED
};


/* --- prototypes --- */
static void gtk_wrap_box_class_init    (GtkWrapBoxClass    *klass);
static void gtk_wrap_box_init          (GtkWrapBox         *wbox);
static void gtk_wrap_box_set_property  (GObject            *object,
                                        guint               property_id,
                                        const GValue       *value,
                                        GParamSpec         *pspec);
static void gtk_wrap_box_get_property  (GObject            *object,
                                        guint               property_id,
                                        GValue             *value,
                                        GParamSpec         *pspec);
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 void gtk_wrap_box_map           (GtkWidget          *widget);
static void gtk_wrap_box_unmap         (GtkWidget          *widget);
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 GType gtk_wrap_box_child_type   (GtkContainer       *container);


/* --- variables --- */
static gpointer parent_class = NULL;


/* --- functions --- */
GType
gtk_wrap_box_get_type (void)
{
  static GType wrap_box_type = 0;

  if (! wrap_box_type)
    {
      const GTypeInfo wrap_box_info =
      {
        sizeof (GtkWrapBoxClass),
        NULL,                /* base_init */
        NULL,                /* base_finalize */
        (GClassInitFunc) gtk_wrap_box_class_init,
        NULL,                /* class_finalize */
        NULL,                /* class_data */
        sizeof (GtkWrapBox),
        0,                /* n_preallocs */
        (GInstanceInitFunc) gtk_wrap_box_init,
      };

      wrap_box_type = g_type_register_static (GTK_TYPE_CONTAINER, "GtkWrapBox",
                                              &wrap_box_info, 0);
    }

  return wrap_box_type;
}

static void
gtk_wrap_box_class_init (GtkWrapBoxClass *class)
{
  GObjectClass *object_class;
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;

  object_class = G_OBJECT_CLASS (class);
  widget_class = GTK_WIDGET_CLASS (class);
  container_class = GTK_CONTAINER_CLASS (class);

  parent_class = g_type_class_peek_parent (class);

  object_class->set_property = gtk_wrap_box_set_property;
  object_class->get_property = gtk_wrap_box_get_property;

  widget_class->map = gtk_wrap_box_map;
  widget_class->unmap = gtk_wrap_box_unmap;

  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;

  class->rlist_line_children = NULL;

  g_object_class_install_property (object_class,
                                   PROP_HOMOGENEOUS,
                                   g_param_spec_boolean ("homogeneous",
                                                         NULL,
                                                         NULL,
                                                         FALSE,
                                                         G_PARAM_READWRITE));
  g_object_class_install_property (object_class,
                                   PROP_JUSTIFY,
                                   g_param_spec_enum ("justify",
                                                      NULL,
                                                      NULL,
                                                      GTK_TYPE_JUSTIFICATION,
                                                      GTK_JUSTIFY_LEFT,
                                                      G_PARAM_READWRITE));
  g_object_class_install_property (object_class,
                                   PROP_HSPACING,
                                   g_param_spec_uint ("hspacing",
                                                      NULL,
                                                      NULL,
                                                      0,
                                                      G_MAXINT,
                                                      0,
                                                      G_PARAM_READWRITE));

  g_object_class_install_property (object_class,
                                   PROP_VSPACING,
                                   g_param_spec_uint ("vspacing",
                                                      NULL,
                                                      NULL,
                                                      0,
                                                      G_MAXINT,
                                                      0,
                                                      G_PARAM_READWRITE));

  g_object_class_install_property (object_class,
                                   PROP_LINE_JUSTIFY,
                                   g_param_spec_enum ("line-justify",
                                                      NULL,
                                                      NULL,
                                                      GTK_TYPE_JUSTIFICATION,
                                                      GTK_JUSTIFY_BOTTOM,
                                                      G_PARAM_READWRITE));

  g_object_class_install_property (object_class,
                                   PROP_ASPECT_RATIO,
                                   g_param_spec_float ("aspect-ratio",
                                                       NULL,
                                                       NULL,
                                                       0.0,
                                                       G_MAXFLOAT,
                                                       1.0,
                                                       G_PARAM_READWRITE));

  g_object_class_install_property (object_class,
                                   PROP_CURRENT_RATIO,
                                   g_param_spec_float ("current-ratio",
                                                       NULL,
                                                       NULL,
                                                       0.0,
                                                       G_MAXFLOAT,
                                                       1.0,
                                                       G_PARAM_READABLE));

  g_object_class_install_property (object_class,
                                   PROP_CHILD_LIMIT,
                                   g_param_spec_uint ("max-children-per-line",
                                                      NULL,
                                                      NULL,
                                                      1,
                                                      32767,
                                                      32767,
                                                      G_PARAM_READWRITE));

  gtk_container_class_install_child_property (container_class,
                                              CHILD_PROP_POSITION,
                                              g_param_spec_int ("position",
                                                                NULL,
                                                                NULL,
                                                                -1, G_MAXINT, 0,
                                                                G_PARAM_READWRITE));
  gtk_container_class_install_child_property (container_class,
                                              CHILD_PROP_HEXPAND,
                                              g_param_spec_boolean ("hexpand",
                                                                    NULL,
                                                                    NULL,
                                                                    FALSE,
                                                                    G_PARAM_READWRITE));
  gtk_container_class_install_child_property (container_class,
                                              CHILD_PROP_HFILL,
                                              g_param_spec_boolean ("hfill",
                                                                    NULL,
                                                                    NULL,
                                                                    FALSE,
                                                                    G_PARAM_READWRITE));
  gtk_container_class_install_child_property (container_class,
                                              CHILD_PROP_VEXPAND,
                                              g_param_spec_boolean ("vexpand",
                                                                    NULL,
                                                                    NULL,
                                                                    FALSE,
                                                                    G_PARAM_READWRITE));
  gtk_container_class_install_child_property (container_class,
                                              CHILD_PROP_VFILL,
                                              g_param_spec_boolean ("vfill",
                                                                    NULL,
                                                                    NULL,
                                                                    FALSE,
                                                                    G_PARAM_READWRITE));
  gtk_container_class_install_child_property (container_class,
                                              CHILD_PROP_WRAPPED,
                                              g_param_spec_boolean ("wrapped",
                                                                    NULL,
                                                                    NULL,
                                                                    FALSE,
                                                                    G_PARAM_READWRITE));
}

static void
gtk_wrap_box_init (GtkWrapBox *wbox)
{
  gtk_widget_set_has_window (GTK_WIDGET (wbox), FALSE);

  wbox->homogeneous = FALSE;
  wbox->hspacing = 0;
  wbox->vspacing = 0;
  wbox->justify = GTK_JUSTIFY_LEFT;
  wbox->line_justify = GTK_JUSTIFY_BOTTOM;
  wbox->n_children = 0;
  wbox->children = NULL;
  wbox->aspect_ratio = 1.0;
  wbox->child_limit = 32767;
}

static void
gtk_wrap_box_set_property (GObject      *object,
                           guint         property_id,
                           const GValue *value,
                           GParamSpec   *pspec)
{
  GtkWrapBox *wbox = GTK_WRAP_BOX (object);

  switch (property_id)
    {
    case PROP_HOMOGENEOUS:
      gtk_wrap_box_set_homogeneous (wbox, g_value_get_boolean (value));
      break;
    case PROP_JUSTIFY:
      gtk_wrap_box_set_justify (wbox, g_value_get_enum (value));
      break;
    case PROP_LINE_JUSTIFY:
      gtk_wrap_box_set_line_justify (wbox, g_value_get_enum (value));
      break;
    case PROP_HSPACING:
      gtk_wrap_box_set_hspacing (wbox, g_value_get_uint (value));
      break;
    case PROP_VSPACING:
      gtk_wrap_box_set_vspacing (wbox, g_value_get_uint (value));
      break;
    case PROP_ASPECT_RATIO:
      gtk_wrap_box_set_aspect_ratio (wbox, g_value_get_float (value));
      break;
    case PROP_CHILD_LIMIT:
      if (wbox->child_limit != g_value_get_uint (value))
        gtk_widget_queue_resize (GTK_WIDGET (wbox));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
gtk_wrap_box_get_property (GObject    *object,
                           guint       property_id,
                           GValue     *value,
                           GParamSpec *pspec)
{
  GtkWrapBox *wbox = GTK_WRAP_BOX (object);
  GtkWidget *widget = GTK_WIDGET (object);

  switch (property_id)
    {
    case PROP_HOMOGENEOUS:
      g_value_set_boolean (value, wbox->homogeneous);
      break;
    case PROP_JUSTIFY:
      g_value_set_enum (value, wbox->justify);
      break;
    case PROP_LINE_JUSTIFY:
      g_value_set_enum (value, wbox->line_justify);
      break;
    case PROP_HSPACING:
      g_value_set_uint (value, wbox->hspacing);
      break;
    case PROP_VSPACING:
      g_value_set_uint (value, wbox->vspacing);
      break;
    case PROP_ASPECT_RATIO:
      g_value_set_float (value, wbox->aspect_ratio);
      break;
    case PROP_CURRENT_RATIO:
      {
        GtkAllocation alloc;

        gtk_widget_get_allocation (widget, &alloc);

        g_value_set_float (value, (((gfloat) alloc.width) /
                                   ((gfloat) alloc.height)));
      }
      break;
    case PROP_CHILD_LIMIT:
      g_value_set_uint (value, wbox->child_limit);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
gtk_wrap_box_set_child_property (GtkContainer    *container,
                                 GtkWidget       *child,
                                 guint            property_id,
                                 const GValue    *value,
                                 GParamSpec      *pspec)
{
  GtkWrapBox *wbox = GTK_WRAP_BOX (container);
  gboolean hexpand = FALSE, hfill = FALSE;
  gboolean vexpand = FALSE, vfill = FALSE;
  gboolean wrapped = FALSE;

  if (property_id != CHILD_PROP_POSITION)
    gtk_wrap_box_query_child_packing (wbox, child,
                                      &hexpand, &hfill,
                                      &vexpand, &vfill,
                                      &wrapped);

  switch (property_id)
    {
    case CHILD_PROP_POSITION:
      gtk_wrap_box_reorder_child (wbox, child, g_value_get_int (value));
      break;
    case CHILD_PROP_HEXPAND:
      gtk_wrap_box_set_child_packing (wbox, child,
                                      g_value_get_boolean (value), hfill,
                                      vexpand, vfill,
                                      wrapped);
      break;
    case CHILD_PROP_HFILL:
      gtk_wrap_box_set_child_packing (wbox, child,
                                      hexpand, g_value_get_boolean (value),
                                      vexpand, vfill,
                                      wrapped);
      break;
    case CHILD_PROP_VEXPAND:
      gtk_wrap_box_set_child_packing (wbox, child,
                                      hexpand, hfill,
                                      g_value_get_boolean (value), vfill,
                                      wrapped);
      break;
    case CHILD_PROP_VFILL:
      gtk_wrap_box_set_child_packing (wbox, child,
                                      hexpand, hfill,
                                      vexpand, g_value_get_boolean (value),
                                      wrapped);
      break;
    case CHILD_PROP_WRAPPED:
      gtk_wrap_box_set_child_packing (wbox, child,
                                      hexpand, hfill,
                                      vexpand, vfill,
                                      g_value_get_boolean (value));
      break;
    default:
      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
      break;
    }
}

static void
gtk_wrap_box_get_child_property (GtkContainer    *container,
                                 GtkWidget       *child,
                                 guint            property_id,
                                 GValue          *value,
                                 GParamSpec      *pspec)
{
  GtkWrapBox *wbox = GTK_WRAP_BOX (container);
  gboolean hexpand = FALSE, hfill = FALSE;
  gboolean vexpand = FALSE, vfill = FALSE;
  gboolean wrapped = FALSE;

  if (property_id != CHILD_PROP_POSITION)
    gtk_wrap_box_query_child_packing (wbox, child,
                                      &hexpand, &hfill,
                                      &vexpand, &vfill,
                                      &wrapped);

  switch (property_id)
    {
      GtkWrapBoxChild *child_info;
      guint i;
    case CHILD_PROP_POSITION:
      i = 0;
      for (child_info = wbox->children; child_info; child_info = child_info->next)
        {
          if (child_info->widget == child)
            break;
          i += 1;
        }
      g_value_set_int (value, child_info ? i : -1);
      break;
    case CHILD_PROP_HEXPAND:
      g_value_set_boolean (value, hexpand);
      break;
    case CHILD_PROP_HFILL:
      g_value_set_boolean (value, hfill);
      break;
    case CHILD_PROP_VEXPAND:
      g_value_set_boolean (value, vexpand);
      break;
    case CHILD_PROP_VFILL:
      g_value_set_boolean (value, vfill);
      break;
    case CHILD_PROP_WRAPPED:
      g_value_set_boolean (value, wrapped);
      break;
    default:
      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
      break;
    }
}

static GType
gtk_wrap_box_child_type        (GtkContainer *container)
{
  return GTK_TYPE_WIDGET;
}

void
gtk_wrap_box_set_homogeneous (GtkWrapBox *wbox,
                              gboolean    homogeneous)
{
  g_return_if_fail (GTK_IS_WRAP_BOX (wbox));

  homogeneous = homogeneous != FALSE;
  if (wbox->homogeneous != homogeneous)
    {
      wbox->homogeneous = homogeneous;
      gtk_widget_queue_resize (GTK_WIDGET (wbox));
    }
}

void
gtk_wrap_box_set_hspacing (GtkWrapBox *wbox,
                           guint       hspacing)
{
  g_return_if_fail (GTK_IS_WRAP_BOX (wbox));

  if (wbox->hspacing != hspacing)
    {
      wbox->hspacing = hspacing;
      gtk_widget_queue_resize (GTK_WIDGET (wbox));
    }
}

void
gtk_wrap_box_set_vspacing (GtkWrapBox *wbox,
                           guint       vspacing)
{
  g_return_if_fail (GTK_IS_WRAP_BOX (wbox));

  if (wbox->vspacing != vspacing)
    {
      wbox->vspacing = vspacing;
      gtk_widget_queue_resize (GTK_WIDGET (wbox));
    }
}

void
gtk_wrap_box_set_justify (GtkWrapBox      *wbox,
                          GtkJustification justify)
{
  g_return_if_fail (GTK_IS_WRAP_BOX (wbox));
  g_return_if_fail (justify <= GTK_JUSTIFY_FILL);

  if (wbox->justify != justify)
    {
      wbox->justify = justify;
      gtk_widget_queue_resize (GTK_WIDGET (wbox));
    }
}

void
gtk_wrap_box_set_line_justify (GtkWrapBox      *wbox,
                               GtkJustification line_justify)
{
  g_return_if_fail (GTK_IS_WRAP_BOX (wbox));
  g_return_if_fail (line_justify <= GTK_JUSTIFY_FILL);

  if (wbox->line_justify != line_justify)
    {
      wbox->line_justify = line_justify;
      gtk_widget_queue_resize (GTK_WIDGET (wbox));
    }
}

void
gtk_wrap_box_set_aspect_ratio (GtkWrapBox *wbox,
                               gfloat      aspect_ratio)
{
  g_return_if_fail (GTK_IS_WRAP_BOX (wbox));

  aspect_ratio = CLAMP (aspect_ratio, 1.0 / 256.0, 256.0);

  if (wbox->aspect_ratio != aspect_ratio)
    {
      wbox->aspect_ratio = aspect_ratio;
      gtk_widget_queue_resize (GTK_WIDGET (wbox));
    }
}

void
gtk_wrap_box_pack (GtkWrapBox *wbox,
                   GtkWidget  *child,
                   gboolean    hexpand,
                   gboolean    hfill,
                   gboolean    vexpand,
                   gboolean    vfill)
{
  g_return_if_fail (GTK_IS_WRAP_BOX (wbox));
  g_return_if_fail (GTK_IS_WIDGET (child));
  g_return_if_fail (gtk_widget_get_parent (child) == NULL);

  gtk_wrap_box_pack_wrapped (wbox, child, hexpand, hfill, vexpand, vfill, FALSE);
}

void
gtk_wrap_box_pack_wrapped (GtkWrapBox *wbox,
                           GtkWidget  *child,
                           gboolean    hexpand,
                           gboolean    hfill,
                           gboolean    vexpand,
                           gboolean    vfill,
                           gboolean    wrapped)
{
  GtkWrapBoxChild *child_info;

  g_return_if_fail (GTK_IS_WRAP_BOX (wbox));
  g_return_if_fail (GTK_IS_WIDGET (child));
  g_return_if_fail (gtk_widget_get_parent (child) == NULL);

  child_info = g_slice_new (GtkWrapBoxChild);

  child_info->widget = child;
  child_info->hexpand = hexpand ? TRUE : FALSE;
  child_info->hfill = hfill ? TRUE : FALSE;
  child_info->vexpand = vexpand ? TRUE : FALSE;
  child_info->vfill = vfill ? TRUE : FALSE;
  child_info->wrapped = wrapped ? TRUE : FALSE;
  child_info->next = NULL;
  if (wbox->children)
    {
      GtkWrapBoxChild *last = wbox->children;

      while (last->next)
        last = last->next;
      last->next = child_info;
    }
  else
    wbox->children = child_info;
  wbox->n_children++;

  gtk_widget_set_parent (child, GTK_WIDGET (wbox));

  if (gtk_widget_get_realized (GTK_WIDGET (wbox)))
    gtk_widget_realize (child);

  if (gtk_widget_get_visible (GTK_WIDGET (wbox)) && gtk_widget_get_visible (child))
    {
      if (gtk_widget_get_mapped (GTK_WIDGET (wbox)))
        gtk_widget_map (child);

      gtk_widget_queue_resize (child);
    }
}

void
gtk_wrap_box_reorder_child (GtkWrapBox *wbox,
                            GtkWidget  *child,
                            gint        position)
{
  GtkWrapBoxChild *child_info, *last = NULL;

  g_return_if_fail (GTK_IS_WRAP_BOX (wbox));
  g_return_if_fail (GTK_IS_WIDGET (child));

  for (child_info = wbox->children; child_info; last = child_info, child_info = last->next)
    if (child_info->widget == child)
      break;

  if (child_info && wbox->children->next)
    {
      GtkWrapBoxChild *tmp;

      if (last)
        last->next = child_info->next;
      else
        wbox->children = child_info->next;

      last = NULL;
      tmp = wbox->children;
      while (position && tmp->next)
        {
          position--;
          last = tmp;
          tmp = last->next;
        }

      if (position)
        {
          tmp->next = child_info;
          child_info->next = NULL;
        }
      else
        {
          child_info->next = tmp;
          if (last)
            last->next = child_info;
          else
            wbox->children = child_info;
        }

      if (gtk_widget_get_visible (child) && gtk_widget_get_visible (GTK_WIDGET (wbox)))
        gtk_widget_queue_resize (child);
    }
}

void
gtk_wrap_box_query_child_packing (GtkWrapBox *wbox,
                                  GtkWidget  *child,
                                  gboolean   *hexpand,
                                  gboolean   *hfill,
                                  gboolean   *vexpand,
                                  gboolean   *vfill,
                                  gboolean   *wrapped)
{
  GtkWrapBoxChild *child_info;

  g_return_if_fail (GTK_IS_WRAP_BOX (wbox));
  g_return_if_fail (GTK_IS_WIDGET (child));

  for (child_info = wbox->children; child_info; child_info = child_info->next)
    if (child_info->widget == child)
      break;

  if (child_info)
    {
      if (hexpand)
        *hexpand = child_info->hexpand;
      if (hfill)
        *hfill = child_info->hfill;
      if (vexpand)
        *vexpand = child_info->vexpand;
      if (vfill)
        *vfill = child_info->vfill;
      if (wrapped)
        *wrapped = child_info->wrapped;
    }
}

void
gtk_wrap_box_set_child_packing (GtkWrapBox *wbox,
                                GtkWidget  *child,
                                gboolean    hexpand,
                                gboolean    hfill,
                                gboolean    vexpand,
                                gboolean    vfill,
                                gboolean    wrapped)
{
  GtkWrapBoxChild *child_info;

  g_return_if_fail (GTK_IS_WRAP_BOX (wbox));
  g_return_if_fail (GTK_IS_WIDGET (child));

  hexpand = hexpand != FALSE;
  hfill = hfill != FALSE;
  vexpand = vexpand != FALSE;
  vfill = vfill != FALSE;
  wrapped = wrapped != FALSE;

  for (child_info = wbox->children; child_info; child_info = child_info->next)
    if (child_info->widget == child)
      break;

  if (child_info &&
      (child_info->hexpand != hexpand || child_info->vexpand != vexpand ||
       child_info->hfill != hfill || child_info->vfill != vfill ||
       child_info->wrapped != wrapped))
    {
      child_info->hexpand = hexpand;
      child_info->hfill = hfill;
      child_info->vexpand = vexpand;
      child_info->vfill = vfill;
      child_info->wrapped = wrapped;

      if (gtk_widget_get_visible (child) && gtk_widget_get_visible (GTK_WIDGET (wbox)))
        gtk_widget_queue_resize (child);
    }
}

guint*
gtk_wrap_box_query_line_lengths (GtkWrapBox *wbox,
                                 guint      *_n_lines)
{
  GtkWrapBoxChild *next_child = NULL;
  GtkAllocation area, allocation;
  gboolean expand_line;
  GSList *slist;
  guint max_child_size, border, n_lines = 0, *lines = NULL;

  if (_n_lines)
    *_n_lines = 0;
  g_return_val_if_fail (GTK_IS_WRAP_BOX (wbox), NULL);

  gtk_widget_get_allocation (GTK_WIDGET (wbox), &allocation);
  border = gtk_container_get_border_width (GTK_CONTAINER (wbox));
  area.x = allocation.x + border;
  area.y = allocation.y + border;
  area.width = MAX (1, (gint) allocation.width - border * 2);
  area.height = MAX (1, (gint) allocation.height - border * 2);

  next_child = wbox->children;
  slist = GTK_WRAP_BOX_GET_CLASS (wbox)->rlist_line_children (wbox,
                                                              &next_child,
                                                              &area,
                                                              &max_child_size,
                                                              &expand_line);
  while (slist)
    {
      guint l = n_lines++;

      lines = g_renew (guint, lines, n_lines);
      lines[l] = g_slist_length (slist);
      g_slist_free (slist);

      slist = GTK_WRAP_BOX_GET_CLASS (wbox)->rlist_line_children (wbox,
                                                                  &next_child,
                                                                  &area,
                                                                  &max_child_size,
                                                                  &expand_line);
    }

  if (_n_lines)
    *_n_lines = n_lines;

  return lines;
}

static void
gtk_wrap_box_map (GtkWidget *widget)
{
  GtkWrapBox *wbox = GTK_WRAP_BOX (widget);
  GtkWrapBoxChild *child;

  gtk_widget_set_mapped (GTK_WIDGET (wbox), TRUE);

  for (child = wbox->children; child; child = child->next)
    if (gtk_widget_get_visible (child->widget) &&
        !gtk_widget_get_mapped (child->widget))
      gtk_widget_map (child->widget);
}

static void
gtk_wrap_box_unmap (GtkWidget *widget)
{
  GtkWrapBox *wbox = GTK_WRAP_BOX (widget);
  GtkWrapBoxChild *child;

  gtk_widget_set_mapped (GTK_WIDGET (wbox), FALSE);

  for (child = wbox->children; child; child = child->next)
    if (gtk_widget_get_visible (child->widget) &&
        gtk_widget_get_mapped (child->widget))
      gtk_widget_unmap (child->widget);
}

static void
gtk_wrap_box_add (GtkContainer *container,
                  GtkWidget    *widget)
{
  gtk_wrap_box_pack (GTK_WRAP_BOX (container), widget, FALSE, TRUE, FALSE, TRUE);
}

static void
gtk_wrap_box_remove (GtkContainer *container,
                     GtkWidget    *widget)
{
  GtkWrapBox *wbox = GTK_WRAP_BOX (container);
  GtkWrapBoxChild *child, *last = NULL;

  child = wbox->children;
  while (child)
    {
      if (child->widget == widget)
        {
          gboolean was_visible;

          was_visible = gtk_widget_get_visible (widget);
          gtk_widget_unparent (widget);

          if (last)
            last->next = child->next;
          else
            wbox->children = child->next;
          g_slice_free (GtkWrapBoxChild, child);
          wbox->n_children--;

          if (was_visible)
            gtk_widget_queue_resize (GTK_WIDGET (container));

          break;
        }

      last = child;
      child = last->next;
    }
}

static void
gtk_wrap_box_forall (GtkContainer *container,
                     gboolean      include_internals,
                     GtkCallback   callback,
                     gpointer      callback_data)
{
  GtkWrapBox *wbox = GTK_WRAP_BOX (container);
  GtkWrapBoxChild *child;

  child = wbox->children;
  while (child)
    {
      GtkWidget *widget = child->widget;

      child = child->next;

      callback (widget, callback_data);
    }
}
