[gtk+] Added height-for-width management for GtkComboBox/GtkCellView



commit 9c26bd525f4e470350a99d11cc51cac1196ae7ba
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date:   Wed Aug 18 19:39:59 2010 -0400

    Added height-for-width management for GtkComboBox/GtkCellView
    
    now GtkComboBox (and GtkCellView) request and allocate children/cells
    in a height-for-width manner.

 gtk/gtkcellview.c |  513 +++++++++++++++++++++++++++++++---------
 gtk/gtkcellview.h |   12 +
 gtk/gtkcombobox.c |  673 +++++++++++++++++++++++++++++++++++------------------
 3 files changed, 860 insertions(+), 338 deletions(-)
---
diff --git a/gtk/gtkcellview.c b/gtk/gtkcellview.c
index a02fc72..bf15a3e 100644
--- a/gtk/gtkcellview.c
+++ b/gtk/gtkcellview.c
@@ -24,6 +24,8 @@
 #include "gtkintl.h"
 #include "gtkcellrenderertext.h"
 #include "gtkcellrendererpixbuf.h"
+#include "gtksizerequest.h"
+#include "gtkcellsizerequest.h"
 #include "gtkprivate.h"
 #include <gobject/gmarshal.h>
 #include "gtkbuildable.h"
@@ -35,6 +37,7 @@ struct _GtkCellViewCellInfo
   GtkCellRenderer *cell;
 
   gint requested_width;
+  gint natural_width;
   gint real_width;
   guint expand : 1;
   guint pack : 1;
@@ -68,8 +71,6 @@ static void        gtk_cell_view_set_property             (GObject          *obj
                                                            const GValue     *value,
                                                            GParamSpec       *pspec);
 static void        gtk_cell_view_finalize                 (GObject          *object);
-static void        gtk_cell_view_size_request             (GtkWidget        *widget,
-                                                           GtkRequisition   *requisition);
 static void        gtk_cell_view_size_allocate            (GtkWidget        *widget,
                                                            GtkAllocation    *allocation);
 static gboolean    gtk_cell_view_expose                   (GtkWidget        *widget,
@@ -120,6 +121,22 @@ static void       gtk_cell_view_buildable_custom_tag_end       (GtkBuildable
 								const gchar   	      *tagname,
 								gpointer      	      *data);
 
+static void       gtk_cell_view_size_request_init              (GtkSizeRequestIface   *iface);
+static void       gtk_cell_view_get_width                      (GtkSizeRequest        *widget,
+								gint                  *minimum_size,
+								gint                  *natural_size);
+static void       gtk_cell_view_get_height                     (GtkSizeRequest        *widget,
+								gint                  *minimum_size,
+								gint                  *natural_size);
+static void       gtk_cell_view_get_width_for_height           (GtkSizeRequest        *widget,
+								gint                   avail_size,
+								gint                  *minimum_size,
+								gint                  *natural_size);
+static void       gtk_cell_view_get_height_for_width           (GtkSizeRequest        *widget,
+								gint                   avail_size,
+								gint                  *minimum_size,
+								gint                  *natural_size);
+
 static GtkBuildableIface *parent_buildable_iface;
 
 
@@ -136,7 +153,10 @@ G_DEFINE_TYPE_WITH_CODE (GtkCellView, gtk_cell_view, GTK_TYPE_WIDGET,
 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
 						gtk_cell_view_cell_layout_init)
 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
-						gtk_cell_view_buildable_init))
+						gtk_cell_view_buildable_init)
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_SIZE_REQUEST,
+						gtk_cell_view_size_request_init))
+
 
 static void
 gtk_cell_view_class_init (GtkCellViewClass *klass)
@@ -150,7 +170,6 @@ gtk_cell_view_class_init (GtkCellViewClass *klass)
 
   widget_class->expose_event = gtk_cell_view_expose;
   widget_class->size_allocate = gtk_cell_view_size_allocate;
-  widget_class->size_request = gtk_cell_view_size_request;
 
   /* properties */
   g_object_class_install_property (gobject_class,
@@ -314,99 +333,109 @@ gtk_cell_view_finalize (GObject *object)
 }
 
 static void
-gtk_cell_view_size_request (GtkWidget      *widget,
-                            GtkRequisition *requisition)
+gtk_cell_view_size_allocate (GtkWidget     *widget,
+                             GtkAllocation *allocation)
 {
-  GList *i;
-  gboolean first_cell = TRUE;
-  GtkCellView *cellview;
+  GtkCellView      *cellview;
+  GtkRequestedSize *sizes;
+  GList            *list;
+  gint              n_visible_cells, n_expand_cells;
+  gint              avail_width = 0;
+  gint              extra_per_cell, extra_extra, i;
+  gboolean          first_cell = TRUE;
+
+  widget->allocation = *allocation;
 
   cellview = GTK_CELL_VIEW (widget);
 
-  requisition->width = 0;
-  requisition->height = 0;
+  avail_width = allocation->width;
 
-  if (cellview->priv->displayed_row)
-    gtk_cell_view_set_cell_data (cellview);
+  /* Count visible/expand children */
+  for (n_visible_cells = 0, n_expand_cells = 0, list = cellview->priv->cell_list; 
+       list; list = list->next)
+    {
+      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
 
-  for (i = cellview->priv->cell_list; i; i = i->next)
+      n_visible_cells++;
+
+      if (info->expand)
+        n_expand_cells++;
+    }
+
+  sizes = g_new0 (GtkRequestedSize, n_visible_cells);
+
+  /* checking how much extra space we have */
+  for (i = 0, list = cellview->priv->cell_list; list; list = list->next)
     {
-      gint width, height;
-      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
+      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
 
       if (!gtk_cell_renderer_get_visible (info->cell))
         continue;
 
-      if (!first_cell)
-        requisition->width += cellview->priv->spacing;
+      sizes[i].data = info;
+      sizes[i].minimum_size = info->requested_width;
+      sizes[i].natural_size = info->natural_width;
 
-      gtk_cell_renderer_get_size (info->cell, widget, NULL, NULL, NULL,
-                                  &width, &height);
+      if (!first_cell)
+	avail_width -= cellview->priv->spacing;
 
-      info->requested_width = width;
-      requisition->width += width;
-      requisition->height = MAX (requisition->height, height);
+      avail_width -= sizes[i].minimum_size;
 
       first_cell = FALSE;
-    }
-}
 
-static void
-gtk_cell_view_size_allocate (GtkWidget     *widget,
-                             GtkAllocation *allocation)
-{
-  GList *i;
-  gint expand_cell_count = 0;
-  gint full_requested_width = 0;
-  gint extra_space;
-  GtkCellView *cellview;
+      i++;
+    }
 
-  widget->allocation = *allocation;
+  avail_width = gtk_distribute_natural_allocation (MAX (0, avail_width), n_visible_cells, sizes);
 
