[gtk+/native-layout-incubator-2] Make treeviews/columns leverage height-for-width cell renderers



commit cff0582dc0dc19c5be12573af54229be388a2b47
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date:   Wed Jun 30 11:28:21 2010 -0400

    Make treeviews/columns leverage height-for-width cell renderers
    
    For columns; these methods are added to replace
    gtk_tree_view_column_get_size():
        gtk_tree_view_column_get_natural_width()
        gtk_tree_view_column_get_height_for_width()
        gtk_tree_view_column_allocate_width()
    
    This code includes a nasty workaround to monitor a possible
    parent scrolled window's allocation - this code protects against
    a possible feedback loop between the height-for-width wrapping
    treeview and scrolled window (the fight occurs because the scrolled
    window allocates a new width when the content height changes to
    show/hide the scrollbars).

 gtk/gtktreeprivate.h    |   21 ++
 gtk/gtktreeview.c       |  653 ++++++++++++++++++++++++++-------------
 gtk/gtktreeviewcolumn.c |  784 ++++++++++++++++++++++++++++-------------------
 gtk/gtktreeviewcolumn.h |   12 +
 4 files changed, 940 insertions(+), 530 deletions(-)
---
diff --git a/gtk/gtktreeprivate.h b/gtk/gtktreeprivate.h
index e8887e0..d1aa9df 100644
--- a/gtk/gtktreeprivate.h
+++ b/gtk/gtktreeprivate.h
@@ -77,6 +77,8 @@ enum
   */
 #define TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER(tree_view) (10*TREE_VIEW_HEADER_HEIGHT(tree_view))
 
+#define GTK_TREE_VIEW_COLUMN_GET_PRIVATE(column) (G_TYPE_INSTANCE_GET_PRIVATE ((column), GTK_TYPE_TREE_VIEW_COLUMN, GtkTreeViewColumnPrivate))
+
 typedef struct _GtkTreeViewColumnReorder GtkTreeViewColumnReorder;
 struct _GtkTreeViewColumnReorder
 {
@@ -99,6 +101,14 @@ struct _GtkTreeViewPrivate
   gint width;
   gint height;
 
+  /* Track parent scrolled window size to 
+   * avoid feed back loops with scrollbar allocations
+   * and h4w cell renderers in the layout.
+   */
+  gint prev_parent_width;
+  gint prev_parent_height;
+  gint consecutive_allocations;
+
   /* Adjustments */
   GtkAdjustment *hadjustment;
   GtkAdjustment *vadjustment;
@@ -298,6 +308,16 @@ struct _GtkTreeViewPrivate
 
   /* Whether our key press handler is to avoid sending an unhandled binding to the search entry */
   guint search_entry_avoid_unhandled_binding : 1;
+
+  /* Mark dirty state for resizes that originate from changes in
+   * the full rendered content size, from resizes that originate
+   * from */
+  guint content_size_dirty : 1;
+};
+
+struct _GtkTreeViewColumnPrivate
+{
+  gint natural_width;
 };
 
 #ifdef __GNUC__
@@ -457,6 +477,7 @@ void		  _gtk_tree_view_column_cell_draw_focus  (GtkTreeViewColumn  *tree_column,
 							  const GdkRectangle *expose_area,
 							  guint               flags);
 void		  _gtk_tree_view_column_cell_set_dirty	 (GtkTreeViewColumn  *tree_column,
+							  gboolean            recalculate_width,
 							  gboolean            install_handler);
 void              _gtk_tree_view_column_get_neighbor_sizes (GtkTreeViewColumn *column,
 							    GtkCellRenderer   *cell,
diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c
index 387bdde..a7846c8 100644
--- a/gtk/gtktreeview.c
+++ b/gtk/gtktreeview.c
@@ -27,6 +27,7 @@
 #include "gtktreednd.h"
 #include "gtktreeprivate.h"
 #include "gtkcellrenderer.h"
+#include "gtksizerequest.h"
 #include "gtkmain.h"
 #include "gtkmarshalers.h"
 #include "gtkbuildable.h"
@@ -43,6 +44,7 @@
 #include "gtkframe.h"
 #include "gtktreemodelsort.h"
 #include "gtktooltip.h"
+#include "gtkscrolledwindow.h"
 #include "gtkprivate.h"
 #include "gtkalias.h"
 
@@ -165,8 +167,6 @@ static void     gtk_tree_view_destroy              (GtkObject        *object);
 static void     gtk_tree_view_realize              (GtkWidget        *widget);
 static void     gtk_tree_view_unrealize            (GtkWidget        *widget);
 static void     gtk_tree_view_map                  (GtkWidget        *widget);
-static void     gtk_tree_view_size_request         (GtkWidget        *widget,
-						    GtkRequisition   *requisition);
 static void     gtk_tree_view_size_allocate        (GtkWidget        *widget,
 						    GtkAllocation    *allocation);
 static gboolean gtk_tree_view_expose               (GtkWidget        *widget,
@@ -299,7 +299,8 @@ static gboolean do_validate_rows         (GtkTreeView *tree_view,
 					  gboolean     size_request);
 static gboolean validate_rows            (GtkTreeView *tree_view);
 static gboolean presize_handler_callback (gpointer     data);
-static void     install_presize_handler  (GtkTreeView *tree_view);
+static void     install_presize_handler  (GtkTreeView *tree_view,
+					  gboolean     content_dirty);
 static void     install_scroll_sync_handler (GtkTreeView *tree_view);
 static void     gtk_tree_view_set_top_row   (GtkTreeView *tree_view,
 					     GtkTreePath *path,
@@ -465,6 +466,15 @@ static GtkTreeViewColumn *gtk_tree_view_get_drop_column (GtkTreeView       *tree
 							 GtkTreeViewColumn *column,
 							 gint               drop_position);
 
+/* GtkSizeRequest */
+static void gtk_tree_view_size_request_init   (GtkSizeRequestIface *iface);
+static void gtk_tree_view_get_width           (GtkSizeRequest *widget,
+					       gint           *minimum_size,
+					       gint           *natural_size);
+static void gtk_tree_view_get_height          (GtkSizeRequest *widget,
+					       gint           *minimum_size,
+					       gint           *natural_size);
+
 /* GtkBuildable */
 static void     gtk_tree_view_buildable_add_child          (GtkBuildable      *tree_view,
 							    GtkBuilder        *builder,
@@ -489,7 +499,10 @@ static guint tree_view_signals [LAST_SIGNAL] = { 0 };
 
 G_DEFINE_TYPE_WITH_CODE (GtkTreeView, gtk_tree_view, GTK_TYPE_CONTAINER,
 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
-						gtk_tree_view_buildable_init))
+						gtk_tree_view_buildable_init)
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_SIZE_REQUEST,
+                                                gtk_tree_view_size_request_init))
+
 
 static void
 gtk_tree_view_class_init (GtkTreeViewClass *class)
@@ -519,7 +532,6 @@ gtk_tree_view_class_init (GtkTreeViewClass *class)
   widget_class->map = gtk_tree_view_map;
   widget_class->realize = gtk_tree_view_realize;
   widget_class->unrealize = gtk_tree_view_unrealize;
-  widget_class->size_request = gtk_tree_view_size_request;
   widget_class->size_allocate = gtk_tree_view_size_allocate;
   widget_class->button_press_event = gtk_tree_view_button_press;
   widget_class->button_release_event = gtk_tree_view_button_release;
@@ -1866,7 +1878,7 @@ gtk_tree_view_realize (GtkWidget *widget)
   gtk_tree_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
   gtk_tree_view_set_enable_tree_lines (tree_view, tree_view->priv->tree_lines_enabled);
 
-  install_presize_handler (tree_view); 
+  install_presize_handler (tree_view, TRUE); 
 }
 
 static void
@@ -2046,37 +2058,6 @@ gtk_tree_view_update_size (GtkTreeView *tree_view)
     tree_view->priv->height = tree_view->priv->tree->root->offset;
 }
 
-static void
-gtk_tree_view_size_request (GtkWidget      *widget,
-			    GtkRequisition *requisition)
-{
-  GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
-  GList *tmp_list;
-
-  /* we validate some rows initially just to make sure we have some size. 
-   * In practice, with a lot of static lists, this should get a good width.
-   */
-  do_validate_rows (tree_view, FALSE);
-  gtk_tree_view_size_request_columns (tree_view);
-  gtk_tree_view_update_size (GTK_TREE_VIEW (widget));
-
-  requisition->width = tree_view->priv->width;
-  requisition->height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
-
-  tmp_list = tree_view->priv->children;
-
-  while (tmp_list)
-    {
-      GtkTreeViewChild *child = tmp_list->data;
-      GtkRequisition child_requisition;
-
-      tmp_list = tmp_list->next;
-
-      if (gtk_widget_get_visible (child->widget))
-        gtk_widget_size_request (child->widget, &child_requisition);
-    }
-}
-
 static int
 gtk_tree_view_calculate_width_before_expander (GtkTreeView *tree_view)
 {
@@ -2185,6 +2166,28 @@ gtk_tree_view_get_real_requested_width_from_column (GtkTreeView       *tree_view
   return real_requested_width;
 }
 
+static gint
+gtk_tree_view_get_real_natural_width_from_column (GtkTreeView       *tree_view,
+                                                  GtkTreeViewColumn *column)
+{
+  GtkTreeViewColumnPrivate *column_priv;
+  gint button_natural_width;
+  gint column_natural_width;
+
+  column_priv = GTK_TREE_VIEW_COLUMN_GET_PRIVATE (column);
+  column_natural_width = column_priv->natural_width;
+
+  if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE))
+    {
+      gtk_size_request_get_width (GTK_SIZE_REQUEST (column->button),
+				  NULL, &button_natural_width);
+
+      column_natural_width = MAX (column_natural_width, button_natural_width);
+    }
+
+  return column_natural_width;
+}
+
 /* GtkWidget::size_allocate helper */
 static void
 gtk_tree_view_size_allocate_columns (GtkWidget *widget,
@@ -2195,15 +2198,23 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget,
   GtkTreeViewColumn *column;
   GtkAllocation allocation;
   gint width = 0;
-  gint extra, extra_per_column, extra_for_last;
-  gint full_requested_width = 0;
+  gint extra, extra_per_column;
+  gint column_width, i, horizontal_separator;
+  gint grid_line_width, draw_vgrid_lines;
   gint number_of_expand_columns = 0;
   gboolean column_changed = FALSE;
   gboolean rtl;
   gboolean update_expand;
+  GArray *array;
+  GtkRequestedSize *sizes;
   
   tree_view = GTK_TREE_VIEW (widget);
 
+  gtk_widget_style_get (widget,
+			"horizontal-separator", &horizontal_separator,
+			"grid-line-width", &grid_line_width,
+			NULL);
+
   for (last_column = g_list_last (tree_view->priv->columns);
        last_column && !(GTK_TREE_VIEW_COLUMN (last_column->data)->visible);
        last_column = last_column->prev)
@@ -2216,25 +2227,56 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget,
        first_column = first_column->next)
     ;
 
