Index: demo/generic-position-demo.c
===================================================================
--- demo/generic-position-demo.c	(revision 0)
+++ demo/generic-position-demo.c	(revision 0)
@@ -0,0 +1,221 @@
+#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;
+
+gdouble          item_x = 0.0;
+gdouble          item_y = 0.0;
+gdouble          item_width = 0.0;
+gdouble          item_height = 0.0;
+
+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),
+                    "x", &item_x,
+                    "y", &item_y,
+                    "width", &item_width,
+                    "height", &item_height,
+                    NULL);
+
+      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;
+
+    if (drag_mode == MODE_MOVE)
+    {
+      g_object_set (G_OBJECT (item), "x", item_x + rel_x, "y", item_y + rel_y, NULL);
+    }
+    else
+    {
+      gdouble new_width = MAX(item_width + rel_x, 5.0);
+      gdouble new_height = MAX(item_height + rel_y, 5.0);
+
+      g_object_set (G_OBJECT (item), "width", new_width, "height", new_height, 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));
+
+  /* Test clipping of GooCanvasGroup: We put the rectangle and the ellipse into
+   * a group with width=200 and height=200. */
+  item = goo_canvas_group_new (root, "x", 50.0, "y", 350.0, "width", 200.0, "height", 200.0, NULL);
+  /*goo_canvas_item_rotate(item, 45.0, 150.0, 450.0);*/
+
+  child = goo_canvas_rect_new (item, 0.0, 0.0, 100, 100, "fill-color", "blue", NULL);
+  setup_dnd_handlers (GOO_CANVAS (canvas), child);
+  goo_canvas_item_rotate(child, 45.0, 50.0, 50.0);
+
+  child = goo_canvas_ellipse_new (item, 150, 00, 50, 50, "fill-color", "red", NULL);
+  setup_dnd_handlers (GOO_CANVAS (canvas), child);
+
+  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", "x", 10.0, "y", 25.0, 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: demo/Makefile.am
===================================================================
--- demo/Makefile.am	(revision 20)
+++ 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 generic-position-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)
 
+generic_position_demo_SOURCES = \
+	generic-position-demo.c
+
+generic_position_demo_LDADD = $(top_builddir)/src/libgoocanvas.la @PACKAGE_LIBS@ $(INTLLIBS)
+
 mv_table_demo_SOURCES = \
 	mv-table-demo.c
 
Index: src/goocanvasellipse.c
===================================================================
--- src/goocanvasellipse.c	(revision 20)
+++ src/goocanvasellipse.c	(working copy)
@@ -37,7 +37,12 @@ enum {
   PROP_CENTER_X,
   PROP_CENTER_Y,
   PROP_RADIUS_X,
-  PROP_RADIUS_Y
+  PROP_RADIUS_Y,
+
+  PROP_X,
+  PROP_Y,
+  PROP_WIDTH,
+  PROP_HEIGHT
 };
 
 
@@ -92,6 +97,36 @@ goo_canvas_ellipse_install_common_proper
 							_("The vertical radius of the ellipse"),
 							0.0, G_MAXDOUBLE, 0.0,
 							G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_X,