-  cellview = GTK_CELL_VIEW (widget);
+  /* Deal with any expand space... */
+  if (n_expand_cells > 0)
+    {
+      extra_per_cell = avail_width / n_expand_cells;
+      extra_extra    = avail_width % n_expand_cells;
+    }
+  else
+    /* Everything just left-aligned if no cells expand */
+    extra_per_cell = extra_extra = 0;
 
-  /* checking how much extra space we have */
-  for (i = cellview->priv->cell_list; i; i = i->next)
+  for (i = 0, list = cellview->priv->cell_list; list; list = list->next)
     {
-      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
+      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
 
       if (!gtk_cell_renderer_get_visible (info->cell))
         continue;
 
+      info->real_width = sizes[i].minimum_size;
+
       if (info->expand)
-        expand_cell_count++;
+        {
+	  info->real_width += extra_per_cell;
 
-      full_requested_width += info->requested_width;
+	  if (extra_extra)
+	    {
+	      info->real_width++;
+	      extra_extra--;
+	    }
+	}
+      
+      /* increment index into sizes for visible children */
+      i++;
     }
 
-  extra_space = widget->allocation.width - full_requested_width;
-  if (extra_space < 0)
-    extra_space = 0;
-  else if (extra_space > 0 && expand_cell_count > 0)
-    extra_space /= expand_cell_count;
-
-  for (i = cellview->priv->cell_list; i; i = i->next)
-    {
-      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
-
-      if (!gtk_cell_renderer_get_visible (info->cell))
-        continue;
-
-      info->real_width = info->requested_width +
-        (info->expand ? extra_space : 0);
-    }
+  g_free (sizes);
 }
 
 static gboolean
 gtk_cell_view_expose (GtkWidget      *widget,
                       GdkEventExpose *event)
 {
-  GList *i;
+  GList *list;
   GtkCellView *cellview;
   GdkRectangle area;
   GtkCellRendererState state;
   gboolean rtl = (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL);
-
+  GtkPackType packing;
+  
   cellview = GTK_CELL_VIEW (widget);
 
   if (!gtk_widget_is_drawable (widget))
@@ -437,7 +466,6 @@ gtk_cell_view_expose (GtkWidget      *widget,
   area = widget->allocation;
 
   /* we draw on our very own window, initialize x and y to zero */
-  area.x = widget->allocation.x + (rtl ? widget->allocation.width : 0); 
   area.y = widget->allocation.y;
 
   if (gtk_widget_get_state (widget) == GTK_STATE_PRELIGHT)
@@ -447,55 +475,44 @@ gtk_cell_view_expose (GtkWidget      *widget,
   else
     state = 0;
       
-  /* PACK_START */
-  for (i = cellview->priv->cell_list; i; i = i->next)
+  for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
     {
-      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
+      if (packing == GTK_PACK_START)
+	area.x = widget->allocation.x + (rtl ? widget->allocation.width : 0); 
+      else
+	area.x = rtl ? widget->allocation.x : (widget->allocation.x + widget->allocation.width);  
 
-      if (info->pack == GTK_PACK_END)
-        continue;
-
-      if (!gtk_cell_renderer_get_visible (info->cell))
-        continue;
-
-      area.width = info->real_width;
-      if (rtl)                                             
-         area.x -= area.width;
-
-      gtk_cell_renderer_render (info->cell,
-                                event->window,
-                                widget,
-                                /* FIXME! */
-                                &area, &area, &event->area, state);
-
-      if (!rtl)                                           
-         area.x += info->real_width;
-    }
-
-   area.x = rtl ? widget->allocation.x : (widget->allocation.x + widget->allocation.width);  
-
-  /* PACK_END */
-  for (i = cellview->priv->cell_list; i; i = i->next)
-    {
-      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
-
-      if (info->pack == GTK_PACK_START)
-        continue;
-
-      if (!gtk_cell_renderer_get_visible (info->cell))
-        continue;
-
-      area.width = info->real_width;
-      if (!rtl)
-         area.x -= area.width;   
-
-      gtk_cell_renderer_render (info->cell,
-                                widget->window,
-                                widget,
-                                /* FIXME ! */
-                                &area, &area, &event->area, state);
-      if (rtl)
-         area.x += info->real_width;
+      for (list = cellview->priv->cell_list; list; list = list->next)
+	{
+	  GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
+
+	  if (info->pack != packing)
+	    continue;
+
+	  if (!gtk_cell_renderer_get_visible (info->cell))
+	    continue;
+
+	  area.width = info->real_width;
+
+	  if ((packing == GTK_PACK_START && rtl) ||
+	      (packing == GTK_PACK_END && !rtl))
+	    area.x -= area.width;
+
+	  gtk_cell_renderer_render (info->cell,
+				    event->window,
+				    widget,
+				    /* FIXME! */
+				    &area, &area, &event->area, state);
+
+	  if ((packing == GTK_PACK_START && !rtl) ||
+	      (packing == GTK_PACK_END && rtl))
+	    {
+	      area.x += area.width;
+	      area.x += cellview->priv->spacing;
+	    }
+	  else
+	    area.x -= cellview->priv->spacing;
+	}
     }
 
   return FALSE;
@@ -584,6 +601,8 @@ gtk_cell_view_cell_layout_pack_start (GtkCellLayout   *layout,
   info->pack = GTK_PACK_START;
 
   cellview->priv->cell_list = g_list_append (cellview->priv->cell_list, info);
+
+  gtk_widget_queue_resize (GTK_WIDGET (cellview));
 }
 
 static void
@@ -604,6 +623,8 @@ gtk_cell_view_cell_layout_pack_end (GtkCellLayout   *layout,
   info->pack = GTK_PACK_END;
 
   cellview->priv->cell_list = g_list_append (cellview->priv->cell_list, info);
+
+  gtk_widget_queue_resize (GTK_WIDGET (cellview));
 }
 
 static void
@@ -983,32 +1004,108 @@ gtk_cell_view_get_displayed_row (GtkCellView *cell_view)
  * Return value: %TRUE
  *
  * Since: 2.6
+ * 
+ * Deprecated: 3.0: Use gtk_cell_view_get_desired_width_of_row() and
+ * gtk_cell_view_get_desired_height_for_width_of_row() instead.
  */
 gboolean
 gtk_cell_view_get_size_of_row (GtkCellView    *cell_view,
                                GtkTreePath    *path,
                                GtkRequisition *requisition)
 {
-  GtkTreeRowReference *tmp;
   GtkRequisition req;
 
   g_return_val_if_fail (GTK_IS_CELL_VIEW (cell_view), FALSE);
   g_return_val_if_fail (path != NULL, FALSE);
-  g_return_val_if_fail (requisition != NULL, FALSE);
+
+  /* Return the minimum height for the minimum width */
+  gtk_cell_view_get_desired_width_of_row (cell_view, path, &req.width, NULL);
+  gtk_cell_view_get_desired_height_for_width_of_row (cell_view, path, req.width, &req.height, NULL);
+
+  if (requisition)
+    *requisition = req;
+
+  return TRUE;
+}
+
+
+/**
+ * gtk_cell_view_get_desired_width_of_row:
+ * @cell_view: a #GtkCellView
+ * @path: a #GtkTreePath 
+ * @minimum_size: location to store the minimum size 
+ * @natural_size: location to store the natural size 
+ *
+ * Sets @minimum_size and @natural_size to the width desired by @cell_view 
+ * to display the model row pointed to by @path.
+ * 
+ * Since: 3.0
+ */
+void
+gtk_cell_view_get_desired_width_of_row (GtkCellView     *cell_view,
+					GtkTreePath     *path,
+					gint            *minimum_size,
+					gint            *natural_size)
+{
+  GtkTreeRowReference *tmp;
+
+  g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
+  g_return_if_fail (path != NULL);
+  g_return_if_fail (minimum_size != NULL || natural_size != NULL);
 
   tmp = cell_view->priv->displayed_row;
   cell_view->priv->displayed_row =
     gtk_tree_row_reference_new (cell_view->priv->model, path);
 
-  gtk_cell_view_size_request (GTK_WIDGET (cell_view), requisition);
+  gtk_cell_view_get_width (GTK_SIZE_REQUEST (cell_view), minimum_size, natural_size);
 
   gtk_tree_row_reference_free (cell_view->priv->displayed_row);
   cell_view->priv->displayed_row = tmp;
 
-  /* restore actual size info */
-  gtk_cell_view_size_request (GTK_WIDGET (cell_view), &req);
+  /* Restore active size (this will restore the cellrenderer info->width/requested_width's) */
+  gtk_cell_view_get_width (GTK_SIZE_REQUEST (cell_view), NULL, NULL);
+}
+
 
-  return TRUE;
+/**
+ * gtk_cell_view_get_desired_height_for_width_of_row:
+ * @cell_view: a #GtkCellView
+ * @path: a #GtkTreePath 
+ * @avail_size: available width
+ * @minimum_size: location to store the minimum height 
+ * @natural_size: location to store the natural height
+ *
+ * Sets @minimum_size and @natural_size to the height desired by @cell_view 
+ * if it were allocated a width of @avail_size to display the model row 
+ * pointed to by @path.
+ * 
+ * Since: 3.0
+ */
+void
+gtk_cell_view_get_desired_height_for_width_of_row (GtkCellView     *cell_view,
+						   GtkTreePath     *path,
+						   gint             avail_size,
+						   gint            *minimum_size,
+						   gint            *natural_size)
+{
+  GtkTreeRowReference *tmp;
+
+  g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
+  g_return_if_fail (path != NULL);
+  g_return_if_fail (minimum_size != NULL || natural_size != NULL);
+
+  tmp = cell_view->priv->displayed_row;
+  cell_view->priv->displayed_row =
+    gtk_tree_row_reference_new (cell_view->priv->model, path);
+
+  /* Then get the collective height_for_width based on the cached values */
+  gtk_cell_view_get_height_for_width (GTK_SIZE_REQUEST (cell_view), avail_size, minimum_size, natural_size);
+
+  gtk_tree_row_reference_free (cell_view->priv->displayed_row);
+  cell_view->priv->displayed_row = tmp;
+
+  /* Restore active size (this will restore the cellrenderer info->width/requested_width's) */
+  gtk_cell_view_get_width (GTK_SIZE_REQUEST (cell_view), NULL, NULL);
 }
 
 /**
@@ -1099,3 +1196,193 @@ gtk_cell_view_buildable_custom_tag_end (GtkBuildable *buildable,
     parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname,
 					    data);
 }
+
+static void
+gtk_cell_view_size_request_init (GtkSizeRequestIface *iface)
+{
+  iface->get_width            = gtk_cell_view_get_width;
+  iface->get_height           = gtk_cell_view_get_height;
+  iface->get_width_for_height = gtk_cell_view_get_width_for_height;
+  iface->get_height_for_width = gtk_cell_view_get_height_for_width;
+}
+
+static void
+gtk_cell_view_get_width  (GtkSizeRequest      *widget,
+			  gint                *minimum_size,
+			  gint                *natural_size)
+{
+  GList *list;
+  gint cell_min, cell_nat;
+  gboolean first_cell = TRUE;
+  GtkCellView *cellview = GTK_CELL_VIEW (widget);
+  gint minimum, natural;
+
+  minimum = natural = 0;
+
+  if (cellview->priv->displayed_row)
+    gtk_cell_view_set_cell_data (cellview);
+
+  for (list = cellview->priv->cell_list; list; list = list->next)
+    {
+      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
+
+      if (gtk_cell_renderer_get_visible (info->cell))
+        {
+	  
+	  if (!first_cell)
+	    {
+	      minimum += cellview->priv->spacing;
+	      natural += cellview->priv->spacing;
+	    }
+
+	  gtk_cell_size_request_get_width (GTK_CELL_SIZE_REQUEST (info->cell),
+					   GTK_WIDGET (cellview), &cell_min, &cell_nat);
+	  
+	  info->requested_width = cell_min;
+	  info->natural_width   = cell_nat;
+	  
+	  minimum += info->requested_width;
+	  natural += info->natural_width;
+
+	  first_cell = FALSE;
+        }
+    }
+
+  if (minimum_size)
+    *minimum_size = minimum;
+
+  if (natural_size)
+    *natural_size = natural;
+}
+
+static void       
+gtk_cell_view_get_height (GtkSizeRequest      *widget,
+			  gint                *minimum_size,
+			  gint                *natural_size)
+{
+  gint minimum_width;
+
+  /* CellViews only need to respond to height-for-width mode (cellview is pretty much
+   * an implementation detail of GtkComboBox) */
+  gtk_cell_view_get_width (widget, &minimum_width, NULL);
+  gtk_cell_view_get_height_for_width (widget, minimum_width, minimum_size, natural_size);
+}
+
+static void       
+gtk_cell_view_get_width_for_height (GtkSizeRequest      *widget,
+				    gint                 for_size,
+				    gint                *minimum_size,
+				    gint                *natural_size)
+{
+  /* CellViews only need to respond to height-for-width mode (cellview is pretty much
+   * an implementation detail of GtkComboBox) */
+  gtk_cell_view_get_width (widget, minimum_size, natural_size);
+}
+
+static void       
+gtk_cell_view_get_height_for_width (GtkSizeRequest      *widget,
+				    gint                 for_size,
+				    gint                *minimum_size,
+				    gint                *natural_size)
+{
+  GtkCellView      *cellview = GTK_CELL_VIEW (widget);
+  GList            *list;
+  GtkRequestedSize *sizes;
+  GArray           *array;
+  gint              minimum, natural, avail_size;
+  gboolean          first_cell = TRUE;
+  gint              n_expand_cells = 0;
+  gint              extra_per_cell, extra_extra, i;
+
+  minimum = natural = 0;
+  avail_size = for_size;
+
+  array = g_array_new (0, TRUE, sizeof (GtkRequestedSize));
+
+  if (cellview->priv->displayed_row)
+    gtk_cell_view_set_cell_data (cellview);
+
+  /* First allocate the right width to all cells */
+  for (list = cellview->priv->cell_list; list; list = list->next)
+    {
+      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
+
+      if (gtk_cell_renderer_get_visible (info->cell))
+        {
+	  GtkRequestedSize requested;
+
+	  gtk_cell_size_request_get_width (GTK_CELL_SIZE_REQUEST (info->cell),
+					   GTK_WIDGET (cellview), 
+					   &requested.minimum_size, 
+					   &requested.natural_size);
+
+	  requested.data = info;
+	  g_array_append_val (array, requested);
+
+	  avail_size -= requested.minimum_size;
+
+	  if (!first_cell)
+	    avail_size -= cellview->priv->spacing;
+
+	  first_cell = FALSE;
+
+	  if (info->expand)
+	    n_expand_cells++;
+        }
+    }
+
+  sizes      = (GtkRequestedSize *)array->data;
+  avail_size = gtk_distribute_natural_allocation (MAX (0, avail_size), array->len, sizes);
+
+  /* Deal with any expand space... */
+  if (n_expand_cells > 0)
+    {
+      extra_per_cell = avail_size / n_expand_cells;
+      extra_extra    = avail_size % n_expand_cells;
+    }
+  else
+    /* Everything just left-aligned if no cells expand */
+    extra_per_cell = extra_extra = 0;
+
+  /* Now get the height for the real width of each cell */
+  for (i = 0, list = cellview->priv->cell_list; list; list = list->next)
+    {
+      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)list->data;
+      gint cell_minimum, cell_natural;
+
+      if (gtk_cell_renderer_get_visible (info->cell))
+        {
+	  gint cell_width = sizes[i].minimum_size;
+
+	  g_assert (sizes[i].data == info);
+
+	  if (info->expand)
+	    {
+	      cell_width += extra_per_cell;
+	      if (extra_extra)
+		{
+		  cell_width++;
+		  extra_extra--;
+		}
+	    }
+
+	  /* Get the height for the real width of this cell */
+	  gtk_cell_size_request_get_height_for_width (GTK_CELL_SIZE_REQUEST (info->cell),
+						      GTK_WIDGET (widget),
+						      cell_width, &cell_minimum, &cell_natural);
+
+	  minimum = MAX (minimum, cell_minimum);
+	  natural = MAX (natural, cell_natural);
+
+	  /* increment sizes[] index for visible cells */
+	  i++;
+        }
+    }
+
+  g_array_free (array, TRUE);
+
+  if (minimum_size)
+    *minimum_size = minimum;
+  if (natural_size)
+    *natural_size = natural;
+}
diff --git a/gtk/gtkcellview.h b/gtk/gtkcellview.h
index a58f97e..cb6b269 100644
--- a/gtk/gtkcellview.h
+++ b/gtk/gtkcellview.h
@@ -69,6 +69,18 @@ GtkTreePath      *gtk_cell_view_get_displayed_row       (GtkCellView     *cell_v
 gboolean          gtk_cell_view_get_size_of_row         (GtkCellView     *cell_view,
                                                          GtkTreePath     *path,
                                                          GtkRequisition  *requisition);
+gboolean          gtk_cell_view_get_size_of_row         (GtkCellView     *cell_view,
+                                                         GtkTreePath     *path,
+                                                         GtkRequisition  *requisition);
+void              gtk_cell_view_get_desired_width_of_row(GtkCellView     *cell_view,
+                                                         GtkTreePath     *path,
+                                                         gint            *minimum_size,
+                                                         gint            *natural_size);
+void              gtk_cell_view_get_desired_height_for_width_of_row(GtkCellView     *cell_view,
+								    GtkTreePath     *path,
+								    gint             avail_size,
+								    gint            *minimum_size,
+								    gint            *natural_size);
 
 void              gtk_cell_view_set_background_color    (GtkCellView     *cell_view,
                                                          const GdkColor  *color);
diff --git a/gtk/gtkcombobox.c b/gtk/gtkcombobox.c
index e8a535f..6dd236b 100644
--- a/gtk/gtkcombobox.c
+++ b/gtk/gtkcombobox.c
@@ -38,6 +38,7 @@
 #include "gtktreeselection.h"
 #include "gtkvseparator.h"
 #include "gtkwindow.h"
+#include "gtksizerequest.h"
 #include "gtkprivate.h"
 
 #include <gdk/gdkkeysyms.h>
@@ -107,8 +108,9 @@ struct _GtkComboBoxPrivate
   guint scroll_timer;
   guint resize_idle_id;
 
-  gint width;
-  gint height;
+  gint  minimum_width;
+  gint  natural_width;
+
   GSList *cells;
 
   guint popup_in_progress : 1;
@@ -272,14 +274,12 @@ static void     gtk_combo_box_menu_position        (GtkMenu          *menu,
                                                     gint             *push_in,
                                                     gpointer          user_data);
 
-static gint     gtk_combo_box_calc_requested_width (GtkComboBox      *combo_box,
-                                                    GtkTreePath      *path);
+static void     gtk_combo_box_update_requested_width(GtkComboBox      *combo_box,
+						     GtkTreePath      *path);
 static void     gtk_combo_box_remeasure            (GtkComboBox      *combo_box);
 
 static void     gtk_combo_box_unset_model          (GtkComboBox      *combo_box);
 
-static void     gtk_combo_box_size_request         (GtkWidget        *widget,
-                                                    GtkRequisition   *requisition);
 static void     gtk_combo_box_size_allocate        (GtkWidget        *widget,
                                                     GtkAllocation    *allocation);
 static void     gtk_combo_box_forall               (GtkContainer     *container,
@@ -464,8 +464,24 @@ static void     gtk_combo_box_buildable_custom_tag_end       (GtkBuildable  *bui
 							      gpointer      *data);
 
 /* GtkCellEditable method implementations */
-static void gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
-					 GdkEvent        *event);
+static void     gtk_combo_box_start_editing                  (GtkCellEditable *cell_editable,
+							      GdkEvent        *event);
+
+static void     gtk_combo_box_size_request_init              (GtkSizeRequestIface *iface);
+static void     gtk_combo_box_get_width                      (GtkSizeRequest      *widget,
+							      gint                *minimum_size,
+							      gint                *natural_size);
+static void     gtk_combo_box_get_height                     (GtkSizeRequest      *widget,
+							      gint                *minimum_size,
+							      gint                *natural_size);
+static void     gtk_combo_box_get_width_for_height           (GtkSizeRequest        *widget,
+							      gint                   avail_size,
+							      gint                  *minimum_size,
+							      gint                  *natural_size);
+static void     gtk_combo_box_get_height_for_width           (GtkSizeRequest        *widget,
+							      gint                   avail_size,
+							      gint                  *minimum_size,
+							      gint                  *natural_size);
 
 
 G_DEFINE_TYPE_WITH_CODE (GtkComboBox, gtk_combo_box, GTK_TYPE_BIN,
@@ -474,7 +490,9 @@ G_DEFINE_TYPE_WITH_CODE (GtkComboBox, gtk_combo_box, GTK_TYPE_BIN,
 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE,
 						gtk_combo_box_cell_editable_init)
 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
-						gtk_combo_box_buildable_init))
+						gtk_combo_box_buildable_init)
+			 G_IMPLEMENT_INTERFACE (GTK_TYPE_SIZE_REQUEST,
+						gtk_combo_box_size_request_init))
 
 
 /* common */
