 ChangeLog.gtk-extended-layout                  |  438 ++++
 docs/reference/ChangeLog.gtk-extended-layout   |    6 +
 docs/reference/gtk/gtk-docs.sgml               |    2 +
 docs/reference/gtk/gtk-sections.txt            |   20 +
 docs/reference/gtk/gtk.types                   |    1 +
 docs/reference/gtk/tmpl/gtkextendedlayout.sgml |   85 +
 gtk/Makefile.am                                |    2 +
 gtk/gtk.h                                      |    1 +
 gtk/gtk.symbols                                |   20 +
 gtk/gtkalignment.c                             |  139 +-
 gtk/gtkbin.c                                   |  140 ++-
 gtk/gtkbutton.c                                |  133 +-
 gtk/gtkcellrenderertext.c                      |   41 +-
 gtk/gtkcellview.c                              |  106 +-
 gtk/gtkenums.h                                 |    9 +
 gtk/gtkextendedlayout.c                        |  284 +++
 gtk/gtkextendedlayout.h                        |  103 +
 gtk/gtkframe.c                                 |   68 +-
 gtk/gtkhbox.c                                  |  631 +++++--
 gtk/gtkhbox.h                                  |    9 +-
 gtk/gtklabel.c                                 |  359 +++-
 gtk/gtklabel.h                                 |    3 +
 gtk/gtkplug-x11.c                              |   26 +
 gtk/gtkplug.c                                  |   17 +-
 gtk/gtkplugprivate.h                           |    9 +
 gtk/gtkprivate.h                               |    4 +
 gtk/gtkscrolledwindow.c                        |   46 +-
 gtk/gtksocket-x11.c                            |   64 +-
 gtk/gtksocket.c                                |   57 +-
 gtk/gtksocketprivate.h                         |    9 +
 gtk/gtktable.c                                 |  254 ++-
 gtk/gtktable.h                                 |    4 +
 gtk/gtktreeprivate.h                           |    7 +
 gtk/gtktreeview.c                              |  142 ++-
 gtk/gtktreeviewcolumn.c                        |  130 +-
 gtk/gtktreeviewcolumn.h                        |    5 +-
 gtk/gtkvbox.c                                  |  347 ++--
 tests/Makefile.am                              |   14 +-
 tests/autotestextendedlayout.c                 |  702 +++++++
 tests/testellipsise.c                          |  121 +-
 tests/testextendedlayout.c                     | 2654 ++++++++++++++++++++++++
 41 files changed, 6600 insertions(+), 612 deletions(-)

diff --git a/ChangeLog.gtk-extended-layout b/ChangeLog.gtk-extended-layout
new file mode 100644
index 0000000..9416055
--- /dev/null
+++ b/ChangeLog.gtk-extended-layout
@@ -0,0 +1,438 @@
+2007-08-28  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtktable.c: Make sure natural_delta is initialized.
+
+2007-08-28  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c: Test size-for-allocation for GtkTable.
+	* gtk/gtk.symbols, gtk/gtktable.c, gtk/gtktable.h: Add orientation
+	property needed for choosing a size-for-allocation strategy.
+
+2007-08-28  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkhbox.c: Consider width-for-height information.
+	* gtk/gtkvbox.c, gtk/gtkprivate.h: Move GtkChildSizeRequest
+	to gtkprivate.h.
+
+2007-08-20  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkvbox.c: Properly calculate minimum_height in size-allocate.
+	So I yell it into the night: Height-for-width is shiny and bright!
+
+2007-08-20  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtklabel.c: Announce size-for-allocation features only when
+	ellipsis or wrapping with full size allocation are active.
+	* tests/testextendedlayout.c: Display size-for-allocation information
+	in statusbar.
+
+2007-08-20  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c: Improve contrast for currently
+	selected widget, show pointer address of widget in status bar.
+	Provider names in size-for-allocation test.
+
+2007-08-20  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c: Start real testing of height-for-width
+	and width-for-height.
+	* gtk/gtklabel.c, gtk/gtklabel.h, gtk/gtk.symbols: Add "full-size"
+	property to be able to provide a meaningfull implementation of
+	height-for-width and width-for-height.
+	* gtk/gtkvbox.c: Consider height-for-width information.
+
+2007-08-13  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c: Provide context menu to directly select
+	testable widgets. Defer self invokation for socket testing until
+	sockets are realized: Plugs cannot receive the "embedded" signal
+	before their socket is realized.
+
+2007-08-13  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testellipsise.c: Pimped the test for better demonstration. 
+	* gtk/gtklabel.c: Correctly calulate layout width for rotated
+	text when ellipses are used.
+
+2007-08-12  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkbin.c: Filter feature set returned by the child to match
+	the subset supported by bins. Ensure natural size matches at least
+	the requested size.
+	* gtk/gtkextendedlayout.c: Ensure natural size matches at least
+	the requested size by considering explicit size requests.
+	* gtk/gtkhbox.c, gtk/gtkvbox.c, gtk/gtktable.c, gtk/gtktable.h: 
+	Delegate natural size calculation to the (modified) size_request
+	method for more accurate resutls. This trick was developed already
+	for GtkLabel, but not recognized before working on GtkScrolledWindow.
+	* gtk/gtktreeview.c: Skip invisible columns for natural size.
+	* tests/autotestextendedlayout.c: Test natural size properties
+	for all classes implementing GtkExtendLayout.
+
+2007-08-06  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtklabel.c: Practice consistent pixel rounding to avoid GtkLabel
+	showing ellipses when PANGO_PIXELS and PANGO_PIXELS_FLOOR differ by
+	less then 0.5 pixels.
+
+2007-08-06  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtktreeviewcolumn.c:
+	Rename gtk_tree_view_column_cell_get_size_impl
+	to gtk_tree_view_column_cell_get_real_size to
+	fit naming conventions.
+	* gtk/gtkscrolledwindow.c:
+	Provide and consider natural size information.
+	* tests/testextendedlayout.c:
+	Show natural size of the hovered widget in statusbar.
+
+2007-08-06  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtktreeprivate.h, gtk/gtktreeviewcolumn.h: 
+	Introduce GtkTreeViewColumnPrivate.
+	* gtk/gtktreeview.c, gtk/gtktreeviewcolumn.c:
+	Provide and consider natural size information.
+
+2007-08-06  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c: Use tree-view instead of notebook
+	tabs for test case navigation. Support running a sub-set of tests.
+	Display plug-id of sockets as tooltip.
+	
+2007-08-06  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c: Improve layout of natural size for
+	various widgets test and add a test for GtkTreeViews within
+	GtkScrolledWindows to it.
+
+2007-08-06  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtksocket.c: Repair sizing algorithm - gtk_socket_size_request
+	and gtk_socket_extended_layout_get_natural_size were calling each
+	other's windowing backend function. Was I drunk when commiting the
+	previous revision?
+
+2007-08-01  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkcellrenderertext.c: Provide natural size information.
+	* gtk/gtkcellview.c: Consider and provide natural size information.
+	* tests/testextendedlayout.c: Change background of testing cell view.
+
+2007-08-01  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkcellview.c: Seems there is no point in distinguishing between
+	GTK_PACK_START and GTK_PACK_END during size allocation. The loops only
+	differ in the packing check.
+
+2007-08-01  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkcellview.c: Fix up the mess I commited before.
+	Rule of the thumb: Never write code when tired or in hurry.
+
+2007-07-29  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkcellview.c: Avoid rounding errors in size
+	allocation by adopting the algorithm used by GtkHBox.
+
+2007-07-28  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c: Preparing natural 
+	size tests for GtkCellView and GtkTreeView.
+
+2007-07-27  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c: Add paned widgets to
+	the natural size test for GtkAlignment and GtkSocket.
+	* gtk/gtksocket-x11.c: Fix some cast.
+
+2007-07-27  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkalignment.c, gtk/gtksocket.c, gtk/gtksocket-x11.c,
+	gtk/gtksocketprivate.h: Consider natural size of the child.
+	* gtk/gtkplug.c, gtk/gtkplug-x11.c, gtk/gtkplugprivate.h:
+	Expose natural size via _GTK_NATURAL_SIZE window property.
+	* tests/testextendedlayout.c: Test natural size handling
+	of GtkAlignment, GtkPlug and GtkSocket.
+
+2007-07-26  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtktable.c: Seems natural size for spanning cells worked already.
+	* tests/testextendedlayout.c: Test spanning cells. Do not expand
+	succeeded root nodes.
+
+2007-07-24  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkhbox.c, gtk/gtktable.c, gtk/gtkvbox.c: Also apply natural
+	size, when there are no expandable children. Fix natural size 
+	calculation for GtkVBox.
+	* tests/testextendedlayout.c: More extensive natural size testing.
+	Make sure that really all guide groups are tested. Add detail
+	to test case label in results page.
+
+2007-07-24  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtktable.c, gtk/gtktable.h: Consider natural size to allocate 
+	widgets spanning a single, rename GtkTableRowCol::natural_size.
+	Remove some lost g_print statements.
+	* tests/testextendedlayout.c: Test natural size allocation in
+	GtkTable for simple cases.
+
+2007-07-23  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtktable.c, gtk/gtktable.h: Provide natural size information.
+	* tests/autotestextendedlayout.c: Implemented some initial natural 
+	size tests for GtkTable.
+
+2007-07-18  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkextendedlayout.h, gtk/gtkhbox.c, gtk/gtkvbox.c:
+	Make the type check part of the extended layout feature check macros.
+
+2007-07-16  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testellipsise.c: Extend the test to support rotations.
+	* gtk/gtklabel.c: Support ellipsizing and wrapping on labels
+	rotated by multiples of 90°.
+
+2007-07-16  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkvbox.c, tests/testextendedlayout.c:
+	Implement and test natural size support for GtkVBox.
+	* gtk/gtkhbox.c: Cleanup issues found when implementing
+	natural size for GtkVBox.
+
+2007-07-15  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c: Improve test result grouping.
+
+2007-07-04  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkbin.c: Propagate baseline offset to the child widget.
+	* gtk/gtkhbox.c: Properly apply baseline alignment by using the
+	baseline found during requisition calculation for size allocation.
+	* tests/testextendedlayout.c: Add yscale variantes of GtkAlignment
+	to the baseline tests. Improve widget names. Split status message
+	into two lines.
+
+2007-07-03  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c: Allow choosing initially active 
+	notebook page from command line and create more sane widget names
+	for the sake of g_print-debugging.
+
+2007-07-02  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkbin.c: Properly evaluate natural size.
+	* gtk/gtkhbox.c: Consider natural size for child placement.
+	* gtk/gtklabel.c: Drop any PangoLayout width assigned during the
+	size allocation process to get proper natural size readings.
+	* tests/testextendedlayout.c: Add some GtkHPaned to the natural
+	size test for dynamic evaluation of the algorithm. Create guides for
+	the button of the height-for-width test for debugging, as it is
+	affected by natural sizing of GtkHBox.
+
+2007-07-01  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtk.symbols, gtk/gtkextendedlayout.c, gtk/gtkextendedlayout.h:
+	Add gtk_extended_layout_set_baseline_offset to allow widget to adjust
+	their baselines to the findings of their parent container.
+	* gtk/gtkhbox.c: Use gtk_extended_layout_set_baseline_offset to obey
+	the uniform height constraint of the horizontal box. Do not cache
+	baselines found during requisition evaluation, as baselines can
+	change, when assigning a widget more space than requested.
+	* gtk/gtklabel.c: Implement gtk_extended_layout_set_baseline_offset.
+
+2007-07-01  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c: Invalidate previously selected
+	widget when choosing another one, to avoid drawing artifacts.
+
+2007-07-01  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c: Improve drawing of baseline guides.
+
+2007-07-01  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkhbox.c: Properly report feature set and consider
+	baseline_policy when evaluating the baseline of the box.
+	* gtk/gtklabel.c: Adjust the coding style of get_features
+	to match the style of GtkHBox.
+
+2007-07-01  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkhbox.c: Make baseline alignment work, by ignoring the uniform
+	height constraint a GtkHBox implies. TODO: Obey that constraint.
+
+2007-07-01  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkhbox.c: Rudimentary implementation of GtkExtendLayout.
+
+2007-06-30  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c: Provide button variant of the 
+	GtkHBox baseline alignment test, as those buttons show some
+	shortcomings of my current baseline concept.
+
+2007-06-30  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c:
+	Provide status text, when no widget is selected.
+
+2007-06-29  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkframe.c: Drop g_debug messages.
+
+2007-06-29  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c: Move buttons to top and
+	provide textual information about currently hovered widget.
+
+2007-06-29  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c: Move code to setup UI
+	from main to test_suite_new.
+
+2007-06-29  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c: Split Baseline Alignment tests into
+	separate tests and move the notebook tabs to the right.
+	* gtk/gtkhbox.c: Properly decode "debug-wanted" object
+	tag with GPOINTER_TO_INT.
+
+2007-06-29  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkextendedlayout.h, gtk/gtkextendedlayout.c,
+	gtk/gtk.symbols: Add gtk_extended_layout_get_padding.
+	* gtk/gtkalignment.c, gtk/gtkbin.c, gtk/gtkbutton.c, 
+	gtk/gtkframe.c: Implement gtk_extended_layout_get_padding.
+	* tests/autotestextendedlayout.c: Verify padding information 
+	provided via gtk_extended_layout_get_padding.
+
+2007-06-28  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkhbox.c: Merge separate, but nearly identical child allocation
+	code for start and end packing into one single loop. This should avoid
+	programming errors caused by extensive code duplication, as they happend 
+	to me with the previous commit. Maybe some advanced compiler is able
+	to do the trivial loop unrolling.
+	* tests/testextendedlayout.c: Use exteriour guides instead of
+	separators to identify GtkHBox allocation and extend the baseline
+	alignment tests for GtkHBox.
+
+2007-06-28  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkhbox.c, gtk/gtkhbox.h, gtk/gtk.symbols:
+	Initial, buggish implementation of baseline alignment in GtkHBox.
+	* tests/testextendedlayout.c: Add baseline alignment tests.
+
+2007-06-28  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkenums.h: Introduce GtkBaselinePolicy.
+	* gtk/gtk.symbols: Add missing symbols.
+	* gtk/gtkextendedlayout.c, gtk/gtkextendedlayout.h:
+	  Introduce gtk_extended_layout_get_single_baseline.
+
+2007-06-28  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkbin.c: Prevent undefined behaviour when
+	gtk_widget_translate_coordinates fails.
+	
+2007-06-20  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c: Provide checkboxes 
+	to choose guide types at runtime.
+
+2007-06-20  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c: Don't create
+	the tile pixmap with every expose event.
+
+2007-06-20  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkextendedlayout.h, tests/testextendedlayout.c:
+	Dynamically highlight the currently hovered widget and its guides.
+
+2007-06-20  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtkbin.c, tests/autotestextendedlayout.c,
+	tests/testextendedlayout.c: Implement GtkExtendedLayout for GtkBin.
+
+2007-06-20  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtklabel.c, tests/autotestextendedlayout.c:
+	Implement get_natural_size for GtkLabel.
+
+2007-06-20  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtklabel.c, tests/autotestextendedlayout.c: 
+	Implement get_height_for_width for GtkLabel.
+
+2007-06-20  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/autotestextendedlayout.c: Correct some assumptions,
+	write baseline values to console.
+
+2007-06-20  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtklabel.c: Remove some late hacking crack.
+
+2007-06-20  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/gtklabel.c, tests/autotestextendedlayout.c,
+	tests/testextendedlayout.c, tests/Makefile.am:
+	Implementing get_baselines for GtkLabel and testing it.
+
+2007-06-19  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* gtk/Makefile.am, gtk/gtk.h, gtk/gtkextendedlayout.c, 
+	gtk/gtkextendedlayout.h: Implement GtkExtendLayout interface.
+
+2007-06-18  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+       * gdk/*, gtk/*: Resynching with trunk to avoid future
+       merge conflicts with the giant GtkBuilder patch.
+
+2007-05-29  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* testextendedlayout.c: Implement formal testing.
+
+2007-05-29  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* testextendedlayout.c: Create initial height-for-width test.
+
+2007-05-29  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* testextendedlayout.c: Also retreive base line information
+	for GtkTextView widgets.
+
+2007-05-28  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* testextendedlayout.c: Ported guides drawing to legacy
+	GdkGC API, which looks worse but supports IncludeInferiors.
+
+2007-05-28  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* testextendedlayout.c: Provide some hint which label
+	a guide belongs to.
+
+2007-05-28  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* testextendedlayout.c: Some refactoring to get rid
+	of the global list of allocation guides.
+
+2007-05-27  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c, tests/Makefile.am:
+	Build-fixes for testextendedlayout.
+
+2007-04-21  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c: Initial implementation of
+	base line test.
+
+2007-04-20  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* tests/testextendedlayout.c, tests/Makefile.am,
+	ChangeLog.gtk-extended-layout: Creating testing framework,
+	starting with natural size testing.
+
diff --git a/docs/reference/ChangeLog.gtk-extended-layout b/docs/reference/ChangeLog.gtk-extended-layout
new file mode 100644
index 0000000..96c0329
--- /dev/null
+++ b/docs/reference/ChangeLog.gtk-extended-layout
@@ -0,0 +1,6 @@
+2007-06-19  Mathias Hasselmann  <mathias.hasselmann@gmx.de>
+
+	* docs/reference/gtk/tmpl/gtkextendedlayout.sgml,
+	docs/reference/gtk/gtk-docs.sgml, docs/reference/gtk/gtk-sections.txt,
+	docs/reference/gtk/gtk.types: Implement GtkExtendLayout interface.
+
diff --git a/docs/reference/gtk/gtk-docs.sgml b/docs/reference/gtk/gtk-docs.sgml
index 114e143..c919471 100644
--- a/docs/reference/gtk/gtk-docs.sgml
+++ b/docs/reference/gtk/gtk-docs.sgml
@@ -37,6 +37,7 @@
 <!ENTITY GtkEntryCompletion SYSTEM "xml/gtkentrycompletion.xml">
 <!ENTITY GtkEventBox SYSTEM "xml/gtkeventbox.xml">
 <!ENTITY GtkExpander SYSTEM "xml/gtkexpander.xml">
+<!ENTITY GtkExtendedLayout SYSTEM "xml/gtkextendedlayout.xml">
 <!ENTITY GtkFileChooser SYSTEM "xml/gtkfilechooser.xml">
 <!ENTITY GtkFileChooserButton SYSTEM "xml/gtkfilechooserbutton.xml">
 <!ENTITY GtkFileChooserDialog SYSTEM "xml/gtkfilechooserdialog.xml">
@@ -555,6 +556,7 @@ that is, GUI components such as #GtkButton or #GtkTextView.
         &GtkBox;
         &GtkButtonBox;
         &GtkContainer;
+        &GtkExtendedLayout;
         &GtkItem;
         &GtkMenuShell;
         &GtkMisc;
diff --git a/docs/reference/gtk/gtk-sections.txt b/docs/reference/gtk/gtk-sections.txt
index 92fda0f..d713412 100644
--- a/docs/reference/gtk/gtk-sections.txt
+++ b/docs/reference/gtk/gtk-sections.txt
@@ -1318,6 +1318,26 @@ GtkExpanderPrivate
 </SECTION>
 
 <SECTION>
+<FILE>gtkextendedlayout</FILE>
+<TITLE>GtkExtendedLayout</TITLE>
+GtkExtendedLayout
+GtkExtendedLayoutIface
+gtk_extended_layout_get_baselines
+gtk_extended_layout_get_features
+gtk_extended_layout_get_height_for_width
+gtk_extended_layout_get_natural_size
+gtk_extended_layout_get_width_for_height
+<SUBSECTION Standard>
+GTK_EXTENDED_LAYOUT
+GTK_EXTENDED_LAYOUT_CLASS
+GTK_EXTENDED_LAYOUT_GET_IFACE
+GTK_IS_EXTENDED_LAYOUT
+GTK_TYPE_EXTENDED_LAYOUT
+<SUBSECTION Private>
+gtk_extended_layout_get_type
+</SECTION>
+
+<SECTION>
 <FILE>gtkfilechooser</FILE>
 <TITLE>GtkFileChooser</TITLE>
 GtkFileChooser
diff --git a/docs/reference/gtk/gtk.types b/docs/reference/gtk/gtk.types
index fde19ea..5d1bbfb 100644
--- a/docs/reference/gtk/gtk.types
+++ b/docs/reference/gtk/gtk.types
@@ -54,6 +54,7 @@ gtk_entry_completion_get_type
 gtk_entry_get_type
 gtk_event_box_get_type
 gtk_expander_get_type
+gtk_extended_layout_get_type
 gtk_file_chooser_button_get_type
 gtk_file_chooser_dialog_get_type
 gtk_file_chooser_get_type
diff --git a/docs/reference/gtk/tmpl/gtkextendedlayout.sgml b/docs/reference/gtk/tmpl/gtkextendedlayout.sgml
new file mode 100644
index 0000000..4571504
--- /dev/null
+++ b/docs/reference/gtk/tmpl/gtkextendedlayout.sgml
@@ -0,0 +1,85 @@
+<!-- ##### SECTION Title ##### -->
+GtkExtendedLayout
+
+<!-- ##### SECTION Short_Description ##### -->
+
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### STRUCT GtkExtendedLayout ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### STRUCT GtkExtendedLayoutIface ##### -->
+<para>
+
+</para>
+
+@g_iface: 
+@get_features: 
+@get_height_for_width: 
+@get_width_for_height: 
+@get_natural_size: 
+@get_baselines: 
+
+<!-- ##### FUNCTION gtk_extended_layout_get_baselines ##### -->
+<para>
+
+</para>
+
+@layout: 
+@baselines: 
+@Returns: 
+
+
+<!-- ##### FUNCTION gtk_extended_layout_get_features ##### -->
+<para>
+
+</para>
+
+@layout: 
+@Returns: 
+
+
+<!-- ##### FUNCTION gtk_extended_layout_get_height_for_width ##### -->
+<para>
+
+</para>
+
+@layout: 
+@width: 
+@Returns: 
+
+
+<!-- ##### FUNCTION gtk_extended_layout_get_natural_size ##### -->
+<para>
+
+</para>
+
+@layout: 
+@requisition: 
+
+
+<!-- ##### FUNCTION gtk_extended_layout_get_width_for_height ##### -->
+<para>
+
+</para>
+
+@layout: 
+@height: 
+@Returns: 
+
+
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 75c60eb..d5b8e92 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -181,6 +181,7 @@ gtk_public_h_sources =          \
 	gtkenums.h		\
 	gtkeventbox.h		\
 	gtkexpander.h		\
+	gtkextendedlayout.h	\
 	gtkfilechooser.h        \
 	gtkfilechooserbutton.h  \
 	gtkfilechooserdialog.h  \
@@ -443,6 +444,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 bdfaef9..c3051bd 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 b049b29..53ea15a 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -1320,6 +1320,20 @@ 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_features
+gtk_extended_layout_get_height_for_width
+gtk_extended_layout_get_width_for_height
+gtk_extended_layout_get_natural_size
+gtk_extended_layout_get_baselines
+gtk_extended_layout_set_baseline_offset
+gtk_extended_layout_get_single_baseline
+gtk_extended_layout_get_padding
+#endif
+#endif
+
 #if IN_HEADER(__GTK_FILE_CHOOSER_H__)
 #if IN_FILE(__GTK_FILE_CHOOSER_C__)
 gtk_file_chooser_add_filter
@@ -1659,6 +1673,8 @@ gtk_handle_box_set_snap_edge
 #if IN_FILE(__GTK_HBOX_C__)
 gtk_hbox_get_type G_GNUC_CONST
 gtk_hbox_new
+gtk_hbox_get_baseline_policy
+gtk_hbox_set_baseline_policy
 #endif
 #endif
 
@@ -2059,6 +2075,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
@@ -2081,6 +2098,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
 
@@ -3530,6 +3548,7 @@ gtk_table_get_col_spacing
 gtk_table_get_default_col_spacing
 gtk_table_get_default_row_spacing
 gtk_table_get_homogeneous
+gtk_table_get_orientation
 gtk_table_get_row_spacing
 gtk_table_get_type G_GNUC_CONST
 gtk_table_new
@@ -3537,6 +3556,7 @@ gtk_table_resize
 gtk_table_set_col_spacing
 gtk_table_set_col_spacings
 gtk_table_set_homogeneous
+gtk_table_set_orientation
 gtk_table_set_row_spacing
 gtk_table_set_row_spacings
 #endif
diff --git a/gtk/gtkalignment.c b/gtk/gtkalignment.c
index 5f852d0..fafb18c 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"
@@ -50,10 +51,7 @@ enum {
 
 struct _GtkAlignmentPrivate
 {
-  guint padding_top;
-  guint padding_bottom;
-  guint padding_left;
-  guint padding_right;
+  GtkBorder padding;
 };
 
 static void gtk_alignment_size_request  (GtkWidget         *widget,
@@ -69,7 +67,11 @@ static void gtk_alignment_get_property (GObject         *object,
                                         GValue          *value,
                                         GParamSpec      *pspec);
 
-G_DEFINE_TYPE (GtkAlignment, gtk_alignment, GTK_TYPE_BIN)
+static void gtk_alignment_extended_layout_init (GtkExtendedLayoutIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GtkAlignment, gtk_alignment, GTK_TYPE_BIN,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
+						gtk_alignment_extended_layout_init))
 
 static void
 gtk_alignment_class_init (GtkAlignmentClass *class)
@@ -211,10 +213,10 @@ gtk_alignment_init (GtkAlignment *alignment)
 
   /* Initialize padding with default values: */
   priv = GTK_ALIGNMENT_GET_PRIVATE (alignment);
-  priv->padding_top = 0;
-  priv->padding_bottom = 0;
-  priv->padding_left = 0;
-  priv->padding_right = 0;
+  priv->padding.top = 0;
+  priv->padding.bottom = 0;
+  priv->padding.left = 0;
+  priv->padding.right = 0;
 }
 
 GtkWidget*
@@ -282,29 +284,29 @@ gtk_alignment_set_property (GObject         *object,
     case PROP_TOP_PADDING:
       gtk_alignment_set_padding (alignment,
 			 g_value_get_uint (value),
-			 priv->padding_bottom,
-			 priv->padding_left,
-			 priv->padding_right);
+			 priv->padding.bottom,
+			 priv->padding.left,
+			 priv->padding.right);
       break;
     case PROP_BOTTOM_PADDING:
       gtk_alignment_set_padding (alignment,
-			 priv->padding_top,
+			 priv->padding.top,
 			 g_value_get_uint (value),
-			 priv->padding_left,
-			 priv->padding_right);
+			 priv->padding.left,
+			 priv->padding.right);
       break;
     case PROP_LEFT_PADDING:
       gtk_alignment_set_padding (alignment,
-			 priv->padding_top,
-			 priv->padding_bottom,
+			 priv->padding.top,
+			 priv->padding.bottom,
 			 g_value_get_uint (value),
-			 priv->padding_right);
+			 priv->padding.right);
       break;
     case PROP_RIGHT_PADDING:
       gtk_alignment_set_padding (alignment,
-			 priv->padding_top,
-			 priv->padding_bottom,
-			 priv->padding_left,
+			 priv->padding.top,
+			 priv->padding.bottom,
+			 priv->padding.left,
 			 g_value_get_uint (value));
       break;
     
@@ -343,16 +345,16 @@ gtk_alignment_get_property (GObject         *object,
 
     /* Padding: */
     case PROP_TOP_PADDING:
-      g_value_set_uint (value, priv->padding_top);
+      g_value_set_uint (value, priv->padding.top);
       break;
     case PROP_BOTTOM_PADDING:
-      g_value_set_uint (value, priv->padding_bottom);
+      g_value_set_uint (value, priv->padding.bottom);
       break;
     case PROP_LEFT_PADDING:
-      g_value_set_uint (value, priv->padding_left);
+      g_value_set_uint (value, priv->padding.left);
       break;
     case PROP_RIGHT_PADDING:
-      g_value_set_uint (value, priv->padding_right);
+      g_value_set_uint (value, priv->padding.right);
       break;
       
     default:
@@ -433,8 +435,8 @@ gtk_alignment_size_request (GtkWidget      *widget,
       requisition->height += child_requisition.height;
 
       /* Request extra space for the padding: */
-      requisition->width += (priv->padding_left + priv->padding_right);
-      requisition->height += (priv->padding_top + priv->padding_bottom);
+      requisition->width += (priv->padding.left + priv->padding.right);
+      requisition->height += (priv->padding.top + priv->padding.bottom);
     }
 }
 
