Logo Search packages:      
Sourcecode: glrr-widgets version File versions  Download package

gtktreecontainer.c

/*
   Copyright (c) 2004, 2005 by AOSASA Shigeru and Red Hat, Inc.
   All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are
   met:

   - Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

   - Redistributions in binary form must reproduce the above
   copyright notice, this list of conditions and the following
   disclaimer in the documentation and/or other materials provided
   with the distribution.  

   - Neither the name of the AOSASA Shigeru, Red Hat, Inc
   nor the names of its contributors may be used to endorse or
   promote products derived from this software without specific
   prior written permission.

   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
   OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/*
   This software development was supported by
   Information-technorogy Promotion Agency, Japan (IPA).
*/

#include "gtktreecontainer.h"
#include "gtktreecontainertypes.h"

#include "glrr-glib.h"


/* #define DEBUG_CLEAR_EXPOSE_REGION */
/* #define DEBUG_DRAW_RECTANGLE_BRANCH_BBOX */


enum {
  PROP_0,
  PROP_TREE_STYLE,
  PROP_INDENT,
  PROP_ALIGN,
  PROP_DEPTH_ALIGN,
  PROP_TREE_PAD,
  PROP_NODE_PAD
};

static void gtk_tree_container_class_init (GtkTreeContainerClass *klass);
static void gtk_tree_container_init       (GtkTreeContainer  *tree_container);
static void gtk_tree_container_set_property  (GObject        *object,
                                    guint           property_id,
                                    const GValue   *value,
                                    GParamSpec     *pspec);
static void gtk_tree_container_get_property  (GObject        *object,
                                    guint           property_id,
                                    GValue         *value,
                                    GParamSpec     *pspec);
static void     gtk_tree_container_map           (GtkWidget      *widget);
static void     gtk_tree_container_unmap         (GtkWidget      *widget);
static void     gtk_tree_container_realize       (GtkWidget      *widget);
static void     gtk_tree_container_size_request  (GtkWidget      *widget,
                                      GtkRequisition *requisition);
static void     gtk_tree_container_size_allocate (GtkWidget      *widget,
                                      GtkAllocation  *allocation);
static gboolean gtk_tree_container_expose        (GtkWidget      *widget,
                                      GdkEventExpose *event);
static void gtk_tree_container_add    (GtkContainer   *container,
                               GtkWidget      *widget);
static void gtk_tree_container_remove (GtkContainer   *container,
                               GtkWidget      *widget);
static void gtk_tree_container_forall (GtkContainer   *container,
                               gboolean        include_internals,
                               GtkCallback     callback,
                               gpointer        callback_data);

static void gtk_tree_container_foreach_node         (GtkTreeContainer     *tree_container,
                                         GtkCallback           callback,
                                         gpointer              callback_data);
static void gtk_tree_container_foreach_node_widget  (GtkTreeContainer     *tree_container,
                                         GtkCallback           callback,
                                         gpointer              callback_data);
static void gtk_tree_container_size_allocate_children_recurse
                                          (GtkTreeContainer     *tree_container,
                                         GtkTreeContainerNode *tree_node,
                                         GtkAllocation        *allocation);
static void gtk_tree_container_expose_branch        (GtkTreeContainer     *tree_container,
                                         GtkTreeContainerNode *tree_node,
                                         GdkEventExpose       *event);
static void gtk_tree_container_paint_branch         (GtkTreeContainer     *tree_container,
                                         GtkTreeContainerNode *tree_node,
                                         GdkEventExpose       *event);
static void gtk_tree_container_node_width_max       (GtkTreeContainerNode *tree_node,
                                         gint           *max_width);
static void gtk_tree_container_check_nodes_resized  (GtkTreeContainer     *tree_container);
static void gtk_tree_container_node_invalidate_requisitions
                                        (GtkTreeContainerNode *tree_node);
static void gtk_tree_container_node_ancestors_invalidate_requisitions
                                        (GtkTreeContainerNode *tree_node);

static void                  gtk_tree_container_append_root_node
                                          (GtkTreeContainer     *tree_container,
                                           GtkTreeContainerNode *root_node);
static void                  gtk_tree_container_attach_node
                                          (GtkTreeContainer     *tree_container,
                                           GtkTreeContainerNode *node);
static void                  gtk_tree_container_detach_node
                                          (GtkTreeContainer     *tree_container,
                                           GtkTreeContainerNode *node);

static GtkTreeContainerNode* gtk_tree_container_find_node_by_widget
                                          (GtkTreeContainer     *tree_container,
                                           GtkWidget            *node_widget);
static GtkTreeContainerNode* gtk_tree_container_find_node_at_pos
                                          (GtkTreeContainer     *tree_container,
                                           gint                  x,
                                           gint                  y);

static void                  gtk_tree_container_node_init
                                          (GtkTreeContainerNode *node);
static void                  gtk_tree_container_node_destroy
                                          (GtkTreeContainerNode *node);
static GtkTreeContainerNode* gtk_tree_container_node_new
                                          (GtkTreeContainer     *tree_container,
                                           GtkWidget            *node_widget);
static void                  gtk_tree_container_node_set_parent
                                          (GtkTreeContainer     *tree_container,
                                           GtkTreeContainerNode *node,
                                           GtkTreeContainerNode *parent_node);
static void                  gtk_tree_container_node_unlink
                                          (GtkTreeContainer     *tree_container,
                                           GtkTreeContainerNode *node);
static void                  gtk_tree_container_node_reparent
                                          (GtkTreeContainer     *tree_container,
                                           GtkTreeContainerNode *node,
                                           GtkTreeContainerNode *parent_node);
static void                  gtk_tree_container_node_children_reparent
                                          (GtkTreeContainer     *tree_container,
                                           GtkTreeContainerNode *node,
                                           GtkTreeContainerNode *parent_node);

static void get_top_node_offset         (GtkTreeContainer     *tree_container,
                               GtkTreeContainerNode *tree_node,
                               gint                 *offset_x,
                               gint                 *offset_y);
static void get_children_bbox_offset    (GtkTreeContainer     *tree_container,
                               GtkTreeContainerNode *tree_node,
                               gint                 *offset_x,
                               gint                 *offset_y);
static void get_descendants_bbox_offset (GtkTreeContainer     *tree_container,
                               GtkTreeContainerNode *tree_node,
                               gint                 *offset_x,
                               gint                 *offset_y);
static void get_top_node_size       (GtkTreeContainer     *tree_container,
                               GtkTreeContainerNode *tree_node,
                               gint                 *width,
                               gint                 *height);
static void get_subtree_bbox_size   (GtkTreeContainer     *tree_container,
                               GtkTreeContainerNode *tree_node,
                               gint                 *width,
                               gint                 *height);
static void get_descendants_bbox_size     (GtkTreeContainer     *tree_container,
                               GtkTreeContainerNode *tree_node,
                               gint                 *width,
                               gint                 *height);
static void get_children_bbox_size  (GtkTreeContainer     *tree_container,
                               GtkTreeContainerNode *tree_node,
                               gint                 *width,
                               gint                 *height);
static void get_branch_offset       (GtkTreeContainer     *tree_container,
                               GtkTreeContainerNode *tree_node,
                               gint                 *offset_x,
                               gint                 *offset_y);
static void get_branch_size         (GtkTreeContainer     *tree_container,
                               GtkTreeContainerNode *tree_node,
                               gint                 *width,
                               gint                 *height);
static void make_top_node_allocation         (GtkTreeContainer     *tree_container,
                                    GtkTreeContainerNode *tree_node,
                                    GtkAllocation        *allocation,
                                    GtkAllocation        *child_allocation);
static void make_descendants_bbox_allocation (GtkTreeContainer     *tree_container,
                                    GtkTreeContainerNode *tree_node,
                                    GtkAllocation        *allocation,
                                    GtkAllocation        *descendants_allocation);


static GtkContainerClass *parent_class = NULL;