@@ -496,7 +514,6 @@ gtk_combo_box_class_init (GtkComboBoxClass *klass)
 
   widget_class = (GtkWidgetClass *)klass;
   widget_class->size_allocate = gtk_combo_box_size_allocate;
-  widget_class->size_request = gtk_combo_box_size_request;
   widget_class->expose_event = gtk_combo_box_expose_event;
   widget_class->scroll_event = gtk_combo_box_scroll_event;
   widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate;
@@ -938,8 +955,9 @@ gtk_combo_box_init (GtkComboBox *combo_box)
   _gtk_bin_set_child (GTK_BIN (combo_box), priv->cell_view);
   gtk_widget_show (priv->cell_view);
 
-  priv->width = 0;
-  priv->height = 0;
+  priv->minimum_width = 0;
+  priv->natural_width = 0;
+
   priv->wrap_width = 0;
 
   priv->active = -1;
@@ -1543,23 +1561,37 @@ gtk_combo_box_menu_position_over (GtkMenu  *menu,
 				  gboolean *push_in,
 				  gpointer  user_data)
 {
-  GtkComboBox *combo_box;
-  GtkWidget *active;
-  GtkWidget *child;
-  GtkWidget *widget;
-  GtkRequisition requisition;
-  GList *children;
-  gint screen_width;
-  gint menu_xpos;
-  gint menu_ypos;
-  gint menu_width;
+  GtkComboBox    *combo_box;
+  GtkWidget      *active;
+  GtkWidget      *child;
+  GtkWidget      *widget;
+  GtkRequisition  requisition;
+  GList          *children;
+  guint           horizontal_padding, border_width;
+  gint            screen_width;
+  gint            menu_item_avail_width;
+  gint            min_item_height;
+  gint            menu_xpos;
+  gint            menu_ypos;
+  gint            menu_width;
 
   combo_box = GTK_COMBO_BOX (user_data);
   widget = GTK_WIDGET (combo_box);
 
-  gtk_widget_get_child_requisition (GTK_WIDGET (menu), &requisition);
+  gtk_widget_style_get (GTK_WIDGET (menu),
+                        "horizontal-padding", &horizontal_padding,
+			NULL);
+
+  border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
+
+  /* Get the minimum height for minimum width of the menu */
+  gtk_size_request_get_size (GTK_SIZE_REQUEST (menu), &requisition, NULL);
   menu_width = requisition.width;
 
+  /* Get the size for the height-for-width menu-item requests */
+  menu_item_avail_width = 
+    menu_width - (border_width + horizontal_padding + widget->style->xthickness) * 2;
+
   active = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
 
   menu_xpos = widget->allocation.x;
@@ -1567,8 +1599,9 @@ gtk_combo_box_menu_position_over (GtkMenu  *menu,
 
   if (active != NULL)
     {
-      gtk_widget_get_child_requisition (active, &requisition);
-      menu_ypos -= requisition.height / 2;
+      gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (active), 
+					     menu_item_avail_width, &min_item_height, NULL);
+      menu_ypos -= min_item_height / 2;
     }
 
   children = GTK_MENU_SHELL (combo_box->priv->popup_widget)->children;
@@ -1581,8 +1614,9 @@ gtk_combo_box_menu_position_over (GtkMenu  *menu,
 
       if (gtk_widget_get_visible (child))
 	{
-	  gtk_widget_get_child_requisition (child, &requisition);
-	  menu_ypos -= requisition.height;
+	  gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (child), 
+						 menu_item_avail_width, &min_item_height, NULL);
+	  menu_ypos -= min_item_height;
 	}
 
       children = children->next;
@@ -1830,8 +1864,7 @@ gtk_combo_box_menu_popup (GtkComboBox *combo_box,
   GtkComboBoxPrivate *priv = combo_box->priv;
   GtkTreePath *path;
   gint active_item;
-  GtkRequisition requisition;
-  gint width;
+  gint width, min_width;
   
   update_menu_sensitivity (combo_box, priv->popup_widget);
 
@@ -1853,10 +1886,10 @@ gtk_combo_box_menu_popup (GtkComboBox *combo_box,
     {
       width = GTK_WIDGET (combo_box)->allocation.width;
       gtk_widget_set_size_request (priv->popup_widget, -1, -1);
-      gtk_widget_size_request (priv->popup_widget, &requisition);
+      gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->popup_widget), &min_width, NULL);
       
       gtk_widget_set_size_request (priv->popup_widget,
-				   MAX (width, requisition.width), -1);
+				   MAX (width, min_width), -1);
     }
   
   gtk_menu_popup (GTK_MENU (priv->popup_widget),
@@ -2093,13 +2126,12 @@ gtk_combo_box_popdown (GtkComboBox *combo_box)
   priv->grab_keyboard = NULL;
 }
 