@@ -446,6 +448,7 @@ gtk_alignment_size_allocate (GtkWidget     *widget,
   GtkBin *bin;
   GtkAllocation child_allocation;
   GtkRequisition child_requisition;
+  GtkRequisition child_natural_size;
   gint width, height;
   gint border_width;
   gint padding_horizontal, padding_vertical;
@@ -465,12 +468,24 @@ gtk_alignment_size_allocate (GtkWidget     *widget,
       border_width = GTK_CONTAINER (alignment)->border_width;
 
       priv = GTK_ALIGNMENT_GET_PRIVATE (widget);
-      padding_horizontal = priv->padding_left + priv->padding_right;
-      padding_vertical = priv->padding_top + priv->padding_bottom;
+      padding_horizontal = priv->padding.left + priv->padding.right;
+      padding_vertical = priv->padding.top + priv->padding.bottom;
 
       width = allocation->width - padding_horizontal - 2 * border_width;
       height = allocation->height - padding_vertical - 2 * border_width;
     
+      if (GTK_EXTENDED_LAYOUT_HAS_NATURAL_SIZE (bin->child))
+        {
+          gtk_extended_layout_get_natural_size (GTK_EXTENDED_LAYOUT (bin->child), 
+                                                &child_natural_size);
+
+          child_requisition.width = MAX (child_requisition.width, child_natural_size.width);
+          child_requisition.width = MIN (child_requisition.width, width);
+
+          child_requisition.height = MAX (child_requisition.height, child_natural_size.height);
+          child_requisition.height = MIN (child_requisition.height, height);
+        }
+
       if (width > child_requisition.width)
 	child_allocation.width = (child_requisition.width *
 				  (1.0 - alignment->xscale) +
@@ -486,11 +501,11 @@ gtk_alignment_size_allocate (GtkWidget     *widget,
 	child_allocation.height = height;
 
       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
-	child_allocation.x = (1.0 - alignment->xalign) * (width - child_allocation.width) + allocation->x + border_width + priv->padding_right;
+	child_allocation.x = (1.0 - alignment->xalign) * (width - child_allocation.width) + allocation->x + border_width + priv->padding.right;
       else 
-	child_allocation.x = alignment->xalign * (width - child_allocation.width) + allocation->x + border_width + priv->padding_left;
+	child_allocation.x = alignment->xalign * (width - child_allocation.width) + allocation->x + border_width + priv->padding.left;
 
-      child_allocation.y = alignment->yalign * (height - child_allocation.height) + allocation->y + border_width + priv->padding_top;
+      child_allocation.y = alignment->yalign * (height - child_allocation.height) + allocation->y + border_width + priv->padding.top;
 
       gtk_widget_size_allocate (bin->child, &child_allocation);
     }
@@ -526,24 +541,24 @@ gtk_alignment_set_padding (GtkAlignment    *alignment,
 
   g_object_freeze_notify (G_OBJECT (alignment));
 
-  if (priv->padding_top != padding_top)
+  if (priv->padding.top != padding_top)
     {
-      priv->padding_top = padding_top;
+      priv->padding.top = padding_top;
       g_object_notify (G_OBJECT (alignment), "top-padding");
     }
-  if (priv->padding_bottom != padding_bottom)
+  if (priv->padding.bottom != padding_bottom)
     {
-      priv->padding_bottom = padding_bottom;
+      priv->padding.bottom = padding_bottom;
       g_object_notify (G_OBJECT (alignment), "bottom-padding");
     }
-  if (priv->padding_left != padding_left)
+  if (priv->padding.left != padding_left)
     {
-      priv->padding_left = padding_left;
+      priv->padding.left = padding_left;
       g_object_notify (G_OBJECT (alignment), "left-padding");
     }
-  if (priv->padding_right != padding_right)
+  if (priv->padding.right != padding_right)
     {
-      priv->padding_right = padding_right;
+      priv->padding.right = padding_right;
       g_object_notify (G_OBJECT (alignment), "right-padding");
     }
 
@@ -579,16 +594,50 @@ gtk_alignment_get_padding (GtkAlignment    *alignment,
   GtkAlignmentPrivate *priv;
  
   g_return_if_fail (GTK_IS_ALIGNMENT (alignment));
-
   priv = GTK_ALIGNMENT_GET_PRIVATE (alignment);
+
   if(padding_top)
-    *padding_top = priv->padding_top;
+    *padding_top = priv->padding.top;
   if(padding_bottom)
-    *padding_bottom = priv->padding_bottom;
+    *padding_bottom = priv->padding.bottom;
   if(padding_left)
-    *padding_left = priv->padding_left;
+    *padding_left = priv->padding.left;
   if(padding_right)
-    *padding_right = priv->padding_right;
+    *padding_right = priv->padding.right;
+}
+
+static GtkExtendedLayoutFeatures
+gtk_alignment_extended_layout_get_features (GtkExtendedLayout *layout)
+{
+  GtkExtendedLayoutFeatures features;
+
+  features = 
+    GTK_EXTENDED_LAYOUT_CLASS (gtk_alignment_parent_class)->
+    get_features (layout);
+
+  return features | GTK_EXTENDED_LAYOUT_PADDING;
+}
+
+static void
+gtk_alignment_extended_layout_get_padding (GtkExtendedLayout *layout,
+                                           GtkBorder         *padding)
+{
+  GtkAlignmentPrivate *priv = GTK_ALIGNMENT_GET_PRIVATE (layout);
+  gint border_width = GTK_CONTAINER (layout)->border_width;
+
+  *padding = priv->padding;
+
+  padding->top += border_width;
+  padding->left += border_width;
+  padding->right += border_width;
+  padding->bottom += border_width;
+}
+
+static void
+gtk_alignment_extended_layout_init (GtkExtendedLayoutIface *iface)
+{
+  iface->get_features = gtk_alignment_extended_layout_get_features;
+  iface->get_padding = gtk_alignment_extended_layout_get_padding;
 }
 
 #define __GTK_ALIGNMENT_C__
diff --git a/gtk/gtkbin.c b/gtk/gtkbin.c
index d6f5d5e..f833633 100644
--- a/gtk/gtkbin.c
+++ b/gtk/gtkbin.c
@@ -26,6 +26,7 @@
 
 #include <config.h>
 #include "gtkbin.h"
+#include "gtkextendedlayout.h"
 #include "gtkintl.h"
 #include "gtkalias.h"
 
@@ -34,13 +35,16 @@ static void gtk_bin_add         (GtkContainer   *container,
 static void gtk_bin_remove      (GtkContainer   *container,
 			         GtkWidget      *widget);
 static void gtk_bin_forall      (GtkContainer   *container,
-				 gboolean	include_internals,
+				 gboolean        include_internals,
 				 GtkCallback     callback,
 				 gpointer        callback_data);
 static GType gtk_bin_child_type (GtkContainer   *container);
 
+static void  gtk_bin_extended_layout_interface_init (GtkExtendedLayoutIface *iface);
 
-G_DEFINE_ABSTRACT_TYPE (GtkBin, gtk_bin, GTK_TYPE_CONTAINER)
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkBin, gtk_bin, GTK_TYPE_CONTAINER,
+                                  G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
+						         gtk_bin_extended_layout_interface_init))
 
 static void
 gtk_bin_class_init (GtkBinClass *class)
@@ -69,8 +73,8 @@ gtk_bin_child_type (GtkContainer *container)
 {
   if (!GTK_BIN (container)->child)
     return GTK_TYPE_WIDGET;
-  else
-    return G_TYPE_NONE;
+
+  return G_TYPE_NONE;
 }
 
 static void
@@ -151,5 +155,133 @@ gtk_bin_get_child (GtkBin *bin)
   return bin->child;
 }
 
+static GtkExtendedLayoutFeatures
+gtk_bin_extended_layout_get_features (GtkExtendedLayout *layout)
+{
+  GtkBin *bin = GTK_BIN (layout);
+
+  if (GTK_IS_EXTENDED_LAYOUT (bin->child))
+      return
+        gtk_extended_layout_get_features (GTK_EXTENDED_LAYOUT (bin->child)) &
+        (GTK_EXTENDED_LAYOUT_HEIGHT_FOR_WIDTH |
+         GTK_EXTENDED_LAYOUT_WIDTH_FOR_HEIGHT |
+         GTK_EXTENDED_LAYOUT_NATURAL_SIZE |
+         GTK_EXTENDED_LAYOUT_BASELINES);
+
+  return 0;
+}
+
+static gint
+gtk_bin_extended_layout_get_height_for_width (GtkExtendedLayout *layout,
+                                              gint               width)
+{
+  GtkBin *bin = GTK_BIN (layout);
+
+  g_return_val_if_fail (GTK_IS_EXTENDED_LAYOUT (bin->child), -1);
+
+  layout = GTK_EXTENDED_LAYOUT (bin->child);
+  return gtk_extended_layout_get_height_for_width (layout, width);
+}
+
+static gint
+gtk_bin_extended_layout_get_width_for_height (GtkExtendedLayout *layout,
+                                              gint               height)
+{
+  GtkBin *bin = GTK_BIN (layout);
+
+  g_return_val_if_fail (GTK_IS_EXTENDED_LAYOUT (bin->child), -1);
+
+  layout = GTK_EXTENDED_LAYOUT (bin->child);
+  return gtk_extended_layout_get_width_for_height (layout, height);
+}
+
+static void
+gtk_bin_extended_layout_get_natural_size (GtkExtendedLayout *layout,
+                                          GtkRequisition    *requisition)
+{
+  GtkBin *bin = GTK_BIN (layout);
+  GtkRequisition minimum_size;
+
+  g_return_if_fail (GTK_IS_EXTENDED_LAYOUT (bin->child));
+
+  gtk_widget_size_request (GTK_WIDGET (layout), &minimum_size);
+  gtk_extended_layout_get_natural_size (GTK_EXTENDED_LAYOUT (bin->child),
+                                        requisition);
+
+  if (GTK_EXTENDED_LAYOUT_HAS_PADDING (layout))
+    {
+      GtkBorder padding;
+
+      gtk_extended_layout_get_padding (layout, &padding);
+
+      requisition->width += padding.left + padding.right;
+      requisition->height += padding.top + padding.bottom;
+    }
+
+  requisition->width = MAX (requisition->width, minimum_size.width);
+  requisition->height = MAX (requisition->height, minimum_size.height);
+}
+
+static gint
+gtk_bin_extended_layout_get_baselines (GtkExtendedLayout  *layout,
+                                       gint              **baselines)
+{
+  GtkBin *bin = GTK_BIN (layout);
+  gint num_lines;
+
+  g_return_val_if_fail (GTK_IS_EXTENDED_LAYOUT (bin->child), -1);
+
+  num_lines = gtk_extended_layout_get_baselines (
+    GTK_EXTENDED_LAYOUT (bin->child), baselines);
+
+  if (baselines)
+    {
+      gint *baseptr = *baselines;
+      gint *baseend = baseptr + num_lines;
+
+      GtkBorder padding;
+
+      if (GTK_EXTENDED_LAYOUT_HAS_PADDING (bin))
+        gtk_extended_layout_get_padding (layout, &padding);
+      else
+        padding.top = GTK_CONTAINER (bin)->border_width;
+
+      while (baseptr < baseend)
+        *baseptr++ += padding.top;
+    }
+
+  return num_lines;
+}
+
+static void
+gtk_bin_extended_layout_set_baseline_offset (GtkExtendedLayout *layout,
+                                             gint               offset)
+{
+  GtkBin *bin = GTK_BIN (layout);
+
+  g_return_if_fail (GTK_IS_EXTENDED_LAYOUT (bin->child));
+
+  if (GTK_EXTENDED_LAYOUT_HAS_PADDING (layout))
+    {
+      GtkBorder padding;
+
+      gtk_extended_layout_get_padding (layout, &padding);
+      offset -= padding.top;
+    }
+
+  gtk_extended_layout_set_baseline_offset (GTK_EXTENDED_LAYOUT (bin->child), offset);
+}
+
+static void
+gtk_bin_extended_layout_interface_init (GtkExtendedLayoutIface *iface)
+{
+  iface->get_features = gtk_bin_extended_layout_get_features;
+  iface->get_height_for_width = gtk_bin_extended_layout_get_height_for_width;
+  iface->get_width_for_height = gtk_bin_extended_layout_get_width_for_height;
+  iface->get_natural_size = gtk_bin_extended_layout_get_natural_size;
+  iface->get_baselines = gtk_bin_extended_layout_get_baselines;
+  iface->set_baseline_offset = gtk_bin_extended_layout_set_baseline_offset;
+}
+
 #define __GTK_BIN_C__
 #include "gtkaliasdef.c"
diff --git a/gtk/gtkbutton.c b/gtk/gtkbutton.c
index 0021689..54ef7aa 100644
--- a/gtk/gtkbutton.c
+++ b/gtk/gtkbutton.c
@@ -26,8 +26,9 @@
 
 #include <config.h>
 #include <string.h>
-#include "gtkalignment.h"
 #include "gtkbutton.h"
+#include "gtkalignment.h"
+#include "gtkextendedlayout.h"
 #include "gtklabel.h"
 #include "gtkmain.h"
 #include "gtkmarshalers.h"
@@ -141,10 +142,13 @@ static void gtk_button_state_changed   (GtkWidget             *widget,
 static void gtk_button_grab_notify     (GtkWidget             *widget,
 					gboolean               was_grabbed);
 
+static void gtk_button_extended_layout_init (GtkExtendedLayoutIface *iface);
 
 static guint button_signals[LAST_SIGNAL] = { 0 };
 
-G_DEFINE_TYPE (GtkButton, gtk_button, GTK_TYPE_BIN)
+G_DEFINE_TYPE_WITH_CODE (GtkButton, gtk_button, GTK_TYPE_BIN,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
+						gtk_button_extended_layout_init))
 
 static void
 gtk_button_class_init (GtkButtonClass *klass)
@@ -1137,26 +1141,56 @@ gtk_button_size_request (GtkWidget      *widget,
 }
 
 static void
-gtk_button_size_allocate (GtkWidget     *widget,
-			  GtkAllocation *allocation)
+gtk_button_extended_layout_get_padding (GtkExtendedLayout *layout,
+                                        GtkBorder         *padding)
 {
-  GtkButton *button = GTK_BUTTON (widget);
-  GtkAllocation child_allocation;
+  GtkWidget *widget = GTK_WIDGET (layout);
 
   gint border_width = GTK_CONTAINER (widget)->border_width;
-  gint xthickness = GTK_WIDGET (widget)->style->xthickness;
-  gint ythickness = GTK_WIDGET (widget)->style->ythickness;
+  gint xthickness = widget->style->xthickness;
+  gint ythickness = widget->style->ythickness;
   GtkBorder default_border;
   GtkBorder inner_border;
   gint focus_width;
   gint focus_pad;
 
-  gtk_button_get_props (button, &default_border, NULL, &inner_border, NULL);
-  gtk_widget_style_get (GTK_WIDGET (widget),
+  gtk_button_get_props (GTK_BUTTON (layout), 
+                        &default_border, NULL,
+                        &inner_border, NULL);
+
+  gtk_widget_style_get (widget,
 			"focus-line-width", &focus_width,
 			"focus-padding", &focus_pad,
 			NULL);
- 
+
+  padding->left = border_width + inner_border.left + xthickness;
+  padding->right = border_width + inner_border.right + xthickness;
+  padding->bottom = border_width + inner_border.bottom + ythickness;
+  padding->top = border_width + inner_border.top + ythickness;
+      
+  if (GTK_WIDGET_CAN_DEFAULT (widget))
+    {
+      padding->left += default_border.left;
+      padding->right += default_border.right;
+      padding->bottom += default_border.bottom;
+      padding->top += default_border.top;
+    }
+
+  if (GTK_WIDGET_CAN_FOCUS (widget))
+    {
+      padding->left += focus_width + focus_pad;
+      padding->right += focus_width + focus_pad;
+      padding->bottom += focus_width + focus_pad;
+      padding->top += focus_width + focus_pad;
+    }
+}
+
+static void
+gtk_button_size_allocate (GtkWidget     *widget,
+			  GtkAllocation *allocation)
+{
+  GtkButton *button = GTK_BUTTON (widget);
+  gint border_width = GTK_CONTAINER (widget)->border_width;
 			    
   widget->allocation = *allocation;
 
@@ -1169,48 +1203,29 @@ gtk_button_size_allocate (GtkWidget     *widget,
 
   if (GTK_BIN (button)->child && GTK_WIDGET_VISIBLE (GTK_BIN (button)->child))
     {
-      child_allocation.x = widget->allocation.x + border_width + inner_border.left + xthickness;
-      child_allocation.y = widget->allocation.y + border_width + inner_border.top + ythickness;
-      
-      child_allocation.width = MAX (1, widget->allocation.width -
-                                    xthickness * 2 -
-                                    inner_border.left -
-                                    inner_border.right -
-				    border_width * 2);
-      child_allocation.height = MAX (1, widget->allocation.height -
-                                     ythickness * 2 -
-                                     inner_border.top -
-                                     inner_border.bottom -
-				     border_width * 2);
-
-      if (GTK_WIDGET_CAN_DEFAULT (button))
-	{
-	  child_allocation.x += default_border.left;
-	  child_allocation.y += default_border.top;
-	  child_allocation.width =  MAX (1, child_allocation.width - default_border.left - default_border.right);
-	  child_allocation.height = MAX (1, child_allocation.height - default_border.top - default_border.bottom);
-	}
+      GtkAllocation child_allocation;
+      GtkBorder padding;
 
-      if (GTK_WIDGET_CAN_FOCUS (button))
-	{
-	  child_allocation.x += focus_width + focus_pad;
-	  child_allocation.y += focus_width + focus_pad;
-	  child_allocation.width =  MAX (1, child_allocation.width - (focus_width + focus_pad) * 2);
-	  child_allocation.height = MAX (1, child_allocation.height - (focus_width + focus_pad) * 2);
-	}
+      gtk_button_extended_layout_get_padding (GTK_EXTENDED_LAYOUT (widget), &padding);
+
+      child_allocation.x = widget->allocation.x + padding.left;
+      child_allocation.y = widget->allocation.y + padding.top;
+      child_allocation.width = MAX (1, widget->allocation.width - padding.left - padding.right);
+      child_allocation.height = MAX (1, widget->allocation.height - padding.bottom - padding.top);
 
       if (button->depressed)
-	{
-	  gint child_displacement_x;
-	  gint child_displacement_y;
-	  
-	  gtk_widget_style_get (widget,
-				"child-displacement-x", &child_displacement_x, 
-				"child-displacement-y", &child_displacement_y,
-				NULL);
-	  child_allocation.x += child_displacement_x;
-	  child_allocation.y += child_displacement_y;
-	}
+        {
+          gint child_displacement_x;
+          gint child_displacement_y;
+          
+          gtk_widget_style_get (widget,
+                                "child-displacement-x", &child_displacement_x, 
+                                "child-displacement-y", &child_displacement_y,
+                                NULL);
+
+          child_allocation.x += child_displacement_x;
+          child_allocation.y += child_displacement_y;
+        }
 
       gtk_widget_size_allocate (GTK_BIN (button)->child, &child_allocation);
     }
@@ -2060,6 +2075,24 @@ gtk_button_get_image_position (GtkButton *button)
   return priv->image_position;
 }
 
+static GtkExtendedLayoutFeatures
+gtk_button_extended_layout_get_features (GtkExtendedLayout *layout)
+{
+  GtkExtendedLayoutFeatures features;
+
+  features = 
+    GTK_EXTENDED_LAYOUT_CLASS (gtk_button_parent_class)->
+    get_features (layout);
+
+  return features | GTK_EXTENDED_LAYOUT_PADDING;
+}
+
+static void
+gtk_button_extended_layout_init (GtkExtendedLayoutIface *iface)
+{
+  iface->get_features = gtk_button_extended_layout_get_features;
+  iface->get_padding = gtk_button_extended_layout_get_padding;
+}
 
 #define __GTK_BUTTON_C__
 #include "gtkaliasdef.c"  
diff --git a/gtk/gtkcellrenderertext.c b/gtk/gtkcellrenderertext.c
index e65057d..1999030 100644
--- a/gtk/gtkcellrenderertext.c
+++ b/gtk/gtkcellrenderertext.c
@@ -20,6 +20,7 @@
 #include <config.h>
 #include <stdlib.h>
 #include "gtkcellrenderertext.h"
+#include "gtkextendedlayout.h"
 #include "gtkeditable.h"
 #include "gtkentry.h"
 #include "gtkmarshalers.h"
@@ -61,6 +62,8 @@ static GtkCellEditable *gtk_cell_renderer_text_start_editing (GtkCellRenderer
 							      GdkRectangle         *cell_area,
 							      GtkCellRendererState  flags);
 
+static void       gtk_cell_renderer_text_extended_layout_init (GtkExtendedLayoutIface *iface);
+
 enum {
   EDITED,
   LAST_SIGNAL
@@ -148,9 +151,12 @@ struct _GtkCellRendererTextPrivate
   gint wrap_width;
   
   GtkWidget *entry;
+  GtkWidget *owner;
 };
 
-G_DEFINE_TYPE (GtkCellRendererText, gtk_cell_renderer_text, GTK_TYPE_CELL_RENDERER)
+G_DEFINE_TYPE_WITH_CODE (GtkCellRendererText, gtk_cell_renderer_text, GTK_TYPE_CELL_RENDERER,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
+						gtk_cell_renderer_text_extended_layout_init))
 
 static void
 gtk_cell_renderer_text_init (GtkCellRendererText *celltext)
@@ -1492,6 +1498,7 @@ get_size (GtkCellRenderer *cell,
   GtkCellRendererTextPrivate *priv;
 
   priv = GTK_CELL_RENDERER_TEXT_GET_PRIVATE (cell);
+  priv->owner = widget;
 
   if (celltext->calc_fixed_height)
     {
@@ -1923,5 +1930,37 @@ gtk_cell_renderer_text_set_fixed_height_from_font (GtkCellRendererText *renderer
     }
 }
 
+static GtkExtendedLayoutFeatures
+gtk_cell_renderer_text_extended_layout_get_features (GtkExtendedLayout *layout)
+{
+  return GTK_EXTENDED_LAYOUT_NATURAL_SIZE;
+}
+
+static void
+gtk_cell_renderer_text_extended_layout_get_natural_size (GtkExtendedLayout *layout,
+                                                         GtkRequisition    *requisition)
+{
+  GtkCellRendererTextPrivate *priv;
+  PangoEllipsizeMode ellipsize;
+
+  priv = GTK_CELL_RENDERER_TEXT_GET_PRIVATE (layout);
+
+  ellipsize = priv->ellipsize;
+  priv->ellipsize = PANGO_ELLIPSIZE_NONE;
+
+  get_size (GTK_CELL_RENDERER (layout),
+            priv->owner, NULL, NULL, NULL, NULL,
+            &requisition->width, &requisition->height);
+
+  priv->ellipsize = ellipsize;
+}
+
+static void
+gtk_cell_renderer_text_extended_layout_init (GtkExtendedLayoutIface *iface)
+{
+  iface->get_features = gtk_cell_renderer_text_extended_layout_get_features;
+  iface->get_natural_size = gtk_cell_renderer_text_extended_layout_get_natural_size;
+}
+
 #define __GTK_CELL_RENDERER_TEXT_C__
 #include "gtkaliasdef.c"
diff --git a/gtk/gtkcellview.c b/gtk/gtkcellview.c
index 65f760b..9d36555 100644
--- a/gtk/gtkcellview.c
+++ b/gtk/gtkcellview.c
@@ -21,10 +21,12 @@
 #include <string.h>
 #include "gtkcellview.h"
 #include "gtkcelllayout.h"
+#include "gtkextendedlayout.h"
 #include "gtkintl.h"
 #include "gtksignal.h"
 #include "gtkcellrenderertext.h"
 #include "gtkcellrendererpixbuf.h"
+#include "gtkextendedlayout.h"
 #include "gtkprivate.h"
 #include <gobject/gmarshal.h>
 #include "gtkbuildable.h"
@@ -36,6 +38,7 @@ struct _GtkCellViewCellInfo
   GtkCellRenderer *cell;
 
   gint requested_width;
+  gint natural_width;
   gint real_width;
   guint expand : 1;
   guint pack : 1;
@@ -121,6 +124,9 @@ static void       gtk_cell_view_buildable_custom_tag_end       (GtkBuildable
 								const gchar   	      *tagname,
 								gpointer      	      *data);
 
+/* extended layout */
+static void       gtk_cell_view_extended_layout_init           (GtkExtendedLayoutIface *iface);
+
 static GtkBuildableIface *parent_buildable_iface;
 
 #define GTK_CELL_VIEW_GET_PRIVATE(obj)    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_CELL_VIEW, GtkCellViewPrivate))
@@ -138,7 +144,9 @@ 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_EXTENDED_LAYOUT,
+						gtk_cell_view_extended_layout_init))
 
 static void
 gtk_cell_view_class_init (GtkCellViewClass *klass)
@@ -317,6 +325,7 @@ gtk_cell_view_size_request (GtkWidget      *widget,
   GList *i;
   gboolean first_cell = TRUE;
   GtkCellView *cellview;
+  GtkRequisition natural_size;
 
   cellview = GTK_CELL_VIEW (widget);
 
@@ -341,6 +350,15 @@ gtk_cell_view_size_request (GtkWidget      *widget,
                                   &width, &height);
 
       info->requested_width = width;
+
+      if (GTK_EXTENDED_LAYOUT_HAS_NATURAL_SIZE (info->cell))
+        {
+          gtk_extended_layout_get_natural_size (GTK_EXTENDED_LAYOUT (info->cell), &natural_size);
+          info->natural_width = natural_size.width;
+        }
+      else
+        info->natural_width = info->requested_width;
+
       requisition->width += width;
       requisition->height = MAX (requisition->height, height);
 
@@ -352,11 +370,13 @@ 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;
+  GList *i;
+
+  gint nexpand_cells = 0;
+  gint requested_width = 0;
+  gint natural_width = 0;
+  gint available, natural, extra;
 
   widget->allocation = *allocation;
 
@@ -371,43 +391,43 @@ gtk_cell_view_size_allocate (GtkWidget     *widget,
         continue;
 
       if (info->expand)
-        expand_cell_count++;
+        nexpand_cells++;
 
-      full_requested_width += info->requested_width;
+      requested_width += info->requested_width;
+      natural_width += info->natural_width - info->requested_width;
     }
 
-  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;
+  available = MAX (0, widget->allocation.width - requested_width);
+  natural = MIN (available, natural_width);
+  available -= natural;
+
+  if (nexpand_cells > 0)
+    extra = available / nexpand_cells;
+  else
+    extra = 0;
 
-  /* iterate list for PACK_START cells */
   for (i = cellview->priv->cell_list; i; i = i->next)
     {
       GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
 
-      if (info->pack == GTK_PACK_END)
-        continue;
-
       if (!info->cell->visible)
         continue;
 
-      info->real_width = info->requested_width + (info->expand?extra_space:0);
-    }
+      info->real_width = info->requested_width;
 
-  /* iterate list for PACK_END cells */
-  for (i = cellview->priv->cell_list; i; i = i->next)
-    {
-      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
+      if (natural_width > 0)
+          info->real_width += natural * (info->natural_width - info->requested_width) / natural_width;
 
-      if (info->pack == GTK_PACK_START)
-        continue;
-
-      if (!info->cell->visible)
-        continue;
+      if (info->expand)
+        {
+          if (1 == nexpand_cells)
+            info->real_width += available;
+          else
+            info->real_width += extra;
 
-      info->real_width = info->requested_width + (info->expand?extra_space:0);
+          nexpand_cells -= 1;
+          available -= extra;
+        }
     }
 }
 
@@ -1128,6 +1148,36 @@ gtk_cell_view_buildable_custom_tag_end (GtkBuildable *buildable,
 					    data);
 }
 
+static GtkExtendedLayoutFeatures
+gtk_cell_view_extended_layout_get_features (GtkExtendedLayout *layout)
+{
+  return GTK_EXTENDED_LAYOUT_NATURAL_SIZE;
+}
+
+static void
+gtk_cell_view_extended_layout_get_natural_size (GtkExtendedLayout *layout,
+                                                GtkRequisition    *requisition)
+{
+  GList *i;
+
+  requisition->width = 0;
+  requisition->height = GTK_WIDGET (layout)->requisition.height;
+
+  for (i = GTK_CELL_VIEW (layout)->priv->cell_list; i; i = i->next)
+    {
+      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
+
+      if (info->cell->visible)
+        requisition->width += info->natural_width;
+    }
+}
+
+static void
+gtk_cell_view_extended_layout_init (GtkExtendedLayoutIface *iface)
+{
+  iface->get_features = gtk_cell_view_extended_layout_get_features;
+  iface->get_natural_size = gtk_cell_view_extended_layout_get_natural_size;
+}
 
 #define __GTK_CELL_VIEW_C__
 #include "gtkaliasdef.c"
diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h
index 6fabacf..eeb3ac7 100644
--- a/gtk/gtkenums.h
+++ b/gtk/gtkenums.h
@@ -82,6 +82,15 @@ typedef enum
   GTK_BUTTONBOX_CENTER
 } GtkButtonBoxStyle;
 
+/* Baseline alignment policy */
+typedef enum
+{
+  GTK_BASELINE_NONE,
+  GTK_BASELINE_FIRST,
+  GTK_BASELINE_AVERAGE,
+  GTK_BASELINE_LAST
+} GtkBaselinePolicy;
+
 /* Curve types */
 typedef enum
 {
diff --git a/gtk/gtkextendedlayout.c b/gtk/gtkextendedlayout.c
new file mode 100644
index 0000000..1237ad2
--- /dev/null
+++ b/gtk/gtkextendedlayout.c
@@ -0,0 +1,284 @@
+/* gtkextendedlayout.c
+ * Copyright (C) 2007 Mathias Hasselmann <mathias.hasselmann@gmx.de>
+ *
+ * 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_features:
+ * @layout: a #GtkExtendedLayout
+ *
+ * Query which extended layout features are supported.
+ *
+ * Returns: the #GtkExtendedLayoutFeatures supported
+ *
+ * Since: 2.14
+ **/
+GtkExtendedLayoutFeatures
+gtk_extended_layout_get_features (GtkExtendedLayout *layout)
+{
+  GtkExtendedLayoutIface *iface;
+
+  g_return_val_if_fail (GTK_IS_EXTENDED_LAYOUT (layout), 0);
+
+  iface = GTK_EXTENDED_LAYOUT_GET_IFACE (layout);
+
+  g_return_val_if_fail (iface->get_features, 0);
+  return iface->get_features(layout);
+}
+
+/**
+ * gtk_extended_layout_get_height_for_width:
+ * @layout: a #GtkExtendedLayout
+ * @width: the horizontal space available
+ *
+ * Query which vertical space this extended layout item would require,
+ * given the specified amount of horizontal space is made available.
+ *
+ * Returns: the height required when #width is assigned or -1 on error
+ *
+ * Since: 2.14
+ **/
+gint
+gtk_extended_layout_get_height_for_width (GtkExtendedLayout *layout,
+                                          gint               width)
+{
+  GtkExtendedLayoutIface *iface;
+
+  g_return_val_if_fail (GTK_IS_EXTENDED_LAYOUT (layout), -1);
+
+  iface = GTK_EXTENDED_LAYOUT_GET_IFACE (layout);
+
+  g_return_val_if_fail (iface->get_height_for_width, -1);
+  return iface->get_height_for_width(layout, width);
+}
+
+/**
+ * gtk_extended_layout_get_width_for_height:
+ * @layout: a #GtkExtendedLayout
+ * @height: the vertical space available
+ *
+ * Query which horizontal space this extended layout item would require,
+ * given the specified amount of vertical space is made available.
+ *
+ * Returns: the width required when #height is assigned or -1 on error
+ *
+ * Since: 2.14
+ **/
+gint
+gtk_extended_layout_get_width_for_height (GtkExtendedLayout *layout,
+                                          gint               height)
+{
+  GtkExtendedLayoutIface *iface;
+
+  g_return_val_if_fail (GTK_IS_EXTENDED_LAYOUT (layout), -1);
+
+  iface = GTK_EXTENDED_LAYOUT_GET_IFACE (layout);
+
+  g_return_val_if_fail (iface->get_width_for_height, -1);
+  return iface->get_width_for_height(layout, height);
+}
+
+/**
+ * gtk_extended_layout_get_natural_size:
+ * @layout: a #GtkExtendedLayout
+ * @requisition: a #GtkRequisition to be filled in
+ *
+ * Query the natural (preferred) size of the layout item. 
+ * This natural size can be larger than the requested size,
+ * e.g. if the item contains optional or ellipized text.
+ *
+ * Since: 2.14
+ **/
+void
+gtk_extended_layout_get_natural_size (GtkExtendedLayout *layout,
+                                      GtkRequisition    *requisition)
+{
+  GtkWidgetAuxInfo *aux_info = NULL;
+  GtkExtendedLayoutIface *iface;
+
+  g_return_if_fail (GTK_IS_EXTENDED_LAYOUT (layout));
+  g_return_if_fail (NULL != requisition);
+
+  iface = GTK_EXTENDED_LAYOUT_GET_IFACE (layout);
+
+  g_return_if_fail (iface->get_natural_size);
+  iface->get_natural_size(layout, requisition);
+
+/* XXX The following hack is needed to ensure natural_size >= requested_size
+ *     even if GtkWidget and GtkSizeGroup tweak a widget's size requisition. */
+/* FIXME Should I just use gtk_widget_size_request? This would be more reliable
+ *       but also would hide bugs in natural_size implementations. */
+
+  if (GTK_IS_WIDGET (layout))
+    aux_info = _gtk_widget_get_aux_info (GTK_WIDGET (layout), FALSE);
+
+  if (aux_info)
+    {
+      if (aux_info->width > 0)
+	requisition->width = MAX (requisition->width, aux_info->width);
+      if (aux_info && aux_info->height > 0)
+	requisition->height = MAX (requisition->height, aux_info->height);
+    }
+}
+
+/**
+ * gtk_extended_layout_get_baselines:
+ * @layout: a #GtkExtendedLayout
+ * @baselines: an array of baselines to be filled in
+ *
+ * Query the baselines of the layout item. Baselines are imaginary 
+ * horizontal lines used to vertically align text and images.
+ *
+ * Returns: the number of entries in @baselines or -1 on error
+ *
+ * Since: 2.14
+ **/
+gint
+gtk_extended_layout_get_baselines (GtkExtendedLayout  *layout,
+                                   gint              **baselines)
+{
+  GtkExtendedLayoutIface *iface;
+
+  g_return_val_if_fail (GTK_IS_EXTENDED_LAYOUT (layout), -1);
+  g_return_val_if_fail (NULL != baselines, -1);
+
+  iface = GTK_EXTENDED_LAYOUT_GET_IFACE (layout);
+
+  g_return_val_if_fail (iface->get_baselines, -1);
+  return iface->get_baselines(layout, baselines);
+}
+
+/**
+ * gtk_extended_layout_set_baseline_offset:
+ * @layout: a #GtkExtendedLayout
+ * @offset: the offset to consider
+ *
+ * Informs the layout item about the offset it should apply to its baselines,
+ * to properly align them with all other baseline aware children of its parent.
+ *
+ * Since: 2.14
+ **/
+void
+gtk_extended_layout_set_baseline_offset (GtkExtendedLayout *layout,
+                                         gint               offset)
+{
+  GtkExtendedLayoutIface *iface;
+
+  g_return_if_fail (GTK_IS_EXTENDED_LAYOUT (layout));
+
+  iface = GTK_EXTENDED_LAYOUT_GET_IFACE (layout);
+
+  g_return_if_fail (iface->set_baseline_offset);
+  iface->set_baseline_offset (layout, offset);
+}
+
+/**
+ * gtk_extended_layout_get_single_baseline:
+ * @layout: a #GtkExtendedLayout
+ * @policy: specifies the policy for choosing a baseline
+ *
+ * Queries a single baseline of the layout item.
+ *
+ * Since: 2.14
+ **/
+gint
+gtk_extended_layout_get_single_baseline (GtkExtendedLayout *layout,
+                                         GtkBaselinePolicy  policy)
+{
+  gint *baselines = NULL;
+  gint offset = -1;
+  gint count, i;
+
+  g_return_val_if_fail (GTK_BASELINE_NONE != policy, -1);
+
+  count = gtk_extended_layout_get_baselines (layout, &baselines);
+
+  if (count > 0)
+    {
+      switch (policy)
+        {
+          case GTK_BASELINE_NONE:
+            break;
+
+          case GTK_BASELINE_FIRST:
+            offset = baselines [0];
+            break;
+
+          case GTK_BASELINE_LAST:
+            offset = baselines [count - 1];
+            break;
+
+          case GTK_BASELINE_AVERAGE:
+            for (i = 0, offset = 0; i < count; ++i)
+              offset += baselines[i];
+
+            offset = (offset + count - 1) / count;
+            break;
+        }
+
+      g_free (baselines);
+    }
+
+  return offset;
+}
+
+/**
+ * gtk_extended_layout_get_padding:
+ * @layout: a #GtkExtendedLayout
+ * @padding: an #GtkBorder to be filled with the padding
+ *
+ * Query the padding this layout item puts arround its content.
+ *
+ * Since: 2.14
+ **/
+void
+gtk_extended_layout_get_padding (GtkExtendedLayout *layout,
+                                 GtkBorder         *padding)
+{
+  GtkExtendedLayoutIface *iface;
+
+  g_return_if_fail (GTK_IS_EXTENDED_LAYOUT (layout));
+  g_return_if_fail (NULL != padding);
+
+  iface = GTK_EXTENDED_LAYOUT_GET_IFACE (layout);
+
+  g_return_if_fail (iface->get_padding);
+  iface->get_padding(layout, padding);
+}
+
+#define __GTK_EXTENDED_LAYOUT_C__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkextendedlayout.h b/gtk/gtkextendedlayout.h
new file mode 100644
index 0000000..82e84e7
--- /dev/null
+++ b/gtk/gtkextendedlayout.h
@@ -0,0 +1,103 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2007 Mathias Hasselmann <mathias.hasselmann@gmx.de>
+ *
+ * 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))
+
+#define GTK_EXTENDED_LAYOUT_HAS_WIDTH_FOR_HEIGHT(obj) \
+  (GTK_IS_EXTENDED_LAYOUT((obj)) && (GTK_EXTENDED_LAYOUT_WIDTH_FOR_HEIGHT & \
+   gtk_extended_layout_get_features ((GtkExtendedLayout*)(obj))))
+#define GTK_EXTENDED_LAYOUT_HAS_HEIGHT_FOR_WIDTH(obj) \
+  (GTK_IS_EXTENDED_LAYOUT((obj)) && (GTK_EXTENDED_LAYOUT_HEIGHT_FOR_WIDTH & \
+   gtk_extended_layout_get_features ((GtkExtendedLayout*)(obj))))
+#define GTK_EXTENDED_LAYOUT_HAS_NATURAL_SIZE(obj) \
+  (GTK_IS_EXTENDED_LAYOUT((obj)) && (GTK_EXTENDED_LAYOUT_NATURAL_SIZE & \
+   gtk_extended_layout_get_features ((GtkExtendedLayout*)(obj))))
+#define GTK_EXTENDED_LAYOUT_HAS_BASELINES(obj) \
+  (GTK_IS_EXTENDED_LAYOUT((obj)) && (GTK_EXTENDED_LAYOUT_BASELINES & \
+   gtk_extended_layout_get_features ((GtkExtendedLayout*)(obj))))
+#define GTK_EXTENDED_LAYOUT_HAS_PADDING(obj) \
+  (GTK_IS_EXTENDED_LAYOUT((obj)) && (GTK_EXTENDED_LAYOUT_PADDING & \
+   gtk_extended_layout_get_features ((GtkExtendedLayout*)(obj))))
+
+typedef struct _GtkExtendedLayout           GtkExtendedLayout;
+typedef struct _GtkExtendedLayoutIface      GtkExtendedLayoutIface;
+
+/*< flags >*/
+typedef enum {
+  GTK_EXTENDED_LAYOUT_NONE             = 0,
+  GTK_EXTENDED_LAYOUT_WIDTH_FOR_HEIGHT = 1 << 0, 
+  GTK_EXTENDED_LAYOUT_HEIGHT_FOR_WIDTH = 1 << 1,
+  GTK_EXTENDED_LAYOUT_NATURAL_SIZE     = 1 << 2,
+  GTK_EXTENDED_LAYOUT_BASELINES        = 1 << 3,
+  GTK_EXTENDED_LAYOUT_PADDING          = 1 << 4
+} GtkExtendedLayoutFeatures;
+
+struct _GtkExtendedLayoutIface
+{
+  GTypeInterface g_iface;
+
+  /* virtual table */
+
+  GtkExtendedLayoutFeatures (*get_features)         (GtkExtendedLayout  *layout);
+  gint                      (*get_height_for_width) (GtkExtendedLayout  *layout,
+                                                     gint                width);
+  gint                      (*get_width_for_height) (GtkExtendedLayout  *layout,
+                                                     gint                height);
+  void                      (*get_natural_size)     (GtkExtendedLayout  *layout,
+                                                     GtkRequisition     *requisition);
+  gint                      (*get_baselines)        (GtkExtendedLayout  *layout,
+                                                     gint              **baselines);
+  void                      (*set_baseline_offset)  (GtkExtendedLayout  *layout,
+                                                     gint                offset);
+  void                      (*get_padding)          (GtkExtendedLayout  *layout,
+                                                     GtkBorder          *padding);
+};
+
+
+GType                     gtk_extended_layout_get_type             (void) G_GNUC_CONST;
+GtkExtendedLayoutFeatures gtk_extended_layout_get_features         (GtkExtendedLayout  *layout);
+gint                      gtk_extended_layout_get_height_for_width (GtkExtendedLayout  *layout,
+                                                                    gint                width);
+gint                      gtk_extended_layout_get_width_for_height (GtkExtendedLayout  *layout,
+                                                                    gint                height);
+void                      gtk_extended_layout_get_natural_size     (GtkExtendedLayout  *layout,
+                                                                    GtkRequisition     *requisition);
+gint                      gtk_extended_layout_get_baselines        (GtkExtendedLayout  *layout,
+                                                                    gint              **baselines);
+void                      gtk_extended_layout_set_baseline_offset  (GtkExtendedLayout  *layout,
+                                                                    gint                offset);
+gint                      gtk_extended_layout_get_single_baseline  (GtkExtendedLayout  *layout,
+                                                                    GtkBaselinePolicy   policy);
+void                      gtk_extended_layout_get_padding          (GtkExtendedLayout  *layout,
+                                                                    GtkBorder          *padding);
+
+G_END_DECLS
+
+#endif /* __GTK_EXTENDED_LAYOUT_H__ */
diff --git a/gtk/gtkframe.c b/gtk/gtkframe.c
index e118823..4325282 100644
--- a/gtk/gtkframe.c
+++ b/gtk/gtkframe.c
@@ -27,6 +27,7 @@
 #include <config.h>
 #include <string.h>
 #include "gtkframe.h"
+#include "gtkextendedlayout.h"
 #include "gtklabel.h"
 #include "gtkprivate.h"
 #include "gtkintl.h"
@@ -81,9 +82,13 @@ static void gtk_frame_buildable_add_child           (GtkBuildable *buildable,
 						     GObject      *child,
 						     const gchar  *type);
 