GType
gtk_tree_container_get_type (void)
{
  static GType tree_container_type = 0;

  if (!tree_container_type)
    {
      static const GTypeInfo tree_container_info =
      {
      sizeof (GtkTreeContainerClass),
      NULL,       /* base_init */
      NULL,       /* base_finalize */
      (GClassInitFunc) gtk_tree_container_class_init,
      NULL,       /* class_finalize */
      NULL,       /* class_data */
      sizeof (GtkTreeContainer),
      0,          /* n_preallocs */
      (GInstanceInitFunc) gtk_tree_container_init,
        NULL,           /* value_table */
      };

      tree_container_type = g_type_register_static (GTK_TYPE_CONTAINER,
                                        "GtkTreeContainer",
                                        &tree_container_info, 0);
    }

  return tree_container_type;
}

static void
gtk_tree_container_class_init (GtkTreeContainerClass *klass)
{
  GObjectClass   *gobject_class;
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;

  gobject_class = G_OBJECT_CLASS (klass);
  object_class = (GtkObjectClass*) klass;
  widget_class = (GtkWidgetClass*) klass;
  container_class = (GtkContainerClass*) klass;

  parent_class = g_type_class_peek_parent (klass);

  gobject_class->set_property = gtk_tree_container_set_property;
  gobject_class->get_property = gtk_tree_container_get_property;

  widget_class->map = gtk_tree_container_map;
  widget_class->unmap = gtk_tree_container_unmap;
  widget_class->realize = gtk_tree_container_realize;
  widget_class->size_request = gtk_tree_container_size_request;
  widget_class->size_allocate = gtk_tree_container_size_allocate;
  widget_class->expose_event = gtk_tree_container_expose;

  container_class->add = gtk_tree_container_add;
  container_class->remove = gtk_tree_container_remove;
  container_class->forall = gtk_tree_container_forall;


  g_object_class_install_property (gobject_class,
                           PROP_TREE_STYLE,
                                   g_param_spec_enum ("tree_style",
                                          "Tree Style",
                                          "XXX",
                                          GTK_TYPE_TREE_STYLE,
                                          GTK_TREE_STYLE_SPRAY,
                                          G_PARAM_READABLE | G_PARAM_WRITABLE));

  g_object_class_install_property (gobject_class,
                           PROP_INDENT,
                                   g_param_spec_uint ("indent",
                                          "indent",
                                          "XXX",
                                          0,
                                          G_MAXUINT,
                                          20,
                                          G_PARAM_READABLE | G_PARAM_WRITABLE));

  g_object_class_install_property (gobject_class,
                           PROP_ALIGN,
                                   g_param_spec_float ("align",
                                           "align",
                                           "XXX",
                                           0.0,
                                           1.0,
                                           0.5,
                                           G_PARAM_READABLE | G_PARAM_WRITABLE));

  g_object_class_install_property (gobject_class,
                           PROP_DEPTH_ALIGN,
                                   g_param_spec_boolean ("depth_align",
                                           "Depth Align",
                                           "XXX",
                                           TRUE,
                                           G_PARAM_READABLE | G_PARAM_WRITABLE));

  g_object_class_install_property (gobject_class,
                           PROP_TREE_PAD,
                                   g_param_spec_uint ("tree_pad",
                                          "Tree Pad",
                                          "XXX",
                                          0,
                                          G_MAXUINT,
                                          20,
                                          G_PARAM_READABLE | G_PARAM_WRITABLE));

  g_object_class_install_property (gobject_class,
                           PROP_NODE_PAD,
                                   g_param_spec_uint ("node_pad",
                                          "Node Pad",
                                          "XXX",
                                          0,
                                          G_MAXUINT,
                                          20,
                                          G_PARAM_READABLE | G_PARAM_WRITABLE));
}

static void
gtk_tree_container_init (GtkTreeContainer *tree_container)
{
#if 0
  GTK_WIDGET_SET_FLAGS (tree_container, GTK_NO_WINDOW);
#else
  GTK_WIDGET_UNSET_FLAGS (tree_container, GTK_NO_WINDOW);
#endif

  tree_container->tree_style  = GTK_TREE_STYLE_SPRAY;
  tree_container->indent      = 20;
  tree_container->align       = 0.5;
  tree_container->depth_align = TRUE;

  tree_container->tree_pad = 6;
  tree_container->node_pad = 2;

  tree_container->root_nodes = NULL;

  tree_container->node_ht
    = g_hash_table_new (g_direct_hash,
                  g_direct_equal);
}

static void
gtk_tree_container_set_property (GObject      *object,
                         guint         property_id,
                         const GValue *value,
                         GParamSpec   *pspec)
{
  GtkTreeContainer *tree_container;
  
  tree_container = GTK_TREE_CONTAINER (object);

  
  switch (property_id)
    {
    case PROP_TREE_STYLE:
      gtk_tree_container_set_style (tree_container,
                            g_value_get_enum (value));
      break;

    case PROP_INDENT:
      gtk_tree_container_set_indent (tree_container,
                             g_value_get_uint (value));
      break;

    case PROP_ALIGN:
      gtk_tree_container_set_align (tree_container,
                            g_value_get_float (value));
      break;

    case PROP_DEPTH_ALIGN:
      gtk_tree_container_set_depth_align (tree_container,
                                g_value_get_boolean (value));
      break;

    case PROP_TREE_PAD:
      gtk_tree_container_set_tree_pad (tree_container,
                               g_value_get_uint (value));
      break;

    case PROP_NODE_PAD:
      gtk_tree_container_set_node_pad (tree_container,
                               g_value_get_uint (value));
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
gtk_tree_container_get_property (GObject    *object,
                         guint       property_id,
                         GValue     *value,
                         GParamSpec *pspec)
{
  GtkTreeContainer *tree_container;

  tree_container = GTK_TREE_CONTAINER (object);


  switch (property_id)
    {
    case PROP_TREE_STYLE:
      g_value_set_enum (value, tree_container->tree_style);
      break;

    case PROP_INDENT:
      g_value_set_uint (value, tree_container->indent);
      break;

    case PROP_ALIGN:
      g_value_set_float (value, tree_container->align);
      break;

    case PROP_DEPTH_ALIGN:
      g_value_set_boolean (value, tree_container->depth_align);
      break;

    case PROP_TREE_PAD:
      g_value_set_uint (value, tree_container->tree_pad);
      break;

    case PROP_NODE_PAD:
      g_value_set_uint (value, tree_container->node_pad);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
gtk_tree_container_map (GtkWidget *widget)
{
  GtkTreeContainer *tree_container;
  
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_TREE_CONTAINER (widget));


  tree_container = GTK_TREE_CONTAINER (widget);
  GTK_WIDGET_SET_FLAGS (tree_container, GTK_MAPPED);

  gtk_container_foreach (GTK_CONTAINER (tree_container),
                   (GtkCallback)gtk_widget_map, NULL);

  if (!GTK_WIDGET_NO_WINDOW (tree_container))
    gdk_window_show (GTK_WIDGET (tree_container)->window);
}

static void
gtk_tree_container_unmap (GtkWidget *widget)
{
  GtkTreeContainer *tree_container;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_TREE_CONTAINER (widget));


  tree_container = GTK_TREE_CONTAINER (widget);
  GTK_WIDGET_UNSET_FLAGS (tree_container, GTK_MAPPED);

  if (!GTK_WIDGET_NO_WINDOW (tree_container))
    gdk_window_hide (GTK_WIDGET (tree_container)->window);

  gtk_container_foreach (GTK_CONTAINER (tree_container),
                   (GtkCallback)gtk_widget_unmap, NULL);
}

static void
gtk_tree_container_realize (GtkWidget *widget)
{
  GdkWindowAttr attributes;
  gint attributes_mask;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_TREE_CONTAINER (widget));


  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);

  attributes.window_type = GDK_WINDOW_CHILD;
  attributes.x = widget->allocation.x;
  attributes.y = widget->allocation.y;
  attributes.width = widget->allocation.width;
  attributes.height = widget->allocation.height;
  attributes.wclass = GDK_INPUT_OUTPUT;
  attributes.visual = gtk_widget_get_visual (widget);
  attributes.colormap = gtk_widget_get_colormap (widget);
  attributes.event_mask = gtk_widget_get_events (widget);
  attributes.event_mask |= GDK_EXPOSURE_MASK;

  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;

  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
                           &attributes, attributes_mask);
  gdk_window_set_user_data (widget->window, widget);

  widget->style = gtk_style_attach (widget->style, widget->window);
  gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
}