+  draw_vgrid_lines =
+    tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_VERTICAL
+    || tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH;
+
   allocation.y = 0;
   allocation.height = tree_view->priv->header_height;
 
   rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
 
-  /* find out how many extra space and expandable columns we have */
+  extra = widget->allocation.width;
+
+  array = g_array_new (0, TRUE, sizeof (GtkRequestedSize));
+
   for (list = tree_view->priv->columns; list != last_column->next; list = list->next)
     {
+      GtkRequestedSize requested;
+      gint padding;
+
       column = (GtkTreeViewColumn *)list->data;
 
       if (!column->visible)
 	continue;
 
-      full_requested_width += gtk_tree_view_get_real_requested_width_from_column (tree_view, column);
+      /* Calculate padding for this column and store it in our client pointer for the next loop */
+      padding = horizontal_separator;
+      if (draw_vgrid_lines)
+        {
+	  if (list == first_column || list == last_column)
+	    padding += grid_line_width / 2.0;
+	  else
+	    padding += grid_line_width;
+	}
+
+      requested.data         = GINT_TO_POINTER (padding);
+      requested.minimum_size = gtk_tree_view_get_real_requested_width_from_column (tree_view, column);
+      requested.natural_size = gtk_tree_view_get_real_natural_width_from_column (tree_view, column);
+      g_array_append_val (array, requested);
+
+      /* Subtract each column's request and padding from the remaining allocation */
+      extra -= requested.minimum_size;
+      extra -= padding;
 
       if (column->expand)
 	number_of_expand_columns++;
     }
 
+  /* Distribute as much of remaining 'size' as possible before sharing expand space */
+  sizes = (GtkRequestedSize *)array->data;
+  extra = gtk_distribute_natural_allocation (MAX (extra, 0), array->len, sizes);
+
   /* Only update the expand value if the width of the widget has changed,
    * or the number of expand columns has changed, or if there are no expand
    * columns, or if we didn't have an size-allocation yet after the
@@ -2247,21 +2289,8 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget,
 
   tree_view->priv->post_validation_flag = FALSE;
 
-  if (!update_expand)
-    {
-      extra = tree_view->priv->last_extra_space;
-      extra_for_last = MAX (widget->allocation.width - full_requested_width - extra, 0);
-    }
-  else
-    {
-      extra = MAX (widget->allocation.width - full_requested_width, 0);
-      extra_for_last = 0;
-
-      tree_view->priv->last_extra_space = extra;
-    }
-
   if (number_of_expand_columns > 0)
-    extra_per_column = extra/number_of_expand_columns;
+    extra_per_column = extra / number_of_expand_columns;
   else
     extra_per_column = 0;
 
@@ -2271,12 +2300,12 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget,
       tree_view->priv->last_number_of_expand_columns = number_of_expand_columns;
     }
 
-  for (list = (rtl ? last_column : first_column); 
+  for (list = (rtl ? last_column : first_column), i = 0; 
        list != (rtl ? first_column->prev : last_column->next);
        list = (rtl ? list->prev : list->next)) 
     {
-      gint real_requested_width = 0;
       gint old_width;
+      gint padding;
 
       column = list->data;
       old_width = column->width;
@@ -2297,56 +2326,43 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget,
 	  gtk_widget_size_allocate (tree_view->priv->drag_column->button,
 				    &drag_allocation);
 	  width += drag_allocation.width;
+
+	  i++;
 	  continue;
 	}
 
-      real_requested_width = gtk_tree_view_get_real_requested_width_from_column (tree_view, column);
-
-      allocation.x = width;
-      column->width = real_requested_width;
+      column_width = sizes[i].minimum_size;
+      padding      = GPOINTER_TO_INT (sizes[i].data);
 
       if (column->expand)
 	{
-	  if (number_of_expand_columns == 1)
-	    {
-	      /* We add the remander to the last column as
-	       * */
-	      column->width += extra;
-	    }
-	  else
-	    {
-	      column->width += extra_per_column;
-	      extra -= extra_per_column;
-	      number_of_expand_columns --;
-	    }
+	  column_width += extra_per_column;
 	}
-      else if (number_of_expand_columns == 0 &&
-	       list == last_column)
+      /* If no columns expand, the last one takes the remaining space */
+      else if (number_of_expand_columns == 0 && list == last_column)
 	{
-	  column->width += extra;
+	  column_width += extra;
 	}
 
-      /* In addition to expand, the last column can get even more
-       * extra space so all available space is filled up.
-       */
-      if (extra_for_last > 0 && list == last_column)
-	column->width += extra_for_last;
+      gtk_tree_view_column_allocate_width (column, column_width);
 
-      g_object_notify (G_OBJECT (column), "width");
+      /* Add padding between columns and allocate the buttons... */
+      allocation.x      = width;
+      allocation.width  = column_width + padding;
+      width            += allocation.width;
 
-      allocation.width = column->width;
-      width += column->width;
+      gtk_widget_size_allocate (column->button, &allocation);
 
-      if (column->width > old_width)
+      if (column_width > old_width)
         column_changed = TRUE;
 
-      gtk_widget_size_allocate (column->button, &allocation);
-
       if (column->window)
 	gdk_window_move_resize (column->window,
                                 allocation.x + (rtl ? 0 : allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
 				allocation.y,
                                 TREE_VIEW_DRAG_WIDTH, allocation.height);
+
+      i++; // itterate through visible columns
     }
 
   /* We change the width here.  The user might have been resizing columns,
@@ -2355,7 +2371,7 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget,
   tree_view->priv->width = width;
   if (width_changed)
     *width_changed = TRUE;
-
+  
   if (column_changed)
     gtk_widget_queue_draw (GTK_WIDGET (tree_view));
 }
@@ -2369,6 +2385,7 @@ gtk_tree_view_size_allocate (GtkWidget     *widget,
   GList *tmp_list;
   gboolean width_changed = FALSE;
   gint old_width = widget->allocation.width;
+  gboolean scroll_window_feedback = FALSE;
 
   if (allocation->width != widget->allocation.width)
     width_changed = TRUE;
@@ -2392,11 +2409,49 @@ gtk_tree_view_size_allocate (GtkWidget     *widget,
       gtk_widget_size_allocate (child->widget, &allocation);
     }
 
+  /* If we get various width allocations while our content did not change
+   * and the parent is a scrolled window and it's own allocation has not
+   * changed:
+   *
+   * Stop setting the adjustments until
+   *  a.) Treeview content size changes
+   *  b.) Parent's allocation changes
+   */
+  if (GTK_IS_SCROLLED_WINDOW (gtk_widget_get_parent (widget)))
+    {
+      GtkWidget *swindow = gtk_widget_get_parent (widget);
+      GtkAllocation salloc;
+      
+      gtk_widget_get_allocation (swindow, &salloc);
+      
+      if (!tree_view->priv->content_size_dirty &&
+	  tree_view->priv->prev_parent_width == salloc.width &&
+	  tree_view->priv->prev_parent_height == salloc.height)
+	{
+	  /* Feedback detected */
+	  if (tree_view->priv->consecutive_allocations >= 3)
+	    scroll_window_feedback = TRUE;
+	  else
+	    tree_view->priv->consecutive_allocations++;
+	}
+      else
+	tree_view->priv->consecutive_allocations = 0;
+      
+      tree_view->priv->content_size_dirty = FALSE;
+      tree_view->priv->prev_parent_width  = salloc.width;
+      tree_view->priv->prev_parent_height = salloc.height;
+    }
+  else
+    tree_view->priv->consecutive_allocations = 0;
+  
+
   /* We size-allocate the columns first because the width of the
    * tree view (used in updating the adjustments below) might change.
    */
   gtk_tree_view_size_allocate_columns (widget, &width_changed);
 
+  if (!scroll_window_feedback)
+    {
   tree_view->priv->hadjustment->page_size = allocation->width;
   tree_view->priv->hadjustment->page_increment = allocation->width * 0.9;
   tree_view->priv->hadjustment->step_increment = allocation->width * 0.1;
@@ -2438,6 +2493,7 @@ gtk_tree_view_size_allocate (GtkWidget     *widget,
   tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->page_size, tree_view->priv->height);
 
   gtk_adjustment_changed (tree_view->priv->vadjustment);
+    }
 
   /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
   if (tree_view->priv->height <= tree_view->priv->vadjustment->page_size)
@@ -2513,6 +2569,32 @@ gtk_tree_view_size_allocate (GtkWidget     *widget,
 	    gtk_widget_queue_draw (widget);
 	}
     }