+static void gtk_frame_extended_layout_init (GtkExtendedLayoutIface *iface);
+
 G_DEFINE_TYPE_WITH_CODE (GtkFrame, gtk_frame, GTK_TYPE_BIN,
 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
-						gtk_frame_buildable_init))
+						gtk_frame_buildable_init);
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
+						gtk_frame_extended_layout_init))
 
 static void
 gtk_frame_class_init (GtkFrameClass *class)
@@ -696,33 +701,62 @@ gtk_frame_compute_child_allocation (GtkFrame      *frame,
 }
 
 static void
-gtk_frame_real_compute_child_allocation (GtkFrame      *frame,
-					 GtkAllocation *child_allocation)
+gtk_frame_extended_layout_get_padding (GtkExtendedLayout *layout,
+                                       GtkBorder         *padding)
 {
-  GtkWidget *widget = GTK_WIDGET (frame);
-  GtkAllocation *allocation = &widget->allocation;
-  GtkRequisition child_requisition;
+  GtkFrame *frame = GTK_FRAME (layout);
+  GtkWidget *widget = GTK_WIDGET (layout);
+  gint border_width = GTK_CONTAINER (layout)->border_width;
+
   gint top_margin;
 
   if (frame->label_widget)
     {
+      GtkRequisition child_requisition;
       gtk_widget_get_child_requisition (frame->label_widget, &child_requisition);
       top_margin = MAX (child_requisition.height, widget->style->ythickness);
     }
   else
     top_margin = widget->style->ythickness;
+
+  padding->left = padding->right = border_width + widget->style->xthickness;
+  padding->bottom = border_width + widget->style->ythickness;
+  padding->top = border_width + top_margin;
+}
+
+static void
+gtk_frame_real_compute_child_allocation (GtkFrame      *frame,
+					 GtkAllocation *child_allocation)
+{
+  GtkAllocation *allocation = &GTK_WIDGET (frame)->allocation;
+  GtkBorder padding;
+
+  gtk_frame_extended_layout_get_padding ((GtkExtendedLayout*)frame, &padding);
   
-  child_allocation->x = (GTK_CONTAINER (frame)->border_width +
-			 widget->style->xthickness);
-  child_allocation->width = MAX(1, (gint)allocation->width - child_allocation->x * 2);
-  
-  child_allocation->y = (GTK_CONTAINER (frame)->border_width + top_margin);
-  child_allocation->height = MAX (1, ((gint)allocation->height - child_allocation->y -
-				      (gint)GTK_CONTAINER (frame)->border_width -
-				      (gint)widget->style->ythickness));
-  
-  child_allocation->x += allocation->x;
-  child_allocation->y += allocation->y;
+  child_allocation->x = padding.left + allocation->x;
+  child_allocation->y = padding.top + allocation->y;
+
+  child_allocation->width = MAX(1, (gint)allocation->width - padding.left - padding.right);
+  child_allocation->height = MAX (1, (gint)allocation->height - padding.top - padding.bottom);
+}
+
+static GtkExtendedLayoutFeatures
+gtk_frame_extended_layout_get_features (GtkExtendedLayout *layout)
+{
+  GtkExtendedLayoutFeatures features;
+
+  features = 
+    GTK_EXTENDED_LAYOUT_CLASS (gtk_frame_parent_class)->
+    get_features (layout);
+
+  return features | GTK_EXTENDED_LAYOUT_PADDING;
+}
+
+static void
+gtk_frame_extended_layout_init (GtkExtendedLayoutIface *iface)
+{
+  iface->get_features = gtk_frame_extended_layout_get_features;
+  iface->get_padding = gtk_frame_extended_layout_get_padding;
 }
 
 #define __GTK_FRAME_C__
diff --git a/gtk/gtkhbox.c b/gtk/gtkhbox.c
index 3140cac..e0a905a 100644
--- a/gtk/gtkhbox.c
+++ b/gtk/gtkhbox.c
@@ -26,27 +26,68 @@
 
 #include <config.h>
 #include "gtkhbox.h"
+#include "gtkextendedlayout.h"
 #include "gtkintl.h"
 #include "gtkalias.h"
+#include "gtkprivate.h"
 
 
+enum {
+  PROP_0,
+  PROP_BASELINE_POLICY
+};
+
+#define GTK_HBOX_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_HBOX, GtkHBoxPrivate))
+
+typedef struct _GtkHBoxPrivate GtkHBoxPrivate;
+
+struct _GtkHBoxPrivate
+{
+  GtkBaselinePolicy baseline_policy;
+  gint baseline;
+};
+
+static void gtk_hbox_set_property  (GObject        *object,
+		                    guint           prop_id,
+		                    const GValue   *value,
+		                    GParamSpec     *pspec);
+static void gtk_hbox_get_property  (GObject        *object,
+		                    guint           prop_id,
+		                    GValue         *value,
+		                    GParamSpec     *pspec);
+
 static void gtk_hbox_size_request  (GtkWidget      *widget,
 				    GtkRequisition *requisition);
 static void gtk_hbox_size_allocate (GtkWidget      *widget,
 				    GtkAllocation  *allocation);
 
+static void gtk_hbox_extended_layout_interface_init (GtkExtendedLayoutIface *iface);
 
-G_DEFINE_TYPE (GtkHBox, gtk_hbox, GTK_TYPE_BOX)
+G_DEFINE_TYPE_WITH_CODE (GtkHBox, gtk_hbox, GTK_TYPE_BOX,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
+						gtk_hbox_extended_layout_interface_init))
 
 static void
 gtk_hbox_class_init (GtkHBoxClass *class)
 {
-  GtkWidgetClass *widget_class;
+  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
 
-  widget_class = (GtkWidgetClass*) class;
+  gobject_class->set_property = gtk_hbox_set_property;
+  gobject_class->get_property = gtk_hbox_get_property;
 
   widget_class->size_request = gtk_hbox_size_request;
   widget_class->size_allocate = gtk_hbox_size_allocate;
+
+  g_type_class_add_private (gobject_class, sizeof (GtkHBoxPrivate));
+  
+  g_object_class_install_property (gobject_class,
+				   PROP_BASELINE_POLICY,
+				   g_param_spec_enum ("baseline-policy", 
+						      P_("Baseline policy"), 
+						      P_("Indicates which baseline of children to use for vertical alignment"),
+						      GTK_TYPE_BASELINE_POLICY, GTK_BASELINE_NONE,
+						      G_PARAM_READWRITE));
 }
 
 static void
@@ -69,21 +110,81 @@ gtk_hbox_new (gboolean homogeneous,
 }
 
 
+static void 
+gtk_hbox_set_property (GObject      *object,
+		       guint         prop_id,
+		       const GValue *value,
+		       GParamSpec   *pspec)
+{
+  GtkHBoxPrivate *priv = GTK_HBOX_GET_PRIVATE (object);
+
+  switch (prop_id)
+    {
+    case PROP_BASELINE_POLICY:
+      priv->baseline_policy = g_value_get_enum (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void 
+gtk_hbox_get_property (GObject    *object,
+		       guint       prop_id,
+		       GValue     *value,
+		       GParamSpec *pspec)
+{
+  GtkHBoxPrivate *priv = GTK_HBOX_GET_PRIVATE (object);
+
+  switch (prop_id)
+    {
+    case PROP_BASELINE_POLICY:
+      g_value_set_enum (value, priv->baseline_policy);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
 static void
-gtk_hbox_size_request (GtkWidget      *widget,
-		       GtkRequisition *requisition)
+gtk_hbox_natural_size_request (GtkWidget      *child,
+                               GtkRequisition *requisition,
+                               GtkWidget      *widget)
 {
-  GtkBox *box;
+  GtkExtendedLayoutFeatures features = 0;
+
+  if (GTK_IS_EXTENDED_LAYOUT (child))
+    features = gtk_extended_layout_get_features ((GtkExtendedLayout*) child);
+
+  if (features & GTK_EXTENDED_LAYOUT_WIDTH_FOR_HEIGHT)
+    {
+      requisition->width = gtk_extended_layout_get_width_for_height (
+        (GtkExtendedLayout*) child, widget->allocation.height);
+      requisition->height = widget->allocation.height;
+    }
+  else if (features & GTK_EXTENDED_LAYOUT_NATURAL_SIZE)
+    gtk_extended_layout_get_natural_size ((GtkExtendedLayout*) child, requisition);
+  else 
+    gtk_widget_size_request (child, requisition);
+}
+
+static void
+gtk_hbox_real_size_request (GtkWidget      *widget,
+		            GtkRequisition *requisition,
+                            GtkChildSizeRequest  size_request_func)
+{
+  GtkBox *box = GTK_BOX (widget);
+
+  gint nvis_children;
   GtkBoxChild *child;
   GList *children;
-  gint nvis_children;
-  gint width;
 
-  box = GTK_BOX (widget);
   requisition->width = 0;
   requisition->height = 0;
-  nvis_children = 0;
 
+  nvis_children = 0;
   children = box->children;
   while (children)
     {
@@ -91,31 +192,82 @@ gtk_hbox_size_request (GtkWidget      *widget,
       children = children->next;
 
       if (GTK_WIDGET_VISIBLE (child->widget))
-	{
-	  GtkRequisition child_requisition;
+        nvis_children += 1;
+    }
 
-	  gtk_widget_size_request (child->widget, &child_requisition);
+  if (nvis_children > 0)
+    {
+      GtkHBoxPrivate *priv = GTK_HBOX_GET_PRIVATE (widget);
+      gint *baselines = NULL;
+      gint i;
+
+      if (priv->baseline_policy != GTK_BASELINE_NONE)
+        {
+          baselines = g_newa (gint, nvis_children);
+          priv->baseline = 0;
+
+          i = 0;
+          children = box->children;
+          while (children)
+            {
+              child = children->data;
+              children = children->next;
+
+              if (GTK_WIDGET_VISIBLE (child->widget))
+                {
+                  if (GTK_EXTENDED_LAYOUT_HAS_BASELINES (child->widget))
+                    {
+                      GtkExtendedLayout *layout = GTK_EXTENDED_LAYOUT (child->widget);
 
-	  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;
-	    }
+                      gtk_extended_layout_set_baseline_offset (layout, 0);
+                      baselines[i] = gtk_extended_layout_get_single_baseline (layout, priv->baseline_policy);
+                      priv->baseline = MAX (priv->baseline, baselines[i]);
+                    }
+                  else
+                    baselines[i] = 0;
 
-	  requisition->height = MAX (requisition->height, child_requisition.height);
+                  ++i;
+                }
+            }
+        }
 
-	  nvis_children += 1;
-	}
-    }
+      i = 0;
+      children = box->children;
+      while (children)
+        {
+          child = children->data;
+          children = children->next;
+
+          if (GTK_WIDGET_VISIBLE (child->widget))
+            {
+              GtkRequisition child_requisition;
+              size_request_func (child->widget, &child_requisition, widget);
+
+              if (box->homogeneous)
+                {
+                  gint width = child_requisition.width + child->padding * 2;
+                  requisition->width = MAX (requisition->width, width);
+                }
+              else
+                {
+                  requisition->width += child_requisition.width + child->padding * 2;
+                }
+
+              if (baselines)
+                {
+                  gint padding = MAX (priv->baseline - baselines[i], 0);
+                  child_requisition.height += padding;
+                }
+
+              requisition->height = MAX (requisition->height, child_requisition.height);
+
+              ++i;
+            }
+        }
 
-  if (nvis_children > 0)
-    {
       if (box->homogeneous)
 	requisition->width *= nvis_children;
+
       requisition->width += (nvis_children - 1) * box->spacing;
     }
 
@@ -124,26 +276,26 @@ gtk_hbox_size_request (GtkWidget      *widget,
 }
 
 static void
+gtk_hbox_size_request (GtkWidget      *widget,
+		       GtkRequisition *requisition)
+{
+  gtk_hbox_real_size_request (widget, requisition, (GtkChildSizeRequest)gtk_widget_size_request);
+}
+
+static void
 gtk_hbox_size_allocate (GtkWidget     *widget,
 			GtkAllocation *allocation)
 {
   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;
+  GtkBoxChild *child;
+  GList *children;
 
   box = GTK_BOX (widget);
   widget->allocation = *allocation;
 
-  direction = gtk_widget_get_direction (widget);
-  
   nvis_children = 0;
   nexpand_children = 0;
   children = box->children;
@@ -163,148 +315,313 @@ gtk_hbox_size_allocate (GtkWidget     *widget,
 
   if (nvis_children > 0)
     {
-      if (box->homogeneous)
-	{
-	  width = (allocation->width -
-		   GTK_CONTAINER (box)->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;
-	}
+      GtkHBoxPrivate *priv = GTK_HBOX_GET_PRIVATE (widget);
 
-      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);
+      GtkPackType packing;
+      GtkTextDirection direction;
+      gint border_width;
 
-      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;
-	    }
-	}
+      GtkAllocation child_allocation;
+      gint *natural_requisitions;
+      gint *minimum_requisitions;
+
+      gint available, natural, extra;
+      gint minimum_width, natural_width;
+      gint i;
+
+      direction = gtk_widget_get_direction (widget);
+      border_width = GTK_CONTAINER (box)->border_width;
+
+      minimum_width = widget->requisition.width;
+      natural_width = 0;
 
-      x = allocation->x + allocation->width - GTK_CONTAINER (box)->border_width;
+      natural_requisitions = g_newa (gint, nvis_children);
+      minimum_requisitions = g_newa (gint, nvis_children);
 
+      i = 0;
       children = box->children;
+
       while (children)
-	{
-	  child = children->data;
-	  children = children->next;
+        {
+          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);
+          if (GTK_WIDGET_VISIBLE (child->widget))
+            {
+              GtkRequisition child_requisition;
 
-              if (box->homogeneous)
-                {
-                  if (nvis_children == 1)
-                    child_width = width;
-                  else
-                    child_width = extra;
+              gtk_widget_size_request (child->widget, &child_requisition);
+              minimum_requisitions[i] = child_requisition.width;
+              natural_requisitions[i] = 0;
 
-                  nvis_children -= 1;
-                  width -= extra;
-                }
-              else
+              if (GTK_IS_EXTENDED_LAYOUT (child->widget))
                 {
-		  child_width = child_requisition.width + child->padding * 2;
+                  GtkExtendedLayout *layout = (GtkExtendedLayout*)child->widget;
+                  GtkExtendedLayoutFeatures features = gtk_extended_layout_get_features (layout);
 
-                  if (child->expand)
+                  if (features & GTK_EXTENDED_LAYOUT_WIDTH_FOR_HEIGHT)
                     {
-                      if (nexpand_children == 1)
-                        child_width += width;
-                      else
-                        child_width += extra;
+                      gint width = gtk_extended_layout_get_width_for_height (layout, allocation->height);
+                      gint dx = minimum_requisitions[i] - width;
 
-                      nexpand_children -= 1;
-                      width -= extra;
+                      if (dx > 0)
+                {
+                          minimum_requisitions[i] = width;
+                          minimum_width -= dx;
+                        }
+                    }
+                  else if (features & GTK_EXTENDED_LAYOUT_NATURAL_SIZE)
+                    {
+                      gtk_extended_layout_get_natural_size (layout, &child_requisition);
+                      natural_requisitions[i] = child_requisition.width - minimum_requisitions[i];
                     }
                 }
 
-              if (child->fill)
+              natural_width += natural_requisitions[i++];
+            }
+        }
+
+      if (box->homogeneous)
+	{
+	  available = (allocation->width - border_width * 2 -
+		      (nvis_children - 1) * box->spacing);
+	  extra = available / nvis_children;
+          natural = 0;
+	}
+      else
+	{
+          if (nexpand_children > 0 || natural_width > 0)
+            {
+              available = (gint)allocation->width - minimum_width;
+              natural = MAX (0, MIN (available, natural_width));
+              available -= natural;
+            }
+          else
+            {
+              available = 0;
+              natural = 0;
+            }
+
+          if (nexpand_children > 0)
+	    extra = MAX (0, available / nexpand_children);
+          else
+            extra = 0;
+	}
+
+      child_allocation.y = allocation->y + border_width;
+      child_allocation.height = MAX (1, (gint) allocation->height - (gint) border_width * 2);
+
+      for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
+        {
+          gint x;
+
+          if (GTK_PACK_START == packing)
+            x = allocation->x + border_width;
+          else
+            x = allocation->x + allocation->width - border_width;
+
+          i = 0;
+          children = box->children;
+          while (children)
+            {
+              child = children->data;
+              children = children->next;
+
+              if (GTK_WIDGET_VISIBLE (child->widget))
                 {
-                  child_allocation.width = MAX (1, (gint)child_width - (gint)child->padding * 2);
-                  child_allocation.x = x + child->padding - child_width;
-                }
-              else
+                  if (child->pack == packing)
+                    {
+                      gint child_width;
+
+                      if (box->homogeneous)
+                        {
+                          if (nvis_children == 1)
+                            child_width = available;
+                          else
+                            child_width = extra;
+
+                          nvis_children -= 1;
+                          available -= extra;
+                        }
+                      else
+                        {
+                          child_width = minimum_requisitions[i] + child->padding * 2;
+
+                          if (child->expand)
+                            {
+                              if (nexpand_children == 1)
+                                child_width += available;
+                              else
+                                child_width += extra;
+
+                              nexpand_children -= 1;
+                              available -= extra;
+                            }
+                        }
+
+		      if (natural_width > 0)
+                        child_width += natural * natural_requisitions[i] / natural_width;
+
+                      if (child->fill)
+                        {
+                          child_allocation.width = MAX (1, child_width - (gint)child->padding * 2);
+                          child_allocation.x = x + child->padding;
+                        }
+                      else
+                        {
+                          child_allocation.width = minimum_requisitions[i];
+                          child_allocation.x = x + (child_width - child_allocation.width) / 2;
+                        }
+
+                      if (GTK_PACK_END == packing)
+                        child_allocation.x -= child_width;
+
+                      if (GTK_TEXT_DIR_RTL == direction)
+                        child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
+
+                      gtk_widget_size_allocate (child->widget, &child_allocation);
+
+                      if (GTK_PACK_START == packing)
+                        x += child_width + box->spacing;
+                      else
+                        x -= child_width + box->spacing;
+                    } /* packing */
+
+                  ++i;
+                } /* visible */
+            } /* while children */
+        } /* for packing */
+
+      if (GTK_BASELINE_NONE != priv->baseline_policy)
+        {
+          i = 0;
+          children = box->children;
+          while (children)
+            {
+              child = children->data;
+              children = children->next;
+
+              if (GTK_WIDGET_VISIBLE (child->widget))
                 {
-		  child_allocation.width = child_requisition.width;
-                  child_allocation.x = x + (child_width - child_allocation.width) / 2 - child_width;
+                  if (GTK_EXTENDED_LAYOUT_HAS_BASELINES (child->widget))
+                    {
+                      GtkExtendedLayout *layout = GTK_EXTENDED_LAYOUT (child->widget);
+                      gint baseline, dy;
+
+                      gtk_extended_layout_set_baseline_offset (layout, 0);
+                      baseline = gtk_extended_layout_get_single_baseline (layout, priv->baseline_policy);
+                      dy = priv->baseline - baseline;
+
+                      gtk_extended_layout_set_baseline_offset (layout, dy);
+                    }
+
+                  ++i;
                 }
+            }
+        } /* baseline_policy */
+    } /* nvis_children */
+}
 
-	      if (direction == GTK_TEXT_DIR_RTL)
-		child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
+/**
+ * gtk_hbox_get_baseline_policy:
+ * @hbox: a #GtkHBox
+ *
+ * Returns which baseline of children is used 
+ * to vertically align them.
+ *
+ * Return value: the baseline alignment policy of the @hbox.
+ **/
+GtkBaselinePolicy
+gtk_hbox_get_baseline_policy (GtkHBox *hbox)
+{
+  g_return_val_if_fail (GTK_IS_HBOX (hbox), GTK_BASELINE_NONE);
+  return GTK_HBOX_GET_PRIVATE (hbox)->baseline_policy;
+}
 
-              gtk_widget_size_allocate (child->widget, &child_allocation);
+/**
+ * gtk_hbox_set_baseline_policy:
+ * @box: a #GtkBox
+ * @policy: the baseline alignment policy
+ *
+ * Sets the #GtkHBox:baseline-policy property of @vbox, 
+ * which is the policy to vertically align children.
+ */
+void
+gtk_hbox_set_baseline_policy (GtkHBox           *hbox,
+                              GtkBaselinePolicy  policy)
+{
+  g_return_if_fail (GTK_IS_HBOX (hbox));
+  g_object_set (hbox, "baseline-policy", policy, NULL);
+}
 
-              x -= (child_width + box->spacing);
-	    }
-	}
+static GtkExtendedLayoutFeatures
+gtk_hbox_extended_layout_get_features (GtkExtendedLayout *layout)
+{
+  GtkExtendedLayoutFeatures features;
+  GtkHBoxPrivate *priv;
+
+  features = GTK_EXTENDED_LAYOUT_NATURAL_SIZE;
+  priv = GTK_HBOX_GET_PRIVATE (layout);
+
+  if (GTK_BASELINE_NONE != priv->baseline_policy)
+    features |= GTK_EXTENDED_LAYOUT_BASELINES;
+  
+  return features;
+}
+
+static void
+gtk_hbox_extended_layout_get_natural_size (GtkExtendedLayout *layout,
+                                           GtkRequisition    *requisition)
+{
+  gtk_hbox_real_size_request (GTK_WIDGET (layout), requisition, gtk_hbox_natural_size_request);
+}
+
+static gint
+gtk_hbox_extended_layout_get_baselines (GtkExtendedLayout  *layout,
+                                        gint              **baselines)
+{
+  GtkHBoxPrivate *priv;
+  GtkBox *box;
+
+  gint hbox_baseline = 0;
+  gint child_baseline;
+
+  GtkBoxChild *child;
+  GList *children;
+
+  priv = GTK_HBOX_GET_PRIVATE (layout);
+
+  g_return_val_if_fail (GTK_BASELINE_NONE != priv->baseline_policy, -1);
+
+  box = GTK_BOX (layout);
+  children = box->children;
+
+  while (children)
+    {
+      child = children->data;
+      children = children->next;
+
+      if (GTK_WIDGET_VISIBLE (child->widget) &&
+          GTK_EXTENDED_LAYOUT_HAS_BASELINES (child->widget))
+        {
+          child_baseline = gtk_extended_layout_get_single_baseline (
+            GTK_EXTENDED_LAYOUT (child->widget), priv->baseline_policy);
+          hbox_baseline = MAX (hbox_baseline, child_baseline);
+        }
     }
+
+  *baselines = g_new (gint, 1);
+  *baselines[0] = hbox_baseline;
+
+  return 1;
+}
+
+static void
+gtk_hbox_extended_layout_interface_init (GtkExtendedLayoutIface *iface)
+{
+  iface->get_features = gtk_hbox_extended_layout_get_features;
+  iface->get_natural_size = gtk_hbox_extended_layout_get_natural_size;
+  iface->get_baselines = gtk_hbox_extended_layout_get_baselines;
 }
 
 #define __GTK_HBOX_C__
diff --git a/gtk/gtkhbox.h b/gtk/gtkhbox.h
index 5296e21..ed546a0 100644
--- a/gtk/gtkhbox.h
+++ b/gtk/gtkhbox.h
@@ -56,10 +56,13 @@ struct _GtkHBoxClass
 };
 
 
-GType	   gtk_hbox_get_type (void) G_GNUC_CONST;
-GtkWidget* gtk_hbox_new	     (gboolean homogeneous,
-			      gint spacing);
+GType	          gtk_hbox_get_type            (void) G_GNUC_CONST;
+GtkWidget*        gtk_hbox_new	               (gboolean           homogeneous,
+                                                gint               spacing);
 
+GtkBaselinePolicy gtk_hbox_get_baseline_policy (GtkHBox           *hbox);
+void              gtk_hbox_set_baseline_policy (GtkHBox           *hbox,
+                                                GtkBaselinePolicy  policy);
 
 G_END_DECLS
 
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index 09fcb85..50ab66b 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -25,6 +25,7 @@
 
 #include <config.h>
 #include <math.h>
+#include <stdlib.h>
 #include <string.h>
 #include "gtklabel.h"
 #include "gtkaccellabel.h"
@@ -43,16 +44,23 @@
 #include "gtknotebook.h"
 #include "gtkstock.h"
 #include "gtkbindings.h"
+#include "gtkextendedlayout.h"
 #include "gtkprivate.h"
 #include "gtkalias.h"
 
 #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;
+  gint baseline_offset;
+  gboolean full_size;
 }
 GtkLabelPrivate;
 
@@ -95,7 +103,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 };
@@ -195,9 +204,14 @@ static gint gtk_label_move_forward_word  (GtkLabel        *label,
 static gint gtk_label_move_backward_word (GtkLabel        *label,
 					  gint             start);
 
+static void gtk_label_extended_layout_interface_init (GtkExtendedLayoutIface *iface);
+
+
 static GQuark quark_angle = 0;
 
-G_DEFINE_TYPE (GtkLabel, gtk_label, GTK_TYPE_MISC)
+G_DEFINE_TYPE_WITH_CODE (GtkLabel, gtk_label, GTK_TYPE_MISC,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
+						gtk_label_extended_layout_interface_init))
 
 static void
 add_move_binding (GtkBindingSet  *binding_set,
@@ -512,6 +526,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 +719,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 +805,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);
@@ -1970,7 +2007,7 @@ get_label_wrap_width (GtkLabel *label)
   GtkLabelPrivate *priv;
 
   priv = GTK_LABEL_GET_PRIVATE (label);
-  
+
   if (priv->wrap_width < 0)
     {
       if (priv->width_chars > 0 || priv->max_width_chars > 0)
@@ -2005,14 +2042,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 +2093,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;
@@ -2084,14 +2121,14 @@ gtk_label_ensure_layout (GtkLabel *label)
 
 	      wrap_width = get_label_wrap_width (label);
 	      width = MIN (width, wrap_width);
-	      width = MIN (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 +2175,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 +2203,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 +2233,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 (logical.width > width)
-	    pango_layout_set_width (label->layout, width);
+              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 (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 +2409,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 +2422,49 @@ 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) +
+	     priv->baseline_offset);
 
   if (xp)
     *xp = x;
@@ -3676,6 +3776,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
@@ -4237,5 +4363,126 @@ gtk_label_do_popup (GtkLabel       *label,
                     0, gtk_get_current_event_time ());
 }
 
+static GtkExtendedLayoutFeatures
+gtk_label_extended_layout_get_features (GtkExtendedLayout *layout)
+{
+  GtkLabel *label = GTK_LABEL (layout);
+  GtkLabelPrivate *priv = GTK_LABEL_GET_PRIVATE (label);
+  GtkExtendedLayoutFeatures features;
+
+  features =
+    GTK_EXTENDED_LAYOUT_NATURAL_SIZE |
+    GTK_EXTENDED_LAYOUT_BASELINES;
+
+  if (label->ellipsize || (label->wrap && priv->full_size))
+    {
+      gdouble angle = gtk_label_get_angle (label);
+
+      if (0 == angle || 180 == angle)
+        features |= GTK_EXTENDED_LAYOUT_HEIGHT_FOR_WIDTH;
+      else if (90 == angle || 270 == angle)
+        features |= GTK_EXTENDED_LAYOUT_WIDTH_FOR_HEIGHT;
+    }
+
+  return features;
+}
+
+static gint
+gtk_label_extended_layout_get_size_for_allocation (GtkExtendedLayout *layout,
+                                                   gint               size)
+{
+  GtkLabel *label = GTK_LABEL (layout);
+  gdouble angle = gtk_label_get_angle (label);
+  PangoLayout *tmp;
+
+  /* this test is slightly more tolerant than the get_features test */
+  g_assert (0 == (((gint)angle) % 90));
+
+  gtk_label_ensure_layout (label);
+  tmp = pango_layout_copy (label->layout);
+  pango_layout_set_width (tmp, PANGO_SCALE * size);
+  pango_layout_get_pixel_size (tmp, NULL, &size);
+  g_object_unref (tmp);
+
+  return size;
+}
+
+static void
+gtk_label_extended_layout_get_natural_size (GtkExtendedLayout *layout,
+                                            GtkRequisition    *requisition)
+{
+  GtkLabel *label;
+  gboolean ellipsize;
+  PangoLayout *tmp;
+
+  label = GTK_LABEL (layout);
+  gtk_label_ensure_layout (label);
+
+  ellipsize = label->ellipsize;
+  label->ellipsize = PANGO_ELLIPSIZE_NONE;
+
+  tmp = label->layout;
+  label->layout = pango_layout_copy (tmp);
+
+  pango_layout_set_width (label->layout, -1);
+  gtk_label_size_request (GTK_WIDGET (label), requisition);
+
+  g_object_unref (label->layout);
+  label->ellipsize = ellipsize;
+  label->layout = tmp;
+}
+
+static gint
+gtk_label_extended_layout_get_baselines (GtkExtendedLayout  *layout,
+                                         gint              **baselines)
+{
+  GtkLabel *label;
+  gint num_lines;
+  GSList *lines;
+
+  label = GTK_LABEL (layout);
+  gtk_label_ensure_layout (label);
+  lines = pango_layout_get_lines_readonly (label->layout);
+  num_lines = g_slist_length (lines);
+
+  if (baselines)
+    {
+      PangoRectangle log;
+      gint *baseptr;
+      gint offset;
+
+      get_layout_location (label, NULL, &offset);
+      offset -= GTK_WIDGET (label)->allocation.y;
+
+      *baselines = baseptr = g_new (gint, num_lines);
+
+      for (; lines; lines = lines->next, ++baseptr, offset += log.height)
+        {
+          pango_layout_line_get_pixel_extents (lines->data, NULL, &log);
+          *baseptr = offset + PANGO_ASCENT (log);
+        }
+    }
+
+  return num_lines;
+}
+
+static void
+gtk_label_extended_layout_set_baseline_offset (GtkExtendedLayout *layout,
+                                               gint               offset)
+{
+  GTK_LABEL_GET_PRIVATE (layout)->baseline_offset = offset;
+}
+
+static void
+gtk_label_extended_layout_interface_init (GtkExtendedLayoutIface *iface)
+{
+  iface->get_features = gtk_label_extended_layout_get_features;
+  iface->get_height_for_width = gtk_label_extended_layout_get_size_for_allocation;
+  iface->get_width_for_height = gtk_label_extended_layout_get_size_for_allocation;
+  iface->get_natural_size = gtk_label_extended_layout_get_natural_size;
+  iface->get_baselines = gtk_label_extended_layout_get_baselines;
+  iface->set_baseline_offset = gtk_label_extended_layout_set_baseline_offset;
+}
+
 #define __GTK_LABEL_C__
 #include "gtkaliasdef.c"
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/gtk/gtkplug-x11.c b/gtk/gtkplug-x11.c
index 028b2fe..721272e 100644
--- a/gtk/gtkplug-x11.c
+++ b/gtk/gtkplug-x11.c
@@ -32,6 +32,7 @@
 #include "gtkplugprivate.h"
 
 #include "x11/gdkx.h"
+#include <X11/Xatom.h>
 
 #include "gtkxembed.h"
 #include "gtkalias.h"
@@ -330,3 +331,28 @@ _gtk_plug_windowing_filter_func (GdkXEvent *gdk_xevent,
 
   return return_val;
 }
+
+void
+_gtk_plug_windowing_publish_natural_size (GtkPlug        *plug,
+					  GtkRequisition *requisition)
+{
+  GtkWidget *widget = GTK_WIDGET (plug);
+  GdkDisplay *display;
+  GdkWindow *window;
+  gint32 data[2];
+  Atom property;
+
+  gtk_widget_realize (widget);
+
+  window = GTK_WIDGET (plug)->window;
+  display = gdk_drawable_get_display (window);
+  property = gdk_x11_get_xatom_by_name_for_display (display, "_GTK_NATURAL_SIZE");
+
+  data[0] = requisition->width;
+  data[1] = requisition->height;
+
+  XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+		   GDK_WINDOW_XWINDOW (window), property,
+                   XA_CARDINAL, 32, PropModeReplace,
+                   (unsigned char*)data, 2);
+}
diff --git a/gtk/gtkplug.c b/gtk/gtkplug.c
index 8266804..36008bd 100644
--- a/gtk/gtkplug.c
+++ b/gtk/gtkplug.c
@@ -33,6 +33,7 @@
 #include "gtkintl.h"
 #include "gtkprivate.h"
 #include "gtkplugprivate.h"
+#include "gtkextendedlayout.h"
 
 #include "gtkalias.h"
 
@@ -675,11 +676,13 @@ static void
 gtk_plug_size_allocate (GtkWidget     *widget,
 			GtkAllocation *allocation)
 {
+  GtkBin *bin = GTK_BIN (widget);
+  GtkRequisition natural_size;
+
   if (GTK_WIDGET_TOPLEVEL (widget))
     GTK_WIDGET_CLASS (gtk_plug_parent_class)->size_allocate (widget, allocation);
   else
     {
-      GtkBin *bin = GTK_BIN (widget);
 
       widget->allocation = *allocation;
 
@@ -700,8 +703,18 @@ gtk_plug_size_allocate (GtkWidget     *widget,
 	  
 	  gtk_widget_size_allocate (bin->child, &child_allocation);
 	}
-      
     }
+
+  if (GTK_EXTENDED_LAYOUT_HAS_NATURAL_SIZE (bin->child))
+    {
+      gtk_extended_layout_get_natural_size (
+        GTK_EXTENDED_LAYOUT (bin->child),
+        &natural_size);
+
+      _gtk_plug_windowing_publish_natural_size (GTK_PLUG (widget), &natural_size);
+    }
+  else
+    _gtk_plug_windowing_publish_natural_size (GTK_PLUG (widget), &widget->requisition);
 }
 
 static gboolean
diff --git a/gtk/gtkplugprivate.h b/gtk/gtkplugprivate.h
index fb173c1..c9fa296 100644
--- a/gtk/gtkplugprivate.h
+++ b/gtk/gtkplugprivate.h
@@ -138,4 +138,13 @@ GdkFilterReturn _gtk_plug_windowing_filter_func (GdkXEvent *gdk_xevent,
 						 GdkEvent  *event,
 						 gpointer   data);
 
+/**
+ * _gtk_plug_windowing_publish_natural_size:
+ *
+ * @plug: a #GtkPlug
+ * @requisition: a requisition with the natural size
+ */
+void _gtk_plug_windowing_publish_natural_size (GtkPlug        *plug,
+					       GtkRequisition *requisition);
+
 #endif /* __GTK_PLUG_PRIVATE_H__ */
diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h
index 690cc8a..5399ee5 100644
--- a/gtk/gtkprivate.h
+++ b/gtk/gtkprivate.h
@@ -104,6 +104,10 @@ gboolean _gtk_fnmatch (const char *pattern,
 #define GTK_PARAM_WRITABLE G_PARAM_WRITABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
 #define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
 
+typedef void (*GtkChildSizeRequest) (GtkWidget      *child,
+			             GtkRequisition *requisition,
+                                     GtkWidget      *widget);
+
 G_END_DECLS
 
 #endif /* __GTK_PRIVATE_H__ */
diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
index 74afb1b..3cbcdb6 100644
--- a/gtk/gtkscrolledwindow.c
+++ b/gtk/gtkscrolledwindow.c
@@ -31,6 +31,7 @@
 #include "gtkmarshalers.h"
 #include "gtkscrolledwindow.h"
 #include "gtkwindow.h"
+#include "gtkextendedlayout.h"
 #include "gtkprivate.h"
 #include "gtkintl.h"
 #include "gtkalias.h"
@@ -144,9 +145,13 @@ static void gtk_scrolled_window_adjustment_changed (GtkAdjustment          *adju
 
 static void gtk_scrolled_window_update_real_placement (GtkScrolledWindow   *scrolled_window);
 
+static void gtk_scrolled_window_extended_layout_init (GtkExtendedLayoutIface *iface);
+
 static guint signals[LAST_SIGNAL] = {0};
 
-G_DEFINE_TYPE (GtkScrolledWindow, gtk_scrolled_window, GTK_TYPE_BIN)
+G_DEFINE_TYPE_WITH_CODE (GtkScrolledWindow, gtk_scrolled_window, GTK_TYPE_BIN,
+			 G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
+						gtk_scrolled_window_extended_layout_init))
 
 static void
 add_scroll_binding (GtkBindingSet  *binding_set,
@@ -1165,8 +1170,9 @@ gtk_scrolled_window_move_focus_out (GtkScrolledWindow *scrolled_window,
 }
 
 static void
-gtk_scrolled_window_size_request (GtkWidget      *widget,
-				  GtkRequisition *requisition)
+gtk_scrolled_window_real_size_request (GtkWidget      *widget,
+				       GtkRequisition *requisition,
+				       gboolean        consider_natural_size)
 {
   GtkScrolledWindow *scrolled_window;
   GtkBin *bin;
@@ -1197,7 +1203,12 @@ gtk_scrolled_window_size_request (GtkWidget      *widget,
   
   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
     {
-      gtk_widget_size_request (bin->child, &child_requisition);
+      if (consider_natural_size &&
+          GTK_EXTENDED_LAYOUT_HAS_NATURAL_SIZE (bin->child))
+        gtk_extended_layout_get_natural_size (GTK_EXTENDED_LAYOUT (bin->child),
+                                              &child_requisition);
+      else
+        gtk_widget_size_request (bin->child, &child_requisition);
 
       if (scrolled_window->hscrollbar_policy == GTK_POLICY_NEVER)
 	requisition->width += child_requisition.width;
@@ -1257,6 +1268,13 @@ gtk_scrolled_window_size_request (GtkWidget      *widget,
 }
 
 static void
+gtk_scrolled_window_size_request (GtkWidget      *widget,
+				  GtkRequisition *requisition)
+{
+  gtk_scrolled_window_real_size_request (widget, requisition, FALSE);
+}
+
+static void
 gtk_scrolled_window_relative_allocation (GtkWidget     *widget,
 					 GtkAllocation *allocation)
 {
@@ -1707,5 +1725,25 @@ _gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window)
     }
 }
 
+static GtkExtendedLayoutFeatures
+gtk_scrolled_window_extended_layout_get_features (GtkExtendedLayout *layout)
+{
+  return GTK_EXTENDED_LAYOUT_NATURAL_SIZE;
+}
+
+static void
+gtk_scrolled_window_extended_layout_get_natural_size (GtkExtendedLayout *layout,
+                                                       GtkRequisition    *requisition)
+{
+  gtk_scrolled_window_real_size_request (GTK_WIDGET (layout), requisition, TRUE);
+}
+
+static void 
+gtk_scrolled_window_extended_layout_init (GtkExtendedLayoutIface *iface)
+{
+  iface->get_features = gtk_scrolled_window_extended_layout_get_features;
+  iface->get_natural_size = gtk_scrolled_window_extended_layout_get_natural_size;
+}
+
 #define __GTK_SCROLLED_WINDOW_C__
 #include "gtkaliasdef.c"
diff --git a/gtk/gtksocket-x11.c b/gtk/gtksocket-x11.c
index 5d1f693..393974e 100644
--- a/gtk/gtksocket-x11.c
+++ b/gtk/gtksocket-x11.c
@@ -39,6 +39,7 @@
 #include "gtkdnd.h"
 
 #include "x11/gdkx.h"
+#include <X11/Xatom.h>
 
 #ifdef HAVE_XFIXES
 #include <X11/extensions/Xfixes.h>
@@ -93,7 +94,7 @@ _gtk_socket_windowing_size_request (GtkSocket *socket)
 
   socket->request_width = 1;
   socket->request_height = 1;
-	  
+
   if (XGetWMNormalHints (GDK_WINDOW_XDISPLAY (socket->plug_window),
 			 GDK_WINDOW_XWINDOW (socket->plug_window),
 			 &hints, &supplied))
@@ -115,6 +116,63 @@ _gtk_socket_windowing_size_request (GtkSocket *socket)
 }
 
 void
+_gtk_socket_windowing_get_natural_size (GtkSocket *socket)
+{
+  GtkSocketPrivate *priv;
+  GdkDisplay *display;
+
+  Atom property, type;
+  int format, status;
+
+  unsigned long nitems, bytes_after;
+  unsigned char *data;
+  gint32 *data_long;
+
+  priv = _gtk_socket_get_private (socket);
+
+  priv->natural_width = 1;
+  priv->natural_height = 1;
+
+  if (GTK_WIDGET_MAPPED (socket))
+    {
+      display = gdk_drawable_get_display (socket->plug_window);
+      property = gdk_x11_get_xatom_by_name_for_display (display, "_GTK_NATURAL_SIZE");
+
+      gdk_error_trap_push ();
+      status = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
+                                   GDK_WINDOW_XWINDOW (socket->plug_window),
+                                   property, 0, 2, False, XA_CARDINAL,
+                                   &type, &format, &nitems, &bytes_after,
+                                   &data);
+      gdk_error_trap_pop ();
+
+      priv->have_natural_size = TRUE;
+
+      if (Success != status || !type)
+        return;
+
+      if (type != XA_CARDINAL)
+        {
+          g_warning ("_GTK_NATURAL_SIZE property has wrong type: %d\n", (int)type);
+          return;
+        }
+
+      if (nitems < 2)
+        {
+          g_warning ("_GTK_NATURAL_SIZE too short\n");
+          XFree (data);
+          return;
+        }
+
+      data_long = (gint32*) data;
+      priv->natural_width = MAX (1, data_long[0]);
+      priv->natural_height = MAX (1, data_long[1]);
+
+      XFree (data);
+    }
+}
+
+void
 _gtk_socket_windowing_send_key_event (GtkSocket *socket,
 				      GdkEvent  *gdk_event,
 				      gboolean   mask_key_presses)
@@ -594,6 +652,10 @@ _gtk_socket_windowing_filter_func (GdkXEvent *gdk_xevent,
 		}
 	      return_val = GDK_FILTER_REMOVE;
 	    }
+	  else if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_GTK_NATURAL_SIZE"))
+	    {
+              _gtk_socket_windowing_get_natural_size (socket);
+	    }
 	}
       break;
     case ReparentNotify:
diff --git a/gtk/gtksocket.c b/gtk/gtksocket.c
index af66729..1bd6738 100644
--- a/gtk/gtksocket.c
+++ b/gtk/gtksocket.c
@@ -36,6 +36,7 @@
 #include "gtkprivate.h"
 #include "gtksocket.h"
 #include "gtksocketprivate.h"
+#include "gtkextendedlayout.h"
 #include "gtkdnd.h"
 #include "gtkintl.h"
 
@@ -67,6 +68,7 @@ static void     gtk_socket_forall               (GtkContainer     *container,
 						 GtkCallback       callback,
 						 gpointer          callback_data);
 
+static void gtk_socket_extended_layout_interface_init (GtkExtendedLayoutIface *iface);
 
 /* Local data */
 
@@ -98,7 +100,9 @@ _gtk_socket_get_private (GtkSocket *socket)
   return G_TYPE_INSTANCE_GET_PRIVATE (socket, GTK_TYPE_SOCKET, GtkSocketPrivate);
 }
 
-G_DEFINE_TYPE (GtkSocket, gtk_socket, GTK_TYPE_CONTAINER)
+G_DEFINE_TYPE_WITH_CODE (GtkSocket, gtk_socket, GTK_TYPE_CONTAINER,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
+                                                gtk_socket_extended_layout_interface_init))
 
 static void
 gtk_socket_finalize (GObject *object)
@@ -185,6 +189,8 @@ gtk_socket_class_init (GtkSocketClass *class)
 static void
 gtk_socket_init (GtkSocket *socket)
 {
+  GtkSocketPrivate *priv;
+
   socket->request_width = 0;
   socket->request_height = 0;
   socket->current_width = 0;
@@ -199,6 +205,9 @@ gtk_socket_init (GtkSocket *socket)
 
   socket->accel_group = gtk_accel_group_new ();
   g_object_set_data (G_OBJECT (socket->accel_group), I_("gtk-socket"), socket);
+
+  priv = _gtk_socket_get_private (socket);
+  priv->have_natural_size = FALSE;
 }
 
 /**
@@ -1015,5 +1024,51 @@ _gtk_socket_advance_toplevel_focus (GtkSocket        *socket,
     }
 }
 
+static GtkExtendedLayoutFeatures
+gtk_socket_extended_layout_get_features (GtkExtendedLayout *layout)
+{
+  return GTK_EXTENDED_LAYOUT_NATURAL_SIZE;
+}
+
+static void
+gtk_socket_extended_layout_get_natural_size (GtkExtendedLayout *layout,
+                                             GtkRequisition    *requisition)
+{
+  GtkSocket *socket = GTK_SOCKET (layout);
+  GtkSocketPrivate *priv;
+
+  if (socket->plug_widget)
+    {
+      gtk_extended_layout_get_natural_size (
+        GTK_EXTENDED_LAYOUT (socket->plug_widget),
+        requisition);
+    }
+  else
+    {
+      priv = _gtk_socket_get_private (socket);
+
+      if (socket->is_mapped && !priv->have_natural_size && socket->plug_window)
+	_gtk_socket_windowing_get_natural_size (socket);
+
+      if (socket->is_mapped && priv->have_natural_size)
+	{
+	  requisition->width = MAX (priv->natural_width, 1);
+	  requisition->height = MAX (priv->natural_height, 1);
+	}
+      else
+	{
+	  requisition->width = 1;
+	  requisition->height = 1;
+	}
+    }
+}
+
+static void
+gtk_socket_extended_layout_interface_init (GtkExtendedLayoutIface *iface)
+{
+  iface->get_features = gtk_socket_extended_layout_get_features;
+  iface->get_natural_size = gtk_socket_extended_layout_get_natural_size;
+}
+
 #define __GTK_SOCKET_C__
 #include "gtkaliasdef.c"
diff --git a/gtk/gtksocketprivate.h b/gtk/gtksocketprivate.h
index 85852fb..3779e43 100644
--- a/gtk/gtksocketprivate.h
+++ b/gtk/gtksocketprivate.h
@@ -31,6 +31,9 @@ typedef struct _GtkSocketPrivate GtkSocketPrivate;
 struct _GtkSocketPrivate
 {
   gint resize_count;
+  gint natural_width;
+  gint natural_height;
+  gboolean have_natural_size;
 };
 
 /* In gtksocket.c: */
@@ -84,6 +87,12 @@ void _gtk_socket_windowing_end_embedding_toplevel (GtkSocket *socket);
 void _gtk_socket_windowing_size_request (GtkSocket *socket);
 
 /**
+ * _gtk_socket_windowing_get_natural_size:
+ *
+ */
+void _gtk_socket_windowing_get_natural_size (GtkSocket *socket);
+
+/**
  * _gtk_socket_windowing_send_key_event:
  *
  */
diff --git a/gtk/gtktable.c b/gtk/gtktable.c
index 9002ad3..eb94c5d 100644
--- a/gtk/gtktable.c
+++ b/gtk/gtktable.c
@@ -26,10 +26,16 @@
 
 #include <config.h>
 #include "gtktable.h"
+#include "gtkextendedlayout.h"
+#include "gtksizegroup.h"
 #include "gtkprivate.h"
 #include "gtkintl.h"
 #include "gtkalias.h"
 
+#define GTK_TABLE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_TABLE, GtkTablePrivate))
+
+typedef struct _GtkTablePrivate GtkTablePrivate;
+
 enum
 {
   PROP_0,
@@ -37,7 +43,8 @@ enum
   PROP_N_COLUMNS,
   PROP_COLUMN_SPACING,
   PROP_ROW_SPACING,
-  PROP_HOMOGENEOUS
+  PROP_HOMOGENEOUS,
+  PROP_ORIENTATION
 };
 
 enum
@@ -53,6 +60,10 @@ enum
   CHILD_PROP_Y_PADDING
 };
   
+struct _GtkTablePrivate
+{
+  GtkOrientation orientation;
+};
 
 static void gtk_table_finalize	    (GObject	    *object);
 static void gtk_table_size_request  (GtkWidget	    *widget,
@@ -97,8 +108,11 @@ static void gtk_table_size_allocate_init  (GtkTable *table);
 static void gtk_table_size_allocate_pass1 (GtkTable *table);
 static void gtk_table_size_allocate_pass2 (GtkTable *table);
 
+static void gtk_table_extended_layout_interface_init (GtkExtendedLayoutIface *iface);
 
-G_DEFINE_TYPE (GtkTable, gtk_table, GTK_TYPE_CONTAINER)
+G_DEFINE_TYPE_WITH_CODE (GtkTable, gtk_table, GTK_TYPE_CONTAINER,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
+                                                gtk_table_extended_layout_interface_init))
 
 static void
 gtk_table_class_init (GtkTableClass *class)
@@ -121,7 +135,8 @@ gtk_table_class_init (GtkTableClass *class)
   container_class->child_type = gtk_table_child_type;
   container_class->set_child_property = gtk_table_set_child_property;
   container_class->get_child_property = gtk_table_get_child_property;
-  
+
+  g_type_class_add_private (class, sizeof (GtkTablePrivate));
 
   g_object_class_install_property (gobject_class,
                                    PROP_N_ROWS,
@@ -166,6 +181,14 @@ gtk_table_class_init (GtkTableClass *class)
 							 P_("If TRUE, the table cells are all the same width/height"),
 							 FALSE,
 							 GTK_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+                                   PROP_ORIENTATION,
+                                   g_param_spec_enum ("orientation",
+						      P_("Orientation"),
+						      P_("The orientation used for child allocation"),
+						      GTK_TYPE_ORIENTATION,
+						      GTK_ORIENTATION_VERTICAL,
+						      GTK_PARAM_READWRITE));
 
   gtk_container_class_install_child_property (container_class,
 					      CHILD_PROP_LEFT_ATTACH,
@@ -258,6 +281,9 @@ gtk_table_get_property (GObject      *object,
     case PROP_HOMOGENEOUS:
       g_value_set_boolean (value, table->homogeneous);
       break;
+    case PROP_ORIENTATION:
+      g_value_set_enum (value, GTK_TABLE_GET_PRIVATE (table)->orientation);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -291,6 +317,9 @@ gtk_table_set_property (GObject      *object,
     case PROP_HOMOGENEOUS:
       gtk_table_set_homogeneous (table, g_value_get_boolean (value));
       break;
+    case PROP_ORIENTATION:
+      gtk_table_set_orientation (table, g_value_get_enum (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -440,6 +469,8 @@ gtk_table_get_child_property (GtkContainer    *container,
 static void
 gtk_table_init (GtkTable *table)
 {
+  GtkTablePrivate *priv;
+
   GTK_WIDGET_SET_FLAGS (table, GTK_NO_WINDOW);
   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (table), FALSE);
   
@@ -452,6 +483,9 @@ gtk_table_init (GtkTable *table)
   table->row_spacing = 0;
   table->homogeneous = FALSE;
 
+  priv = GTK_TABLE_GET_PRIVATE (table);
+  priv->orientation = GTK_ORIENTATION_VERTICAL;
+
   gtk_table_resize (table, 1, 1);
 }
 
@@ -795,6 +829,52 @@ gtk_table_get_homogeneous (GtkTable *table)
   return table->homogeneous;
 }
 
+/**
+ * gtk_table_set_orientation:
+ * @table: a #GtkTable
+ * @orientation: the orientation to use
+ *
+ * Changes the orientation of the table. The orientation specifies if
+ * height-for-width or width-for-height is used for child allocation.
+ * With an orientation of #GTK_ORIENTATION_VERTICAL heigth-for-width
+ * and for #GTK_ORIENTATION_HORIZONTAL width-for-heigth is used.
+ **/
+void
+gtk_table_set_orientation (GtkTable       *table,
+			   GtkOrientation  orientation)
+{
+  GtkTablePrivate *priv;
+
+  g_return_if_fail (GTK_IS_TABLE (table));
+  priv = GTK_TABLE_GET_PRIVATE (table);
+
+  if (orientation != priv->orientation)
+    {
+      priv->orientation = orientation;
+      
+      if (GTK_WIDGET_VISIBLE (table))
+	gtk_widget_queue_resize (GTK_WIDGET (table));
+
+      g_object_notify (G_OBJECT (table), "orientation");
+    }
+}
+
+/**
+ * gtk_table_get_orientation:
+ * @table: a #GtkTable
+ *
+ * Returns whether the table prefers height-for-width or width-for-height
+ * for child allocation. (See gtk_table_set_orientation ())
+ *
+ * Return value: the orientation used
+ **/
+GtkOrientation
+gtk_table_get_orientation (GtkTable *table)
+{
+  g_return_val_if_fail (GTK_IS_TABLE (table), GTK_ORIENTATION_VERTICAL);
+  return GTK_TABLE_GET_PRIVATE (table)->orientation;
+}
+
 static void
 gtk_table_finalize (GObject *object)
 {
@@ -811,8 +891,9 @@ gtk_table_finalize (GObject *object)
 }
 
 static void
-gtk_table_size_request (GtkWidget      *widget,
-			GtkRequisition *requisition)
+gtk_table_real_size_request (GtkWidget      *widget,
+			     GtkRequisition *requisition,
+                             gboolean        consider_natural_size)
 {
   GtkTable *table;
   gint row, col;
@@ -830,14 +911,24 @@ gtk_table_size_request (GtkWidget      *widget,
   gtk_table_size_request_pass2 (table);
   gtk_table_size_request_pass3 (table);
   gtk_table_size_request_pass2 (table);
+
+  if (consider_natural_size)
+    {
+      for (col = 0; col < table->ncols; col++)
+        requisition->width += table->cols[col].natural_size;
+      for (row = 0; row < table->nrows; row++)
+        requisition->height += table->rows[row].natural_size;
+    }
+  else
+    {
+      for (col = 0; col < table->ncols; col++)
+        requisition->width += table->cols[col].requisition;
+      for (row = 0; row < table->nrows; row++)
+        requisition->height += table->rows[row].requisition;
+    }
   
-  for (col = 0; col < table->ncols; col++)
-    requisition->width += table->cols[col].requisition;
   for (col = 0; col + 1 < table->ncols; col++)
     requisition->width += table->cols[col].spacing;
-  
-  for (row = 0; row < table->nrows; row++)
-    requisition->height += table->rows[row].requisition;
   for (row = 0; row + 1 < table->nrows; row++)
     requisition->height += table->rows[row].spacing;
   
@@ -846,6 +937,13 @@ gtk_table_size_request (GtkWidget      *widget,
 }
 
 static void
+gtk_table_size_request (GtkWidget      *widget,
+			GtkRequisition *requisition)
+{
+  gtk_table_real_size_request (widget, requisition, FALSE);
+}
+
+static void
 gtk_table_size_allocate (GtkWidget     *widget,
 			 GtkAllocation *allocation)
 {
@@ -942,11 +1040,13 @@ gtk_table_size_request_init (GtkTable *table)
   for (row = 0; row < table->nrows; row++)
     {
       table->rows[row].requisition = 0;
+      table->rows[row].natural_size = 0;
       table->rows[row].expand = FALSE;
     }
   for (col = 0; col < table->ncols; col++)
     {
       table->cols[col].requisition = 0;
+      table->cols[col].natural_size = 0;
       table->cols[col].expand = FALSE;
     }
   
@@ -966,7 +1066,7 @@ gtk_table_size_request_init (GtkTable *table)
 	table->rows[child->top_attach].expand = TRUE;
     }
 }
-
+#include "gtklabel.h"
 static void
 gtk_table_size_request_pass1 (GtkTable *table)
 {
@@ -983,15 +1083,24 @@ gtk_table_size_request_pass1 (GtkTable *table)
       
       if (GTK_WIDGET_VISIBLE (child->widget))
 	{
-	  GtkRequisition child_requisition;
+	  GtkRequisition child_requisition, child_natural_size;
 	  gtk_widget_get_child_requisition (child->widget, &child_requisition);
 
+          if (GTK_EXTENDED_LAYOUT_HAS_NATURAL_SIZE (child->widget))
+            gtk_extended_layout_get_natural_size (GTK_EXTENDED_LAYOUT (child->widget), 
+                                                  &child_natural_size);
+          else
+            child_natural_size = child_requisition;
+
 	  /* Child spans a single column.
 	   */
 	  if (child->left_attach == (child->right_attach - 1))
 	    {
 	      width = child_requisition.width + child->xpadding * 2;
 	      table->cols[child->left_attach].requisition = MAX (table->cols[child->left_attach].requisition, width);
+
+	      width = child_natural_size.width + child->xpadding * 2;
+              table->cols[child->left_attach].natural_size = MAX (table->cols[child->left_attach].natural_size, width);
 	    }
 	  
 	  /* Child spans a single row.
@@ -1000,6 +1109,9 @@ gtk_table_size_request_pass1 (GtkTable *table)
 	    {
 	      height = child_requisition.height + child->ypadding * 2;
 	      table->rows[child->top_attach].requisition = MAX (table->rows[child->top_attach].requisition, height);
+
+	      height = child_natural_size.height + child->ypadding * 2;
+              table->rows[child->top_attach].natural_size = MAX (table->rows[child->top_attach].natural_size, height);
 	    }
 	}
     }
@@ -1046,14 +1158,25 @@ gtk_table_size_request_pass3 (GtkTable *table)
       
       if (GTK_WIDGET_VISIBLE (child->widget))
 	{
+          GtkRequisition child_natural_size;
+          GtkRequisition child_requisition;
+
+	  if (child->left_attach != (child->right_attach - 1) ||
+	      child->top_attach != (child->bottom_attach - 1))
+            {
+	      gtk_widget_get_child_requisition (child->widget, &child_requisition);
+	      
+              if (GTK_EXTENDED_LAYOUT_HAS_NATURAL_SIZE (child->widget))
+                gtk_extended_layout_get_natural_size (GTK_EXTENDED_LAYOUT (child->widget), 
+                                                      &child_natural_size);
+              else
+                child_natural_size = child_requisition;
+            }
+
 	  /* Child spans multiple columns.
 	   */
 	  if (child->left_attach != (child->right_attach - 1))
 	    {
-	      GtkRequisition child_requisition;
-
-	      gtk_widget_get_child_requisition (child->widget, &child_requisition);
-	      
 	      /* Check and see if there is already enough space
 	       *  for the child.
 	       */
@@ -1101,10 +1224,6 @@ gtk_table_size_request_pass3 (GtkTable *table)
 	   */
 	  if (child->top_attach != (child->bottom_attach - 1))
 	    {
-	      GtkRequisition child_requisition;
-
-	      gtk_widget_get_child_requisition (child->widget, &child_requisition);
-
 	      /* Check and see if there is already enough space
 	       *  for the child.
 	       */
@@ -1353,10 +1472,13 @@ gtk_table_size_allocate_pass1 (GtkTable *table)
   gint real_width;
   gint real_height;
   gint width, height;
+  gint natural_delta;
+  gint natural_size;
   gint row, col;
   gint nexpand;
   gint nshrink;
   gint extra;
+  gint delta;
   
   /* If we were allocated more space than we requested
    *  then we have to expand any expandable rows and columns
@@ -1365,6 +1487,7 @@ gtk_table_size_allocate_pass1 (GtkTable *table)
   
   real_width = GTK_WIDGET (table)->allocation.width - GTK_CONTAINER (table)->border_width * 2;
   real_height = GTK_WIDGET (table)->allocation.height - GTK_CONTAINER (table)->border_width * 2;
+  natural_delta = 0;
   
   if (table->homogeneous)
     {
@@ -1403,29 +1526,45 @@ gtk_table_size_allocate_pass1 (GtkTable *table)
       for (col = 0; col < table->ncols; col++)
 	{
 	  width += table->cols[col].requisition;
+          delta = table->cols[col].natural_size - table->cols[col].requisition;
+
 	  if (table->cols[col].expand)
 	    nexpand += 1;
 	  if (table->cols[col].shrink)
 	    nshrink += 1;
+
+	  if (delta > 0)
+            natural_delta += delta;
 	}
       for (col = 0; col + 1 < table->ncols; col++)
 	width += table->cols[col].spacing;
       
       /* Check to see if we were allocated more width than we requested.
        */
-      if ((width < real_width) && (nexpand >= 1))
+      if ((width < real_width) && (nexpand >= 1 || natural_delta > 0))
 	{
 	  width = real_width - width;
-	  
+          natural_size = MAX (0, MIN (width, natural_delta));
+          width = width - natural_size;
+
 	  for (col = 0; col < table->ncols; col++)
-	    if (table->cols[col].expand)
-	      {
-		extra = width / nexpand;
-		table->cols[col].allocation += extra;
-		
-		width -= extra;
-		nexpand -= 1;
-	      }
+            {
+              if (natural_size)
+                {
+                  delta = table->cols[col].natural_size - table->cols[col].requisition;
+                  delta = natural_size * delta / natural_delta;
+                  table->cols[col].allocation += delta;
+                }
+
+              if (table->cols[col].expand)
+                {
+                  extra = width / nexpand;
+                  table->cols[col].allocation += extra;
+                  
+                  width -= extra;
+                  nexpand -= 1;
+                }
+            }
 	}
       
       /* Check to see if we were allocated less width than we requested,
@@ -1496,29 +1635,44 @@ gtk_table_size_allocate_pass1 (GtkTable *table)
       for (row = 0; row < table->nrows; row++)
 	{
 	  height += table->rows[row].requisition;
+          delta = table->rows[row].natural_size - table->rows[row].requisition;
+
 	  if (table->rows[row].expand)
 	    nexpand += 1;
 	  if (table->rows[row].shrink)
 	    nshrink += 1;
+	  if (table->rows[row].natural_size > table->rows[row].requisition)
+            natural_delta += delta;
 	}
       for (row = 0; row + 1 < table->nrows; row++)
 	height += table->rows[row].spacing;
       
       /* Check to see if we were allocated more height than we requested.
        */
-      if ((height < real_height) && (nexpand >= 1))
+      if ((height < real_height) && (nexpand >= 1 || natural_delta > 0))
 	{
 	  height = real_height - height;
-	  
+          natural_size = MAX (0, MIN (height, natural_delta));
+          height = height - natural_size;
+
 	  for (row = 0; row < table->nrows; row++)
-	    if (table->rows[row].expand)
-	      {
-		extra = height / nexpand;
-		table->rows[row].allocation += extra;
-		
-		height -= extra;
-		nexpand -= 1;
-	      }
+            {
+              if (natural_size)
+                {
+                  delta = table->rows[row].natural_size - table->rows[row].requisition;
+                  delta = natural_size * delta / natural_delta;
+                  table->rows[row].allocation += delta;
+                }
+
+              if (table->rows[row].expand)
+                {
+                  extra = height / nexpand;
+                  table->rows[row].allocation += extra;
+                  
+                  height -= extra;
+                  nexpand -= 1;
+                }
+            }
 	}
       
       /* Check to see if we were allocated less height than we requested.
@@ -1636,5 +1790,25 @@ gtk_table_size_allocate_pass2 (GtkTable *table)
     }
 }
 
+static GtkExtendedLayoutFeatures
+gtk_table_extended_layout_get_features (GtkExtendedLayout *layout)
+{
+  return GTK_EXTENDED_LAYOUT_NATURAL_SIZE;
+}
+
+static void
+gtk_table_extended_layout_get_natural_size (GtkExtendedLayout *layout,
+                                            GtkRequisition    *requisition)
+{
+  gtk_table_real_size_request (GTK_WIDGET (layout), requisition, TRUE);
+}
+
+static void
+gtk_table_extended_layout_interface_init (GtkExtendedLayoutIface *iface)
+{
+  iface->get_features = gtk_table_extended_layout_get_features;
+  iface->get_natural_size = gtk_table_extended_layout_get_natural_size;
+}
+
 #define __GTK_TABLE_C__
 #include "gtkaliasdef.c"
diff --git a/gtk/gtktable.h b/gtk/gtktable.h
index f925d33..85ca9b0 100644
--- a/gtk/gtktable.h
+++ b/gtk/gtktable.h
@@ -93,6 +93,7 @@ struct _GtkTableRowCol
   guint expand : 1;
   guint shrink : 1;
   guint empty : 1;
+  guint16 natural_size;
 };
 
 
@@ -139,6 +140,9 @@ void	   gtk_table_set_homogeneous  (GtkTable	       *table,
 				       gboolean		homogeneous);
 gboolean   gtk_table_get_homogeneous  (GtkTable        *table);
 
+void           gtk_table_set_orientation  (GtkTable       *table,
+					   GtkOrientation  orientation);
+GtkOrientation gtk_table_get_orientation  (GtkTable       *table);
 
 G_END_DECLS
 
diff --git a/gtk/gtktreeprivate.h b/gtk/gtktreeprivate.h
index 9d49f39..d47c1f7 100644
--- a/gtk/gtktreeprivate.h
+++ b/gtk/gtktreeprivate.h
@@ -77,6 +77,8 @@ enum
   */
 #define TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER(tree_view) (10*TREE_VIEW_HEADER_HEIGHT(tree_view))
 
+#define GTK_TREE_VIEW_COLUMN_GET_PRIVATE(column) (G_TYPE_INSTANCE_GET_PRIVATE ((column), GTK_TYPE_TREE_VIEW_COLUMN, GtkTreeViewColumnPrivate))
+
 typedef struct _GtkTreeViewColumnReorder GtkTreeViewColumnReorder;
 struct _GtkTreeViewColumnReorder
 {
@@ -268,6 +270,11 @@ struct _GtkTreeViewPrivate
   gint tooltip_column;
 };
 
+struct _GtkTreeViewColumnPrivate
+{
+  gint natural_width;
+};
+
 #ifdef __GNUC__
 
 #define TREE_VIEW_INTERNAL_ASSERT(expr, ret)     G_STMT_START{          \
diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c
index 5b393a3..3cff8a6 100644
--- a/gtk/gtktreeview.c
+++ b/gtk/gtktreeview.c
@@ -43,6 +43,7 @@
 #include "gtkframe.h"
 #include "gtktreemodelsort.h"
 #include "gtktooltip.h"
+#include "gtkextendedlayout.h"
 #include "gtkprivate.h"
 #include "gtkalias.h"
 
@@ -470,6 +471,9 @@ static void gtk_tree_view_buildable_add_child (GtkBuildable *tree_view,
 					       const gchar *type);
 static void gtk_tree_view_buildable_init      (GtkBuildableIface *iface);
 
+/* GtkExtendedLayout */
+static void gtk_tree_view_extended_layout_init (GtkExtendedLayoutIface *iface);
+
 
 static gboolean scroll_row_timeout                   (gpointer     data);
 static void     add_scroll_timeout                   (GtkTreeView *tree_view);
@@ -484,7 +488,9 @@ static guint tree_view_signals [LAST_SIGNAL] = { 0 };
 
 G_DEFINE_TYPE_WITH_CODE (GtkTreeView, gtk_tree_view, GTK_TYPE_CONTAINER,
 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
-						gtk_tree_view_buildable_init))
+						gtk_tree_view_buildable_init)
+			 G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
+						gtk_tree_view_extended_layout_init))
 
 static void
 gtk_tree_view_class_init (GtkTreeViewClass *class)
@@ -2157,6 +2163,31 @@ gtk_tree_view_get_real_requested_width_from_column (GtkTreeView       *tree_view
   return real_requested_width;
 }
 
+static gint
+gtk_tree_view_get_real_natural_width_from_column (GtkTreeView       *tree_view,
+                                                  GtkTreeViewColumn *column)
+{
+  GtkTreeViewColumnPrivate *column_priv;
+  GtkRequisition button_natural_size;
+  gint column_natural_width;
+
+  column_priv = GTK_TREE_VIEW_COLUMN_GET_PRIVATE (column);
+  column_natural_width = column_priv->natural_width;
+
+  if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE))
+    {
+      if (GTK_EXTENDED_LAYOUT_HAS_NATURAL_SIZE (column->button))
+        gtk_extended_layout_get_natural_size (GTK_EXTENDED_LAYOUT (column->button),
+                                              &button_natural_size);
+      else
+        button_natural_size.width = column->button_request;
+
+      column_natural_width = MAX (column_natural_width, button_natural_size.width);
+    }
+
+  return column_natural_width;
+}
+
 /* GtkWidget::size_allocate helper */
 static void
 gtk_tree_view_size_allocate_columns (GtkWidget *widget)
@@ -2165,9 +2196,10 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget)
   GList *list, *first_column, *last_column;
   GtkTreeViewColumn *column;
   GtkAllocation allocation;
-  gint width = 0;
-  gint extra, extra_per_column;
+  gint width = 0, natural_width;
+  gint extra_width, extra_per_column;
   gint full_requested_width = 0;
+  gint full_natural_width = 0;
   gint number_of_expand_columns = 0;
   gboolean column_changed = FALSE;
   gboolean rtl;
@@ -2200,14 +2232,19 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget)
 	continue;
 
       full_requested_width += gtk_tree_view_get_real_requested_width_from_column (tree_view, column);
+      full_natural_width += gtk_tree_view_get_real_natural_width_from_column (tree_view, column);
 
       if (column->expand)
 	number_of_expand_columns++;
     }
 
-  extra = MAX (widget->allocation.width - full_requested_width, 0);
+  full_natural_width -= full_requested_width;
+  extra_width = MAX (0, widget->allocation.width - full_requested_width);
+  natural_width = MIN (extra_width, full_natural_width);
+  extra_width -= natural_width;
+
   if (number_of_expand_columns > 0)
-    extra_per_column = extra/number_of_expand_columns;
+    extra_per_column = extra_width/number_of_expand_columns;
   else
     extra_per_column = 0;
 
@@ -2216,6 +2253,7 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget)
        list = (rtl ? list->prev : list->next)) 
     {
       gint real_requested_width = 0;
+      gint real_natural_width = 0;
       gint old_width;
 
       column = list->data;
@@ -2241,29 +2279,34 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget)
 	}
 
       real_requested_width = gtk_tree_view_get_real_requested_width_from_column (tree_view, column);
+      real_natural_width = gtk_tree_view_get_real_natural_width_from_column (tree_view, column);
+      real_natural_width -= real_requested_width;
 
       allocation.x = width;
       column->width = real_requested_width;
 
+      if (full_natural_width > 0)
+        column->width += natural_width * real_natural_width / full_natural_width;
+
       if (column->expand)
 	{
 	  if (number_of_expand_columns == 1)
 	    {
 	      /* We add the remander to the last column as
 	       * */
-	      column->width += extra;
+	      column->width += extra_width;
 	    }
 	  else
 	    {
 	      column->width += extra_per_column;
-	      extra -= extra_per_column;
+	      extra_width -= extra_per_column;
 	      number_of_expand_columns --;
 	    }
 	}
       else if (number_of_expand_columns == 0 &&
 	       list == last_column)
 	{
-	  column->width += extra;
+	  column->width += extra_width;
 	}
 
       g_object_notify (G_OBJECT (column), "width");