+				   g_param_spec_double ("x",
+							"X",
+							_("The x coordinate of the rectangle"),
+							-G_MAXDOUBLE,
+							G_MAXDOUBLE, 0.0,
+							G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_Y,
+				   g_param_spec_double ("y",
+							"Y",
+							_("The y coordinate of the ellipse"),
+							-G_MAXDOUBLE,
+							G_MAXDOUBLE, 0.0,
+							G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_WIDTH,
+				   g_param_spec_double ("width",
+							_("Width"),
+							_("The width of the ellipse"),
+							0.0, G_MAXDOUBLE, 0.0,
+							G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_HEIGHT,
+				   g_param_spec_double ("height",
+							_("Height"),
+							_("The height of the ellipse"),
+							0.0, G_MAXDOUBLE, 0.0,
+							G_PARAM_READWRITE));
 }
 
 
@@ -224,6 +259,18 @@ goo_canvas_ellipse_get_common_property (
     case PROP_RADIUS_Y:
       g_value_set_double (value, ellipse_data->radius_y);
       break;
+    case PROP_X:
+      g_value_set_double (value, ellipse_data->center_x - ellipse_data->radius_x/2.0);
+      break;
+    case PROP_Y:
+      g_value_set_double (value, ellipse_data->center_y - ellipse_data->radius_y/2.0);
+      break;
+    case PROP_WIDTH:
+      g_value_set_double (value, 2.0 * ellipse_data->radius_x);
+      break;
+    case PROP_HEIGHT:
+      g_value_set_double (value, 2.0 * ellipse_data->radius_y);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -255,15 +302,39 @@ goo_canvas_ellipse_set_common_property (
     {
     case PROP_CENTER_X:
       ellipse_data->center_x = g_value_get_double (value);
+      g_object_notify(object, "x");
       break;
     case PROP_CENTER_Y:
       ellipse_data->center_y = g_value_get_double (value);
+      g_object_notify(object, "y");
       break;
     case PROP_RADIUS_X:
       ellipse_data->radius_x = g_value_get_double (value);
+      g_object_notify(object, "width");
       break;
     case PROP_RADIUS_Y:
       ellipse_data->radius_y = g_value_get_double (value);
+      g_object_notify(object, "height");
+      break;
+    case PROP_X:
+      ellipse_data->center_x = g_value_get_double (value) + ellipse_data->radius_x/2.0;
+      g_object_notify(object, "center-x");
+      break;
+    case PROP_Y:
+      ellipse_data->center_y = g_value_get_double (value) + ellipse_data->radius_y/2.0;
+      g_object_notify(object, "center-y");
+      break;
+    case PROP_WIDTH:
+      ellipse_data->center_x += g_value_get_double (value) / 2.0 - ellipse_data->radius_x;
+      ellipse_data->radius_x = g_value_get_double (value) / 2.0;
+      g_object_notify(object, "center-x");
+      g_object_notify(object, "radius-x");
+      break;
+    case PROP_HEIGHT:
+      ellipse_data->center_y += g_value_get_double (value) / 2.0 - ellipse_data->radius_y;
+      ellipse_data->radius_y = g_value_get_double (value) / 2.0;
+      g_object_notify(object, "center-y");
+      g_object_notify(object, "radius-y");
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Index: src/goocanvaspolyline.c
===================================================================
--- src/goocanvaspolyline.c	(revision 20)
+++ src/goocanvaspolyline.c	(working copy)
@@ -117,7 +117,12 @@ enum {
   PROP_END_ARROW,
   PROP_ARROW_LENGTH,
   PROP_ARROW_WIDTH,
-  PROP_ARROW_TIP_LENGTH
+  PROP_ARROW_TIP_LENGTH,
+
+  PROP_X,
+  PROP_Y,
+  PROP_WIDTH,
+  PROP_HEIGHT
 };
 
 
@@ -180,6 +185,36 @@ 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_install_property (gobject_class, PROP_X,
+				   g_param_spec_double ("x",
+							"X",
+							_("The x coordinate of the path"),
+							-G_MAXDOUBLE,
+							G_MAXDOUBLE, 0.0,
+							G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_Y,
+				   g_param_spec_double ("y",
+							"Y",
+							_("The y coordinate of the path"),
+							-G_MAXDOUBLE,
+							G_MAXDOUBLE, 0.0,
+							G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_WIDTH,
+				   g_param_spec_double ("width",
+							_("Width"),
+							_("The width of the polyline"),
+							0.0, G_MAXDOUBLE, 0.0,
+							G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_HEIGHT,
+				   g_param_spec_double ("height",
+							_("Height"),
+							_("The height of the polyline"),
+							0.0, G_MAXDOUBLE, 0.0,
+							G_PARAM_READWRITE));
 }
 
 
@@ -212,6 +247,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,
@@ -219,6 +280,7 @@ goo_canvas_polyline_get_common_property 
 					 GParamSpec           *pspec)
 {
   GooCanvasPoints *points;
+  GooCanvasBounds  extent;
 
   switch (prop_id)
     {
@@ -257,6 +319,22 @@ 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_X:
+      goo_canvas_polyline_get_extent (polyline_data, &extent);
+      g_value_set_double (value, extent.x1);
+      break;
+    case PROP_Y:
+      goo_canvas_polyline_get_extent (polyline_data, &extent);
+      g_value_set_double (value, extent.y1);
+      break;
+    case PROP_WIDTH:
+      goo_canvas_polyline_get_extent (polyline_data, &extent);
+      g_value_set_double (value, extent.x2 - extent.x1);
+      break;
+    case PROP_HEIGHT:
+      goo_canvas_polyline_get_extent (polyline_data, &extent);
+      g_value_set_double (value, extent.y2 - extent.y1);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -428,6 +506,9 @@ goo_canvas_polyline_set_common_property 
 					 GParamSpec           *pspec)
 {
   GooCanvasPoints *points;
+  GooCanvasBounds  extent;
+  gdouble rel_x, rel_y;
+  guint i;
 
   switch (prop_id)
     {
@@ -452,6 +533,10 @@ goo_canvas_polyline_set_common_property 
 		  polyline_data->num_points * 2 * sizeof (double));
 	}
       polyline_data->reconfigure_arrows = TRUE;
+      g_object_notify(object, "x");
+      g_object_notify(object, "y");
+      g_object_notify(object, "width");
+      g_object_notify(object, "height");
       break;
     case PROP_CLOSE_PATH:
       polyline_data->close_path = g_value_get_boolean (value);
@@ -480,6 +565,52 @@ 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_X:
+      if (polyline_data->num_points > 0)
+        {
+          goo_canvas_polyline_get_extent (polyline_data, &extent);
+          rel_x = g_value_get_double (value) - extent.x1;
+          for(i = 0; i < polyline_data->num_points; ++ i)
+            polyline_data->coords[2 * i] += rel_x;
+          g_object_notify(object, "points");
+        }
+      break;
+    case PROP_Y:
+      if (polyline_data->num_points > 0)
+        {
+          goo_canvas_polyline_get_extent (polyline_data, &extent);
+          rel_y = g_value_get_double (value) - extent.y1;
+          for (i = 0; i < polyline_data->num_points; ++ i)
+            polyline_data->coords[2 * i + 1] += rel_y;
+          g_object_notify(object, "points");
+        }
+      break;
+    case PROP_WIDTH:
+      if (polyline_data->num_points >= 2)
+        {
+          goo_canvas_polyline_get_extent (polyline_data, &extent);
+          if (extent.x2 - extent.x1 != 0.0)
+            {
+              rel_x = g_value_get_double(value) / (extent.x2 - extent.x1);
+              for (i = 0; i < polyline_data->num_points; ++ i)
+                polyline_data->coords[2 * i] = extent.x1 + (polyline_data->coords[2 * i] - extent.x1) * rel_x;
+              g_object_notify(object, "points");
+            }
+        }
+      break;
+    case PROP_HEIGHT:
+      if (polyline_data->num_points >= 2)
+        {
+          goo_canvas_polyline_get_extent (polyline_data, &extent);
+          if (extent.y2 - extent.y1 != 0.0)
+            {
+              rel_y = g_value_get_double(value) / (extent.y2 - extent.y1);
+              for (i = 0; i < polyline_data->num_points; ++ i)
+                polyline_data->coords[2 * i + 1] = extent.y1 + (polyline_data->coords[2 * i + 1] - extent.y1) * rel_y;
+              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 20)
+++ src/goocanvasgroup.c	(working copy)
@@ -25,6 +25,10 @@
  * goo_canvas_item_rotate(), and the properties such as "visibility" and
  * "pointer-events".
  *
+ * The group's children items are clipped to the region specified by the
+ * #GooCanvasGroup:width and #GooCanvasGroup:height properties unless width
+ * or height are -1, in which case the clipping is deactivated.
+ *
  * To create a #GooCanvasGroup use goo_canvas_group_new().
  *
  * To get or set the properties of an existing #GooCanvasGroup, use
@@ -40,9 +44,36 @@
 #include "goocanvasmarshal.h"
 #include "goocanvasatk.h"
 
+typedef struct _GooCanvasGroupPrivate GooCanvasGroupPrivate;
+struct _GooCanvasGroupPrivate {
+  gdouble x;
+  gdouble y;
+  gdouble width;
+  gdouble height;
+};
+
+#define GOO_CANVAS_GROUP_GET_PRIVATE(group)  \
+   (G_TYPE_INSTANCE_GET_PRIVATE ((group), GOO_TYPE_CANVAS_GROUP, GooCanvasGroupPrivate))
+
+enum {
+  PROP_0,
+
+  PROP_X,
+  PROP_Y,
+  PROP_WIDTH,
+  PROP_HEIGHT
+};
 
 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,14 +81,52 @@ 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_install_property (gobject_class, PROP_X,
+				   g_param_spec_double ("x",
+							"X",
+							_("The x coordinate of the group"),
+							-G_MAXDOUBLE,
+							G_MAXDOUBLE, 0.0,
+							G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_Y,
+				   g_param_spec_double ("y",
+							"Y",
+							_("The y coordinate of the group"),
+							-G_MAXDOUBLE,
+							G_MAXDOUBLE, 0.0,
+							G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_WIDTH,
+				   g_param_spec_double ("width",
+							_("Width"),
+							_("The width of the group, or -1"),
+							-G_MAXDOUBLE,
+							G_MAXDOUBLE, -1.0,
+							G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_HEIGHT,
+				   g_param_spec_double ("height",
+							_("Height"),
+							_("The height of the group, or -1"),
+							-G_MAXDOUBLE,
+							G_MAXDOUBLE, -1.0,
+							G_PARAM_READWRITE));
+}
 
 static void
 goo_canvas_group_class_init (GooCanvasGroupClass *klass)
 {
   GObjectClass *gobject_class = (GObjectClass*) klass;
+  g_type_class_add_private (gobject_class, sizeof (GooCanvasGroupPrivate));
 
   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,13 +135,23 @@ 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);
 }
 
 
 static void
 goo_canvas_group_init (GooCanvasGroup *group)
 {
+  GooCanvasGroupPrivate* priv;
+  priv = GOO_CANVAS_GROUP_GET_PRIVATE(group);
+
   group->items = g_ptr_array_sized_new (8);
+
+  priv->x = 0.0;
+  priv->y = 0.0;
+  priv->width = -1.0;
+  priv->height = -1.0;
 }
 
 
@@ -145,6 +224,86 @@ goo_canvas_group_finalize (GObject *obje
   G_OBJECT_CLASS (goo_canvas_group_parent_class)->finalize (object);
 }
 
+static void
+goo_canvas_group_get_common_property (GObject           *object,
+                                      guint              prop_id,
+                                      GValue            *value,
+                                      GParamSpec        *pspec)
+{
+  GooCanvasGroup *group = (GooCanvasGroup*) object;
+
+  switch (prop_id)
+    {
+    case PROP_X:
+      g_value_set_double (value, GOO_CANVAS_GROUP_GET_PRIVATE (group)->x);
+      break;
+    case PROP_Y:
+      g_value_set_double (value, GOO_CANVAS_GROUP_GET_PRIVATE (group)->y);
+      break;
+    case PROP_WIDTH:
+      g_value_set_double (value, GOO_CANVAS_GROUP_GET_PRIVATE (group)->width);
+      break;
+    case PROP_HEIGHT:
+      g_value_set_double (value, GOO_CANVAS_GROUP_GET_PRIVATE (group)->height);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+goo_canvas_group_get_property (GObject               *object,
+                               guint                  prop_id,
+                               GValue                *value,
+                               GParamSpec            *pspec)
+{
+  goo_canvas_group_get_common_property (object, prop_id, value, pspec);
+}
+
+static void
+goo_canvas_group_set_common_property (GObject           *object,
+                                      guint              prop_id,
+                                      const GValue      *value,
+                                      GParamSpec        *pspec)
+{
+  switch (prop_id)
+    {
+    case PROP_X:
+      GOO_CANVAS_GROUP_GET_PRIVATE (object)->x = g_value_get_double (value);
+      break;
+    case PROP_Y:
+      GOO_CANVAS_GROUP_GET_PRIVATE (object)->y = g_value_get_double (value);
+      break;
+    case PROP_WIDTH:
+      GOO_CANVAS_GROUP_GET_PRIVATE (object)->width = g_value_get_double (value);
+      break;
+    case PROP_HEIGHT:
+      GOO_CANVAS_GROUP_GET_PRIVATE (object)->height = g_value_get_double (value);
+      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;
+
+  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, prop_id, value, pspec);
+  goo_canvas_item_simple_changed (simple, TRUE);
+}
 
 static void
 goo_canvas_group_add_child     (GooCanvasItem  *item,
@@ -234,11 +393,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);
 }
 
@@ -375,6 +534,7 @@ goo_canvas_group_update  (GooCanvasItem 
 {
   GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
   GooCanvasGroup *group = (GooCanvasGroup*) item;
+  GooCanvasGroupPrivate *priv = GOO_CANVAS_GROUP_GET_PRIVATE (group);
   GooCanvasBounds child_bounds;
   gboolean initial_bounds = TRUE;
   gint i;
@@ -394,35 +554,44 @@ goo_canvas_group_update  (GooCanvasItem 
 
       cairo_save (cr);
       if (simple->simple_data->transform)
-	cairo_transform (cr, simple->simple_data->transform);
+        cairo_transform (cr, simple->simple_data->transform);
+
+      cairo_translate (cr, priv->x, priv->y);
 
       for (i = 0; i < group->items->len; i++)
-	{
-	  GooCanvasItem *child = group->items->pdata[i];
+        {
+          GooCanvasItem *child = group->items->pdata[i];
 
-	  goo_canvas_item_update (child, entire_tree, cr, &child_bounds);
-	  
-	  /* If the child has non-empty bounds, compute the union. */
-	  if (child_bounds.x1 < child_bounds.x2
-	      && child_bounds.y1 < child_bounds.y2)
-	    {
-	      if (initial_bounds)
-		{
-		  simple->bounds.x1 = child_bounds.x1;
-		  simple->bounds.y1 = child_bounds.y1;
-		  simple->bounds.x2 = child_bounds.x2;
-		  simple->bounds.y2 = child_bounds.y2;
-		  initial_bounds = FALSE;
-		}
-	      else
-		{
-		  simple->bounds.x1 = MIN (simple->bounds.x1, child_bounds.x1);
-		  simple->bounds.y1 = MIN (simple->bounds.y1, child_bounds.y1);
-		  simple->bounds.x2 = MAX (simple->bounds.x2, child_bounds.x2);
-		  simple->bounds.y2 = MAX (simple->bounds.y2, child_bounds.y2);
-		}
-	    }
+          goo_canvas_item_update (child, entire_tree, cr, &child_bounds);
+          
+          /* If the child has non-empty bounds, compute the union. */
+          if (child_bounds.x1 < child_bounds.x2
+              && child_bounds.y1 < child_bounds.y2)
+            {
+              if (initial_bounds)
+                {
+                  simple->bounds.x1 = child_bounds.x1;
+                  simple->bounds.y1 = child_bounds.y1;
+                  simple->bounds.x2 = child_bounds.x2;
+                  simple->bounds.y2 = child_bounds.y2;
+                  initial_bounds = FALSE;
+                }
+              else
+                {
+                  simple->bounds.x1 = MIN (simple->bounds.x1, child_bounds.x1);
+                  simple->bounds.y1 = MIN (simple->bounds.y1, child_bounds.y1);
+                  simple->bounds.x2 = MAX (simple->bounds.x2, child_bounds.x2);
+                  simple->bounds.y2 = MAX (simple->bounds.y2, child_bounds.y2);
+                }
+            }
+        }
+
+      if(priv->width > 0 && priv->height > 0)
+        {
+          g_object_notify (G_OBJECT (item), "width");
+          g_object_notify (G_OBJECT (item), "height");
 	}
+
       cairo_restore (cr);
     }
 
@@ -442,6 +611,7 @@ goo_canvas_group_get_items_at (GooCanvas
   GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
   GooCanvasItemSimpleData *simple_data = simple->simple_data;
   GooCanvasGroup *group = (GooCanvasGroup*) item;
+  GooCanvasGroupPrivate *priv = GOO_CANVAS_GROUP_GET_PRIVATE (group);
   gboolean visible = parent_visible;
   int i;
 
@@ -469,6 +639,8 @@ goo_canvas_group_get_items_at (GooCanvas
   if (simple_data->transform)
     cairo_transform (cr, simple_data->transform);
 
+  cairo_translate (cr, priv->x, priv->y);
+
   /* If the group has a clip path, check if the point is inside it. */
   if (simple_data->clip_path_commands)
     {
@@ -484,6 +656,20 @@ goo_canvas_group_get_items_at (GooCanvas
 	}
     }
 
+  if (priv->width > 0.0 && priv->height > 0.0)
+    {
+      double user_x = x, user_y = y;
+
+      cairo_device_to_user (cr, &user_x, &user_y);
+      cairo_rectangle (cr, 0.0, 0.0, priv->width, priv->height);
+      cairo_set_fill_rule (cr, simple_data->clip_fill_rule);
+      if (!cairo_in_fill (cr, user_x, user_y))
+	{
+	  cairo_restore (cr);
+	  return found_items;
+	}
+    }
+
   /* Step up from the bottom of the children to the top, adding any items
      found to the start of the list. */
   for (i = 0; i < group->items->len; i++)
@@ -509,6 +695,7 @@ goo_canvas_group_paint (GooCanvasItem   
   GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
   GooCanvasItemSimpleData *simple_data = simple->simple_data;
   GooCanvasGroup *group = (GooCanvasGroup*) item;
+  GooCanvasGroupPrivate *priv = GOO_CANVAS_GROUP_GET_PRIVATE (group);
   gint i;
 
   /* Skip the item if the bounds don't intersect the expose rectangle. */
@@ -527,6 +714,8 @@ goo_canvas_group_paint (GooCanvasItem   
   if (simple_data->transform)
     cairo_transform (cr, simple_data->transform);
 
+  cairo_translate (cr, priv->x, priv->y);
+
   /* Clip with the group's clip path, if it is set. */
   if (simple_data->clip_path_commands)
     {
@@ -535,6 +724,13 @@ goo_canvas_group_paint (GooCanvasItem   
       cairo_clip (cr);
     }
 
+  if (priv->width > 0.0 && priv->height > 0.0)
+    {
+      cairo_rectangle (cr, 0.0, 0.0, priv->width, priv->height);
+      cairo_set_fill_rule (cr, simple_data->clip_fill_rule);
+      cairo_clip (cr);
+    }
+
   for (i = 0; i < group->items->len; i++)
     {
       GooCanvasItem *child = group->items->pdata[i];
@@ -596,6 +792,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,
@@ -607,16 +811,27 @@ static void
 goo_canvas_group_model_class_init (GooCanvasGroupModelClass *klass)
 {
   GObjectClass *gobject_class = (GObjectClass*) klass;
+  g_type_class_add_private (gobject_class, sizeof (GooCanvasGroupPrivate));
 
   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);
 }
 
 
 static void
 goo_canvas_group_model_init (GooCanvasGroupModel *gmodel)
 {
+  GooCanvasGroupPrivate *priv = GOO_CANVAS_GROUP_GET_PRIVATE (gmodel);
   gmodel->children = g_ptr_array_sized_new (8);
+
+  priv->x = 0.0;
+  priv->y = 0.0;
+  priv->width = -1.0;
+  priv->height = -1.0;
 }
 
 
@@ -689,6 +904,26 @@ 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;
+
+  goo_canvas_group_get_common_property (object, prop_id, value, pspec);
+}
+
+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, 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 +976,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 20)
+++ src/goocanvaspath.c	(working copy)
@@ -33,12 +33,18 @@
 #include <glib/gi18n-lib.h>
 #include <gtk/gtk.h>
 #include "goocanvaspath.h"
+#include "goocanvas.h"
 
 
 enum {
   PROP_0,
 
   PROP_DATA,
+
+  PROP_X,
+  PROP_Y,
+  PROP_WIDTH,
+  PROP_HEIGHT
 };
 
 static void canvas_item_interface_init   (GooCanvasItemIface  *iface);
@@ -65,6 +71,36 @@ goo_canvas_path_install_common_propertie
 							_("The sequence of path commands"),
 							NULL,
 							G_PARAM_WRITABLE));
+
+  g_object_class_install_property (gobject_class, PROP_X,
+				   g_param_spec_double ("x",
+							"X",
+							_("The x coordinate of the path"),
+							-G_MAXDOUBLE,
+							G_MAXDOUBLE, 0.0,
+							G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_Y,
+				   g_param_spec_double ("y",
+							"Y",
+							_("The y coordinate of the path"),
+							-G_MAXDOUBLE,
+							G_MAXDOUBLE, 0.0,
+							G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_WIDTH,
+				   g_param_spec_double ("width",
+							_("Width"),
+							_("The width of the path"),
+							0.0, G_MAXDOUBLE, 0.0,
+							G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_HEIGHT,
+				   g_param_spec_double ("height",
+							_("Height"),
+							_("The height of the path"),
+							0.0, G_MAXDOUBLE, 0.0,
+							G_PARAM_READWRITE));
 }
 
 
@@ -172,16 +208,159 @@ 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_command_move_position (GooCanvasPathCommand *cmd,
+                                       gdouble x,
+                                       gdouble y)
+{
+  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:
+      if (!cmd->simple.relative)
+        {
+          cmd->simple.x += x;
+          cmd->simple.y += y;
+        }
+      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:
+      if (!cmd->curve.relative)
+        {
+          cmd->curve.x += x;
+          cmd->curve.y += y;
+          cmd->curve.x1 += x;
+          cmd->curve.y1 += y;
+          cmd->curve.x2 += x;
+          cmd->curve.y2 += y;
+        }
+      break;
+    case GOO_CANVAS_PATH_ELLIPTICAL_ARC:
+      if (!cmd->arc.relative)
+        {
+          cmd->arc.x += x;
+          cmd->arc.y += y;
+        }
+      break;
+    default:
+      g_assert_not_reached();
+      break;
+    }
+}
+
+/* ref_x,ref_y is the center of the scaling. */
+static void
+goo_canvas_path_command_resize_position (GooCanvasPathCommand *cmd,
+                                         gdouble ref_x,
+                                         gdouble ref_y,
+                                         gdouble ratio_x,
+                                         gdouble ratio_y)
+{
+  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:
+      if (cmd->simple.relative)
+        {
+          cmd->simple.x *= ratio_x;
+          cmd->simple.y *= ratio_y;
+        }
+      else
+        {
+          cmd->simple.x = ref_x + (cmd->simple.x - ref_x) * ratio_x;
+          cmd->simple.y = ref_y + (cmd->simple.y - ref_y) * ratio_y;
+        }
+      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:
+      if (cmd->curve.relative)
+        {
+          cmd->curve.x *= ratio_x;
+          cmd->curve.y *= ratio_y;
+          cmd->curve.x1 *= ratio_x;
+          cmd->curve.y1 *= ratio_y;
+          cmd->curve.x2 *= ratio_x;
+          cmd->curve.y2 *= ratio_y;
+        }
+      else
+        {
+          cmd->curve.x =  ref_x + (cmd->curve.x -  ref_x) * ratio_x;
+          cmd->curve.y =  ref_y + (cmd->curve.y -  ref_y) * ratio_y;
+          cmd->curve.x1 = ref_x + (cmd->curve.x1 - ref_x) * ratio_x;
+          cmd->curve.y1 = ref_y + (cmd->curve.y1 - ref_y) * ratio_y;
+          cmd->curve.x2 = ref_x + (cmd->curve.x2 - ref_x) * ratio_x;
+          cmd->curve.y2 = ref_y + (cmd->curve.y2 - ref_y) * ratio_y;
+        }
+      break;
+    case GOO_CANVAS_PATH_ELLIPTICAL_ARC:
+      if (cmd->arc.relative)
+        {
+          cmd->arc.x *= ratio_x;
+          cmd->arc.y *= ratio_y;
+        }
+      else
+        {
+          cmd->arc.x = ref_x + (cmd->arc.x - ref_x) * ratio_x;
+          cmd->arc.y = ref_y + (cmd->arc.y - ref_y) * ratio_y;
+        }
+      break;
+    default:
+      g_assert_not_reached();
+      break;
+  }
+}
 
 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_X:
+      goo_canvas_path_common_get_extent(canvas, path_data, &extent);
+      g_value_set_double (value, extent.x1);
+      break;
+    case PROP_Y:
+      goo_canvas_path_common_get_extent(canvas, path_data, &extent);
+      g_value_set_double (value, extent.y1);
+      break;
+    case PROP_WIDTH:
+      goo_canvas_path_common_get_extent(canvas, path_data, &extent);
+      g_value_set_double (value, extent.x2 - extent.x1);
+      break;
+    case PROP_HEIGHT:
+      goo_canvas_path_common_get_extent(canvas, path_data, &extent);
+      g_value_set_double (value, extent.y2 - extent.y1);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -195,26 +374,99 @@ 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 extent;
+  GooCanvasPathCommand *cmd;
+  gdouble rel_x, rel_y;
+  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, "x");
+      g_object_notify(object, "y");
+      g_object_notify(object, "width");
+      g_object_notify(object, "height");
+      break;
+    case PROP_X:
+      if (path_data->path_commands->len > 0)
+        {
+          goo_canvas_path_common_get_extent (canvas, path_data, &extent);
+          rel_x = g_value_get_double (value) - extent.x1;
+
+          for (i = 0; i < path_data->path_commands->len; ++ i)
+            {
+              cmd = &g_array_index (path_data->path_commands, GooCanvasPathCommand, i);
+              goo_canvas_path_command_move_position (cmd, rel_x, 0.0);
+            }
+          g_object_notify (object, "data");
+        }
+      break;
+    case PROP_Y:
+      if (path_data->path_commands->len > 0)
+        {
+          goo_canvas_path_common_get_extent (canvas, path_data, &extent);
+          rel_y = g_value_get_double (value) - extent.y1;
+
+          for (i = 0; i < path_data->path_commands->len; ++ i)
+            {
+              cmd = &g_array_index (path_data->path_commands, GooCanvasPathCommand, i);
+              goo_canvas_path_command_move_position (cmd, 0.0, rel_y);
+            }
+          g_object_notify (object, "data");
+        }
+      break;
+    case PROP_WIDTH:
+      if (path_data->path_commands->len >= 2)
+        {
+          goo_canvas_path_common_get_extent (canvas, path_data, &extent);
+          if (extent.x2 - extent.x1 != 0.0)
+            {
+              rel_x = g_value_get_double (value) / (extent.x2 - extent.x1);
+
+              for (i = 0; i < path_data->path_commands->len; ++ i)
+                {
+                  cmd = &g_array_index (path_data->path_commands, GooCanvasPathCommand, i);
+                  goo_canvas_path_command_resize_position (cmd, extent.x1, 0.0, rel_x, 1.0);
+                }
+              g_object_notify (object, "data");
+            }
+        }
+      break;
+    case PROP_HEIGHT:
+      if (path_data->path_commands->len >= 2)
+        {
+          goo_canvas_path_common_get_extent (canvas, path_data, &extent);
+          if (extent.y2 - extent.y1 != 0.0)
+            {
+              rel_y = g_value_get_double (value) / (extent.y2 - extent.y1);
+
+              for (i = 0; i < path_data->path_commands->len; ++ i)
+                {
+                  cmd = &g_array_index (path_data->path_commands, GooCanvasPathCommand, i);
+                  goo_canvas_path_command_resize_position (cmd, 0.0, extent.y1, 1.0, rel_y);
+                }
+              g_object_notify (object, "data");
+            }
+        }
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -238,8 +490,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);
 }
 
@@ -496,8 +748,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);
 }
 
 
@@ -509,8 +761,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/goocanvaswidget.c
===================================================================
--- src/goocanvaswidget.c	(revision 20)
+++ src/goocanvaswidget.c	(working copy)
@@ -12,6 +12,10 @@
  *
  * GooCanvasWidget provides support for placing any GtkWidget in the canvas.
  *
+ * The #GooCanvasWidget:width and #GooCanvasWidget:height properties specify
+ * the widget's size. If either of them is -1, then the requested size of the
+ * widget is used instead, which is the default for both width and height.
+ *
  * Note that there are a number of limitations in the use of #GooCanvasWidget:
  *
  * <itemizedlist><listitem><para>
@@ -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)
+  {
+    if (width) *width = 0.0;
+    if (height) *height = 0.0;
+  }
+  else
+  {
+    /* Compute the new bounds. */
+    if (witem->width < 0 || witem->height < 0)
+    {
+      gtk_widget_size_request (witem->widget, &requisition);
+    }
+
+    if (width) *width = witem->width < 0 ? requisition.width : witem->width;
+    if (height) *height = witem->height < 0 ? requisition.height : witem->height;
+  }
+}
 
 static void
 goo_canvas_widget_set_widget (GooCanvasWidget *witem,
@@ -187,6 +257,11 @@ goo_canvas_widget_set_widget (GooCanvasW
 	  gtk_widget_set_parent (widget, GTK_WIDGET (simple->canvas));
 	}
     }
+
+  if (witem->width < 0.0)
+    g_object_notify (G_OBJECT (witem), "width");
+  if (witem->height < 0.0)
+    g_object_notify (G_OBJECT (witem), "height");
 }
 
 
@@ -214,6 +289,8 @@ goo_canvas_widget_get_property  (GObject
   GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
   GooCanvasWidget *witem = (GooCanvasWidget*) object;
 
+  gdouble width, height;
+
   switch (prop_id)
     {
     case PROP_WIDGET:
@@ -226,10 +303,12 @@ goo_canvas_widget_get_property  (GObject
       g_value_set_double (value, witem->y);
       break;
     case PROP_WIDTH:
-      g_value_set_double (value, witem->width);
+      goo_canvas_widget_get_widget_size(witem, &width, NULL);
+      g_value_set_double (value, width);
       break;
     case PROP_HEIGHT:
-      g_value_set_double (value, witem->height);
+      goo_canvas_widget_get_widget_size(witem, NULL, &height);
+      g_value_set_double (value, height);
       break;
     case PROP_ANCHOR:
       g_value_set_enum (value, witem->anchor);
@@ -347,53 +426,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;
@@ -531,7 +576,7 @@ goo_canvas_widget_class_init (GooCanvasW
   g_object_class_install_property (gobject_class, PROP_WIDTH,
 				   g_param_spec_double ("width",
 							_("Width"),
-							_("The width of the widget, or -1 to use its requested width"),
+							_("The width of the widget, or -1 to use the requested width"),
 							-G_MAXDOUBLE,
 							G_MAXDOUBLE, -1.0,
 							G_PARAM_READWRITE));
@@ -539,11 +584,12 @@ goo_canvas_widget_class_init (GooCanvasW
   g_object_class_install_property (gobject_class, PROP_HEIGHT,
 				   g_param_spec_double ("height",
 							_("Height"),
-							_("The height of the widget, or -1 to use its requested height"),
+							_("The height of the widget, or -1 to use the requested height"),
 							-G_MAXDOUBLE,
 							G_MAXDOUBLE, -1.0,
 							G_PARAM_READWRITE));
 
+
   g_object_class_install_property (gobject_class, PROP_ANCHOR,
 				   g_param_spec_enum ("anchor",
 						      _("Anchor"),
@@ -555,5 +601,3 @@ goo_canvas_widget_class_init (GooCanvasW
   g_object_class_override_property (gobject_class, PROP_VISIBILITY,
 				    "visibility");
 }
-
-
Index: src/goocanvastext.c
===================================================================
--- src/goocanvastext.c	(revision 20)
+++ src/goocanvastext.c	(working copy)
@@ -19,6 +19,10 @@
  * #GooCanvasItem functions such as goo_canvas_item_raise() and
  * goo_canvas_item_rotate().
  *
+ * The #GooCanvasText:width and #GooCanvasText:height properties specify the
+ * area of the item. If it exceeds that area because there is too much text,
+ * it is clipped. The properties can be set to -1 to disable clipping.
+ *
  * To create a #GooCanvasText use goo_canvas_text_new().
  *
  * To get or set the properties of an existing #GooCanvasText, use
@@ -30,6 +34,13 @@
 #include "goocanvastext.h"
 #include "goocanvas.h"
 
+typedef struct _GooCanvasTextPrivate GooCanvasTextPrivate;
+struct _GooCanvasTextPrivate {
+  gdouble height;
+};
+
+#define GOO_CANVAS_TEXT_GET_PRIVATE(text) \
+   (G_TYPE_INSTANCE_GET_PRIVATE ((text), GOO_TYPE_CANVAS_TEXT, GooCanvasTextPrivate))
 
 enum {
   PROP_0,
@@ -37,6 +48,7 @@ enum {
   PROP_X,
   PROP_Y,
   PROP_WIDTH,
+  PROP_HEIGHT,
   PROP_TEXT,
   PROP_USE_MARKUP,
   PROP_ANCHOR,
@@ -45,6 +57,16 @@ enum {
   PROP_WRAP
 };
 
+static PangoLayout*
+goo_canvas_text_create_layout (GooCanvasItemSimpleData *simple_data,
+			       GooCanvasTextData       *text_data,
+			       gdouble                  layout_width,
+			       gdouble                  layout_height,
+			       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,
@@ -121,6 +143,15 @@ goo_canvas_text_install_common_propertie
 							G_MAXDOUBLE, -1.0,
 							G_PARAM_READWRITE));
 
+  g_object_class_install_property (gobject_class, PROP_HEIGHT,
+				   g_param_spec_double ("height",
+							_("Height"),
+							_("The height to use to layout the text"),
+							-G_MAXDOUBLE,
+							G_MAXDOUBLE, -1.0,
+							G_PARAM_READWRITE));
+
+
   g_object_class_install_property (gobject_class, PROP_ANCHOR,
 				   g_param_spec_enum ("anchor",
 						      _("Anchor"),
@@ -142,6 +173,9 @@ goo_canvas_text_install_common_propertie
 static void
 goo_canvas_text_init (GooCanvasText *text)
 {
+  GooCanvasTextPrivate* priv;
+  priv = GOO_CANVAS_TEXT_GET_PRIVATE (text);
+
   text->text_data = g_slice_new0 (GooCanvasTextData);
   text->text_data->width = -1.0;
   text->text_data->anchor = GTK_ANCHOR_NW;
@@ -149,6 +183,8 @@ goo_canvas_text_init (GooCanvasText *tex
   text->text_data->wrap = PANGO_WRAP_WORD;
 
   text->layout_width = -1.0;
+
+  priv->height = -1.0;
 }
 
 
@@ -261,6 +297,9 @@ goo_canvas_text_get_common_property (GOb
     case PROP_WIDTH:
       g_value_set_double (value, text_data->width);
       break;
+    case PROP_HEIGHT:
+      g_value_set_double (value, GOO_CANVAS_TEXT_GET_PRIVATE (object)->height);
+      break;
     case PROP_TEXT:
       g_value_set_string (value, text_data->text);
       break;
@@ -292,10 +331,54 @@ goo_canvas_text_get_property (GObject   
 			      GValue               *value,
 			      GParamSpec           *pspec)
 {
+  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
+  GooCanvasItemSimpleData *simple_data = simple->simple_data;
   GooCanvasText *text = (GooCanvasText*) object;
+  GooCanvasTextPrivate *priv;
+  cairo_t *cr;
+  PangoLayout *layout;
+  GooCanvasBounds bounds;
 
-  goo_canvas_text_get_common_property (object, text->text_data, prop_id,
-				       value, pspec);
+  switch (prop_id)
+  {
+  case PROP_WIDTH:
+    priv = GOO_CANVAS_TEXT_GET_PRIVATE (object);
+    if (text->text_data->width < 0.0)
+      {
+        cr = goo_canvas_create_cairo_context(simple->canvas);
+
+        layout = goo_canvas_text_create_layout (simple_data, text->text_data,
+                                                text->text_data->width,
+                                                priv->height, cr,
+                                                &bounds, NULL, NULL);
+        g_object_unref (layout);
+        cairo_destroy (cr);
+
+        g_value_set_double (value, bounds.x2 - bounds.x1);
+        return;
+      }
+    break;
+  case PROP_HEIGHT:
+    priv = GOO_CANVAS_TEXT_GET_PRIVATE (object);
+    if (priv->height < 0.0)
+      {
+        cr = goo_canvas_create_cairo_context(simple->canvas);
+
+        layout = goo_canvas_text_create_layout (simple_data, text->text_data,
+                                                text->text_data->width,
+                                                priv->height, cr,
+                                                &bounds, NULL, NULL);
+        g_object_unref (layout);
+        cairo_destroy (cr);
+
+        g_value_set_double (value, bounds.y2 - bounds.y1);
+        return;
+      }
+    break;
+  }
+
+  goo_canvas_text_get_common_property (object, text->text_data,
+                                       prop_id, value, pspec);
 }
 
 
@@ -317,6 +400,9 @@ goo_canvas_text_set_common_property (GOb
     case PROP_WIDTH:
       text_data->width = g_value_get_double (value);
       break;
+    case PROP_HEIGHT:
+      GOO_CANVAS_TEXT_GET_PRIVATE (object)->height = g_value_get_double (value);
+      break;
     case PROP_TEXT:
       g_free (text_data->text);
       text_data->text = g_value_dup_string (value);
@@ -368,6 +454,7 @@ static PangoLayout*
 goo_canvas_text_create_layout (GooCanvasItemSimpleData *simple_data,
 			       GooCanvasTextData       *text_data,
 			       gdouble                  layout_width,
+			       gdouble                  layout_height,
 			       cairo_t                 *cr,
 			       GooCanvasBounds         *bounds,
 			       gdouble	               *origin_x_return,
@@ -376,6 +463,8 @@ goo_canvas_text_create_layout (GooCanvas
   GooCanvasStyle *style = simple_data->style;
   GValue *svalue;
   PangoLayout *layout;
+  PangoLayoutIter *iter;
+  gint index;
   PangoContext *context;
   PangoRectangle ink_rect, logical_rect;
   double logical_width, logical_height, align_width, origin_x, origin_y;
@@ -419,6 +508,26 @@ goo_canvas_text_create_layout (GooCanvas
 
   pango_layout_set_wrap (layout, text_data->wrap);
 
+  /* Cut text if height is set, so that we don't render partially obscured
+   * lines. */
+  if (layout_height > 0.0)
+    {
+      iter = pango_layout_get_iter (layout);
+
+      do
+        {
+          pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
+          if(logical_rect.y + logical_rect.height > layout_height * PANGO_SCALE)
+            {
+              index = pango_layout_iter_get_index (iter);
+              pango_layout_set_text (layout, text_data->text, index);
+	      break;
+            }
+        } while (pango_layout_iter_next_line (iter));
+
+      pango_layout_iter_free (iter);
+    }
+
   if (bounds)
     {
       /* Get size of the text, so we can position it according to anchor. */
@@ -534,6 +643,7 @@ goo_canvas_text_update  (GooCanvasItemSi
 			 cairo_t             *cr)
 {
   GooCanvasText *text = (GooCanvasText*) simple;
+  GooCanvasTextPrivate *priv = GOO_CANVAS_TEXT_GET_PRIVATE (text);
   PangoLayout *layout;
 
   /* Initialize the layout width to the text item's specified width property.
@@ -543,9 +653,12 @@ goo_canvas_text_update  (GooCanvasItemSi
 
   /* Compute the new bounds. */
   layout = goo_canvas_text_create_layout (simple->simple_data, text->text_data,
-					  text->layout_width, cr,
+					  text->layout_width, priv->height, cr,
 					  &simple->bounds, NULL, NULL);
   g_object_unref (layout);
+
+  if (priv->height > 0.0 && simple->bounds.y2 - simple->bounds.y1 > priv->height)
+    simple->bounds.y2 = simple->bounds.y1 + priv->height;
 }
 
 
@@ -573,6 +686,7 @@ goo_canvas_text_is_item_at (GooCanvasIte
 {
   GooCanvasItemSimpleData *simple_data = simple->simple_data;
   GooCanvasText *text = (GooCanvasText*) simple;
+  GooCanvasTextPrivate *priv = GOO_CANVAS_TEXT_GET_PRIVATE (text);
   PangoLayout *layout;
   GooCanvasBounds bounds;
   PangoLayoutIter *iter;
@@ -592,7 +706,8 @@ goo_canvas_text_is_item_at (GooCanvasIte
     return FALSE;
 
   layout = goo_canvas_text_create_layout (simple_data, text->text_data,
-					  text->layout_width, cr, &bounds,
+					  text->layout_width, priv->height,
+                                          cr, &bounds,
 					  &origin_x, &origin_y);
 
   /* Convert the coordinates into Pango units. */
@@ -642,7 +757,9 @@ goo_canvas_text_paint (GooCanvasItemSimp
 		       cairo_t               *cr,
 		       const GooCanvasBounds *bounds)
 {
+  GooCanvasItemSimpleData *simple_data = simple->simple_data;
   GooCanvasText *text = (GooCanvasText*) simple;
+  GooCanvasTextPrivate *priv = GOO_CANVAS_TEXT_GET_PRIVATE (text);
   PangoLayout *layout;
   GooCanvasBounds layout_bounds;
   gdouble origin_x, origin_y;
@@ -655,11 +772,21 @@ goo_canvas_text_paint (GooCanvasItemSimp
 
   cairo_new_path (cr);
   layout = goo_canvas_text_create_layout (simple->simple_data, text->text_data,
-					  text->layout_width, cr,
+					  text->layout_width, priv->height, cr,
 					  &layout_bounds,
 					  &origin_x, &origin_y);
+  cairo_save (cr);
+
+  if (priv->height > 0.0)
+    {
+      cairo_rectangle (cr, origin_x, origin_y, text->layout_width, priv->height);
+      cairo_set_fill_rule (cr, simple_data->clip_fill_rule);
+      cairo_clip (cr);
+    }
   cairo_move_to (cr, origin_x, origin_y);
   pango_cairo_show_layout (cr, layout);
+
+  cairo_restore (cr);
   g_object_unref (layout);
 }
 
@@ -672,6 +799,7 @@ goo_canvas_text_get_requested_height (Go
   GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
   GooCanvasItemSimpleData *simple_data = simple->simple_data;
   GooCanvasText *text = (GooCanvasText*) item;
+  GooCanvasTextPrivate *priv = GOO_CANVAS_TEXT_GET_PRIVATE (text);
   PangoLayout *layout;
   gdouble height;
 
@@ -692,15 +820,22 @@ goo_canvas_text_get_requested_height (Go
   if (simple_data->transform)
     text->layout_width /= simple_data->transform->xx;
 
-  /* Create layout with given width. */
-  layout = goo_canvas_text_create_layout (simple_data, text->text_data,
-					  text->layout_width, cr,
+  if (priv->height < 0.0)
+    {
+     /* Create layout with given width. */
+      layout = goo_canvas_text_create_layout (simple_data, text->text_data,
+					  text->layout_width, priv->height, cr,
 					  &simple->bounds, NULL, NULL);
-  g_object_unref (layout);
+      g_object_unref (layout);
 
-  /* Convert to the parent's coordinate space. As above,  we only need to
-     support a simple scale operation here. */
-  height = simple->bounds.y2 - simple->bounds.y1;
+      height = simple->bounds.y2 - simple->bounds.y1;
+    }
+  else
+    {
+      height = priv->height;
+    }
+      /* Convert to the parent's coordinate space. As above,  we only need to
+         support a simple scale operation here. */
   if (simple_data->transform)
     height *= simple_data->transform->yy;
 
@@ -732,6 +867,7 @@ goo_canvas_text_get_natural_extents (Goo
 {
   GooCanvasItem *item = (GooCanvasItem*) text;
   GooCanvasItemSimple *simple = (GooCanvasItemSimple*) text;
+  GooCanvasTextPrivate *priv = GOO_CANVAS_TEXT_GET_PRIVATE (text);
   PangoLayout *layout;
   cairo_t *cr;
 
@@ -740,8 +876,8 @@ goo_canvas_text_get_natural_extents (Goo
 
   cr = goo_canvas_create_cairo_context (simple->canvas);
   layout = goo_canvas_text_create_layout (simple->simple_data, text->text_data,
-					  text->text_data->width, cr, NULL,
-					  NULL, NULL);
+					  text->text_data->width, priv->height,
+                                          cr, NULL, NULL, NULL);
   pango_layout_get_extents (layout, ink_rect, logical_rect);
   g_object_unref (layout);
   cairo_destroy (cr);
@@ -782,6 +918,8 @@ goo_canvas_text_class_init (GooCanvasTex
   GObjectClass *gobject_class = (GObjectClass*) klass;
   GooCanvasItemSimpleClass *simple_class = (GooCanvasItemSimpleClass*) klass;
 
+  g_type_class_add_private (gobject_class, sizeof (GooCanvasTextPrivate));
+
   gobject_class->finalize = goo_canvas_text_finalize;
 
   gobject_class->get_property = goo_canvas_text_get_property;
@@ -842,6 +980,8 @@ goo_canvas_text_model_class_init (GooCan
 {
   GObjectClass *gobject_class = (GObjectClass*) klass;
 
+  g_type_class_add_private (gobject_class, sizeof (GooCanvasTextPrivate));
+
   gobject_class->finalize     = goo_canvas_text_model_finalize;
 
   gobject_class->get_property = goo_canvas_text_model_get_property;
@@ -854,10 +994,15 @@ goo_canvas_text_model_class_init (GooCan
 static void
 goo_canvas_text_model_init (GooCanvasTextModel *tmodel)
 {
+  GooCanvasTextPrivate *priv;
+  priv = GOO_CANVAS_TEXT_GET_PRIVATE (tmodel);
+
   tmodel->text_data.width = -1.0;
   tmodel->text_data.anchor = GTK_ANCHOR_NW;
   tmodel->text_data.ellipsize = PANGO_ELLIPSIZE_NONE;
   tmodel->text_data.wrap = PANGO_WRAP_WORD;
+
+  priv->height = -1.0;
 }
 
 
Index: src/goocanvastable.c
===================================================================
--- src/goocanvastable.c	(revision 20)
+++ src/goocanvastable.c	(working copy)
@@ -51,6 +51,8 @@
 enum
 {
   PROP_0,
+  PROP_X,
+  PROP_Y,
   PROP_WIDTH,
   PROP_HEIGHT,
   PROP_ROW_SPACING,
@@ -164,6 +166,10 @@ struct _GooCanvasTableLayoutData
   GooCanvasTableDimensionLayoutData *dldata[2];
   GooCanvasTableChildLayoutData *children;
 
+  /* Position of the table */
+  gdouble x;
+  gdouble y;
+
   /* This is TRUE if we are rounding everything to the nearest integer. */
   gboolean integer_layout;
 
@@ -219,20 +225,11 @@ static void
 goo_canvas_table_install_common_properties (GObjectClass *gobject_class,
 					    InstallChildPropertyFunc install_child_property)
 {
-  g_object_class_install_property (gobject_class, PROP_WIDTH,
-                                   g_param_spec_double ("width",
-							_("Width"),
-							_("The requested width of the table, or -1 to use the default width"),
-							-G_MAXDOUBLE,
-							G_MAXDOUBLE, -1.0,
-							G_PARAM_READWRITE));
-  g_object_class_install_property (gobject_class, PROP_HEIGHT,
-                                   g_param_spec_double ("height",
-							_("Height"),
-							_("The requested height of the table, or -1 to use the default height"),
-							-G_MAXDOUBLE,
-							G_MAXDOUBLE, -1.0,
-							G_PARAM_READWRITE));
+  /* Override from GooCanvasGroup */
+  g_object_class_override_property (gobject_class, PROP_X, "x");
+  g_object_class_override_property (gobject_class, PROP_Y, "y");
+  g_object_class_override_property (gobject_class, PROP_WIDTH, "width");
+  g_object_class_override_property (gobject_class, PROP_HEIGHT, "height");
 
   /* FIXME: Support setting individual row/col spacing. */
   g_object_class_install_property (gobject_class, PROP_ROW_SPACING,
@@ -430,6 +427,9 @@ goo_canvas_table_init_data (GooCanvasTab
   table_data->children = g_array_new (0, 0, sizeof (GooCanvasTableChild));
 
   table_data->layout_data = g_slice_new (GooCanvasTableLayoutData);
+  table_data->layout_data->x = 0.0;
+  table_data->layout_data->y = 0.0;
+
   table_data->layout_data->children = NULL;
   for (d = 0; d < 2; d++)
     {
@@ -572,6 +572,12 @@ goo_canvas_table_get_common_property (GO
 {
   switch (prop_id)
     {
+    case PROP_X:
+      g_value_set_double (value, table_data->layout_data->x);
+      break;
+    case PROP_Y:
+      g_value_set_double (value, table_data->layout_data->y);
+      break;
     case PROP_WIDTH:
       g_value_set_double (value, table_data->width);
       break;
@@ -617,8 +623,28 @@ goo_canvas_table_get_property (GObject  
 {
   GooCanvasTable *table = (GooCanvasTable*) object;
 
+  switch (prop_id)
+    {
+    case PROP_WIDTH:
+      if (table->table_data->width < 0.0)
+        {
+          g_value_set_double (value, table->table_data->layout_data->natural_size[HORZ]);
+	  return;
+	}
+
+      break;
+    case PROP_HEIGHT:
+      if (table->table_data->height < 0.0)
+        {
+          g_value_set_double (value, table->table_data->layout_data->natural_size[VERT]);
+	  return;
+	}
+
+      break;
+    }
+
   goo_canvas_table_get_common_property (object, table->table_data,
-					prop_id, value, pspec);
+                                        prop_id, value, pspec);
 }
 
 
@@ -633,6 +659,12 @@ goo_canvas_table_set_common_property (GO
 
   switch (prop_id)
     {
+    case PROP_X:
+      table_data->layout_data->x = g_value_get_double (value);
+      break;
+    case PROP_Y:
+      table_data->layout_data->y = g_value_get_double (value);
+      break;
     case PROP_WIDTH:
       table_data->width = g_value_get_double (value);
       break;
@@ -1929,7 +1961,7 @@ goo_canvas_table_get_requested_area (Goo
   GooCanvasItemSimpleData *simple_data = simple->simple_data;
   GooCanvasTable *table = (GooCanvasTable*) item;
   GooCanvasTableData *table_data = table->table_data;
-  GooCanvasTableLayoutData *layout_data;
+  GooCanvasTableLayoutData *layout_data = table_data->layout_data;
   GooCanvasTableDimensionLayoutData *rows, *columns;
   gdouble width = 0.0, height = 0.0;
   gint row, column, end;
@@ -1953,6 +1985,8 @@ goo_canvas_table_get_requested_area (Goo
   if (simple_data->transform)
     cairo_transform (cr, simple_data->transform);
 
+  cairo_translate (cr, layout_data->x, layout_data->y);
+
   /* Initialize the layout data, get the requested sizes of all children, and
      set the expand, shrink and empty flags. */
   goo_canvas_table_init_layout_data (table);
@@ -1964,7 +1998,6 @@ goo_canvas_table_get_requested_area (Goo
   goo_canvas_table_size_request_pass3 (table, HORZ);
   goo_canvas_table_size_request_pass2 (table, HORZ);
 
-  layout_data = table_data->layout_data;
   rows = layout_data->dldata[VERT];
   columns = layout_data->dldata[HORZ];
 
@@ -2006,6 +2039,11 @@ 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), "width");
+    g_object_notify (G_OBJECT (table), "height");
+  }
 
   /* If the height has been set, that overrides the calculations. */
   if (table_data->height > 0.0)
@@ -2049,6 +2087,8 @@ goo_canvas_table_get_requested_height (G
   if (simple_data->transform)
     cairo_transform (cr, simple_data->transform);
 
+  cairo_translate (cr, layout_data->x, layout_data->y);
+
   /* Convert the width from the parent's coordinate space. Note that we only
      need to support a simple scale operation here. */
   if (simple_data->transform)
@@ -2132,12 +2172,14 @@ goo_canvas_table_allocate_area (GooCanva
 		   -(allocated_area->y1 - requested_area->y1));
   if (simple_data->transform)
     cairo_transform (cr, simple_data->transform);
+  cairo_translate (cr, layout_data->x, layout_data->y);
   goo_canvas_table_update_requested_heights (item, cr);
   cairo_restore (cr);
 
   cairo_save (cr);
   if (simple_data->transform)
     cairo_transform (cr, simple_data->transform);
+  cairo_translate (cr, layout_data->x, layout_data->y);
 
   /* Calculate the table's bounds. */
   simple->bounds.x1 = simple->bounds.y1 = 0.0;
@@ -2233,6 +2275,7 @@ goo_canvas_table_paint (GooCanvasItem   
   cairo_save (cr);
   if (simple_data->transform)
     cairo_transform (cr, simple_data->transform);
+  cairo_translate (cr, layout_data->x, layout_data->y);
 
   /* Clip with the table's clip path, if it is set. */
   if (simple_data->clip_path_commands)
@@ -2536,6 +2579,7 @@ goo_canvas_table_get_items_at (GooCanvas
   cairo_save (cr);
   if (simple_data->transform)
     cairo_transform (cr, simple_data->transform);
+  cairo_translate (cr, layout_data->x, layout_data->y);
 
   cairo_device_to_user (cr, &user_x, &user_y);
 
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 20)
+++ ChangeLog	(working copy)
@@ -1,3 +1,21 @@
+2008-11-01  Armin Burgmeier  <armin@openismus.com>
+
+	* src/goocanvasellipse.c:
+	* src/goocanvaspolyline.c:
+	* src/goocanvasgroup.c:
+	* src/goocanvaspath.c:
+	* src/goocanvaswidget.c:
+	* src/goocanvastext.c:
+	* src/goocanvastable.c: Added "x", "y", "width" and "height"
+	properties were they do not exist already. GooCanvasGroup clips its
+	children if they are out of the specified area, and GooCanvasText
+	clips the text. It is possible to turn off the clipping by using -1
+	for width or height, respectively, which is the default.
+
+	* demo/generic-position-demo.c:
+	* demo/Makefile.am: Added a demo showing how this can be used to
+	generically resize items using drag and drop.
+
 2008-10-15  Damon Chaplin  <damon@gnome.org>
 
 	* src/goocanvaspolyline.c (goo_canvas_polyline_is_item_at): only check