static void
gtk_tree_container_size_request (GtkWidget        *widget,
                         GtkRequisition   *requisition)
{
  GtkTreeContainer     *tree_container;
  GtkTreeContainerNode *root_node;
  gint                  tree_width;
  gint                  tree_height;
  GList                *tmp;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_TREE_CONTAINER (widget));


  tree_container = GTK_TREE_CONTAINER (widget);


  requisition->width  = 0;
  requisition->height = 0;

  tmp = tree_container->root_nodes;
  while (tmp != NULL)
    {
      root_node = tmp->data;
      tmp = g_list_next (tmp);

      get_subtree_bbox_size (tree_container, root_node,
                       &tree_width, &tree_height);

      requisition->height += (((requisition->height > 0) && (tree_height > 0))
                        ? tree_container->tree_pad
                        : 0);

      requisition->width  = MAX (requisition->width, tree_width);
      requisition->height += tree_height;
    }
}

static void
gtk_tree_container_size_allocate_children_recurse (GtkTreeContainer     *tree_container,
                                       GtkTreeContainerNode *node,
                                       GtkAllocation        *allocation)
{
  GtkAllocation         tmp_allocation;
  GNode                *tmp_gnode;
  GtkTreeContainerNode *child;
  gint                  tmp_width;
  gint                  tmp_height;

  g_return_if_fail (tree_container != NULL);
  g_return_if_fail (GTK_IS_TREE_CONTAINER (tree_container));
  g_return_if_fail (node != NULL);


  tmp_allocation = *allocation;

  if (   !GTK_TREE_CONTAINER_NODE_IS_LEAF (node)
      && GTK_TREE_CONTAINER_NODE_IS_EXPANDED (node))
    {
      make_descendants_bbox_allocation (tree_container,
                              node,
                              allocation,
                              &tmp_allocation);

      tmp_gnode = g_node_first_child (node->gnode);
      while (tmp_gnode != NULL)
      {
        child = tmp_gnode->data;
        tmp_gnode = g_node_next_sibling (tmp_gnode);

        if (GTK_WIDGET_VISIBLE (child->widget))
          {
            gtk_tree_container_size_allocate_children_recurse
                        (tree_container, child, &tmp_allocation);

            get_subtree_bbox_size (tree_container, child,
                             &tmp_width, &tmp_height);

            tmp_allocation.x += 0;
            tmp_allocation.y += tmp_height;

            tmp_allocation.y += tree_container->node_pad;
          }
      }
    }

  make_top_node_allocation (tree_container,
                      node,
                      allocation,
                      &tmp_allocation);
  gtk_widget_size_allocate (GTK_WIDGET (node->widget), &tmp_allocation);
}

static void
gtk_tree_container_size_allocate (GtkWidget     *widget,
                          GtkAllocation *allocation)
{
  GtkTreeContainer     *tree_container;
  GtkTreeContainerNode *root_node;
  GtkAllocation         tmp_allocation;
  GList                *tmp;
  gint                  tree_width;
  gint                  tree_height;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_TREE_CONTAINER (widget));


  tree_container = GTK_TREE_CONTAINER (widget);

  GTK_WIDGET (tree_container)->allocation = *allocation;

  if (   !GTK_WIDGET_NO_WINDOW (tree_container)
      && GTK_WIDGET_REALIZED (tree_container))
    {
      gdk_window_move_resize (GTK_WIDGET (tree_container)->window,
                        allocation->x, 
                        allocation->y,
                        allocation->width, 
                        allocation->height);
    }


  gtk_tree_container_check_nodes_resized (tree_container);


  tmp_allocation = *allocation;
  tmp = tree_container->root_nodes;
  while (tmp != NULL)
    {
      root_node = tmp->data;
      tmp = g_list_next (tmp);

      get_subtree_bbox_size (tree_container, root_node,
                       &tree_width, &tree_height);

      tmp_allocation.width  = tree_width;
      tmp_allocation.height = tree_height;

      gtk_tree_container_size_allocate_children_recurse
                  (tree_container, root_node, &tmp_allocation);

      tmp_allocation.x += 0;
      tmp_allocation.y += tree_height;

      tmp_allocation.y += ((tree_height > 0)
                     ? tree_container->tree_pad
                     : 0);
    }

  gtk_widget_queue_draw (GTK_WIDGET (tree_container));
}

