State of natural size, Havoc/Behdad style



Attached the current state of natural size, with an API similiar to Havoc's wishes and implementing the natural size allocation they both suggested.

Havoc suggest to have two methods: get_desired_width() and get_desired_height(), but I implemented get_desired_size(), since I do not see how to implement this for GtkLabel, without recalculating the entire layout four times on each desired size request.
-- 
GMX FreeMail: 1 GB Postfach, 5 E-Mail-Adressen, 10 Free SMS.
Alle Infos und kostenlose Anmeldung: http://www.gmx.net/de/go/freemail

-- 
Mathias Hasselmann
http://taschenorakel.de/mathias/

Ist Ihr Browser Vista-kompatibel? Jetzt die neuesten 
Browser-Versionen downloaden: http://www.gmx.net/de/go/browser
>From 1a64cd913c90ce8dd3ce4f17de714741a3db1b33 Mon Sep 17 00:00:00 2001
From: Mathias Hasselmann <mathias openismus com>
Date: Sat, 5 Jan 2008 11:36:29 +0100
Subject: =?utf-8?q?*=20tests/testellipsise.c:=20Extend=20the=20test=20to=20support=20rotations.
=20*=20gtk/gtklabel.c:=20Support=20ellipsizing=20and=20wrapping=20on=20labels
=20rotated=20by=20multiples=20of=2090=C2=B0.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit

---
 ChangeLog             |    6 ++
 gtk/gtk.symbols       |    2 +
 gtk/gtklabel.c        |  223 +++++++++++++++++++++++++++++++++++++------------
 gtk/gtklabel.h        |    3 +
 tests/testellipsise.c |  112 +++++++++++++++++++++++--
 5 files changed, 285 insertions(+), 61 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 69119de..fb5f18d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2008-01-02  Mathias Hasselmann  <mathias openismus com>
+
+	* tests/testellipsise.c: Extend the test to support rotations.
+	* gtk/gtklabel.c: Support ellipsizing and wrapping on labels
+	rotated by multiples of 90°.
+
 2008-01-04  Mathias Hasselmann  <mathias openismus com>
 
 	Avoid some compiler warnings (#507000).
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index f5de635..e353959 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -2065,6 +2065,7 @@ gtk_label_get_type G_GNUC_CONST
 gtk_label_get_use_markup
 gtk_label_get_use_underline
 gtk_label_get_width_chars
+gtk_label_get_full_size
 gtk_label_new
 gtk_label_new_with_mnemonic
 gtk_label_select_region
@@ -2087,6 +2088,7 @@ gtk_label_set_text_with_mnemonic
 gtk_label_set_use_markup
 gtk_label_set_use_underline
 gtk_label_set_width_chars
+gtk_label_set_full_size
 #endif
 #endif
 
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index cfe3df4..b3fe927 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -48,11 +48,16 @@
 
 #define GTK_LABEL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_LABEL, GtkLabelPrivate))
 
+#ifndef INFINITY /* Why again don't we use C99? */
+#define INFINITY HUGE_VAL
+#endif
+
 typedef struct
 {
   gint wrap_width;
   gint width_chars;
   gint max_width_chars;
+  gboolean full_size;
 }
 GtkLabelPrivate;
 
@@ -95,7 +100,8 @@ enum {
   PROP_WIDTH_CHARS,
   PROP_SINGLE_LINE_MODE,
   PROP_ANGLE,
-  PROP_MAX_WIDTH_CHARS
+  PROP_MAX_WIDTH_CHARS,
+  PROP_FULL_SIZE
 };
 
 static guint signals[LAST_SIGNAL] = { 0 };
@@ -512,6 +518,23 @@ gtk_label_class_init (GtkLabelClass *class)
                                                      G_MAXINT,
                                                      -1,
                                                      GTK_PARAM_READWRITE));
+
+  /**
+   * GtkLabel:full-size:
+   *
+   * Use the entire space the widget got assigned for text wrapping. Overrides
+   * any #GtkLabel:width-chars, #GtkLabel:max-width-chars and screen size based
+   * constraints. Requires #GtkLabel:angle to be 0°, 90°, 180° or 270°.
+   *
+   * Since: 2.14
+   **/
+  g_object_class_install_property (gobject_class,
+                                   PROP_FULL_SIZE,
+                                   g_param_spec_boolean ("full-size",
+							 P_("Full size"),
+							 P_("Use the entire size of the widget to wrap text"),
+                                                        FALSE,
+                                                        GTK_PARAM_READWRITE));
   /*
    * Key bindings
    */
