commit ba625350fdd1e8ec0e902155100fc586c6e50aa6
Author: Tristan Van Berkom <tristan.van.berkom@gmail.com>
Date:   Tue Oct 5 15:20:23 2010 +0900

    Added logic to GtkScrolledWindow when allocating height-for-width children.
    
    This patch makes the scrolled window reconsider allocating the child
    the full width or height (depending on the child's request mode) without
    a scrollbar. For instance when the child is height-for-width; the child
    will first be tested if the content's height for full allocated width
    (without a vscrollbar) will allow the contents height for that width
    to fit the allocated height.
    
    Patch is a simplified version of code inspected in st-scroll-view.c.
    Note that this patch assumes children will begin to scroll only after
    reaching their minimum size; adding a property to the future
    GtkScrollableIface to decide whether to scroll-to-minimum or scroll-to-natural
    will effect this code (it should then reconsider whether the child
    will scroll below the natural size instead of the minimum).
    
    Patch also offloads border-width to the parent GtkContainerClass using
    gtk_container_class_handle_border_width().
    
    Patch addresses bug 629778.

diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
index 6825428..d0bcfd1 100644
--- a/gtk/gtkscrolledwindow.c
+++ b/gtk/gtkscrolledwindow.c
@@ -231,6 +231,7 @@ gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
   container_class->add = gtk_scrolled_window_add;
   container_class->remove = gtk_scrolled_window_remove;
   container_class->forall = gtk_scrolled_window_forall;
+  gtk_container_class_handle_border_width (container_class);
 
   class->scrollbar_spacing = -1;
 
@@ -1108,15 +1109,10 @@ gtk_scrolled_window_draw (GtkWidget *widget,
         }
       else
         {
-          GtkContainer *container = GTK_CONTAINER (widget);
-          guint border_width;
-
-          border_width = gtk_container_get_border_width (container);
-
-          relative_allocation.x = border_width;
-          relative_allocation.y = border_width;
-          relative_allocation.width = gtk_widget_get_allocated_width (widget) - 2 * border_width;
-          relative_allocation.height = gtk_widget_get_allocated_height (widget) - 2 * border_width;
+          relative_allocation.x = 0;
+          relative_allocation.y = 0;
+          relative_allocation.width = gtk_widget_get_allocated_width (widget);
+          relative_allocation.height = gtk_widget_get_allocated_height (widget);
         }
 
       gtk_paint_shadow (style,
@@ -1299,8 +1295,9 @@ gtk_scrolled_window_relative_allocation (GtkWidget     *widget,
   GtkScrolledWindow *scrolled_window;
   GtkScrolledWindowPrivate *priv;
   GtkStyle *style;
-  gint scrollbar_spacing;
-  guint border_width;
+  gint sb_spacing;
+  gint sb_width;
+  gint sb_height;
 
   g_return_if_fail (widget != NULL);
   g_return_if_fail (allocation != NULL);
@@ -1308,11 +1305,14 @@ gtk_scrolled_window_relative_allocation (GtkWidget     *widget,
   scrolled_window = GTK_SCROLLED_WINDOW (widget);
   priv = scrolled_window->priv;
 
-  scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
+  /* Get possible scrollbar dimensions */
+  sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
+  gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL);
+  gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL);
 
-  border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
-  allocation->x = border_width;
-  allocation->y = border_width;
+  /* Subtract some things from our available allocation size */
+  allocation->x = 0;
+  allocation->y = 0;
 
   if (priv->shadow_type != GTK_SHADOW_NONE)
     {
@@ -1327,11 +1327,8 @@ gtk_scrolled_window_relative_allocation (GtkWidget     *widget,
 
   if (priv->vscrollbar_visible)
     {
-      GtkRequisition vscrollbar_requisition;
       gboolean is_rtl;
 
-      gtk_widget_get_preferred_size (priv->vscrollbar,
-                                     &vscrollbar_requisition, NULL);
       is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
   
       if ((!is_rtl && 
@@ -1340,22 +1337,18 @@ gtk_scrolled_window_relative_allocation (GtkWidget     *widget,
 	  (is_rtl && 
 	   (priv->real_window_placement == GTK_CORNER_TOP_LEFT ||
 	    priv->real_window_placement == GTK_CORNER_BOTTOM_LEFT)))
-	allocation->x += (vscrollbar_requisition.width +  scrollbar_spacing);
+	allocation->x += (sb_width +  sb_spacing);
 
-      allocation->width = MAX (1, allocation->width - (vscrollbar_requisition.width + scrollbar_spacing));
+      allocation->width = MAX (1, allocation->width - (sb_width + sb_spacing));
     }
   if (priv->hscrollbar_visible)
     {
-      GtkRequisition hscrollbar_requisition;
-
-      gtk_widget_get_preferred_size (priv->hscrollbar,
-                                     &hscrollbar_requisition, NULL);
 
       if (priv->real_window_placement == GTK_CORNER_BOTTOM_LEFT ||
 	  priv->real_window_placement == GTK_CORNER_BOTTOM_RIGHT)
-	allocation->y += (hscrollbar_requisition.height + scrollbar_spacing);
+	allocation->y += (sb_height + sb_spacing);
 
-      allocation->height = MAX (1, allocation->height - (hscrollbar_requisition.height + scrollbar_spacing));
+      allocation->height = MAX (1, allocation->height - (sb_height + sb_spacing));
     }
 }
 
@@ -1371,9 +1364,12 @@ gtk_scrolled_window_size_allocate (GtkWidget     *widget,
   GtkAllocation child_allocation;
   GtkWidget *child;
   gboolean scrollbars_within_bevel;
-  gint scrollbar_spacing;
-  guint border_width;
-  
+  gint sb_spacing;
+  gint sb_width;
+  gint sb_height;
+ 
+
+
   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
   g_return_if_fail (allocation != NULL);
 
@@ -1381,13 +1377,14 @@ gtk_scrolled_window_size_allocate (GtkWidget     *widget,
   bin = GTK_BIN (scrolled_window);
   priv = scrolled_window->priv;
 
-  scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
+  /* Get possible scrollbar dimensions */
+  sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
+  gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL);
+  gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL);
 
   style = gtk_widget_get_style (widget);
   gtk_widget_style_get (widget, "scrollbars-within-bevel", &scrollbars_within_bevel, NULL);
 
-  border_width = gtk_container_get_border_width (GTK_CONTAINER (scrolled_window));
-
   gtk_widget_set_allocation (widget, allocation);
 
   if (priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
@@ -1402,10 +1399,131 @@ gtk_scrolled_window_size_allocate (GtkWidget     *widget,
   child = gtk_bin_get_child (bin);
   if (child && gtk_widget_get_visible (child))
     {
+      gint child_min_width;
+      gint child_min_height;
       gboolean previous_hvis;
       gboolean previous_vvis;
       guint count = 0;
+
+      /* In the case that both scrollbars are visible in the previous round,
+       * we dont do our guess-work before hand because it's possible some
+       * infinite recursion was detected (leave it up to the child scrollable
+       * widget in this case to drive the scrollbar visibility completely 
+       * with the adjustment values).
+       */
+      if (!priv->vscrollbar_visible || !priv->hscrollbar_visible)
+	{
+
+	  /* Determine scrollbar visibility first via hfw apis */
+	  if (gtk_widget_get_request_mode (child) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
+	    {
+	      gtk_widget_get_preferred_width (child, &child_min_width, NULL);
+	      
+	      if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
+		{
+		  /* First try without a vertical scrollbar if the content will fit the height
+		   * given the extra width of the scrollbar */
+		  gtk_widget_get_preferred_height_for_width (child, allocation->width, 
+							     &child_min_height, NULL);
+		  
+		  if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
+		    {
+		      /* Does the content height fit the allocation height ? */
+		      priv->vscrollbar_visible = child_min_height > allocation->height;
+		      
+		      /* Does the content width fit the allocation with minus a possible scrollbar ? */
+		      priv->hscrollbar_visible = 
+			child_min_width > allocation->width - 
+			(priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
+		      
+		      /* Now that we've guessed the hscrollbar, does the content height fit
+		       * the possible new allocation height ? */
+		      priv->vscrollbar_visible = 
+			child_min_height > allocation->height - 
+			(priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
+		      
+		      /* Now that we've guessed the vscrollbar, does the content width fit
+		       * the possible new allocation width ? */
+		      priv->hscrollbar_visible = 
+			child_min_width > allocation->width - 
+			(priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
+		    }
+		  else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
+		    {
+		      priv->hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER;
+		      priv->vscrollbar_visible = child_min_height > allocation->height - 
+			(priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
+		    }
+		}
+	      else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
+		{
+		  priv->vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER;
+		  
+		  if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
+		    priv->hscrollbar_visible = 
+		      child_min_width > allocation->width - 
+		      (priv->vscrollbar_visible ? 0 : sb_width + sb_spacing);
+		  else
+		    priv->hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER;
+		}
+	    } 
+	  else /* GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT */
+	    {
+	      gtk_widget_get_preferred_height (child, &child_min_height, NULL);
+	      
+	      if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
+		{
+		  /* First try without a horizontal scrollbar if the content will fit the width
+		   * given the extra height of the scrollbar */
+		  gtk_widget_get_preferred_width_for_height (child, allocation->height, 
+							     &child_min_width, NULL);
+		  
+		  if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
+		    {
+		      /* Does the content width fit the allocation width ? */
+		      priv->hscrollbar_visible = child_min_width > allocation->width;
+		      
+		      /* Does the content height fit the allocation with minus a possible scrollbar ? */
+		      priv->vscrollbar_visible = 
+			child_min_height > allocation->height - 
+			(priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
+		      
+		      /* Now that we've guessed the vscrollbar, does the content width fit
+		       * the possible new allocation width ? */
+		      priv->hscrollbar_visible = 
+			child_min_width > allocation->width - 
+			(priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
+		      
+		      /* Now that we've guessed the hscrollbar, does the content height fit
+		       * the possible new allocation height ? */
+		      priv->vscrollbar_visible = 
+			child_min_height > allocation->height - 
+			(priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
+		    }
+		  else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
+		    {
+		      priv->vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER;
+		      priv->hscrollbar_visible = child_min_width > allocation->width - 
+			(priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
+		    }
+		}
+	      else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
+		{
+		  priv->hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER;
+		  
+		  if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
+		    priv->vscrollbar_visible = 
+		      child_min_height > allocation->height - 
+		      (priv->hscrollbar_visible ? 0 : sb_height + sb_spacing);
+		  else
+		    priv->vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER;
+		}
+	    }
+	}
       
+      
+      /* Now we fall back to the old algorithm which takes into account the child
+       * updates to the v/h adjustments to decide visibility after child allocations */
       do
 	{
 	  gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
@@ -1450,11 +1568,6 @@ gtk_scrolled_window_size_allocate (GtkWidget     *widget,
 
   if (priv->hscrollbar_visible)
     {
-      GtkRequisition hscrollbar_requisition;
-
-      gtk_widget_get_preferred_size (priv->hscrollbar,
-                                     &hscrollbar_requisition, NULL);
-
       if (!gtk_widget_get_visible (priv->hscrollbar))
 	gtk_widget_show (priv->hscrollbar);
 
@@ -1463,14 +1576,14 @@ gtk_scrolled_window_size_allocate (GtkWidget     *widget,
 	  priv->real_window_placement == GTK_CORNER_TOP_RIGHT)
 	child_allocation.y = (relative_allocation.y +
 			      relative_allocation.height +
-			      scrollbar_spacing +
+			      sb_spacing +
 			      (priv->shadow_type == GTK_SHADOW_NONE ?
 			       0 : style->ythickness));
       else
-	child_allocation.y = border_width;
+	child_allocation.y = 0;
 
       child_allocation.width = relative_allocation.width;
-      child_allocation.height = hscrollbar_requisition.height;
+      child_allocation.height = sb_height;
       child_allocation.x += allocation->x;
       child_allocation.y += allocation->y;
 
@@ -1499,13 +1612,9 @@ gtk_scrolled_window_size_allocate (GtkWidget     *widget,
 
   if (priv->vscrollbar_visible)
     {
-      GtkRequisition vscrollbar_requisition;
       if (!gtk_widget_get_visible (priv->vscrollbar))
 	gtk_widget_show (priv->vscrollbar);
 
-      gtk_widget_get_preferred_size (priv->vscrollbar,
-                                     &vscrollbar_requisition, NULL);
-
       if ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL && 
 	   (priv->real_window_placement == GTK_CORNER_TOP_RIGHT ||
 	    priv->real_window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
@@ -1514,14 +1623,14 @@ gtk_scrolled_window_size_allocate (GtkWidget     *widget,
 	    priv->real_window_placement == GTK_CORNER_BOTTOM_LEFT)))
 	child_allocation.x = (relative_allocation.x +
 			      relative_allocation.width +
-			      scrollbar_spacing +
+			      sb_spacing +
 			      (priv->shadow_type == GTK_SHADOW_NONE ?
 			       0 : style->xthickness));
       else
-	child_allocation.x = border_width;
+	child_allocation.x = 0;
 
       child_allocation.y = relative_allocation.y;
-      child_allocation.width = vscrollbar_requisition.width;
+      child_allocation.width = sb_width;
       child_allocation.height = relative_allocation.height;
       child_allocation.x += allocation->x;
       child_allocation.y += allocation->y;
@@ -1815,7 +1924,6 @@ gtk_scrolled_window_get_preferred_size (GtkWidget      *widget,
   GtkStyle *style;
   GtkWidget *child;
   gint min_child_size, nat_child_size;
-  guint border_width;
 
   scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
 
@@ -1910,11 +2018,10 @@ gtk_scrolled_window_get_preferred_size (GtkWidget      *widget,
 	extra_width = scrollbar_spacing + vscrollbar_requisition.width;
     }
 
-  border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
-  minimum_req.width += border_width * 2 + MAX (0, extra_width);
-  minimum_req.height += border_width * 2 + MAX (0, extra_height);
-  natural_req.width += border_width * 2 + MAX (0, extra_width);
-  natural_req.height += border_width * 2 + MAX (0, extra_height);
+  minimum_req.width += MAX (0, extra_width);
+  minimum_req.height += MAX (0, extra_height);
+  natural_req.width += MAX (0, extra_width);
+  natural_req.height += MAX (0, extra_height);
 
   if (priv->shadow_type != GTK_SHADOW_NONE)
     {