static void
gtk_tree_container_paint_branch (GtkTreeContainer     *tree_container,
                         GtkTreeContainerNode *node,
                         GdkEventExpose       *event)
{
  if (   !GTK_TREE_CONTAINER_NODE_IS_LEAF (node)
      && GTK_TREE_CONTAINER_NODE_IS_EXPANDED (node)
      && GTK_WIDGET_VISIBLE (node->widget))
    {
      GdkRectangle branch_area;
      GdkRectangle dummy;
      GNode *tmp;
      GtkWidget *tmp_widget;
      GtkWidget *node_widget;

      node_widget = node->widget;

      {
      gint tmp_top_offset_x;
      gint tmp_top_offset_y;
      gint tmp_width;
      gint tmp_height;
      gint tmp_offset_x;
      gint tmp_offset_y;

      get_branch_offset (tree_container, node,
                     &tmp_offset_x, &tmp_offset_y);
      get_branch_size   (tree_container, node,
                     &tmp_width, &tmp_height);
      get_top_node_offset (tree_container, node,
                       &tmp_top_offset_x, &tmp_top_offset_y);

      branch_area.x = (node_widget->allocation.x - tmp_top_offset_x
                   + tmp_offset_x);
      branch_area.y = (node_widget->allocation.y - tmp_top_offset_y
                   + tmp_offset_y);
      branch_area.width  = tmp_width;
      branch_area.height = tmp_height;

#ifdef DEBUG_DRAW_RECTANGLE_BRANCH_BBOX
      gdk_draw_rectangle (GTK_WIDGET (tree_container)->window,
                      GTK_WIDGET (tree_container)->style->white_gc,
                      TRUE,
                      branch_area.x, branch_area.y,
                      branch_area.width, branch_area.height);
#endif
      }

      if (gdk_rectangle_intersect (&event->area,
                           &branch_area,
                           &dummy))
      {
        switch (tree_container->tree_style)
          {
          case GTK_TREE_STYLE_STEPWISE:
            {
            gint x1;
            gint y1;
            gint x2;
            gint y2;

            x1 = (node_widget->allocation.x
                  + (tree_container->indent / 2));
            y1 = (node_widget->allocation.y
                  + (node_widget->allocation.height));

            tmp = g_node_first_child (node->gnode);
            while (tmp != NULL)
              {
                tmp_widget = ((GtkTreeContainerNode*)tmp->data)->widget;
                x2 = (tmp_widget->allocation.x - 1);
                y2 = (tmp_widget->allocation.y
                    + (tmp_widget->allocation.height / 2));

                gdk_draw_line (GTK_WIDGET (tree_container)->window,
                           GTK_WIDGET (tree_container)->style->black_gc,
                           x1, y2, x2, y2);

                tmp = g_node_next_sibling (tmp);
              }
            gdk_draw_line (GTK_WIDGET (tree_container)->window,
                         GTK_WIDGET (tree_container)->style->black_gc,
                         x1, y1, x1, y2);
            }
            break;

          case GTK_TREE_STYLE_SPRAY:
            {
            if (g_node_n_children (node->gnode) > 1)
              {
                gint x0;
                gint x1;
                gint x2;
                gint y2;
                gint y2_min;
                gint y2_max;
                gint x3;
                gint y3;

                x0 = (node_widget->allocation.x + node_widget->allocation.width);
                x1 = (x0 + (tree_container->indent / 2));

                y2_min = G_MAXINT;
                y2_max = G_MININT;

                tmp = g_node_first_child (node->gnode);
                while (tmp != NULL)
                  {
                  tmp_widget = GTK_WIDGET (((GtkTreeContainerNode*)tmp->data)->widget);

                  x2 = (tmp_widget->allocation.x - 1);
                  y2 = (tmp_widget->allocation.y
                        + (tmp_widget->allocation.height / 2));
            
                  gdk_draw_line (GTK_WIDGET (tree_container)->window,
                               GTK_WIDGET (tree_container)->style->black_gc,
                               x1, y2, x2, y2);

                  y2_min = MIN (y2_min, y2);
                  y2_max = MAX (y2_max, y2);
                  
                  tmp = g_node_next_sibling (tmp);
                  }

                gdk_draw_line (GTK_WIDGET (tree_container)->window,
                           GTK_WIDGET (tree_container)->style->black_gc,
                           x1, y2_min, x1, y2_max);


                if (node_widget->allocation.height >= (y2_max - y2_min))
                  {
                  x3 = (node_widget->allocation.x + node_widget->allocation.width);
                  y3 = (y2_min + ((y2_max - y2_min) * tree_container->align));

                  gdk_draw_line (GTK_WIDGET (tree_container)->window,
                               GTK_WIDGET (tree_container)->style->black_gc,
                               x3, y3, x1, y3);
                  }
                else
                  {
                  gint y4;

                  x3 = (node_widget->allocation.x + node_widget->allocation.width);
                  y3 = (node_widget->allocation.y
                        + (node_widget->allocation.height / 2));
                  
                  gdk_draw_line (GTK_WIDGET (tree_container)->window,
                               GTK_WIDGET (tree_container)->style->black_gc,
                               x3, y3, x1, y3);


                  y4 = ((y2_max + y2_min) / 2);

                  gdk_draw_line (GTK_WIDGET (tree_container)->window,
                               GTK_WIDGET (tree_container)->style->black_gc,
                               x1, y3, x1, y4);
                  }
              }
            else
              {
                gint x1;
                gint y1;
                gint x2;
                gint y2;

                tmp = g_node_first_child (node->gnode);
                tmp_widget = GTK_WIDGET (((GtkTreeContainerNode*)tmp->data)->widget);
                
                if (node_widget->allocation.height
                  < tmp_widget->allocation.height)
                  {
                  y1 = (node_widget->allocation.y
                        + (node_widget->allocation.height) / 2);
                  y2 = y1;
                  }
                else
                  {
                  y1 = (tmp_widget->allocation.y
                        + (tmp_widget->allocation.height / 2));
                  y2 = y1;
                  }
                x1 = (node_widget->allocation.x
                    + node_widget->allocation.width);
                x2 = (tmp_widget->allocation.x - 1);
                gdk_draw_line (GTK_WIDGET (tree_container)->window,
                           GTK_WIDGET (tree_container)->style->black_gc,
                           x1, y1, x2, y2);
              }
            }
            break;

          default:
            break;
          }
      }
    }
}

static void
gtk_tree_container_expose_branch (GtkTreeContainer     *tree_container,
                          GtkTreeContainerNode *node,
                          GdkEventExpose       *event)
{
  GdkRectangle subtree_area;
  GdkRectangle dummy;

  {
    gint top_offset_x;
    gint top_offset_y;
    gint subtree_x;
    gint subtree_y;
    gint subtree_width;
    gint subtree_height;

    get_top_node_offset (tree_container,
                   node,
                   &top_offset_x,
                   &top_offset_y);
    get_subtree_bbox_size (tree_container,
                     node,
                     &subtree_width,
                     &subtree_height);

    subtree_x = (node->widget->allocation.x - top_offset_x);
    subtree_y = (node->widget->allocation.y - top_offset_y);

    subtree_area.x      = subtree_x;
    subtree_area.y      = subtree_y;
    subtree_area.width  = subtree_width;
    subtree_area.height = subtree_height;
  }

  if (gdk_rectangle_intersect (&event->area,
                         &subtree_area,
                         &dummy))
    {
      gtk_tree_container_paint_branch (tree_container,
                               node,
                               event);


      {
      GNode *tmp;
      GtkTreeContainerNode *tmp_node;

      tmp = g_node_children (node->gnode);
      while (tmp != NULL)
        {
          tmp_node = tmp->data;
          tmp = g_node_next_sibling (tmp);

          gtk_tree_container_expose_branch (tree_container,
                                    tmp_node,
                                    event);
        }
      }
    }
}

static gboolean
gtk_tree_container_expose (GtkWidget      *widget,
                     GdkEventExpose *event)
{
  GtkTreeContainer *tree_container;

  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_TREE_CONTAINER (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);


  tree_container = GTK_TREE_CONTAINER (widget);
  
  if (GTK_WIDGET_DRAWABLE (tree_container))
    {
#ifdef DEBUG_CLEAR_EXPOSE_REGION
      gdk_window_clear_area (GTK_WIDGET (tree_container)->window,
                       event->area.x,
                       event->area.y,
                       event->area.width,
                       event->area.height);
#endif

      {
      GList *tmp;
      GtkTreeContainerNode *root_node;


      tmp = tree_container->root_nodes;
      while (tmp != NULL)
        {
          root_node = tmp->data;
          tmp = g_list_next (tmp);


          gtk_tree_container_expose_branch (tree_container,
                                    root_node,
                                    event);
        }
      }


      (* GTK_WIDGET_CLASS (parent_class)->expose_event)
                                    (GTK_WIDGET (tree_container),
                                     event);
    }

  return FALSE;
}

static void
gtk_tree_container_add (GtkContainer *container,
                  GtkWidget    *widget)
{
  gtk_tree_container_append_node_widget (GTK_TREE_CONTAINER (container),
                               widget,
                               NULL);
}

static void
gtk_tree_container_remove (GtkContainer *container,
                     GtkWidget    *widget)
{
  GtkTreeContainer *tree_container;
  GtkTreeContainerNode *node;

  g_return_if_fail (container != NULL);
  g_return_if_fail (GTK_IS_TREE_CONTAINER (container));
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_WIDGET (widget));
  g_return_if_fail (widget->parent == GTK_WIDGET (container));


  tree_container = GTK_TREE_CONTAINER (container);

  gtk_tree_container_remove_node_widget (GTK_TREE_CONTAINER (container),
                               widget);
}



typedef struct _ForeachData ForeachData;

struct _ForeachData {
  GFunc    func;
  gpointer func_data;
};

static void
foreach_node_widget (gpointer data,
                 gpointer user_data)
{
  ForeachData *d;
  GFunc    func;
  gpointer func_data;

  GtkWidget *widget;
  GtkTreeContainerNode *node;

  node = (GtkTreeContainerNode *)data;
  widget = node->widget;

  d = (ForeachData *)user_data;

  func = d->func;
  func_data = d->func_data;

  (* func) (widget, func_data);
}


static void
gtk_tree_container_forall (GtkContainer *container,
                     gboolean      include_internals,
                     GtkCallback   callback,
                     gpointer      callback_data)
{
  GtkTreeContainer *tree_container;
  GList *tmp;
  GtkTreeContainerNode *root_node;
  ForeachData data;

  g_return_if_fail (container != NULL);
  g_return_if_fail (GTK_IS_TREE_CONTAINER (container));


  tree_container = GTK_TREE_CONTAINER (container);

  data.func      = (GFunc)callback;
  data.func_data = callback_data;

  tmp = tree_container->root_nodes;
  while (tmp != NULL)
    {
      root_node = (GtkTreeContainerNode*)(tmp->data);
      tmp = g_list_next (tmp);

      g_node_forall_subtree_data (root_node->gnode,
                          (GFunc)foreach_node_widget,
                          &data);
    }
}