@@ -5592,10 +5635,13 @@ validate_row (GtkTreeView *tree_view,
 
   for (list = tree_view->priv->columns; list; list = list->next)
     {
-      gint tmp_width;
-      gint tmp_height;
+      GtkTreeViewColumnPrivate *column_priv;
+      GtkRequisition requested_size;
+      GtkRequisition natural_size;
+      gint padding;
 
       column = list->data;
+      column_priv =  GTK_TREE_VIEW_COLUMN_GET_PRIVATE (column);
 
       if (! column->visible)
 	continue;
@@ -5606,14 +5652,19 @@ validate_row (GtkTreeView *tree_view,
       gtk_tree_view_column_cell_set_cell_data (column, tree_view->priv->model, iter,
 					       GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT),
 					       node->children?TRUE:FALSE);
-      gtk_tree_view_column_cell_get_size (column,
-					  NULL, NULL, NULL,
-					  &tmp_width, &tmp_height);
+      gtk_tree_view_column_cell_get_size (column, NULL, NULL, NULL,
+					  &requested_size.width,
+                                          &requested_size.height);
+      if (GTK_EXTENDED_LAYOUT_HAS_NATURAL_SIZE (column))
+        gtk_extended_layout_get_natural_size (GTK_EXTENDED_LAYOUT (column),
+                                              &natural_size);
+      else
+        natural_size = requested_size;
 
       if (!is_separator)
 	{
-          tmp_height += vertical_separator;
-	  height = MAX (height, tmp_height);
+          requested_size.height += vertical_separator;
+	  height = MAX (height, requested_size.height);
 	  height = MAX (height, tree_view->priv->expander_size);
 	}
       else
@@ -5621,26 +5672,31 @@ validate_row (GtkTreeView *tree_view,
 
       if (gtk_tree_view_is_expander_column (tree_view, column))
         {
-	  tmp_width = tmp_width + horizontal_separator + (depth - 1) * tree_view->priv->level_indentation;
+	  padding = horizontal_separator + (depth - 1) * tree_view->priv->level_indentation;
 
 	  if (TREE_VIEW_DRAW_EXPANDERS (tree_view))
-	    tmp_width += depth * tree_view->priv->expander_size;
+	    padding += depth * tree_view->priv->expander_size;
 	}
       else
-	tmp_width = tmp_width + horizontal_separator;
+	padding = horizontal_separator;
 
       if (draw_vgrid_lines)
         {
 	  if (list->data == first_column || list->data == last_column)
-	    tmp_width += grid_line_width / 2.0;
+	    padding += grid_line_width / 2;
 	  else
-	    tmp_width += grid_line_width;
+	    padding += grid_line_width;
 	}
 
-      if (tmp_width > column->requested_width)
+      requested_size.width += padding;
+      natural_size.width += padding;
+
+      if (requested_size.width > column->requested_width ||
+          natural_size.width > column_priv->natural_width)
 	{
 	  retval = TRUE;
-	  column->requested_width = tmp_width;
+	  column->requested_width = requested_size.width;
+          column_priv->natural_width = natural_size.width;
 	}
     }
 
@@ -15146,7 +15202,7 @@ gtk_tree_view_set_grid_lines (GtkTreeView           *tree_view,
 	  !priv->grid_line_gc)
 	{
 	  gint line_width;
-	  guint8 *dash_list;
+	  gint8 *dash_list;
 
 	  gtk_widget_style_get (widget,
 				"grid-line-width", &line_width,
@@ -15232,7 +15288,7 @@ gtk_tree_view_set_enable_tree_lines (GtkTreeView *tree_view,
       if (enabled && !priv->tree_line_gc)
 	{
 	  gint line_width;
-	  guint8 *dash_list;
+	  gint8 *dash_list;
 	  gtk_widget_style_get (widget,
 				"tree-line-width", &line_width,
 				"tree-line-pattern", (gchar *)&dash_list,
@@ -15647,5 +15703,43 @@ gtk_tree_view_get_tooltip_column (GtkTreeView *tree_view)
   return tree_view->priv->tooltip_column;
 }
 
+static GtkExtendedLayoutFeatures
+gtk_tree_view_extended_layout_get_features (GtkExtendedLayout *layout)
+{
+  return GTK_EXTENDED_LAYOUT_NATURAL_SIZE;
+}
+
+static void
+gtk_tree_view_extended_layout_get_natural_size (GtkExtendedLayout *layout,
+                                                GtkRequisition    *requisition)
+{       
+  GtkTreeView *tree_view;
+  gint natural_width = 0;
+  GList *column_iter;
+
+  tree_view = GTK_TREE_VIEW (layout);
+
+  gtk_widget_size_request (GTK_WIDGET (layout), requisition);
+
+  for (column_iter = tree_view->priv->columns; column_iter; column_iter = column_iter->next)
+    {
+      GtkTreeViewColumn *column = column_iter->data;
+
+      if (!column->visible)
+	continue;
+
+      natural_width += gtk_tree_view_get_real_natural_width_from_column (tree_view, column);
+    }
+
+  requisition->width = natural_width;
+}
+
+static void
+gtk_tree_view_extended_layout_init (GtkExtendedLayoutIface *iface)
+{
+  iface->get_features = gtk_tree_view_extended_layout_get_features;
+  iface->get_natural_size = gtk_tree_view_extended_layout_get_natural_size;
+}
+
 #define __GTK_TREE_VIEW_C__
 #include "gtkaliasdef.c"
diff --git a/gtk/gtktreeviewcolumn.c b/gtk/gtktreeviewcolumn.c
index ea8adbb..3d5f37a 100644
--- a/gtk/gtktreeviewcolumn.c
+++ b/gtk/gtktreeviewcolumn.c
@@ -29,6 +29,7 @@
 #include "gtkhbox.h"
 #include "gtkmarshalers.h"
 #include "gtkarrow.h"
+#include "gtkextendedlayout.h"
 #include "gtkprivate.h"
 #include "gtkintl.h"
 #include "gtkalias.h"
@@ -152,7 +153,10 @@ static GList *gtk_tree_view_column_cell_prev                   (GtkTreeViewColum
 static void gtk_tree_view_column_clear_attributes_by_info      (GtkTreeViewColumn      *tree_column,
 					                        GtkTreeViewColumnCellInfo *info);
 /* GtkBuildable implementation */
-static void gtk_tree_view_column_buildable_init                 (GtkBuildableIface     *iface);
+static void gtk_tree_view_column_buildable_init                (GtkBuildableIface      *iface);
+
+/* GtkExtendedLayout implementation */
+static void gtk_tree_view_column_extended_layout_init          (GtkExtendedLayoutIface *iface);
 
 static guint tree_column_signals[LAST_SIGNAL] = { 0 };
 
@@ -160,7 +164,9 @@ G_DEFINE_TYPE_WITH_CODE (GtkTreeViewColumn, gtk_tree_view_column, GTK_TYPE_OBJEC
 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
 						gtk_tree_view_column_cell_layout_init)
 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
-						gtk_tree_view_column_buildable_init))
+						gtk_tree_view_column_buildable_init)
+			 G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
+						gtk_tree_view_column_extended_layout_init))
 
 
 static void
@@ -175,6 +181,8 @@ gtk_tree_view_column_class_init (GtkTreeViewColumnClass *class)
   object_class->finalize = gtk_tree_view_column_finalize;
   object_class->set_property = gtk_tree_view_column_set_property;
   object_class->get_property = gtk_tree_view_column_get_property;
+
+  g_type_class_add_private (class, sizeof (GtkTreeViewColumnPrivate));
   
   tree_column_signals[CLICKED] =
     g_signal_new (I_("clicked"),
@@ -2556,26 +2564,16 @@ gtk_tree_view_column_cell_set_cell_data (GtkTreeViewColumn *tree_column,
 
 }
 
-/**
- * gtk_tree_view_column_cell_get_size:
- * @tree_column: A #GtkTreeViewColumn.
- * @cell_area: The area a cell in the column will be allocated, or %NULL
- * @x_offset: location to return x offset of a cell relative to @cell_area, or %NULL
- * @y_offset: location to return y offset of a cell relative to @cell_area, or %NULL
- * @width: location to return width needed to render a cell, or %NULL
- * @height: location to return height needed to render a cell, or %NULL
- * 
- * Obtains the width and height needed to render the column.  This is used
- * primarily by the #GtkTreeView.
- **/
 void
-gtk_tree_view_column_cell_get_size (GtkTreeViewColumn *tree_column,
-				    GdkRectangle      *cell_area,
-				    gint              *x_offset,
-				    gint              *y_offset,
-				    gint              *width,
-				    gint              *height)
+gtk_tree_view_column_cell_get_real_size (GtkTreeViewColumn *tree_column,
+                                         GdkRectangle      *cell_area,
+                                         gint              *x_offset,
+                                         gint              *y_offset,
+                                         gint              *width,
+                                         gint              *height,
+                                         gboolean           consider_natural_size)
 {
+
   GList *list;
   gboolean first_cell = TRUE;
   gint focus_line_width;
@@ -2583,43 +2581,77 @@ gtk_tree_view_column_cell_get_size (GtkTreeViewColumn *tree_column,
   g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column));
 
   if (height)
-    * height = 0;
+    *height = 0;
   if (width)
-    * width = 0;
+    *width = 0;
 
   gtk_widget_style_get (tree_column->tree_view, "focus-line-width", &focus_line_width, NULL);
   
   for (list = tree_column->cell_list; list; list = list->next)
     {
       GtkTreeViewColumnCellInfo *info = (GtkTreeViewColumnCellInfo *) list->data;
+      GtkRequisition req = { 0, 0 };
       gboolean visible;
-      gint new_height = 0;
-      gint new_width = 0;
+
       g_object_get (info->cell, "visible", &visible, NULL);
 
       if (visible == FALSE)
-	continue;
+        continue;
 
       if (first_cell == FALSE && width)
-	*width += tree_column->spacing;
+        *width += tree_column->spacing;
 
-      gtk_cell_renderer_get_size (info->cell,
-				  tree_column->tree_view,
-				  cell_area,
-				  x_offset,
-				  y_offset,
-				  &new_width,
-				  &new_height);
+      if (consider_natural_size && GTK_EXTENDED_LAYOUT_HAS_NATURAL_SIZE (info->cell))
+        gtk_extended_layout_get_natural_size (GTK_EXTENDED_LAYOUT (info->cell), &req);
+      else
+        gtk_cell_renderer_get_size (info->cell, tree_column->tree_view,
+                                    cell_area, x_offset, y_offset,
+                                    &req.width, &req.height);
+
+      req.width += focus_line_width * 2;
+      req.height += focus_line_width * 2;
+
+      if (!consider_natural_size) 
+        {
+          req.width = MAX (info->requested_width, req.width);
+          req.height = MAX (0, req.height);
+
+          info->requested_width = req.width;
+        }
 
-      if (height)
-	* height = MAX (*height, new_height + focus_line_width * 2);
-      info->requested_width = MAX (info->requested_width, new_width + focus_line_width * 2);
       if (width)
-	* width += info->requested_width;
+        *width = req.width;
+      if (height)
+        *height = req.height;
+
       first_cell = FALSE;
     }
 }
 
+/**
+ * gtk_tree_view_column_cell_get_size:
+ * @tree_column: A #GtkTreeViewColumn.
+ * @cell_area: The area a cell in the column will be allocated, or %NULL
+ * @x_offset: location to return x offset of a cell relative to @cell_area, or %NULL
+ * @y_offset: location to return y offset of a cell relative to @cell_area, or %NULL
+ * @width: location to return width needed to render a cell, or %NULL
+ * @height: location to return height needed to render a cell, or %NULL
+ * 
+ * Obtains the width and height needed to render the column.  This is used
+ * primarily by the #GtkTreeView.
+ **/
+void
+gtk_tree_view_column_cell_get_size (GtkTreeViewColumn *tree_column,
+				    GdkRectangle      *cell_area,
+				    gint              *x_offset,
+				    gint              *y_offset,
+				    gint              *width,
+				    gint              *height)
+{
+  gtk_tree_view_column_cell_get_real_size (tree_column, cell_area,
+                                           x_offset, y_offset, width, height, FALSE);
+}
+
 /* rendering, event handling and rendering focus are somewhat complicated, and
  * quite a bit of code.  Rather than duplicate them, we put them together to
  * keep the code in one place.
@@ -3745,5 +3777,29 @@ gtk_tree_view_column_get_tree_view (GtkTreeViewColumn *tree_column)
   return tree_column->tree_view;
 }
 
+static GtkExtendedLayoutFeatures
+gtk_tree_view_column_extended_layout_get_features (GtkExtendedLayout *layout)
+{
+  return GTK_EXTENDED_LAYOUT_NATURAL_SIZE;
+}
+
+static void
+gtk_tree_view_column_extended_layout_get_natural_size (GtkExtendedLayout *layout,
+                                                       GtkRequisition    *requisition)
+{
+  gtk_tree_view_column_cell_get_real_size (GTK_TREE_VIEW_COLUMN (layout),
+                                           NULL, NULL, NULL,
+                                           &requisition->width,
+                                           &requisition->height,
+                                           TRUE);
+}
+
+static void 
+gtk_tree_view_column_extended_layout_init (GtkExtendedLayoutIface *iface)
+{
+  iface->get_features = gtk_tree_view_column_extended_layout_get_features;
+  iface->get_natural_size = gtk_tree_view_column_extended_layout_get_natural_size;
+}
+
 #define __GTK_TREE_VIEW_COLUMN_C__
 #include "gtkaliasdef.c"
diff --git a/gtk/gtktreeviewcolumn.h b/gtk/gtktreeviewcolumn.h
index d7f14cb..b045648 100644
--- a/gtk/gtktreeviewcolumn.h
+++ b/gtk/gtktreeviewcolumn.h
@@ -46,8 +46,9 @@ typedef enum
   GTK_TREE_VIEW_COLUMN_FIXED
 } GtkTreeViewColumnSizing;
 
-typedef struct _GtkTreeViewColumn      GtkTreeViewColumn;
-typedef struct _GtkTreeViewColumnClass GtkTreeViewColumnClass;
+typedef struct _GtkTreeViewColumn        GtkTreeViewColumn;
+typedef struct _GtkTreeViewColumnClass   GtkTreeViewColumnClass;
+typedef struct _GtkTreeViewColumnPrivate GtkTreeViewColumnPrivate;
 
 typedef void (* GtkTreeCellDataFunc) (GtkTreeViewColumn *tree_column,
 				      GtkCellRenderer   *cell,
diff --git a/gtk/gtkvbox.c b/gtk/gtkvbox.c
index 65633d8..2966586 100644
--- a/gtk/gtkvbox.c
+++ b/gtk/gtkvbox.c
@@ -26,8 +26,10 @@
 
 #include <config.h>
 #include "gtkvbox.h"
+#include "gtkextendedlayout.h"
 #include "gtkintl.h"
 #include "gtkalias.h"
+#include "gtkprivate.h"
 
 
 static void gtk_vbox_size_request  (GtkWidget      *widget,
@@ -35,7 +37,11 @@ static void gtk_vbox_size_request  (GtkWidget      *widget,
 static void gtk_vbox_size_allocate (GtkWidget      *widget,
 				    GtkAllocation  *allocation);
 
-G_DEFINE_TYPE (GtkVBox, gtk_vbox, GTK_TYPE_BOX)
+static void gtk_vbox_extended_layout_interface_init (GtkExtendedLayoutIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GtkVBox, gtk_vbox, GTK_TYPE_BOX,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
+						gtk_vbox_extended_layout_interface_init))
 
 static void
 gtk_vbox_class_init (GtkVBoxClass *class)
@@ -67,17 +73,37 @@ gtk_vbox_new (gboolean homogeneous,
   return GTK_WIDGET (vbox);
 }
 
+static void
+gtk_vbox_natural_size_request (GtkWidget      *child,
+			       GtkRequisition *requisition,
+                               GtkWidget      *widget)
+{
+  GtkExtendedLayoutFeatures features = 0;
+
+  if (GTK_IS_EXTENDED_LAYOUT (child))
+    features = gtk_extended_layout_get_features ((GtkExtendedLayout*) child);
+
+  if (features & GTK_EXTENDED_LAYOUT_HEIGHT_FOR_WIDTH)
+    {
+      requisition->width = widget->allocation.width;
+      requisition->height = gtk_extended_layout_get_height_for_width (
+        (GtkExtendedLayout*) child, widget->allocation.width);
+    }
+  else if (features & GTK_EXTENDED_LAYOUT_NATURAL_SIZE)
+    gtk_extended_layout_get_natural_size ((GtkExtendedLayout*) child, requisition);
+  else 
+    gtk_widget_size_request (child, requisition);
+}
 
 static void
-gtk_vbox_size_request (GtkWidget      *widget,
-		       GtkRequisition *requisition)
+gtk_vbox_real_size_request (GtkWidget           *widget,
+		            GtkRequisition      *requisition,
+                            GtkChildSizeRequest  size_request_func)
 {
   GtkBox *box;
   GtkBoxChild *child;
-  GtkRequisition child_requisition;
   GList *children;
   gint nvis_children;
-  gint height;
 
   box = GTK_BOX (widget);
   requisition->width = 0;
@@ -92,11 +118,12 @@ gtk_vbox_size_request (GtkWidget      *widget,
 
       if (GTK_WIDGET_VISIBLE (child->widget))
 	{
-	  gtk_widget_size_request (child->widget, &child_requisition);
+          GtkRequisition child_requisition;
+          size_request_func (child->widget, &child_requisition, widget);
 
 	  if (box->homogeneous)
 	    {
-	      height = child_requisition.height + child->padding * 2;
+              gint height = child_requisition.height + child->padding * 2;
 	      requisition->height = MAX (requisition->height, height);
 	    }
 	  else
@@ -122,19 +149,22 @@ gtk_vbox_size_request (GtkWidget      *widget,
 }
 
 static void
+gtk_vbox_size_request (GtkWidget      *widget,
+		       GtkRequisition *requisition)
+{
+  gtk_vbox_real_size_request (widget, requisition, (GtkChildSizeRequest)gtk_widget_size_request);
+}
+
+static void
 gtk_vbox_size_allocate (GtkWidget     *widget,
 			GtkAllocation *allocation)
 {
   GtkBox *box;
-  GtkBoxChild *child;
-  GList *children;
-  GtkAllocation child_allocation;
+
   gint nvis_children;
   gint nexpand_children;
-  gint child_height;
-  gint height;
-  gint extra;
-  gint y;
+  GtkBoxChild *child;
+  GList *children;
 
   box = GTK_BOX (widget);
   widget->allocation = *allocation;
@@ -158,141 +188,196 @@ gtk_vbox_size_allocate (GtkWidget     *widget,
 
   if (nvis_children > 0)
     {
-      if (box->homogeneous)
-	{
-	  height = (allocation->height -
-		   GTK_CONTAINER (box)->border_width * 2 -
-		   (nvis_children - 1) * box->spacing);
-	  extra = height / nvis_children;
-	}
-      else if (nexpand_children > 0)
-	{
-	  height = (gint) allocation->height - (gint) widget->requisition.height;
-	  extra = height / nexpand_children;
-	}
-      else
-	{
-	  height = 0;
-	  extra = 0;
-	}
+      GtkPackType packing;
+      gint border_width;
 
-      y = allocation->y + GTK_CONTAINER (box)->border_width;
-      child_allocation.x = allocation->x + GTK_CONTAINER (box)->border_width;
-      child_allocation.width = MAX (1, (gint) allocation->width - (gint) GTK_CONTAINER (box)->border_width * 2);
+      GtkAllocation child_allocation;
+      gint *natural_requisitions;
+      gint *minimum_requisitions;
 
-      children = box->children;
-      while (children)
-	{
-	  child = children->data;
-	  children = children->next;
+      gint available, natural, extra;
+      gint minimum_height, natural_height;
+      gint i;
 
-	  if ((child->pack == GTK_PACK_START) && GTK_WIDGET_VISIBLE (child->widget))
-	    {
-	      if (box->homogeneous)
-		{
-		  if (nvis_children == 1)
-		    child_height = height;
-		  else
-		    child_height = extra;
-
-		  nvis_children -= 1;
-		  height -= extra;
-		}
-	      else
-		{
-		  GtkRequisition child_requisition;
-
-		  gtk_widget_get_child_requisition (child->widget, &child_requisition);
-		  child_height = child_requisition.height + child->padding * 2;
-
-		  if (child->expand)
-		    {
-		      if (nexpand_children == 1)
-			child_height += height;
-		      else
-			child_height += extra;
-
-		      nexpand_children -= 1;
-		      height -= extra;
-		    }
-		}
-
-	      if (child->fill)
-		{
-		  child_allocation.height = MAX (1, child_height - (gint)child->padding * 2);
-		  child_allocation.y = y + child->padding;
-		}
-	      else
-		{
-		  GtkRequisition child_requisition;
-
-		  gtk_widget_get_child_requisition (child->widget, &child_requisition);
-		  child_allocation.height = child_requisition.height;
-		  child_allocation.y = y + (child_height - child_allocation.height) / 2;
-		}
-
-	      gtk_widget_size_allocate (child->widget, &child_allocation);
-
-	      y += child_height + box->spacing;
-	    }
-	}
+      border_width = GTK_CONTAINER (box)->border_width;
 
-      y = allocation->y + allocation->height - GTK_CONTAINER (box)->border_width;
+      minimum_height = widget->requisition.height;
+      natural_height = 0;
 
+      natural_requisitions = g_newa (gint, nvis_children);
+      minimum_requisitions = g_newa (gint, nvis_children);
+
+      i = 0;
       children = box->children;
+
       while (children)
-	{
-	  child = children->data;
-	  children = children->next;
+        {
+          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);
+          if (GTK_WIDGET_VISIBLE (child->widget))
+            {
+              GtkRequisition child_requisition;
 
-              if (box->homogeneous)
-                {
-                  if (nvis_children == 1)
-                    child_height = height;
-                  else
-                    child_height = extra;
+              gtk_widget_size_request (child->widget, &child_requisition);
+              minimum_requisitions[i] = child_requisition.height;
+              natural_requisitions[i] = 0;
 
-                  nvis_children -= 1;
-                  height -= extra;
-                }
-              else
+              if (GTK_IS_EXTENDED_LAYOUT (child->widget))
                 {
-		  child_height = child_requisition.height + child->padding * 2;
-
-                  if (child->expand)
+                  GtkExtendedLayout *layout = (GtkExtendedLayout*)child->widget;
+                  GtkExtendedLayoutFeatures features = gtk_extended_layout_get_features (layout);
+              
+                  if (features & GTK_EXTENDED_LAYOUT_HEIGHT_FOR_WIDTH)
                     {
-                      if (nexpand_children == 1)
-                        child_height += height;
-                      else
-                        child_height += extra;
-
-                      nexpand_children -= 1;
-                      height -= extra;
+                      gint height = gtk_extended_layout_get_height_for_width (layout, allocation->width);
+                      gint dy = minimum_requisitions[i] - height;
+
+                      if (dy > 0)
+                        {
+                          minimum_requisitions[i] = height;
+                          minimum_height -= dy;
+                        }
+                    }
+                  else if (features & GTK_EXTENDED_LAYOUT_NATURAL_SIZE)
+                    {
+                      gtk_extended_layout_get_natural_size (layout, &child_requisition);
+                      natural_requisitions[i] = child_requisition.height - minimum_requisitions[i];
                     }
                 }
 
-              if (child->fill)
-                {
-                  child_allocation.height = MAX (1, child_height - (gint)child->padding * 2);
-                  child_allocation.y = y + child->padding - child_height;
-                }
-              else
+              natural_height += natural_requisitions[i++];
+            }
+        }
+
+      if (box->homogeneous)
+	{
+	  available = (allocation->height - border_width * 2 -
+		      (nvis_children - 1) * box->spacing);
+	  extra = available / nvis_children;
+          natural = 0;
+	}
+      else
+	{
+          if (nexpand_children > 0 || natural_height > 0)
+            {
+	      available = (gint)allocation->height - minimum_height;
+              natural = MAX (0, MIN (available, natural_height));
+              available -= natural;
+            }
+          else
+            {
+              available = 0;
+              natural = 0;
+            }
+
+          if (nexpand_children > 0)
+	    extra = MAX (0, available / nexpand_children);
+          else
+            extra = 0;
+	}
+
+      child_allocation.x = allocation->x + border_width;
+      child_allocation.width = MAX (1, (gint) allocation->width - (gint) border_width * 2);
+
+      for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
+        {
+          gint y;
+
+          if (GTK_PACK_START == packing)
+            y = allocation->y + border_width;
+          else
+            y = allocation->y + allocation->height - border_width;
+
+          i = 0;
+          children = box->children;
+          while (children)
+            {
+              child = children->data;
+              children = children->next;
+
+              if (GTK_WIDGET_VISIBLE (child->widget))
                 {
-		  child_allocation.height = child_requisition.height;
-                  child_allocation.y = y + (child_height - child_allocation.height) / 2 - child_height;
-                }
+                  if (child->pack == packing)
+                    {
+                      gint child_height;
+
+                      if (box->homogeneous)
+                        {
+                          if (nvis_children == 1)
+                            child_height = available;
+                          else
+                            child_height = extra;
+
+                          nvis_children -= 1;
+                          available -= extra;
+                        }
+                      else
+                        {
+                          child_height = minimum_requisitions[i] + child->padding * 2;
+
+                          if (child->expand)
+                            {
+                              if (nexpand_children == 1)
+                                child_height += available;
+                              else
+                                child_height += extra;
+
+                              nexpand_children -= 1;
+                              available -= extra;
+                            }
+                        }
+
+		      if (natural_height > 0)
+                        child_height += natural * natural_requisitions[i] / natural_height;
+
+                      if (child->fill)
+                        {
+                          child_allocation.height = MAX (1, child_height - (gint)child->padding * 2);
+                          child_allocation.y = y + child->padding;
+                        }
+                      else
+                        {
+                          child_allocation.height = minimum_requisitions[i];
+                          child_allocation.y = y + (child_height - child_allocation.height) / 2;
+                        }
 
-              gtk_widget_size_allocate (child->widget, &child_allocation);
+                      if (GTK_PACK_END == packing)
+                        child_allocation.y -= child_height;
 
-              y -= (child_height + box->spacing);
-	    }
-	}
-    }
+                      gtk_widget_size_allocate (child->widget, &child_allocation);
+
+                      if (GTK_PACK_START == packing)
+	                y += child_height + box->spacing;
+                      else
+                        y -= child_height + box->spacing;
+                    } /* packing */
+
+                  ++i;
+                } /* visible */
+            } /* while children */
+        } /* for packing */
+    } /* nvis_children */
+}
+
+static GtkExtendedLayoutFeatures
+gtk_vbox_extended_layout_get_features (GtkExtendedLayout *layout)
+{
+  return GTK_EXTENDED_LAYOUT_NATURAL_SIZE;
+}
+
+static void
+gtk_vbox_extended_layout_get_natural_size (GtkExtendedLayout *layout,
+                                           GtkRequisition    *requisition)
+{
+  gtk_vbox_real_size_request (GTK_WIDGET (layout), requisition, gtk_vbox_natural_size_request);
+}
+
+static void
+gtk_vbox_extended_layout_interface_init (GtkExtendedLayoutIface *iface)
+{
+  iface->get_features = gtk_vbox_extended_layout_get_features;
+  iface->get_natural_size = gtk_vbox_extended_layout_get_natural_size;
 }
 
 #define __GTK_VBOX_C__
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 5f9bbf0..0f89f5e 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -24,10 +24,11 @@ if USE_X11
 testsocket_programs = testsocket testsocket_child
 endif
 
-TESTS = floatingtest buildertest
+TESTS = floatingtest buildertest autotestextendedlayout
 
 noinst_PROGRAMS =			\
 	autotestfilechooser		\
+	autotestextendedlayout		\
 	autotestkeywords		\
 	floatingtest			\
 	buildertest			\
@@ -44,6 +45,7 @@ noinst_PROGRAMS =			\
 	testdnd				\
 	testellipsise			\
 	testentrycompletion 		\
+	testextendedlayout 		\
 	testfilechooser			\
 	testfilechooserbutton		\
 	testframe			\
@@ -106,6 +108,7 @@ endif
 endif
 
 autotestfilechooser_DEPENDENCIES = $(TEST_DEPS)
+autotestextendedlayout_DEPENDENCIES = $(TEST_DEPS)
 simple_DEPENDENCIES = $(TEST_DEPS)
 floatingtest_DEPENDENCIES = $(TEST_DEPS)
 buildertest_DEPENDENCIES = $(TEST_DEPS)
@@ -124,6 +127,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)
@@ -163,6 +167,7 @@ testtooltips_DEPENDENCIES = $(TEST_DEPS)
 testvolumebutton_DEPENDENCIES = $(TEST_DEPS)
 
 autotestfilechooser_LDADD = $(LDADDS)
+autotestextendedlayout_LDADD = $(LDADDS)
 simple_LDADD = $(LDADDS)
 floatingtest_LDADD = $(LDADDS)
 buildertest_LDADD = $(LDADDS)
@@ -179,6 +184,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)
@@ -231,10 +237,16 @@ buildertest_LDFLAGS = -export-dynamic
 autotestfilechooser_SOURCES =	\
 	autotestfilechooser.c
 
+autotestextendedlayout_SOURCES =	\
+	autotestextendedlayout.c
+
 testentrycompletion_SOURCES = 	\
 	prop-editor.c		\
 	testentrycompletion.c
 
+testextendedlayout_SOURCES = 	\
+	testextendedlayout.c
+
 testfilechooser_SOURCES = 	\
 	prop-editor.c		\
 	testfilechooser.c 	