+
+  /* Need to do this when the real allocated width changes
+   * and upon the first allocation */
+  if (allocation->width != old_width &&
+      !tree_view->priv->fixed_height_mode)
+    {
+      /* Have the tree recalculate height-for-width of all rows when the width changes
+       */
+      _gtk_rbtree_mark_invalid (tree_view->priv->tree);
+      for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
+	{
+	  GtkTreeViewColumn *column;
+	  
+	  column = tmp_list->data;
+	  if (! column->visible)
+	    continue;
+	  
+	  if (column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE ||
+	      column->column_type == GTK_TREE_VIEW_COLUMN_GROW_ONLY)
+	    _gtk_tree_view_column_cell_set_dirty (column, FALSE, FALSE);
+	}
+      
+      install_presize_handler (tree_view, FALSE);
+      
+      gtk_widget_queue_resize (GTK_WIDGET (tree_view));
+    }
 }
 
 /* Grabs the focus and unsets the GTK_TREE_VIEW_DRAW_KEYFOCUS flag */
@@ -4436,11 +4518,12 @@ gtk_tree_view_bin_expose (GtkWidget      *widget,
 
       max_height = ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node));
 
-      cell_offset = 0;
-      highlight_x = 0; /* should match x coord of first cell */
+      cell_offset         = 0;
+      highlight_x         = 0; /* should match x coord of first cell */
       expander_cell_width = 0;
 
-      background_area.y = y_offset + event->area.y;
+      /* 'background_area' itterates vertically over rows in the exposed area */
+      background_area.y      = y_offset + event->area.y;
       background_area.height = max_height;
 
       flags = 0;
@@ -4482,62 +4565,68 @@ gtk_tree_view_bin_expose (GtkWidget      *widget,
 	  if (!column->visible)
             continue;
 
-	  if (cell_offset > event->area.x + event->area.width ||
-	      cell_offset + column->width < event->area.x)
-	    {
-	      cell_offset += column->width;
-	      continue;
-	    }
-
-          if (column->show_sort_indicator)
-	    flags |= GTK_CELL_RENDERER_SORTED;
-          else
-            flags &= ~GTK_CELL_RENDERER_SORTED;
-
-	  if (cursor == node)
-            flags |= GTK_CELL_RENDERER_FOCUSED;
-          else
-            flags &= ~GTK_CELL_RENDERER_FOCUSED;
-
-	  background_area.x = cell_offset;
+	  /* Adjust 'background_area' for this column's portion of this row */
+	  background_area.x     = cell_offset;
 	  background_area.width = column->width;
 
-          cell_area = background_area;
-          cell_area.y += vertical_separator / 2;
-          cell_area.x += horizontal_separator / 2;
+	  /* Create 'cell_area' to render this column inside this row */
+          cell_area         = background_area;
+          cell_area.y      += vertical_separator / 2;
+          cell_area.x      += horizontal_separator / 2;
           cell_area.height -= vertical_separator;
-	  cell_area.width -= horizontal_separator;
 
+	  /* Calculate padding */
 	  if (draw_vgrid_lines)
 	    {
 	      if (list == first_column)
 	        {
-		  cell_area.width -= grid_line_width / 2;
+		  background_area.width += grid_line_width / 2;
 		}
 	      else if (list == last_column)
 	        {
 		  cell_area.x += grid_line_width / 2;
-		  cell_area.width -= grid_line_width / 2;
+		  background_area.width += grid_line_width / 2;
 		}
 	      else
 	        {
 	          cell_area.x += grid_line_width / 2;
-	          cell_area.width -= grid_line_width;
+		  background_area.width += grid_line_width;
 		}
 	    }
-
+	  
 	  if (draw_hgrid_lines)
 	    {
 	      cell_area.y += grid_line_width / 2;
 	      cell_area.height -= grid_line_width;
 	    }
 
+	  /* Now that we've deduced the real rendering area,
+	   * do a couple of assertions to make sure we're rendereing
+	   * a relevant area. 
+	   */
+	  if (cell_offset > event->area.x + event->area.width ||
+	      cell_offset + column->width < event->area.x)
+	    {
+	      cell_offset += background_area.width;
+	      continue;
+	    }
+
 	  if (cairo_region_contains_rectangle (event->region, &background_area) == CAIRO_REGION_OVERLAP_OUT)
 	    {
-	      cell_offset += column->width;
+	      cell_offset += background_area.width;
 	      continue;
 	    }
 
+          if (column->show_sort_indicator)
+	    flags |= GTK_CELL_RENDERER_SORTED;
+          else
+            flags &= ~GTK_CELL_RENDERER_SORTED;
+
+	  if (cursor == node)
+            flags |= GTK_CELL_RENDERER_FOCUSED;
+          else
+            flags &= ~GTK_CELL_RENDERER_FOCUSED;
+
 	  gtk_tree_view_column_cell_set_cell_data (column,
 						   tree_view->priv->model,
 						   &iter,
@@ -4677,59 +4766,42 @@ gtk_tree_view_bin_expose (GtkWidget      *widget,
                */
               highlight_x = cell_area.x;
 	      expander_cell_width = cell_area.width;
-
-	      if (is_separator)
-		gtk_paint_hline (widget->style,
-				 event->window,
-				 state,
-				 &cell_area,
-				 widget,
-				 NULL,
-				 cell_area.x,
-				 cell_area.x + cell_area.width,
-				 cell_area.y + cell_area.height / 2);
-	      else
-		_gtk_tree_view_column_cell_render (column,
-						   event->window,
-						   &background_area,
-						   &cell_area,
-						   &event->area,
-						   flags);
-	      if (TREE_VIEW_DRAW_EXPANDERS(tree_view)
-		  && (node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT)
-		{
-		  if (!got_pointer)
-		    {
-		      gdk_window_get_pointer (tree_view->priv->bin_window, 
-					      &pointer_x, &pointer_y, NULL);
-		      got_pointer = TRUE;
-		    }
-
-		  gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget),
-					    tree,
-					    node,
-					    pointer_x, pointer_y);
-		}
 	    }
+
+	  /* Do the actual rendering */
+	  if (is_separator)
+	    gtk_paint_hline (widget->style,
+			     event->window,
+			     state,
+			     &cell_area,
+			     widget,
+			     NULL,
+			     cell_area.x,
+			     cell_area.x + cell_area.width,
+			     cell_area.y + cell_area.height / 2);
 	  else
+	    _gtk_tree_view_column_cell_render (column,
+					       event->window,
+					       &background_area,
+					       &cell_area,
+					       &event->area,
+					       flags);
+
+	  if (gtk_tree_view_is_expander_column (tree_view, column) &&
+	      TREE_VIEW_DRAW_EXPANDERS(tree_view) &&
+	      (node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT)
 	    {
-	      if (is_separator)
-		gtk_paint_hline (widget->style,
-				 event->window,
-				 state,
-				 &cell_area,
-				 widget,
-				 NULL,
-				 cell_area.x,
-				 cell_area.x + cell_area.width,
-				 cell_area.y + cell_area.height / 2);
-	      else
-		_gtk_tree_view_column_cell_render (column,
-						   event->window,
-						   &background_area,
-						   &cell_area,
-						   &event->area,
-						   flags);
+	      if (!got_pointer)
+		{
+		  gdk_window_get_pointer (tree_view->priv->bin_window, 
+					  &pointer_x, &pointer_y, NULL);
+		  got_pointer = TRUE;
+		}
+	      
+	      gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget),
+					tree,
+					node,
+					pointer_x, pointer_y);
 	    }
 
 	  if (gtk_tree_view_is_expander_column (tree_view, column) &&
@@ -4818,7 +4890,7 @@ gtk_tree_view_bin_expose (GtkWidget      *widget,
 						     flags);
 	    }
 
-	  cell_offset += column->width;
+	  cell_offset += background_area.width;
 	}
 
       if (node == drag_highlight)
@@ -4940,6 +5012,10 @@ gtk_tree_view_bin_expose (GtkWidget      *widget,
 			     width, tmp_height);
 	}
 