static void
gtk_tree_container_foreach_node (GtkTreeContainer *tree_container,
                         GtkCallback       callback,
                         gpointer          callback_data)
{
  g_hash_table_foreach_value (tree_container->node_ht,
                        (GFunc)callback,
                        callback_data);
}

static void
gtk_tree_container_foreach_node_widget (GtkTreeContainer *tree_container,
                              GtkCallback       callback,
                              gpointer          callback_data)
{
  g_hash_table_foreach_key (tree_container->node_ht,
                      (GFunc)callback,
                      callback_data);
}


GtkWidget*
gtk_tree_container_new (void)
{
  GtkTreeContainer *tree_container;

  tree_container = g_object_new (GTK_TYPE_TREE_CONTAINER,
                         NULL);

  return GTK_WIDGET (tree_container);
}

void
gtk_tree_container_set_style (GtkTreeContainer *tree_container,
                        GtkTreeStyle      tree_style)
{
  g_return_if_fail (tree_container != NULL);
  g_return_if_fail (GTK_IS_TREE_CONTAINER (tree_container));


  if (tree_container->tree_style != tree_style)
    {
      tree_container->tree_style = tree_style;

      gtk_tree_container_foreach_node (tree_container,
                               (GtkCallback)gtk_tree_container_node_invalidate_requisitions,
                               NULL);

      gtk_widget_queue_resize (GTK_WIDGET (tree_container));
    }
}

void
gtk_tree_container_set_indent (GtkTreeContainer *tree_container,
                         guint             indent)
{
  g_return_if_fail (tree_container != NULL);
  g_return_if_fail (GTK_IS_TREE_CONTAINER (tree_container));


  if (tree_container->indent != indent)
    {
      tree_container->indent = indent;

      gtk_tree_container_foreach_node (tree_container,
                               (GtkCallback)gtk_tree_container_node_invalidate_requisitions,
                               NULL);

      gtk_widget_queue_resize (GTK_WIDGET (tree_container));
    }
}

void
gtk_tree_container_set_align (GtkTreeContainer *tree_container,
                        gfloat            align)
{
  g_return_if_fail (tree_container != NULL);
  g_return_if_fail (GTK_IS_TREE_CONTAINER (tree_container));


  if (tree_container->align != align)
    {
      tree_container->align = align;

      gtk_tree_container_foreach_node (tree_container,
                               (GtkCallback)gtk_tree_container_node_invalidate_requisitions,
                               NULL);

      gtk_widget_queue_resize (GTK_WIDGET (tree_container));
    }
}

void
gtk_tree_container_set_depth_align (GtkTreeContainer *tree_container,
                            gboolean          depth_align)
{
  g_return_if_fail (tree_container != NULL);
  g_return_if_fail (GTK_IS_TREE_CONTAINER (tree_container));


  if (tree_container->depth_align != depth_align)
    {
      tree_container->depth_align = depth_align;

      gtk_tree_container_foreach_node (tree_container,
                               (GtkCallback)gtk_tree_container_node_invalidate_requisitions,
                               NULL);

      gtk_widget_queue_resize (GTK_WIDGET (tree_container));
    }
}

void
gtk_tree_container_set_tree_pad (GtkTreeContainer *tree_container,
                         guint             tree_pad)
{
  g_return_if_fail (tree_container != NULL);
  g_return_if_fail (GTK_IS_TREE_CONTAINER (tree_container));


  if (tree_container->tree_pad != tree_pad)
    {
      tree_container->tree_pad = tree_pad;

      gtk_tree_container_foreach_node (tree_container,
                               (GtkCallback)gtk_tree_container_node_invalidate_requisitions,
                               NULL);

      gtk_widget_queue_resize (GTK_WIDGET (tree_container));
    }
}

void
gtk_tree_container_set_node_pad (GtkTreeContainer *tree_container,
                         guint             node_pad)
{
  g_return_if_fail (tree_container != NULL);
  g_return_if_fail (GTK_IS_TREE_CONTAINER (tree_container));


  if (tree_container->node_pad != node_pad)
    {
      tree_container->node_pad = node_pad;

      gtk_tree_container_foreach_node (tree_container,
                               (GtkCallback)gtk_tree_container_node_invalidate_requisitions,
                               NULL);

      gtk_widget_queue_resize (GTK_WIDGET (tree_container));
    }
}


static GtkTreeContainerNode*
gtk_tree_container_find_node_at_pos_recurse (GtkTreeContainer     *tree_container,
                                   GtkTreeContainerNode *node,
                                   gint                  x,
                                   gint                  y)
{
  GtkTreeContainerNode *tree_node_at_pos;
  GtkAllocation *allocation;


  tree_node_at_pos = NULL;

  allocation = &(node->widget->allocation);

  if (tree_node_at_pos == NULL)
    {
      if (GTK_WIDGET_VISIBLE (node->widget) &&
        ((x >= (allocation->x))                     &&
         (x <= (allocation->x + allocation->width)) &&
         (y >= (allocation->y))                     &&
         (y <= (allocation->y + allocation->height))))
      {
        tree_node_at_pos = node;
      }
    }

  if (tree_node_at_pos == NULL)
    {
      gint top_node_offset_x;
      gint top_node_offset_y;
      gint descendants_offset_x;
      gint descendants_offset_y;
      gint descendants_width;
      gint descendants_height;
      gint descendants_x;
      gint descendants_y;

      get_top_node_offset (tree_container,
                     node,
                     &top_node_offset_x,
                     &top_node_offset_y);
      get_descendants_bbox_offset (tree_container,
                           node,
                           &descendants_offset_x,
                           &descendants_offset_y);
      get_descendants_bbox_size (tree_container,
                         node,
                         &descendants_width,
                         &descendants_height);

      descendants_x = (allocation->x - top_node_offset_x + descendants_offset_x);
      descendants_y = (allocation->y - top_node_offset_y + descendants_offset_y);

      if ((x >= (descendants_x))                     &&
        (x <= (descendants_x + descendants_width)) &&
        (y >= (descendants_y))                     &&
        (y <= (descendants_y + descendants_height)))
      {
        GNode *tmp;
        GtkTreeContainerNode *tmp_node;

        tmp = g_node_children (node->gnode);
        while (tmp != NULL)
          {
            tmp_node = tmp->data;
            tmp = g_node_next_sibling (tmp);

            tree_node_at_pos
            = gtk_tree_container_find_node_at_pos_recurse (tree_container,
                                                 tmp_node,
                                                 x,
                                                 y);
            if (tree_node_at_pos != NULL)
            {
              break;
            }
          }
      }
    }

  return tree_node_at_pos;
}

static GtkTreeContainerNode*
gtk_tree_container_find_node_at_pos (GtkTreeContainer *tree_container,
                             gint              x,
                             gint              y)
{
  GList *tmp;
  GtkTreeContainerNode *root_node;
  GtkTreeContainerNode *tree_node_at_pos;


  tree_node_at_pos = NULL;

  tmp = tree_container->root_nodes;
  while (tmp != NULL)
    {
      root_node = tmp->data;
      tmp = g_list_next (tmp);


      tree_node_at_pos
      = gtk_tree_container_find_node_at_pos_recurse (tree_container,
                                           root_node,
                                           x,
                                           y);

      if (tree_node_at_pos != NULL)
      {
        break;
      }
    }


  return tree_node_at_pos;
}

GtkWidget*
gtk_tree_container_get_node_widget_at_pos (GtkTreeContainer *tree_container,
                                 gint              x,
                                 gint              y)
{
  GtkTreeContainerNode *node;
  GtkWidget *widget_at_pos;

  node = gtk_tree_container_find_node_at_pos (tree_container, x, y);

  if (node != NULL)
    {
      widget_at_pos = node->widget;
    }
  else
    {
      widget_at_pos = NULL;
    }

  return widget_at_pos;
}