-static gint
-gtk_combo_box_calc_requested_width (GtkComboBox *combo_box,
-                                    GtkTreePath *path)
+static void
+gtk_combo_box_update_requested_width (GtkComboBox *combo_box,
+				      GtkTreePath *path)
 {
   GtkComboBoxPrivate *priv = combo_box->priv;
-  gint padding;
-  GtkRequisition req;
+  gint                padding, min_width, nat_width;
 
   if (priv->cell_view)
     gtk_widget_style_get (priv->cell_view,
@@ -2112,168 +2144,24 @@ gtk_combo_box_calc_requested_width (GtkComboBox *combo_box,
   padding += BONUS_PADDING;
 
   if (priv->cell_view)
-    gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (priv->cell_view),
-                                   path, &req);
+    gtk_cell_view_get_desired_width_of_row (GTK_CELL_VIEW (priv->cell_view),
+					    path, &min_width, &nat_width);
   else
-    req.width = 0;
+    min_width = nat_width = 0;
 
-  return req.width + padding;
-}
+  min_width += padding;
+  nat_width += padding;
 
-static void
-gtk_combo_box_remeasure (GtkComboBox *combo_box)
-{
-  GtkComboBoxPrivate *priv = combo_box->priv;
-  GtkTreeIter iter;
-  GtkTreePath *path;
-
-  if (!priv->model ||
-      !gtk_tree_model_get_iter_first (priv->model, &iter))
-    return;
-
-  priv->width = 0;
-  priv->height = 0;
-
-  path = gtk_tree_path_new_from_indices (0, -1);
-
-  do
+  if (min_width > priv->minimum_width || nat_width > priv->natural_width)
     {
-      GtkRequisition req;
+      priv->minimum_width = MAX (priv->minimum_width, min_width);
+      priv->natural_width = MAX (priv->natural_width, nat_width);
 
       if (priv->cell_view)
-	gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (priv->cell_view), 
-                                       path, &req);
-      else
-        {
-          req.width = 0;
-          req.height = 0;
-        }
-
-      priv->width = MAX (priv->width, req.width);
-      priv->height = MAX (priv->height, req.height);
-
-      gtk_tree_path_next (path);
-    }
-  while (gtk_tree_model_iter_next (priv->model, &iter));
-
-  gtk_tree_path_free (path);
-}
-
-static void
-gtk_combo_box_size_request (GtkWidget      *widget,
-                            GtkRequisition *requisition)
-{
-  gint width, height;
-  gint focus_width, focus_pad;
-  gint font_size;
-  gint arrow_size;
-  guint border_width;
-  GtkRequisition bin_req;
-  PangoContext *context;
-  PangoFontMetrics *metrics;
-  PangoFontDescription *font_desc;
-
-  GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
-  GtkComboBoxPrivate *priv = combo_box->priv;
- 
-  /* common */
-  gtk_widget_size_request (gtk_bin_get_child (GTK_BIN (widget)), &bin_req);
-  gtk_combo_box_remeasure (combo_box);
-  bin_req.width = MAX (bin_req.width, priv->width);
-  bin_req.height = MAX (bin_req.height, priv->height);
-
-  gtk_widget_style_get (GTK_WIDGET (widget),
-			"focus-line-width", &focus_width,
-			"focus-padding", &focus_pad,
-			"arrow-size", &arrow_size,
-			NULL);
-
-  font_desc = gtk_bin_get_child (GTK_BIN (widget))->style->font_desc;
-  context = gtk_widget_get_pango_context (widget);
-  metrics = pango_context_get_metrics (context, font_desc,
-				       pango_context_get_language (context));
-  font_size = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
-			    pango_font_metrics_get_descent (metrics));
-  pango_font_metrics_unref (metrics);
-
-  arrow_size = MAX (arrow_size, font_size);
-
-  gtk_widget_set_size_request (priv->arrow, arrow_size, arrow_size);
-
-  if (!priv->tree_view)
-    {
-      /* menu mode */
-
-      if (priv->cell_view)
-        {
-          GtkRequisition button_req, sep_req, arrow_req;
-          gint xthickness, ythickness;
-
-          gtk_widget_size_request (priv->button, &button_req);
-          border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
-          xthickness = priv->button->style->xthickness;
-          ythickness = priv->button->style->ythickness;
-
-          bin_req.width = MAX (bin_req.width, priv->width);
-          bin_req.height = MAX (bin_req.height, priv->height);
-
-          gtk_widget_size_request (priv->separator, &sep_req);
-          gtk_widget_size_request (priv->arrow, &arrow_req);
-
-          height = MAX (sep_req.height, arrow_req.height);
-          height = MAX (height, bin_req.height);
-
-          width = bin_req.width + sep_req.width + arrow_req.width;
-
-          height += 2*(border_width + ythickness + focus_width + focus_pad);
-          width  += 2*(border_width + xthickness + focus_width + focus_pad);
-
-          requisition->width = width;
-          requisition->height = height;
-        }
-      else
-        {
-          GtkRequisition but_req;
-
-          gtk_widget_size_request (priv->button, &but_req);
-
-          requisition->width = bin_req.width + but_req.width;
-          requisition->height = MAX (bin_req.height, but_req.height);
-        }
-    }
-  else
-    {
-      /* list mode */
-      GtkRequisition button_req, frame_req;
-
-      /* sample + frame */
-      *requisition = bin_req;
-
-      requisition->width += 2 * focus_width;
-      
-      if (priv->cell_view_frame)
-        {
-	  gtk_widget_size_request (priv->cell_view_frame, &frame_req);
-	  if (priv->has_frame)
-	    {
-              border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
-
-	      requisition->width += 2 * (border_width + GTK_WIDGET (priv->cell_view_frame)->style->xthickness);
-	      requisition->height += 2 * (border_width + GTK_WIDGET (priv->cell_view_frame)->style->ythickness);
-	    }
-        }
-
-      /* the button */
-      gtk_widget_size_request (priv->button, &button_req);
-
-      requisition->height = MAX (requisition->height, button_req.height);
-      requisition->width += button_req.width;
-    }
-
-  if (GTK_SHADOW_NONE != priv->shadow_type)
-    {
-      requisition->height += 2 * widget->style->ythickness;
-      requisition->width += 2 * widget->style->xthickness;
+	{
+	  gtk_widget_set_size_request (priv->cell_view, min_width, -1);
+	  gtk_widget_queue_resize (priv->cell_view);
+	}
     }
 }
 