+      /* This remaining portion of the loop finds the appropriate next GtkTreeIter
+       * to render and increments the render offset (top down row-by-row inside
+       * the dirty exposed area).
+       */
       y_offset += max_height;
       if (node->children)
 	{
@@ -5692,7 +5768,7 @@ validate_row (GtkTreeView *tree_view,
   gint focus_pad;
   gint grid_line_width;
   gboolean wide_separators;
-  gint separator_height;
+  gint separator_height, bin_window_width = -1;
 
   /* double check the row needs validating */
   if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) &&
@@ -5701,6 +5777,9 @@ validate_row (GtkTreeView *tree_view,
 
   is_separator = row_is_separator (tree_view, iter, NULL);
 
+  if (tree_view->priv->bin_window)
+    gdk_drawable_get_size (tree_view->priv->bin_window, &bin_window_width, NULL);
+
   gtk_widget_style_get (GTK_WIDGET (tree_view),
 			"focus-padding", &focus_pad,
 			"focus-line-width", &focus_line_width,
@@ -5728,12 +5807,15 @@ validate_row (GtkTreeView *tree_view,
        first_column = first_column->next)
     ;
 
+  /* Start by calculating the overall width of the row */
   for (list = tree_view->priv->columns; list; list = list->next)
     {
-      gint tmp_width;
-      gint tmp_height;
+      GtkTreeViewColumnPrivate *column_priv;
+      gint minimum_width, natural_width;
+      gint padding;
 
       column = list->data;
+      column_priv = GTK_TREE_VIEW_COLUMN_GET_PRIVATE (column);
 
       if (! column->visible)
 	continue;
@@ -5741,51 +5823,84 @@ validate_row (GtkTreeView *tree_view,
       if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID) && !column->dirty)
 	continue;
 
+      /* First time around setup the cell data for each column */
       gtk_tree_view_column_cell_set_cell_data (column, tree_view->priv->model, iter,
 					       GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT),
 					       node->children?TRUE:FALSE);
-      gtk_tree_view_column_cell_get_size (column,
-					  NULL, NULL, NULL,
-					  &tmp_width, &tmp_height);
 
-      if (!is_separator)
-	{
-          tmp_height += vertical_separator;
-	  height = MAX (height, tmp_height);
-	  height = MAX (height, tree_view->priv->expander_size);
-	}
-      else
-        {
-          if (wide_separators)
-            height = separator_height + 2 * focus_pad;
-          else
-            height = 2 + 2 * focus_pad;
-        }
+      gtk_tree_view_column_get_natural_width (column, &minimum_width, &natural_width);
 
       if (gtk_tree_view_is_expander_column (tree_view, column))
         {
-	  tmp_width = tmp_width + horizontal_separator + (depth - 1) * tree_view->priv->level_indentation;
+	  padding = horizontal_separator + (depth - 1) * tree_view->priv->level_indentation;
 
 	  if (TREE_VIEW_DRAW_EXPANDERS (tree_view))
-	    tmp_width += depth * tree_view->priv->expander_size;
+	    padding += depth * tree_view->priv->expander_size;
 	}
       else
-	tmp_width = tmp_width + horizontal_separator;
+	padding = horizontal_separator;
 
       if (draw_vgrid_lines)
         {
-	  if (list->data == first_column || list->data == last_column)
-	    tmp_width += grid_line_width / 2.0;
+	  if (list->data == first_column || list == last_column)
+	    padding += grid_line_width / 2.0;
 	  else
-	    tmp_width += grid_line_width;
+	    padding += grid_line_width;
 	}
 
-      if (tmp_width > column->requested_width)
+      minimum_width += padding;
+      natural_width += padding;
+
+      if (minimum_width > column->requested_width ||
+          natural_width > column_priv->natural_width)
 	{
 	  retval = TRUE;
-	  column->requested_width = tmp_width;
+	  column->requested_width    = MAX (column->requested_width,    minimum_width);
+	  column_priv->natural_width = MAX (column_priv->natural_width, natural_width);
 	}
     }
+  
+  /* Finished calculating width of row, now get the height for the collective width */
+  if (!is_separator)
+    {
+      for (list = tree_view->priv->columns; list; list = list->next)
+	{
+	  GtkTreeViewColumnPrivate *column_priv;
+	  gint column_height;
+	  
+	  column = list->data;
+	  column_priv = GTK_TREE_VIEW_COLUMN_GET_PRIVATE (column);
+	  
+	  if (!column->visible)
+	    continue;
+	  
+	  if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID) && !column->dirty)
+	    continue;
+	  
+	  
+	  /* If we have an allocation then we need to fetch the row height for the
+	   * allocated width of the column, otherwise we'll settle for the
+	   * height for the minimum width for the initial request phase.
+	   */
+	  if (column->width > 0)
+	    gtk_tree_view_column_get_height_for_width (column, column->width, 
+						       &column_height, NULL);
+	  else
+	    gtk_tree_view_column_get_height_for_width (column, column->requested_width, 
+						       &column_height, NULL);
+	  
+	  column_height += vertical_separator;
+	  height = MAX (height, column_height);
+	  height = MAX (height, tree_view->priv->expander_size);
+	}
+    }
+  else /* row is a separator */
+    {
+      if (wide_separators)
+	height = separator_height + 2 * focus_pad;
+      else
+	height = 2 + 2 * focus_pad;
+    }
 
   if (draw_hgrid_lines)
     height += grid_line_width;
@@ -6419,8 +6534,12 @@ presize_handler_callback (gpointer data)
 }
 
 static void
-install_presize_handler (GtkTreeView *tree_view)
+install_presize_handler (GtkTreeView *tree_view,
+			 gboolean     content_dirty)
 {
+  if (content_dirty)
+    tree_view->priv->content_size_dirty = TRUE;
+
   if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
     return;
 
@@ -6580,7 +6699,7 @@ _gtk_tree_view_install_mark_rows_col_dirty (GtkTreeView *tree_view)
 {
   tree_view->priv->mark_rows_col_dirty = TRUE;
 
-  install_presize_handler (tree_view);
+  install_presize_handler (tree_view, FALSE);
 }
 
 /*
@@ -6597,7 +6716,7 @@ _gtk_tree_view_column_autosize (GtkTreeView *tree_view,
   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
   g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (column));
 
-  _gtk_tree_view_column_cell_set_dirty (column, FALSE);
+  _gtk_tree_view_column_cell_set_dirty (column, TRUE, FALSE);
 
   do_presize_handler (tree_view);
   while (validate_rows (tree_view));
@@ -7725,7 +7844,7 @@ gtk_tree_view_set_fixed_height_mode (GtkTreeView *tree_view,
       tree_view->priv->fixed_height = -1;
 
       /* force a revalidation */
-      install_presize_handler (tree_view);
+      install_presize_handler (tree_view, TRUE);
     }
   else 
     {
@@ -7747,6 +7866,8 @@ gtk_tree_view_set_fixed_height_mode (GtkTreeView *tree_view,
       
       if (tree_view->priv->tree)
 	initialize_fixed_height_mode (tree_view);
+
+      tree_view->priv->content_size_dirty = TRUE;
     }
 
   g_object_notify (G_OBJECT (tree_view), "fixed-height-mode");
@@ -8055,7 +8176,7 @@ gtk_tree_view_style_set (GtkWidget *widget,
   for (list = tree_view->priv->columns; list; list = list->next)
     {
       column = list->data;
-      _gtk_tree_view_column_cell_set_dirty (column, TRUE);
+      _gtk_tree_view_column_cell_set_dirty (column, TRUE, TRUE);
     }
 
   tree_view->priv->fixed_height = -1;
@@ -8338,7 +8459,7 @@ gtk_tree_view_row_changed (GtkTreeModel *model,
 
           if (column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
             {
-              _gtk_tree_view_column_cell_set_dirty (column, TRUE);
+              _gtk_tree_view_column_cell_set_dirty (column, TRUE, TRUE);
             }
         }
     }
@@ -8346,9 +8467,12 @@ gtk_tree_view_row_changed (GtkTreeModel *model,
  done:
   if (!tree_view->priv->fixed_height_mode &&
       gtk_widget_get_realized (GTK_WIDGET (tree_view)))
-    install_presize_handler (tree_view);
+    install_presize_handler (tree_view, TRUE);
+
   if (free_path)
     gtk_tree_path_free (path);
+
+  tree_view->priv->content_size_dirty = TRUE;
 }
 
 static void
@@ -8461,9 +8585,11 @@ gtk_tree_view_row_inserted (GtkTreeModel *model,
 	gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
     }
   else
-    install_presize_handler (tree_view);
+    install_presize_handler (tree_view, TRUE);
   if (free_path)
     gtk_tree_path_free (path);
+
+  tree_view->priv->content_size_dirty = TRUE;
 }
 
 static void
@@ -8523,8 +8649,7 @@ gtk_tree_view_row_has_child_toggled (GtkTreeModel *model,
 	  for (list = tree_view->priv->columns; list; list = list->next)
 	    if (GTK_TREE_VIEW_COLUMN (list->data)->visible)
 	      {
-		GTK_TREE_VIEW_COLUMN (list->data)->dirty = TRUE;
-		_gtk_tree_view_column_cell_set_dirty (GTK_TREE_VIEW_COLUMN (list->data), TRUE);
+		_gtk_tree_view_column_cell_set_dirty (GTK_TREE_VIEW_COLUMN (list->data), TRUE, TRUE);
 		break;
 	      }
 	}
@@ -8591,7 +8716,7 @@ gtk_tree_view_row_deleted (GtkTreeModel *model,
   for (list = tree_view->priv->columns; list; list = list->next)
     if (((GtkTreeViewColumn *)list->data)->visible &&
 	((GtkTreeViewColumn *)list->data)->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
-      _gtk_tree_view_column_cell_set_dirty ((GtkTreeViewColumn *)list->data, TRUE);
+      _gtk_tree_view_column_cell_set_dirty ((GtkTreeViewColumn *)list->data, TRUE, TRUE);
 
   /* Ensure we don't have a dangling pointer to a dead node */
   ensure_unprelighted (tree_view);
@@ -10850,7 +10975,7 @@ gtk_tree_view_set_model (GtkTreeView  *tree_view,
       gtk_tree_path_free (path);
 
       /*  FIXME: do I need to do this? gtk_tree_view_create_buttons (tree_view); */
-      install_presize_handler (tree_view);
+      install_presize_handler (tree_view, TRUE);
     }
 
   g_object_notify (G_OBJECT (tree_view), "model");
@@ -10860,6 +10985,8 @@ gtk_tree_view_set_model (GtkTreeView  *tree_view,
 
   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
     gtk_widget_queue_resize (GTK_WIDGET (tree_view));
+
+  tree_view->priv->content_size_dirty = TRUE;
 }
 
 /**
@@ -11058,7 +11185,7 @@ gtk_tree_view_columns_autosize (GtkTreeView *tree_view)
       column = list->data;
       if (column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
 	continue;
-      _gtk_tree_view_column_cell_set_dirty (column, TRUE);
+      _gtk_tree_view_column_cell_set_dirty (column, TRUE, TRUE);
       dirty = TRUE;
     }
 
@@ -11241,7 +11368,7 @@ gtk_tree_view_remove_column (GtkTreeView       *tree_view,
 
 	  tmp_column = GTK_TREE_VIEW_COLUMN (list->data);
 	  if (tmp_column->visible)
-	    _gtk_tree_view_column_cell_set_dirty (tmp_column, TRUE);
+	    _gtk_tree_view_column_cell_set_dirty (tmp_column, TRUE, TRUE);
 	}
 
       if (tree_view->priv->n_columns == 0 &&
@@ -11311,7 +11438,7 @@ gtk_tree_view_insert_column (GtkTreeView       *tree_view,
 	{
 	  column = GTK_TREE_VIEW_COLUMN (list->data);
 	  if (column->visible)
-	    _gtk_tree_view_column_cell_set_dirty (column, TRUE);
+	    _gtk_tree_view_column_cell_set_dirty (column, TRUE, TRUE);
 	}
       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
     }
@@ -11719,7 +11846,7 @@ gtk_tree_view_scroll_to_cell (GtkTreeView       *tree_view,
       tree_view->priv->scroll_to_row_align = row_align;
       tree_view->priv->scroll_to_col_align = col_align;
 
-      install_presize_handler (tree_view);
+      install_presize_handler (tree_view, FALSE);
     }
   else
     {
@@ -12132,7 +12259,7 @@ gtk_tree_view_real_expand_row (GtkTreeView *tree_view,
   if (animate)
     add_expand_collapse_timeout (tree_view, tree, node, TRUE);
 
-  install_presize_handler (tree_view);
+  install_presize_handler (tree_view, TRUE);
 
   g_signal_emit (tree_view, tree_view_signals[ROW_EXPANDED], 0, &iter, path);
   if (open_all && node->children)
@@ -12243,7 +12370,7 @@ gtk_tree_view_real_collapse_row (GtkTreeView *tree_view,
       if (column->visible == FALSE)
 	continue;
       if (gtk_tree_view_column_get_sizing (column) == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
-	_gtk_tree_view_column_cell_set_dirty (column, TRUE);
+	_gtk_tree_view_column_cell_set_dirty (column, TRUE, TRUE);
     }
 
   if (tree_view->priv->destroy_count_func)
@@ -15671,5 +15798,103 @@ gtk_tree_view_get_tooltip_column (GtkTreeView *tree_view)
   return tree_view->priv->tooltip_column;
 }
 
+
+static void
+gtk_tree_view_get_minimum_size (GtkWidget      *widget,
+				GtkRequisition *requisition)
+{
+  GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
+  GList *tmp_list;
+
+  /* we validate some rows initially just to make sure we have some size. 
+   * In practice, with a lot of static lists, this should get a good width.
+   */
+  do_validate_rows (tree_view, FALSE);
+  gtk_tree_view_size_request_columns (tree_view);
+  gtk_tree_view_update_size (GTK_TREE_VIEW (widget));
+
+  requisition->width = tree_view->priv->width;
+  requisition->height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
+
+  tmp_list = tree_view->priv->children;
+
+  while (tmp_list)
+    {
+      GtkTreeViewChild *child = tmp_list->data;
+      GtkRequisition child_requisition;
+
+      tmp_list = tmp_list->next;
+
+      if (gtk_widget_get_visible (child->widget))
+        gtk_widget_size_request (child->widget, &child_requisition);
+    }
+}
+
+static void
+gtk_tree_view_size_request_init (GtkSizeRequestIface *iface)
+{
+  iface->get_width  = gtk_tree_view_get_width;
+  iface->get_height = gtk_tree_view_get_height;
+}
+
+static void
+gtk_tree_view_get_size (GtkSizeRequest *widget,
+			GtkOrientation  orientation,
+			gint           *minimum_size,
+			gint           *natural_size)
+{
+  GtkTreeView *tree_view;
+  gint natural_width = 0;
+  GList *column_iter;
+  GtkRequisition requisition;
+
+  tree_view = GTK_TREE_VIEW (widget);
+
+  gtk_tree_view_get_minimum_size (GTK_WIDGET (widget), &requisition);
+
+  if (orientation == GTK_ORIENTATION_HORIZONTAL)
+    {
+      for (column_iter = tree_view->priv->columns; column_iter; column_iter = column_iter->next)
+	{
+	  GtkTreeViewColumn *column = column_iter->data;
+	  
+	  if (!column->visible)
+	    continue;
+	  
+	  natural_width += gtk_tree_view_get_real_natural_width_from_column (tree_view, column);
+	}
+
+      if (minimum_size)
+	*minimum_size = requisition.width;
+
+      if (natural_size)
+	*natural_size = MAX (requisition.width, natural_width);
+    }
+  else
+    {
+      if (minimum_size)
+	*minimum_size = requisition.height;
+
+      if (natural_size)
+	*natural_size = requisition.height;
+    }
+}
+
+static void
+gtk_tree_view_get_width (GtkSizeRequest *widget,
+			 gint           *minimum_size,
+			 gint           *natural_size)
+{
+  gtk_tree_view_get_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
+}
+
+static void
+gtk_tree_view_get_height (GtkSizeRequest *widget,
+			  gint           *minimum_size,
+			  gint           *natural_size)
+{
+  gtk_tree_view_get_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
+}
+
 #define __GTK_TREE_VIEW_C__
 #include "gtkaliasdef.c"
diff --git a/gtk/gtktreeviewcolumn.c b/gtk/gtktreeviewcolumn.c
index 0d953de..90b170b 100644
--- a/gtk/gtktreeviewcolumn.c
+++ b/gtk/gtktreeviewcolumn.c
@@ -26,9 +26,12 @@
 #include "gtkbutton.h"
 #include "gtkalignment.h"
 #include "gtklabel.h"
+#include "gtkcellsizerequest.h"
+#include "gtksizerequest.h"
 #include "gtkhbox.h"
 #include "gtkmarshalers.h"
 #include "gtkarrow.h"
+#include "gtkdebug.h"
 #include "gtkprivate.h"
 #include "gtkintl.h"
 #include "gtkalias.h"
@@ -70,6 +73,7 @@ struct _GtkTreeViewColumnCellInfo
   gpointer func_data;
   GDestroyNotify destroy;
   gint requested_width;
+  gint natural_width;
   gint real_width;
   guint expand : 1;
   guint pack : 1;
@@ -164,6 +168,7 @@ G_DEFINE_TYPE_WITH_CODE (GtkTreeViewColumn, gtk_tree_view_column, GTK_TYPE_OBJEC
 						gtk_tree_view_column_buildable_init))
 
 
+
 static void
 gtk_tree_view_column_class_init (GtkTreeViewColumnClass *class)
 {
@@ -344,6 +349,8 @@ gtk_tree_view_column_class_init (GtkTreeViewColumnClass *class)
                                                      G_MAXINT,
                                                      -1,
                                                      GTK_PARAM_READWRITE));