static void
gtk_tree_container_node_invalidate_requisitions (GtkTreeContainerNode *node)
{
  if (node != NULL)
    {
      node->children_requisition_is_valid = FALSE;
      node->descendants_requisition_is_valid = FALSE;
    }
}

static void
gtk_tree_container_node_ancestors_invalidate_requisitions (GtkTreeContainerNode *node)
{
  g_node_forall_ancestors_data (node->gnode,
                        (GFunc)gtk_tree_container_node_invalidate_requisitions,
                        NULL);
}

static void
gtk_tree_container_node_width_max (GtkTreeContainerNode *node,
                           gint                 *max_width)
{
  GtkRequisition node_requisition;


#if 0
  gtk_widget_get_child_requisition (GTK_WIDGET (node->widget),
                            &node_requisition);
#else
  gtk_widget_size_request (GTK_WIDGET (node->widget),
                     &node_requisition);
#endif

#if 0
  if (GTK_WIDGET_VISIBLE (node->widget))
    *max_width = MAX (*max_width, node_requisition.width);
#else
  if (   GTK_WIDGET_VISIBLE (node->widget)
      && !GTK_TREE_CONTAINER_NODE_IS_LEAF (node))
    *max_width = MAX (*max_width, node_requisition.width);
#endif
}







static GtkTreeContainerNode *
gtk_tree_container_find_node_by_widget (GtkTreeContainer *tree_container,
                              GtkWidget        *node_widget)
{
  GtkTreeContainerNode *node;

  node = g_hash_table_lookup (tree_container->node_ht,
                        node_widget);

  return node;
}



void
gtk_tree_container_append_node_widget (GtkTreeContainer *tree_container,
                               GtkWidget        *widget,
                               GtkWidget        *parent)
{
  GtkTreeContainerNode *node;
  GtkTreeContainerNode *parent_node;

  g_return_if_fail (tree_container != NULL);
  g_return_if_fail (GTK_IS_TREE_CONTAINER (tree_container));
  g_return_if_fail (widget != NULL);

  node = gtk_tree_container_node_new (tree_container,
                              widget);
  parent_node = gtk_tree_container_find_node_by_widget (tree_container,
                                          parent);

  gtk_tree_container_node_set_parent (tree_container,
                              node,
                              parent_node);

  gtk_tree_container_attach_node (tree_container,
                          node);
}

void
gtk_tree_container_remove_node_widget (GtkTreeContainer *tree_container,
                               GtkWidget        *widget)
{
  GtkTreeContainerNode *node;

  g_return_if_fail (tree_container != NULL);
  g_return_if_fail (GTK_IS_TREE_CONTAINER (tree_container));
  g_return_if_fail (widget != NULL);

  node = gtk_tree_container_find_node_by_widget (tree_container,
                                     widget);

  gtk_tree_container_node_unlink (tree_container, node);
  gtk_tree_container_node_children_reparent (tree_container, node, NULL);

  gtk_tree_container_detach_node (tree_container, node);
}

static void
gtk_tree_container_attach_node (GtkTreeContainer     *tree_container,
                        GtkTreeContainerNode *node)
{
  g_hash_table_insert (tree_container->node_ht,
                   node->widget, node);


  gtk_widget_set_parent (GTK_WIDGET (node->widget), GTK_WIDGET (tree_container));
  gtk_widget_queue_resize (GTK_WIDGET (tree_container));
}

static void
gtk_tree_container_detach_node (GtkTreeContainer     *tree_container,
                        GtkTreeContainerNode *node)
{
  gboolean   was_visible;

  was_visible = GTK_WIDGET_VISIBLE (node->widget);

  gtk_widget_unparent (GTK_WIDGET (node->widget));

  if (was_visible)
    gtk_widget_queue_resize (GTK_WIDGET (tree_container));


  g_hash_table_remove (tree_container->node_ht,
                   node->widget);


  gtk_tree_container_node_destroy (node);
}

static void
gtk_tree_container_node_init (GtkTreeContainerNode *node)
{
  node->gnode = g_node_new ((gpointer)node);

  node->widget = NULL;

  node->node_requisition.width = 0;
  node->node_requisition.height = 0;
  node->children_requisition.width = 0;
  node->children_requisition.height = 0;
  node->descendants_requisition.width = 0;
  node->descendants_requisition.height = 0;

  node->children_requisition_is_valid = FALSE;
  node->descendants_requisition_is_valid = FALSE;

  node->resized = FALSE;

  node->expanded = TRUE;
}

static GtkTreeContainerNode *
gtk_tree_container_node_new (GtkTreeContainer *tree_container,
                       GtkWidget        *node_widget)
{
  GtkTreeContainerNode *node;

  node = g_new (GtkTreeContainerNode, 1);

  gtk_tree_container_node_init (node);

  node->widget = node_widget;

  return node;
}

static void
gtk_tree_container_node_destroy (GtkTreeContainerNode *node)
{
  g_node_destroy (node->gnode);
  g_free (node);
}

static void
gtk_tree_container_append_root_node (GtkTreeContainer     *tree_container,
                             GtkTreeContainerNode *root_node)
{
  tree_container->root_nodes = g_list_append (tree_container->root_nodes,
                                    root_node);
}

static void
gtk_tree_container_remove_root_node (GtkTreeContainer     *tree_container,
                             GtkTreeContainerNode *root_node)
{
  tree_container->root_nodes = g_list_remove (tree_container->root_nodes,
                                    root_node);
}

static void
gtk_tree_container_node_set_parent (GtkTreeContainer     *tree_container,
                            GtkTreeContainerNode *node,
                            GtkTreeContainerNode *parent_node)
{
  if (parent_node != NULL)
    {
      g_node_append (parent_node->gnode,
                 node->gnode);
    }
  else
    {
      gtk_tree_container_append_root_node (tree_container,
                                 node);
    }

  gtk_tree_container_node_ancestors_invalidate_requisitions (node);
}

static void
gtk_tree_container_node_unlink (GtkTreeContainer     *tree_container,
                        GtkTreeContainerNode *node)
{
  gtk_tree_container_node_ancestors_invalidate_requisitions (node);


  if (G_NODE_IS_ROOT (node->gnode))
    {
      gtk_tree_container_remove_root_node (tree_container,
                                 node);
    }

  g_node_unlink (node->gnode);
}

static void
gtk_tree_container_node_reparent (GtkTreeContainer     *tree_container,
                          GtkTreeContainerNode *node,
                          GtkTreeContainerNode *parent_node)
{
  gtk_tree_container_node_unlink (tree_container, node);
  gtk_tree_container_node_set_parent (tree_container, node, parent_node);
}

static void
gtk_tree_container_node_children_reparent (GtkTreeContainer     *tree_container,
                                 GtkTreeContainerNode *node,
                                 GtkTreeContainerNode *parent_node)
{
  GNode *tmp;
  GtkTreeContainerNode *tmp_node;


  tmp = g_node_first_child (node->gnode);
  while (tmp)
    {
      tmp_node = tmp->data;
      tmp = g_node_next_sibling (tmp);

      gtk_tree_container_node_reparent (tree_container,
                              tmp_node,
                              parent_node);
    }
}



static void
gtk_tree_container_node_check_resized (GtkTreeContainerNode *node)
{
  GtkRequisition node_requisition;

  gtk_widget_size_request (node->widget, &node_requisition);

  if ((node->node_requisition.width  != node_requisition.width) ||
      (node->node_requisition.height != node_requisition.height))
    {
      gtk_tree_container_node_ancestors_invalidate_requisitions (node);
      gtk_widget_queue_resize (GTK_WIDGET (node->widget)->parent);
    }

  node->node_requisition.width  = node_requisition.width;
  node->node_requisition.height = node_requisition.height;
}