diff --git a/tests/autotestextendedlayout.c b/tests/autotestextendedlayout.c
new file mode 100644
index 0000000..6b47477
--- /dev/null
+++ b/tests/autotestextendedlayout.c
@@ -0,0 +1,702 @@
+#include <config.h>
+#include <string.h>
+#include <stdlib.h>
+#include <glib/gprintf.h>
+#include <gtk/gtk.h>
+
+/*****************************************************************************/
+
+#define log_test(condition) \
+  log_test_impl(G_STRFUNC, __LINE__, (condition), #condition)
+#define log_testf(condition, format, ...) \
+  log_test_impl(G_STRFUNC, __LINE__, (condition), \
+                #condition "\n      " format, __VA_ARGS__)
+#define log_testi(expected, number) G_STMT_START { \
+    const gint i = (expected), j = (number); \
+    log_test_impl(G_STRFUNC, __LINE__, i == j, #number " is " #expected \
+                  "\n      actual number %d, expected: %d", j, i); \
+  } G_STMT_END
+
+#define log_info(format, ...) G_STMT_START { \
+  if (debug) \
+    g_print ("INFO: %s: " format "\n", G_STRFUNC, __VA_ARGS__); \
+  } G_STMT_END
+
+#define log_int_array(values, length) \
+  log_int_array_impl (G_STRFUNC, #values, (values), (length))
+#define log_int(value) \
+  log_info (#value " is %d\n", (value));
+
+/*****************************************************************************/
+
+static const gchar lorem_ipsum[] =
+  "<span weight=\"bold\" size=\"xx-large\">"
+  "Lorem ipsum</span> dolor sit amet, consectetuer "
+  "adipiscing elit. Aliquam sed erat. Proin lectus "
+  "orci, venenatis pharetra, egestas id, tincidunt "
+  "vel, eros. Integer fringilla. Aenean justo ipsum, "        
+  "luctus ut, volutpat laoreet, vehicula in, libero.";
+
+static int num_failures = 0;
+static int num_warnings = 0;
+static int num_errors = 0;
+static int num_criticals = 0;
+static gboolean debug = FALSE;
+
+static GLogFunc default_log_handler;
+
+/*****************************************************************************/
+
+static void
+log_int_array_impl (const gchar *function,
+                    const gchar *var_name,
+                    const gint  *values,
+                    gsize        length)
+{
+  GString *tmp;
+
+  if (!debug)
+    return;
+
+  tmp = g_string_new ("");
+  
+  if (length--)
+    {
+      g_string_append_printf (tmp, "{ %d", *values++);
+
+      while (length--)
+        g_string_append_printf (tmp, ", %d", *values++);
+
+      g_string_append (tmp, " }");
+    }
+  else
+    {
+      g_string_append (tmp, "empty");
+    }
+
+  g_print ("INFO: %s: %s is %s\n", function, var_name, tmp->str);
+  g_string_free (tmp, TRUE);
+}
+
+static gboolean
+log_test_impl (const gchar *function,
+               gint         lineno,
+               gboolean     passed, 
+               const gchar *test_name, ...)
+{
+  va_list args;
+  char *str;
+
+  if (!passed)
+    ++num_failures;
+
+  va_start (args, test_name);
+  str = g_strdup_vprintf (test_name, args);
+  va_end (args);
+
+  if (!passed || debug)
+    g_printf ("%s: %s, line %d: %s\033[0m\n",
+              passed ? "PASS" : "\033[1;31mFAIL", 
+              function, lineno, str);
+
+  g_free (str);
+  return passed;
+}
+
+static void
+log_override_cb (const gchar   *log_domain,
+		 GLogLevelFlags log_level,
+		 const gchar   *message,
+		 gpointer       user_data)
+{
+  if (log_level & G_LOG_LEVEL_WARNING)
+    num_warnings++;
+
+  if (log_level & G_LOG_LEVEL_ERROR)
+    num_errors++;
+
+  if (log_level & G_LOG_LEVEL_CRITICAL)
+    num_criticals++;
+
+  (* default_log_handler) (log_domain, log_level, message, user_data);
+}
+
+/*****************************************************************************/
+
+static void
+gtk_label_test_baselines (void)
+{
+  GtkExtendedLayout *layout;
+  GtkWidget *widget;
+  GtkLabel *label;
+
+  gint num_baselines;
+  gint *baselines;
+
+  widget = g_object_ref_sink (gtk_label_new (NULL));
+  layout = GTK_EXTENDED_LAYOUT (widget);
+  label = GTK_LABEL (widget);
+
+  baselines = NULL;
+  gtk_label_set_text (label, NULL);
+  num_baselines = gtk_extended_layout_get_baselines (layout, &baselines);
+  log_int_array (baselines, num_baselines);
+  log_testi (1, num_baselines);
+  log_test (NULL != baselines); 
+  g_free (baselines);
+
+  baselines = NULL;
+  gtk_label_set_text (label, "");
+  num_baselines = gtk_extended_layout_get_baselines (layout, &baselines);
+  log_int_array (baselines, num_baselines);
+  log_testi (1, num_baselines);
+  log_test (NULL != baselines); 
+  g_free (baselines);
+
+  baselines = NULL;
+  gtk_label_set_text (label, "First Line");
+  num_baselines = gtk_extended_layout_get_baselines (layout, &baselines);
+  log_int_array (baselines, num_baselines);
+  log_testi (1, num_baselines);
+  log_test (NULL != baselines); 
+  g_free (baselines);
+
+  baselines = NULL;
+  gtk_label_set_text (label, "First Line\n");
+  num_baselines = gtk_extended_layout_get_baselines (layout, &baselines);
+  log_int_array (baselines, num_baselines);
+  log_testi (2, num_baselines);
+  log_test (NULL != baselines); 
+  g_free (baselines);
+
+  baselines = NULL;
+  gtk_label_set_text (label, "First Line\nSecond Line");
+  num_baselines = gtk_extended_layout_get_baselines (layout, &baselines);
+  log_int_array (baselines, num_baselines);
+  log_testi (2, num_baselines);
+  log_test (NULL != baselines); 
+  g_free (baselines);
+
+  baselines = NULL;
+  gtk_label_set_markup (label, "First Line\n<big>Second Line</big>\nThird Line");
+  num_baselines = gtk_extended_layout_get_baselines (layout, &baselines);
+  log_int_array (baselines, num_baselines);
+  log_testi (3, num_baselines);
+  log_test (NULL != baselines); 
+  g_free (baselines);
+
+  g_object_unref (widget);
+}
+
+static void
+gtk_label_test_height_for_width (void)
+{
+  GtkExtendedLayout *layout;
+  GtkWidget *widget;
+  GtkLabel *label;
+
+  PangoLayout *ref;
+  gint i, cx, cy, rcx, rcy;
+  double cy_min, cy_max;
+
+  widget = g_object_ref_sink (gtk_label_new (NULL));
+  layout = GTK_EXTENDED_LAYOUT (widget);
+  label = GTK_LABEL (widget);
+
+  gtk_label_set_markup (label, lorem_ipsum);
+  gtk_label_set_line_wrap_mode (label, PANGO_WRAP_CHAR);
+  gtk_label_set_line_wrap (label, TRUE);
+
+  ref = pango_layout_copy (gtk_label_get_layout (label));
+  pango_layout_set_width (ref, -1);
+  pango_layout_get_pixel_size (ref, &rcx, &rcy);
+  g_object_unref (ref);
+
+  log_info ("preferred layout size: %d\303\227%d", rcx, rcy);
+
+  for (i = 5; i >= 1; --i)
+    {
+      cx = rcx * i;
+      cy = gtk_extended_layout_get_height_for_width (layout, cx);
+      log_info ("scale is %d, so width is %d. results in height of %d.", i, cx, cy);
+      log_testi (rcy, cy);
+    }
+
+  cy_min = rcy;
+  cy_max = rcy * 2.5;
+
+  for (i = 2, cx = rcx / 2; cx >= rcy; ++i, cx = rcx / i, cy_min = cy, cy_max += rcy)
+    {
+      cy = gtk_extended_layout_get_height_for_width (layout, cx);
+
+      log_info ("scale is 1/%d, so width is %d. results in height of %d.", i, cx, cy);
+      log_testf (cy_min <= cy && cy <= cy_max, "%f \342\211\244 %d  \342\211\244 %f",
+                 cy_min, cy, cy_max);
+    }
+
+  g_object_unref (widget);
+}
+
+static void
+gtk_label_test_natural_size (void)
+{
+  GtkExtendedLayout *layout;
+  GtkWidget *widget;
+  GtkLabel *label;
+
+  const GEnumClass *ellipsize_class;
+  const GEnumClass *wrap_class;
+
+  GtkRequisition minimum;
+  GtkRequisition natural;
+
+  gint i, j;
+
+  widget = g_object_ref_sink (gtk_label_new (lorem_ipsum));
+  layout = GTK_EXTENDED_LAYOUT (widget);
+  label = GTK_LABEL (widget);
+
+  ellipsize_class = g_type_class_peek_static (PANGO_TYPE_ELLIPSIZE_MODE);
+  wrap_class = g_type_class_peek_static (PANGO_TYPE_WRAP_MODE);
+
+  for (i = -1; i < (gint)wrap_class->n_values; ++i)
+    { /*      ^^^^^^^^^^^^ deal with the unsigned integer (some really annyoing invention) */
+      const GEnumValue *const wrap = (i >= 0 ? &wrap_class->values[i] : NULL);
+      const gchar *wrap_mode = (wrap ? wrap->value_nick : "none");
+
+      gtk_label_set_line_wrap (label, NULL != wrap);
+
+      if (wrap)
+        gtk_label_set_line_wrap_mode (label, wrap->value);
+
+      gtk_label_set_ellipsize (label, PANGO_ELLIPSIZE_NONE);
+      gtk_widget_size_request (widget, &minimum);
+
+      log_test (minimum.width > 100);
+
+      log_info ("wrap mode `%s' leads to a minimum size of %d\303\227%d.", 
+                wrap_mode, minimum.width, minimum.height);
+
+      for (j = 0; j < ellipsize_class->n_values; ++j)
+        {
+          const GEnumValue *const ellipsize = &ellipsize_class->values[j];
+
+          gtk_label_set_ellipsize (label, ellipsize->value);
+          gtk_extended_layout_get_natural_size (layout, &natural);
+
+          log_info ("ellipsize mode `%s' leads to a natural size of %d\303\227%d.", 
+                    ellipsize->value_nick, natural.width, natural.height);
+          log_test (!memcmp (&natural, &minimum, sizeof natural)); 
+        }
+    }
+}
+
+static void
+gtk_label_test_extended_layout (void)
+{
+  GtkExtendedLayoutFeatures features;
+  GtkExtendedLayoutIface *iface;
+  GtkExtendedLayout *layout;
+  GtkWidget *widget;
+  GtkLabel *label;
+
+  widget = g_object_ref_sink (gtk_label_new (NULL));
+  layout = GTK_EXTENDED_LAYOUT (widget);
+  label = GTK_LABEL (widget);
+
+  /* vtable */
+
+  log_test (GTK_IS_EXTENDED_LAYOUT (label));
+  iface = GTK_EXTENDED_LAYOUT_GET_IFACE (label);
+
+  log_test (NULL != iface->get_features);
+  log_test (NULL != iface->get_height_for_width);
+  log_test (NULL == iface->get_width_for_height);
+  log_test (NULL != iface->get_natural_size);
+  log_test (NULL != iface->get_baselines);
+
+  /* feature set */
+
+  features = gtk_extended_layout_get_features (layout);
+
+  log_test (0 == (features & GTK_EXTENDED_LAYOUT_HEIGHT_FOR_WIDTH));
+  log_test (0 == (features & GTK_EXTENDED_LAYOUT_WIDTH_FOR_HEIGHT));
+  log_test (0 != (features & GTK_EXTENDED_LAYOUT_NATURAL_SIZE));
+  log_test (0 != (features & GTK_EXTENDED_LAYOUT_BASELINES));
+
+  gtk_label_set_line_wrap (label, TRUE);
+  features = gtk_extended_layout_get_features (layout);
+
+  log_test (0 != (features & GTK_EXTENDED_LAYOUT_HEIGHT_FOR_WIDTH));
+  log_test (0 == (features & GTK_EXTENDED_LAYOUT_WIDTH_FOR_HEIGHT));
+  log_test (0 != (features & GTK_EXTENDED_LAYOUT_NATURAL_SIZE));
+  log_test (0 != (features & GTK_EXTENDED_LAYOUT_BASELINES));
+
+  g_object_unref (widget);
+
+  gtk_label_test_baselines ();
+  gtk_label_test_height_for_width ();
+  gtk_label_test_natural_size ();
+}
+
+static void
+gtk_bin_test_extended_layout (void)
+{
+  const GType types[] = 
+    { 
+      GTK_TYPE_ALIGNMENT, GTK_TYPE_BUTTON,
+      GTK_TYPE_EVENT_BOX, GTK_TYPE_FRAME,
+      G_TYPE_INVALID
+    };
+
+  GtkExtendedLayoutFeatures features;
+  GtkExtendedLayoutIface *iface;
+  GtkExtendedLayout *layout;
+
+  GtkWidget *window;
+  GtkWidget *label;
+  GtkWidget *bin;
+
+  gint baseline_label;
+  gint baseline_bin;
+
+  int i, y;
+
+  for (i = 0; types[i]; ++i)
+    {
+      log_info ("Testing %s", g_type_name (types[i]));
+
+      label = gtk_label_new (g_type_name (types[i]));
+
+      bin = g_object_new (types[i], NULL);
+      gtk_container_add (GTK_CONTAINER (bin), label);
+      layout = GTK_EXTENDED_LAYOUT (bin);
+
+      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+      gtk_container_add (GTK_CONTAINER (window), bin);
+      gtk_widget_show_all (window);
+
+      /* vtable */
+
+      log_test (GTK_IS_EXTENDED_LAYOUT (bin));
+      iface = GTK_EXTENDED_LAYOUT_GET_IFACE (bin);
+
+      log_test (NULL != iface->get_features);
+      log_test (NULL != iface->get_height_for_width);
+      log_test (NULL != iface->get_width_for_height);
+      log_test (NULL != iface->get_natural_size);
+      log_test (NULL != iface->get_baselines);
+
+      /* feature set */
+
+      features = gtk_extended_layout_get_features (layout);
+
+      log_test (0 == (features & GTK_EXTENDED_LAYOUT_HEIGHT_FOR_WIDTH));
+      log_test (0 == (features & GTK_EXTENDED_LAYOUT_WIDTH_FOR_HEIGHT));
+      log_test (0 != (features & GTK_EXTENDED_LAYOUT_NATURAL_SIZE));
+      log_test (0 != (features & GTK_EXTENDED_LAYOUT_BASELINES));
+
+      gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+      features = gtk_extended_layout_get_features (layout);
+
+      log_test (0 != (features & GTK_EXTENDED_LAYOUT_HEIGHT_FOR_WIDTH));
+      log_test (0 == (features & GTK_EXTENDED_LAYOUT_WIDTH_FOR_HEIGHT));
+      log_test (0 != (features & GTK_EXTENDED_LAYOUT_NATURAL_SIZE));
+      log_test (0 != (features & GTK_EXTENDED_LAYOUT_BASELINES));
+
+      /* verify baseline propagation */
+
+      baseline_label =
+        gtk_extended_layout_get_single_baseline (
+        GTK_EXTENDED_LAYOUT (label), GTK_BASELINE_FIRST);
+      baseline_bin =
+        gtk_extended_layout_get_single_baseline (
+        GTK_EXTENDED_LAYOUT (bin), GTK_BASELINE_FIRST);
+
+      if (!gtk_widget_translate_coordinates (label, bin, 0, 0, NULL, &y))
+        log_testf (FALSE, "failed to translate GtkLabel coordinates "
+                          "into %s coordinates", g_type_name (types[i]));
+
+      log_testi (baseline_label + y, baseline_bin);
+
+      gtk_container_set_border_width (GTK_CONTAINER (bin), 23);
+
+      baseline_bin =
+        gtk_extended_layout_get_single_baseline (
+        GTK_EXTENDED_LAYOUT (bin), GTK_BASELINE_FIRST);
+
+      log_testi (baseline_label + y + 23, baseline_bin);
+
+      /* verify padding injection */
+
+      if (GTK_TYPE_ALIGNMENT == types[i])
+        {
+          log_test (0 != (features & GTK_EXTENDED_LAYOUT_PADDING));
+
+          gtk_alignment_set_padding (GTK_ALIGNMENT (bin), 5, 7, 11, 13);
+
+          baseline_bin =
+            gtk_extended_layout_get_single_baseline (
+            GTK_EXTENDED_LAYOUT (bin), GTK_BASELINE_FIRST);
+
+          log_testi (baseline_label + y + 28, baseline_bin);
+        }
+
+      gtk_widget_destroy (window);
+    }
+}
+
+static void
+gtk_table_test_extended_layout (void)
+{
+  const gchar *numbers[] =
+    {
+      "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine",
+      "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen",
+      NULL
+    };
+
+  GtkExtendedLayoutFeatures features;
+  GtkRequisition natural_sizes[4];
+
+  GtkWidget *labels[G_N_ELEMENTS(numbers) - 1];
+  GtkWidget *table;
+  GtkWidget *window;
+
+  int i, row, col;
+
+  table = gtk_table_new (4, 4, FALSE);
+
+  for (i = 0, row; numbers[i]; ++i)
+    {
+      col = i % 4;
+      row = i / 4;
+
+      labels[i] = gtk_label_new (numbers[i]);
+      gtk_table_attach (GTK_TABLE (table), labels[i], col, col + 1, row, row + 1,
+                        (col > 0 ? GTK_FILL : 0) | (2 == col ? GTK_EXPAND : 0),
+                        (row > 0 ? GTK_FILL : 0) | (2 == row ? GTK_EXPAND : 0),
+                        6, 6);
+    }
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_container_add (GTK_CONTAINER (window), table);
+  gtk_widget_show_all (window);
+
+  /* feature set */
+
+  features = gtk_extended_layout_get_features (GTK_EXTENDED_LAYOUT (table));
+
+  log_test (0 == (features & GTK_EXTENDED_LAYOUT_HEIGHT_FOR_WIDTH));
+  log_test (0 == (features & GTK_EXTENDED_LAYOUT_WIDTH_FOR_HEIGHT));
+  log_test (0 != (features & GTK_EXTENDED_LAYOUT_NATURAL_SIZE));
+  log_test (0 == (features & GTK_EXTENDED_LAYOUT_BASELINES));
+
+  /* natural size changes are propagated */ 
+
+  gtk_extended_layout_get_natural_size (GTK_EXTENDED_LAYOUT (labels[7]), &natural_sizes[0]);
+  gtk_extended_layout_get_natural_size (GTK_EXTENDED_LAYOUT (table), &natural_sizes[1]);
+
+  gtk_label_set_text (GTK_LABEL (labels[7]), "This text is much longer than the number");
+
+  gtk_extended_layout_get_natural_size (GTK_EXTENDED_LAYOUT (labels[7]), &natural_sizes[2]);
+  gtk_extended_layout_get_natural_size (GTK_EXTENDED_LAYOUT (table), &natural_sizes[3]);
+
+  log_testf (natural_sizes[0].width < natural_sizes[2].width,
+             "Natural size of the label grows: %d < %d",
+             natural_sizes[0].width, natural_sizes[2].width);
+  log_testf (natural_sizes[1].width < natural_sizes[3].width,
+             "Natural size of the table grows: %d < %d",
+	     natural_sizes[1].width, natural_sizes[3].width);
+}
+
+/*****************************************************************************/
+
+static void
+extended_layout_check_child (GtkWidget *child,
+                             gpointer   data);
+
+static void
+extended_layout_natural_size_test (GtkExtendedLayout *layout)
+{
+  GtkRequisition natural_size;
+  GtkRequisition requisition;
+  GtkWidget *parent = NULL;
+  gboolean passed = TRUE;
+
+  g_return_if_fail (GTK_EXTENDED_LAYOUT_HAS_NATURAL_SIZE (layout));
+
+  if (GTK_IS_WIDGET (layout))
+    {
+      parent = gtk_widget_get_parent (GTK_WIDGET (layout));
+      gtk_widget_size_request (GTK_WIDGET (layout), &requisition);
+    }
+  else if (GTK_IS_CELL_RENDERER (layout))
+    {
+      parent = gtk_cell_view_new ();
+      gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (parent),
+                                  GTK_CELL_RENDERER (layout),
+                                  FALSE);
+      gtk_cell_renderer_get_size (GTK_CELL_RENDERER (layout),
+                                  parent, NULL, NULL, NULL,
+                                  &requisition.width,
+                                  &requisition.height);
+    }
+  else if (GTK_IS_TREE_VIEW_COLUMN (layout))
+    {
+      parent = gtk_tree_view_new ();
+      gtk_tree_view_append_column (GTK_TREE_VIEW (parent),
+                                   GTK_TREE_VIEW_COLUMN (layout));
+
+      requisition.width =
+        gtk_tree_view_column_get_width (
+        GTK_TREE_VIEW_COLUMN (layout));
+      requisition.height = 0;
+    }
+  else
+    {
+      g_warning ("%s: Unsupported object category: %s",
+                 G_STRFUNC, G_OBJECT_TYPE_NAME (layout));
+      return;
+    }
+
+  gtk_extended_layout_get_natural_size (layout, &natural_size);
+
+  passed &= log_testf (
+    natural_size.width >= requisition.width,
+    "parent=%s, item=%s(%p), natural-width=%d, requisition=%d",
+    parent ? G_OBJECT_TYPE_NAME (parent) : "(none)",
+    G_OBJECT_TYPE_NAME (layout), layout,
+    natural_size.width, requisition.width);
+  passed &= log_testf (
+    natural_size.height >= requisition.height,
+    "parent=%s, item=%s(%p), natural-height=%d, requisition=%d",
+    parent ? G_OBJECT_TYPE_NAME (parent) : "(none)",
+    G_OBJECT_TYPE_NAME (layout), layout,
+    natural_size.height, requisition.height);
+
+  if (!passed && GTK_IS_CONTAINER (layout))
+    gtk_container_foreach (GTK_CONTAINER (layout),
+                           extended_layout_check_child,
+                           NULL); 
+}
+
+static void
+extended_layout_check_child (GtkWidget *child,
+                             gpointer   data)
+{
+  if (GTK_EXTENDED_LAYOUT_HAS_NATURAL_SIZE (child))
+    extended_layout_natural_size_test (GTK_EXTENDED_LAYOUT (child));
+}
+
+static void
+introspective_test (const gchar *argv0)
+{
+  gint n_extended_layout_types = 0;
+  gint n_extended_layout_roots = 0;
+  GModule *module = NULL;
+  GError *error = NULL;
+  GIOChannel *file;
+
+  gchar *filename;
+  gchar *dirname;
+  gchar *line;
+
+  dirname = g_path_get_dirname (argv0);
+  filename = g_build_filename (dirname,
+                               "..", "..", "docs", "reference",
+                               "gtk", "gtk.types", NULL);
+  file = g_io_channel_new_file (filename, "r", &error);
+
+  g_free (dirname);
+
+  if (error)
+    goto cleanup;
+
+  module = g_module_open (NULL, G_MODULE_BIND_LOCAL);
+
+  while (G_IO_STATUS_NORMAL ==
+         g_io_channel_read_line (file, &line, NULL, NULL, &error))
+    {
+      gpointer get_type_function;
+
+      line = g_strstrip (line);
+
+      if (g_str_has_suffix (line, "_get_type") &&
+          g_module_symbol (module, line, &get_type_function))
+        {
+          GType type = ((GType(*)()) get_type_function) ();
+
+          if (g_type_is_a (type, GTK_TYPE_EXTENDED_LAYOUT) &&
+              G_TYPE_IS_INSTANTIATABLE (type))
+            {
+              GtkExtendedLayoutIface *iface;
+              GtkExtendedLayout *object;
+              gpointer type_class;
+
+              type_class = g_type_class_ref (type);
+              iface = GTK_EXTENDED_LAYOUT_CLASS (type_class);
+
+              log_testf (NULL != iface->get_features, "%s", g_type_name (type));
+
+              if (!g_type_is_a (g_type_parent (type), GTK_TYPE_EXTENDED_LAYOUT))
+                ++n_extended_layout_roots;
+
+              if (!G_TYPE_IS_ABSTRACT (type))
+                {
+                  object = g_object_new (type, NULL);
+                  log_testf (NULL != object, "%s", g_type_name (type));
+
+                  if (GTK_EXTENDED_LAYOUT_HAS_NATURAL_SIZE (object))
+                    extended_layout_natural_size_test (GTK_EXTENDED_LAYOUT (object));
+                }
+
+              g_type_class_unref (type_class);
+              ++n_extended_layout_types;
+            }
+        }
+
+      g_free (line);
+    }
+
+  log_testi (72, n_extended_layout_types);
+  log_testi (10, n_extended_layout_roots);
+
+cleanup:
+  if (error)
+    {
+      g_warning ("%s: %s", filename, error->message);
+      g_error_free (error);
+    }
+
+  if (module)
+    g_module_close (module);
+  if (file)
+    g_io_channel_unref (file);
+
+  g_free (filename);
+}
+
+/*****************************************************************************/
+
+int
+main(int argc, char **argv)
+{
+  default_log_handler = g_log_set_default_handler (log_override_cb, NULL);
+
+  gtk_init (&argc, &argv);
+
+  debug = (NULL != g_getenv ("DEBUG"));
+
+  gtk_label_test_extended_layout ();
+  gtk_bin_test_extended_layout ();
+  gtk_table_test_extended_layout ();
+  introspective_test (argv[0]);
+
+  log_testi (0, num_warnings);
+  log_testi (0, num_errors);
+  log_testi (0, num_criticals);
+  log_testi (0, num_failures);
+  
+  return MAX(0, num_failures - 1);
+}
diff --git a/tests/testellipsise.c b/tests/testellipsise.c
index 0783d61..26f5304 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,123 @@ 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 };
+  GtkRequisition natural_size;
+  GtkWidget *label = data;
+  cairo_t *cr;
+  gint x, y;
+
+  cr = gdk_cairo_create (widget->window);
+  cairo_translate (cr, -0.5, -0.5);
+  cairo_set_line_width (cr, 1);
+
+  cairo_set_source_rgb (cr, 1, 1, 1);
+  cairo_rectangle (cr, 0, 0, widget->allocation.width, widget->allocation.height);
+  cairo_fill (cr);
+
+  gtk_widget_translate_coordinates (label, widget, 0, 0, &x, &y);
+  layout = gtk_widget_create_pango_layout (widget, "");
+
+  pango_layout_set_markup (layout,
+    "<span color='#c33'>\342\227\217 requisition</span>\n"
+    "<span color='#3c3'>\342\227\217 natural size</span>\n"
+    "<span color='#33c'>\342\227\217 allocation</span>", -1);
+
+  pango_cairo_show_layout (cr, layout);
+  g_object_unref (layout);
+
+  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);
+
+  gtk_extended_layout_get_natural_size (GTK_EXTENDED_LAYOUT (label),
+                                        &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);
+
+  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 (ebox, "expose-event", G_CALLBACK (ebox_expose_event_cb), label);
 
   gtk_widget_show_all (window);
 