@@ -688,6 +711,9 @@ gtk_label_set_property (GObject      *object,
     case PROP_MAX_WIDTH_CHARS:
       gtk_label_set_max_width_chars (label, g_value_get_int (value));
       break;
+    case PROP_FULL_SIZE:
+      gtk_label_set_full_size (label, g_value_get_boolean (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -771,6 +797,9 @@ gtk_label_get_property (GObject     *object,
     case PROP_MAX_WIDTH_CHARS:
       g_value_set_int (value, gtk_label_get_max_width_chars (label));
       break;
+    case PROP_FULL_SIZE:
+      g_value_set_int (value, gtk_label_get_full_size (label));
+      break;
 
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -2005,14 +2034,14 @@ gtk_label_ensure_layout (GtkLabel *label)
       PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
       gdouble angle = gtk_label_get_angle (label);
 
-      if (angle != 0.0 && !label->wrap && !label->ellipsize && !label->select_info)
+      if (angle != 0.0 && !label->select_info)
 	{
+          PangoMatrix matrix = PANGO_MATRIX_INIT;
+
 	  /* We rotate the standard singleton PangoContext for the widget,
 	   * depending on the fact that it's meant pretty much exclusively
 	   * for our use.
 	   */
-	  PangoMatrix matrix = PANGO_MATRIX_INIT;
-	  
 	  pango_matrix_rotate (&matrix, angle);
 
 	  pango_context_set_matrix (gtk_widget_get_pango_context (widget), &matrix);
@@ -2056,8 +2085,8 @@ gtk_label_ensure_layout (GtkLabel *label)
       pango_layout_set_single_paragraph_mode (label->layout, label->single_line_mode);
 
       if (label->ellipsize)
-	pango_layout_set_width (label->layout, 
-				widget->allocation.width * PANGO_SCALE);
+        pango_layout_set_width (label->layout,
+                                widget->allocation.width * PANGO_SCALE);
       else if (label->wrap)
 	{
 	  GtkWidgetAuxInfo *aux_info;
@@ -2086,12 +2115,12 @@ gtk_label_ensure_layout (GtkLabel *label)
 	      width = MIN (width, wrap_width);
 	      width = MIN (width,
 			   PANGO_SCALE * (gdk_screen_get_width (screen) + 1) / 2);
-	      
+
 	      pango_layout_set_width (label->layout, width);
 	      pango_layout_get_extents (label->layout, NULL, &logical_rect);
 	      width = logical_rect.width;
 	      height = logical_rect.height;
-	      
+
 	      /* Unfortunately, the above may leave us with a very unbalanced looking paragraph,
 	       * so we try short search for a narrower width that leaves us with the same height
 	       */
@@ -2138,8 +2167,8 @@ gtk_label_size_request (GtkWidget      *widget,
 {
   GtkLabel *label;
   GtkLabelPrivate *priv;
-  gint width, height;
   PangoRectangle logical_rect;
+  PangoRectangle required_rect;
   GtkWidgetAuxInfo *aux_info;
   
   g_return_if_fail (GTK_IS_LABEL (widget));
@@ -2166,39 +2195,21 @@ gtk_label_size_request (GtkWidget      *widget,
 
   gtk_label_ensure_layout (label);
 
-  width = label->misc.xpad * 2;
-  height = label->misc.ypad * 2;
-
   aux_info = _gtk_widget_get_aux_info (widget, FALSE);
 
-  if (label->have_transform)
-    {
-      PangoRectangle rect;
-      PangoContext *context = pango_layout_get_context (label->layout);
-      const PangoMatrix *matrix = pango_context_get_matrix (context);
-
-      pango_layout_get_extents (label->layout, NULL, &rect);
-      pango_matrix_transform_rectangle (matrix, &rect);
-      pango_extents_to_pixels (&rect, NULL);
-      
-      requisition->width = width + rect.width;
-      requisition->height = height + rect.height;
-
-      return;
-    }
-  else
-    pango_layout_get_extents (label->layout, NULL, &logical_rect);
+  pango_layout_get_extents (label->layout, NULL, &logical_rect);
+  required_rect.x = required_rect.y = 0;
 
   if ((label->wrap || label->ellipsize || 
        priv->width_chars > 0 || priv->max_width_chars > 0) && 
       aux_info && aux_info->width > 0)
-    width += aux_info->width;
+    required_rect.width = aux_info->width;
   else if (label->ellipsize || priv->width_chars > 0 || priv->max_width_chars > 0)
     {
-      width += PANGO_PIXELS (get_label_char_width (label));
+      required_rect.width = PANGO_PIXELS (get_label_char_width (label));
     }
   else
-    width += PANGO_PIXELS (logical_rect.width);
+    required_rect.width = PANGO_PIXELS (logical_rect.width);
 
   if (label->single_line_mode)
     {
@@ -2214,39 +2225,102 @@ gtk_label_size_request (GtkWidget      *widget,
       descent = pango_font_metrics_get_descent (metrics);
       pango_font_metrics_unref (metrics);
     
-      height += PANGO_PIXELS (ascent + descent);
+      required_rect.height = PANGO_PIXELS (ascent + descent);
     }
   else
-    height += PANGO_PIXELS (logical_rect.height);
+    required_rect.height = PANGO_PIXELS (logical_rect.height);
 
-  requisition->width = width;
-  requisition->height = height;
+  if (label->have_transform)
+    {
+      PangoContext *context = pango_layout_get_context (label->layout);
+      const PangoMatrix *matrix = pango_context_get_matrix (context);
+      pango_matrix_transform_pixel_rectangle (matrix, &required_rect);
+    }
+
+  requisition->width = required_rect.width + label->misc.xpad * 2;
+  requisition->height = required_rect.height + label->misc.ypad * 2;
 }
 
 static void
 gtk_label_size_allocate (GtkWidget     *widget,
                          GtkAllocation *allocation)
 {
-  GtkLabel *label;
-
-  label = GTK_LABEL (widget);
+  GtkLabel *label = GTK_LABEL (widget);
 
   (* GTK_WIDGET_CLASS (gtk_label_parent_class)->size_allocate) (widget, allocation);
 
-  if (label->ellipsize)
+  if (label->ellipsize || GTK_LABEL_GET_PRIVATE (label)->full_size)
     {
       if (label->layout)
 	{
-	  gint width;
 	  PangoRectangle logical;
+          PangoRectangle bounds;
 
-	  width = (allocation->width - label->misc.xpad * 2) * PANGO_SCALE;
+          bounds.x = bounds.y = 0;
+          bounds.width = allocation->width - label->misc.xpad * 2;
+          bounds.height = allocation->height - label->misc.ypad * 2;
 
 	  pango_layout_set_width (label->layout, -1);
-	  pango_layout_get_extents (label->layout, NULL, &logical);
+	  pango_layout_get_pixel_extents (label->layout, NULL, &logical);
+
+          if (label->have_transform)
+            {
+              PangoContext *context = gtk_widget_get_pango_context (widget);
+              const PangoMatrix *matrix = pango_context_get_matrix (context);
+
+              const gdouble dx = matrix->xx; /* cos (M_PI * angle / 180) */
+              const gdouble dy = matrix->xy; /* sin (M_PI * angle / 180) */
+
+              if (fabs (dy) < 0.01)
+                {
+                  if (logical.width > bounds.width)
+	            pango_layout_set_width (label->layout, bounds.width * PANGO_SCALE);
+                }
+              else if (fabs (dx) < 0.01)
+                {
+                  if (logical.width > bounds.height)
+	            pango_layout_set_width (label->layout, bounds.height * PANGO_SCALE);
+                }
+              else
+                {
+                  gdouble x0, y0, x1, y1, length;
+                  gboolean vertical;
+                  gint cy;
+
+                  x0 = bounds.width / 2;
+                  y0 = dx ? x0 * dy / dx : dy * INFINITY;
+                  vertical = fabs (y0) > bounds.height / 2;
 
-	  if (logical.width > width)
-	    pango_layout_set_width (label->layout, width);
+                  if (vertical)
+                    {
+                      y0 = bounds.height/2;
+                      x0 = dy ? y0 * dx / dy : dx * INFINITY;
+                    }
+
+                  length = 2 * sqrt (x0 * x0 + y0 * y0);
+                  pango_layout_set_width (label->layout, rint (length * PANGO_SCALE));
+                  pango_layout_get_pixel_size (label->layout, NULL, &cy);
+
+                  x1 = +dy * cy/2;
+                  y1 = -dx * cy/2;
+
+                  if (vertical)
+                    {
+                      y0 = bounds.height/2 + y1 - y0;
+                      x0 = -y0 * dx/dy;
+                    }
+                  else
+                    {
+                      x0 = bounds.width/2 + x1 - x0;
+                      y0 = -x0 * dy/dx;
+                    }
+
+                  length = length - sqrt (x0 * x0 + y0 * y0) * 2;
+                  pango_layout_set_width (label->layout, rint (length * PANGO_SCALE));
+                }
+            }
+	  else if (logical.width > bounds.width)
+	    pango_layout_set_width (label->layout, bounds.width * PANGO_SCALE);
 	}
     }
 
@@ -2327,7 +2401,9 @@ get_layout_location (GtkLabel  *label,
   GtkWidget *widget; 
   GtkLabelPrivate *priv;
   gfloat xalign;
-  gint req_width, x, y;
+  gint req_width;
+  gint req_height;
+  gint x, y;
   
   misc = GTK_MISC (label);
   widget = GTK_WIDGET (label);
@@ -2338,33 +2414,48 @@ get_layout_location (GtkLabel  *label,
   else
     xalign = 1.0 - misc->xalign;
 
-  if (label->ellipsize || priv->width_chars > 0)
+  if (label->ellipsize || priv->width_chars > 0 || GTK_LABEL_GET_PRIVATE (label)->full_size)
     {
       int width;
       PangoRectangle logical;
 
       width = pango_layout_get_width (label->layout);
-      pango_layout_get_pixel_extents (label->layout, NULL, &logical);
+      pango_layout_get_extents (label->layout, NULL, &logical);
+
+      if (label->have_transform)
+        {
+          PangoContext *context = gtk_widget_get_pango_context (widget);
+          const PangoMatrix *matrix = pango_context_get_matrix (context);
+          pango_matrix_transform_rectangle (matrix, &logical);
+        }
+
+      pango_extents_to_pixels (&logical, NULL);
 
       req_width = logical.width;
+      req_height = logical.height;
+
       if (width != -1)
 	req_width = MIN(PANGO_PIXELS (width), req_width);
+
       req_width += 2 * misc->xpad;
+      req_height += 2 * misc->ypad;
     }
   else
-    req_width = widget->requisition.width;
+    {
+      req_width = widget->requisition.width;
+      req_height = widget->requisition.height;
+    }
 
   x = floor (widget->allocation.x + (gint)misc->xpad +
-	      xalign * (widget->allocation.width - req_width));
+	     xalign * (widget->allocation.width - req_width));
 
   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
     x = MAX (x, widget->allocation.x + misc->xpad);
   else
     x = MIN (x, widget->allocation.x + widget->allocation.width - misc->xpad);
 
-  y = floor (widget->allocation.y + (gint)misc->ypad 
-             + MAX (((widget->allocation.height - widget->requisition.height) * misc->yalign),
-	     0));
+  y = floor (widget->allocation.y + (gint)misc->ypad +
+	     misc->yalign * (widget->allocation.height - req_height));
 
   if (xp)
     *xp = x;
@@ -3676,6 +3767,32 @@ gtk_label_set_use_underline (GtkLabel *label,
     gtk_label_setup_mnemonic (label, label->mnemonic_keyval);
 }
 
+gboolean
+gtk_label_get_full_size (GtkLabel *label)
+{
+  g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
+  return GTK_LABEL_GET_PRIVATE (label)->full_size;
+}
+
+void
+gtk_label_set_full_size (GtkLabel *label,
+			 gboolean  setting)
+{
+  GtkLabelPrivate *priv;
+
+  g_return_if_fail (GTK_IS_LABEL (label));
+  priv = GTK_LABEL_GET_PRIVATE (label);
+
+  if (priv->full_size != setting)
+    {
+      priv->full_size = setting;
+
+      g_object_notify (G_OBJECT (label), "full-size");
+      gtk_label_invalidate_wrap_width (label);
+      gtk_widget_queue_resize (GTK_WIDGET (label));
+    }
+}
+
 /**
  * gtk_label_get_use_underline:
  * @label: a #GtkLabel
diff --git a/gtk/gtklabel.h b/gtk/gtklabel.h
index 5656ebb..ac90f1d 100644
--- a/gtk/gtklabel.h
+++ b/gtk/gtklabel.h
@@ -119,6 +119,9 @@ gboolean              gtk_label_get_use_markup    (GtkLabel      *label);
 void                  gtk_label_set_use_underline (GtkLabel      *label,
 						   gboolean       setting);
 gboolean              gtk_label_get_use_underline (GtkLabel      *label);
+void                  gtk_label_set_full_size     (GtkLabel      *label,
+						   gboolean       setting);
+gboolean              gtk_label_get_full_size     (GtkLabel      *label);
 
 void     gtk_label_set_markup_with_mnemonic       (GtkLabel         *label,
 						   const gchar      *str);
diff --git a/tests/testellipsise.c b/tests/testellipsise.c
index 0783d61..157884a 100644
--- a/tests/testellipsise.c
+++ b/tests/testellipsise.c
@@ -26,6 +26,21 @@
 #include <gtk/gtk.h>
 
 static void
+redraw_event_box (GtkWidget *widget)
+{
+  while (widget)
+    {
+      if (GTK_IS_EVENT_BOX (widget))
+        {
+          gtk_widget_queue_draw (widget);
+          break;
+        }
+
+      widget = gtk_widget_get_parent (widget);
+    }
+}
+
+static void
 combo_changed_cb (GtkWidget *combo,
 		  gpointer   data)
 {
@@ -33,33 +48,114 @@ combo_changed_cb (GtkWidget *combo,
   gint active;
 
   active = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
-  
   gtk_label_set_ellipsize (GTK_LABEL (label), (PangoEllipsizeMode)active);
+  redraw_event_box (label);
+}
+
+static void
+scale_changed_cb (GtkRange *range,
+		  gpointer   data)
+{
+  double angle = gtk_range_get_value (range);
+  GtkWidget *label = GTK_WIDGET (data);
+
+  gtk_label_set_angle (GTK_LABEL (label), angle);
+  redraw_event_box (label);
+}
+
+static gboolean
+ebox_expose_event_cb (GtkWidget      *widget,
+                      GdkEventExpose *event,
+                      gpointer        data)
+{
+  PangoLayout *layout;
+  const double dashes[] = { 6, 18 };
+  GtkWidget *label = data;
+  gint x, y, dx, dy;
+  cairo_t *cr;
+
+  gtk_widget_translate_coordinates (label, widget, 0, 0, &x, &y);
+
+  cr = gdk_cairo_create (widget->window);
+  cairo_translate (cr, -0.5, -0.5);
+  cairo_set_line_width (cr, 1);
+
+  cairo_rectangle (cr,
+                   x + 0.5 * (label->allocation.width - label->requisition.width),
+                   y + 0.5 * (label->allocation.height - label->requisition.height),
+                   label->requisition.width, label->requisition.height);
+  cairo_set_source_rgb (cr, 0.8, 0.2, 0.2);
+  cairo_set_dash (cr, NULL, 0, 0);
+  cairo_stroke (cr);
+
+  cairo_rectangle (cr, x, y, label->allocation.width, label->allocation.height);
+  cairo_set_source_rgb (cr, 0.2, 0.2, 0.8);
+  cairo_set_dash (cr, dashes, 2, 0.5);
+  cairo_stroke (cr);
+
+  layout = gtk_widget_create_pango_layout (widget, NULL);
+
+  pango_layout_set_markup (layout,
+    "<span color='#c33'>\342\200\242 requisition</span>\n"
+    "<span color='#33c'>\342\200\242 allocation</span>", -1);
+
+  pango_layout_get_pixel_size (layout, &dx, &dy);
+
+  cairo_set_source_rgba (cr, 1, 1, 1, 0.8);
+  cairo_rectangle (cr, 0, 0, dx + 12, dy + 8);
+  cairo_fill (cr);
+
+  cairo_translate (cr, 6, 4);
+  pango_cairo_show_layout (cr, layout);
+
+  g_object_unref (layout);
+  cairo_destroy (cr);
+
+  return FALSE;
 }
 
 int
 main (int argc, char *argv[])
 {
-  GtkWidget *window, *vbox, *hbox, *label, *combo;
+  GtkWidget *window, *vbox, *label;
+  GtkWidget *combo, *scale, *align, *ebox;
 
   gtk_init (&argc, &argv);
 
   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_container_set_border_width (GTK_CONTAINER (window), 12);
+  gtk_window_set_default_size (GTK_WINDOW (window), 400, 300);
   g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
-  vbox = gtk_vbox_new (0, FALSE);
+
+  vbox = gtk_vbox_new (FALSE, 6);
   gtk_container_add (GTK_CONTAINER (window), vbox);
-  hbox = gtk_hbox_new (0, FALSE);
-  gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
-  label = gtk_label_new ("This label may be ellipsized\nto make it fit.");
-  gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+
   combo = gtk_combo_box_new_text ();
+  scale = gtk_hscale_new_with_range (0, 360, 1);
+  label = gtk_label_new ("This label may be ellipsized\nto make it fit.");
+
   gtk_combo_box_append_text (GTK_COMBO_BOX (combo), "NONE");
   gtk_combo_box_append_text (GTK_COMBO_BOX (combo), "START");
   gtk_combo_box_append_text (GTK_COMBO_BOX (combo), "MIDDLE");
   gtk_combo_box_append_text (GTK_COMBO_BOX (combo), "END");
   gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
-  gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
+
+  align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+  gtk_container_add (GTK_CONTAINER (align), label);
+
+  ebox = gtk_event_box_new ();
+  gtk_widget_set_app_paintable (ebox, TRUE);
+  gtk_container_add (GTK_CONTAINER (ebox), align);
+
+  gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (vbox), ebox, TRUE, TRUE, 0);
+
+  g_object_set_data (G_OBJECT (label), "combo", combo);
+
   g_signal_connect (combo, "changed", G_CALLBACK (combo_changed_cb), label);
+  g_signal_connect (scale, "value-changed", G_CALLBACK (scale_changed_cb), label);
+  g_signal_connect_after (ebox, "expose-event", G_CALLBACK (ebox_expose_event_cb), label);
 
   gtk_widget_show_all (window);
 
-- 
1.5.3.7

>From 7012c88d5c645b45277d75c873fffb12af4a71cc Mon Sep 17 00:00:00 2001
From: Mathias Hasselmann <mathias openismus com>
Date: Sat, 5 Jan 2008 11:37:48 +0100
Subject: Merge nearly identical size-allocation loops for start and end packing.

* gtk/gtkhbox.c: Merge loops in gtk_hbox_size_allocate.
---
 ChangeLog     |    6 ++
 gtk/gtkhbox.c |  174 +++++++++++++++++++++------------------------------------
 2 files changed, 69 insertions(+), 111 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index fb5f18d..aebf3c5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2008-01-02  Mathias Hasselmann  <mathias openismus com>
 
+	Merge nearly identical size-allocation loops for start and end packing.
+
+	* gtk/gtkhbox.c: Merge loops in gtk_hbox_size_allocate.
+
+2008-01-02  Mathias Hasselmann  <mathias openismus com>
+
 	* tests/testellipsise.c: Extend the test to support rotations.
 	* gtk/gtklabel.c: Support ellipsizing and wrapping on labels
 	rotated by multiples of 90°.
diff --git a/gtk/gtkhbox.c b/gtk/gtkhbox.c
index 3140cac..c949251 100644
--- a/gtk/gtkhbox.c
+++ b/gtk/gtkhbox.c
@@ -138,6 +138,7 @@ gtk_hbox_size_allocate (GtkWidget     *widget,
   gint extra;
   gint x;
   GtkTextDirection direction;
+  GtkPackType packing;
 
   box = GTK_BOX (widget);
   widget->allocation = *allocation;
@@ -163,10 +164,11 @@ gtk_hbox_size_allocate (GtkWidget     *widget,
 
   if (nvis_children > 0)
     {
+      gint border_width = GTK_CONTAINER (box)->border_width;
+
       if (box->homogeneous)
 	{
-	  width = (allocation->width -
-		   GTK_CONTAINER (box)->border_width * 2 -
+	  width = (allocation->width - border_width * 2 -
 		   (nvis_children - 1) * box->spacing);
 	  extra = width / nvis_children;
 	}
@@ -181,129 +183,79 @@ gtk_hbox_size_allocate (GtkWidget     *widget,
 	  extra = 0;
 	}
 
-      x = allocation->x + GTK_CONTAINER (box)->border_width;
-      child_allocation.y = allocation->y + GTK_CONTAINER (box)->border_width;
-      child_allocation.height = MAX (1, (gint) allocation->height - (gint) GTK_CONTAINER (box)->border_width * 2);
-
-      children = box->children;
-      while (children)
-	{
-	  child = children->data;
-	  children = children->next;
-
-	  if ((child->pack == GTK_PACK_START) && GTK_WIDGET_VISIBLE (child->widget))
-	    {
-	      if (box->homogeneous)
-		{
-		  if (nvis_children == 1)
-		    child_width = width;
-		  else
-		    child_width = extra;
-
-		  nvis_children -= 1;
-		  width -= extra;
-		}
-	      else
-		{
-		  GtkRequisition child_requisition;
-
-		  gtk_widget_get_child_requisition (child->widget, &child_requisition);
-
-		  child_width = child_requisition.width + child->padding * 2;
-
-		  if (child->expand)
-		    {
-		      if (nexpand_children == 1)
-			child_width += width;
-		      else
-			child_width += extra;
-
-		      nexpand_children -= 1;
-		      width -= extra;
-		    }
-		}
-
-	      if (child->fill)
-		{
-		  child_allocation.width = MAX (1, (gint) child_width - (gint) child->padding * 2);
-		  child_allocation.x = x + child->padding;
-		}
-	      else
-		{
-		  GtkRequisition child_requisition;
-
-		  gtk_widget_get_child_requisition (child->widget, &child_requisition);
-		  child_allocation.width = child_requisition.width;
-		  child_allocation.x = x + (child_width - child_allocation.width) / 2;
-		}
-
-	      if (direction == GTK_TEXT_DIR_RTL)
-		child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
-
-	      gtk_widget_size_allocate (child->widget, &child_allocation);
-
-	      x += child_width + box->spacing;
-	    }
-	}
-
-      x = allocation->x + allocation->width - GTK_CONTAINER (box)->border_width;
+      child_allocation.y = allocation->y + border_width;
+      child_allocation.height = MAX (1, allocation->height - border_width * 2);
 
-      children = box->children;
-      while (children)
-	{
-	  child = children->data;
-	  children = children->next;
-
-	  if ((child->pack == GTK_PACK_END) && GTK_WIDGET_VISIBLE (child->widget))
-	    {
-	      GtkRequisition child_requisition;
-	      gtk_widget_get_child_requisition (child->widget, &child_requisition);
+      for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
+        {
+          if (GTK_PACK_START == packing)
+            x = allocation->x + GTK_CONTAINER (box)->border_width;
+          else
+            x = allocation->x + allocation->width - border_width;;
 
-              if (box->homogeneous)
-                {
-                  if (nvis_children == 1)
-                    child_width = width;
-                  else
-                    child_width = extra;
+          children = box->children;
+          while (children)
+            {
+              child = children->data;
+              children = children->next;
 
-                  nvis_children -= 1;
-                  width -= extra;
-                }
-              else
+              if ((child->pack == packing) && GTK_WIDGET_VISIBLE (child->widget))
                 {
-		  child_width = child_requisition.width + child->padding * 2;
+                  GtkRequisition child_requisition;
+                  gtk_widget_get_child_requisition (child->widget, &child_requisition);
 
-                  if (child->expand)
+                  if (box->homogeneous)
                     {
-                      if (nexpand_children == 1)
-                        child_width += width;
+                      if (nvis_children == 1)
+                        child_width = width;
                       else
-                        child_width += extra;
+                        child_width = extra;
 
-                      nexpand_children -= 1;
+                      nvis_children -= 1;
                       width -= extra;
                     }
-                }
+                  else
+                    {
+                      child_width = child_requisition.width + child->padding * 2;
+
+                      if (child->expand)
+                        {
+                          if (nexpand_children == 1)
+                            child_width += width;
+                          else
+                            child_width += extra;
+
+                          nexpand_children -= 1;
+                          width -= extra;
+                        }
+                    }
 
-              if (child->fill)
-                {
-                  child_allocation.width = MAX (1, (gint)child_width - (gint)child->padding * 2);
-                  child_allocation.x = x + child->padding - child_width;
-                }
-              else
-                {
-		  child_allocation.width = child_requisition.width;
-                  child_allocation.x = x + (child_width - child_allocation.width) / 2 - child_width;
-                }
+                  if (child->fill)
+                    {
+                      child_allocation.width = MAX (1, (gint) child_width - (gint) child->padding * 2);
+                      child_allocation.x = x + child->padding;
+                    }
+                  else
+                    {
+                      child_allocation.width = child_requisition.width;
+                      child_allocation.x = x + (child_width - child_allocation.width) / 2;
+                    }
 
-	      if (direction == GTK_TEXT_DIR_RTL)
-		child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
+                  if (GTK_PACK_END == packing)
+                    child_allocation.x -= child_width;
 
-              gtk_widget_size_allocate (child->widget, &child_allocation);
+                  if (direction == GTK_TEXT_DIR_RTL)
+                    child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
 
-              x -= (child_width + box->spacing);
-	    }
-	}
+                  gtk_widget_size_allocate (child->widget, &child_allocation);
+
+                  if (GTK_PACK_START == packing)
+                    x += child_width + box->spacing;
+                  else
+                    x -= child_width + box->spacing;
+                }
+            }
+        }
     }
 }
 
-- 
1.5.3.7

>From 8c04efe87e1d3c7e43e0f89b1762255598b34639 Mon Sep 17 00:00:00 2001
From: Mathias Hasselmann <mathias openismus com>
Date: Tue, 8 Jan 2008 19:05:49 +0100
Subject: Introduce GtkExtendedLayout interface.

---
 ChangeLog                  |    4 +
 gtk/Makefile.am            |    2 +
 gtk/gtk.h                  |    1 +
 gtk/gtk.symbols            |   12 ++
 gtk/gtkalignment.c         |   14 ++-
 gtk/gtkextendedlayout.c    |  146 +++++++++++++++++++++++++++
 gtk/gtkextendedlayout.h    |   74 ++++++++++++++
 gtk/gtkhbox.c              |  235 ++++++++++++++++++++++++++++++++-----------
 gtk/gtklabel.c             |  239 ++++++++++++++++++++++++++++++++++----------
 gtk/gtksizegroup.c         |  221 ++++++++++++++++++++++++++++------------
 gtk/gtksizegroup.h         |    5 +-
 gtk/gtkwidget.c            |  124 +++++++++++++++++++++--
 gtk/gtkwidget.h            |   14 +++
 tests/Makefile.am          |    5 +-
 tests/testellipsise.c      |   29 +++++-
 tests/testextendedlayout.c |  120 ++++++++++++++++++++++
 16 files changed, 1049 insertions(+), 196 deletions(-)
 create mode 100644 gtk/gtkextendedlayout.c
 create mode 100644 gtk/gtkextendedlayout.h
 create mode 100644 tests/testextendedlayout.c

diff --git a/ChangeLog b/ChangeLog
index aebf3c5..c299a61 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2008-01-03  Mathias Hasselmann  <mathias openismus com>
+
+	Introduce GtkExtendedLayout interface.
+
 2008-01-02  Mathias Hasselmann  <mathias openismus com>
 
 	Merge nearly identical size-allocation loops for start and end packing.
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 5a19467..d614794 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -183,6 +183,7 @@ gtk_public_h_sources =          \
 	gtkenums.h		\
 	gtkeventbox.h		\
 	gtkexpander.h		\
+	gtkextendedlayout.h	\
 	gtkfilechooser.h        \
 	gtkfilechooserbutton.h  \
 	gtkfilechooserdialog.h  \
@@ -447,6 +448,7 @@ gtk_base_c_sources =            \
 	gtkentrycompletion.c	\
 	gtkeventbox.c		\
 	gtkexpander.c		\
+	gtkextendedlayout.c	\
 	gtkfilechooser.c	\
 	gtkfilechooserbutton.c	\
 	gtkfilechooserdefault.c	\
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 0ce693c..36670bb 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -81,6 +81,7 @@
 #include <gtk/gtkenums.h>
 #include <gtk/gtkeventbox.h>
 #include <gtk/gtkexpander.h>
+#include <gtk/gtkextendedlayout.h>
 #include <gtk/gtkfilesel.h>
 #include <gtk/gtkfixed.h>
 #include <gtk/gtkfilechooserbutton.h>
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index e353959..2a00927 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -1326,6 +1326,15 @@ gtk_expander_set_use_underline
 #endif
 #endif
 
+#if IN_HEADER(__GTK_EXTENDED_LAYOUT_H__)
+#if IN_FILE(__GTK_EXTENDED_LAYOUT_C__)
+gtk_extended_layout_get_type G_GNUC_CONST
+gtk_extended_layout_get_desired_size
+gtk_extended_layout_get_height_for_width
+gtk_extended_layout_get_width_for_height
+#endif
+#endif
+
 #if IN_HEADER(__GTK_FILE_CHOOSER_H__)
 #if IN_FILE(__GTK_FILE_CHOOSER_C__)
 gtk_file_chooser_add_filter
@@ -4793,6 +4802,9 @@ gtk_widget_show_all
 gtk_widget_show_now
 gtk_widget_size_allocate
 gtk_widget_size_request
+gtk_widget_get_desired_size
+gtk_widget_get_height_for_width
+gtk_widget_get_width_for_height
 gtk_widget_style_get G_GNUC_NULL_TERMINATED
 gtk_widget_style_get_property
 gtk_widget_style_get_valist
diff --git a/gtk/gtkalignment.c b/gtk/gtkalignment.c
index 5f852d0..9e2b70c 100644
--- a/gtk/gtkalignment.c
+++ b/gtk/gtkalignment.c
@@ -26,6 +26,7 @@
 
 #include <config.h>
 #include "gtkalignment.h"
+#include "gtkextendedlayout.h"
 #include "gtkprivate.h"
 #include "gtkintl.h"
 #include "gtkalias.h"
@@ -460,7 +461,9 @@ gtk_alignment_size_allocate (GtkWidget     *widget,
   
   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
     {
-      gtk_widget_get_child_requisition (bin->child, &child_requisition);
+      GtkExtendedLayout *layout = GTK_EXTENDED_LAYOUT (bin->child);
+
+      gtk_extended_layout_get_desired_size (layout, NULL, &child_requisition);
 
       border_width = GTK_CONTAINER (alignment)->border_width;
 
@@ -470,7 +473,14 @@ gtk_alignment_size_allocate (GtkWidget     *widget,
 
       width = allocation->width - padding_horizontal - 2 * border_width;
       height = allocation->height - padding_vertical - 2 * border_width;
-    
+
+      if (child_requisition.width > width)
+        gtk_extended_layout_get_height_for_width (layout, width, NULL,
+                                                  &child_requisition.height);
+      else if (child_requisition.height > height)
+        gtk_extended_layout_get_width_for_height (layout, height, NULL,
+                                                  &child_requisition.width);
+
       if (width > child_requisition.width)
 	child_allocation.width = (child_requisition.width *
 				  (1.0 - alignment->xscale) +
diff --git a/gtk/gtkextendedlayout.c b/gtk/gtkextendedlayout.c
new file mode 100644
index 0000000..b9d35c7
--- /dev/null
+++ b/gtk/gtkextendedlayout.c
@@ -0,0 +1,146 @@
+/* gtkextendedlayout.c
+ * Copyright (C) 2007 Openismus GmbH
+ *
+ * Author:
+ *      Mathias Hasselmann <mathias openismus com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include <config.h>
+#include "gtkextendedlayout.h"
+#include "gtkintl.h"
+#include "gtkalias.h"
+
+GType
+gtk_extended_layout_get_type (void)
+{
+  static GType extended_layout_type = 0;
+
+  if (G_UNLIKELY(!extended_layout_type))
+    extended_layout_type =
+      g_type_register_static_simple (G_TYPE_INTERFACE, I_("GtkExtendedLayout"),
+                                     sizeof (GtkExtendedLayoutIface),
+                                     NULL, 0, NULL, 0);
+
+  return extended_layout_type;
+}
+
+/**
+ * gtk_extended_layout_get_desired_size:
+ * @layout: a #GtkExtendedLayout instance
+ * @minimum_size: location for storing the minimum size, or %NULL
+ * @natural_size: location for storing the preferred size, or %NULL
+ *
+ * Retreives an extended layout item's desired size.
+ *
+ * Since: 2.16
+ */
+void
+gtk_extended_layout_get_desired_size (GtkExtendedLayout *layout,
+                                      GtkRequisition    *minimum_size,
+                                      GtkRequisition    *natural_size)
+{
+  GtkExtendedLayoutIface *iface;
+
+  g_return_if_fail (GTK_IS_EXTENDED_LAYOUT (layout));
+  g_return_if_fail (NULL != minimum_size || NULL != natural_size);
+
+  iface = GTK_EXTENDED_LAYOUT_GET_IFACE (layout);
+  iface->get_desired_size (layout, minimum_size, natural_size);
+}
+
+/**
+ * gtk_extended_layout_get_width_for_height:
+ * @layout: a #GtkExtendedLayout instance
+ * @height: the size which is available for allocation
+ * @minimum_size: location for storing the minimum size, or %NULL
+ * @natural_size: location for storing the preferred size, or %NULL
+ *
+ * Retreives an extended layout item's desired width if it would given
+ * the size specified in @height.
+ *
+ * Since: 2.16
+ */
+void
+gtk_extended_layout_get_width_for_height (GtkExtendedLayout *layout,
+                                          gint               height,
+                                          gint              *minimum_width,
+                                          gint              *natural_width)
+{
+  GtkExtendedLayoutIface *iface;
+
+  g_return_if_fail (GTK_IS_EXTENDED_LAYOUT (layout));
+  iface = GTK_EXTENDED_LAYOUT_GET_IFACE (layout);
+
+  if (iface->get_width_for_height)
+    iface->get_width_for_height (layout, height, minimum_width, natural_width);
+  else
+    {
+      GtkRequisition minimum_size;
+      GtkRequisition natural_size;
+
+      iface->get_desired_size (layout, &minimum_size, &natural_size);
+
+      if (minimum_width)
+        *minimum_width = minimum_size.width;
+      if (natural_width)
+        *natural_width = natural_size.width;
+    }
+}
+
+/**
+ * gtk_extended_layout_get_height_for_width:
+ * @layout: a #GtkExtendedLayout instance
+ * @width: the size which is available for allocation
+ * @minimum_size: location for storing the minimum size, or %NULL
+ * @natural_size: location for storing the preferred size, or %NULL
+ *
+ * Retreives an extended layout item's desired height if it would given
+ * the size specified in @width.
+ *
+ * Since: 2.16
+ */
+void
+gtk_extended_layout_get_height_for_width (GtkExtendedLayout *layout,
+                                          gint               width,
+                                          gint              *minimum_height,
+                                          gint              *natural_height)
+{
+  GtkExtendedLayoutIface *iface;
+
+  g_return_if_fail (GTK_IS_EXTENDED_LAYOUT (layout));
+  iface = GTK_EXTENDED_LAYOUT_GET_IFACE (layout);
+
+  if (iface->get_height_for_width)
+    iface->get_height_for_width (layout, width, minimum_height, natural_height);
+  else
+    {
+      GtkRequisition minimum_size;
+      GtkRequisition natural_size;
+
+      iface->get_desired_size (layout, &minimum_size, &natural_size);
+
+      if (minimum_height)
+        *minimum_height = minimum_size.height;
+      if (natural_height)
+        *natural_height = natural_size.height;
+    }
+}
+
+#define __GTK_EXTENDED_LAYOUT_C__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkextendedlayout.h b/gtk/gtkextendedlayout.h
new file mode 100644
index 0000000..9944fed
--- /dev/null
+++ b/gtk/gtkextendedlayout.h
@@ -0,0 +1,74 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2007 Openismus GmbH
+ *
+ * Author:
+ *      Mathias Hasselmann <mathias openismus com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_EXTENDED_LAYOUT_H__
+#define __GTK_EXTENDED_LAYOUT_H__
+
+#include <gtk/gtkwidget.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_EXTENDED_LAYOUT            (gtk_extended_layout_get_type ())
+#define GTK_EXTENDED_LAYOUT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_EXTENDED_LAYOUT, GtkExtendedLayout))
+#define GTK_EXTENDED_LAYOUT_CLASS(klass)    ((GtkExtendedLayoutIface*)g_type_interface_peek ((klass), GTK_TYPE_EXTENDED_LAYOUT))
+#define GTK_IS_EXTENDED_LAYOUT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_EXTENDED_LAYOUT))
+#define GTK_EXTENDED_LAYOUT_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_EXTENDED_LAYOUT, GtkExtendedLayoutIface))
+
+typedef struct _GtkExtendedLayout           GtkExtendedLayout;
+typedef struct _GtkExtendedLayoutIface      GtkExtendedLayoutIface;
+
+struct _GtkExtendedLayoutIface
+{
+  GTypeInterface g_iface;
+
+  /* virtual table */
+
+  void (*get_desired_size)     (GtkExtendedLayout  *layout,
+                                GtkRequisition     *minimum_size,
+                                GtkRequisition     *natural_size);
+  void (*get_width_for_height) (GtkExtendedLayout  *layout,
+                                gint                height,
+                                gint               *minimum_width,
+                                gint               *natural_width);
+  void (*get_height_for_width) (GtkExtendedLayout  *layout,
+                                gint                width,
+                                gint               *minimum_height,
+                                gint               *natural_height);
+};
+
+GType gtk_extended_layout_get_type             (void) G_GNUC_CONST;
+
+void  gtk_extended_layout_get_desired_size     (GtkExtendedLayout *layout,
+                                                GtkRequisition    *minimum_size,
+                                                GtkRequisition    *natural_size);
+void  gtk_extended_layout_get_width_for_height (GtkExtendedLayout *layout,
+                                                gint               height,
+                                                gint              *minimum_width,
+                                                gint              *natural_width);
+void  gtk_extended_layout_get_height_for_width (GtkExtendedLayout *layout,
+                                                gint               width,
+                                                gint              *minimum_height,
+                                                gint              *natural_height);
+
+G_END_DECLS
+
+#endif /* __GTK_EXTENDED_LAYOUT_H__ */
diff --git a/gtk/gtkhbox.c b/gtk/gtkhbox.c
index c949251..bb5d199 100644
--- a/gtk/gtkhbox.c
+++ b/gtk/gtkhbox.c
@@ -26,18 +26,36 @@
 
 #include <config.h>
 #include "gtkhbox.h"
+#include "gtkextendedlayout.h"
 #include "gtkintl.h"
 #include "gtkalias.h"
 
+typedef struct _GtkBoxDesiredSizes GtkBoxDesiredSizes;
+typedef struct _GtkBoxSpreading    GtkBoxSpreading;
 
-static void gtk_hbox_size_request  (GtkWidget      *widget,
-				    GtkRequisition *requisition);
-static void gtk_hbox_size_allocate (GtkWidget      *widget,
-				    GtkAllocation  *allocation);
+struct _GtkBoxDesiredSizes
+{
+  gint minimum_size;
+  gint natural_size;
+};
 
+struct _GtkBoxSpreading
+{
+  GtkBoxChild *child;
+  gint index;
+};
 
-G_DEFINE_TYPE (GtkHBox, gtk_hbox, GTK_TYPE_BOX)
+static void gtk_hbox_get_desired_size      (GtkExtendedLayout *layout,
+                                            GtkRequisition    *minimum_size,
+                                            GtkRequisition    *natural_size);
+static void gtk_hbox_size_allocate         (GtkWidget         *widget,
+                                            GtkAllocation     *allocation);
 
+static void gtk_hbox_layout_interface_init (GtkExtendedLayoutIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GtkHBox, gtk_hbox, GTK_TYPE_BOX,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
+                                                gtk_hbox_layout_interface_init))
 static void
 gtk_hbox_class_init (GtkHBoxClass *class)
 {
@@ -45,11 +63,16 @@ gtk_hbox_class_init (GtkHBoxClass *class)
 
   widget_class = (GtkWidgetClass*) class;
 
-  widget_class->size_request = gtk_hbox_size_request;
   widget_class->size_allocate = gtk_hbox_size_allocate;
 }
 
 static void
+gtk_hbox_layout_interface_init (GtkExtendedLayoutIface *iface)
+{
+  iface->get_desired_size = gtk_hbox_get_desired_size;
+}
+
+static void
 gtk_hbox_init (GtkHBox *hbox)
 {
 }
@@ -70,57 +93,103 @@ gtk_hbox_new (gboolean homogeneous,
 
 
 static void
-gtk_hbox_size_request (GtkWidget      *widget,
-		       GtkRequisition *requisition)
+gtk_hbox_get_desired_size (GtkExtendedLayout *layout,
+                           GtkRequisition    *minimum_size,
+                           GtkRequisition    *natural_size)
 {
   GtkBox *box;
-  GtkBoxChild *child;
   GList *children;
   gint nvis_children;
-  gint width;
+  gint border_width;
 
-  box = GTK_BOX (widget);
-  requisition->width = 0;
-  requisition->height = 0;
-  nvis_children = 0;
+  box = GTK_BOX (layout);
+  border_width = GTK_CONTAINER (box)->border_width;
+
+  minimum_size->width = minimum_size->height = 0;
+  natural_size->width = natural_size->height = 0;
 
+  nvis_children = 0;
   children = box->children;
   while (children)
     {
+      GtkBoxChild *child;
+
       child = children->data;
       children = children->next;
 
       if (GTK_WIDGET_VISIBLE (child->widget))
-	{
-	  GtkRequisition child_requisition;
+        {
+          GtkRequisition child_minimum_size;
+          GtkRequisition child_natural_size;
 
-	  gtk_widget_size_request (child->widget, &child_requisition);
+          gtk_widget_get_desired_size (child->widget,
+                                       &child_minimum_size,
+                                       &child_natural_size);
 
-	  if (box->homogeneous)
-	    {
-	      width = child_requisition.width + child->padding * 2;
-	      requisition->width = MAX (requisition->width, width);
-	    }
-	  else
-	    {
-	      requisition->width += child_requisition.width + child->padding * 2;
-	    }
+          if (box->homogeneous)
+            {
+              gint width;
 
-	  requisition->height = MAX (requisition->height, child_requisition.height);
+              width = child_minimum_size.width + child->padding * 2;
+              minimum_size->width = MAX (minimum_size->width, width);
 
-	  nvis_children += 1;
-	}
+              width = child_natural_size.width + child->padding * 2;
+              natural_size->width = MAX (natural_size->width, width);
+            }
+          else
+            {
+              minimum_size->width += child_minimum_size.width + child->padding * 2;
+              natural_size->width += child_natural_size.width + child->padding * 2;
+            }
+
+          minimum_size->height = MAX (minimum_size->height, child_minimum_size.height);
+          natural_size->height = MAX (natural_size->height, child_natural_size.height);
+
+          nvis_children += 1;
+        }
     }
 
   if (nvis_children > 0)
     {
       if (box->homogeneous)
-	requisition->width *= nvis_children;
-      requisition->width += (nvis_children - 1) * box->spacing;
+        {
+          minimum_size->width *= nvis_children;
+          natural_size->width *= nvis_children;
+        }
+
+      minimum_size->width += (nvis_children - 1) * box->spacing;
+      natural_size->width += (nvis_children - 1) * box->spacing;
     }
 
-  requisition->width += GTK_CONTAINER (box)->border_width * 2;
-  requisition->height += GTK_CONTAINER (box)->border_width * 2;
+  minimum_size->width += border_width * 2;
+  minimum_size->height += border_width * 2;
+
+  natural_size->width += border_width * 2;
+  natural_size->height += border_width * 2;
+}
+
+static gint
+gtk_vbox_compare_gap (gconstpointer p1,
+                      gconstpointer p2,
+                      gpointer      data)
+{
+  GtkBoxDesiredSizes *sizes = data;
+  const GtkBoxSpreading *c1 = p1;
+  const GtkBoxSpreading *c2 = p2;
+
+  const gint d1 = MAX (sizes[c1->index].natural_size -
+                       sizes[c1->index].minimum_size,
+                       0);
+  const gint d2 = MAX (sizes[c2->index].natural_size -
+                       sizes[c2->index].minimum_size,
+                       0);
+
+  gint delta = (d1 - d2);
+
+  if (0 == delta)
+    delta = (c1->index - c2->index);
+
+  return delta;
 }
 
 static void
@@ -130,21 +199,12 @@ gtk_hbox_size_allocate (GtkWidget     *widget,
   GtkBox *box;
   GtkBoxChild *child;
   GList *children;
-  GtkAllocation child_allocation;
   gint nvis_children;
   gint nexpand_children;
-  gint child_width;
-  gint width;
-  gint extra;
-  gint x;
-  GtkTextDirection direction;
-  GtkPackType packing;
 
   box = GTK_BOX (widget);
   widget->allocation = *allocation;
 
-  direction = gtk_widget_get_direction (widget);
-  
   nvis_children = 0;
   nexpand_children = 0;
   children = box->children;
@@ -165,23 +225,78 @@ gtk_hbox_size_allocate (GtkWidget     *widget,
   if (nvis_children > 0)
     {
       gint border_width = GTK_CONTAINER (box)->border_width;
+      GtkTextDirection direction = gtk_widget_get_direction (widget);
+      GtkAllocation child_allocation;
+
+      GtkBoxSpreading *spreading = g_newa (GtkBoxSpreading, nvis_children);
+      GtkBoxDesiredSizes *sizes = g_newa (GtkBoxDesiredSizes, nvis_children);
+
+      GtkPackType packing;
+      gint child_width;
+
+      gint width;
+      gint extra;
+      gint x, i;
+
+      width = (allocation->width - border_width * 2 -
+               (nvis_children - 1) * box->spacing);
 
       if (box->homogeneous)
 	{
-	  width = (allocation->width - border_width * 2 -
-		   (nvis_children - 1) * box->spacing);
 	  extra = width / nvis_children;
 	}
-      else if (nexpand_children > 0)
-	{
-	  width = (gint) allocation->width - (gint) widget->requisition.width;
-	  extra = width / nexpand_children;
-	}
       else
-	{
-	  width = 0;
-	  extra = 0;
-	}
+        {
+          /* Retreive desired size for visible children */
+
+          for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
+            {
+              i = 0;
+              children = box->children;
+              while (children)
+                {
+                  child = children->data;
+                  children = children->next;
+
+                  if (child->pack == packing && GTK_WIDGET_VISIBLE (child->widget))
+                    {
+                      gtk_widget_get_width_for_height (child->widget,
+                                                       allocation->height,
+                                                       &sizes[i].minimum_size,
+                                                       &sizes[i].natural_size);
+
+                      width -= sizes[i].minimum_size;
+
+                      spreading[i].index = i;
+                      spreading[i].child = child;
+
+                      i += 1;
+                    }
+                }
+            }
+
+          /* Sort children by difference between natural and minimum size */
+
+          g_qsort_with_data (spreading,
+                             nvis_children, sizeof (GtkBoxSpreading),
+                             gtk_vbox_compare_gap, sizes);
+
+          for (i = 0; width > 0 && i < nvis_children; ++i)
+            {
+              extra = sizes[spreading[i].index].natural_size
+                    - sizes[spreading[i].index].minimum_size;
+
+              extra = MIN (width, extra);
+              width -= extra;
+
+              sizes[spreading[i].index].minimum_size += extra;
+            }
+
+          if (nexpand_children > 0)
+            extra = width / nexpand_children;
+          else
+            extra = 0;
+        }
 
       child_allocation.y = allocation->y + border_width;
       child_allocation.height = MAX (1, allocation->height - border_width * 2);
@@ -193,17 +308,17 @@ gtk_hbox_size_allocate (GtkWidget     *widget,
           else
             x = allocation->x + allocation->width - border_width;;
 
+          /* Allocate child positions */
+
+          i = 0;
           children = box->children;
           while (children)
             {
               child = children->data;
               children = children->next;
 
-              if ((child->pack == packing) && GTK_WIDGET_VISIBLE (child->widget))
+              if (child->pack == packing && GTK_WIDGET_VISIBLE (child->widget))
                 {
-                  GtkRequisition child_requisition;
-                  gtk_widget_get_child_requisition (child->widget, &child_requisition);
-
                   if (box->homogeneous)
                     {
                       if (nvis_children == 1)
@@ -216,7 +331,7 @@ gtk_hbox_size_allocate (GtkWidget     *widget,
                     }
                   else
                     {
-                      child_width = child_requisition.width + child->padding * 2;
+                      child_width = sizes[i].minimum_size + child->padding * 2;
 
                       if (child->expand)
                         {
@@ -237,7 +352,7 @@ gtk_hbox_size_allocate (GtkWidget     *widget,
                     }
                   else
                     {
-                      child_allocation.width = child_requisition.width;
+                      child_allocation.width = sizes[i].minimum_size;
                       child_allocation.x = x + (child_width - child_allocation.width) / 2;
                     }
 
@@ -253,6 +368,8 @@ gtk_hbox_size_allocate (GtkWidget     *widget,
                     x += child_width + box->spacing;
                   else
                     x -= child_width + box->spacing;
+
+                  i += 1;
                 }
             }
         }
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index b3fe927..4f73664 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -43,6 +43,7 @@
 #include "gtknotebook.h"
 #include "gtkstock.h"
 #include "gtkbindings.h"
+#include "gtkextendedlayout.h"
 #include "gtkprivate.h"
 #include "gtkalias.h"
 
@@ -67,7 +68,7 @@ struct _GtkLabelSelectionInfo
   gint selection_anchor;
   gint selection_end;
   GtkWidget *popup_menu;
-  
+
   gint drag_start_x;
   gint drag_start_y;
 
@@ -116,8 +117,6 @@ static void gtk_label_get_property      (GObject          *object,
 					 GParamSpec       *pspec);
 static void gtk_label_destroy           (GtkObject        *object);
 static void gtk_label_finalize          (GObject          *object);
-static void gtk_label_size_request      (GtkWidget        *widget,
-					 GtkRequisition   *requisition);
 static void gtk_label_size_allocate     (GtkWidget        *widget,
                                          GtkAllocation    *allocation);
 static void gtk_label_state_changed     (GtkWidget        *widget,
@@ -201,10 +200,25 @@ static gint gtk_label_move_forward_word  (GtkLabel        *label,
 static gint gtk_label_move_backward_word (GtkLabel        *label,
 					  gint             start);
 
-static GQuark quark_angle = 0;
+static void gtk_label_layout_interface_init (GtkExtendedLayoutIface *iface);
 
-G_DEFINE_TYPE (GtkLabel, gtk_label, GTK_TYPE_MISC)
+static void gtk_label_get_desired_size      (GtkExtendedLayout      *layout,
+                                             GtkRequisition         *minimum_size,
+                                             GtkRequisition         *natural_size);
+static void gtk_label_get_width_for_height  (GtkExtendedLayout      *layout,
+                                             gint                    height,
+                                             gint                   *minimum_width,
+                                             gint                   *natural_width);
+static void gtk_label_get_height_for_width  (GtkExtendedLayout      *layout,
+                                             gint                    width,
+                                             gint                   *minimum_height,
+                                             gint                   *natural_height);
 
+static GQuark quark_angle = 0;
+
+G_DEFINE_TYPE_WITH_CODE (GtkLabel, gtk_label, GTK_TYPE_MISC,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
+                                                gtk_label_layout_interface_init))
 static void
 add_move_binding (GtkBindingSet  *binding_set,
 		  guint           keyval,
@@ -229,6 +243,14 @@ add_move_binding (GtkBindingSet  *binding_set,
 }
 
 static void
+gtk_label_layout_interface_init (GtkExtendedLayoutIface *iface)
+{
+  iface->get_desired_size = gtk_label_get_desired_size;
+  iface->get_width_for_height = gtk_label_get_width_for_height;
+  iface->get_height_for_width = gtk_label_get_height_for_width;
+}
+
+static void
 gtk_label_class_init (GtkLabelClass *class)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
@@ -243,8 +265,7 @@ gtk_label_class_init (GtkLabelClass *class)
   gobject_class->finalize = gtk_label_finalize;
 
   object_class->destroy = gtk_label_destroy;
-  
-  widget_class->size_request = gtk_label_size_request;
+
   widget_class->size_allocate = gtk_label_size_allocate;
   widget_class->state_changed = gtk_label_state_changed;
   widget_class->style_set = gtk_label_style_set;
@@ -2161,23 +2182,35 @@ gtk_label_ensure_layout (GtkLabel *label)
     }
 }
 
+static gint
+get_single_line_height (GtkWidget   *widget,
+                        PangoLayout *layout)
+{
+  PangoContext *context;
+  PangoFontMetrics *metrics;
+  gint ascent, descent;
+
+  context = pango_layout_get_context (layout);
+  metrics = pango_context_get_metrics (context, widget->style->font_desc,
+                                       pango_context_get_language (context));
+
+  ascent = pango_font_metrics_get_ascent (metrics);
+  descent = pango_font_metrics_get_descent (metrics);
+  pango_font_metrics_unref (metrics);
+
+  return PANGO_PIXELS (ascent + descent);
+}
+
 static void
-gtk_label_size_request (GtkWidget      *widget,
-			GtkRequisition *requisition)
+gtk_label_get_desired_size (GtkExtendedLayout *layout,
+                            GtkRequisition    *minimum_size,
+                            GtkRequisition    *natural_size)
 {
-  GtkLabel *label;
-  GtkLabelPrivate *priv;
-  PangoRectangle logical_rect;
+  GtkLabelPrivate *priv = GTK_LABEL_GET_PRIVATE (layout);
+  GtkLabel *label = GTK_LABEL (layout);
   PangoRectangle required_rect;
-  GtkWidgetAuxInfo *aux_info;
-  
-  g_return_if_fail (GTK_IS_LABEL (widget));
-  g_return_if_fail (requisition != NULL);
-  
-  label = GTK_LABEL (widget);
-  priv = GTK_LABEL_GET_PRIVATE (widget);
 
-  /*  
+  /*
    * If word wrapping is on, then the height requisition can depend
    * on:
    *
@@ -2195,50 +2228,152 @@ gtk_label_size_request (GtkWidget      *widget,
 
   gtk_label_ensure_layout (label);
 
-  aux_info = _gtk_widget_get_aux_info (widget, FALSE);
+  if (minimum_size)
+    {
+      GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (GTK_WIDGET (label), FALSE);
 
-  pango_layout_get_extents (label->layout, NULL, &logical_rect);
-  required_rect.x = required_rect.y = 0;
+      pango_layout_get_extents (label->layout, NULL, &required_rect);
+      required_rect.x = required_rect.y = 0;
 
-  if ((label->wrap || label->ellipsize || 
-       priv->width_chars > 0 || priv->max_width_chars > 0) && 
-      aux_info && aux_info->width > 0)
-    required_rect.width = aux_info->width;
-  else if (label->ellipsize || priv->width_chars > 0 || priv->max_width_chars > 0)
-    {
-      required_rect.width = PANGO_PIXELS (get_label_char_width (label));
+      if (label->ellipsize || priv->width_chars > 0 || priv->max_width_chars > 0)
+        {
+          /* backup the Pango layout, as get_label_char_width() scrambles it */
+
+          PangoLayout *backup = label->layout;
+          label->layout = pango_layout_copy (label->layout);
+
+          required_rect.width = get_label_char_width (label);
+
+          g_object_unref (label->layout);
+          label->layout = backup;
+        }
+
+      if (label->single_line_mode)
+        required_rect.height = get_single_line_height (GTK_WIDGET (label), label->layout);
+
+      if (label->have_transform)
+        {
+          PangoContext *context = pango_layout_get_context (label->layout);
+          const PangoMatrix *matrix = pango_context_get_matrix (context);
+          pango_matrix_transform_rectangle (matrix, &required_rect);
+        }
+
+      required_rect.width = PANGO_PIXELS_CEIL (required_rect.width);
+      required_rect.height = PANGO_PIXELS_CEIL (required_rect.height);
+
+      if ((label->wrap || label->ellipsize ||
+           priv->width_chars > 0 || priv->max_width_chars > 0) &&
+          aux_info && aux_info->width > 0)
+        required_rect.width = aux_info->width;
+
+      minimum_size->width = required_rect.width + label->misc.xpad * 2;
+      minimum_size->height = required_rect.height + label->misc.ypad * 2;
     }
-  else
-    required_rect.width = PANGO_PIXELS (logical_rect.width);
 
-  if (label->single_line_mode)
+  if (natural_size)
     {
-      PangoContext *context;
-      PangoFontMetrics *metrics;
-      gint ascent, descent;
+      PangoLayout *natural_layout = pango_layout_copy (label->layout);
 
-      context = pango_layout_get_context (label->layout);
-      metrics = pango_context_get_metrics (context, widget->style->font_desc,
-                                           pango_context_get_language (context));
+      pango_layout_set_width (natural_layout, -1);
+      pango_layout_set_ellipsize (natural_layout, PANGO_ELLIPSIZE_NONE);
 
-      ascent = pango_font_metrics_get_ascent (metrics);
-      descent = pango_font_metrics_get_descent (metrics);
-      pango_font_metrics_unref (metrics);
-    
-      required_rect.height = PANGO_PIXELS (ascent + descent);
+      pango_layout_get_extents (natural_layout, NULL, &required_rect);
+      required_rect.x = required_rect.y = 0;
+
+      if (label->single_line_mode)
+        required_rect.height = get_single_line_height (GTK_WIDGET (label), label->layout);
+
+      if (label->have_transform)
+        {
+          PangoContext *context = pango_layout_get_context (natural_layout);
+          const PangoMatrix *matrix = pango_context_get_matrix (context);
+          pango_matrix_transform_rectangle (matrix, &required_rect);
+        }
+
+      required_rect.width = PANGO_PIXELS_CEIL (required_rect.width);
+      required_rect.height = PANGO_PIXELS_CEIL (required_rect.height);
+
+      natural_size->width = required_rect.width + label->misc.xpad * 2;
+      natural_size->height = required_rect.height + label->misc.ypad * 2;
+
+      g_object_unref (natural_layout);
     }
-  else
-    required_rect.height = PANGO_PIXELS (logical_rect.height);
+}
+
+static void
+get_size_for_allocation (GtkLabel *label,
+                         gint      allocation,
+                         gint     *minimum_size,
+                         gint     *natural_size)
+{
+  PangoLayout *layout;
+
+  gtk_label_ensure_layout (label);
+  layout = pango_layout_copy (label->layout);
+  pango_layout_set_width (layout, PANGO_SCALE * allocation);
 
-  if (label->have_transform)
+  if (minimum_size)
+    pango_layout_get_pixel_size (layout, NULL, minimum_size);
+
+  if (natural_size)
     {
-      PangoContext *context = pango_layout_get_context (label->layout);
-      const PangoMatrix *matrix = pango_context_get_matrix (context);
-      pango_matrix_transform_pixel_rectangle (matrix, &required_rect);
+//      pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE);
+      pango_layout_get_pixel_size (layout, NULL, natural_size);
     }
 
-  requisition->width = required_rect.width + label->misc.xpad * 2;
-  requisition->height = required_rect.height + label->misc.ypad * 2;
+  g_object_unref (layout);
+}
+
+static void
+gtk_label_get_width_for_height (GtkExtendedLayout *layout,
+                                gint               height,
+                                gint              *minimum_width,
+                                gint              *natural_width)
+{
+  GtkLabel *label = GTK_LABEL (layout);
+  gdouble angle = gtk_label_get_angle (label);
+
+  if (90 == angle || 270 == angle)
+    get_size_for_allocation (label, height, minimum_width, natural_width);
+  else
+    {
+      GtkRequisition minimum_size, natural_size;
+
+      gtk_extended_layout_get_desired_size (layout,
+                                            minimum_width ? &minimum_size : NULL,
+                                            natural_width ? &natural_size : NULL);
+
+      if (minimum_width)
+        *minimum_width = minimum_size.width;
+      if (natural_width)
+        *natural_width = natural_size.width;
+    }
+}
+
+static void
+gtk_label_get_height_for_width (GtkExtendedLayout *layout,
+                                gint               width,
+                                gint              *minimum_height,
+                                gint              *natural_height)
+{
+  GtkLabel *label = GTK_LABEL (layout);
+  gdouble angle = gtk_label_get_angle (label);
+
+  if (0 == angle || 180 == angle)
+    get_size_for_allocation (label, width, minimum_height, natural_height);
+  else
+    {
+      GtkRequisition minimum_size, natural_size;
+
+      gtk_extended_layout_get_desired_size (layout,
+                                            minimum_height ? &minimum_size : NULL,
+                                            natural_height ? &natural_size : NULL);
+
+      if (minimum_height)
+        *minimum_height = minimum_size.height;
+      if (natural_height)
+        *natural_height = natural_size.height;
+    }
 }
 
 static void
diff --git a/gtk/gtksizegroup.c b/gtk/gtksizegroup.c
index 477c45f..2cd431b 100644
--- a/gtk/gtksizegroup.c
+++ b/gtk/gtksizegroup.c
@@ -25,8 +25,18 @@
 #include "gtkprivate.h"
 #include "gtksizegroup.h"
 #include "gtkbuildable.h"
+#include "gtkextendedlayout.h"
 #include "gtkalias.h"
 
+#define GTK_SIZE_GROUP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_SIZE_GROUP, GtkSizeGroupPrivate))
+
+typedef struct _GtkSizeGroupPrivate GtkSizeGroupPrivate;
+
+struct _GtkSizeGroupPrivate
+{
+  GtkRequisition natural_size;
+};
+
 enum {
   PROP_0,
   PROP_MODE,
@@ -314,6 +324,7 @@ gtk_size_group_class_init (GtkSizeGroupClass *klass)
 							 GTK_PARAM_READWRITE));
   
   initialize_size_group_quarks ();
+  g_type_class_add_private (klass, sizeof (GtkSizeGroupPrivate));
 }
 
 static void
@@ -588,25 +599,49 @@ gtk_size_group_get_widgets (GtkSizeGroup *size_group)
   return size_group->widgets;
 }
 
-static gint
-get_base_dimension (GtkWidget        *widget,
-		    GtkSizeGroupMode  mode)
+static void
+get_base_dimensions (GtkWidget        *widget,
+                     GtkSizeGroupMode  mode,
+                     gint             *minimum_size,
+                     gint             *natural_size)
 {
   GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
 
   if (mode == GTK_SIZE_GROUP_HORIZONTAL)
     {
-      if (aux_info && aux_info->width > 0)
-	return aux_info->width;
-      else
-	return widget->requisition.width;
+      if (minimum_size)
+        {
+          if (aux_info && aux_info->width > 0)
+            *minimum_size = aux_info->width;
+          else
+            *minimum_size = widget->requisition.width;
+        }
+
+      if (natural_size)
+        {
+          if (aux_info)
+            *natural_size = aux_info->natural_size.width;
+          else
+            *natural_size = widget->requisition.width;
+        }
     }
   else
     {
-      if (aux_info && aux_info->height > 0)
-	return aux_info->height;
-      else
-	return widget->requisition.height;
+      if (minimum_size)
+        {
+          if (aux_info && aux_info->height > 0)
+            *minimum_size = aux_info->height;
+          else
+            *minimum_size = widget->requisition.height;
+        }
+
+      if (natural_size)
+        {
+          if (aux_info)
+            *natural_size = aux_info->natural_size.height;
+          else
+            *natural_size = widget->requisition.height;
+        }
     }
 }
 
@@ -615,31 +650,39 @@ do_size_request (GtkWidget *widget)
 {
   if (GTK_WIDGET_REQUEST_NEEDED (widget))
     {
-      gtk_widget_ensure_style (widget);      
+      GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, TRUE);
+
+      gtk_widget_ensure_style (widget);
       GTK_PRIVATE_UNSET_FLAG (widget, GTK_REQUEST_NEEDED);
-      g_signal_emit_by_name (widget,
-			     "size_request",
-			     &widget->requisition);
+
+      gtk_extended_layout_get_desired_size (GTK_EXTENDED_LAYOUT (widget),
+                                            &widget->requisition,
+                                            &aux_info->natural_size);
+
+      g_assert (widget->requisition.width <= aux_info->natural_size.width);
+      g_assert (widget->requisition.height <= aux_info->natural_size.height);
     }
 }
 
-static gint
-compute_base_dimension (GtkWidget        *widget,
-			GtkSizeGroupMode  mode)
+static void
+compute_base_dimensions (GtkWidget        *widget,
+                         GtkSizeGroupMode  mode,
+                         gint             *minimum_size,
+                         gint             *natural_size)
 {
   do_size_request (widget);
-
-  return get_base_dimension (widget, mode);
+  get_base_dimensions (widget, mode, minimum_size, natural_size);
 }
 
-static gint
+static void
 compute_dimension (GtkWidget        *widget,
-		   GtkSizeGroupMode  mode)
+                   GtkSizeGroupMode  mode,
+                   gint             *minimum_size,
+                   gint             *natural_size)
 {
   GSList *widgets = NULL;
   GSList *groups = NULL;
   GSList *tmp_list;
-  gint result = 0;
 
   add_widget_to_closure (widget, mode, &groups, &widgets);
 
@@ -650,16 +693,26 @@ compute_dimension (GtkWidget        *widget,
   
   if (!groups)
     {
-      result = compute_base_dimension (widget, mode);
+      compute_base_dimensions (widget, mode, minimum_size, natural_size);
     }
   else
     {
       GtkSizeGroup *group = groups->data;
+      GtkSizeGroupPrivate *priv = GTK_SIZE_GROUP_GET_PRIVATE (group);
+
+      gint result_minimum_size = 0;
+      gint result_natural_size = 0;
 
       if (mode == GTK_SIZE_GROUP_HORIZONTAL && group->have_width)
-	result = group->requisition.width;
+        {
+          result_minimum_size = group->requisition.width;
+          result_natural_size = priv->natural_size.width;
+        }
       else if (mode == GTK_SIZE_GROUP_VERTICAL && group->have_height)
-	result = group->requisition.height;
+        {
+          result_minimum_size = group->requisition.height;
+          result_natural_size = priv->natural_size.height;
+        }
       else
 	{
 	  tmp_list = widgets;
@@ -667,13 +720,20 @@ compute_dimension (GtkWidget        *widget,
 	    {
 	      GtkWidget *tmp_widget = tmp_list->data;
 
-	      gint dimension = compute_base_dimension (tmp_widget, mode);
+              gint tmp_widget_minimum_size;
+              gint tmp_widget_natural_size;
 
-	      if (GTK_WIDGET_MAPPED (tmp_widget) || !group->ignore_hidden)
-		{
-		  if (dimension > result)
-		    result = dimension;
-		}
+              compute_base_dimensions (tmp_widget, mode,
+                                       &tmp_widget_minimum_size,
+                                       &tmp_widget_natural_size);
+
+              if (GTK_WIDGET_MAPPED (tmp_widget) || !group->ignore_hidden)
+                {
+                  if (result_minimum_size < tmp_widget_minimum_size)
+                    result_minimum_size = tmp_widget_minimum_size;
+                  if (result_natural_size < tmp_widget_natural_size)
+                    result_natural_size = tmp_widget_natural_size;
+                }
 
 	      tmp_list = tmp_list->next;
 	    }
@@ -682,38 +742,45 @@ compute_dimension (GtkWidget        *widget,
 	  while (tmp_list)
 	    {
 	      GtkSizeGroup *tmp_group = tmp_list->data;
+              GtkSizeGroupPrivate *tmp_priv = GTK_SIZE_GROUP_GET_PRIVATE (tmp_group);
 
 	      if (mode == GTK_SIZE_GROUP_HORIZONTAL)
 		{
 		  tmp_group->have_width = TRUE;
-		  tmp_group->requisition.width = result;
+                  tmp_group->requisition.width = result_minimum_size;
+                  tmp_priv->natural_size.width = result_natural_size;
 		}
 	      else
 		{
 		  tmp_group->have_height = TRUE;
-		  tmp_group->requisition.height = result;
+                  tmp_group->requisition.height = result_minimum_size;
+                  tmp_priv->natural_size.height = result_natural_size;
 		}
 	      
 	      tmp_list = tmp_list->next;
 	    }
 	}
+
+      if (minimum_size)
+        *minimum_size = result_minimum_size;
+      if (natural_size)
+        *natural_size = result_natural_size;
     }
 
   g_slist_foreach (widgets, (GFunc)g_object_unref, NULL);
 
   g_slist_free (widgets);
   g_slist_free (groups);
-
-  return result;
 }
 
-static gint
-get_dimension (GtkWidget        *widget,
-	       GtkSizeGroupMode  mode)
+static void
+get_dimensions (GtkWidget        *widget,
+                GtkSizeGroupMode  mode,
+                gint             *minimum_size,
+                gint             *natural_size)
 {
   GSList *widgets = NULL;
   GSList *groups = NULL;
-  gint result = 0;
 
   add_widget_to_closure (widget, mode, &groups, &widgets);
 
@@ -722,22 +789,31 @@ get_dimension (GtkWidget        *widget,
 
   if (!groups)
     {
-      result = get_base_dimension (widget, mode);
+      get_base_dimensions (widget, mode, minimum_size, natural_size);
     }
   else
     {
       GtkSizeGroup *group = groups->data;
+      GtkSizeGroupPrivate *priv = GTK_SIZE_GROUP_GET_PRIVATE (group);
 
       if (mode == GTK_SIZE_GROUP_HORIZONTAL && group->have_width)
-	result = group->requisition.width;
+        {
+          if (minimum_size)
+            *minimum_size = group->requisition.width;
+          if (natural_size)
+            *natural_size = priv->natural_size.width;
+        }
       else if (mode == GTK_SIZE_GROUP_VERTICAL && group->have_height)
-	result = group->requisition.height;
+        {
+          if (minimum_size)
+            *minimum_size = group->requisition.height;
+          if (natural_size)
+            *natural_size = priv->natural_size.height;
+        }
     }
 
   g_slist_free (widgets);
   g_slist_free (groups);
-
-  return result;
 }
 
 static void
@@ -752,11 +828,23 @@ get_fast_child_requisition (GtkWidget      *widget,
     {
       if (aux_info->width > 0)
 	requisition->width = aux_info->width;
-      if (aux_info && aux_info->height > 0)
+      if (aux_info->height > 0)
 	requisition->height = aux_info->height;
     }
 }
 
+static void
+get_fast_natural_size (GtkWidget      *widget,
+                       GtkRequisition *requisition)
+{
+  GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
+
+  if (aux_info)
+    *requisition = aux_info->natural_size;
+  else
+    *requisition = widget->requisition;
+}
+
 /**
  * _gtk_size_group_get_child_requisition:
  * @widget: a #GtkWidget
@@ -775,8 +863,8 @@ _gtk_size_group_get_child_requisition (GtkWidget      *widget,
     {
       if (get_size_groups (widget))
 	{
-	  requisition->width = get_dimension (widget, GTK_SIZE_GROUP_HORIZONTAL);
-	  requisition->height = get_dimension (widget, GTK_SIZE_GROUP_VERTICAL);
+	  get_dimensions (widget, GTK_SIZE_GROUP_HORIZONTAL, &requisition->width, NULL);
+	  get_dimensions (widget, GTK_SIZE_GROUP_VERTICAL, &requisition->height, NULL);
 
 	  /* Only do the full computation if we actually have size groups */
 	}
@@ -786,41 +874,40 @@ _gtk_size_group_get_child_requisition (GtkWidget      *widget,
 }
 
 /**
- * _gtk_size_group_compute_requisition:
+ * _gtk_size_group_compute_desired_size:
  * @widget: a #GtkWidget
- * @requisition: location to store computed requisition.
+ * @minimum_size: location to store computed minimum size
+ * @natural_size: location to store computed natural size
  * 
- * Compute the requisition of a widget taking into account grouping of
+ * Compute the desired size of a widget taking into account grouping of
  * the widget's requisition with other widgets.
  **/
 void
-_gtk_size_group_compute_requisition (GtkWidget      *widget,
-				     GtkRequisition *requisition)
+_gtk_size_group_compute_desired_size (GtkWidget      *widget,
+                                      GtkRequisition *minimum_size,
+                                      GtkRequisition *natural_size)
 {
-  gint width;
-  gint height;
-
   initialize_size_group_quarks ();
 
   if (get_size_groups (widget))
     {
       /* Only do the full computation if we actually have size groups */
-      
-      width = compute_dimension (widget, GTK_SIZE_GROUP_HORIZONTAL);
-      height = compute_dimension (widget, GTK_SIZE_GROUP_VERTICAL);
 
-      if (requisition)
-	{
-	  requisition->width = width;
-	  requisition->height = height;
-	}
+      compute_dimension (widget, GTK_SIZE_GROUP_HORIZONTAL,
+                         minimum_size ? &minimum_size->width : NULL,
+                         natural_size ? &natural_size->width : NULL);
+      compute_dimension (widget, GTK_SIZE_GROUP_VERTICAL,
+                         minimum_size ? &minimum_size->height : NULL,
+                         natural_size ? &natural_size->height : NULL);
     }
   else
     {
       do_size_request (widget);
-      
-      if (requisition)
-	get_fast_child_requisition (widget, requisition);
+
+      if (minimum_size)
+        get_fast_child_requisition (widget, minimum_size);
+      if (natural_size)
+        get_fast_natural_size (widget, natural_size);
     }
 }
 
diff --git a/gtk/gtksizegroup.h b/gtk/gtksizegroup.h
index 4a83153..1bcb164 100644
--- a/gtk/gtksizegroup.h
+++ b/gtk/gtksizegroup.h
@@ -98,8 +98,9 @@ GSList *         gtk_size_group_get_widgets   (GtkSizeGroup     *size_group);
 
 void _gtk_size_group_get_child_requisition (GtkWidget      *widget,
 					    GtkRequisition *requisition);
-void _gtk_size_group_compute_requisition   (GtkWidget      *widget,
-					    GtkRequisition *requisition);
+void _gtk_size_group_compute_desired_size  (GtkWidget      *widget,
+                                            GtkRequisition *minimum_size,
+                                            GtkRequisition *natural_size);
 void _gtk_size_group_queue_resize          (GtkWidget      *widget);
 
 G_END_DECLS
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 1aa1bba..738a668 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -53,6 +53,7 @@
 #include "gtkinvisible.h"
 #include "gtkbuildable.h"
 #include "gtkbuilderprivate.h"
+#include "gtkextendedlayout.h"
 #include "gtkalias.h"
 
 #define WIDGET_CLASS(w)	 GTK_WIDGET_GET_CLASS (w)
@@ -269,7 +270,11 @@ static void             gtk_widget_buildable_custom_finished    (GtkBuildable
 static void             gtk_widget_buildable_parser_finished    (GtkBuildable     *buildable,
                                                                  GtkBuilder       *builder);
 
-     
+static void             gtk_widget_layout_interface_init        (GtkExtendedLayoutIface *iface);
+static void             gtk_widget_real_get_desired_size        (GtkExtendedLayout *layout,
+                                                                 GtkRequisition    *minimum_size,
+                                                                 GtkRequisition    *natural_size);
+
 static void gtk_widget_set_usize_internal (GtkWidget *widget,
 					   gint       width,
 					   gint       height);
@@ -342,6 +347,13 @@ gtk_widget_get_type (void)
 	NULL /* interface data */
       };
 
+      const GInterfaceInfo layout_info =
+      {
+	(GInterfaceInitFunc) gtk_widget_layout_interface_init,
+	(GInterfaceFinalizeFunc) NULL,
+	NULL /* interface data */
+      };
+
       widget_type = g_type_register_static (GTK_TYPE_OBJECT, "GtkWidget",
                                            &widget_info, G_TYPE_FLAG_ABSTRACT);
 
@@ -349,7 +361,8 @@ gtk_widget_get_type (void)
                                    &accessibility_info) ;
       g_type_add_interface_static (widget_type, GTK_TYPE_BUILDABLE,
                                    &buildable_info) ;
-
+      g_type_add_interface_static (widget_type, GTK_TYPE_EXTENDED_LAYOUT,
+                                   &layout_info) ;
     }
 
   return widget_type;
@@ -3629,10 +3642,11 @@ gtk_widget_size_request (GtkWidget	*widget,
 
 #ifdef G_ENABLE_DEBUG
   if (requisition == &widget->requisition)
-    g_warning ("gtk_widget_size_request() called on child widget with request equal\n to widget->requisition. gtk_widget_set_usize() may not work properly.");
+    g_warning ("gtk_widget_size_request() called on child widget with request equal\n"
+               "to widget->requisition. gtk_widget_set_usize() may not work properly.");
 #endif /* G_ENABLE_DEBUG */
 
-  _gtk_size_group_compute_requisition (widget, requisition);
+  _gtk_size_group_compute_desired_size (widget, requisition, NULL);
 }
 
 /**
@@ -8137,14 +8151,11 @@ _gtk_widget_get_aux_info (GtkWidget *widget,
   aux_info = g_object_get_qdata (G_OBJECT (widget), quark_aux_info);
   if (!aux_info && create)
     {
-      aux_info = g_slice_new (GtkWidgetAuxInfo);
+      aux_info = g_slice_new0 (GtkWidgetAuxInfo);
 
       aux_info->width = -1;
       aux_info->height = -1;
-      aux_info->x = 0;
-      aux_info->y = 0;
-      aux_info->x_set = FALSE;
-      aux_info->y_set = FALSE;
+
       g_object_set_qdata (G_OBJECT (widget), quark_aux_info, aux_info);
     }
   
@@ -8907,6 +8918,101 @@ gtk_widget_buildable_parser_finished (GtkBuildable *buildable,
     gtk_widget_grab_focus (GTK_WIDGET (buildable));
 }
 
+/*
+ * GtkExtendedLayout implementation
+ */
+static void
+gtk_widget_real_get_desired_size (GtkExtendedLayout *layout,
+                                  GtkRequisition    *minimum_size,
+                                  GtkRequisition    *natural_size)
+{
+  GtkWidget *widget = GTK_WIDGET (layout);
+  GtkRequisition requisition = widget->requisition;
+
+  g_signal_emit (widget, widget_signals[SIZE_REQUEST], 0, &requisition);
+
+  if (minimum_size)
+    *minimum_size = requisition;
+  if (natural_size)
+    *natural_size = requisition;
+}
+
+/**
+ * gtk_widget_get_desired_size:
+ * @widget: a #GtkWidget
+ * @minimum_size: location for storing the @widget's minimum size, or %NULL
+ * @natural_size: location for storing the @widget's preferred size, or %NULL
+ *
+ * Retreives a widget's desired size, considering restrictions imposed by
+ * #GtkSizeGroup<!-- -->s. See also: gtk_extended_layout_get_desired_size().
+ *
+ * Since: 2.16
+ */
+void
+gtk_widget_get_desired_size (GtkWidget      *widget,
+                             GtkRequisition *minimum_size,
+                             GtkRequisition *natural_size)
+{
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  _gtk_size_group_compute_desired_size (widget, minimum_size, natural_size);
+}
+
+void
+gtk_widget_get_height_for_width (GtkWidget *widget,
+                                 gint       width,
+                                 gint      *minimum_height,
+                                 gint      *natural_height)
+{
+  GtkRequisition minimum_size;
+  GtkRequisition natural_size;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+#if 0
+  TODO: integrate height-for-width with size-groups
+#else
+  gtk_widget_get_desired_size (widget,
+                               minimum_height ? &minimum_size : NULL,
+                               natural_height ? &natural_size : NULL);
+
+  if (minimum_height)
+    *minimum_height = minimum_size.height;
+  if (natural_height)
+    *natural_height = natural_size.height;
+#endif
+}
+
+void
+gtk_widget_get_width_for_height (GtkWidget *widget,
+                                 gint       height,
+                                 gint      *minimum_width,
+                                 gint      *natural_width)
+{
+  GtkRequisition minimum_size;
+  GtkRequisition natural_size;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+#if 0
+  TODO: integrate width-for-height with size-groups
+#else
+  gtk_widget_get_desired_size (widget,
+                               minimum_width ? &minimum_size : NULL,
+                               natural_width ? &natural_size : NULL);
+
+  if (minimum_width)
+    *minimum_width = minimum_size.width;
+  if (natural_width)
+    *natural_width = natural_size.width;
+#endif
+}
+
+static void
+gtk_widget_layout_interface_init (GtkExtendedLayoutIface *iface)
+{
+  iface->get_desired_size = gtk_widget_real_get_desired_size;
+}
+
 typedef struct {
   GObject *object;
   guint    key;
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index b7f889a..cd11c99 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -429,8 +429,11 @@ struct _GtkWidgetAuxInfo
   gint y;
   gint width;
   gint height;
+
   guint x_set : 1;
   guint y_set : 1;
+
+  GtkRequisition natural_size;
 };
 
 struct _GtkWidgetShapeInfo
@@ -495,6 +498,17 @@ void	   gtk_widget_size_request	  (GtkWidget	       *widget,
 					   GtkRequisition      *requisition);
 void	   gtk_widget_size_allocate	  (GtkWidget	       *widget,
 					   GtkAllocation       *allocation);
+void       gtk_widget_get_desired_size    (GtkWidget           *widget,
+					   GtkRequisition      *minimum_size,
+					   GtkRequisition      *natural_size);
+void       gtk_widget_get_height_for_width(GtkWidget           *widget,
+					   gint                 width,
+					   gint                *minimum_height,
+					   gint                *natural_height);
+void       gtk_widget_get_width_for_height(GtkWidget           *widget,
+					   gint                 height,
+					   gint                *minimum_width,
+					   gint                *natural_width);
 void       gtk_widget_get_child_requisition (GtkWidget	       *widget,
 					     GtkRequisition    *requisition);
 void	   gtk_widget_add_accelerator	  (GtkWidget           *widget,
diff --git a/tests/Makefile.am b/tests/Makefile.am
index fd75f47..737a4bf 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -58,7 +58,8 @@ noinst_PROGRAMS =  $(TEST_PROGS)	\
 	testcellrenderertext		\
 	testdnd				\
 	testellipsise			\
-	testentrycompletion 		\
+	testentrycompletion		\
+	testextendedlayout		\
 	testfilechooser			\
 	testfilechooserbutton		\
 	testframe			\
@@ -136,6 +137,7 @@ testcellrenderertext_DEPENDENCIES = $(TEST_DEPS)
 testdnd_DEPENDENCIES = $(TEST_DEPS)
 testellipsise_DEPENDENCIES = $(TEST_DEPS)
 testentrycompletion_DEPENDENCIES = $(TEST_DEPS)
+testextendedlayout_DEPENDENCIES = $(TEST_DEPS)
 testfilechooser_DEPENDENCIES = $(TEST_DEPS)
 testfilechooserbutton_DEPENDENCIES = $(TEST_DEPS)
 testgtk_DEPENDENCIES = $(TEST_DEPS)
@@ -188,6 +190,7 @@ testcellrenderertext_LDADD = $(LDADDS)
 testdnd_LDADD = $(LDADDS)
 testellipsise_LDADD = $(LDADDS)
 testentrycompletion_LDADD = $(LDADDS)
+testextendedlayout_LDADD = $(LDADDS)
 testfilechooser_LDADD = $(LDADDS)
 testfilechooserbutton_LDADD = $(LDADDS)
 testgtk_LDADD = $(LDADDS)
diff --git a/tests/testellipsise.c b/tests/testellipsise.c
index 157884a..5b8b46d 100644
--- a/tests/testellipsise.c
+++ b/tests/testellipsise.c
@@ -70,8 +70,10 @@ ebox_expose_event_cb (GtkWidget      *widget,
 {
   PangoLayout *layout;
   const double dashes[] = { 6, 18 };
+  GtkRequisition natural_size;
   GtkWidget *label = data;
   gint x, y, dx, dy;
+  gchar *markup;
   cairo_t *cr;
 
   gtk_widget_translate_coordinates (label, widget, 0, 0, &x, &y);
@@ -93,14 +95,33 @@ ebox_expose_event_cb (GtkWidget      *widget,
   cairo_set_dash (cr, dashes, 2, 0.5);
   cairo_stroke (cr);
 
-  layout = gtk_widget_create_pango_layout (widget, NULL);
+  gtk_extended_layout_get_desired_size (GTK_EXTENDED_LAYOUT (label),
+                                        NULL, &natural_size);
+
+  cairo_rectangle (cr,
+                   x + 0.5 * (label->allocation.width - natural_size.width),
+                   y + 0.5 * (label->allocation.height - natural_size.height),
+                   natural_size.width, natural_size.height);
+  cairo_set_source_rgb (cr, 0.2, 0.8, 0.2);
+  cairo_set_dash (cr, dashes, 2, 12.5);
+  cairo_stroke (cr);
 
-  pango_layout_set_markup (layout,
-    "<span color='#c33'>\342\200\242 requisition</span>\n"
-    "<span color='#33c'>\342\200\242 allocation</span>", -1);
+  markup = g_strdup_printf (
+    "<span color='#c33'>\342\200\242 requisition:\t%dx%d</span>\n"
+    "<span color='#3c3'>\342\200\242 natural size:\t%dx%d</span>\n"
+    "<span color='#33c'>\342\200\242 allocation:\t%dx%d</span>",
+    label->requisition.width, label->requisition.height,
+    natural_size.width, natural_size.height,
+    label->allocation.width, label->allocation.height);
 
+  layout = gtk_widget_create_pango_layout (widget, NULL);
+  pango_layout_set_markup (layout, markup, -1);
   pango_layout_get_pixel_size (layout, &dx, &dy);
 
+  g_free (markup);
+
+  cairo_translate (cr, 0, widget->allocation.height - dy - 8);
+
   cairo_set_source_rgba (cr, 1, 1, 1, 0.8);
   cairo_rectangle (cr, 0, 0, dx + 12, dy + 8);
   cairo_fill (cr);
diff --git a/tests/testextendedlayout.c b/tests/testextendedlayout.c
new file mode 100644
index 0000000..497ccaf
--- /dev/null
+++ b/tests/testextendedlayout.c
@@ -0,0 +1,120 @@
+#include <gtk/gtk.h>
+#include <math.h>
+
+static void
+size_group_toggled_cb (GtkToggleButton *button,
+                       GtkSizeGroup    *group)
+{
+  if (gtk_toggle_button_get_active (button))
+    gtk_size_group_set_mode (group, GTK_SIZE_GROUP_HORIZONTAL);
+  else
+    gtk_size_group_set_mode (group, GTK_SIZE_GROUP_NONE);
+}
+
+static void
+ellipsize_toggled_cb (GtkToggleButton *button,
+                      GtkWidget       *vbox)
+{
+  GList *rows, *row_iter, *cells, *cell_iter;
+  PangoEllipsizeMode mode;
+
+  if (gtk_toggle_button_get_active (button))
+    mode = PANGO_ELLIPSIZE_END;
+  else
+    mode = PANGO_ELLIPSIZE_NONE;
+
+  rows = gtk_container_get_children (GTK_CONTAINER (vbox));
+  for (row_iter = rows; row_iter; row_iter = row_iter->next)
+    {
+      if (!GTK_IS_CONTAINER (row_iter->data))
+        break;
+
+      cells = gtk_container_get_children (row_iter->data);
+
+      for (cell_iter = cells; cell_iter; cell_iter = cell_iter->next)
+        if (GTK_IS_LABEL (cell_iter->data))
+          gtk_label_set_ellipsize (cell_iter->data, mode);
+
+      g_list_free (cells);
+    }
+
+  g_list_free (rows);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  GtkWidget *window, *vbox, *button;
+  GtkSizeGroup *groups[5];
+  gint x, y;
+
+  gtk_init (&argc, &argv);
+
+  for (x = 0; x < G_N_ELEMENTS (groups); ++x)
+    groups[x] = gtk_size_group_new (GTK_SIZE_GROUP_NONE);
+
+  vbox = gtk_vbox_new (FALSE, 6);
+  gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
+
+  for (y = 0; y < 4; ++y)
+    {
+      GtkWidget *hbox = gtk_hbox_new (FALSE, 6);
+
+      for (x = 0; x < G_N_ELEMENTS (groups); ++x)
+        {
+          gchar *text = g_strdup_printf ("Label #%.0f.%d", pow(10, y), x + 1);
+          GtkWidget *label = gtk_label_new (text);
+          g_free (text);
+
+          text = g_strdup_printf ("label/%d/%d", y, x);
+          gtk_widget_set_name (label, text);
+          g_free (text);
+
+          if (1 != x)
+            gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+          if (x > 0)
+            gtk_box_pack_start (GTK_BOX (hbox), gtk_vseparator_new (), FALSE, TRUE, 0);
+
+          gtk_box_pack_start (GTK_BOX (hbox), label, 1 == x, TRUE, 0);
+          gtk_size_group_add_widget (groups[x], label);
+        }
+
+      gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
+    }
+
+  gtk_box_pack_start (GTK_BOX (vbox), gtk_hseparator_new (), FALSE, TRUE, 0);
+
+  for (x = 0; x < G_N_ELEMENTS (groups); ++x)
+    {
+      gchar *text = g_strdup_printf ("Size Group #%d", x + 1);
+
+      button = gtk_check_button_new_with_label (text);
+      gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 0);
+      g_free (text);
+
+      g_signal_connect (button, "toggled", G_CALLBACK (size_group_toggled_cb), groups[x]);
+  }
+
+  gtk_box_pack_start (GTK_BOX (vbox), gtk_hseparator_new (), FALSE, TRUE, 0);
+
+  button = gtk_check_button_new_with_label ("Ellipsize");
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 0);
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+
+  g_signal_connect (button, "toggled",
+                    G_CALLBACK (ellipsize_toggled_cb),
+                    vbox);
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_container_add (GTK_CONTAINER (window), vbox);
+  gtk_widget_show_all (window);
+
+  g_signal_connect (window, "destroy",
+                    G_CALLBACK (gtk_main_quit),
+                    NULL);
+
+  gtk_main ();
+
+  return 0;
+}
-- 
1.5.3.7



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