static void
gtk_tree_container_check_nodes_resized (GtkTreeContainer *tree_container)
{
  gtk_tree_container_foreach_node (tree_container,
                           (GtkCallback)gtk_tree_container_node_check_resized,
                           NULL);
}








/*
 *
 */

static void
get_top_node_offset (GtkTreeContainer     *tree_container,
                 GtkTreeContainerNode *node,
                 gint                 *offset_x,
                 gint                 *offset_y)
{
  switch (tree_container->tree_style)
    {
    case GTK_TREE_STYLE_STEPWISE:
      *offset_x = 0;
      *offset_y = 0;
      break;

    case GTK_TREE_STYLE_SPRAY:
      if (   GTK_TREE_CONTAINER_NODE_IS_LEAF (node)
        || !GTK_TREE_CONTAINER_NODE_IS_EXPANDED (node))
      {
        *offset_x = 0;
        *offset_y = 0;
      }
      else
      {
        gint dummy;
        gint node_height;
        gint children_height;
        gint descendants_height;
        gint children_y;


        get_children_bbox_offset (tree_container, node,
                            &dummy, &children_y);

        get_children_bbox_size (tree_container, node,
                          &dummy, &children_height);

        get_descendants_bbox_size (tree_container, node,
                             &dummy, &descendants_height);

        get_top_node_size (tree_container, node,
                       &dummy, &node_height);

        if (descendants_height > node_height)
          {
            *offset_x = 0;
            *offset_y = (children_y + ((children_height - node_height)
                               * tree_container->align));
          }
        else
          {
            *offset_x = 0;
            *offset_y = 0;
          }
      }
      break;

    default:
      g_assert_not_reached (); /* XXX */
      break;
    }
}


static void
get_top_node_size (GtkTreeContainer     *tree_container,
               GtkTreeContainerNode *node,
               gint                 *width,
               gint                 *height)
{
  GtkRequisition requisition;

#if 0
  gtk_widget_get_child_requisition (node->widget,
                            &requisition);
#else
  gtk_widget_size_request (node->widget,
                     &requisition);
#endif

  *width  = requisition.width;
  *height = requisition.height;
}

static void
get_descendants_bbox_offset (GtkTreeContainer     *tree_container,
                       GtkTreeContainerNode *node,
                       gint                 *offset_x,
                       gint                 *offset_y)
{
  switch (tree_container->tree_style)
    {
    case GTK_TREE_STYLE_STEPWISE:
      {
      gint dummy;
      gint node_height;


      get_top_node_size (tree_container, node,
                     &dummy, &node_height);

      *offset_x = tree_container->indent;
      *offset_y = node_height;

      *offset_y += tree_container->node_pad;
      }
      break;

    case GTK_TREE_STYLE_SPRAY:
      {
      gint tmp_width;
      gint tmp_height;
      gint max_width;

      gint node_width;
      gint node_height;


      get_top_node_size (tree_container, node,
                     &node_width, &node_height);
      get_descendants_bbox_size (tree_container, node,
                           &tmp_width, &tmp_height);

      if (tree_container->depth_align == TRUE)
        {
          max_width = 0;
          g_node_forall_depth_data (g_node_get_root (node->gnode),
                              g_node_depth (node->gnode),
                              (GFunc)gtk_tree_container_node_width_max,
                              &max_width);
          *offset_x = (tree_container->indent + max_width);
        }
      else
        {
          *offset_x = (tree_container->indent + node_width);
        }

      if (node_height > tmp_height)
        {
#if 0
          *offset_y = (node_height - tmp_height)
                         * (1.0 - tree_container->align);
#else
          *offset_y = (node_height - tmp_height)
                         * tree_container->align;
#endif
        }
      else
        {
          *offset_y = 0;
        }
      }
      break;

    default:
      g_assert_not_reached (); /* XXX */
      break;
    }
}

static void
get_descendants_bbox_size (GtkTreeContainer     *tree_container,
                     GtkTreeContainerNode *node,
                     gint                 *width,
                     gint                 *height)
{
  gint   tmp_width;
  gint   tmp_height;
  gint   descendants_width;
  gint   descendants_height;

  if (   GTK_TREE_CONTAINER_NODE_IS_LEAF (node)
      || !GTK_TREE_CONTAINER_NODE_IS_EXPANDED (node))
    {
      *width = 0;
      *height = 0;
    }
  else
    {
      if (node->descendants_requisition_is_valid == TRUE)
      {
        descendants_width = node->descendants_requisition.width;
        descendants_height = node->descendants_requisition.height;
      }
      else
      {
        GNode *tmp;
        GtkTreeContainerNode *tmp_node;

        descendants_width = 0;
        descendants_height = 0;

        tmp = g_node_first_child (node->gnode);
        while (tmp != NULL)
          {
            tmp_node = tmp->data;
            tmp = g_node_next_sibling (tmp);

            get_subtree_bbox_size (tree_container,
                             tmp_node,
                             &tmp_width,
                             &tmp_height);

            descendants_height += (((descendants_height>0) && (tmp_height>0))
                             ? tree_container->node_pad
                             : 0);

            descendants_width = MAX (descendants_width, tmp_width);
            descendants_height += tmp_height;
          }

        node->descendants_requisition_is_valid = TRUE;
        node->descendants_requisition.width = descendants_width;
        node->descendants_requisition.height = descendants_height;
      }

      *width = descendants_width;
      *height = descendants_height;
    }
}

static void
get_children_bbox_offset (GtkTreeContainer     *tree_container,
                    GtkTreeContainerNode *node,
                    gint                 *offset_x,
                    gint                 *offset_y)
{
  GNode *tmp;
  GtkTreeContainerNode *tmp_node;
  gint top_node_width;
  gint top_node_height;
  gint descendants_bbox_width;
  gint descendants_bbox_height;
  gint descendants_bbox_offset_x;
  gint descendants_bbox_offset_y;
  gint tmp_width;
  gint tmp_height;
  gint tmp_offset_x;
  gint tmp_offset_y;


  get_top_node_size (tree_container, node,
                 &top_node_width, &top_node_height);
  get_descendants_bbox_size   (tree_container, node,
                         &descendants_bbox_width, &descendants_bbox_height);
  get_descendants_bbox_offset (tree_container, node,
                         &descendants_bbox_offset_x, &descendants_bbox_offset_y);


  tmp_node = NULL;

  tmp = g_node_first_child (node->gnode);
  while (tmp != NULL)
    {
      tmp_node = tmp->data;
      tmp = g_node_next_sibling (tmp);

      get_subtree_bbox_size (tree_container,
                       tmp_node,
                       &tmp_width,
                       &tmp_height);

      if (tmp_height > 0)
      break;
    }

  get_top_node_offset (tree_container, tmp_node,
                   &tmp_offset_x, &tmp_offset_y);


  if (top_node_height < descendants_bbox_height)
    {
      *offset_x = descendants_bbox_offset_x;
      *offset_y = (tmp_offset_y);
    }
  else
    {
# if 0
      *offset_x = descendants_bbox_offset_x;
      *offset_y = (tmp_offset_y +
               ((top_node_height - descendants_bbox_height) * (1.0 - tree_container->align)));
# else
      *offset_x = descendants_bbox_offset_x;
      *offset_y = (tmp_offset_y +
               ((top_node_height - descendants_bbox_height) * tree_container->align));
# endif
    }
}