+
+  g_type_class_add_private (class, sizeof (GtkTreeViewColumnPrivate));
 }
 
 static void
@@ -370,6 +377,8 @@ gtk_tree_view_column_cell_layout_init (GtkCellLayoutIface *iface)
 static void
 gtk_tree_view_column_init (GtkTreeViewColumn *tree_column)
 {
+  GtkTreeViewColumnPrivate *priv;
+
   tree_column->button = NULL;
   tree_column->xalign = 0.0;
   tree_column->width = 0;
@@ -395,6 +404,9 @@ gtk_tree_view_column_init (GtkTreeViewColumn *tree_column)
   tree_column->fixed_width = 1;
   tree_column->use_resized_width = FALSE;
   tree_column->title = g_strdup ("");
+
+  priv = GTK_TREE_VIEW_COLUMN_GET_PRIVATE (tree_column);
+  priv->natural_width = -1;
 }
 
 static void
@@ -719,7 +731,7 @@ gtk_tree_view_column_cell_layout_add_attribute (GtkCellLayout   *cell_layout,
   info->attributes = g_slist_prepend (info->attributes, g_strdup (attribute));
 
   if (tree_column->tree_view)
-    _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE);
+    _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE, TRUE);
 }
 
 static void
@@ -751,7 +763,7 @@ gtk_tree_view_column_cell_layout_set_cell_data_func (GtkCellLayout         *cell
   info->destroy = destroy;
 
   if (column->tree_view)
-    _gtk_tree_view_column_cell_set_dirty (column, TRUE);
+    _gtk_tree_view_column_cell_set_dirty (column, TRUE, TRUE);
 }
 
 static void