diff --git a/tests/testextendedlayout.c b/tests/testextendedlayout.c
new file mode 100644
index 0000000..0404c3a
--- /dev/null
+++ b/tests/testextendedlayout.c
@@ -0,0 +1,2654 @@
+/* testextendedlayout.c
+ * Copyright (C) 2007 Mathias Hasselmann <mathias.hasselmann@gmx.de>
+ *
+ * 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 <gtk/gtk.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef GDK_WINDOWING_X11
+#include "x11/gdkx.h"
+#endif
+
+#define IS_VALID_BASELINE(Baseline) ((Baseline) >= 0)
+
+typedef enum _GuideType GuideType;
+typedef enum _TestResult TestResult;
+
+typedef struct _Guide Guide;
+
+typedef struct _TestCase TestCase;
+typedef struct _TestSuite TestSuite;
+
+enum _GuideFlags
+{
+  GUIDE_FLAGS_HORIZONTAL = (1 << 0),
+  GUIDE_FLAGS_VERTICAL = (1 << 1)
+};
+
+enum _GuideType
+{
+  GUIDE_BASELINE,
+
+  GUIDE_INTERIOUR_VERTICAL,
+  GUIDE_INTERIOUR_HORIZONTAL,
+  GUIDE_INTERIOUR_BOTH,
+
+  GUIDE_EXTERIOUR_VERTICAL,
+  GUIDE_EXTERIOUR_HORIZONTAL,
+  GUIDE_EXTERIOUR_BOTH
+};
+
+enum _TestResult
+{
+  TEST_RESULT_NONE,
+  TEST_RESULT_SUCCESS,
+  TEST_RESULT_FAILURE
+};
+
+enum
+{
+  RESULT_COLUMN_MESSAGE,
+  RESULT_COLUMN_WEIGHT,
+  RESULT_COLUMN_ICON,
+  RESULT_COLUMN_RESULT,
+  RESULT_COLUNN_COUNT
+};
+
+enum
+{
+  TEST_COLUMN_LABEL,
+  TEST_COLUMN_SELECTED,
+  TEST_COLUMN_TEST_CASE,
+  TEST_COLUMN_HAS_TEST_CASE,
+  TEST_COLUMN_PAGE_INDEX,
+  TEST_COLUMN_COUNT
+};
+
+struct _Guide
+{
+  GtkWidget *widget;
+  GuideType type;
+  gint group;
+};
+
+struct _TestCase
+{
+  TestSuite *suite;
+  const gchar *name;
+  const gchar *detail;
+  GtkWidget *widget;
+  GList *guides;
+  guint idle;
+};
+
+struct _TestSuite
+{
+  GtkTreeSelection *selection;
+  GtkWidget *test_current_button;
+  GtkListStore *tests;
+
+  GtkWidget *window;
+  GtkWidget *notebook;
+  GtkWidget *baselines;
+  GtkWidget *interiour;
+  GtkWidget *exteriour;
+  GtkWidget *statusbar;
+
+  GtkTreeStore *results;
+  GtkWidget *results_view;
+  gint n_test_cases;
+  gint level;
+
+  GdkPixmap *tile;
+  GtkWidget *current;
+  GtkWidget *hover;
+  gint timestamp;
+
+  GtkTreeIter parent;
+};
+
+static const gchar lorem_ipsum[] =
+  "<span weight=\"bold\" size=\"xx-large\">"
+  "Lorem ipsum</span> dolor sit amet, consectetuer "
+  "adipiscing elit. Aliquam sed erat. Proin lectus "
+  "orci, venenatis pharetra, egestas id, tincidunt "
+  "vel, eros. Integer fringilla. Aenean justo ipsum, "       
+  "luctus ut, volutpat laoreet, vehicula in, libero.";
+
+const gchar *captions[] =
+  { 
+    "<span size='xx-small'>xx-Small</span>",
+    "<span weight='bold'>Bold</span>",
+    "<span size='large'>Large</span>",
+    "<span size='xx-large'>xx-Large</span>",
+    NULL
+  };
+
+static char * mask_xpm[] = 
+  {
+    "20 20 2 1",
+    " 	c #000000",
+    "#	c #FFFFFF",
+    " # # # # # # # # # #",
+    "# # # # # # # # # # ",
+    " # # # # # # # # # #",
+    "# # # # # # # # # # ",
+    " # # # # # # # # # #",
+    "# # # # # # # # # # ",
+    " # # # # # # # # # #",
+    "# # # # # # # # # # ",
+    " # # # # # # # # # #",
+    "# # # # # # # # # # ",
+    " # # # # # # # # # #",
+    "# # # # # # # # # # ",
+    " # # # # # # # # # #",
+    "# # # # # # # # # # ",
+    " # # # # # # # # # #",
+    "# # # # # # # # # # ",
+    " # # # # # # # # # #",
+    "# # # # # # # # # # ",
+    " # # # # # # # # # #",
+    "# # # # # # # # # # "
+  };
+
+static gint8 dashes[] = { 1, 5 };
+
+static void
+set_widget_name (GtkWidget   *widget,
+                 const gchar *format,
+                 ...)
+{
+  gchar *name, *dash;
+  va_list args;
+
+  va_start (args, format);
+  name = g_strdup_vprintf (format, args);
+  va_end (args);
+
+  for(dash = name; NULL != (dash = strchr (dash, ' ')); )
+    *dash = '-';
+
+  gtk_widget_set_name (widget, name);
+  g_free (name);
+}
+
+static Guide*
+guide_new (GtkWidget   *widget,
+           GuideType    type,
+           gint         group)
+{
+  Guide* self = g_new0 (Guide, 1);
+
+  self->widget = widget;
+  self->type = type;
+  self->group = group;
+
+  return self;
+}
+
+static TestCase*
+test_case_new (TestSuite   *suite,
+               const gchar *name,
+               const gchar *detail,
+               GtkWidget   *widget)
+{
+  TestCase* self = g_new0 (TestCase, 1);
+
+  self->suite = suite;
+  self->name = name;
+  self->detail = detail;
+  self->widget = widget;
+
+  return self;
+}
+
+static void
+update_status (TestSuite *suite,
+               GtkWidget *child)
+{
+  const gchar *widget_name = gtk_widget_get_name (child);
+  const gchar *type_name = G_OBJECT_TYPE_NAME (child);
+  GString *status = g_string_new (type_name);
+
+  if (strcmp (widget_name, type_name))
+    g_string_append_printf (status, " (%s)", widget_name);
+
+  g_string_append_printf (status,
+                          "@%p:\nposition=%dx%d; size=%dx%d; requisition=%dx%d",
+                          child,
+                          child->allocation.x,
+                          child->allocation.y,
+                          child->allocation.width,
+                          child->allocation.height,
+                          child->requisition.width,
+                          child->requisition.height);
+
+  if (GTK_IS_EXTENDED_LAYOUT (child))
+    {
+      GtkExtendedLayout *layout = (GtkExtendedLayout*) child;
+
+      if (GTK_EXTENDED_LAYOUT_HAS_NATURAL_SIZE (child))
+        {
+          GtkRequisition requisition;
+
+          gtk_extended_layout_get_natural_size (layout, &requisition);
+
+          g_string_append_printf (status, "; natural-size: %dx%d",
+                                  requisition.width, requisition.height);
+        }
+
+      if (GTK_EXTENDED_LAYOUT_HAS_HEIGHT_FOR_WIDTH (child))
+        {
+          gint height = gtk_extended_layout_get_height_for_width (layout, child->allocation.width);
+
+          g_string_append_printf (status, "; height-for-%d: %d",
+                                  child->allocation.width, height);
+        }
+
+      if (GTK_EXTENDED_LAYOUT_HAS_WIDTH_FOR_HEIGHT (child))
+        {
+          gint width = gtk_extended_layout_get_width_for_height (layout, child->allocation.height);
+
+          g_string_append_printf (status, "; width-for-%d: %d",
+                                  child->allocation.height, width);
+        }
+
+      if (GTK_EXTENDED_LAYOUT_HAS_BASELINES (child))
+        {
+          gint *baselines = NULL;
+          gint num_baselines = 0;
+          gint i;
+
+          num_baselines =
+            gtk_extended_layout_get_baselines (GTK_EXTENDED_LAYOUT (child), 
+                                               &baselines);
+
+          for (i = 0; i < num_baselines; ++i)
+            {
+              g_string_append_printf (status, "%s%d",
+                                      i ? ", " : num_baselines > 1 ?
+                                      "; baselines: " : "; baseline: ",
+                                      baselines[i]);
+            }
+
+          g_free (baselines);
+        }
+
+      if (GTK_EXTENDED_LAYOUT_HAS_PADDING (child))
+        {
+          GtkBorder padding;
+
+          gtk_extended_layout_get_padding (GTK_EXTENDED_LAYOUT (child),
+                                           &padding);
+
+          g_string_append_printf (status, "; padding: %d/%d/%d/%d",
+                                  padding.top, padding.left,
+                                  padding.right, padding.bottom);
+        }
+    }
+
+  gtk_label_set_text (GTK_LABEL (suite->statusbar), status->str);
+  g_string_free (status, TRUE);
+}
+
+static void
+item_activate_cb (GtkWidget *item,
+                  gpointer   data)
+{
+  GtkWidget *widget = data;
+  TestCase *test;
+
+  test = g_object_get_data (G_OBJECT (widget), "test-case");
+  update_status (test->suite, widget);
+  test->suite->current = widget;
+
+  gtk_widget_queue_draw (test->widget);
+}
+
+static void
+test_case_append_guide (TestCase  *self,
+                        GtkWidget *widget,
+                        GuideType  type,
+                        gint       group)
+{
+  const gchar *widget_name;
+  const gchar *type_name;
+  gchar *item_label;
+  GtkWidget *popup;
+  GtkWidget *item;
+  Guide *guide;
+
+  guide = guide_new (widget, type, group);
+  self->guides = g_list_append (self->guides, guide);
+  g_object_set_data (G_OBJECT (widget), "test-case", self);
+
+  widget_name = gtk_widget_get_name (widget);
+  type_name = G_OBJECT_TYPE_NAME (widget);
+
+  item_label = g_strconcat (type_name,
+                            strcmp (widget_name, type_name) ? " (" : NULL,
+                            widget_name, ")", NULL);
+
+  item = gtk_menu_item_new_with_label (item_label);
+  popup = g_object_get_data (G_OBJECT (self->widget), "popup");
+
+  if (!popup)
+    {
+      popup = gtk_menu_new ();
+      g_object_set_data (G_OBJECT (self->widget), "popup", popup);
+    }
+
+  g_signal_connect (item, "activate", G_CALLBACK (item_activate_cb), widget);
+  gtk_menu_shell_append (GTK_MENU_SHELL (popup), item);
+  gtk_widget_show (item);
+
+  g_free (item_label);
+}
+
+static void
+append_natural_size_box (TestCase           *test,
+                         GtkWidget          *parent,
+                         gboolean            vertical,
+                         gboolean            table,
+                         gboolean            ellipses)
+{
+  GtkWidget *container = NULL;
+  GtkWidget *button, *label;
+
+  PangoEllipsizeMode ellipsize_mode;
+  gint i, j, k, l;
+
+  for (i = 0; i < (table ? 9 : 6); ++i)
+    {
+      ellipsize_mode = ellipses ?
+        PANGO_ELLIPSIZE_START + i/(table ? 3 : 2) : 
+        PANGO_ELLIPSIZE_NONE;
+
+      if (!i || (ellipses && 0 == i % (table ? 3 : 2)))
+        {
+          label = gtk_label_new (NULL);
+
+          switch(ellipsize_mode)
+            {
+              case PANGO_ELLIPSIZE_NONE:
+                gtk_label_set_markup (GTK_LABEL (label), "<b>No ellipses</b>");
+                break;
+              case PANGO_ELLIPSIZE_START:
+                gtk_label_set_markup (GTK_LABEL (label), "<b>Ellipses at start</b>");
+                break;
+              case PANGO_ELLIPSIZE_MIDDLE:
+                gtk_label_set_markup (GTK_LABEL (label), "<b>Ellipses in the middle</b>");
+                break;
+              case PANGO_ELLIPSIZE_END:
+                gtk_label_set_markup (GTK_LABEL (label), "<b>Ellipses at end</b>");
+                break;
+            }
+
+          if (vertical)
+            {
+              gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0);
+              gtk_label_set_angle (GTK_LABEL (label), 90);
+            }
+          else
+            {
+              gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+              gtk_label_set_angle (GTK_LABEL (label), 0);
+            }
+
+          gtk_box_pack_start (GTK_BOX (parent), label, FALSE, TRUE, 0);
+        }
+
+      if (table)
+        {
+          k = 1 + i / 3 + i % 3;
+
+          if (i < 7)
+            {
+              if (i < 6)
+                container = gtk_table_new (vertical ? k : 1,
+                                           vertical ? 1 : k, FALSE);
+              else
+                container = gtk_table_new (vertical ? k : 3,
+                                           vertical ? 3 : k, FALSE);
+
+              gtk_table_set_col_spacings (GTK_TABLE (container), 4);
+              gtk_table_set_row_spacings (GTK_TABLE (container), 4);
+            }
+        }
+      else if (vertical)
+        container = gtk_vbox_new (FALSE, 4);
+      else
+        container = gtk_hbox_new (FALSE, 4);
+
+      if (!gtk_widget_get_parent (container))
+        gtk_box_pack_start (GTK_BOX (parent), container, FALSE, TRUE, 0);
+
+      for (j = 0, l = i < 6 ? i / 3 : i - 6; j <= l; ++j)
+        {
+          label = gtk_label_new (NULL);
+          gtk_label_set_markup (GTK_LABEL (label), "<small>Small Button</small>");
+          gtk_label_set_angle (GTK_LABEL (label), vertical ? 90 : 0);
+          gtk_label_set_ellipsize (GTK_LABEL (label), ellipsize_mode);
+
+          button = gtk_button_new ();
+          set_widget_name (button, "small-%d-%d-%d", ellipses, i, j);
+          gtk_container_add (GTK_CONTAINER (button), label);
+
+          if (!table)
+            gtk_box_pack_start (GTK_BOX (container), button, FALSE, TRUE, 0);
+          else if (i < 6)
+            gtk_table_attach (GTK_TABLE (container), button,
+                              vertical ? 0 : j, vertical ? 1 : j + 1,
+                              vertical ? j : 0, vertical ? j + 1 : 1,
+                              GTK_FILL, GTK_FILL, 0, 0);
+          else
+            gtk_table_attach (GTK_TABLE (container), button,
+                              vertical ? i - 6 : j,
+                              vertical ? i - 5 : j < l ? j + 1 : 3,
+                              vertical ? j : i - 6, 
+                              vertical ? (j < l ? j + 1 : 3) : i - 5,
+                              GTK_FILL, GTK_FILL, 0, 0);
+
+          test_case_append_guide (test, button,
+                                  vertical ? GUIDE_EXTERIOUR_HORIZONTAL 
+                                           : GUIDE_EXTERIOUR_VERTICAL,
+                                  6 == i ? 3 : 7 == i && j ? 4 : j);
+        }
+
+      for (j = 0, l = (i < 6 ? i % 3 : 1); j < l; ++j)
+        {
+          label = gtk_label_new (NULL);
+          gtk_label_set_markup (GTK_LABEL (label), "<small>Large Button</small>");
+          gtk_label_set_angle (GTK_LABEL (label), vertical ? 90 : 0);
+
+          button = gtk_button_new ();
+          set_widget_name (button, "large-%d-%d-%d", ellipses, i, j);
+          gtk_container_add (GTK_CONTAINER (button), label);
+
+          if (table)
+            gtk_table_attach (GTK_TABLE (container), button,
+                              vertical ? MAX (0, i - 6) : i/3 + j + 1, 
+                              vertical ? MAX (1, i - 5) : i/3 + j + 2,
+                              vertical ? i/3 + j + 1 : MAX (0, i - 6), 
+                              vertical ? i/3 + j + 2 : MAX (1, i - 5),
+                              vertical ? GTK_FILL : GTK_FILL | GTK_EXPAND,
+                              vertical ? GTK_FILL | GTK_EXPAND : GTK_FILL,
+                              0, 0);
+          else
+            gtk_box_pack_start (GTK_BOX (container), button, TRUE, TRUE, 0);
+
+          test_case_append_guide (test, button, 
+                                  vertical ? GUIDE_EXTERIOUR_HORIZONTAL 
+                                           : GUIDE_EXTERIOUR_VERTICAL,
+                                  i < 6 ? 5 + i + j : 12);
+        }
+    }
+}
+
+static gboolean
+restore_paned (gpointer data)
+{
+  GtkPaned *paned;
+  GtkWidget *hint;
+  gint pos;
+
+
+  paned = GTK_PANED (data);
+  hint = gtk_paned_get_child2 (paned);
+  gtk_widget_set_sensitive (hint, TRUE);
+
+  pos = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (paned), "initial-position"));
+  gtk_paned_set_position (paned, pos);
+
+  return FALSE;
+}
+
+static gboolean
+shrink_paned_timeout (gpointer data)
+{
+  GtkPaned *paned;
+  gint pos;
+
+  paned = GTK_PANED (data);
+  pos = gtk_paned_get_position (paned);
+
+  if (pos < 20)
+    {
+      g_timeout_add (1000, restore_paned, paned);
+      return FALSE;
+    }
+
+  gtk_paned_set_position (paned, pos - 5);
+  return TRUE;
+}
+
+static void
+shrink_paned (GtkWidget *button,
+              gpointer   data)
+{
+  GtkPaned *paned;
+  GtkWidget *hint;
+
+  paned = GTK_PANED (data);
+  hint = gtk_paned_get_child2 (paned);
+  gtk_widget_set_sensitive (hint, FALSE);
+
+  g_object_set_data (G_OBJECT (paned), "initial-position",
+                     GINT_TO_POINTER (gtk_paned_get_position (paned)));
+  g_timeout_add (50, shrink_paned_timeout, paned);
+}
+
+static TestCase*
+natural_size_test_new (TestSuite *suite,
+                       gboolean   vertical,
+                       gboolean   table)
+{
+  GtkWidget *box, *paned, *hint, *button;
+  const gchar *detail;
+  TestCase *test;
+
+  if (vertical)
+    {
+      detail = table ? "GtkTable, vertical" : "GtkVBox";
+      hint = gtk_alignment_new (0.5, 1.0, 1.0, 0.0);
+      box = gtk_hbox_new (FALSE, 6);
+      paned = gtk_vpaned_new ();
+    }
+  else
+    {
+      detail = table ? "GtkTable, horizontal" : "GtkHBox";
+      hint = gtk_alignment_new (1.0, 0.5, 0.0, 1.0);
+      box = gtk_vbox_new (FALSE, 6);
+      paned = gtk_hpaned_new ();
+    }
+
+  test = test_case_new (suite, "Natural Size", detail, paned);
+  gtk_container_set_border_width (GTK_CONTAINER (test->widget), 6);
+
+  gtk_container_set_border_width (GTK_CONTAINER (box), 6);
+  gtk_paned_pack1 (GTK_PANED (test->widget), box, TRUE, TRUE);
+
+  append_natural_size_box (test, box, vertical, table, FALSE);
+  append_natural_size_box (test, box, vertical, table, TRUE);
+
+  button = gtk_button_new_with_label ("Shrink to check ellipsing");
+  g_signal_connect (button, "clicked", G_CALLBACK (shrink_paned), test->widget);
+
+  if (!vertical) 
+    gtk_label_set_angle (GTK_LABEL (GTK_BIN (button)->child), -90);
+
+  gtk_container_set_border_width (GTK_CONTAINER (hint), 6);
+  gtk_container_add (GTK_CONTAINER (hint), button);
+  gtk_paned_pack2 (GTK_PANED (test->widget), hint, FALSE, FALSE);
+
+  return test;
+}
+
+static void
+on_socket_realized (GtkWidget *widget,
+                    gpointer   data)
+{
+  gtk_socket_add_id (GTK_SOCKET (widget), GPOINTER_TO_INT (data)); 
+}
+
+static void
+on_xembed_socket_realized (GtkWidget *widget,
+                           gpointer   data)
+{
+  GdkNativeWindow plug_id = 0;
+  GError *error = NULL;
+  gchar **argv = data;
+  gint child_stdout;
+
+  if (g_spawn_async_with_pipes (NULL, argv, NULL, 0,
+                                NULL, NULL, NULL,
+                                NULL, &child_stdout, NULL,
+                                &error))
+    {
+      gchar *plug_str;
+      char buffer[32];
+      gint len;
+
+      len = read (child_stdout, buffer, sizeof (buffer) - 1);
+      close (child_stdout);
+
+      if (len > 0)
+        {
+          buffer[len] = '\0';
+          plug_id = atoi (buffer);
+        }
+
+      plug_str = g_strdup_printf ("plug-id=%d", plug_id);
+      g_print ("%s: %s\n", gtk_widget_get_name (widget), plug_str);
+      gtk_widget_set_tooltip_text (widget, plug_str);
+      g_free (plug_str);
+    }
+  else
+    {
+      GtkWidget *plug, *label;
+      gchar *error_message;
+
+      error_message = g_strdup_printf (
+        "Failed to create external plug:\n%s",
+        error ? error->message : "No details available.");
+
+      label = gtk_label_new (error_message);
+      g_warning (error_message);
+
+      g_free (error_message);
+      g_clear_error (&error);
+
+      if (argv[2] && g_str_equal (argv[2], "--vertical"))
+        gtk_label_set_angle (GTK_LABEL (label), 90);
+
+      plug = gtk_plug_new (0);
+      gtk_container_add (GTK_CONTAINER (plug), label);
+      gtk_widget_show_all (plug);
+
+      plug_id = gtk_plug_get_id (GTK_PLUG (plug));
+    }
+
+  gtk_socket_add_id (GTK_SOCKET (widget), plug_id); 
+}
+
+static void
+natural_size_test_misc_create_child (TestCase  *test,
+                                     GtkWidget *box,
+                                     gchar     *arg0,
+                                     gint       orientation,
+                                     gint       type)
+{
+  const gchar *type_names[] =
+  {
+    "align", "socket-gtkplug", "socket-xembed",
+    "cell-view", "tree-view", "tree-view-scrolled",
+  };
+
+  const gchar *numbers[] = 
+  { 
+    "First", "Second", "Third", "Fourth", "Fifth",
+    "Sixth", "Seventh", "Eighth", "Nineth", NULL
+  };
+
+  GtkWidget *label, *child, *view, *align, *plug;
+  GdkNativeWindow plug_id;
+
+  GtkListStore *store = NULL;
+  GtkTreeViewColumn *column;
+  GtkCellRenderer *cell;
+  GtkTreePath *path;
+
+  gchar **argv = NULL;
+  gint i, argc;
+
+  GdkColor color;
+
+  if (type >= 3)
+    {
+      store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+
+      for (i = 0; numbers[i] && (type > 4 || i < 2); ++i)
+        {
+          gchar *small = g_strdup_printf ("%s Small Cell", numbers[i]);
+          gchar *large = g_strdup_printf ("%s Large Cell", numbers[i]);
+
+          GtkTreeIter iter;
+
+          gtk_list_store_append (store, &iter);
+          gtk_list_store_set (store, &iter, 0, GTK_STOCK_ABOUT,
+                                            1, small, 2, large, -1);
+
+          g_free (large);
+          g_free (small);
+        }
+    }
+
+  for (i = 0; i < 2; ++i)
+    {
+      label = gtk_label_new ("Hello World");
+
+      gtk_label_set_ellipsize (GTK_LABEL (label),
+                               i ? PANGO_ELLIPSIZE_END : 
+                                   PANGO_ELLIPSIZE_NONE);
+
+      if (!orientation)
+        gtk_label_set_angle (GTK_LABEL (label), 90);
+
+      switch (type)
+        {
+          case 0:
+            child = label;
+            break;
+
+          case 1:
+            plug = gtk_plug_new (0);
+            plug_id = gtk_plug_get_id (GTK_PLUG (plug));
+            gtk_container_add (GTK_CONTAINER (plug), label);
+            gtk_widget_show_all (plug);
+
+            child = gtk_socket_new ();
+
+            g_signal_connect (child, "realize",
+                              G_CALLBACK (on_socket_realized),
+                              GINT_TO_POINTER (plug_id));
+            break;
+
+          case 2:
+            child = gtk_socket_new ();
+
+            argv = g_new0 (gchar*, 5);
+            argc = 0;
+
+            argv[argc++] = arg0;
+            argv[argc++] = "--action=create-plug";
+
+            if (!orientation)
+              argv[argc++] = "--vertical";
+            if (i)
+              argv[argc++] = "--ellipsize";
+
+            g_signal_connect_data (child, "realize",
+                                   G_CALLBACK (on_xembed_socket_realized),
+                                   argv, (GClosureNotify) g_free, 0);
+            break;
+
+          case 3:
+            child = gtk_cell_view_new ();
+
+            if (gdk_color_parse ("#ffc", &color))
+              gtk_cell_view_set_background_color (GTK_CELL_VIEW (child), &color);
+
+            cell = gtk_cell_renderer_pixbuf_new ();
+            gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (child), cell, FALSE);
+            gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (child), cell,
+                                            "stock-id", 0, NULL);
+
+            cell = gtk_cell_renderer_text_new ();
+            gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (child), cell, FALSE);
+            gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (child), cell,
+                                            "text", 1, NULL);
+
+            if (i)
+              g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+
+            cell = gtk_cell_renderer_text_new ();
+            gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (child), cell, TRUE);
+            gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (child), cell,
+                                            "text", 2, NULL);
+
+            gtk_cell_view_set_model (GTK_CELL_VIEW (child),
+                                     GTK_TREE_MODEL (store));
+
+            path = gtk_tree_path_new_from_indices (0, -1);
+            gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (child), path);
+            gtk_tree_path_free (path);
+
+            break;
+
+          case 4:
+          case 5:
+            view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
+            gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
+
+            cell = gtk_cell_renderer_pixbuf_new ();
+            column = gtk_tree_view_column_new_with_attributes (NULL, cell, "stock-id", 0, NULL);
+            gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
+
+            cell = gtk_cell_renderer_text_new ();
+            column = gtk_tree_view_column_new_with_attributes ("Bar", cell, "text", 1, NULL);
+            gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
+
+            if (i)
+              g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+
+            cell = gtk_cell_renderer_text_new ();
+            column = gtk_tree_view_column_new_with_attributes ("Foo", cell, "text", 2, NULL);
+            gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
+
+            if (type > 4)
+              {
+                child = gtk_scrolled_window_new (NULL, NULL);
+                gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (child),
+                                                     GTK_SHADOW_IN);
+                gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (child),
+                                                GTK_POLICY_NEVER,
+                                                GTK_POLICY_ALWAYS);
+                gtk_container_add (GTK_CONTAINER (child), view);
+              }
+            else
+              child = view;
+
+            break;
+
+          default:
+            continue;
+        }
+
+      align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+      gtk_box_pack_start (GTK_BOX (box), align, FALSE, TRUE, 0);
+      gtk_container_add (GTK_CONTAINER (align), child);
+
+      set_widget_name (child, "%s-%s-ellipsize-%s",
+                       orientation ? "horizontal" : "vertical",
+                       type_names[type], i ? "end" : "none");
+
+      test_case_append_guide (test, child, 
+                              orientation ? GUIDE_EXTERIOUR_VERTICAL
+                                          : GUIDE_EXTERIOUR_HORIZONTAL,
+                              type < 3 ? orientation : type - 1);
+    }
+}
+
+static TestCase*
+natural_size_test_misc_new (TestSuite *suite,
+                            gchar     *arg0)
+{
+  const gchar *captions[] = 
+  {
+    "<b>GtkAligment</b>",
+    "<b>GtkSocket with GtkPlug</b>",
+    "<b>GtkSocket with XEMBED</b>",
+    "<b>GtkCellView</b>",
+    "<b>GtkTreeView</b>",
+    "<b>GtkTreeView within GtkScrolledWindow</b>"
+  };
+
+  GtkWidget *box, *hpaned, *vpaned, *label;
+  gint orientation, type;
+  TestCase *test;
+
+  test = test_case_new (suite,  "Natural Size", "Various Widgets", 
+                        gtk_vbox_new (FALSE, 6));
+
+  gtk_container_set_border_width (GTK_CONTAINER (test->widget), 6);
+
+  vpaned = NULL; /* silence the gcc */
+
+  for (orientation = 0; orientation < 2; ++orientation)
+    {
+      label = gtk_label_new ("Move the handle to test\n"
+                             "natural size allocation");
+      gtk_misc_set_padding (GTK_MISC (label), 6, 6);
+
+      if (orientation)
+        {
+          gtk_label_set_angle (GTK_LABEL (label), 90);
+          gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+
+          hpaned = gtk_hpaned_new ();
+          box = gtk_hbox_new (FALSE, 6);
+
+          gtk_box_pack_start (GTK_BOX (box), label, FALSE, TRUE, 0);
+          gtk_box_pack_start (GTK_BOX (box), gtk_vseparator_new (), FALSE, TRUE, 0);
+          gtk_box_pack_start (GTK_BOX (box), vpaned, TRUE, TRUE, 0);
+
+          gtk_paned_pack2 (GTK_PANED (hpaned), box, TRUE, FALSE);
+
+          box = gtk_vbox_new (FALSE, 6);
+
+          gtk_paned_pack1 (GTK_PANED (hpaned), box, TRUE, TRUE);
+          gtk_box_pack_start (GTK_BOX (test->widget), hpaned, TRUE, TRUE, 0);
+        }
+      else
+        {
+          gtk_misc_set_alignment (GTK_MISC (label), 0.5, 1.0);
+
+          vpaned = gtk_vpaned_new ();
+          box = gtk_hbox_new (FALSE, 6);
+
+          gtk_paned_pack1 (GTK_PANED (vpaned), box, TRUE, TRUE);
+          gtk_paned_pack2 (GTK_PANED (vpaned), label, FALSE, FALSE);
+        }
+
+      for (type = 0; type < (orientation ? 6 : 3); ++type)
+        {
+          label = gtk_label_new (NULL);
+          gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+          gtk_label_set_markup (GTK_LABEL (label), captions[type]);
+          gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+          gtk_box_pack_start (GTK_BOX (box), label, FALSE, TRUE, 0);
+
+          if (!orientation)
+            gtk_label_set_angle (GTK_LABEL (label), 90);
+
+          natural_size_test_misc_create_child (test, box, arg0, orientation, type);
+        }
+    }
+
+  return test;
+}
+
+static TestCase*
+size_for_allocation_test_new (TestSuite *suite,
+                              gboolean   vertical,
+                              gboolean   table)
+{
+  GtkWidget *container, *child;
+  TestCase *test;
+  int i;
+
+  if (vertical)
+    {
+      test = test_case_new (suite,
+                            "Size for Allocation", 
+                            table ? "Height for Width, GtkTable"
+                                  : "Height for Width, GtkVBox",
+                            gtk_hpaned_new ());
+
+      if (table)
+        {
+          container = gtk_table_new (4, 1, FALSE);
+          gtk_table_set_orientation (GTK_TABLE (container), 
+                                     GTK_ORIENTATION_VERTICAL);
+        }
+      else
+        container = gtk_vbox_new (FALSE, 6);
+
+      child = gtk_label_new ("Move the handle to test\n"
+                             "height-for-width requests");
+
+      gtk_label_set_angle (GTK_LABEL (child), 90);
+    }
+  else
+    {
+      test = test_case_new (suite,
+                            "Size for Allocation", 
+                            table ? "Width for Height, GtkTable"
+                                  : "Width for Height, GtkHBox",
+                            gtk_vpaned_new ());
+
+      if (table)
+        {
+          container = gtk_table_new (1, 4, FALSE);
+          gtk_table_set_orientation (GTK_TABLE (container), 
+                                     GTK_ORIENTATION_HORIZONTAL);
+        }
+      else
+        container = gtk_hbox_new (FALSE, 6);
+
+      child = gtk_label_new ("Move the handle to test\n"
+                             "width-for-height requests");
+    }
+
+  gtk_container_set_border_width (GTK_CONTAINER (test->widget), 6);
+  gtk_container_set_border_width (GTK_CONTAINER (container), 6);
+  gtk_misc_set_padding (GTK_MISC (child), 6, 6);
+
+  gtk_paned_pack1 (GTK_PANED (test->widget), container, TRUE, FALSE);
+  gtk_paned_pack2 (GTK_PANED (test->widget), child, FALSE, FALSE);
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (2 != i)
+        {       
+          child = gtk_label_new (lorem_ipsum);
+          gtk_label_set_line_wrap (GTK_LABEL (child), TRUE);
+          gtk_label_set_use_markup (GTK_LABEL (child), TRUE);
+          test_case_append_guide (test, child, GUIDE_EXTERIOUR_BOTH, -1);
+          test_case_append_guide (test, child, GUIDE_INTERIOUR_BOTH, -1);
+
+          if (!table)
+            gtk_box_pack_start (GTK_BOX (container), child, FALSE, TRUE, 0);
+          else if (vertical)
+            gtk_table_attach (GTK_TABLE (container), child, 0, 1, i, i + 1,
+                              GTK_EXPAND|GTK_FILL, GTK_FILL, 0, 0);
+          else
+            gtk_table_attach (GTK_TABLE (container), child, i, i + 1, 0, 1, 
+                              GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0);
+
+          if (i > 0)
+            gtk_label_set_full_size (GTK_LABEL (child), TRUE);
+
+          if (i > 2)
+            gtk_label_set_angle (GTK_LABEL (child), vertical ? 180 : 270);
+          else if (!vertical)
+            gtk_label_set_angle (GTK_LABEL (child), 90);
+
+          set_widget_name (child, "%s-label-and-%g-degree",
+                           i > 0 ? "full-size" : "regular",
+                           gtk_label_get_angle (GTK_LABEL (child)));
+        }
+      else
+        {       
+          child = gtk_button_new ();
+          set_widget_name (child, "the-button");
+          gtk_container_add (GTK_CONTAINER (child),
+                             gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO,
+                                                       GTK_ICON_SIZE_DIALOG));
+
+          if (!table)
+            gtk_box_pack_start (GTK_BOX (container), child, TRUE, TRUE, 0);
+          else if (vertical)
+            gtk_table_attach (GTK_TABLE (container), child, 0, 1, i, i + 1,
+                              GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0);
+          else
+            gtk_table_attach (GTK_TABLE (container), child, i, i + 1, 0, 1,
+                              GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0);
+        }
+    }
+
+  return test;
+}
+
+static TestCase*
+baseline_test_new (TestSuite *suite)
+{
+  GtkWidget *child;
+  GtkWidget *view;
+  GtkWidget *label;
+
+  TestCase *test = test_case_new (suite, 
+                                  "Baseline Alignment", "Real-World Example",
+                                  gtk_table_new (3, 3, FALSE));
+
+  gtk_container_set_border_width (GTK_CONTAINER (test->widget), 12);
+  gtk_table_set_col_spacings (GTK_TABLE (test->widget), 6);
+  gtk_table_set_row_spacings (GTK_TABLE (test->widget), 6);
+
+  child = gtk_entry_new ();
+  gtk_entry_set_text (GTK_ENTRY (child), "Test...");
+  test_case_append_guide (test, child, GUIDE_BASELINE, 0);
+  gtk_table_attach (GTK_TABLE (test->widget), child, 1, 2, 0, 1,
+                    GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+
+  label = gtk_label_new_with_mnemonic ("_Title:");
+  test_case_append_guide (test, label, GUIDE_BASELINE, 0);
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+  gtk_label_set_mnemonic_widget  (GTK_LABEL (label), child);
+  gtk_table_attach (GTK_TABLE (test->widget), label, 0, 1, 0, 1,
+                    GTK_FILL, GTK_FILL, 0, 0);
+
+  label = gtk_label_new_with_mnemonic ("Notice on\ntwo rows.");
+  test_case_append_guide (test, label, GUIDE_BASELINE, 0);
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+  gtk_table_attach (GTK_TABLE (test->widget), label, 2, 3, 0, 2,
+                    GTK_FILL, GTK_FILL, 0, 0);
+
+  child = gtk_font_button_new ();
+  test_case_append_guide (test, child, GUIDE_BASELINE, 1);
+  gtk_table_attach (GTK_TABLE (test->widget), child, 1, 2, 1, 2,
+                    GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+
+  label = gtk_label_new_with_mnemonic ("_Font:");
+  test_case_append_guide (test, label, GUIDE_BASELINE, 1);
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+  gtk_label_set_mnemonic_widget  (GTK_LABEL (label), child);
+  gtk_table_attach (GTK_TABLE (test->widget), label, 0, 1, 1, 2,
+                    GTK_FILL, GTK_FILL, 0, 0);
+
+  view = gtk_text_view_new ();
+  gtk_widget_set_size_request (view, 200, -1);
+  gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view),
+                               GTK_WRAP_WORD);
+  test_case_append_guide (test, view, GUIDE_BASELINE, 2);
+  gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)),
+                            lorem_ipsum, -1);
+
+  child = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (child),
+                                  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (child),
+                                       GTK_SHADOW_IN);
+  gtk_container_add (GTK_CONTAINER (child), view);
+
+  gtk_table_attach (GTK_TABLE (test->widget), child, 1, 3, 2, 3,
+                    GTK_FILL | GTK_EXPAND,
+                    GTK_FILL | GTK_EXPAND,
+                    0, 0);
+
+  label = gtk_label_new_with_mnemonic ("_Comment:");
+  test_case_append_guide (test, label, GUIDE_BASELINE, 2);
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+  gtk_label_set_mnemonic_widget  (GTK_LABEL (label), child);
+  gtk_table_attach (GTK_TABLE (test->widget), label, 0, 1, 2, 3,
+                    GTK_FILL, GTK_FILL, 0, 0);
+
+  return test;
+}
+
+static TestCase*
+baseline_test_bin_new (TestSuite *suite)
+{
+  GtkWidget *bin;
+  GtkWidget *label;
+  GtkWidget *table;
+
+  int i, j;
+
+  const GType types[] = 
+    { 
+      GTK_TYPE_ALIGNMENT, GTK_TYPE_BUTTON, 
+      GTK_TYPE_EVENT_BOX, GTK_TYPE_FRAME, 
+      G_TYPE_INVALID
+    };
+
+  TestCase *test = test_case_new (suite, 
+                                  "Baseline Alignment", "Various GtkBins",
+                                  gtk_alignment_new (0.5, 0.5, 0.0, 0.0));
+
+  table = gtk_table_new (G_N_ELEMENTS (types) - 1, 
+                         G_N_ELEMENTS (captions),
+                         FALSE);
+
+  gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+  gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+  gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+  gtk_container_add (GTK_CONTAINER (test->widget), table);
+
+  for (i = 0; types[i]; ++i)
+    {
+      label = gtk_label_new (g_type_name (types[i]));
+      gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+
+      gtk_table_attach (GTK_TABLE (table), label, 0, 1,
+                        i, i + 1, GTK_FILL, GTK_FILL, 0, 0);
+
+      for (j = 0; captions[j]; ++j)
+        {
+          bin = g_object_new (types[i], NULL);
+          label = gtk_label_new (NULL);
+
+          gtk_label_set_markup (GTK_LABEL (label), captions[j]);
+          gtk_container_add (GTK_CONTAINER (bin), label);
+
+          test_case_append_guide (test, bin, GUIDE_BASELINE, i);
+          gtk_table_attach (GTK_TABLE (table), bin, j + 1, j + 2,
+                            i, i + 1, GTK_FILL, GTK_FILL, 0, 0);
+        }
+    }
+
+  return test;
+}
+
+static TestCase*
+baseline_test_hbox_new (TestSuite *suite,
+                        gboolean   buttons)
+{
+  GtkWidget *bin;
+  GtkWidget *child;
+  GtkWidget *table;
+  GtkWidget *hbox;
+
+  int i, j;
+
+  const gchar *names[] = 
+    {
+      "default", "baseline", "baseline and bottom-padding", 
+      "baseline and top-padding", "baseline and border-width",
+      NULL
+    };
+
+  TestCase *test = test_case_new (suite, "Baseline Alignment",
+                                  buttons ? "GtkHBox and Buttons" 
+                                          : "GtkHBox and Labels",
+                                  gtk_alignment_new (0.5, 0.5, 0.0, 0.0));
+
+  table = gtk_table_new (G_N_ELEMENTS (names) - 1, 
+                         G_N_ELEMENTS (captions),
+                         FALSE);
+
+  gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+  gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+  gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+  gtk_container_add (GTK_CONTAINER (test->widget), table);
+
+  for (i = 0; names[i]; ++i)
+    {
+      child = gtk_label_new (names[i]);
+      gtk_misc_set_alignment (GTK_MISC (child), 0.0, 0.5);
+  
+      hbox = gtk_hbox_new (FALSE, 6);
+      test_case_append_guide (test, hbox, GUIDE_EXTERIOUR_BOTH, -1);
+      set_widget_name (hbox, "hbox-%s", names[i]);
+
+      if (i > 0)
+        gtk_hbox_set_baseline_policy (GTK_HBOX (hbox), GTK_BASELINE_FIRST);
+
+      gtk_table_attach (GTK_TABLE (table), child,
+                        0, 1, i, i + 1,
+                        GTK_FILL, GTK_FILL, 0, 0);
+      gtk_table_attach (GTK_TABLE (table), hbox,
+                        1, G_N_ELEMENTS (captions), i, i + 1,
+                        GTK_FILL, GTK_FILL, 0, 0);
+
+      for (j = i ? -3 : 0; captions[MAX (0, j)]; ++j)
+        {
+          child = gtk_label_new (NULL);
+          gtk_label_set_markup (GTK_LABEL (child), captions[MAX (0, j)]);
+
+          if (buttons)
+            {
+              bin = gtk_button_new ();
+              gtk_container_add (GTK_CONTAINER (bin), child);
+              child = bin;
+            }
+
+          test_case_append_guide (test, child, GUIDE_BASELINE, i);
+
+          if (j < 0 && i > 1)
+            {
+              bin = gtk_alignment_new (0.5, 0.5, 0.0, (j + 3) * 0.5);
+
+              set_widget_name (bin, "align-%s-%s-%d",
+                               buttons ? "button" : "label",
+                               names[i], (j + 3) * 50);
+
+              switch (i) 
+                {
+                  case 2:
+                    gtk_alignment_set_padding (GTK_ALIGNMENT (bin), 0, 25, 0, 0);
+                    break;
+
+                  case 3:
+                    gtk_alignment_set_padding (GTK_ALIGNMENT (bin), 25, 0, 0, 0);
+                    break;
+
+                  case 4:
+                    gtk_container_set_border_width (GTK_CONTAINER (bin), 12);
+                    break;
+                }
+
+              gtk_container_add (GTK_CONTAINER (bin), child);
+              test_case_append_guide (test, bin, GUIDE_BASELINE, i);
+              child = bin;
+            }
+
+          gtk_box_pack_start (GTK_BOX (hbox), child, FALSE, TRUE, 0);
+        }
+    }
+
+  return test;
+}
+
+static gboolean
+get_extends (GtkWidget    *widget,
+             GtkWidget    *toplevel,
+             GdkRectangle *extends)
+{
+  *extends = widget->allocation;
+
+  return
+    GTK_WIDGET_VISIBLE (widget) &&
+    gtk_widget_translate_coordinates (widget, toplevel, 0, 0,
+                                      &extends->x, &extends->y);
+}
+
+static gboolean
+get_interiour (GtkWidget    *widget,
+                GtkWidget    *toplevel,
+               GdkRectangle *extends)
+{
+  if (GTK_IS_LABEL (widget))
+    {
+      PangoLayout *layout;
+      PangoRectangle log;
+      GtkLabel *label;
+
+      label = GTK_LABEL (widget);
+      layout = gtk_label_get_layout (label);
+      pango_layout_get_pixel_extents (layout, NULL, &log);
+      gtk_label_get_layout_offsets (label, &log.x, &log.y);
+
+      log.x -= toplevel->allocation.x;
+      log.y -= toplevel->allocation.y;
+
+      g_assert (sizeof log == sizeof *extends);
+      memcpy (extends, &log, sizeof *extends);
+    }
+
+  return FALSE;
+}
+
+static gint
+get_baseline_of_layout (PangoLayout *layout)
+{
+  PangoLayoutLine *line;
+  PangoRectangle log;
+
+  line = pango_layout_get_line_readonly (layout, 0);
+  pango_layout_line_get_pixel_extents (line, NULL, &log);
+  return PANGO_ASCENT (log);
+}
+
+static gint
+get_baselines_of_text_view (GtkTextView *view, gint **baselines)
+{
+  GtkTextBuffer *buffer = gtk_text_view_get_buffer (view);
+  GtkTextAttributes *attrs = gtk_text_view_get_default_attributes (view);
+  PangoContext *context = gtk_widget_get_pango_context (GTK_WIDGET (view));
+  PangoLayout *layout = pango_layout_new (context);
+
+  GtkTextIter start, end;
+  GdkRectangle bounds;
+  gchar *text;
+
+  gtk_text_buffer_get_start_iter (buffer, &start);
+  gtk_text_iter_get_attributes (&start, attrs);
+
+  end = start;
+  gtk_text_iter_forward_to_line_end (&end);
+  text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+  gtk_text_view_get_iter_location (view, &start, &bounds);
+
+  pango_layout_set_width (layout, PANGO_SCALE *
+                          GTK_WIDGET (view)->allocation.width);
+  pango_layout_set_font_description (layout, attrs->font);
+  pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
+  pango_layout_set_text (layout, text, -1);
+
+  gtk_text_view_buffer_to_window_coords (view, GTK_TEXT_WINDOW_TEXT,
+                                         0, bounds.y, NULL, &bounds.y);
+  bounds.y += get_baseline_of_layout (layout);
+
+  gtk_text_attributes_unref (attrs);
+  g_object_unref (layout);
+  g_free (text);
+
+  *baselines = g_new(gint, 1);
+  *baselines[0] = bounds.y;
+
+  return 1;
+}
+
+static gint
+get_baselines (GtkWidget *widget, gint **baselines)
+{
+  if (GTK_IS_EXTENDED_LAYOUT (widget) &&
+      GTK_EXTENDED_LAYOUT_HAS_BASELINES (widget))
+    return gtk_extended_layout_get_baselines (GTK_EXTENDED_LAYOUT (widget), baselines);
+  if (GTK_IS_TEXT_VIEW (widget))
+    return get_baselines_of_text_view (GTK_TEXT_VIEW (widget), baselines);
+
+  return -1;
+}
+
+static void
+draw_baselines (GdkDrawable  *drawable,
+                GdkGC        *gc,
+                GtkWidget    *toplevel,
+                GdkRectangle *extends,
+                gint          baseline)
+{
+  const gint x0 = toplevel->allocation.x;
+  const gint y0 = toplevel->allocation.y;
+  const gint cx = toplevel->allocation.width;
+
+  const gint xa = x0 + extends->x;
+  const gint xe = xa + extends->width - 1;
+  const gint ya = y0 + extends->y + baseline;
+
+  gdk_draw_line (drawable, gc, xa, ya - 5, xa, ya + 2);
+  gdk_draw_line (drawable, gc, xa - 5, ya, xe + 5, ya);
+  gdk_draw_line (drawable, gc, xe, ya - 5, xe, ya + 2);
+
+  gdk_gc_set_line_attributes (gc, 1, GDK_LINE_ON_OFF_DASH,
+                              GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
+
+  gdk_gc_set_dashes (gc, x0 % (dashes[0] + dashes[1]), dashes, 2);
+  gdk_draw_line (drawable, gc, x0, ya, xa - 5, ya);
+
+  gdk_gc_set_dashes (gc, (xe + 2) % (dashes[0] + dashes[1]), dashes, 2);
+  gdk_draw_line (drawable, gc, xe + 5, ya, x0 + cx - 1, ya);
+
+  gdk_gc_set_line_attributes (gc, 1, GDK_LINE_SOLID,
+                              GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
+}
+
+static void
+draw_extends (GdkDrawable  *drawable,
+              GdkGC        *gc,
+              GtkWidget    *toplevel,
+              GdkRectangle *extends)
+{
+  const gint x0 = toplevel->allocation.x;
+  const gint y0 = toplevel->allocation.y;
+  const gint cx = toplevel->allocation.width;
+  const gint cy = toplevel->allocation.height;
+
+  const gint xa = x0 + extends->x;
+  const gint xe = xa + extends->width - 1;
+
+  const gint ya = y0 + extends->y;
+  const gint ye = ya + extends->height - 1;
+
+  gdk_draw_line (drawable, gc, xa, y0, xa, y0 + cy - 1);
+  gdk_draw_line (drawable, gc, xe, y0, xe, y0 + cy - 1);
+  gdk_draw_line (drawable, gc, x0, ya, x0 + cx - 1, ya);
+  gdk_draw_line (drawable, gc, x0, ye, x0 + cx - 1, ye);
+}
+
+static gint
+test_case_eval_guide (const TestCase  *self,
+                      const Guide     *guide,
+                      GdkRectangle    *extends,
+                      gint           **baselines)
+{
+  gint num_baselines = -1;
+
+  if (get_extends (guide->widget, self->widget, extends))
+    {
+      *baselines = NULL;
+
+      switch (guide->type)
+        {
+          case GUIDE_BASELINE:
+            num_baselines = get_baselines (guide->widget, baselines);
+            break;
+
+          case GUIDE_INTERIOUR_BOTH:
+          case GUIDE_INTERIOUR_VERTICAL:
+          case GUIDE_INTERIOUR_HORIZONTAL:
+            get_interiour (guide->widget, self->widget, extends);
+            num_baselines = 0;
+            break;
+
+          case GUIDE_EXTERIOUR_BOTH:
+          case GUIDE_EXTERIOUR_VERTICAL:
+          case GUIDE_EXTERIOUR_HORIZONTAL:
+            num_baselines = 0;
+            break;
+        }
+    }
+
+  return num_baselines;
+}
+
+static gboolean
+guide_is_compatible (const Guide    *self,
+                     const Guide    *other)
+{
+  switch (self->type)
+    {
+      case GUIDE_BASELINE:
+        return
+          GUIDE_BASELINE == other->type;
+
+      case GUIDE_INTERIOUR_BOTH:
+      case GUIDE_EXTERIOUR_BOTH:
+        return
+          GUIDE_INTERIOUR_BOTH == other->type ||
+          GUIDE_EXTERIOUR_BOTH == other->type;
+
+      case GUIDE_INTERIOUR_VERTICAL:
+      case GUIDE_EXTERIOUR_VERTICAL:
+        return
+          GUIDE_INTERIOUR_VERTICAL == other->type ||
+          GUIDE_EXTERIOUR_VERTICAL == other->type;
+
+      case GUIDE_INTERIOUR_HORIZONTAL:
+      case GUIDE_EXTERIOUR_HORIZONTAL:
+        return
+          GUIDE_INTERIOUR_HORIZONTAL == other->type ||
+          GUIDE_EXTERIOUR_HORIZONTAL == other->type;
+    }
+
+  g_return_val_if_reached (FALSE);
+}
+
+static gboolean
+test_case_compare_guides (const TestCase *self,
+                          const Guide    *guide1,
+                          const Guide    *guide2)
+{
+  gint *baselines1 = NULL, *baselines2 = NULL;
+  GdkRectangle extends1, extends2;
+  gboolean equal = FALSE;
+
+  if (guide_is_compatible (guide1, guide2) &&
+      test_case_eval_guide (self, guide1, &extends1, &baselines1) >= 0 &&
+      test_case_eval_guide (self, guide2, &extends2, &baselines2) >= 0)
+    {
+      switch (guide1->type)
+        {
+          case GUIDE_BASELINE:
+            equal =
+              IS_VALID_BASELINE (*baselines1) &&
+              IS_VALID_BASELINE (*baselines2) &&
+              extends1.y + *baselines1 == extends2.y + *baselines2;
+            break;
+
+          case GUIDE_INTERIOUR_HORIZONTAL:
+          case GUIDE_EXTERIOUR_HORIZONTAL:
+            equal =
+              extends1.height == extends2.height &&
+              extends1.y == extends2.y;
+            break;
+
+          case GUIDE_INTERIOUR_VERTICAL:
+          case GUIDE_EXTERIOUR_VERTICAL:
+            equal =
+              extends1.width == extends2.width &&
+              extends1.x == extends2.x;
+            break;
+
+          case GUIDE_INTERIOUR_BOTH:
+          case GUIDE_EXTERIOUR_BOTH:
+            equal = !memcpy (&extends1, &extends2, sizeof extends1);
+            break;
+
+        }
+    }
+
+  g_free (baselines1);
+  g_free (baselines2);
+
+  return equal;
+}
+
+static const gchar*
+guide_type_get_color (GuideType type,
+                      gboolean  is_current)
+{
+  switch (type) 
+    {
+      case GUIDE_BASELINE: return is_current ? "#f00" : "#00f";
+      default: return is_current ? "#000" : "#fff";
+    }
+}
+
+static gboolean
+draw_guides (gpointer data)
+{
+  TestCase *test = data;
+  GdkDrawable *drawable;
+  const GList *iter;
+  gint iteration;
+
+  GdkGCValues values;
+  GdkGC *gc;
+
+  gboolean show_baselines;
+  gboolean show_interiour;
+  gboolean show_exteriour;
+
+  values.subwindow_mode = GDK_INCLUDE_INFERIORS;
+  drawable = test->widget->window;
+
+  gc = gdk_gc_new_with_values (drawable, &values,
+                               GDK_GC_SUBWINDOW);
+
+  gdk_gc_set_tile (gc, test->suite->tile);
+
+  show_baselines =
+    test->suite->baselines && gtk_toggle_button_get_active (
+    GTK_TOGGLE_BUTTON (test->suite->baselines));
+  show_interiour =
+    test->suite->interiour && gtk_toggle_button_get_active (
+    GTK_TOGGLE_BUTTON (test->suite->interiour));;
+  show_exteriour = 
+    test->suite->exteriour && gtk_toggle_button_get_active (
+    GTK_TOGGLE_BUTTON (test->suite->exteriour));;
+
+  for (iteration = 0; iteration < 3; ++iteration)
+    {
+      for (iter = test->guides; iter; iter = iter->next)
+        {
+          const Guide *guide = iter->data;
+          gboolean is_current = (guide->widget == test->suite->current);
+          GdkRectangle extends;
+          gint num_baselines;
+          gint *baselines;
+          gint i;
+
+          if (!is_current != !iteration)
+            continue;
+
+          if (is_current)
+            {
+              if (test->suite->timestamp >= 3)
+                continue;
+
+              if (1 == iteration)
+                {
+                  gdk_gc_set_fill (gc, GDK_TILED);
+                  gdk_gc_set_function (gc, GDK_OR);
+
+                  gdk_draw_rectangle (drawable, gc, TRUE, 
+                                      guide->widget->allocation.x,
+                                      guide->widget->allocation.y,
+                                      guide->widget->allocation.width,
+                                      guide->widget->allocation.height);
+
+                  gdk_gc_set_function (gc, GDK_COPY);
+                  gdk_gc_set_fill (gc, GDK_SOLID);
+
+                  continue;
+                }
+            }
+
+          gdk_color_parse (guide_type_get_color (guide->type, is_current),
+                           &values.foreground);
+
+          gdk_gc_set_rgb_fg_color (gc, &values.foreground);
+
+          num_baselines = test_case_eval_guide (test, guide, &extends, &baselines);
+
+          if (num_baselines > 0)
+            {
+              g_assert (NULL != baselines);
+
+              if (show_baselines)
+                for (i = 0; i < num_baselines; ++i)
+                  draw_baselines (drawable, gc, test->widget, &extends, baselines[i]);
+            }
+          else if (num_baselines > -1)
+            {
+              if ((show_interiour && (
+                   guide->type == GUIDE_INTERIOUR_VERTICAL ||
+                   guide->type == GUIDE_INTERIOUR_HORIZONTAL ||
+                   guide->type == GUIDE_INTERIOUR_BOTH)) ||
+                  (show_exteriour && (
+                   guide->type == GUIDE_EXTERIOUR_VERTICAL ||
+                   guide->type == GUIDE_EXTERIOUR_HORIZONTAL ||
+                   guide->type == GUIDE_EXTERIOUR_BOTH)))
+                draw_extends (drawable, gc, test->widget, &extends);
+            }
+
+          g_free (baselines);
+        }
+    }
+
+  g_object_unref (gc);
+  test->idle = 0;
+
+  return FALSE;
+}
+
+static gboolean          
+expose_cb (GtkWidget      *widget,
+           GdkEventExpose *event,
+           gpointer        data)
+{
+  TestCase *test = data;
+
+  if (0 == test->idle)
+    {
+      if (widget != test->widget)
+        gtk_widget_queue_draw (test->widget);
+
+      test->idle = g_idle_add (draw_guides, test);
+    }
+
+  return FALSE;
+}
+
+static void
+realize_cb (GtkWidget *widget,
+            gpointer   data)
+{
+  TestCase *test = data;
+
+  if (widget->window != test->widget->window)
+    g_signal_connect_after (widget, "expose-event",
+                            G_CALLBACK (expose_cb), test);
+}
+
+static void
+attach_sub_windows (GtkWidget *widget,
+                    gpointer   data)
+{
+  g_signal_connect_after (widget, "realize", G_CALLBACK (realize_cb), data);
+
+  if (GTK_IS_CONTAINER (widget))
+    gtk_container_forall (GTK_CONTAINER (widget), attach_sub_windows, data);
+}
+
+static void
+test_suite_insert_page (TestSuite   *self, 
+                        TestCase    *test,
+                        GtkWidget   *widget,
+                        const gchar *label)
+{
+  GtkTreeModel *model = GTK_TREE_MODEL (self->tests);
+  TestCase *prev = NULL;
+  GtkTreeIter iter;
+  gint i, n_rows;
+
+  if (!widget && test)
+    widget = test->widget;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  n_rows = gtk_tree_model_iter_n_children (model, NULL);
+
+  gtk_notebook_insert_page (GTK_NOTEBOOK (self->notebook), 
+                            widget, NULL, self->n_test_cases);
+
+  gtk_list_store_insert (self->tests, &iter, n_rows);
+
+  gtk_list_store_set (self->tests, &iter,
+                      TEST_COLUMN_LABEL, label, 
+                      TEST_COLUMN_SELECTED, NULL != test, 
+                      TEST_COLUMN_HAS_TEST_CASE, NULL != test, 
+                      TEST_COLUMN_PAGE_INDEX, self->n_test_cases,
+                      TEST_COLUMN_TEST_CASE, test,
+                      -1);
+
+  for (i = n_rows - 1; i >= 0 && NULL == prev &&
+       gtk_tree_model_iter_nth_child (model, &iter, NULL, i);
+       --i)
+    gtk_tree_model_get (GTK_TREE_MODEL (self->tests), &iter,
+                        TEST_COLUMN_TEST_CASE, &prev, 
+                        -1);
+
+  if (NULL == test || (prev && strcmp (test->name, prev->name)))
+    {
+      gtk_list_store_insert (self->tests, &iter, n_rows);
+      gtk_list_store_set (self->tests, &iter,
+                          TEST_COLUMN_HAS_TEST_CASE, FALSE,
+                          TEST_COLUMN_PAGE_INDEX, -1,
+                          -1);
+    }
+
+  if (test)
+    ++self->n_test_cases;
+}
+
+static void
+test_suite_append (TestSuite *self,
+                   TestCase  *test)
+{
+  GString *markup = g_string_new (test->name);
+
+  g_string_printf (markup, "<b>%s</b>", test->name);
+
+  if (test->detail)
+    g_string_append_printf (markup, "\n<small>%s</small>", test->detail);
+
+  test_suite_insert_page (self, test, NULL, markup->str);
+  g_string_free (markup, TRUE);
+
+  g_signal_connect_after (test->widget, "expose-event",
+                          G_CALLBACK (expose_cb), test);
+  g_signal_connect_after (test->widget, "realize",
+                          G_CALLBACK (realize_cb), test);
+  g_object_set_data_full (G_OBJECT(test->widget), 
+                          "test-case", test, g_free);
+
+  gtk_container_forall (GTK_CONTAINER (test->widget),
+                        attach_sub_windows, test);
+}
+
+static void 
+realize_notebook_cb (GtkWidget *widget,
+                     gpointer   data)
+{
+  TestSuite *suite = data;
+
+  suite->tile =
+    gdk_pixmap_colormap_create_from_xpm_d (
+    suite->notebook->window, NULL, NULL, NULL,
+    mask_xpm);
+}
+
+static void
+test_suite_free (TestSuite* self)
+{       
+  g_object_unref (self->tile);
+  g_free (self);
+}
+
+static void
+test_suite_start (TestSuite *self)
+{
+  if (0 == self->level++)
+    {
+      g_print ("\033[1mStarting test suite.\033[0m\n");
+      gtk_tree_store_clear (self->results);
+    }
+}
+
+static void
+test_suite_stop (TestSuite *self)
+{
+  if (0 == --self->level)
+    {
+      gtk_notebook_set_current_page (GTK_NOTEBOOK (self->notebook),
+                                     self->n_test_cases);
+      g_print ("\033[1mTest suite stopped.\033[0m\n");
+    }
+}
+
+static const gchar*
+test_result_to_string (TestResult result)
+{
+  switch (result)
+  {
+    case TEST_RESULT_NONE: return NULL;
+    case TEST_RESULT_SUCCESS: return "SUCCESS";
+    case TEST_RESULT_FAILURE: return "FAILURE";
+  }
+
+  g_return_val_if_reached (NULL);
+}
+
+static const gchar*
+test_result_to_icon (TestResult result)
+{
+  switch (result)
+  {
+    case TEST_RESULT_NONE: return GTK_STOCK_EXECUTE;
+    case TEST_RESULT_SUCCESS: return GTK_STOCK_OK;
+    case TEST_RESULT_FAILURE: return GTK_STOCK_DIALOG_ERROR;
+  }
+
+  g_return_val_if_reached (NULL);
+}
+
+static void
+test_suite_report (TestSuite   *self,
+                   const gchar *message,
+                   gint         group,
+                   TestResult   result)
+{
+  const gchar *text = test_result_to_string (result);
+  const gchar *icon = test_result_to_icon (result);
+
+  GtkTreeIter iter;
+
+  if (message)
+    {
+      PangoWeight weight = PANGO_WEIGHT_NORMAL;
+      GtkTreePath *path;
+
+      if (TEST_RESULT_NONE != result)
+        {
+          g_print ("   - %s: %s\n", message, text);
+          gtk_tree_store_append (self->results, &iter, &self->parent);
+        }
+      else if (group < 0)
+        {
+          g_print ("\033[1mTesting: %s\033[0m\n", message);
+          gtk_tree_store_append (self->results, &self->parent, NULL);
+          weight = PANGO_WEIGHT_BOLD;
+          iter = self->parent;
+
+        }
+      else
+        {
+          if (gtk_tree_store_iter_depth (self->results, &self->parent) < 1 ||
+              !gtk_tree_model_iter_parent (GTK_TREE_MODEL (self->results), &iter, &self->parent))
+              iter = self->parent;
+
+          g_print (" * %s\n", message);
+          gtk_tree_store_append (self->results, &self->parent, &iter);
+          iter = self->parent;
+        }
+
+      gtk_tree_store_set (self->results, &iter, 
+                          RESULT_COLUMN_MESSAGE, message, 
+                          RESULT_COLUMN_WEIGHT, weight, 
+                          RESULT_COLUMN_RESULT, text, 
+                          RESULT_COLUMN_ICON, icon,
+                          -1);
+
+      if (TEST_RESULT_FAILURE == result)
+        {
+          path = gtk_tree_model_get_path (GTK_TREE_MODEL (self->results), &iter);
+          gtk_tree_view_expand_to_path (GTK_TREE_VIEW (self->results_view), path);
+          gtk_tree_path_free (path);
+        }
+    }
+  else
+    {
+      if (-1 == group && gtk_tree_model_iter_parent (
+          GTK_TREE_MODEL (self->results), &iter, &self->parent))
+        self->parent = iter;
+
+      gtk_tree_store_set (self->results, &self->parent,
+                          RESULT_COLUMN_RESULT, text, 
+                          RESULT_COLUMN_ICON, icon,
+                          -1);
+    }
+}
+
+static void
+test_suite_run (TestSuite *self,
+                gint       index)
+{
+  GtkNotebook *notebook;
+  GtkWidget *page;
+  TestCase *test;
+
+  notebook = GTK_NOTEBOOK (self->notebook);
+
+  if (-1 == index)
+    index = gtk_notebook_get_current_page (notebook);
+
+  page = gtk_notebook_get_nth_page (notebook, index);
+  test = g_object_get_data (G_OBJECT (page), "test-case");
+
+  if (NULL != test)
+    {
+      TestResult test_result = TEST_RESULT_SUCCESS;
+      gint last_group = -1;
+      gchar *message;
+      GList *oiter;
+      gint o, group;
+
+      message = test->detail ?
+        g_strdup_printf ("%s (%s)", test->name, test->detail) :
+        g_strdup (test->name);
+
+      test_suite_start (self);
+      test_suite_report (self, message, -1, TEST_RESULT_NONE);
+
+      g_free (message);
+
+      for (oiter = test->guides; oiter; oiter = oiter->next)
+        last_group = MAX (last_group, ((const Guide*)oiter->data)->group);
+
+      for (group = 0; group <= last_group; ++group)
+        {
+          const Guide *oguide;
+
+          for (o = 0, oiter = test->guides; oiter; ++o, oiter = oiter->next)
+            {
+              oguide = oiter->data;
+
+              if (oguide->group == group)
+                break;
+            }
+
+          if (oiter)
+            {
+              TestResult group_result = TEST_RESULT_SUCCESS;
+              const gchar *widget_name;
+              const gchar *type_name;
+
+              GList *iiter;
+              gint i;
+
+              widget_name = gtk_widget_get_name (oguide->widget);
+              type_name = G_OBJECT_TYPE_NAME (oguide->widget);
+
+              message = g_strdup_printf (
+                "Group %d, Guide %d (%s%s%s)",
+                oguide->group, o, type_name,
+                strcmp (type_name, widget_name) ? ": " : "",
+                strcmp (type_name, widget_name) ? widget_name : "");
+
+              test_suite_report (self, message, oguide->group, TEST_RESULT_NONE);
+              g_free (message);
+
+              for(i = 0, iiter = test->guides; iiter; ++i, iiter = iiter->next)
+                {
+                  const Guide *iguide = iiter->data;
+
+                  if (iguide->group == oguide->group)
+                    {
+                      widget_name = gtk_widget_get_name (iguide->widget);
+                      type_name = G_OBJECT_TYPE_NAME (iguide->widget);
+
+                      message = g_strdup_printf (
+                        "Guide %d (%s%s%s)", i, type_name,
+                        strcmp (type_name, widget_name) ? ": " : "",
+                        strcmp (type_name, widget_name) ? widget_name : "");
+
+                      if (test_case_compare_guides (test, oguide, iguide))
+                        {
+                          test_suite_report (self, message, oguide->group, TEST_RESULT_SUCCESS);
+                        }
+                      else
+                        {
+                          test_suite_report (self, message, oguide->group, TEST_RESULT_FAILURE);
+                          group_result = TEST_RESULT_FAILURE;
+                          test_result = TEST_RESULT_FAILURE;
+                        }
+
+                      g_free (message);
+                    }
+                }
+
+              test_suite_report (self, NULL, oguide->group, group_result);
+            }
+        }
+
+      test_suite_report (self, NULL, -1, test_result);
+      test_suite_stop (self);
+    }
+}
+
+static void
+test_current_cb (GtkWidget *widget,
+                 gpointer  data)
+{
+  TestSuite *suite = data;
+  test_suite_run (suite, -1);
+}
+
+static void
+test_suite_show_and_run_test (TestSuite *self,
+                              gint       page)
+{
+  GTimer *timer = g_timer_new ();
+
+  gtk_notebook_set_current_page (GTK_NOTEBOOK (self->notebook), page);
+  g_timer_start (timer);
+
+  while (g_timer_elapsed (timer, NULL) < 0.3 &&
+         !gtk_main_iteration_do (FALSE))
+    {
+      if (!gtk_events_pending ())
+        g_usleep (500);
+    }
+
+  test_suite_run (self, -1);
+  g_timer_destroy (timer);
+}
+
+static void
+test_selected_cb (GtkWidget *widget,
+                  gpointer   data)
+{
+  TestSuite *suite = data;
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+
+  model = GTK_TREE_MODEL (suite->tests);
+  test_suite_start (suite);
+
+  if (gtk_tree_model_get_iter_first (model, &iter))
+    {
+      do
+        {
+          gboolean selected = FALSE;
+          gint page_index = -1;
+
+          gtk_tree_model_get (model, &iter, 
+                              TEST_COLUMN_SELECTED, &selected,
+                              TEST_COLUMN_PAGE_INDEX, &page_index,
+                              -1);
+
+          if (page_index >= 0 && selected)
+            test_suite_show_and_run_test (suite, page_index);
+        }
+      while (gtk_tree_model_iter_next (model, &iter));
+    }
+
+  test_suite_stop (suite);
+}
+
+static void
+test_all_cb (GtkWidget *widget,
+             gpointer  data)
+{
+  TestSuite *suite = data;
+  gint i;
+
+  test_suite_start (suite);
+
+  for (i = 0; i < suite->n_test_cases; ++i)
+    test_suite_show_and_run_test (suite, i);
+
+  test_suite_stop (suite);
+}
+
+static void
+switch_page_cb (GtkNotebook     *notebook,
+                GtkNotebookPage *page,
+                gint             index,
+                gpointer         data)
+{
+  TestSuite *suite = data;
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+  gint page_index;
+
+  gtk_widget_set_sensitive (suite->test_current_button,
+                            index < suite->n_test_cases);
+
+  model = GTK_TREE_MODEL (suite->tests);
+
+  if (gtk_tree_model_get_iter_first (model, &iter))
+    {
+      do
+        {
+          gtk_tree_model_get (model, &iter, 
+                              TEST_COLUMN_PAGE_INDEX, &page_index,
+                              -1);
+
+          if (page_index == index)
+            {
+              gtk_tree_selection_select_iter (suite->selection, &iter);
+              break;
+            }
+        }
+      while (gtk_tree_model_iter_next (model, &iter));
+    }
+}
+
+static GtkWidget*
+find_widget_at_position (GtkWidget *widget,
+                         gint       x,
+                         gint       y)
+{
+  if (x < 0 || x >= widget->allocation.width ||
+      y < 0 || y >= widget->allocation.height)
+    return NULL;
+
+  if (GTK_IS_CONTAINER (widget))
+    {
+      GtkWidget *child;
+      GList *children;
+      GList *iter;
+
+      gint rx, ry;
+
+      children = gtk_container_get_children (GTK_CONTAINER (widget));
+
+      for (iter = children; iter; iter = iter->next)
+        {
+          gtk_widget_translate_coordinates (widget, iter->data, x, y, &rx, &ry);
+          child = find_widget_at_position (iter->data, rx, ry);
+
+          if (child)
+            {
+              widget = child;
+              break;
+            }
+        }
+
+      g_list_free (children);
+    }
+
+  return widget;
+}
+
+static void
+queue_redraw (GtkWidget *page, 
+              GtkWidget *child)
+{
+  gint x, y;
+
+  gtk_widget_translate_coordinates (child, page, 0, 0, &x, &y);
+
+  gtk_widget_queue_draw_area (page,
+                              page->allocation.x,
+                              page->allocation.y + y,
+                              page->allocation.width,
+                              child->allocation.height);
+  gtk_widget_queue_draw_area (page,
+                              page->allocation.x + x,
+                              page->allocation.y,
+                              child->allocation.width,
+                              page->allocation.height);
+}
+
+static gboolean           
+watch_pointer_cb (gpointer data)
+{
+  TestSuite *suite = data;
+  GtkWidget *prev = suite->current;
+  TestCase *test = NULL;
+
+  gboolean dirty;
+  GtkWidget *page;
+  GtkWidget *child;
+  gint i, x, y;
+
+  i = gtk_notebook_get_current_page (GTK_NOTEBOOK (suite->notebook));
+  page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (suite->notebook), i);
+
+  gtk_widget_get_pointer (page, &x, &y);
+  child = find_widget_at_position (page, x, y);
+
+  while (child)
+    {
+      test = g_object_get_data (G_OBJECT(child), "test-case");
+
+      if (test)
+        break;
+
+      child = gtk_widget_get_parent (child);
+    }
+
+  dirty = suite->current && !(suite->timestamp % 3);
+  suite->timestamp = (suite->timestamp + 1) % 6;
+
+  if (!test)
+    {
+      dirty = (NULL != suite->current);
+
+      if (suite->current)
+        gtk_label_set_text (GTK_LABEL (suite->statusbar),
+                            "No widget selected.\n");
+
+      suite->current = NULL;
+    }
+  else if (child != suite->hover)
+    {
+      if (child != suite->current)
+        update_status (suite, child);
+
+      suite->current = child;
+      suite->hover = child;
+      dirty = TRUE;
+    }
+
+  if (dirty)
+    {
+      if (suite->current)
+        {
+          if (prev && prev != suite->current)
+            queue_redraw (page, prev);
+
+          queue_redraw (page, suite->current);
+        }
+      else
+        {
+          gtk_widget_queue_draw (page);
+        }
+    }
+
+  return TRUE;
+}
+
+static gboolean
+button_press_event_cb (GtkWidget      *widget,
+                       GdkEventButton *event,
+                       gpointer        data)
+{
+  TestSuite *suite = data;
+  GtkWidget *popup = NULL;
+  GtkWidget *page;
+  gint i;
+
+  if (3 != event->button)
+    return FALSE;
+
+  i = gtk_notebook_get_current_page (GTK_NOTEBOOK (suite->notebook));
+  page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (suite->notebook), i);
+  popup = g_object_get_data (G_OBJECT (page), "popup");
+
+  gtk_menu_popup (GTK_MENU (popup),
+                  NULL, NULL, NULL, NULL,
+                  event->button, event->time);
+
+  return TRUE;
+}
+
+static void
+test_suite_setup_results_page (TestSuite *self)
+{
+  GtkTreeViewColumn *column;
+  GtkCellRenderer *cell;
+  GtkWidget *scroller;
+
+  self->results = gtk_tree_store_new (RESULT_COLUNN_COUNT,
+                                      G_TYPE_STRING, PANGO_TYPE_WEIGHT,
+                                      G_TYPE_STRING, G_TYPE_STRING);
+
+  self->results_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (self->results));
+  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (self->results_view), FALSE);
+
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_expand (column, TRUE);
+  gtk_tree_view_append_column (GTK_TREE_VIEW (self->results_view), column);
+
+  cell = gtk_cell_renderer_pixbuf_new ();
+  gtk_tree_view_column_pack_start (column, cell, FALSE);
+  gtk_tree_view_column_set_attributes (column, cell, 
+                                       "icon-name", RESULT_COLUMN_ICON, NULL);
+
+  cell = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (column, cell, TRUE);
+  gtk_tree_view_column_set_attributes (column, cell, 
+                                       "text", RESULT_COLUMN_MESSAGE,
+                                       "weight", RESULT_COLUMN_WEIGHT, NULL);
+
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_expand (column, FALSE);
+  gtk_tree_view_append_column (GTK_TREE_VIEW (self->results_view), column);
+
+  cell = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (column, cell, TRUE);
+  gtk_tree_view_column_set_attributes (column, cell, 
+                                       "text", RESULT_COLUMN_RESULT, NULL);
+
+  scroller = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller),
+                                  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller),
+                                       GTK_SHADOW_IN);
+  gtk_container_set_border_width (GTK_CONTAINER (scroller), 12);
+  gtk_container_add (GTK_CONTAINER (scroller), self->results_view);
+
+  test_suite_insert_page (self, NULL, scroller, "<b>Test Results</b>");
+
+  g_signal_connect (self->notebook, "realize",
+                    G_CALLBACK (realize_notebook_cb), self);
+}
+
+static gboolean
+tests_is_separator (GtkTreeModel *model,
+                    GtkTreeIter  *iter,
+                    gpointer      data)
+{
+  gchar *label;
+
+  gtk_tree_model_get (model, iter, TEST_COLUMN_LABEL, &label, -1);
+  g_free (label);
+
+  return (NULL == label);
+}
+
+static void
+test_case_toggled (GtkCellRendererToggle *cell,
+                   gchar                 *path,
+                   gpointer               data)
+{
+  GtkTreeIter iter;
+
+  if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (data), &iter, path))
+    gtk_list_store_set (GTK_LIST_STORE (data), &iter, TEST_COLUMN_SELECTED,
+                        !gtk_cell_renderer_toggle_get_active (cell),
+                        -1);
+}
+
+static void
+selection_changed (GtkTreeSelection *selection,
+                   gpointer          data)
+{
+  TestSuite *suite = data;
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+  gint page_index;
+
+  if (gtk_tree_selection_get_selected (selection, &model, &iter))
+    {
+      gtk_tree_model_get (model, &iter, 
+                          TEST_COLUMN_PAGE_INDEX, &page_index, 
+                          -1);
+
+      if (page_index >= 0)
+        gtk_notebook_set_current_page (GTK_NOTEBOOK (suite->notebook),
+                                       page_index);
+    }
+}
+
+static void
+test_suite_setup_ui (TestSuite *self)
+{
+  GtkWidget *table, *actions, *button, *align;
+  GtkWidget *view, *scrolled;
+
+  GtkTreeViewColumn *column;
+  GtkCellRenderer *cell;
+
+  self->tests = gtk_list_store_new (TEST_COLUMN_COUNT, 
+                                    G_TYPE_STRING, G_TYPE_BOOLEAN,
+                                    G_TYPE_POINTER, G_TYPE_BOOLEAN, 
+                                    G_TYPE_INT);
+
+  view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (self->tests));
+  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
+  gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (view), tests_is_separator, NULL, NULL);
+
+  self->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+  gtk_tree_selection_set_mode (self->selection, GTK_SELECTION_BROWSE);
+
+  g_signal_connect (self->selection, "changed", G_CALLBACK (selection_changed), self);
+
+  column = gtk_tree_view_column_new ();
+  cell = gtk_cell_renderer_toggle_new ();
+  gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
+  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (column), cell, FALSE);
+  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (column), cell,
+                                  "active", TEST_COLUMN_SELECTED, 
+                                  "activatable", TEST_COLUMN_HAS_TEST_CASE,
+                                  "visible", TEST_COLUMN_HAS_TEST_CASE,
+                                  NULL);
+
+  g_signal_connect (cell, "toggled", G_CALLBACK (test_case_toggled), self->tests);
+
+  column = gtk_tree_view_column_new ();
+  cell = gtk_cell_renderer_text_new ();
+  gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
+  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (column), cell, TRUE);
+  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (column), cell,
+                                  "markup", TEST_COLUMN_LABEL, NULL);
+
+  scrolled = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
+                                       GTK_SHADOW_IN);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
+                                  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+  gtk_container_add (GTK_CONTAINER (scrolled), view);
+
+  self->notebook = gtk_notebook_new ();
+  gtk_notebook_set_show_tabs (GTK_NOTEBOOK (self->notebook), FALSE);
+
+  actions = gtk_hbox_new (TRUE, 12);
+
+  align = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
+  gtk_container_add (GTK_CONTAINER (align), actions);
+
+  button = gtk_button_new_with_mnemonic ("Test _Current Page");
+  g_signal_connect (button, "clicked", G_CALLBACK (test_current_cb), self);
+  gtk_box_pack_start (GTK_BOX (actions), button, FALSE, TRUE, 0);
+
+  self->test_current_button = button;
+  g_signal_connect (self->notebook, "switch-page", G_CALLBACK (switch_page_cb), self);
+
+  button = gtk_button_new_with_mnemonic ("Test _Selected Pages");
+  g_signal_connect (button, "clicked", G_CALLBACK (test_selected_cb), self);
+  gtk_box_pack_start (GTK_BOX (actions), button, FALSE, TRUE, 0);
+
+  button = gtk_button_new_with_mnemonic ("Test _All Pages");
+  g_signal_connect (button, "clicked", G_CALLBACK (test_all_cb), self);
+  gtk_box_pack_start (GTK_BOX (actions), button, FALSE, TRUE, 0);
+
+  actions = gtk_hbox_new (FALSE, 12);
+  gtk_box_pack_end (GTK_BOX (actions), align, TRUE, TRUE, 0);
+
+  gtk_box_pack_start (GTK_BOX (actions),
+                      gtk_label_new ("Guides:"),
+                      FALSE, TRUE, 0);
+
+  self->baselines = gtk_check_button_new_with_mnemonic ("_Baselines");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->baselines), TRUE);
+  gtk_box_pack_start (GTK_BOX (actions), self->baselines, FALSE, TRUE, 0);
+
+  g_signal_connect_swapped (self->baselines, "toggled", 
+                            G_CALLBACK (gtk_widget_queue_draw),
+                            self->notebook);
+
+  self->interiour = gtk_check_button_new_with_mnemonic ("_Interiours");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->interiour), TRUE);
+  gtk_box_pack_start (GTK_BOX (actions), self->interiour, FALSE, TRUE, 0);
+
+  g_signal_connect_swapped (self->interiour, "toggled", 
+                            G_CALLBACK (gtk_widget_queue_draw),
+                            self->notebook);
+  
+  self->exteriour = gtk_check_button_new_with_mnemonic ("_Exteriours");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->exteriour), TRUE);
+  gtk_box_pack_start (GTK_BOX (actions), self->exteriour, FALSE, TRUE, 0);
+  
+  g_signal_connect_swapped (self->exteriour, "toggled", 
+                            G_CALLBACK (gtk_widget_queue_draw),
+                            self->notebook);
+
+  self->statusbar = gtk_label_new ("No widget selected.\n");
+  gtk_misc_set_alignment (GTK_MISC (self->statusbar), 0.0, 0.5);
+  gtk_label_set_ellipsize (GTK_LABEL (self->statusbar),
+                           PANGO_ELLIPSIZE_END);
+
+  table = gtk_table_new (3, 2, FALSE);
+
+  gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+  gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+  gtk_container_set_border_width (GTK_CONTAINER (table), 6);
+
+  gtk_table_attach (GTK_TABLE (table), actions, 0, 2, 0, 1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+  gtk_table_attach (GTK_TABLE (table), scrolled, 0, 1, 1, 2, GTK_FILL, GTK_FILL | GTK_EXPAND, 0, 0);
+  gtk_table_attach (GTK_TABLE (table), self->notebook, 1, 2, 1, 2, GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
+  gtk_table_attach (GTK_TABLE (table), self->statusbar, 0, 2, 2, 3, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+
+  self->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+  g_object_connect (self->window, 
+    "signal::button-press-event", button_press_event_cb, self,
+    "signal::destroy", gtk_main_quit, NULL,
+    NULL);
+
+  g_timeout_add (200, watch_pointer_cb, self);
+
+  gtk_window_set_title (GTK_WINDOW (self->window), "Testing GtkExtendedLayout");
+  gtk_widget_add_events (self->window, GDK_BUTTON_PRESS_MASK);
+  gtk_container_add (GTK_CONTAINER (self->window), table);
+  gtk_widget_grab_focus (view);
+}
+
+static TestSuite*
+test_suite_new (gchar *arg0)
+{       
+  TestSuite* self = g_new0 (TestSuite, 1);
+
+  test_suite_setup_ui (self);
+
+  test_suite_append (self, natural_size_test_new (self, FALSE, FALSE));
+  test_suite_append (self, natural_size_test_new (self, TRUE, FALSE));
+  test_suite_append (self, natural_size_test_new (self, FALSE, TRUE));
+  test_suite_append (self, natural_size_test_new (self, TRUE, TRUE));
+  test_suite_append (self, natural_size_test_misc_new (self, arg0));
+  test_suite_append (self, size_for_allocation_test_new (self, TRUE, FALSE));
+  test_suite_append (self, size_for_allocation_test_new (self, FALSE, FALSE));
+  test_suite_append (self, size_for_allocation_test_new (self, TRUE, TRUE));
+  test_suite_append (self, size_for_allocation_test_new (self, FALSE, TRUE));
+  test_suite_append (self, baseline_test_new (self));
+  test_suite_append (self, baseline_test_bin_new (self));
+  test_suite_append (self, baseline_test_hbox_new (self, FALSE));
+  test_suite_append (self, baseline_test_hbox_new (self, TRUE));
+
+  test_suite_setup_results_page (self);
+
+  return self;
+}
+
+static gboolean
+on_embedding_timeout (gpointer data)
+{
+  GdkNativeWindow plug_id = GPOINTER_TO_INT (data);
+  g_printerr ("Embedding timeout expired for plug %d. Aborting.\n", plug_id);
+  gtk_main_quit ();
+  return FALSE;
+}
+
+static void
+on_embedded (GtkWidget *widget, 
+             gpointer   data)
+{
+  g_source_remove (GPOINTER_TO_INT (data));
+}
+
+static void
+create_plug (gboolean ellipsize, 
+             gboolean vertical)
+{
+  GtkWidget *plug, *label;
+  GdkNativeWindow plug_id;
+  guint timeout;
+
+
+  label = gtk_label_new ("Hello World");
+  
+  if (ellipsize)
+    gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+  if (vertical)
+    gtk_label_set_angle (GTK_LABEL (label), 90);
+
+  plug = gtk_plug_new (0);
+  gtk_container_add (GTK_CONTAINER (plug), label);
+  gtk_widget_show_all (plug);
+
+  plug_id = gtk_plug_get_id (GTK_PLUG (plug));
+  timeout = g_timeout_add (5 * 1000, on_embedding_timeout,
+                           GINT_TO_POINTER (plug_id));
+
+  g_signal_connect (plug, "embedded", 
+                    G_CALLBACK (on_embedded),
+                    GINT_TO_POINTER (timeout));
+  g_signal_connect (plug, "delete-event",
+                    G_CALLBACK (gtk_main_quit),
+                    NULL);
+
+  g_print ("%d\n", plug_id);
+}
+
+int
+main (int argc, char *argv[])
+{
+  GOptionContext *options;
+  TestSuite *suite = NULL;
+  GError *error = NULL;
+
+  gboolean ellipsize = FALSE;
+  gboolean vertical = FALSE;
+  gint initial_page = 0;
+  gchar *action = NULL;
+  
+  GOptionEntry entries[] = 
+  {
+    { "action", 'a', 0, G_OPTION_ARG_STRING, &action, "Action to perform", NULL },
+    { "initial-page", 'p', 0, G_OPTION_ARG_INT, &initial_page, "Initial page of the test suite", NULL },
+    { "ellipsize", 0, 0, G_OPTION_ARG_NONE, &ellipsize, "Add ellipses to labels", NULL },
+    { "vertical", 0, 0, G_OPTION_ARG_NONE, &vertical, "Render vertical layout", NULL },
+    { NULL }
+  };
+
+  gtk_init (&argc, &argv);
+
+  options = g_option_context_new (NULL);
+  g_option_context_add_main_entries (options, entries, NULL);
+  g_option_context_add_group (options, gtk_get_option_group (TRUE));
+  g_option_context_parse (options, &argc, &argv, &error);
+
+  if (error)
+    {
+      g_print ("Usage Error: %s\n", error->message);
+      return 2;
+    }
+
+  if (action && g_str_equal (action, "create-plug"))
+    {
+      create_plug (ellipsize, vertical);
+    }
+  else
+    {
+      suite = test_suite_new (argv[0]);
+      gtk_widget_show_all (suite->window);
+
+      gtk_notebook_set_current_page (GTK_NOTEBOOK (suite->notebook),
+                                     initial_page);
+    }
+
+  gtk_main ();
+
+  if (suite)
+    test_suite_free (suite);
+
+  return 0;
+}
+
+/* vim: set sw=2 sta et: */