static void
get_children_bbox_size (GtkTreeContainer     *tree_container,
                  GtkTreeContainerNode *node,
                  gint                 *width,
                  gint                 *height)
{
  gint children_width;
  gint children_height;

  if (   GTK_TREE_CONTAINER_NODE_IS_LEAF (node)
      || !GTK_TREE_CONTAINER_NODE_IS_EXPANDED (node))
    {
      children_width = 0;
      children_height = 0;
    }
  else
    {
      if (node->children_requisition_is_valid == TRUE)
      {
        children_width = node->children_requisition.width;
        children_height = node->children_requisition.height;
      }
      else
      {
        GNode *tmp;
        GtkTreeContainerNode *tmp_node;
        GtkTreeContainerNode *first_node;
        GtkTreeContainerNode *last_node;
        gint   child_width;
        gint   child_height;

        first_node = NULL;
        last_node = NULL;
        
        tmp = g_node_first_child (node->gnode);
        while (tmp != NULL)
          {
            tmp_node = tmp->data;
            tmp = g_node_next_sibling (tmp);

            get_top_node_size (tree_container,
                         tmp_node,
                         &child_width, &child_height);

            if (child_height > 0)
            {
              first_node = tmp_node;
              break;
            }
          }

        tmp = g_node_last_child (node->gnode);
        while (tmp != NULL)
          {
            tmp_node = tmp->data;
            tmp = g_node_prev_sibling (tmp);

            get_top_node_size (tree_container,
                         tmp_node,
                         &child_width, &child_height);

            if (child_height > 0)
            {
              last_node = tmp_node;
              break;
            }
          }


        if ((first_node == NULL) || (last_node == NULL))
          {
            children_width  = 0;
            children_height = 0;
          }
        else if (first_node == last_node)
          {
            children_width  = child_width;
            children_height = child_height;
          }
        else
          {
            gint dummy;
            gint tmp_width;
            gint tmp_height;
            gint tmp_offset_y;

            children_width  = 0;
            children_height = 0;

            tmp = g_node_next_sibling (first_node->gnode);
            while (tmp != last_node->gnode)
            {
              tmp_node = tmp->data;
              tmp = g_node_next_sibling (tmp);

              get_top_node_size (tree_container,
                             tmp_node,
                             &tmp_width, &dummy);
              get_subtree_bbox_size (tree_container,
                               tmp_node,
                               &dummy, &tmp_height);
              if (tmp_height > 0)
                {
                  children_width = MAX (children_width, tmp_width);
                  children_height += tmp_height;

                  children_height += tree_container->node_pad;
                }
            }

            {
            get_top_node_size (tree_container,
                           first_node,
                           &tmp_width, &dummy);
            get_top_node_offset (tree_container,
                             first_node,
                             &dummy, &tmp_offset_y);
            get_subtree_bbox_size (tree_container,
                               first_node,
                               &dummy, &tmp_height);

            children_width = MAX (children_width, tmp_width);
            children_height += (tmp_height - tmp_offset_y);

            children_height += tree_container->node_pad;
            }

            {
            get_top_node_size (tree_container,
                           last_node,
                           &tmp_width, &tmp_height);
            get_top_node_offset (tree_container,
                             last_node,
                             &dummy, &tmp_offset_y);

            children_width = MAX (children_width, tmp_width);
            children_height += (tmp_offset_y + tmp_height);
            }
          }

        node->children_requisition_is_valid = TRUE;
        node->children_requisition.width = children_width;
        node->children_requisition.height = children_height;
      }
    }

  *width = children_width;
  *height = children_height;
}

static void
get_branch_offset (GtkTreeContainer     *tree_container,
               GtkTreeContainerNode *tree_node,
               gint                 *offset_x,
               gint                 *offset_y)
{
  switch (tree_container->tree_style)
    {
    case GTK_TREE_STYLE_STEPWISE:
      {
      gint dummy;
      gint node_height;

      get_top_node_size (tree_container, tree_node, &dummy, &node_height);

      *offset_x = 0;
      *offset_y = node_height;
      }
      break;

    case GTK_TREE_STYLE_SPRAY:
      {
      gint dummy;
      gint node_width;
      gint children_offset_y;

      get_top_node_size (tree_container, tree_node, &node_width, &dummy);
      get_children_bbox_offset (tree_container, tree_node,
                          &dummy, &children_offset_y);

      *offset_x = node_width;
      *offset_y = children_offset_y;
      }
      break;

    default:
      break;
    }
}

static void
get_branch_size (GtkTreeContainer     *tree_container,
             GtkTreeContainerNode *tree_node,
             gint                 *width,
             gint                 *height)
{
  switch (tree_container->tree_style)
    {
    case GTK_TREE_STYLE_STEPWISE:
      {
      gint tmp_width;
      gint tmp_height;
  
      get_children_bbox_size (tree_container,
                        tree_node,
                        &tmp_width,
                        &tmp_height);

      *width  = (tree_container->indent);
      *height = (tree_container->node_pad + tmp_height);
      }
      break;

    case GTK_TREE_STYLE_SPRAY:
      {
      gint tmp_top_width;
      gint tmp_top_height;
      gint tmp_x;
      gint tmp_y;
      gint tmp_width;
      gint tmp_height;
      gint dummy;

      get_children_bbox_size (tree_container, tree_node,
                        &dummy, &tmp_height);
      get_top_node_size (tree_container, tree_node,
                     &tmp_top_width, &dummy);

#if 1
      get_children_bbox_offset (tree_container, tree_node,
                          &tmp_x, &dummy);
#else
      /* XXX */
      get_descendants_bbox_offset (tree_container, tree_node,
                             &tmp_x, &dummy);
#endif


      *width  = (tmp_x - tmp_top_width);
      *height = (tmp_height);
      }
      break;

    default:
      break;
    }
}

static void
get_subtree_bbox_size (GtkTreeContainer     *tree_container,
                   GtkTreeContainerNode *tree_node,
                   gint                 *width,
                   gint                 *height)
{
  gint tmp_width;
  gint tmp_height;
  gint node_offset_x;
  gint node_offset_y;
  gint node_width;
  gint node_height;
  gint descendants_offset_x;
  gint descendants_offset_y;
  gint descendants_width;
  gint descendants_height;



  get_top_node_offset (tree_container, tree_node,
                   &node_offset_x, &node_offset_y);
  get_top_node_size (tree_container, tree_node,
                 &node_width, &node_height);

  tmp_width  = (node_offset_x + node_width);
  tmp_height = (node_offset_y + node_height);

  if (   !GTK_TREE_CONTAINER_NODE_IS_LEAF (tree_node)
      && GTK_TREE_CONTAINER_NODE_IS_EXPANDED (tree_node))
    {
      get_descendants_bbox_offset (tree_container, tree_node,
                           &descendants_offset_x,
                           &descendants_offset_y);
      get_descendants_bbox_size (tree_container, tree_node,
                         &descendants_width,
                         &descendants_height);

      tmp_width  = MAX (tmp_width,
                  (descendants_offset_x + descendants_width));
      tmp_height = MAX (tmp_height,
                  (descendants_offset_y + descendants_height));
    }

  *width = tmp_width;
  *height = tmp_height;
}

static void
make_top_node_allocation (GtkTreeContainer     *tree_container,
                    GtkTreeContainerNode *tree_node,
                    GtkAllocation        *allocation,
                    GtkAllocation        *child_allocation)
{
  gint offset_x;
  gint offset_y;
  gint width;
  gint height;


  get_top_node_offset (tree_container, tree_node, &offset_x, &offset_y);
  get_top_node_size (tree_container, tree_node, &width, &height);

  child_allocation->x      = (allocation->x + offset_x);
  child_allocation->y      = (allocation->y + offset_y);
  child_allocation->width  = width;
  child_allocation->height = height;
}

static void
make_descendants_bbox_allocation (GtkTreeContainer     *tree_container,
                          GtkTreeContainerNode *tree_node,
                          GtkAllocation        *allocation,
                          GtkAllocation        *descendants_allocation)
{
  gint offset_x;
  gint offset_y;
  gint width;
  gint height;

  get_descendants_bbox_offset (tree_container, tree_node,
                         &offset_x, &offset_y);
  get_descendants_bbox_size (tree_container, tree_node,
                       &width, &height);

  descendants_allocation->x      = (allocation->x + offset_x);
  descendants_allocation->y      = (allocation->y + offset_y);
  descendants_allocation->width  = width;
  descendants_allocation->height = height;
}

Generated by  Doxygen 1.6.0   Back to index