@@ -814,7 +826,7 @@ gtk_tree_view_column_clear_attributes_by_info (GtkTreeViewColumn *tree_column,
   info->attributes = NULL;
 
   if (tree_column->tree_view)
-    _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE);
+    _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE, TRUE);
 }
 
 /* Helper functions
@@ -1723,7 +1735,7 @@ gtk_tree_view_column_set_spacing (GtkTreeViewColumn *tree_column,
 
   tree_column->spacing = spacing;
   if (tree_column->tree_view)
-    _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE);
+    _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE, TRUE);
 }
 
 /**
@@ -1765,7 +1777,7 @@ gtk_tree_view_column_set_visible (GtkTreeViewColumn *tree_column,
   tree_column->visible = visible;
 
   if (tree_column->visible)
-    _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE);
+    _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE, TRUE);
 
   gtk_tree_view_column_update_button (tree_column);
   g_object_notify (G_OBJECT (tree_column), "visible");
@@ -2590,14 +2602,17 @@ gtk_tree_view_column_cell_set_cell_data (GtkTreeViewColumn *tree_column,
  * 
  * Obtains the width and height needed to render the column.  This is used
  * primarily by the #GtkTreeView.
+ *
+ * Deprecated: 3.0: Use gtk_tree_view_column_get_natural_width() and
+ * gtk_tree_view_column_get_height_for_width() instead.
  **/
 void
 gtk_tree_view_column_cell_get_size (GtkTreeViewColumn  *tree_column,
 				    const GdkRectangle *cell_area,
 				    gint               *x_offset,
 				    gint               *y_offset,
-				    gint               *width,
-				    gint               *height)
+                                    gint               *width,
+                                    gint               *height)
 {
   GList *list;
   gboolean first_cell = TRUE;
@@ -2687,6 +2702,7 @@ gtk_tree_view_column_cell_process_action (GtkTreeViewColumn  *tree_column,
   /* If we have rtl text, we need to transform our areas */
   GdkRectangle rtl_cell_area;
   GdkRectangle rtl_background_area;
+  GtkPackType packing;
 
   min_x = G_MAXINT;
   min_y = G_MAXINT;
@@ -2764,343 +2780,186 @@ gtk_tree_view_column_cell_process_action (GtkTreeViewColumn  *tree_column,
   else if (extra_space > 0 && expand_cell_count > 0)
     extra_space /= expand_cell_count;
 
-  /* iterate list for GTK_PACK_START cells */
-  for (list = tree_column->cell_list; list; list = list->next)
-    {
-      GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *) list->data;
-
-      if (info->pack == GTK_PACK_END)
-	continue;
-
-      if (! info->cell->visible)
-	continue;
-
-      if ((info->has_focus || special_cells == 1) && cursor_row)
-	flags |= GTK_CELL_RENDERER_FOCUSED;
-      else
-        flags &= ~GTK_CELL_RENDERER_FOCUSED;
 
-      info->real_width = info->requested_width + (info->expand?extra_space:0);
+  for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
+    {
 
-      /* We constrain ourselves to only the width available */
-      if (real_cell_area.x - focus_line_width + info->real_width > cell_area->x + cell_area->width)
+      for (list = tree_column->cell_list; list; list = list->next)
 	{
-	  info->real_width = cell_area->x + cell_area->width - real_cell_area.x;
-	}   
+	  GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *) list->data;
 
-      if (real_cell_area.x > cell_area->x + cell_area->width)
-	break;
+	  if (info->pack != packing)
+	    continue;
 
-      real_cell_area.width = info->real_width;
-      real_cell_area.width -= 2 * focus_line_width;
+	  if (! info->cell->visible)
+	    continue;
 
-      if (list->next)
-	{
-	  real_background_area.width = info->real_width + depth;
-	}
-      else
-	{
-          /* fill the rest of background for the last cell */
-	  real_background_area.width = background_area->x + background_area->width - real_background_area.x;
-	}
-
-      rtl_cell_area = real_cell_area;
-      rtl_background_area = real_background_area;
-      
-      if (rtl)
-	{
-	  rtl_cell_area.x = cell_area->x + cell_area->width - (real_cell_area.x - cell_area->x) - real_cell_area.width;
-	  rtl_background_area.x = background_area->x + background_area->width - (real_background_area.x - background_area->x) - real_background_area.width;
-	}
+	  if ((info->has_focus || special_cells == 1) && cursor_row)
+	    flags |= GTK_CELL_RENDERER_FOCUSED;
+	  else
+	    flags &= ~GTK_CELL_RENDERER_FOCUSED;
 
-      /* RENDER */
-      if (action == CELL_ACTION_RENDER)
-	{
-	  gtk_cell_renderer_render (info->cell,
-				    window,
-				    tree_column->tree_view,
-				    &rtl_background_area,
-				    &rtl_cell_area,
-				    &real_expose_area, 
-				    flags);
-	}
-      /* FOCUS */
-      else if (action == CELL_ACTION_FOCUS)
-	{
-	  gint x_offset, y_offset, width, height;
+	  /* We constrain ourselves to only the width available */
+	  if (real_cell_area.x - focus_line_width + info->real_width > cell_area->x + cell_area->width)
+	    {
+	      info->real_width = cell_area->x + cell_area->width - real_cell_area.x;
+	    }   
+	  
+	  /* Break out of the inner loop once we itterate out of our allocation 
+	   * (and possibly start the other packing direction) */
+	  if (real_cell_area.x > cell_area->x + cell_area->width)
+	    break;
 
-	  gtk_cell_renderer_get_size (info->cell,
-				      tree_column->tree_view,
-				      &rtl_cell_area,
-				      &x_offset, &y_offset,
-				      &width, &height);
+	  real_cell_area.width = info->real_width;
+	  real_cell_area.width -= 2 * focus_line_width;
 
-	  if (special_cells > 1)
+	  if (list->next)
 	    {
-	      if (info->has_focus)
-	        {
-		  min_x = rtl_cell_area.x + x_offset;
-		  max_x = min_x + width;
-		  min_y = rtl_cell_area.y + y_offset;
-		  max_y = min_y + height;
-		}
+	      real_background_area.width = info->real_width + depth;
 	    }
 	  else
 	    {
-	      if (min_x > (rtl_cell_area.x + x_offset))
-		min_x = rtl_cell_area.x + x_offset;
-	      if (max_x < rtl_cell_area.x + x_offset + width)
-		max_x = rtl_cell_area.x + x_offset + width;
-	      if (min_y > (rtl_cell_area.y + y_offset))
-		min_y = rtl_cell_area.y + y_offset;
-	      if (max_y < rtl_cell_area.y + y_offset + height)
-		max_y = rtl_cell_area.y + y_offset + height;
+	      /* fill the rest of background for the last cell */
+	      real_background_area.width = background_area->x + background_area->width - real_background_area.x;
 	    }
-	}
-      /* EVENT */
-      else if (action == CELL_ACTION_EVENT)
-	{
-	  gboolean try_event = FALSE;
-
-	  if (event)
+	  
+	  rtl_cell_area = real_cell_area;
+	  rtl_background_area = real_background_area;
+      
+	  if (rtl)
 	    {
-	      if (special_cells == 1)
-	        {
-		  /* only 1 activatable cell -> whole column can activate */
-		  if (cell_area->x <= ((GdkEventButton *)event)->x &&
-		      cell_area->x + cell_area->width > ((GdkEventButton *)event)->x)
-		    try_event = TRUE;
-		}
-	      else if (rtl_cell_area.x <= ((GdkEventButton *)event)->x &&
-		  rtl_cell_area.x + rtl_cell_area.width > ((GdkEventButton *)event)->x)
-		  /* only activate cell if the user clicked on an individual
-		   * cell
-		   */
-		try_event = TRUE;
+	      rtl_cell_area.x = cell_area->x + cell_area->width - (real_cell_area.x - cell_area->x) - real_cell_area.width;
+	      rtl_background_area.x = background_area->x + background_area->width - (real_background_area.x - background_area->x) - real_background_area.width;
 	    }
-	  else if (special_cells > 1 && info->has_focus)
-	    try_event = TRUE;
-	  else if (special_cells == 1)
-	    try_event = TRUE;
 
-	  if (try_event)
+	  /* RENDER */
+	  if (action == CELL_ACTION_RENDER)
 	    {
-	      gboolean visible, mode;
-
-	      g_object_get (info->cell,
-			    "visible", &visible,
-			    "mode", &mode,
-			    NULL);
-	      if (visible && mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
+	      gtk_cell_renderer_render (info->cell,
+					window,
+					tree_column->tree_view,
+					&rtl_background_area,
+					&rtl_cell_area,
+					&real_expose_area, 
+					flags);
+	    }
+	  /* FOCUS */
+	  else if (action == CELL_ACTION_FOCUS)
+	    {
+	      gint x_offset, y_offset;
+	      GtkRequisition min_size;
+	      
+	      gtk_cell_size_request_get_size (GTK_CELL_SIZE_REQUEST (info->cell), 
+					      tree_column->tree_view, 
+					      &min_size, NULL);
+	      
+	      _gtk_cell_renderer_calc_offset (info->cell, &rtl_cell_area,
+					      gtk_widget_get_direction (tree_column->tree_view),
+					      min_size.width, min_size.height, &x_offset, &y_offset);
+	      
+	      if (special_cells > 1)
 		{
-		  if (gtk_cell_renderer_activate (info->cell,
-						  event,
-						  tree_column->tree_view,
-						  path_string,
-						  &rtl_background_area,
-						  &rtl_cell_area,
-						  flags))
+		  if (info->has_focus)
 		    {
-                      flags &= ~GTK_CELL_RENDERER_FOCUSED;
-		      return TRUE;
+		      min_x = rtl_cell_area.x + x_offset;
+		      max_x = min_x + min_size.width;
+		      min_y = rtl_cell_area.y + y_offset;
+		      max_y = min_y + min_size.height;
 		    }
 		}
-	      else if (visible && mode == GTK_CELL_RENDERER_MODE_EDITABLE)
+	      else
 		{
-		  *editable_widget =
-		    gtk_cell_renderer_start_editing (info->cell,
-						     event,
-						     tree_column->tree_view,
-						     path_string,
-						     &rtl_background_area,
-						     &rtl_cell_area,
-						     flags);
-
-		  if (*editable_widget != NULL)
-		    {
-		      g_return_val_if_fail (GTK_IS_CELL_EDITABLE (*editable_widget), FALSE);
-		      info->in_editing_mode = TRUE;
-		      gtk_tree_view_column_focus_cell (tree_column, info->cell);
-		      
-                      flags &= ~GTK_CELL_RENDERER_FOCUSED;
-
-		      return TRUE;
-		    }
-		}
-	    }
-	}
-
-      flags &= ~GTK_CELL_RENDERER_FOCUSED;
-
-      real_cell_area.x += (real_cell_area.width + 2 * focus_line_width + tree_column->spacing);
-      real_background_area.x += real_background_area.width + tree_column->spacing;
-
-      /* Only needed for first cell */
-      depth = 0;
-    }
-
-  /* iterate list for PACK_END cells */
-  for (list = g_list_last (tree_column->cell_list); list; list = list->prev)
-    {
-      GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *) list->data;
-
-      if (info->pack == GTK_PACK_START)
-	continue;
-
-      if (! info->cell->visible)
-	continue;
-
-      if ((info->has_focus || special_cells == 1) && cursor_row)
-	flags |= GTK_CELL_RENDERER_FOCUSED;
-      else
-        flags &= ~GTK_CELL_RENDERER_FOCUSED;
-
-      info->real_width = info->requested_width + (info->expand?extra_space:0);
-
-      /* We constrain ourselves to only the width available */
-      if (real_cell_area.x - focus_line_width + info->real_width > cell_area->x + cell_area->width)
-	{
-	  info->real_width = cell_area->x + cell_area->width - real_cell_area.x;
-	}   
-
-      if (real_cell_area.x > cell_area->x + cell_area->width)
-	break;
-
-      real_cell_area.width = info->real_width;
-      real_cell_area.width -= 2 * focus_line_width;
-      real_background_area.width = info->real_width + depth;
-
-      rtl_cell_area = real_cell_area;
-      rtl_background_area = real_background_area;
-      if (rtl)
-	{
-	  rtl_cell_area.x = cell_area->x + cell_area->width - (real_cell_area.x - cell_area->x) - real_cell_area.width;
-	  rtl_background_area.x = background_area->x + background_area->width - (real_background_area.x - background_area->x) - real_background_area.width;
-	}
-
-      /* RENDER */
-      if (action == CELL_ACTION_RENDER)
-	{
-	  gtk_cell_renderer_render (info->cell,
-				    window,
-				    tree_column->tree_view,
-				    &rtl_background_area,
-				    &rtl_cell_area,
-				    &real_expose_area,
-				    flags);
-	}
-      /* FOCUS */
-      else if (action == CELL_ACTION_FOCUS)
-	{
-	  gint x_offset, y_offset, width, height;
-
-	  gtk_cell_renderer_get_size (info->cell,
-				      tree_column->tree_view,
-				      &rtl_cell_area,
-				      &x_offset, &y_offset,
-				      &width, &height);
-
-	  if (special_cells > 1)
-	    {
-	      if (info->has_focus)
-	        {
-		  min_x = rtl_cell_area.x + x_offset;
-		  max_x = min_x + width;
-		  min_y = rtl_cell_area.y + y_offset;
-		  max_y = min_y + height;
+		  if (min_x > (rtl_cell_area.x + x_offset))
+		    min_x = rtl_cell_area.x + x_offset;
+		  if (max_x < rtl_cell_area.x + x_offset + min_size.width)
+		    max_x = rtl_cell_area.x + x_offset + min_size.width;
+		  if (min_y > (rtl_cell_area.y + y_offset))
+		    min_y = rtl_cell_area.y + y_offset;
+		  if (max_y < rtl_cell_area.y + y_offset + min_size.height)
+		    max_y = rtl_cell_area.y + y_offset + min_size.height;
 		}
 	    }
-	  else
-	    {
-	      if (min_x > (rtl_cell_area.x + x_offset))
-		min_x = rtl_cell_area.x + x_offset;
-	      if (max_x < rtl_cell_area.x + x_offset + width)
-		max_x = rtl_cell_area.x + x_offset + width;
-	      if (min_y > (rtl_cell_area.y + y_offset))
-		min_y = rtl_cell_area.y + y_offset;
-	      if (max_y < rtl_cell_area.y + y_offset + height)
-		max_y = rtl_cell_area.y + y_offset + height;
-	    }
-	}
-      /* EVENT */
-      else if (action == CELL_ACTION_EVENT)
-        {
-	  gboolean try_event = FALSE;
-
-	  if (event)
+	  /* EVENT */
+	  else if (action == CELL_ACTION_EVENT)
 	    {
-	      if (special_cells == 1)
-	        {
-		  /* only 1 activatable cell -> whole column can activate */
-		  if (cell_area->x <= ((GdkEventButton *)event)->x &&
-		      cell_area->x + cell_area->width > ((GdkEventButton *)event)->x)
+	      gboolean try_event = FALSE;
+	      
+	      if (event)
+		{
+		  if (special_cells == 1)
+		    {
+		      /* only 1 activatable cell -> whole column can activate */
+		      if (cell_area->x <= ((GdkEventButton *)event)->x &&
+			  cell_area->x + cell_area->width > ((GdkEventButton *)event)->x)
+			try_event = TRUE;
+		    }
+		  else if (rtl_cell_area.x <= ((GdkEventButton *)event)->x &&
+			   rtl_cell_area.x + rtl_cell_area.width > ((GdkEventButton *)event)->x)
+		    /* only activate cell if the user clicked on an individual
+		     * cell
+		     */
 		    try_event = TRUE;
 		}
-	      else if (rtl_cell_area.x <= ((GdkEventButton *)event)->x &&
-		  rtl_cell_area.x + rtl_cell_area.width > ((GdkEventButton *)event)->x)
-		/* only activate cell if the user clicked on an individual
-		 * cell
-		 */
+	      else if (special_cells > 1 && info->has_focus)
 		try_event = TRUE;
-	    }
-	  else if (special_cells > 1 && info->has_focus)
-	    try_event = TRUE;
-	  else if (special_cells == 1)
-	    try_event = TRUE;
-
-	  if (try_event)
-	    {
-	      gboolean visible, mode;
-
-	      g_object_get (info->cell,
-			    "visible", &visible,
-			    "mode", &mode,
-			    NULL);
-	      if (visible && mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
-	        {
-		  if (gtk_cell_renderer_activate (info->cell,
-						  event,
-						  tree_column->tree_view,
-						  path_string,
-						  &rtl_background_area,
-						  &rtl_cell_area,
-						  flags))
+	      else if (special_cells == 1)
+		try_event = TRUE;
+	      
+	      if (try_event)
+		{
+		  gboolean visible, mode;
+		  
+		  g_object_get (info->cell,
+				"visible", &visible,
+				"mode", &mode,
+				NULL);
+		  if (visible && mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
 		    {
-		      flags &= ~GTK_CELL_RENDERER_FOCUSED;
-		      return TRUE;
+		      if (gtk_cell_renderer_activate (info->cell,
+						      event,
+						      tree_column->tree_view,
+						      path_string,
+						      &rtl_background_area,
+						      &rtl_cell_area,
+						      flags))
+			{
+			  flags &= ~GTK_CELL_RENDERER_FOCUSED;
+			  return TRUE;
+			}
 		    }
-		}
-	      else if (visible && mode == GTK_CELL_RENDERER_MODE_EDITABLE)
-	        {
-		  *editable_widget =
-		    gtk_cell_renderer_start_editing (info->cell,
-						     event,
-						     tree_column->tree_view,
-						     path_string,
-						     &rtl_background_area,
-						     &rtl_cell_area,
-						     flags);
-
-		  if (*editable_widget != NULL)
+		  else if (visible && mode == GTK_CELL_RENDERER_MODE_EDITABLE)
 		    {
-		      g_return_val_if_fail (GTK_IS_CELL_EDITABLE (*editable_widget), FALSE);
-		      info->in_editing_mode = TRUE;
-		      gtk_tree_view_column_focus_cell (tree_column, info->cell);
-
-		      flags &= ~GTK_CELL_RENDERER_FOCUSED;
-		      return TRUE;
+		      *editable_widget =
+			gtk_cell_renderer_start_editing (info->cell,
+							 event,
+							 tree_column->tree_view,
+							 path_string,
+							 &rtl_background_area,
+							 &rtl_cell_area,
+							 flags);
+		      
+		      if (*editable_widget != NULL)
+			{
+			  g_return_val_if_fail (GTK_IS_CELL_EDITABLE (*editable_widget), FALSE);
+			  info->in_editing_mode = TRUE;
+			  gtk_tree_view_column_focus_cell (tree_column, info->cell);
+			  
+			  flags &= ~GTK_CELL_RENDERER_FOCUSED;
+			  
+			  return TRUE;
+			}
 		    }
 		}
 	    }
-	}
-
-      flags &= ~GTK_CELL_RENDERER_FOCUSED;
-
-      real_cell_area.x += (real_cell_area.width + 2 * focus_line_width + tree_column->spacing);
-      real_background_area.x += (real_background_area.width + tree_column->spacing);
 
-      /* Only needed for first cell */
-      depth = 0;
+	  flags &= ~GTK_CELL_RENDERER_FOCUSED;
+	  
+	  real_cell_area.x += (real_cell_area.width + 2 * focus_line_width + tree_column->spacing);
+	  real_background_area.x += real_background_area.width + tree_column->spacing;
+	  
+	  /* Only needed for first cell */
+	  depth = 0;
+	}
     }
 
   /* fill focus_rectangle when required */
@@ -3587,19 +3446,28 @@ gtk_tree_view_column_focus_cell (GtkTreeViewColumn *tree_column,
 
 void
 _gtk_tree_view_column_cell_set_dirty (GtkTreeViewColumn *tree_column,
+				      gboolean           recalculate_width,
 				      gboolean           install_handler)
 {
   GList *list;
+  GtkTreeViewColumnPrivate *priv;
 
-  for (list = tree_column->cell_list; list; list = list->next)
-    {
-      GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *) list->data;
+  priv = GTK_TREE_VIEW_COLUMN_GET_PRIVATE (tree_column);
 
-      info->requested_width = 0;
-    }
   tree_column->dirty = TRUE;
-  tree_column->requested_width = -1;
-  tree_column->width = 0;
+
+  if (recalculate_width)
+    {
+      for (list = tree_column->cell_list; list; list = list->next)
+	{
+	  GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *) list->data;
+	  
+	  info->requested_width = 0;
+	  info->natural_width = 0;
+	}
+      tree_column->requested_width = -1;
+      priv->natural_width = -1;
+    }
 
   if (tree_column->tree_view &&
       gtk_widget_get_realized (tree_column->tree_view))
@@ -3612,6 +3480,7 @@ _gtk_tree_view_column_cell_set_dirty (GtkTreeViewColumn *tree_column,
     }
 }
 
+
 void
 _gtk_tree_view_column_start_editing (GtkTreeViewColumn *tree_column,
 				     GtkCellEditable   *cell_editable)
@@ -3744,7 +3613,7 @@ gtk_tree_view_column_queue_resize (GtkTreeViewColumn *tree_column)
   g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
 
   if (tree_column->tree_view)
-    _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE);
+    _gtk_tree_view_column_cell_set_dirty (tree_column, TRUE, TRUE);
 }
 
 /**
@@ -3768,5 +3637,288 @@ gtk_tree_view_column_get_tree_view (GtkTreeViewColumn *tree_column)
   return tree_column->tree_view;
 }
 
+/**
+ * gtk_tree_view_column_get_natural_width:
+ * @tree_column: A #GtkTreeViewColumn
+ * @minimum_width: location for storing the minimum width, or %NULL
+ * @natural_width: location for storing the natural width, or %NULL
+ *
+ * Retreives @tree_column's minimum and natural width.
+ *
+ * Since: 3.0
+ */
+void
+gtk_tree_view_column_get_natural_width (GtkTreeViewColumn       *column,
+					gint                    *minimum_width,
+					gint                    *natural_width)
+{
+  GList         *list;
+  gboolean       first_cell = TRUE;
+  gint           focus_line_width;
+  gint           min = 0, nat = 0;
+
+  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (column));
+
+  gtk_widget_style_get (column->tree_view, "focus-line-width", &focus_line_width, NULL);
+  
+  for (list = column->cell_list; list; list = list->next)
+    {
+      GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *) list->data;
+      gint                       cell_min, cell_nat;
+      gboolean                   visible;
+
+      g_object_get (info->cell, "visible", &visible, NULL);
+
+      if (visible == FALSE)
+	continue;
+
+      if (first_cell == FALSE)
+        {
+	  min += column->spacing;
+	  nat += column->spacing;
+        }
+
+      gtk_cell_size_request_get_width (GTK_CELL_SIZE_REQUEST (info->cell),
+				       column->tree_view, &cell_min, &cell_nat);
+
+      cell_min += focus_line_width * 2;
+      cell_nat += focus_line_width * 2;
+
+      /* Store 'requested_width' and 'natural_width' to cache all the requests
+       * for every row in the column; natural space distribution is only calculated
+       * once for the whole column for now. */
+      info->requested_width = MAX (info->requested_width, cell_min);
+      info->natural_width   = MAX (info->natural_width,   cell_nat);
+
+      /* Return the collective minimum/natural width of all
+       * cached sizes */
+      min += info->requested_width;
+      nat += info->natural_width;
+
+      first_cell = FALSE;
+    }
+
+  if (minimum_width)
+    *minimum_width = min;
+
+  if (natural_width)
+    *natural_width = nat;
+}
+
+/**
+ * gtk_tree_view_column_get_height_for_width:
+ * @tree_column: A #GtkTreeViewColumn
+ * @width: the width available for allocation
+ * @minimum_height: location for storing the minimum height, or %NULL
+ * @natural_height: location for storing the natural height, or %NULL
+ *
+ * Retreives @tree_column's minimum and natural height if it were rendered to 
+ * @widget with the specified @height.
+ *
+ * Since: 3.0
+ */
+void
+gtk_tree_view_column_get_height_for_width (GtkTreeViewColumn *column,
+					   gint               width,
+					   gint              *minimum_height,
+					   gint              *natural_height)
+{
+  GList      *list;
+  GArray     *array;
+  gint        size = width;
+  gint        focus_line_width;
+  gint        expand_cell_count = 0, i;
+  gboolean    first_cell = TRUE;
+  gint        min_height = 0, nat_height = 0;
+  GtkRequestedSize *sizes;
+
+  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (column));
+
+  gtk_widget_style_get (column->tree_view, "focus-line-width", &focus_line_width, NULL);
+
+  array = g_array_new (0, TRUE, sizeof (GtkRequestedSize));
+
+
+  /* First get the overall expand space and collect the cell requests */
+  for (list = column->cell_list; list; list = list->next)
+    {
+      GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *) list->data;
+      gboolean                   visible;
+      GtkRequestedSize           requested;
+
+      g_object_get (info->cell, "visible", &visible, NULL);
+
+      if (visible == FALSE)
+	continue;
+
+      if (info->expand == TRUE)
+	expand_cell_count++;
+
+      if (first_cell == FALSE)
+	size -= column->spacing;
+
+      size -= focus_line_width * 2;
+      size -= info->requested_width;
+
+      /* Here the collective minimum/natural width for all rows
+       * has necessarily been cached by gtk_tree_view_column_get_natural_width().
+       *
+       * As the allocated width is based on the collective widths of all rows,
+       * the allocated width for a cell will be the same in each row.
+       *
+       * However the height-for-width must also be calculated for each
+       * row based on the aligned width in order to determine the row height.
+       *
+       * OPTIMIZE ME: It would be better to calculate the allocations of the cells once
+       * and caching an extra info->allocated_width in order to avoid this
+       * calculation for every row.
+       *
+       * Note we would use the real minimums/naturals as reported
+       * by the cells for each row here if we were to display
+       * the cells unaligned (which is more expensive to calculate
+       * but would allow cells to flow more naturally inside columns).
+       */
+      requested.data         = info;
+      requested.minimum_size = info->requested_width;
+      requested.natural_size = info->natural_width;
+      g_array_append_val (array, requested);
+
+      first_cell = FALSE;
+    }
+
+
+  /* Distribute as much of remaining 'size' as possible before sharing expand space */
+  sizes = (GtkRequestedSize *)array->data;
+  size  = gtk_distribute_natural_allocation (size, array->len, sizes);
+
+  /* The rest gets split up evenly among expanding cells */
+  if (expand_cell_count)
+    size /= expand_cell_count;
+
+  /* Collect the minimum and natural height for the allocations of cells */
+  for (i = 0, list = column->cell_list; list; list = list->next)
+    {
+      GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *) list->data;
+      gint                       cell_min, cell_nat;
+      gboolean                   visible;
+
+      g_object_get (info->cell, "visible", &visible, NULL);
+
+      if (visible == FALSE)
+	continue;
+
+      gtk_cell_size_request_get_height_for_width (GTK_CELL_SIZE_REQUEST (info->cell),
+						  column->tree_view,
+						  sizes[i].minimum_size + (info->expand ? size : 0),
+						  &cell_min, &cell_nat);
+
+      min_height = MAX (min_height, cell_min);
+      nat_height = MAX (nat_height, cell_nat);
+
+      i++;
+    }
+  g_array_free (array, TRUE);
+
+  if (minimum_height)
+    *minimum_height = min_height;
+
+  if (natural_height)
+    *natural_height = nat_height;
+}
+
+/**
+ * gtk_tree_view_column_allocate_width:
+ * @tree_column: A #GtkTreeViewColumn
+ * @width: the overall width of the column
+ *
+ * This allocates a good size for each cell in the column
+ * based on the overall minimum and natural widths of the
+ * cells after having been tested for each column.
+ *
+ * A width must be allocated after the request phase
+ * and before handling events
+ *
+ * Since: 3.0
+ */
+void
+gtk_tree_view_column_allocate_width (GtkTreeViewColumn       *column,
+				     gint                     width)
+{
+  GList      *list;
+  GArray     *array;
+  gint        size = width;
+  gint        focus_line_width;
+  gint        expand_cell_count = 0, i;
+  gboolean    first_cell = TRUE;
+  GtkRequestedSize *sizes;
+
+  g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (column));
+
+  column->width = width;
+
+  gtk_widget_style_get (column->tree_view, "focus-line-width", &focus_line_width, NULL);
+
+  array = g_array_new (0, TRUE, sizeof (GtkRequestedSize));
+
+  /* First get the overall expand space and collect the cell requests */
+  for (list = column->cell_list; list; list = list->next)
+    {
+      GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *) list->data;
+      gboolean                   visible;
+      GtkRequestedSize           requested;
+
+      g_object_get (info->cell, "visible", &visible, NULL);
+
+      if (visible == FALSE)
+	continue;
+
+      if (info->expand == TRUE)
+	expand_cell_count++;
+
+      if (first_cell == FALSE)
+	size -= column->spacing;
+
+      size -= focus_line_width * 2;
+      size -= info->requested_width;
+
+      requested.data         = info;
+      requested.minimum_size = info->requested_width;
+      requested.natural_size = info->natural_width;
+      g_array_append_val (array, requested);
+
+      first_cell = FALSE;
+    }
+
+  /* Distribute as much of remaining 'size' as possible before sharing expand space */
+  sizes = (GtkRequestedSize *)array->data;
+  size  = gtk_distribute_natural_allocation (size, array->len, sizes);
+
+  /* The rest gets split up evenly among expanding cells */
+  if (expand_cell_count > 0)
+    size /= expand_cell_count;
+
+  /* Allocate/assign info->real_width based in minimum/natural size + expand space */
+  for (i = 0, list = column->cell_list; list; list = list->next)
+    {
+      GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *) list->data;
+      gboolean                   visible;
+
+      g_object_get (info->cell, "visible", &visible, NULL);
+
+      if (visible == FALSE)
+	continue;
+
+      info->real_width = sizes[i].minimum_size + (info->expand ? size : 0);
+
+      i++;
+    }
+  g_array_free (array, TRUE);
+
+  g_object_notify (G_OBJECT (column), "width");
+}
+
+
+
+
 #define __GTK_TREE_VIEW_COLUMN_C__
 #include "gtkaliasdef.c"
