Index: demo/Makefile.am
===================================================================
--- demo/Makefile.am	(revision 15)
+++ demo/Makefile.am	(working copy)
@@ -8,7 +8,7 @@ INCLUDES = \
 #	-DGDK_DISABLE_DEPRECATED -DGDK_PIXBUF_DISABLE_DEPRECATED \
 #	-DGTK_DISABLE_DEPRECATED
 
-noinst_PROGRAMS = demo table-demo simple-demo scalability-demo units-demo widgets-demo mv-demo mv-table-demo mv-simple-demo mv-scalability-demo
+noinst_PROGRAMS = demo table-demo extent-demo simple-demo scalability-demo units-demo widgets-demo mv-demo mv-table-demo mv-simple-demo mv-scalability-demo
 
 demo_SOURCES = \
 	demo.c demo-fifteen.c demo-scalability.c demo-grabs.c \
@@ -32,6 +32,11 @@ table_demo_SOURCES = \
 
 table_demo_LDADD = $(top_builddir)/src/libgoocanvas.la @PACKAGE_LIBS@ $(INTLLIBS)
 
+extent_demo_SOURCES = \
+	extent-demo.c
+
+extent_demo_LDADD = $(top_builddir)/src/libgoocanvas.la @PACKAGE_LIBS@ $(INTLLIBS)
+
 mv_table_demo_SOURCES = \
 	mv-table-demo.c
 