@@ -2395,19 +2283,19 @@ gtk_combo_box_size_allocate (GtkWidget     *widget,
 
           if (gtk_widget_get_visible (priv->popup_widget))
             {
-              gint width;
-              GtkRequisition requisition;
+              gint width, min_width;
 
-              /* Warning here, without the check in the position func */
-              gtk_menu_reposition (GTK_MENU (priv->popup_widget));
               if (priv->wrap_width == 0)
                 {
                   width = GTK_WIDGET (combo_box)->allocation.width;
                   gtk_widget_set_size_request (priv->popup_widget, -1, -1);
-                  gtk_widget_size_request (priv->popup_widget, &requisition);
+                  gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->popup_widget), &min_width, NULL);
                   gtk_widget_set_size_request (priv->popup_widget,
-                    MAX (width, requisition.width), -1);
+                    MAX (width, min_width), -1);
                }
+
+              /* reposition the menu after giving it a new width */
+              gtk_menu_reposition (GTK_MENU (priv->popup_widget));
             }
 
 	  child.width = MAX (1, child.width);
@@ -3691,7 +3579,6 @@ gtk_combo_box_menu_row_changed (GtkTreeModel *model,
   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
   GtkComboBoxPrivate *priv = combo_box->priv;
   GtkWidget *item;
-  gint width;
   gboolean is_separator;
 
   if (!priv->popup_widget)
@@ -3736,17 +3623,7 @@ gtk_combo_box_menu_row_changed (GtkTreeModel *model,
       gtk_combo_box_relayout_item (combo_box, item, iter, pitem);
     }
 
-  width = gtk_combo_box_calc_requested_width (combo_box, path);
-
-  if (width > priv->width)
-    {
-      if (priv->cell_view)
-	{
-	  gtk_widget_set_size_request (priv->cell_view, width, -1);
-	  gtk_widget_queue_resize (priv->cell_view);
-	}
-      priv->width = width;
-    }
+  gtk_combo_box_update_requested_width (combo_box, path);
 }
 
 /*
@@ -4250,20 +4127,8 @@ gtk_combo_box_list_row_changed (GtkTreeModel *model,
                                 gpointer      data)
 {
   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
-  GtkComboBoxPrivate *priv = combo_box->priv;
-  gint width;
-
-  width = gtk_combo_box_calc_requested_width (combo_box, path);
 
-  if (width > priv->width)
-    {
-      if (priv->cell_view) 
-	{
-	  gtk_widget_set_size_request (priv->cell_view, width, -1);
-	  gtk_widget_queue_resize (priv->cell_view);
-	}
-      priv->width = width;
-    }
+  gtk_combo_box_update_requested_width (combo_box, path);
 }
 
 /*
@@ -4316,8 +4181,11 @@ gtk_combo_box_cell_layout_pack_start (GtkCellLayout   *layout,
   priv->cells = g_slist_append (priv->cells, info);
 
   if (priv->cell_view)
-    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->cell_view),
-                                cell, expand);
+    {
+      gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->cell_view),
+				  cell, expand);
+
+    }
 
   if (priv->column)
     gtk_tree_view_column_pack_start (priv->column, cell, expand);
@@ -6062,3 +5930,358 @@ gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
     parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname,
 					    data);
 }
+
+
+static void
+gtk_combo_box_size_request_init (GtkSizeRequestIface *iface)
+{
+  iface->get_width            = gtk_combo_box_get_width;
+  iface->get_height           = gtk_combo_box_get_height;
+  iface->get_height_for_width = gtk_combo_box_get_height_for_width;
+  iface->get_width_for_height = gtk_combo_box_get_width_for_height;
+}
+
+static void
+gtk_combo_box_remeasure (GtkComboBox *combo_box)
+{
+  GtkComboBoxPrivate *priv = combo_box->priv;
+  GtkTreeIter iter;
+  GtkTreePath *path;
+
+  if (!priv->model ||
+      !gtk_tree_model_get_iter_first (priv->model, &iter))
+    return;
+
+  priv->minimum_width = priv->natural_width = 0;
+
+  path = gtk_tree_path_new_from_indices (0, -1);
+
+  do
+    {
+      gint row_min = 0, row_nat = 0;
+
+      if (priv->cell_view)
+	gtk_cell_view_get_desired_width_of_row (GTK_CELL_VIEW (priv->cell_view), 
+						path, &row_min, &row_nat);
+
+      priv->minimum_width = MAX (priv->minimum_width, row_min);
+      priv->natural_width = MAX (priv->natural_width, row_nat);
+
+      gtk_tree_path_next (path);
+    }
+  while (gtk_tree_model_iter_next (priv->model, &iter));
+
+  gtk_tree_path_free (path);
+}
+
+
+static void
+gtk_combo_box_measure_height_for_width (GtkComboBox *combo_box,
+					gint         avail_width, 
+					gint        *min_height, 
+					gint        *nat_height)
+{
+  GtkWidget             *child;
+  GtkComboBoxPrivate    *priv = combo_box->priv;
+  GtkTreeIter            iter;
+  GtkTreePath           *path;
+  gint                   child_min, child_nat;
+
+  child = gtk_bin_get_child (GTK_BIN (combo_box));
+
+  gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (child), avail_width,
+					 &child_min, &child_nat);
+
+  if (!priv->model ||
+      !gtk_tree_model_get_iter_first (priv->model, &iter))
+    goto out;
+
+  path = gtk_tree_path_new_from_indices (0, -1);
+
+  do
+    {
+      gint row_min = 0, row_nat = 0;
+
+      if (priv->cell_view)
+	gtk_cell_view_get_desired_height_for_width_of_row (GTK_CELL_VIEW (priv->cell_view), 
+							   path, avail_width, 
+							   &row_min, &row_nat);
+
+      child_min = MAX (child_min, row_min);
+      child_nat = MAX (child_nat, row_nat);
+
+      gtk_tree_path_next (path);
+    }
+  while (gtk_tree_model_iter_next (priv->model, &iter));
+
+  gtk_tree_path_free (path);
+
+ out:
+
+  if (min_height)
+    *min_height = child_min;
+  if (nat_height)
+    *nat_height = child_nat;
+}
+
+
+static void     
+gtk_combo_box_get_width (GtkSizeRequest      *widget,
+			 gint                *minimum_size,
+			 gint                *natural_size)
+{
+  GtkComboBox           *combo_box = GTK_COMBO_BOX (widget);
+  GtkComboBoxPrivate    *priv = combo_box->priv;
+  gint                   focus_width, focus_pad;
+  gint                   font_size, arrow_size;
+  PangoContext          *context;
+  PangoFontMetrics      *metrics;
+  PangoFontDescription  *font_desc;
+  GtkWidget             *child;
+  gint                   minimum_width, natural_width;
+  gint                   child_min, child_nat;
+
+  child = gtk_bin_get_child (GTK_BIN (widget));
+ 
+  /* common */
+  gtk_size_request_get_width (GTK_SIZE_REQUEST (child), &child_min, &child_nat);
+  gtk_combo_box_remeasure (combo_box);
+
+  child_min  = MAX (child_min,  priv->minimum_width);
+  child_nat  = MAX (child_nat,  priv->natural_width);
+
+  gtk_widget_style_get (GTK_WIDGET (widget),
+			"focus-line-width", &focus_width,
+			"focus-padding", &focus_pad,
+			"arrow-size", &arrow_size,
+			NULL);
+
+  font_desc = child->style->font_desc;
+  context = gtk_widget_get_pango_context (GTK_WIDGET (widget));
+  metrics = pango_context_get_metrics (context, font_desc,
+				       pango_context_get_language (context));
+  font_size = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
+			    pango_font_metrics_get_descent (metrics));
+  pango_font_metrics_unref (metrics);
+
+  arrow_size = MAX (arrow_size, font_size);
+
+  gtk_widget_set_size_request (priv->arrow, arrow_size, arrow_size);
+
+  if (!priv->tree_view)
+    {
+      /* menu mode */
+	  
+      if (priv->cell_view)
+        {
+          gint sep_width, arrow_width;
+          gint border_width, xthickness, xpad;
+
+	  border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
+          xthickness   = priv->button->style->xthickness;
+
+          gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->separator), &sep_width, NULL);
+          gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->arrow), &arrow_width, NULL);
+
+	  xpad = 2*(border_width + xthickness + focus_width + focus_pad);
+
+          minimum_width  = child_min + sep_width + arrow_width + xpad;
+          natural_width  = child_nat + sep_width + arrow_width + xpad;
+        }
+      else
+        {
+          gint but_width, but_nat_width;
+
+          gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->button), 
+				      &but_width, &but_nat_width);
+
+          minimum_width  = child_min + but_width;
+          natural_width  = child_nat + but_nat_width;
+        }
+    }
+  else
+    {
+      /* list mode */
+      gint button_width, button_nat_width;
+
+      /* sample + frame */
+      minimum_width = child_min;
+      natural_width = child_nat;
+
+      minimum_width += 2 * focus_width;
+      natural_width += 2 * focus_width;
+      
+      if (priv->cell_view_frame)
+        {
+	  if (priv->has_frame)
+	    {
+	      gint border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
+	      gint xpad         = 2 * (border_width + GTK_WIDGET (priv->cell_view_frame)->style->xthickness);
+
+	      minimum_width  += xpad;
+	      natural_width  += xpad;
+	    }
+        }
+
+      /* the button */
+      gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->button), 
+				  &button_width, &button_nat_width);
+
+      minimum_width += button_width;
+      natural_width += button_nat_width;
+    }
+
+  if (GTK_SHADOW_NONE != priv->shadow_type)
+    {
+      minimum_width  += 2 * GTK_WIDGET (widget)->style->xthickness;
+      natural_width  += 2 * GTK_WIDGET (widget)->style->xthickness;
+    }
+
+  if (minimum_size)
+    *minimum_size = minimum_width;
+
+  if (natural_size)
+    *natural_size = natural_width;
+}
+
+static void
+gtk_combo_box_get_height (GtkSizeRequest      *widget,
+			  gint                *minimum_size,
+			  gint                *natural_size)
+{ 
+  gint min_width;
+
+  /* Combo box is height-for-width only 
+   * (so we always just reserve enough height for the minimum width) */
+  gtk_size_request_get_width (widget, &min_width, NULL);
+  gtk_size_request_get_height_for_width (widget, min_width, minimum_size, natural_size);
+}
+
+static void
+gtk_combo_box_get_width_for_height (GtkSizeRequest        *widget,
+				    gint                   avail_size,
+				    gint                  *minimum_size,
+				    gint                  *natural_size)
+{
+  /* Combo box is height-for-width only 
+   * (so we assume we always reserved enough height for the minimum width) */
+  gtk_size_request_get_width (widget, minimum_size, natural_size);
+}
+
+
+static void
+gtk_combo_box_get_height_for_width (GtkSizeRequest        *widget,
+				    gint                   avail_size,
+				    gint                  *minimum_size,
+				    gint                  *natural_size)
+{
+  GtkComboBox           *combo_box = GTK_COMBO_BOX (widget);
+  GtkComboBoxPrivate    *priv = combo_box->priv;
+  gint                   focus_width, focus_pad;
+  gint                   min_height, nat_height;
+  gint                   size;
+
+  gtk_widget_style_get (GTK_WIDGET (widget),
+			"focus-line-width", &focus_width,
+			"focus-padding", &focus_pad,
+			NULL);
+
+  size = avail_size;
+
+  if (GTK_SHADOW_NONE != priv->shadow_type)
+    size -= GTK_WIDGET (widget)->style->xthickness;
+
+  if (!priv->tree_view)
+    {
+      /* menu mode */
+      if (priv->cell_view)
+        {
+	  /* calculate x/y padding and separator/arrow size */
+          gint sep_width, arrow_width, sep_height, arrow_height;
+          gint border_width, xthickness, ythickness, xpad, ypad;
+
+	  border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
+          xthickness = priv->button->style->xthickness;
+          ythickness = priv->button->style->ythickness;
+
+          gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->separator), &sep_width, NULL);
+          gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->arrow), &arrow_width, NULL);
+          gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (priv->separator), 
+						 sep_width, &sep_height, NULL);
+          gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (priv->arrow), 
+						 arrow_width, &arrow_height, NULL);
+
+	  xpad = 2*(border_width + xthickness + focus_width + focus_pad);
+	  ypad = 2*(border_width + ythickness + focus_width + focus_pad);
+
+	  size -= sep_width + arrow_width + xpad;
+
+	  gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
+
+	  arrow_height = MAX (arrow_height, sep_height);
+	  min_height = MAX (min_height, arrow_height);
+	  nat_height = MAX (nat_height, arrow_height);
+
+	  min_height += ypad;
+	  nat_height += ypad;
+        }
+      else
+        {
+	  /* there is a custom child widget inside (no priv->cell_view) */
+          gint but_width, but_height;
+
+          gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->button), &but_width, NULL);
+          gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (priv->button), 
+						 but_width, &but_height, NULL);
+
+	  size -= but_width;
+
+	  gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
+	  
+	  min_height = MAX (min_height, but_height);
+	  nat_height = MAX (nat_height, but_height);
+        }
+    }
+  else
+    {
+      /* list mode */
+      gint but_width, but_height;
+      gint xpad = 0, ypad = 0;
+
+      gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->button), &but_width, NULL);
+      gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (priv->button), 
+					     but_width, &but_height, NULL);
+      
+      if (priv->cell_view_frame && priv->has_frame)
+	{
+	  gint border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
+
+	  xpad = 2 * (border_width + GTK_WIDGET (priv->cell_view_frame)->style->xthickness);
+	  ypad = 2 * (border_width + GTK_WIDGET (priv->cell_view_frame)->style->ythickness);
+	}
+
+      size -= but_width;
+      size -= 2 * focus_width;
+      size -= xpad;
+
+      gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
+	  
+      min_height = MAX (min_height, but_height);
+      nat_height = MAX (nat_height, but_height);
+
+      min_height += ypad;
+      nat_height += ypad;
+    }
+
+  if (GTK_SHADOW_NONE != priv->shadow_type)
+    {
+      min_height += 2 * GTK_WIDGET (widget)->style->ythickness;
+      nat_height += 2 * GTK_WIDGET (widget)->style->ythickness;
+    }
+
+  if (minimum_size)
+    *minimum_size = min_height;
+
+  if (natural_size)
+    *natural_size = nat_height;
+}



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