2008-09-03  Paolo Bonzini  <bonzini@gnu.org>

	* gtk/gtk.symbols: Add new symbols for GtkViewportConstraint and
	its usage in gtk_viewport.
	* gtk/gtkenums.h: Add GtkViewportConstraint.
	* gtk/gtkviewport.h: Add a new GtkViewportConstraint field and
	its accessors.  Turn the existing shadow_type field into a bitfield.
	* gtk/gtkviewport.c: Add the corresponding property; use
	extended layout info.

	* tests/testextlayoutviewport.c: New.
	* tests/Makefile.am: Add rules for it.
---
 gtk/gtk.symbols               |    3 +
 gtk/gtkenums.h                |    8 +++
 gtk/gtkviewport.c             |  115 +++++++++++++++++++++++++++++++++++++++--
 gtk/gtkviewport.h             |   31 ++++++-----
 tests/Makefile.am             |    7 +++
 tests/testextlayoutviewport.c |   80 ++++++++++++++++++++++++++++
 7 files changed, 236 insertions(+), 18 deletions(-)
 create mode 100644 tests/testextlayoutviewport.c

diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index 842148a..dc9f6a5 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -358,6 +358,7 @@ gtk_icon_theme_error_get_type G_GNUC_CONST
 gtk_toolbar_child_type_get_type G_GNUC_CONST
 gtk_toolbar_space_style_get_type G_GNUC_CONST
 gtk_toolbar_style_get_type G_GNUC_CONST
+gtk_viewport_constraint_get_type G_GNUC_CONST
 #endif
 #endif
 
@@ -4553,11 +4554,13 @@ gtk_vbutton_box_new
 #if IN_FILE(__GTK_VIEWPORT_C__)
 gtk_viewport_get_hadjustment
 gtk_viewport_get_shadow_type
+gtk_viewport_get_size_constraint
 gtk_viewport_get_type G_GNUC_CONST
 gtk_viewport_get_vadjustment
 gtk_viewport_new
 gtk_viewport_set_hadjustment
 gtk_viewport_set_shadow_type
+gtk_viewport_set_size_constraint
 gtk_viewport_set_vadjustment
 #endif
 #endif
diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h
index eeb3ac7..c8cde2a 100644
--- a/gtk/gtkenums.h
+++ b/gtk/gtkenums.h
@@ -413,6 +413,14 @@ typedef enum
   GTK_UPDATE_DELAYED
 } GtkUpdateType;
 
+/* Viewport flags to constrain width or height */
+typedef enum
+{
+  GTK_VIEWPORT_CONSTRAIN_NONE,
+  GTK_VIEWPORT_CONSTRAIN_WIDTH,
+  GTK_VIEWPORT_CONSTRAIN_HEIGHT
+} GtkViewportConstraint;
+
 /* Generic visibility flags */
 typedef enum
 {
diff --git a/gtk/gtkviewport.c b/gtk/gtkviewport.c
index 114415b..c504f33 100644
--- a/gtk/gtkviewport.c
+++ b/gtk/gtkviewport.c
@@ -28,6 +28,7 @@
 #include "gtkviewport.h"
 #include "gtkintl.h"
 #include "gtkmarshalers.h"
+#include "gtkextendedlayout.h"
 #include "gtkprivate.h"
 #include "gtkalias.h"
 
@@ -35,7 +36,8 @@ enum {
   PROP_0,
   PROP_HADJUSTMENT,
   PROP_VADJUSTMENT,
-  PROP_SHADOW_TYPE
+  PROP_SHADOW_TYPE,
+  PROP_SIZE_CONSTRAINT
 };
 
 
@@ -125,6 +127,15 @@ gtk_viewport_class_init (GtkViewportClass *class)
 						      GTK_SHADOW_IN,
 						      GTK_PARAM_READWRITE));
 