Index: demo/extent-demo.c
===================================================================
--- demo/extent-demo.c	(revision 0)
+++ demo/extent-demo.c	(revision 0)
@@ -0,0 +1,215 @@
+#include <stdlib.h>
+#include <goocanvas.h>
+
+typedef enum {
+  MODE_MOVE,
+  MODE_RESIZE
+} Mode;
+
+Mode             drag_mode;
+GooCanvasItem   *drag_item = NULL;
+gdouble          drag_x = 0.0;
+gdouble          drag_y = 0.0;
+GooCanvasBounds  drag_extent;
+
+static gboolean
+on_button_press_event_cb (GooCanvasItem *item,
+                          GooCanvasItem *target_item,
+                          GdkEventButton *event,
+                          gpointer user_data)
+{
+  GooCanvasBounds *extent;
+
+  if(event->state & GDK_CONTROL_MASK)
+  {
+    if(event->button == 1 || event->button == 3)
+    {
+      if(event->button == 1)
+        drag_mode = MODE_MOVE;
+      else
+        drag_mode = MODE_RESIZE;
+
+      drag_item = item;
+      drag_x = event->x;
+      drag_y = event->y;
+
+      g_object_get (G_OBJECT (item), "extent", &extent, NULL);
+      drag_extent = *extent;
+      g_free(extent);
+
+      goo_canvas_pointer_grab (GOO_CANVAS (user_data), item, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, NULL, event->time);
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+static gboolean
+on_button_release_event_cb (GooCanvasItem  *item,
+                            GooCanvasItem  *target_item,
+                            GdkEventButton *event,
+                            gpointer user_data)
+{
+  if (drag_item == item && drag_item != NULL)
+  {
+    goo_canvas_pointer_ungrab (GOO_CANVAS (user_data), drag_item, event->time);
+    drag_item = NULL;
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+static gboolean
+on_motion_notify_event_cb (GooCanvasItem  *item,
+                           GooCanvasItem  *target_item,
+                           GdkEventMotion *event,
+                           gpointer user_data)
+{
+  GooCanvasBounds extent;
+
+  if (drag_item == item && drag_item != NULL)
+  {
+    gdouble rel_x = event->x - drag_x;
+    gdouble rel_y = event->y - drag_y;
+
+    extent = drag_extent;
+    if (drag_mode == MODE_MOVE)
+    {
+      extent.x1 += rel_x;
+      extent.y1 += rel_y;
+      extent.x2 += rel_x;
+      extent.y2 += rel_y;
+    }
+    else
+    {
+      extent.x2 += rel_x;
+      extent.y2 += rel_y;
+      if(extent.x2 - 5.0 < extent.x1) extent.x2 = extent.x1 + 5.0;
+      if(extent.y2 - 5.0 < extent.y1) extent.y2 = extent.y1 + 5.0;
+    }
+
+    g_object_set (G_OBJECT (item), "extent", &extent, NULL);
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+static void
+setup_dnd_handlers (GooCanvas *canvas,
+                    GooCanvasItem *item)
+{
+  g_signal_connect (G_OBJECT (item), "button-press-event", G_CALLBACK (on_button_press_event_cb), canvas);
+  g_signal_connect (G_OBJECT (item), "button-release-event", G_CALLBACK (on_button_release_event_cb), canvas);
+/*  g_signal_connect (G_OBJECT (item), "grab-broken-event", G_CALLBACK (on_button_release_event_cb), canvas);*/
+  g_signal_connect (G_OBJECT (item), "motion-notify-event", G_CALLBACK (on_motion_notify_event_cb), canvas);
+}
+
+
+void
+setup_canvas (GtkWidget *canvas)
+{
+  GooCanvasItem *root;
+  GooCanvasItem *item;
+  GdkPixbuf *pixbuf;
+  GtkWidget *button;
+  GooCanvasItem* child;
+
+  root = goo_canvas_get_root_item (GOO_CANVAS (canvas));
+
+  item = goo_canvas_rect_new (root, 50, 350, 100, 100, "fill-color", "blue", NULL);
+  setup_dnd_handlers (GOO_CANVAS (canvas), item);
+
+  item = goo_canvas_ellipse_new (root, 200, 400, 50, 50, "fill-color", "red", NULL);
+  setup_dnd_handlers (GOO_CANVAS (canvas), item);
+
+  item = goo_canvas_polyline_new (root, FALSE, 5.0, 250.0, 350.0, 275.0, 400.0, 300.0, 350.0, 325.0, 400.0, 350.0, 350.0, "stroke-color", "cyan", "line-width", 5.0, NULL);
+  setup_dnd_handlers (GOO_CANVAS (canvas), item);
+
+  item = goo_canvas_path_new (root, "M20,500 C20,450 100,450 100,500", "stroke-color", "green", "line-width", 5.0, NULL);
+  setup_dnd_handlers (GOO_CANVAS (canvas), item);
+
+  pixbuf = gtk_widget_render_icon (GTK_WIDGET (canvas), GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG, NULL);
+  item = goo_canvas_image_new (root, pixbuf, 150, 450, /*"fill-color", "yellow", */NULL);
+  g_object_unref (pixbuf);
+  setup_dnd_handlers (GOO_CANVAS (canvas), item);
+
+  item = goo_canvas_text_new (root, "Hello, World!", 250, 450, -1, GTK_ANCHOR_NW, "fill-color", "magenta", "wrap", PANGO_WRAP_WORD_CHAR, NULL);
+  setup_dnd_handlers (GOO_CANVAS (canvas), item);
+
+  button = gtk_label_new ("GtkLabel");
+  item = goo_canvas_widget_new (root, button, 50, 550, -1, -1, NULL);
+  setup_dnd_handlers (GOO_CANVAS (canvas), item);
+
+  item = goo_canvas_table_new(root, "horz-grid-line-width", 2.0, "vert-grid-line-width", 2.0, "row-spacing", 2.0, "column-spacing", 2.0, NULL);
+  goo_canvas_item_translate (item, 10.0, 10.0);
+  setup_dnd_handlers (GOO_CANVAS (canvas), item);
+
+  child = goo_canvas_rect_new(item, 10.0, 10.0, 50.0, 50.0, "fill-color", "blue", NULL);
+  setup_dnd_handlers (GOO_CANVAS (canvas), child);
+  goo_canvas_item_set_child_properties(item, child, "column", 0, "row", 0, "columns", 1, "rows", 1, NULL);
+  goo_canvas_item_translate (child, 10.0, 10.0);
+
+  child = goo_canvas_rect_new(item, 0.0, 0.0, 50.0, 50.0, "fill-color", "red", NULL);
+  setup_dnd_handlers (GOO_CANVAS (canvas), child);
+  goo_canvas_item_set_child_properties(item, child, "column", 1, "row", 0, "columns", 1, "rows", 1, NULL);
+
+  child = goo_canvas_rect_new(item, 0.0, 0.0, 50.0, 50.0, "fill-color", "green", NULL);
+  setup_dnd_handlers (GOO_CANVAS (canvas), child);
+  goo_canvas_item_set_child_properties(item, child, "column", 0, "row", 1, "columns", 1, "rows", 1, NULL);
+
+  child = goo_canvas_rect_new(item, 0.0, 0.0, 50.0, 50.0, "fill-color", "yellow", NULL);
+  setup_dnd_handlers (GOO_CANVAS (canvas), child);
+  goo_canvas_item_set_child_properties(item, child, "column", 1, "row", 1, "columns", 1, "rows", 1, NULL);
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  GtkWidget *window, *vbox, *label, *scrolled_win, *canvas;
+
+  /* Initialize GTK+. */
+  gtk_set_locale ();
+  gtk_init (&argc, &argv);
+
+  /* Create the window and widgets. */
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_default_size (GTK_WINDOW (window), 640, 600);
+  gtk_widget_show (window);
+  g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
+
+  vbox = gtk_vbox_new (FALSE, 4);
+  gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
+  gtk_widget_show (vbox);
+  gtk_container_add (GTK_CONTAINER (window), vbox);
+
+  label = gtk_label_new ("Use Ctrl+Left Click to move items or Ctrl+Right Click to resize items");
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+  gtk_widget_show (label);
+
+  /* Create top canvas. */
+  scrolled_win = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win),
+                                       GTK_SHADOW_IN);
+  gtk_widget_show (scrolled_win);
+  gtk_box_pack_start (GTK_BOX (vbox), scrolled_win, TRUE, TRUE, 0);
+
+  canvas = goo_canvas_new ();
+  g_object_set (G_OBJECT (canvas), "integer-layout", TRUE, NULL);
+/*  gtk_widget_set_size_request (canvas, 600, 250);*/
+  goo_canvas_set_bounds (GOO_CANVAS (canvas), 0, 0, 1000, 1000);
+  gtk_widget_show (canvas);
+  gtk_container_add (GTK_CONTAINER (scrolled_win), canvas);
+
+  setup_canvas (canvas);
+
+  gtk_main ();
+
+  return 0;
+}
+
+
Index: src/goocanvasrect.c
===================================================================
--- src/goocanvasrect.c	(revision 15)
+++ src/goocanvasrect.c	(working copy)
@@ -40,7 +40,10 @@ enum {
   PROP_WIDTH,
   PROP_HEIGHT,
   PROP_RADIUS_X,
-  PROP_RADIUS_Y
+  PROP_RADIUS_Y,
+
+  /* overriden */
+  PROP_EXTENT
 };
 
 
@@ -98,6 +101,8 @@ goo_canvas_rect_install_common_propertie
 							_("The vertical radius to use for rounded corners"),
 							0.0, G_MAXDOUBLE, 0.0,
 							G_PARAM_READWRITE));
+
+  g_object_class_override_property(gobject_class, PROP_EXTENT, "extent");
 }
 
 
@@ -200,6 +205,8 @@ goo_canvas_rect_get_common_property (GOb
 				     GValue               *value,
 				     GParamSpec           *pspec)
 {
+  GooCanvasBounds extent;
+
   switch (prop_id)
     {
     case PROP_X:
@@ -220,6 +227,13 @@ goo_canvas_rect_get_common_property (GOb
     case PROP_RADIUS_Y:
       g_value_set_double (value, rect_data->radius_y);
       break;
+    case PROP_EXTENT:
+      extent.x1 = rect_data->x;
+      extent.y1 = rect_data->y;
+      extent.x2 = rect_data->x + rect_data->width;
+      extent.y2 = rect_data->y + rect_data->height;
+      g_value_set_boxed (value, &extent);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -247,19 +261,25 @@ goo_canvas_rect_set_common_property (GOb
 				     const GValue         *value,
 				     GParamSpec           *pspec)
 {
+  GooCanvasBounds* extent;
+
   switch (prop_id)
     {
     case PROP_X:
       rect_data->x = g_value_get_double (value);
+      g_object_notify (object, "extent");
       break;
     case PROP_Y:
       rect_data->y = g_value_get_double (value);
+      g_object_notify (object, "extent");
       break;
     case PROP_WIDTH:
       rect_data->width = g_value_get_double (value);
+      g_object_notify (object, "extent");
       break;
     case PROP_HEIGHT:
       rect_data->height = g_value_get_double (value);
+      g_object_notify (object, "extent");
       break;
     case PROP_RADIUS_X:
       rect_data->radius_x = g_value_get_double (value);
@@ -267,6 +287,17 @@ goo_canvas_rect_set_common_property (GOb
     case PROP_RADIUS_Y:
       rect_data->radius_y = g_value_get_double (value);
       break;
+    case PROP_EXTENT:
+      extent = g_value_get_boxed (value);
+      rect_data->x = MIN (extent->x1, extent->x2);
+      rect_data->y = MIN (extent->y1, extent->y2);
+      rect_data->width = fabs (extent->x2 - extent->x1);
+      rect_data->height = fabs (extent->y2 - extent->y1);
+      g_object_notify (object, "x");
+      g_object_notify (object, "y");
+      g_object_notify (object, "width");
+      g_object_notify (object, "height");
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
Index: src/goocanvasellipse.c
===================================================================
--- src/goocanvasellipse.c	(revision 15)
+++ src/goocanvasellipse.c	(working copy)
@@ -37,7 +37,10 @@ enum {
   PROP_CENTER_X,
   PROP_CENTER_Y,
   PROP_RADIUS_X,
-  PROP_RADIUS_Y
+  PROP_RADIUS_Y,
+
+  /* overriden */
+  PROP_EXTENT
 };
 
 
@@ -92,6 +95,8 @@ goo_canvas_ellipse_install_common_proper
 							_("The vertical radius of the ellipse"),
 							0.0, G_MAXDOUBLE, 0.0,
 							G_PARAM_READWRITE));
+
+  g_object_class_override_property (gobject_class, PROP_EXTENT, "extent");
 }
 
 
@@ -210,6 +215,8 @@ goo_canvas_ellipse_get_common_property (
 					GValue               *value,
 					GParamSpec           *pspec)
 {
+  GooCanvasBounds extent;
+
   switch (prop_id)
     {
     case PROP_CENTER_X:
@@ -224,6 +231,13 @@ goo_canvas_ellipse_get_common_property (
     case PROP_RADIUS_Y:
       g_value_set_double (value, ellipse_data->radius_y);
       break;
+    case PROP_EXTENT:
+      extent.x1 = ellipse_data->center_x - ellipse_data->radius_x;
+      extent.x2 = ellipse_data->center_x + ellipse_data->radius_x;
+      extent.y1 = ellipse_data->center_y - ellipse_data->radius_y;
+      extent.y2 = ellipse_data->center_y + ellipse_data->radius_y;
+      g_value_set_boxed (value, &extent);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -251,19 +265,37 @@ goo_canvas_ellipse_set_common_property (
 					const GValue         *value,
 					GParamSpec           *pspec)
 {
+  GooCanvasBounds* extent;
+
   switch (prop_id)
     {
     case PROP_CENTER_X:
       ellipse_data->center_x = g_value_get_double (value);
+      g_object_notify(object, "extent");
       break;
     case PROP_CENTER_Y:
       ellipse_data->center_y = g_value_get_double (value);
+      g_object_notify(object, "extent");
       break;
     case PROP_RADIUS_X:
       ellipse_data->radius_x = g_value_get_double (value);
+      g_object_notify(object, "extent");
       break;
     case PROP_RADIUS_Y:
       ellipse_data->radius_y = g_value_get_double (value);
+      g_object_notify(object, "extent");
+      break;
+    case PROP_EXTENT:
+      extent = g_value_get_boxed (value);
+      ellipse_data->center_x = (extent->x1 + extent->x2) / 2.0;
+      ellipse_data->radius_x = fabs (extent->x1 - extent->x2) / 2.0;
+      ellipse_data->center_y = (extent->y1 + extent->y2) / 2.0;
+      ellipse_data->radius_y = fabs (extent->y1 - extent->y2) / 2.0;
+
+      g_object_notify(object, "center-x");
+      g_object_notify(object, "center-y");
+      g_object_notify(object, "radius-x");
+      g_object_notify(object, "radius-y");
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Index: src/goocanvasimage.c
===================================================================
--- src/goocanvasimage.c	(revision 15)
+++ src/goocanvasimage.c	(working copy)
@@ -27,6 +27,7 @@
 #include <config.h>
 #include <glib/gi18n-lib.h>
 #include <gtk/gtk.h>
+#include <math.h> /* for fabs() */
 #include "goocanvasprivate.h"
 #include "goocanvasimage.h"
 #include "goocanvas.h"
@@ -43,7 +44,10 @@ enum {
   PROP_HEIGHT,
 
   /* Convenience properties. */
-  PROP_PIXBUF
+  PROP_PIXBUF,
+
+  /* overriden */
+  PROP_EXTENT
 };
 
 static void goo_canvas_image_dispose      (GObject            *object);
@@ -110,6 +114,8 @@ goo_canvas_image_install_common_properti
 							_("The GdkPixbuf to display"),
 							GDK_TYPE_PIXBUF,
 							G_PARAM_WRITABLE));
+
+  g_object_class_override_property (gobject_class, PROP_EXTENT, "extent");
 }
 
 
@@ -226,6 +232,8 @@ goo_canvas_image_get_common_property (GO
 				      GValue               *value,
 				      GParamSpec           *pspec)
 {
+  GooCanvasBounds extent;
+
   switch (prop_id)
     {
     case PROP_PATTERN:
@@ -243,6 +251,13 @@ goo_canvas_image_get_common_property (GO
     case PROP_HEIGHT:
       g_value_set_double (value, image_data->height);
       break;
+    case PROP_EXTENT:
+      extent.x1 = image_data->x;
+      extent.x2 = image_data->x + image_data->width;
+      extent.y1 = image_data->y;
+      extent.y2 = image_data->y + image_data->height;
+      g_value_set_boxed (value, &extent);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -272,6 +287,7 @@ goo_canvas_image_set_common_property (GO
 				      GParamSpec           *pspec)
 {
   GdkPixbuf *pixbuf;
+  GooCanvasBounds* extent;
 
   switch (prop_id)
     {
@@ -282,15 +298,19 @@ goo_canvas_image_set_common_property (GO
       break;
     case PROP_X:
       image_data->x = g_value_get_double (value);
+      g_object_notify(object, "extent");
       break;
     case PROP_Y:
       image_data->y = g_value_get_double (value);
+      g_object_notify(object, "extent");
       break;
     case PROP_WIDTH:
       image_data->width = g_value_get_double (value);
+      g_object_notify(object, "extent");
       break;
     case PROP_HEIGHT:
       image_data->height = g_value_get_double (value);
+      g_object_notify(object, "extent");
       break;
     case PROP_PIXBUF:
       cairo_pattern_destroy (image_data->pattern);
@@ -299,6 +319,18 @@ goo_canvas_image_set_common_property (GO
       image_data->width = pixbuf ? gdk_pixbuf_get_width (pixbuf) : 0;
       image_data->height = pixbuf ? gdk_pixbuf_get_height (pixbuf) : 0;
       break;
+    case PROP_EXTENT:
+      extent = g_value_get_boxed (value);
+      image_data->x = MIN (extent->x1, extent->x2);
+      image_data->y = MIN (extent->y1, extent->y2);
+      image_data->width = fabs(extent->x2 - extent->x1);
+      image_data->height = fabs(extent->y2 - extent->y1);
+      
+      g_object_notify(object, "x");
+      g_object_notify(object, "y");
+      g_object_notify(object, "width");
+      g_object_notify(object, "height");
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
Index: src/goocanvaspolyline.c
===================================================================
--- src/goocanvaspolyline.c	(revision 15)
+++ src/goocanvaspolyline.c	(working copy)
@@ -117,7 +117,10 @@ enum {
   PROP_END_ARROW,
   PROP_ARROW_LENGTH,
   PROP_ARROW_WIDTH,
-  PROP_ARROW_TIP_LENGTH
+  PROP_ARROW_TIP_LENGTH,
+
+  /* overriden */
+  PROP_EXTENT
 };
 
 
@@ -189,6 +192,8 @@ goo_canvas_polyline_install_common_prope
 							_("The length of the arrow tip, as a multiple of the line width"),
 							0.0, G_MAXDOUBLE, 4.0,
 							G_PARAM_READWRITE));
+
+  g_object_class_override_property (gobject_class, PROP_EXTENT, "extent");
 }
 
 
@@ -221,6 +226,32 @@ goo_canvas_polyline_finalize (GObject *o
 
 
 static void
+goo_canvas_polyline_get_extent (GooCanvasPolylineData *polyline_data,
+                                GooCanvasBounds *bounds)
+{
+  guint i;
+
+  if (polyline_data->num_points == 0)
+    {
+      bounds->x1 = bounds->y1 = bounds->x2 = bounds->y2 = 0.0;
+    }
+  else
+    {
+      bounds->x1 = bounds->x2 = polyline_data->coords[0];
+      bounds->y1 = bounds->y2 = polyline_data->coords[1];
+
+      for (i = 1; i < polyline_data->num_points; ++ i)
+        {
+	  bounds->x1 = MIN (bounds->x1, polyline_data->coords[2 * i]);
+	  bounds->y1 = MIN (bounds->y1, polyline_data->coords[2 * i + 1]);
+	  bounds->x2 = MAX (bounds->x2, polyline_data->coords[2 * i]);
+	  bounds->y2 = MAX (bounds->y2, polyline_data->coords[2 * i + 1]);
+        }
+    }
+}
+
+
+static void
 goo_canvas_polyline_get_common_property (GObject              *object,
 					 GooCanvasPolylineData *polyline_data,
 					 guint                 prop_id,
@@ -228,6 +259,7 @@ goo_canvas_polyline_get_common_property 
 					 GParamSpec           *pspec)
 {
   GooCanvasPoints *points;
+  GooCanvasBounds  extent;
 
   switch (prop_id)
     {
@@ -266,6 +298,10 @@ goo_canvas_polyline_get_common_property 
       g_value_set_double (value, polyline_data->arrow_data
 			  ? polyline_data->arrow_data->arrow_tip_length : 4.0);
       break;
+    case PROP_EXTENT:
+      goo_canvas_polyline_get_extent (polyline_data, &extent);
+      g_value_set_boxed (value, &extent);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -437,6 +473,11 @@ goo_canvas_polyline_set_common_property 
 					 GParamSpec           *pspec)
 {
   GooCanvasPoints *points;
+  GooCanvasBounds  old_extent;
+  GooCanvasBounds *new_extent;
+  gdouble old_width, old_height;
+  gdouble new_width, new_height;
+  guint i;
 
   switch (prop_id)
     {
@@ -461,6 +502,7 @@ goo_canvas_polyline_set_common_property 
 		  polyline_data->num_points * 2 * sizeof (double));
 	}
       polyline_data->reconfigure_arrows = TRUE;
+      g_object_notify(object, "extent");
       break;
     case PROP_CLOSE_PATH:
       polyline_data->close_path = g_value_get_boolean (value);
@@ -489,6 +531,27 @@ goo_canvas_polyline_set_common_property 
       polyline_data->arrow_data->arrow_tip_length = g_value_get_double (value);
       polyline_data->reconfigure_arrows = TRUE;
       break;
+    case PROP_EXTENT:
+      goo_canvas_polyline_get_extent (polyline_data, &old_extent);
+      new_extent = g_value_get_boxed (value);
+
+      old_width = old_extent.x2 - old_extent.x1;
+      old_height = old_extent.y2 - old_extent.y1;
+      new_width = new_extent->x2 - new_extent->x1;
+      new_height = new_extent->y2 - new_extent->y1;
+
+      for (i = 0; i < polyline_data->num_points; ++ i)
+        {
+          polyline_data->coords[2 * i] = new_extent->x1
+            + (polyline_data->coords[2 * i] - old_extent.x1)
+            * (new_width / old_width);
+          polyline_data->coords[2 * i + 1] = new_extent->y1
+            + (polyline_data->coords[2 * i + 1] - old_extent.y1)
+            * (new_height / old_height);
+        }
+
+      g_object_notify(object, "points");
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
Index: src/goocanvasgroup.c
===================================================================
--- src/goocanvasgroup.c	(revision 15)
+++ src/goocanvasgroup.c	(working copy)
@@ -32,6 +32,7 @@
  */
 #include <config.h>
 #include <glib/gi18n-lib.h>
+#include <math.h> /* for fabs() */
 #include <gtk/gtk.h>
 #include "goocanvasprivate.h"
 #include "goocanvasgroup.h"
@@ -41,8 +42,23 @@
 #include "goocanvasatk.h"
 
 
+enum {
+  PROP_0,
+
+  /* overriden */
+  PROP_EXTENT
+};
+
 static void goo_canvas_group_dispose	  (GObject            *object);
 static void goo_canvas_group_finalize     (GObject            *object);
+static void goo_canvas_group_get_property (GObject            *object,
+                                           guint               prop_id,
+                                           GValue             *value,
+                                           GParamSpec         *pspec);
+static void goo_canvas_group_set_property (GObject            *object,
+                                           guint               prop_id,
+                                           const GValue       *value,
+                                           GParamSpec         *pspec);
 static void canvas_item_interface_init    (GooCanvasItemIface *iface);
 
 G_DEFINE_TYPE_WITH_CODE (GooCanvasGroup, goo_canvas_group,
@@ -50,6 +66,11 @@ G_DEFINE_TYPE_WITH_CODE (GooCanvasGroup,
 			 G_IMPLEMENT_INTERFACE (GOO_TYPE_CANVAS_ITEM,
 						canvas_item_interface_init))
 
+static void
+goo_canvas_group_install_common_properties (GObjectClass *gobject_class)
+{
+  g_object_class_override_property(gobject_class, PROP_EXTENT, "extent");
+}
 
 static void
 goo_canvas_group_class_init (GooCanvasGroupClass *klass)
@@ -58,6 +79,8 @@ goo_canvas_group_class_init (GooCanvasGr
 
   gobject_class->dispose  = goo_canvas_group_dispose;
   gobject_class->finalize = goo_canvas_group_finalize;
+  gobject_class->get_property = goo_canvas_group_get_property;
+  gobject_class->set_property = goo_canvas_group_set_property;
 
   /* Register our accessible factory, but only if accessibility is enabled. */
   if (!ATK_IS_NO_OP_OBJECT_FACTORY (atk_registry_get_factory (atk_get_default_registry (), GTK_TYPE_WIDGET)))
@@ -66,6 +89,8 @@ goo_canvas_group_class_init (GooCanvasGr
 				     GOO_TYPE_CANVAS_GROUP,
 				     goo_canvas_item_accessible_factory_get_type ());
     }
+
+  goo_canvas_group_install_common_properties (gobject_class);
 }
 
 
@@ -145,6 +170,95 @@ goo_canvas_group_finalize (GObject *obje
   G_OBJECT_CLASS (goo_canvas_group_parent_class)->finalize (object);
 }
 
+static void
+goo_canvas_group_get_extent(GooCanvasGroup  *group,
+                            GooCanvasBounds *extent)
+{
+  GooCanvasBounds bounds;
+  GPtrArray* children;
+  guint i;
+
+  children = group->items;
+
+  if (children->len == 0)
+    {
+      extent->x1 = extent->y1 = extent->x2 = extent->y2 = 0.0;
+    }
+  else
+    {
+      goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (children->pdata[0]), extent);
+      for(i = 1; i < children->len; ++ i)
+        {
+          goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (children->pdata[0]), &bounds);
+          extent->x1 = MIN (extent->x1, bounds.x1);
+          extent->x2 = MAX (extent->x2, bounds.x2);
+          extent->y1 = MIN (extent->y1, bounds.y1);
+          extent->y2 = MAX (extent->y2, bounds.y2);
+        }
+    }
+}
+
+static void
+goo_canvas_group_get_property (GObject               *object,
+                               guint                  prop_id,
+                               GValue                *value,
+                               GParamSpec            *pspec)
+{
+  GooCanvasGroup *group = (GooCanvasGroup*) object;
+  GooCanvasBounds extent;
+
+  switch (prop_id)
+  {
+    case PROP_EXTENT:
+      goo_canvas_group_get_extent(group, &extent);
+      g_value_set_boxed(value, &extent);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+goo_canvas_group_set_common_property (GObject           *object,
+                                      GPtrArray         *children,
+                                      guint              prop_id,
+                                      const GValue      *value,
+                                      GParamSpec        *pspec)
+{
+  switch (prop_id)
+    {
+    case PROP_EXTENT:
+      /* TODO: We would have to store the extent of the group somewhere,
+       * and adjust the bounds correctly, possibly clip away children to be
+       * able to set the extent */
+      g_warning("Setting extent is not yet implemented for GooCanvasGroup");
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+goo_canvas_group_set_property (GObject                  *object,
+                               guint                     prop_id,
+                               const GValue             *value,
+                               GParamSpec               *pspec)
+{
+  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
+  GooCanvasGroup *group = (GooCanvasGroup*) object;
+
+  if (simple->model)
+    {
+      g_warning ("Can't set property of a canvas item with a model - set the model property instead");
+      return;
+    }
+
+  goo_canvas_group_set_common_property (object, group->items, prop_id,
+                                        value, pspec);
+  goo_canvas_item_simple_changed (simple, TRUE);
+}
 
 static void
 goo_canvas_group_add_child     (GooCanvasItem  *item,
@@ -234,11 +348,11 @@ goo_canvas_group_remove_child  (GooCanva
 			     child_num, child_atk_obj);
     }
 
+  g_ptr_array_remove_index (group->items, child_num);
+
   goo_canvas_item_set_parent (child, NULL);
   g_object_unref (child);
 
-  g_ptr_array_remove_index (group->items, child_num);
-
   goo_canvas_item_request_update (item);
 }
 
@@ -423,6 +537,7 @@ goo_canvas_group_update  (GooCanvasItem 
 		}
 	    }
 	}
+      g_object_notify (G_OBJECT (item), "extent");
       cairo_restore (cr);
     }
 
@@ -596,6 +711,14 @@ canvas_item_interface_init (GooCanvasIte
 static void item_model_interface_init (GooCanvasItemModelIface *iface);
 static void goo_canvas_group_model_dispose      (GObject            *object);
 static void goo_canvas_group_model_finalize     (GObject            *object);
+static void goo_canvas_group_model_get_property (GObject            *object,
+                                                 guint               prop_id,
+                                                 GValue             *value,
+                                                 GParamSpec         *pspec);
+static void goo_canvas_group_model_set_property (GObject            *object,
+                                                 guint               prop_id,
+                                                 const GValue       *value,
+                                                 GParamSpec         *pspec);
 
 G_DEFINE_TYPE_WITH_CODE (GooCanvasGroupModel, goo_canvas_group_model,
 			 GOO_TYPE_CANVAS_ITEM_MODEL_SIMPLE,
@@ -610,6 +733,10 @@ goo_canvas_group_model_class_init (GooCa
 
   gobject_class->dispose  = goo_canvas_group_model_dispose;
   gobject_class->finalize = goo_canvas_group_model_finalize;
+  gobject_class->get_property  = goo_canvas_group_model_get_property;
+  gobject_class->set_property = goo_canvas_group_model_set_property;
+
+  goo_canvas_group_install_common_properties (gobject_class);
 }
 
 
@@ -689,6 +816,25 @@ goo_canvas_group_model_finalize (GObject
   G_OBJECT_CLASS (goo_canvas_group_model_parent_class)->finalize (object);
 }
 
+static void goo_canvas_group_model_get_property (GObject            *object,
+                                                 guint               prop_id,
+                                                 GValue             *value,
+                                                 GParamSpec         *pspec)
+{
+  GooCanvasGroupModel *model = (GooCanvasGroupModel*) object;
+}
+
+static void goo_canvas_group_model_set_property (GObject            *object,
+                                                 guint               prop_id,
+                                                 const GValue       *value,
+                                                 GParamSpec         *pspec)
+{
+  GooCanvasGroupModel *model = (GooCanvasGroupModel*) object;
+
+  goo_canvas_group_set_common_property (object, model->children, prop_id,
+                                        value, pspec);
+  g_signal_emit_by_name (model, "changed", TRUE);
+}
 
 extern void _goo_canvas_item_model_emit_child_added (GooCanvasItemModel *model,
 						     gint                position);
@@ -741,11 +887,12 @@ goo_canvas_group_model_remove_child  (Go
 
   child = gmodel->children->pdata[child_num];
   goo_canvas_item_model_set_parent (child, NULL);
-  g_object_unref (child);
 
   g_ptr_array_remove_index (gmodel->children, child_num);
 
   g_signal_emit_by_name (gmodel, "child-removed", child_num);
+
+  g_object_unref (child);
 }
 
 
Index: src/goocanvaspath.c
===================================================================
--- src/goocanvaspath.c	(revision 15)
+++ src/goocanvaspath.c	(working copy)
@@ -33,12 +33,16 @@
 #include <glib/gi18n-lib.h>
 #include <gtk/gtk.h>
 #include "goocanvaspath.h"
+#include "goocanvas.h"
 
 
 enum {
   PROP_0,
 
   PROP_DATA,
+
+  /* override */
+  PROP_EXTENT
 };
 
 static void canvas_item_interface_init   (GooCanvasItemIface  *iface);
@@ -76,6 +80,8 @@ goo_canvas_path_install_common_propertie
 							_("The sequence of path commands"),
 							NULL,
 							G_PARAM_WRITABLE));
+
+  g_object_class_override_property (gobject_class, PROP_EXTENT, "extent");
 }
 
 
@@ -200,16 +206,58 @@ goo_canvas_path_finalize (GObject *objec
   G_OBJECT_CLASS (goo_canvas_path_parent_class)->finalize (object);
 }
 
+static void
+goo_canvas_path_common_get_extent (GooCanvas            *canvas,
+                                   GooCanvasPathData    *path_data,
+                                   GooCanvasBounds      *bounds)
+{
+  cairo_t *cr;
+
+  cr = goo_canvas_create_cairo_context (canvas);
+  goo_canvas_create_path (path_data->path_commands, cr);
+  cairo_path_extents (cr, &bounds->x1, &bounds->y1, &bounds->x2, &bounds->y2);
+  cairo_destroy (cr);
+}
+
+static void
+goo_canvas_path_extent_transform (double *x,
+                                  double *y,
+                                  GooCanvasBounds* old_extent,
+                                  GooCanvasBounds* new_extent,
+                                  gboolean relative)
+{
+  gdouble new_width = new_extent->x2 - new_extent->x1;
+  gdouble new_height = new_extent->y2 - new_extent->y1;
+  gdouble old_width = old_extent->x2 - old_extent->x1;
+  gdouble old_height = old_extent->y2 - old_extent->y1;
+
+  if (relative)
+    {
+      *x *= (new_width / old_width);
+      *y *= (new_height / old_height);
+    }
+  else
+    {
+      *x = new_extent->x1 + (*x - old_extent->x1) * (new_width / old_width);
+      *y = new_extent->y1 + (*y - old_extent->y1) * (new_height / old_height);
+    }
+}
 
 static void
 goo_canvas_path_get_common_property (GObject              *object,
+                                     GooCanvas            *canvas,
 				     GooCanvasPathData    *path_data,
 				     guint                 prop_id,
 				     GValue               *value,
 				     GParamSpec           *pspec)
 {
+  GooCanvasBounds extent;
   switch (prop_id)
     {
+    case PROP_EXTENT:
+      goo_canvas_path_common_get_extent(canvas, path_data, &extent);
+      g_value_set_boxed(value, &extent);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -223,26 +271,79 @@ goo_canvas_path_get_property (GObject   
 			      GValue               *value,
 			      GParamSpec           *pspec)
 {
+  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
   GooCanvasPath *path = (GooCanvasPath*) object;
 
-  goo_canvas_path_get_common_property (object, path->path_data, prop_id,
-				       value, pspec);
+  goo_canvas_path_get_common_property (object, simple->canvas,
+                                       path->path_data, prop_id, value, pspec);
 }
 
 
 static void
 goo_canvas_path_set_common_property (GObject              *object,
+                                     GooCanvas            *canvas,
 				     GooCanvasPathData    *path_data,
 				     guint                 prop_id,
 				     const GValue         *value,
 				     GParamSpec           *pspec)
 {
+  GooCanvasBounds old_extent;
+  GooCanvasBounds *new_extent;
+  GooCanvasPathCommand *cmd;
+  guint i;
+
   switch (prop_id)
     {
     case PROP_DATA:
       if (path_data->path_commands)
 	g_array_free (path_data->path_commands, TRUE);
       path_data->path_commands = goo_canvas_parse_path_data (g_value_get_string (value));
+      g_object_notify(object, "extent");
+      break;
+    case PROP_EXTENT:
+      goo_canvas_path_common_get_extent(canvas, path_data, &old_extent);
+      new_extent = g_value_get_boxed(value);
+
+      for (i = 0; i < path_data->path_commands->len; ++ i)
+        {
+          cmd = &g_array_index (path_data->path_commands, GooCanvasPathCommand, i);
+          switch (cmd->simple.type)
+            {
+            case GOO_CANVAS_PATH_MOVE_TO:
+            case GOO_CANVAS_PATH_CLOSE_PATH:
+            case GOO_CANVAS_PATH_LINE_TO:
+            case GOO_CANVAS_PATH_HORIZONTAL_LINE_TO:
+            case GOO_CANVAS_PATH_VERTICAL_LINE_TO:
+              goo_canvas_path_extent_transform (
+                &cmd->simple.x, &cmd->simple.y,
+                &old_extent, new_extent, cmd->simple.relative);
+              break;
+            case GOO_CANVAS_PATH_CURVE_TO:
+            case GOO_CANVAS_PATH_SMOOTH_CURVE_TO:
+            case GOO_CANVAS_PATH_QUADRATIC_CURVE_TO:
+            case GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO:
+              goo_canvas_path_extent_transform (
+                &cmd->curve.x, &cmd->curve.y,
+                &old_extent, new_extent,
+                cmd->curve.relative);
+              goo_canvas_path_extent_transform (
+                &cmd->curve.x1, &cmd->curve.y1,
+                &old_extent, new_extent,
+                cmd->curve.relative);
+              goo_canvas_path_extent_transform (
+                &cmd->curve.x2, &cmd->curve.y2,
+                &old_extent, new_extent,
+                cmd->curve.relative);
+              break;
+            case GOO_CANVAS_PATH_ELLIPTICAL_ARC:
+              goo_canvas_path_extent_transform (
+                &cmd->arc.x, &cmd->arc.y,
+                &old_extent, new_extent,
+                cmd->arc.relative);
+              break;
+            }
+        }
+      g_object_notify(object, "data");
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -266,8 +367,8 @@ goo_canvas_path_set_property (GObject   
       return;
     }
 
-  goo_canvas_path_set_common_property (object, path->path_data, prop_id,
-				       value, pspec);
+  goo_canvas_path_set_common_property (object, simple->canvas, path->path_data,
+                                       prop_id, value, pspec);
   goo_canvas_item_simple_changed (simple, TRUE);
 }
 
@@ -478,8 +579,8 @@ goo_canvas_path_model_get_property (GObj
 {
   GooCanvasPathModel *pmodel = (GooCanvasPathModel*) object;
 
-  goo_canvas_path_get_common_property (object, &pmodel->path_data, prop_id,
-				       value, pspec);
+  goo_canvas_path_get_common_property (object, NULL, &pmodel->path_data,
+                                       prop_id, value, pspec);
 }
 
 
@@ -491,8 +592,8 @@ goo_canvas_path_model_set_property (GObj
 {
   GooCanvasPathModel *pmodel = (GooCanvasPathModel*) object;
 
-  goo_canvas_path_set_common_property (object, &pmodel->path_data, prop_id,
-				       value, pspec);
+  goo_canvas_path_set_common_property (object, NULL, &pmodel->path_data,
+                                       prop_id, value, pspec);
   g_signal_emit_by_name (pmodel, "changed", TRUE);
 }
 
Index: src/goocanvasitem.c
===================================================================
--- src/goocanvasitem.c	(revision 15)
+++ src/goocanvasitem.c	(working copy)
@@ -434,6 +434,13 @@ goo_canvas_item_base_init (gpointer g_if
 								 FALSE,
 								 G_PARAM_READWRITE));
 
+      g_object_interface_install_property (g_iface,
+                                           g_param_spec_boxed ("extent",
+                                                               "Extent",
+                                                               _("The extents of the untransformed item"),
+                                                                GOO_TYPE_CANVAS_BOUNDS,
+                                                                G_PARAM_READWRITE));
+
       _goo_canvas_style_init ();
 
       initialized = TRUE;
Index: src/goocanvasitemsimple.c
===================================================================
--- src/goocanvasitemsimple.c	(revision 15)
+++ src/goocanvasitemsimple.c	(working copy)
@@ -78,6 +78,8 @@ enum {
   PROP_TITLE,
   PROP_DESCRIPTION,
   PROP_CAN_FOCUS,
+  PROP_EXTENT,
+
   PROP_CLIP_PATH,
   PROP_CLIP_FILL_RULE
 };
@@ -113,7 +115,7 @@ static gboolean goo_canvas_item_simple_d
 G_DEFINE_TYPE_WITH_CODE (GooCanvasItemSimple, goo_canvas_item_simple,
 			 G_TYPE_OBJECT,
 			 G_IMPLEMENT_INTERFACE (GOO_TYPE_CANVAS_ITEM,
-						canvas_item_interface_init))
+					        canvas_item_interface_init))
 
 
 static void
@@ -287,6 +289,9 @@ goo_canvas_item_simple_install_common_pr
   g_object_class_override_property (gobject_class, PROP_CAN_FOCUS,
 				    "can-focus");
 
+  g_object_class_override_property (gobject_class, PROP_EXTENT,
+                                    "extent");
+
   /**
    * GooCanvasItemSimple:clip-path
    *
@@ -580,6 +585,11 @@ goo_canvas_item_simple_get_common_proper
     case PROP_CAN_FOCUS:
       g_value_set_boolean (value, simple_data->can_focus);
       break;
+    case PROP_EXTENT:
+      g_warning("Cannot get the value of the \"extent\" property for a "
+                "GooCanvasItemSimple. You should override this property "
+                "in a subclass.");
+      break;
     case PROP_CLIP_FILL_RULE:
       g_value_set_enum (value, simple_data->clip_fill_rule);
       break;
@@ -811,6 +821,11 @@ goo_canvas_item_simple_set_common_proper
     case PROP_CAN_FOCUS:
       simple_data->can_focus = g_value_get_boolean (value);
       break;
+    case PROP_EXTENT:
+      g_warning("Cannot set the value of the \"extent\" property for a "
+                "GooCanvasItemSimple. You should override this property in "
+                "a subclass.");
+      break;
     case PROP_CLIP_PATH:
       if (simple_data->clip_path_commands)
 	g_array_free (simple_data->clip_path_commands, TRUE);
Index: src/goocanvasitemmodel.c
===================================================================
--- src/goocanvasitemmodel.c	(revision 15)
+++ src/goocanvasitemmodel.c	(working copy)
@@ -256,6 +256,13 @@ goo_canvas_item_model_base_init (gpointe
 								 FALSE,
 								 G_PARAM_READWRITE));
 
+      g_object_interface_install_property (g_iface,
+                                           g_param_spec_boxed ("extent",
+                                                               "Extent",
+                                                               _("The extents of the untransformed item"),
+                                                                GOO_TYPE_CANVAS_BOUNDS,
+                                                                G_PARAM_WRITABLE));
+
       _goo_canvas_style_init ();
 
       initialized = TRUE;
Index: src/goocanvaswidget.c
===================================================================
--- src/goocanvaswidget.c	(revision 15)
+++ src/goocanvaswidget.c	(working copy)
@@ -31,6 +31,7 @@
 #include <config.h>
 #include <glib/gi18n-lib.h>
 #include <gtk/gtk.h>
+#include <math.h> /* for fabs() */
 #include "goocanvas.h"
 #include "goocanvasatk.h"
 
@@ -43,7 +44,10 @@ enum {
   PROP_WIDTH,
   PROP_HEIGHT,
   PROP_ANCHOR,
-  PROP_VISIBILITY
+  PROP_VISIBILITY,
+
+  /* override */
+  PROP_EXTENT
 };
 
 
@@ -152,6 +156,72 @@ goo_canvas_widget_new               (Goo
   return item;
 }
 
+/* Given an area starting from 0 and ranging to the given width, this
+ * function returns the X coordinate of a point at the given anchor. */
+static gdouble
+goo_canvas_widget_anchor_horizontal_pos(GtkAnchorType anchor,
+                                        gdouble width)
+{
+  switch(anchor)
+    {
+    case GTK_ANCHOR_N:
+    case GTK_ANCHOR_CENTER:
+    case GTK_ANCHOR_S:
+      return width / 2.0;
+    case GTK_ANCHOR_NE:
+    case GTK_ANCHOR_E:
+    case GTK_ANCHOR_SE:
+      return width;
+    default:
+      return 0.0;
+    }
+}
+
+/* Given an area starting from 0 and ranging to the given height, this
+ * function returns the Y coordinate of a point at the given anchor. */
+static gdouble
+goo_canvas_widget_anchor_vertical_pos(GtkAnchorType anchor,
+                                      gdouble height)
+{
+  switch (anchor)
+    {
+    case GTK_ANCHOR_W:
+    case GTK_ANCHOR_CENTER:
+    case GTK_ANCHOR_E:
+      return height / 2.0;
+    case GTK_ANCHOR_SW:
+    case GTK_ANCHOR_S:
+    case GTK_ANCHOR_SE:
+      return height;
+    default:
+      return 0.0;
+    }
+}
+
+static void
+goo_canvas_widget_get_widget_size(GooCanvasWidget *witem,
+                                  double          *width,
+                                  double          *height)
+{
+  GtkRequisition requisition;
+
+  if (witem->widget == NULL)
+  {
+    *width = 0.0;
+    *height = 0.0;
+  }
+  else
+  {
+    /* Compute the new bounds. */
+    if (witem->width < 0 || witem->height < 0)
+    {
+      gtk_widget_size_request (witem->widget, &requisition);
+    }
+
+    *width = witem->width < 0 ? requisition.width : witem->width;
+    *height = witem->height < 0 ? requisition.height : witem->height;
+  }
+}
 
 static void
 goo_canvas_widget_set_widget (GooCanvasWidget *witem,
@@ -214,6 +284,9 @@ goo_canvas_widget_get_property  (GObject
   GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
   GooCanvasWidget *witem = (GooCanvasWidget*) object;
 
+  GooCanvasBounds extent;
+  gdouble width, height;
+
   switch (prop_id)
     {
     case PROP_WIDGET:
@@ -237,6 +310,18 @@ goo_canvas_widget_get_property  (GObject
     case PROP_VISIBILITY:
       g_value_set_enum (value, simple->simple_data->visibility);
       break;
+    case PROP_EXTENT:
+      goo_canvas_widget_get_widget_size(witem, &width, &height);
+
+      extent.x1 = witem->x
+        - goo_canvas_widget_anchor_horizontal_pos (witem->anchor, width);
+      extent.x2 = extent.x1 + width;
+      extent.y1 = witem->y
+        - goo_canvas_widget_anchor_vertical_pos (witem->anchor, height);
+      extent.y2 = extent.y1 + height;
+
+      g_value_set_boxed (value, &extent);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -252,26 +337,33 @@ goo_canvas_widget_set_property  (GObject
 {
   GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
   GooCanvasWidget *witem = (GooCanvasWidget*) object;
+  GooCanvasBounds *extent;
 
   switch (prop_id)
     {
     case PROP_WIDGET:
       goo_canvas_widget_set_widget (witem, g_value_get_object (value));
+      g_object_notify(object, "extent");
       break;
     case PROP_X:
       witem->x = g_value_get_double (value);
+      g_object_notify(object, "extent");
       break;
     case PROP_Y:
       witem->y = g_value_get_double (value);
+      g_object_notify(object, "extent");
       break;
     case PROP_WIDTH:
       witem->width = g_value_get_double (value);
+      g_object_notify(object, "extent");
       break;
     case PROP_HEIGHT:
       witem->height = g_value_get_double (value);
+      g_object_notify(object, "extent");
       break;
     case PROP_ANCHOR:
       witem->anchor = g_value_get_enum (value);
+      g_object_notify(object, "extent");
       break;
     case PROP_VISIBILITY:
       simple->simple_data->visibility = g_value_get_enum (value);
@@ -280,6 +372,21 @@ goo_canvas_widget_set_property  (GObject
       else
 	gtk_widget_show (witem->widget);
       break;
+    case PROP_EXTENT:
+      extent = g_value_get_boxed (value);
+      witem->width = fabs(extent->x2 - extent->x1);
+      witem->height = fabs(extent->y2 - extent->y1);
+      witem->x = extent->x1
+        + goo_canvas_widget_anchor_horizontal_pos(witem->anchor, witem->width);
+      witem->y = extent->y1
+        + goo_canvas_widget_anchor_vertical_pos(witem->anchor, witem->height);
+
+      g_object_notify(object, "x");
+      g_object_notify(object, "y");
+      g_object_notify(object, "width");
+      g_object_notify(object, "height");
+
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -347,53 +454,19 @@ goo_canvas_widget_update  (GooCanvasItem
 			   cairo_t             *cr)
 {
   GooCanvasWidget *witem = (GooCanvasWidget*) simple;
-  GtkRequisition requisition;
   gdouble width, height;
 
   if (witem->widget)
     {
-      /* Compute the new bounds. */
-      if (witem->width < 0 || witem->height < 0)
-	{
-	  gtk_widget_size_request (witem->widget, &requisition);
-	}
+      goo_canvas_widget_get_widget_size(witem, &width, &height);
 
       simple->bounds.x1 = witem->x;
       simple->bounds.y1 = witem->y;
-      width = witem->width < 0 ? requisition.width : witem->width;
-      height = witem->height < 0 ? requisition.height : witem->height;
 
-      switch (witem->anchor)
-	{
-	case GTK_ANCHOR_N:
-	case GTK_ANCHOR_CENTER:
-	case GTK_ANCHOR_S:
-	  simple->bounds.x1 -= width / 2.0;
-	  break;
-	case GTK_ANCHOR_NE:
-	case GTK_ANCHOR_E:
-	case GTK_ANCHOR_SE:
-	  simple->bounds.x1 -= width;
-	  break;
-	default:
-	  break;
-	}
-
-      switch (witem->anchor)
-	{
-	case GTK_ANCHOR_W:
-	case GTK_ANCHOR_CENTER:
-	case GTK_ANCHOR_E:
-	  simple->bounds.y1 -= height / 2.0;
-	  break;
-	case GTK_ANCHOR_SW:
-	case GTK_ANCHOR_S:
-	case GTK_ANCHOR_SE:
-	  simple->bounds.y1 -= height;
-	  break;
-	default:
-	  break;
-	}
+      simple->bounds.x1 -=
+        goo_canvas_widget_anchor_horizontal_pos (witem->anchor, width);
+      simple->bounds.y1 -=
+        goo_canvas_widget_anchor_vertical_pos (witem->anchor, height);
 
       simple->bounds.x2 = simple->bounds.x1 + width;
       simple->bounds.y2 = simple->bounds.y1 + height;
@@ -554,6 +627,7 @@ goo_canvas_widget_class_init (GooCanvasW
 
   g_object_class_override_property (gobject_class, PROP_VISIBILITY,
 				    "visibility");
+  g_object_class_override_property (gobject_class, PROP_EXTENT, "extent");
 }
 
 
Index: src/goocanvastext.c
===================================================================
--- src/goocanvastext.c	(revision 15)
+++ src/goocanvastext.c	(working copy)
@@ -27,6 +27,7 @@
 #include <config.h>
 #include <glib/gi18n-lib.h>
 #include <gtk/gtk.h>
+#include <math.h> /* for fabs () */
 #include "goocanvastext.h"
 #include "goocanvas.h"
 
@@ -42,9 +43,21 @@ enum {
   PROP_ANCHOR,
   PROP_ALIGN,
   PROP_ELLIPSIZE,
-  PROP_WRAP
+  PROP_WRAP,
+
+  /* overriden */
+  PROP_EXTENT
 };
 
+static PangoLayout*
+goo_canvas_text_create_layout (GooCanvasItemSimpleData *simple_data,
+			       GooCanvasTextData       *text_data,
+			       gdouble                  layout_width,
+			       cairo_t                 *cr,
+			       GooCanvasBounds         *bounds,
+			       gdouble	               *origin_x_return,
+			       gdouble	               *origin_y_return);
+
 static void goo_canvas_text_finalize     (GObject            *object);
 static void canvas_item_interface_init   (GooCanvasItemIface *iface);
 static void goo_canvas_text_get_property (GObject            *object,
@@ -136,6 +149,8 @@ goo_canvas_text_install_common_propertie
 						      PANGO_TYPE_ALIGNMENT,
 						      PANGO_ALIGN_LEFT,
 						      G_PARAM_READWRITE));
+
+  g_object_class_override_property (gobject_class, PROP_EXTENT, "extent");
 }
 
 
@@ -245,11 +260,18 @@ goo_canvas_text_finalize (GObject *objec
 
 static void
 goo_canvas_text_get_common_property (GObject                 *object,
+                                     GooCanvas               *canvas,
+                                     GooCanvasItemSimpleData *simple_data,
 				     GooCanvasTextData       *text_data,
 				     guint                    prop_id,
 				     GValue                  *value,
 				     GParamSpec              *pspec)
 {
+  PangoLayout *layout;
+  GooCanvasBounds bounds;
+  gdouble origin_x, origin_y;
+  cairo_t *cr;
+
   switch (prop_id)
     {
     case PROP_X:
@@ -279,6 +301,26 @@ goo_canvas_text_get_common_property (GOb
     case PROP_ALIGN:
       g_value_set_enum (value, text_data->alignment);
       break;
+    case PROP_EXTENT:
+      /* This creates a default context if canvas is NULL. However, it does
+       * not make too much sense to query the extent of a GooCanvasTextModel
+       * anyway, since the result depends on the widget's font options the 
+       * model item is displayed in. */
+      cr = goo_canvas_create_cairo_context(canvas);
+
+      layout = goo_canvas_text_create_layout (simple_data, text_data,
+                                              text_data->width, cr, &bounds,
+                                              &origin_x, &origin_y);
+      g_object_unref (layout);
+      cairo_destroy (cr);
+
+      bounds.x1 = origin_x;
+      bounds.y1 = origin_y;
+      if(text_data->width > 0)
+        bounds.x2 = origin_x + text_data->width;
+
+      g_value_set_boxed (value, &bounds);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -292,10 +334,12 @@ goo_canvas_text_get_property (GObject   
 			      GValue               *value,
 			      GParamSpec           *pspec)
 {
+  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
   GooCanvasText *text = (GooCanvasText*) object;
 
-  goo_canvas_text_get_common_property (object, text->text_data, prop_id,
-				       value, pspec);
+  goo_canvas_text_get_common_property (object, simple->canvas,
+                                       simple->simple_data, text->text_data,
+                                       prop_id, value, pspec);
 }
 
 
@@ -306,36 +350,84 @@ goo_canvas_text_set_common_property (GOb
 				     const GValue            *value,
 				     GParamSpec              *pspec)
 {
+  GooCanvasBounds* extent;
+  gdouble height;
+
   switch (prop_id)
     {
     case PROP_X:
       text_data->x = g_value_get_double (value);
+      g_object_notify(object, "extent");
       break;
     case PROP_Y:
       text_data->y = g_value_get_double (value);
+      g_object_notify(object, "extent");
       break;
     case PROP_WIDTH:
       text_data->width = g_value_get_double (value);
+      g_object_notify(object, "extent");
       break;
     case PROP_TEXT:
       g_free (text_data->text);
       text_data->text = g_value_dup_string (value);
+      g_object_notify(object, "extent");
       break;
     case PROP_USE_MARKUP:
       text_data->use_markup = g_value_get_boolean (value);
+      g_object_notify(object, "extent");
       break;
     case PROP_ELLIPSIZE:
       text_data->ellipsize = g_value_get_enum (value);
+      g_object_notify(object, "extent");
       break;
     case PROP_WRAP:
       text_data->wrap = g_value_get_enum (value);
+      g_object_notify(object, "extent");
       break;
     case PROP_ANCHOR:
       text_data->anchor = g_value_get_enum (value);
+      g_object_notify(object, "extent");
       break;
     case PROP_ALIGN:
       text_data->alignment = g_value_get_enum (value);
       break;
+    case PROP_EXTENT:
+      extent = g_value_get_boxed (value);
+      text_data->width = fabs(extent->x1 - extent->x2);
+      height = fabs(extent->y1 - extent->y2);
+      text_data->x = extent->x1;
+      text_data->y = extent->y1;
+      switch (text_data->anchor)
+        {
+        case GTK_ANCHOR_N:
+        case GTK_ANCHOR_CENTER:
+        case GTK_ANCHOR_S:
+          text_data->x -= text_data->width / 2.0;
+          break;
+        case GTK_ANCHOR_NE:
+        case GTK_ANCHOR_E:
+        case GTK_ANCHOR_SE:
+          text_data->x -= text_data->width;
+          break;
+        }
+      switch (text_data->anchor)
+        {
+        case GTK_ANCHOR_W:
+        case GTK_ANCHOR_CENTER:
+        case GTK_ANCHOR_E:
+          text_data->y -= height / 2.0;
+          break;
+        case GTK_ANCHOR_SW:
+        case GTK_ANCHOR_S:
+        case GTK_ANCHOR_SE:
+          text_data->y -= height;
+          break;
+        }
+
+      g_object_notify(object, "x");
+      g_object_notify(object, "y");
+      g_object_notify(object, "width");
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -951,9 +1043,11 @@ goo_canvas_text_model_get_property (GObj
 				    GValue               *value,
 				    GParamSpec           *pspec)
 {
+  GooCanvasItemModelSimple *smodel = (GooCanvasItemModelSimple*) object;
   GooCanvasTextModel *tmodel = (GooCanvasTextModel*) object;
 
-  goo_canvas_text_get_common_property (object, &tmodel->text_data, prop_id,
+  goo_canvas_text_get_common_property (object, NULL, &smodel->simple_data,
+                                       &tmodel->text_data, prop_id,
 				       value, pspec);
 }
 
Index: src/goocanvastable.c
===================================================================
--- src/goocanvastable.c	(revision 15)
+++ src/goocanvastable.c	(working copy)
@@ -60,7 +60,10 @@ enum
   PROP_X_BORDER_SPACING,
   PROP_Y_BORDER_SPACING,
   PROP_VERT_GRID_LINE_WIDTH,
-  PROP_HORZ_GRID_LINE_WIDTH
+  PROP_HORZ_GRID_LINE_WIDTH,
+
+  /* overriden */
+  PROP_EXTENT
 };
 
 enum
@@ -288,6 +291,8 @@ goo_canvas_table_install_common_properti
                                                        0.0, G_MAXDOUBLE, 0.0,
                                                        G_PARAM_READWRITE));
 
+  g_object_class_override_property (gobject_class, PROP_EXTENT, "extent");
+
   /*
    * Child properties.
    */
@@ -616,9 +621,30 @@ goo_canvas_table_get_property (GObject  
 			       GParamSpec           *pspec)
 {
   GooCanvasTable *table = (GooCanvasTable*) object;
+  GooCanvasBounds extent;
+
+  switch (prop_id)
+    {
+    case PROP_EXTENT:
+      extent.x1 = extent.y1 = 0.0;
+      if (table->table_data->width > 0.0)
+        extent.x2 = table->table_data->width;
+      else
+        extent.x2 = table->table_data->layout_data->natural_size[HORZ];
+
+      if (table->table_data->height > 0.0)
+        extent.y2 = table->table_data->height;
+      else
+        extent.y2 = table->table_data->layout_data->natural_size[VERT];
+
+      g_value_set_boxed (value, &extent);
+      break;
+    default:
+      goo_canvas_table_get_common_property (object, table->table_data,
+                                            prop_id, value, pspec);
+      break;
+    }
 
-  goo_canvas_table_get_common_property (object, table->table_data,
-					prop_id, value, pspec);
 }
 
 
@@ -630,14 +656,17 @@ goo_canvas_table_set_common_property (GO
 				      GParamSpec           *pspec)
 {
   gboolean recompute_bounds = TRUE;
+  GooCanvasBounds* extent;
 
   switch (prop_id)
     {
     case PROP_WIDTH:
       table_data->width = g_value_get_double (value);
+      g_object_notify (object, "extent");
       break;
     case PROP_HEIGHT:
       table_data->height = g_value_get_double (value);
+      g_object_notify (object, "extent");
       break;
     case PROP_ROW_SPACING:
       table_data->dimensions[VERT].default_spacing = g_value_get_double (value);
@@ -663,6 +692,14 @@ goo_canvas_table_set_common_property (GO
     case PROP_VERT_GRID_LINE_WIDTH:
       table_data->layout_data->prop_grid_line_width[VERT] = g_value_get_double (value);
       break;
+    case PROP_EXTENT:
+      extent = g_value_get_boxed (value);
+      table_data->width = fabs (extent->x2 - extent->x1);
+      table_data->height = fabs (extent->y2 - extent->y1);
+      /* TODO: Add X/Y property, adjust accordingly */
+      g_object_notify (object, "width");
+      g_object_notify (object, "height");
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1933,7 +1970,7 @@ goo_canvas_table_get_requested_area (Goo
   GooCanvasTableDimensionLayoutData *rows, *columns;
   gdouble width = 0.0, height = 0.0;
   gint row, column, end;
-  
+
   /* We reset the bounds to 0, just in case we are hidden or aren't allocated
      any area. */
   simple->bounds.x1 = simple->bounds.x2 = 0.0;
@@ -2003,6 +2040,8 @@ goo_canvas_table_get_requested_area (Goo
 
   /* Save the natural size, so we know if we have to clip children. */
   layout_data->natural_size[VERT] = height;
+  if (table_data->width < 0.0 || table_data->height < 0.0)
+    g_object_notify (G_OBJECT (table), "extent");
 
   /* If the height has been set, that overrides the calculations. */
   if (table_data->height > 0.0)
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 15)
+++ ChangeLog	(working copy)
@@ -1,3 +1,25 @@
+2008-10-05  Armin Burgmeier  <armin@openismus.com>
+
+	* src/goocanvasitem.c:
+	* src/goocanvasitemmodel.c: Added the "extent" property.
+
+	* src/goocanvasrect.c:
+	* src/goocanvasellipse.c:
+	* src/goocanvasimage.c:
+	* src/goocanvaspolyline.c:
+	* src/goocanvasgroup.c:
+	* src/goocanvaspath.c:
+	* src/goocanvasitemsimple.c:
+	* src/goocanvaswidget.c:
+	* src/goocanvastext.c:
+	* src/goocanvastable.c: Override and implement setting and getting the
+	"extent" property by modifying internal data structures so that the
+	item fits into the bounds given by the extent.
+
+	* demo/extent-demo.c:
+	* demo/Makefile.am: Added a demo showing how this can be used to
+	generically resize items using drag and drop.
+
 2008-09-21  Damon Chaplin  <damon@gnome.org>
 
 	* Released GooCanvas 0.12