diff --git a/gtk/gtktreeviewcolumn.h b/gtk/gtktreeviewcolumn.h
index 4e72235..9b047eb 100644
--- a/gtk/gtktreeviewcolumn.h
+++ b/gtk/gtktreeviewcolumn.h
@@ -51,6 +51,7 @@ typedef enum
 
 typedef struct _GtkTreeViewColumn      GtkTreeViewColumn;
 typedef struct _GtkTreeViewColumnClass GtkTreeViewColumnClass;
+typedef struct _GtkTreeViewColumnPrivate GtkTreeViewColumnPrivate;
 
 typedef void (* GtkTreeCellDataFunc) (GtkTreeViewColumn *tree_column,
 				      GtkCellRenderer   *cell,
@@ -225,6 +226,17 @@ void                    gtk_tree_view_column_cell_get_size       (GtkTreeViewCol
 								  gint                    *y_offset,
 								  gint                    *width,
 								  gint                    *height);
+
+void                    gtk_tree_view_column_get_natural_width   (GtkTreeViewColumn       *column,
+								  gint                    *minimum_width,
+								  gint                    *natural_width);
+void                    gtk_tree_view_column_get_height_for_width(GtkTreeViewColumn       *column,
+								  gint                     width,
+								  gint                    *minimum_height,
+								  gint                    *natural_height);
+void                    gtk_tree_view_column_allocate_width      (GtkTreeViewColumn       *column,
+								  gint                     width);
+
 gboolean                gtk_tree_view_column_cell_is_visible     (GtkTreeViewColumn       *tree_column);
 void                    gtk_tree_view_column_focus_cell          (GtkTreeViewColumn       *tree_column,
 								  GtkCellRenderer         *cell);



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