+  g_object_class_install_property (gobject_class,
+                                   PROP_SIZE_CONSTRAINT,
+                                   g_param_spec_enum ("size-constraint",
+						      P_("Size constraint"),
+						      P_("Determines whether the viewport constrains the width or height of its child"),
+						      GTK_TYPE_VIEWPORT_CONSTRAINT,
+						      GTK_VIEWPORT_CONSTRAIN_NONE,
+						      GTK_PARAM_READWRITE));
+
   widget_class->set_scroll_adjustments_signal =
     g_signal_new (I_("set_scroll_adjustments"),
 		  G_OBJECT_CLASS_TYPE (gobject_class),
@@ -158,6 +169,9 @@ gtk_viewport_set_property (GObject         *object,
     case PROP_SHADOW_TYPE:
       gtk_viewport_set_shadow_type (viewport, g_value_get_enum (value));
       break;
+    case PROP_SIZE_CONSTRAINT:
+      gtk_viewport_set_size_constraint (viewport, g_value_get_enum (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -185,6 +199,9 @@ gtk_viewport_get_property (GObject         *object,
     case PROP_SHADOW_TYPE:
       g_value_set_enum (value, viewport->shadow_type);
       break;
+    case PROP_SIZE_CONSTRAINT:
+      g_value_set_enum (value, viewport->size_constraint);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -200,6 +217,8 @@ gtk_viewport_init (GtkViewport *viewport)
   gtk_container_set_resize_mode (GTK_CONTAINER (viewport), GTK_RESIZE_QUEUE);
   
   viewport->shadow_type = GTK_SHADOW_IN;
+  viewport->size_constraint = GTK_VIEWPORT_CONSTRAIN_NONE;
+
   viewport->view_window = NULL;
   viewport->bin_window = NULL;
   viewport->hadjustment = NULL;
@@ -348,6 +367,50 @@ viewport_reclamp_adjustment (GtkAdjustment *adjustment,
 }
 
 static void
+viewport_child_size_request (GtkViewport   	  *viewport,
+			     GtkWidget     	  *child,
+			     GtkRequisition	  *requisition,
+			     GtkChildSizeRequest   size_request_func)
+{
+  GtkExtendedLayoutFeatures features = 0;
+  gboolean width_for_height;
+  gboolean height_for_width;
+ 
+  if (GTK_IS_EXTENDED_LAYOUT (child))
+    features = gtk_extended_layout_get_features ((GtkExtendedLayout*) child);
+
+  width_for_height = (viewport->size_constraint == GTK_VIEWPORT_CONSTRAIN_HEIGHT &&
+		      (features & GTK_EXTENDED_LAYOUT_WIDTH_FOR_HEIGHT));
+  height_for_width = (viewport->size_constraint == GTK_VIEWPORT_CONSTRAIN_WIDTH &&
+		      (features & GTK_EXTENDED_LAYOUT_HEIGHT_FOR_WIDTH));
+
+  if (height_for_width || width_for_height)
+    {
+      GtkAllocation view_allocation;
+      viewport_get_view_allocation (viewport, &view_allocation);
+      if (height_for_width)
+        {
+          requisition->width = view_allocation.width;
+          requisition->height = gtk_extended_layout_get_height_for_width (
+            (GtkExtendedLayout*) child, view_allocation.width);
+        }
+
+      else
+        {
+          requisition->width = gtk_extended_layout_get_width_for_height (
+            (GtkExtendedLayout*) child, view_allocation.height);
+          requisition->height = view_allocation.height;
+        }
+    }
+
+  else if (features & GTK_EXTENDED_LAYOUT_NATURAL_SIZE)
+    gtk_extended_layout_get_natural_size ((GtkExtendedLayout*) child, requisition);
+
+  else 
+    size_request_func (child, requisition, (GtkWidget *) viewport);
+}
+
+static void
 viewport_set_hadjustment_values (GtkViewport *viewport,
 				 gboolean    *value_changed)
 {
@@ -373,7 +436,8 @@ viewport_set_hadjustment_values (GtkViewport *viewport,
     {
       GtkRequisition child_requisition;
       
-      gtk_widget_get_child_requisition (bin->child, &child_requisition);
+      viewport_child_size_request (viewport, bin->child, &child_requisition,
+				   (GtkChildSizeRequest) gtk_widget_get_child_requisition);
       hadjustment->upper = MAX (child_requisition.width, view_allocation.width);
     }
   else
@@ -410,7 +474,8 @@ viewport_set_vadjustment_values (GtkViewport *viewport,
     {
       GtkRequisition child_requisition;
       
-      gtk_widget_get_child_requisition (bin->child, &child_requisition);
+      viewport_child_size_request (viewport, bin->child, &child_requisition,
+				   (GtkChildSizeRequest) gtk_widget_get_child_requisition);
       vadjustment->upper = MAX (child_requisition.height, view_allocation.height);
     }
   else
@@ -547,6 +612,45 @@ gtk_viewport_get_shadow_type (GtkViewport *viewport)
   return viewport->shadow_type;
 }
 
+
+/** 
+ * gtk_viewport_set_size_constraint:
+ * @viewport: a #GtkViewport.
+ * @type: the new size constraint.
+ *
+ * Sets the size constraint of the viewport.
+ **/ 
+void
+gtk_viewport_set_size_constraint (GtkViewport   *viewport,
+			          GtkViewportConstraint  type)
+{
+  g_return_if_fail (GTK_IS_VIEWPORT (viewport));
+
+  if ((GtkViewportConstraint) viewport->size_constraint != type)
+    {
+      viewport->size_constraint = type;
+      gtk_widget_queue_resize (GTK_WIDGET (viewport));
+      g_object_notify (G_OBJECT (viewport), "size-constraint");
+    }
+}
+
+/**
+ * gtk_viewport_get_size_constraint:
+ * @viewport: a #GtkViewport
+ *
+ * Gets the shadow type of the #GtkViewport. See
+ * gtk_viewport_set_size_constraint().
+ 
+ * Return value: the size constraint
+ **/
+GtkViewportConstraint
+gtk_viewport_get_size_constraint (GtkViewport *viewport)
+{
+  g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), GTK_VIEWPORT_CONSTRAIN_NONE);
+
+  return viewport->size_constraint;
+}
+
 static void
 gtk_viewport_realize (GtkWidget *widget)
 {
@@ -720,7 +824,10 @@ gtk_viewport_size_request (GtkWidget      *widget,
 
   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
     {
-      gtk_widget_size_request (bin->child, &child_requisition);
+      viewport_child_size_request (GTK_VIEWPORT (widget), bin->child,
+				   &child_requisition,
+				   (GtkChildSizeRequest) gtk_widget_size_request);
+
       requisition->width += child_requisition.width;
       requisition->height += child_requisition.height;
     }
diff --git a/gtk/gtkviewport.h b/gtk/gtkviewport.h
index 8b64f17..b07382a 100644
--- a/gtk/gtkviewport.h
+++ b/gtk/gtkviewport.h
@@ -51,7 +51,9 @@ struct _GtkViewport
 {
   GtkBin bin;
 
-  GtkShadowType shadow_type;
+  guint shadow_type : 3;
+  guint size_constraint : 2;
+
   GdkWindow *view_window;
   GdkWindow *bin_window;
   GtkAdjustment *hadjustment;
@@ -68,18 +70,21 @@ struct _GtkViewportClass
 };
 
 
-GType          gtk_viewport_get_type        (void) G_GNUC_CONST;
-GtkWidget*     gtk_viewport_new             (GtkAdjustment *hadjustment,
-					     GtkAdjustment *vadjustment);
-GtkAdjustment* gtk_viewport_get_hadjustment (GtkViewport   *viewport);
-GtkAdjustment* gtk_viewport_get_vadjustment (GtkViewport   *viewport);
-void           gtk_viewport_set_hadjustment (GtkViewport   *viewport,
-					     GtkAdjustment *adjustment);
-void           gtk_viewport_set_vadjustment (GtkViewport   *viewport,
-					     GtkAdjustment *adjustment);
-void           gtk_viewport_set_shadow_type (GtkViewport   *viewport,
-					     GtkShadowType  type);
-GtkShadowType  gtk_viewport_get_shadow_type (GtkViewport   *viewport);
+GType                 gtk_viewport_get_type            (void) G_GNUC_CONST;
+GtkWidget*            gtk_viewport_new                 (GtkAdjustment *hadjustment,
+							GtkAdjustment *vadjustment);
+GtkAdjustment*        gtk_viewport_get_hadjustment     (GtkViewport   *viewport);
+GtkAdjustment*        gtk_viewport_get_vadjustment     (GtkViewport   *viewport);
+void                  gtk_viewport_set_hadjustment     (GtkViewport   *viewport,
+							GtkAdjustment *adjustment);
+void                  gtk_viewport_set_vadjustment     (GtkViewport   *viewport,
+							GtkAdjustment *adjustment);
+void                  gtk_viewport_set_shadow_type     (GtkViewport   *viewport,
+							GtkShadowType  type);
+GtkShadowType         gtk_viewport_get_shadow_type     (GtkViewport   *viewport);
+void                  gtk_viewport_set_size_constraint (GtkViewport   *viewport,
+							GtkViewportConstraint  type);
+GtkViewportConstraint gtk_viewport_get_size_constraint (GtkViewport   *viewport);
 
 
 G_END_DECLS
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 99c19e6..1338dd8 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -45,6 +45,7 @@ noinst_PROGRAMS =			\
 	testellipsise			\
 	testentrycompletion 		\
 	testextendedlayout 		\
+	testextlayoutviewport 		\
 	testfilechooser			\
 	testfilechooserbutton		\
 	testgtk				\
@@ -111,6 +112,7 @@ testdnd_DEPENDENCIES = $(TEST_DEPS)
 testellipsise_DEPENDENCIES = $(TEST_DEPS)
 testentrycompletion_DEPENDENCIES = $(TEST_DEPS)
 testextendedlayout_DEPENDENCIES = $(TEST_DEPS)
+testextlayoutviewport_DEPENDENCIES = $(TEST_DEPS)
 testfilechooser_DEPENDENCIES = $(TEST_DEPS)
 testfilechooserbutton_DEPENDENCIES = $(TEST_DEPS)
 testgtk_DEPENDENCIES = $(TEST_DEPS)
@@ -167,6 +169,7 @@ testdnd_LDADD = $(LDADDS)
 testellipsise_LDADD = $(LDADDS)
 testentrycompletion_LDADD = $(LDADDS)
 testextendedlayout_LDADD = $(LDADDS)
+testextlayoutviewport_LDADD = $(LDADDS)
 testfilechooser_LDADD = $(LDADDS)
 testfilechooserbutton_LDADD = $(LDADDS)
 testgtk_LDADD = $(LDADDS)
@@ -230,6 +234,9 @@ testentrycompletion_SOURCES = 	\
 testextendedlayout_SOURCES = 	\
 	testextendedlayout.c
 
+testextlayoutviewport_SOURCES = 	\
+	testextlayoutviewport.c
+
 testfilechooser_SOURCES = 	\
 	prop-editor.c		\
 	testfilechooser.c 	
diff --git a/tests/testextlayoutviewport.c b/tests/testextlayoutviewport.c
new file mode 100644
index 0000000..724e816
--- /dev/null
+++ b/tests/testextlayoutviewport.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2008 Free Software Foundation, Inc.
+ * Author: Paolo Bonzini <bonzini@gnu.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <gtk/gtk.h>
+
+static void
+option_cb (GtkToggleButton *option,
+	   GtkWidget *viewport)
+{
+  gboolean active = gtk_toggle_button_get_active (option);
+
+  gtk_viewport_set_size_constraint (GTK_VIEWPORT (viewport),
+				    active ? GTK_VIEWPORT_CONSTRAIN_HEIGHT
+					   : GTK_VIEWPORT_CONSTRAIN_WIDTH);
+
+  gtk_label_set_angle (GTK_LABEL (gtk_bin_get_child (GTK_BIN (viewport))),
+		       active ? 90 : 0);
+}
+
+int
+main (int    argc,
+      char **argv)
+{
+  GtkWidget *window;
+  GtkWidget *scrolled_window, *viewport, *label, *vbox, *option;
+  
+  gtk_init (&argc, &argv);
+  
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  g_signal_connect (G_OBJECT (window), "delete-event",
+		    G_CALLBACK (gtk_main_quit), NULL);
+  
+  vbox = gtk_vbox_new (FALSE, 4);
+  gtk_container_add (GTK_CONTAINER (window), vbox);
+  
+  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+  gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
+  
+  viewport = gtk_viewport_new (NULL, NULL);
+  gtk_viewport_set_size_constraint (GTK_VIEWPORT (viewport),
+				    GTK_VIEWPORT_CONSTRAIN_WIDTH);
+  gtk_container_add (GTK_CONTAINER (scrolled_window), viewport);
+  
+#define STR "This is a test %# This is a test %@# This is a test %@#$ "
+  label = gtk_label_new (STR STR STR STR STR STR STR STR STR STR STR STR STR
+			 STR STR STR STR STR STR STR STR STR STR STR STR STR
+			 STR STR STR STR STR STR STR STR STR STR STR STR STR
+			 STR STR STR STR STR STR STR STR STR STR STR STR STR);
+
+  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+  gtk_label_set_full_size (GTK_LABEL (label), TRUE);
+  gtk_misc_set_alignment (&(GTK_LABEL (label)->misc), 0.0, 0.0);
+  gtk_container_add (GTK_CONTAINER (viewport), label);
+
+  option = gtk_check_button_new_with_label ("Horizontal scrollbar");
+  g_signal_connect (G_OBJECT (option), "toggled", G_CALLBACK (option_cb), viewport);
+  
+  gtk_box_pack_end (GTK_BOX (vbox), option, FALSE, FALSE, 0);
+  
+  gtk_widget_show_all (window);
+  gtk_main ();
+  return 0;
+}
-- 
1.5.5

