goocanvas r38 - in trunk: . demo src



Author: damon
Date: Mon Jan 12 23:22:22 2009
New Revision: 38
URL: http://svn.gnome.org/viewvc/goocanvas?rev=38&view=rev

Log:

2009-01-12  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 where 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/mv-generic-position-demo.c:
	    * demo/Makefile.am: Added a demo showing how this can be used to
	    generically resize items using drag and drop.

	    (Patch from bug #555097 committed with quite a few changes by Damon)



Added:
   trunk/demo/generic-position-demo.c
   trunk/demo/mv-generic-position-demo.c
Modified:
   trunk/ChangeLog
   trunk/demo/Makefile.am
   trunk/src/goocanvasellipse.c
   trunk/src/goocanvasgroup.c
   trunk/src/goocanvaspath.c
   trunk/src/goocanvaspolyline.c
   trunk/src/goocanvastable.c
   trunk/src/goocanvastext.c
   trunk/src/goocanvaswidget.c

Modified: trunk/demo/Makefile.am
==============================================================================
--- trunk/demo/Makefile.am	(original)
+++ trunk/demo/Makefile.am	Mon Jan 12 23:22:22 2009
@@ -9,7 +9,7 @@
 #	-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-generic-position-demo mv-simple-demo mv-scalability-demo
 
 demo_SOURCES = \
 	demo.c demo-fifteen.c demo-scalability.c demo-grabs.c \
@@ -68,5 +68,15 @@
 
 widgets_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_generic_position_demo_SOURCES = \
+	mv-generic-position-demo.c
+
+mv_generic_position_demo_LDADD = $(top_builddir)/src/libgoocanvas.la @PACKAGE_LIBS@ $(INTLLIBS)
+
 EXTRA_DIST = flower.png toroid.png
 

Added: trunk/demo/generic-position-demo.c
==============================================================================
--- (empty file)
+++ trunk/demo/generic-position-demo.c	Mon Jan 12 23:22:22 2009
@@ -0,0 +1,217 @@
+#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)
+{
+  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_POINTER_MOTION_HINT_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)
+{
+  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;
+}

Added: trunk/demo/mv-generic-position-demo.c
==============================================================================
--- (empty file)
+++ trunk/demo/mv-generic-position-demo.c	Mon Jan 12 23:22:22 2009
@@ -0,0 +1,250 @@
+#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)
+{
+  GooCanvasItemModel *model = goo_canvas_item_get_model (item);
+
+  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 (model),
+                    "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_POINTER_MOTION_HINT_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)
+{
+  GooCanvasItemModel *model = goo_canvas_item_get_model (item);
+
+  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 (model), "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 (model), "width", new_width, "height", new_height, NULL);
+    }
+
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+
+static void
+on_item_created (GooCanvas          *canvas,
+		 GooCanvasItem      *item,
+		 GooCanvasItemModel *model,
+		 gpointer            data)
+{
+  if (g_object_get_data (G_OBJECT (model), "setup-dnd-signals"))
+    {
+      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);
+    }
+}
+
+
+static GooCanvasItemModel*
+create_model (GdkPixbuf *pixbuf)
+{
+  GooCanvasItemModel *root;
+  GooCanvasItemModel *item;
+  GooCanvasItemModel* child;
+
+  root = goo_canvas_group_model_new (NULL, NULL);
+
+  /* Test clipping of GooCanvasGroup: We put the rectangle and the ellipse into
+   * a group with width=200 and height=200. */
+  item = goo_canvas_group_model_new (root, "x", 50.0, "y", 350.0, "width", 200.0, "height", 200.0, NULL);
+  /*goo_canvas_item_model_rotate(item, 45.0, 150.0, 450.0);*/
+
+  child = goo_canvas_rect_model_new (item, 0.0, 0.0, 100, 100, "fill-color", "blue", NULL);
+  g_object_set_data (G_OBJECT (child), "setup-dnd-signals", "TRUE");
+  goo_canvas_item_model_rotate(child, 45.0, 50.0, 50.0);
+
+  child = goo_canvas_ellipse_model_new (item, 150, 00, 50, 50, "fill-color", "red", NULL);
+  g_object_set_data (G_OBJECT (child), "setup-dnd-signals", "TRUE");
+
+  item = goo_canvas_polyline_model_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);
+  g_object_set_data (G_OBJECT (item), "setup-dnd-signals", "TRUE");
+
+  item = goo_canvas_path_model_new (root, "M20,500 C20,450 100,450 100,500", "stroke-color", "green", "line-width", 5.0, NULL);
+  g_object_set_data (G_OBJECT (item), "setup-dnd-signals", "TRUE");
+
+  item = goo_canvas_image_model_new (root, pixbuf, 150, 450, /*"fill-color", "yellow", */NULL);
+  g_object_unref (pixbuf);
+  g_object_set_data (G_OBJECT (item), "setup-dnd-signals", "TRUE");
+
+  item = goo_canvas_text_model_new (root, "Hello, World!", 250, 450, -1, GTK_ANCHOR_NW, "fill-color", "magenta", "wrap", PANGO_WRAP_WORD_CHAR, NULL);
+  g_object_set_data (G_OBJECT (item), "setup-dnd-signals", "TRUE");
+
+  item = goo_canvas_table_model_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_model_translate (item, 10.0, 10.0);
+  g_object_set_data (G_OBJECT (item), "setup-dnd-signals", "TRUE");
+
+  child = goo_canvas_rect_model_new (item, 10.0, 10.0, 50.0, 50.0, "fill-color", "blue", "x", 10.0, "y", 25.0, NULL);
+  g_object_set_data (G_OBJECT (child), "setup-dnd-signals", "TRUE");
+  goo_canvas_item_model_set_child_properties (item, child, "column", 0, "row", 0, "columns", 1, "rows", 1, NULL);
+  /*goo_canvas_item_model_translate (child, 10.0, 10.0);*/
+
+  child = goo_canvas_rect_model_new (item, 0.0, 0.0, 50.0, 50.0, "fill-color", "red", NULL);
+  g_object_set_data (G_OBJECT (child), "setup-dnd-signals", "TRUE");
+  goo_canvas_item_model_set_child_properties (item, child, "column", 1, "row", 0, "columns", 1, "rows", 1, NULL);
+
+  child = goo_canvas_rect_model_new (item, 0.0, 0.0, 50.0, 50.0, "fill-color", "green", NULL);
+  g_object_set_data (G_OBJECT (child), "setup-dnd-signals", "TRUE");
+  goo_canvas_item_model_set_child_properties(item, child, "column", 0, "row", 1, "columns", 1, "rows", 1, NULL);
+
+  child = goo_canvas_rect_model_new (item, 0.0, 0.0, 50.0, 50.0, "fill-color", "yellow", NULL);
+  g_object_set_data (G_OBJECT (child), "setup-dnd-signals", "TRUE");
+  goo_canvas_item_model_set_child_properties (item, child, "column", 1, "row", 1, "columns", 1, "rows", 1, NULL);
+
+  return root;
+}
+
+
+static GtkWidget*
+create_window (GooCanvasItemModel *model)
+{
+  GtkWidget *window, *vbox, *label, *scrolled_win, *canvas;
+
+  /* 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);
+
+  g_signal_connect (canvas, "item_created",
+		    G_CALLBACK (on_item_created), NULL);
+
+  goo_canvas_set_root_item_model (GOO_CANVAS (canvas), model);
+
+  return window;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  GtkWidget *window;
+  GooCanvasItemModel *model;
+  GdkPixbuf *pixbuf;
+
+  /* Initialize GTK+. */
+  gtk_set_locale ();
+  gtk_init (&argc, &argv);
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  pixbuf = gtk_widget_render_icon (window, GTK_STOCK_DIALOG_WARNING,
+				   GTK_ICON_SIZE_DIALOG, NULL);
+
+  model = create_model (pixbuf);
+
+  /* Create 2 windows to show off multiple views. */
+  window = create_window (model);
+#if 1
+  window = create_window (model);
+#endif
+
+  g_object_unref (model);
+
+  gtk_main ();
+
+  return 0;
+}

Modified: trunk/src/goocanvasellipse.c
==============================================================================
--- trunk/src/goocanvasellipse.c	(original)
+++ trunk/src/goocanvasellipse.c	Mon Jan 12 23:22:22 2009
@@ -23,6 +23,10 @@
  *
  * To get or set the properties of an existing #GooCanvasEllipse, use
  * g_object_get() and g_object_set().
+ *
+ * The ellipse can be specified either with the "center-x", "center-y",
+ * "radius-x" and "radius-y" properties, or with the "x", "y", "width" and
+ * "height" properties.
  */
 #include <config.h>
 #include <math.h>
@@ -37,7 +41,12 @@
   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 +101,36 @@
 							_("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 left side of the ellipse"),
+							-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 top 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 +263,18 @@
     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);
+      break;
+    case PROP_Y:
+      g_value_set_double (value, ellipse_data->center_y - ellipse_data->radius_y);
+      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;
@@ -251,19 +302,55 @@
 					const GValue         *value,
 					GParamSpec           *pspec)
 {
+  gdouble x, y;
+
   switch (prop_id)
     {
     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;
+      g_object_notify (object, "center-x");
+      break;
+    case PROP_Y:
+      ellipse_data->center_y = g_value_get_double (value) + ellipse_data->radius_y;
+      g_object_notify (object, "center-y");
+      break;
+    case PROP_WIDTH:
+      /* Calculate the current x coordinate. */
+      x = ellipse_data->center_x - ellipse_data->radius_x;
+      /* Calculate the new radius_x, which is half the width. */
+      ellipse_data->radius_x = g_value_get_double (value) / 2.0;
+      /* Now calculate the new center_x. */
+      ellipse_data->center_x = x + ellipse_data->radius_x;
+
+      g_object_notify (object, "center-x");
+      g_object_notify (object, "radius-x");
+      break;
+    case PROP_HEIGHT:
+      /* Calculate the current y coordinate. */
+      y = ellipse_data->center_y - ellipse_data->radius_y;
+      /* Calculate the new radius_y, which is half the height. */
+      ellipse_data->radius_y = g_value_get_double (value) / 2.0;
+      /* Now calculate the new center_y. */
+      ellipse_data->center_y = y + ellipse_data->radius_y;
+
+      g_object_notify (object, "center-y");
+      g_object_notify (object, "radius-y");
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -356,6 +443,10 @@
  * To get or set the properties of an existing #GooCanvasEllipseModel, use
  * g_object_get() and g_object_set().
  *
+ * The ellipse can be specified either with the "center-x", "center-y",
+ * "radius-x" and "radius-y" properties, or with the "x", "y", "width" and
+ * "height" properties.
+ *
  * To respond to events such as mouse clicks on the ellipse you must connect
  * to the signal handlers of the corresponding #GooCanvasEllipse objects.
  * (See goo_canvas_get_item() and #GooCanvas::item-created.)

Modified: trunk/src/goocanvasgroup.c
==============================================================================
--- trunk/src/goocanvasgroup.c	(original)
+++ trunk/src/goocanvasgroup.c	Mon Jan 12 23:22:22 2009
@@ -25,6 +25,9 @@
  * goo_canvas_item_rotate(), and the properties such as "visibility" and
  * "pointer-events".
  *
+ * If the #GooCanvasGroup:width and #GooCanvasGroup:height properties are
+ * set to positive values then the group is clipped to the given size.
+ *
  * To create a #GooCanvasGroup use goo_canvas_group_new().
  *
  * To get or set the properties of an existing #GooCanvasGroup, use
@@ -40,9 +43,38 @@
 #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))
+#define GOO_CANVAS_GROUP_MODEL_GET_PRIVATE(group)  \
+   (G_TYPE_INSTANCE_GET_PRIVATE ((group), GOO_TYPE_CANVAS_GROUP_MODEL, 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 +82,53 @@
 			 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 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 height of the group, or -1 to use the default height"),
+                                                       -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 +137,22 @@
 				     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 = 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;
 }
 
 
@@ -146,6 +226,105 @@
 }
 
 
+/* Gets the private data to use, from the model or from the item itself. */
+static GooCanvasGroupPrivate*
+goo_canvas_group_get_private (GooCanvasGroup *group)
+{
+  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) group;
+
+  if (simple->model)
+    return GOO_CANVAS_GROUP_MODEL_GET_PRIVATE (simple->model);
+  else
+    return GOO_CANVAS_GROUP_GET_PRIVATE (group);
+}
+
+
+static void
+goo_canvas_group_get_common_property (GObject               *object,
+                                      GooCanvasGroupPrivate *priv,
+                                      guint                  prop_id,
+                                      GValue                *value,
+                                      GParamSpec            *pspec)
+{
+  switch (prop_id)
+    {
+    case PROP_X:
+      g_value_set_double (value, priv->x);
+      break;
+    case PROP_Y:
+      g_value_set_double (value, priv->y);
+      break;
+    case PROP_WIDTH:
+      g_value_set_double (value, priv->width);
+      break;
+    case PROP_HEIGHT:
+      g_value_set_double (value, priv->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)
+{
+  GooCanvasGroup *group = (GooCanvasGroup*) object;
+  GooCanvasGroupPrivate *priv = goo_canvas_group_get_private (group);
+
+  goo_canvas_group_get_common_property (object, priv, prop_id, value, pspec);
+}
+
+static void
+goo_canvas_group_set_common_property (GObject               *object,
+                                      GooCanvasGroupPrivate *priv,
+                                      guint                  prop_id,
+                                      const GValue          *value,
+                                      GParamSpec            *pspec)
+{
+  switch (prop_id)
+    {
+    case PROP_X:
+      priv->x = g_value_get_double (value);
+      break;
+    case PROP_Y:
+      priv->y = g_value_get_double (value);
+      break;
+    case PROP_WIDTH:
+      priv->width = g_value_get_double (value);
+      break;
+    case PROP_HEIGHT:
+      priv->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;
+  GooCanvasGroup *group = (GooCanvasGroup*) object;
+  GooCanvasGroupPrivate *priv = goo_canvas_group_get_private (group);
+
+  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, priv, prop_id, value, pspec);
+  goo_canvas_item_simple_changed (simple, TRUE);
+}
+
 static void
 goo_canvas_group_add_child     (GooCanvasItem  *item,
 				GooCanvasItem  *child,
@@ -238,11 +417,11 @@
 			     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);
 }
 
@@ -405,6 +584,7 @@
 {
   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;
@@ -424,35 +604,38 @@
 
       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);
-		}
-	    }
-	}
       cairo_restore (cr);
     }
 
@@ -472,6 +655,7 @@
   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;
 
@@ -499,6 +683,8 @@
   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)
     {
@@ -514,6 +700,19 @@
 	}
     }
 
+  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);
+      if (user_x < 0.0 || user_x >= priv->width
+	  || user_y < 0.0 || user_y >= priv->height)
+	{
+	  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++)
@@ -539,6 +738,7 @@
   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. */
@@ -557,6 +757,8 @@
   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)
     {
@@ -565,6 +767,12 @@
       cairo_clip (cr);
     }
 
+  if (priv->width > 0.0 && priv->height > 0.0)
+    {
+      cairo_rectangle (cr, 0.0, 0.0, priv->width, priv->height);
+      cairo_clip (cr);
+    }
+
   for (i = 0; i < group->items->len; i++)
     {
       GooCanvasItem *child = group->items->pdata[i];
@@ -627,6 +835,14 @@
 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,
@@ -638,16 +854,27 @@
 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_MODEL_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;
 }
 
 
@@ -720,6 +947,28 @@
   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;
+  GooCanvasGroupPrivate *priv = GOO_CANVAS_GROUP_MODEL_GET_PRIVATE (model);
+
+  goo_canvas_group_get_common_property (object, priv, 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;
+  GooCanvasGroupPrivate *priv = GOO_CANVAS_GROUP_MODEL_GET_PRIVATE (model);
+
+  goo_canvas_group_set_common_property (object, priv, 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);
@@ -772,11 +1021,12 @@
 
   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);
 }
 
 

Modified: trunk/src/goocanvaspath.c
==============================================================================
--- trunk/src/goocanvaspath.c	(original)
+++ trunk/src/goocanvaspath.c	Mon Jan 12 23:22:22 2009
@@ -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 @@
 							_("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,164 @@
   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_fill_extents (cr, &bounds->x1, &bounds->y1, &bounds->x2, &bounds->y2);
+  cairo_destroy (cr);
+}
+
+
+/* Moves all the absolute points in the command by the given amounts.
+   Relative points don't need to be moved. */
+static void
+goo_canvas_path_move_command (GooCanvasPathCommand *cmd,
+			      gdouble               x_offset,
+			      gdouble               y_offset)
+{
+  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_offset;
+          cmd->simple.y += y_offset;
+        }
+      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_offset;
+          cmd->curve.y += y_offset;
+          cmd->curve.x1 += x_offset;
+          cmd->curve.y1 += y_offset;
+          cmd->curve.x2 += x_offset;
+          cmd->curve.y2 += y_offset;
+        }
+      break;
+    case GOO_CANVAS_PATH_ELLIPTICAL_ARC:
+      if (!cmd->arc.relative)
+        {
+          cmd->arc.x += x_offset;
+          cmd->arc.y += y_offset;
+        }
+      break;
+    default:
+      g_assert_not_reached();
+      break;
+    }
+}
+
+
+/* Scales all the points in the command by the given amounts. Absolute points
+   are scaled about the given origin. */
+static void
+goo_canvas_path_scale_command (GooCanvasPathCommand *cmd,
+			       gdouble               x_origin,
+			       gdouble               y_origin,
+			       gdouble               x_scale,
+			       gdouble               y_scale)
+{
+  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_scale;
+          cmd->simple.y *= y_scale;
+        }
+      else
+        {
+          cmd->simple.x = x_origin + (cmd->simple.x - x_origin) * x_scale;
+          cmd->simple.y = y_origin + (cmd->simple.y - y_origin) * y_scale;
+        }
+      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_scale;
+          cmd->curve.y *= y_scale;
+          cmd->curve.x1 *= x_scale;
+          cmd->curve.y1 *= y_scale;
+          cmd->curve.x2 *= x_scale;
+          cmd->curve.y2 *= y_scale;
+        }
+      else
+        {
+          cmd->curve.x =  x_origin + (cmd->curve.x -  x_origin) * x_scale;
+          cmd->curve.y =  y_origin + (cmd->curve.y -  y_origin) * y_scale;
+          cmd->curve.x1 = x_origin + (cmd->curve.x1 - x_origin) * x_scale;
+          cmd->curve.y1 = y_origin + (cmd->curve.y1 - y_origin) * y_scale;
+          cmd->curve.x2 = x_origin + (cmd->curve.x2 - x_origin) * x_scale;
+          cmd->curve.y2 = y_origin + (cmd->curve.y2 - y_origin) * y_scale;
+        }
+      break;
+    case GOO_CANVAS_PATH_ELLIPTICAL_ARC:
+      if (cmd->arc.relative)
+        {
+          cmd->arc.x *= x_scale;
+          cmd->arc.y *= y_scale;
+        }
+      else
+        {
+          cmd->arc.x = x_origin + (cmd->arc.x - x_origin) * x_scale;
+          cmd->arc.y = y_origin + (cmd->arc.y - y_origin) * y_scale;
+        }
+      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 +379,113 @@
 			      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 x_offset, y_offset, x_scale, y_scale;
+  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)
+        {
+	  /* Calculate the x offset from the current position. */
+          goo_canvas_path_common_get_extent (canvas, path_data, &extent);
+          x_offset = g_value_get_double (value) - extent.x1;
+
+	  /* Add the offset to all the absolute x coordinates. */
+          for (i = 0; i < path_data->path_commands->len; i++)
+            {
+              cmd = &g_array_index (path_data->path_commands,
+				    GooCanvasPathCommand, i);
+              goo_canvas_path_move_command (cmd, x_offset, 0.0);
+            }
+          g_object_notify (object, "data");
+        }
+      break;
+    case PROP_Y:
+      if (path_data->path_commands->len > 0)
+        {
+	  /* Calculate the y offset from the current position. */
+          goo_canvas_path_common_get_extent (canvas, path_data, &extent);
+          y_offset = g_value_get_double (value) - extent.y1;
+
+	  /* Add the offset to all the absolute y coordinates. */
+          for (i = 0; i < path_data->path_commands->len; i++)
+            {
+              cmd = &g_array_index (path_data->path_commands,
+				    GooCanvasPathCommand, i);
+              goo_canvas_path_move_command (cmd, 0.0, y_offset);
+            }
+          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)
+            {
+	      /* Calculate the amount to scale the path. */
+              x_scale = g_value_get_double (value) / (extent.x2 - extent.x1);
+
+	      /* Scale the x coordinates, relative to the left-most point. */
+              for (i = 0; i < path_data->path_commands->len; i++)
+                {
+                  cmd = &g_array_index (path_data->path_commands,
+					GooCanvasPathCommand, i);
+                  goo_canvas_path_scale_command (cmd, extent.x1, 0.0,
+						 x_scale, 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)
+            {
+	      /* Calculate the amount to scale the polyline. */
+              y_scale = g_value_get_double (value) / (extent.y2 - extent.y1);
+
+	      /* Scale the y coordinates, relative to the top-most point. */
+              for (i = 0; i < path_data->path_commands->len; i++)
+                {
+                  cmd = &g_array_index (path_data->path_commands,
+					GooCanvasPathCommand, i);
+                  goo_canvas_path_scale_command (cmd, 0.0, extent.y1,
+						 1.0, y_scale);
+                }
+              g_object_notify (object, "data");
+            }
+        }
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -238,8 +509,8 @@
       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 +767,8 @@
 {
   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 +780,8 @@
 {
   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);
 }
 

Modified: trunk/src/goocanvaspolyline.c
==============================================================================
--- trunk/src/goocanvaspolyline.c	(original)
+++ trunk/src/goocanvaspolyline.c	Mon Jan 12 23:22:22 2009
@@ -117,7 +117,12 @@
   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 @@
 							_("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 left-most point of the polyline"),
+							-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 top-most point of the polyline"),
+							-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 @@
 
 
 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 @@
 					 GParamSpec           *pspec)
 {
   GooCanvasPoints *points;
+  GooCanvasBounds  extent;
 
   switch (prop_id)
     {
@@ -257,6 +319,22 @@
       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 @@
 					 GParamSpec           *pspec)
 {
   GooCanvasPoints *points;
+  GooCanvasBounds  extent;
+  gdouble x_offset, y_offset, x_scale, y_scale;
+  guint i;
 
   switch (prop_id)
     {
@@ -452,6 +533,10 @@
 		  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,68 @@
       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)
+        {
+	  /* Calculate the x offset from the current position. */
+          goo_canvas_polyline_get_extent (polyline_data, &extent);
+          x_offset = g_value_get_double (value) - extent.x1;
+
+	  /* Add the offset to all the x coordinates. */
+          for (i = 0; i < polyline_data->num_points; i++)
+            polyline_data->coords[2 * i] += x_offset;
+
+          g_object_notify (object, "points");
+        }
+      break;
+    case PROP_Y:
+      if (polyline_data->num_points > 0)
+        {
+	  /* Calculate the y offset from the current position. */
+          goo_canvas_polyline_get_extent (polyline_data, &extent);
+          y_offset = g_value_get_double (value) - extent.y1;
+
+	  /* Add the offset to all the y coordinates. */
+          for (i = 0; i < polyline_data->num_points; i++)
+            polyline_data->coords[2 * i + 1] += y_offset;
+
+          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)
+            {
+	      /* Calculate the amount to scale the polyline. */
+              x_scale = g_value_get_double (value) / (extent.x2 - extent.x1);
+
+	      /* Scale the x coordinates, relative to the left-most point. */
+              for (i = 0; i < polyline_data->num_points; i++)
+                polyline_data->coords[2 * i] = extent.x1 + (polyline_data->coords[2 * i] - extent.x1) * x_scale;
+
+              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)
+            {
+	      /* Calculate the amount to scale the polyline. */
+              y_scale = g_value_get_double (value) / (extent.y2 - extent.y1);
+
+	      /* Scale the y coordinates, relative to the top-most point. */
+              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) * y_scale;
+
+              g_object_notify (object, "points");
+            }
+        }
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;

Modified: trunk/src/goocanvastable.c
==============================================================================
--- trunk/src/goocanvastable.c	(original)
+++ trunk/src/goocanvastable.c	Mon Jan 12 23:22:22 2009
@@ -51,6 +51,8 @@
 enum
 {
   PROP_0,
+  PROP_X,
+  PROP_Y,
   PROP_WIDTH,
   PROP_HEIGHT,
   PROP_ROW_SPACING,
@@ -164,6 +166,10 @@
   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 @@
 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 @@
   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 @@
 {
   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;
@@ -618,7 +624,7 @@
   GooCanvasTable *table = (GooCanvasTable*) object;
 
   goo_canvas_table_get_common_property (object, table->table_data,
-					prop_id, value, pspec);
+                                        prop_id, value, pspec);
 }
 
 
@@ -633,6 +639,12 @@
 
   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 +1941,7 @@
   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 +1965,8 @@
   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 +1978,6 @@
   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];
 
@@ -2049,6 +2062,8 @@
   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 +2147,14 @@
 		   -(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 +2250,7 @@
   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 +2554,7 @@
   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);
 

Modified: trunk/src/goocanvastext.c
==============================================================================
--- trunk/src/goocanvastext.c	(original)
+++ trunk/src/goocanvastext.c	Mon Jan 12 23:22:22 2009
@@ -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,15 @@
 #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))
+#define GOO_CANVAS_TEXT_MODEL_GET_PRIVATE(text) \
+   (G_TYPE_INSTANCE_GET_PRIVATE ((text), GOO_TYPE_CANVAS_TEXT_MODEL, GooCanvasTextPrivate))
 
 enum {
   PROP_0,
@@ -37,6 +50,7 @@
   PROP_X,
   PROP_Y,
   PROP_WIDTH,
+  PROP_HEIGHT,
   PROP_TEXT,
   PROP_USE_MARKUP,
   PROP_ANCHOR,
@@ -45,6 +59,15 @@
   PROP_WRAP
 };
 
+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,
@@ -121,6 +144,15 @@
 							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, or -1 to use the natural 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"),
@@ -142,6 +174,8 @@
 static void
 goo_canvas_text_init (GooCanvasText *text)
 {
+  GooCanvasTextPrivate *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 @@
   text->text_data->wrap = PANGO_WRAP_WORD;
 
   text->layout_width = -1.0;
+
+  priv->height = -1.0;
 }
 
 
@@ -243,9 +279,23 @@
 }
 
 
+/* Gets the private data to use, from the model or from the item itself. */
+static GooCanvasTextPrivate*
+goo_canvas_text_get_private (GooCanvasText *text)
+{
+  GooCanvasItemSimple *simple = (GooCanvasItemSimple*) text;
+
+  if (simple->model)
+    return GOO_CANVAS_TEXT_MODEL_GET_PRIVATE (simple->model);
+  else
+    return GOO_CANVAS_TEXT_GET_PRIVATE (text);
+}
+
+
 static void
 goo_canvas_text_get_common_property (GObject                 *object,
 				     GooCanvasTextData       *text_data,
+				     GooCanvasTextPrivate    *priv,
 				     guint                    prop_id,
 				     GValue                  *value,
 				     GParamSpec              *pspec)
@@ -261,6 +311,9 @@
     case PROP_WIDTH:
       g_value_set_double (value, text_data->width);
       break;
+    case PROP_HEIGHT:
+      g_value_set_double (value, priv->height);
+      break;
     case PROP_TEXT:
       g_value_set_string (value, text_data->text);
       break;
@@ -293,15 +346,17 @@
 			      GParamSpec           *pspec)
 {
   GooCanvasText *text = (GooCanvasText*) object;
+  GooCanvasTextPrivate *priv = goo_canvas_text_get_private (text);
 
-  goo_canvas_text_get_common_property (object, text->text_data, prop_id,
-				       value, pspec);
+  goo_canvas_text_get_common_property (object, text->text_data, priv,
+                                       prop_id, value, pspec);
 }
 
 
 static void
 goo_canvas_text_set_common_property (GObject                 *object,
 				     GooCanvasTextData       *text_data,
+				     GooCanvasTextPrivate    *priv,
 				     guint                    prop_id,
 				     const GValue            *value,
 				     GParamSpec              *pspec)
@@ -317,6 +372,9 @@
     case PROP_WIDTH:
       text_data->width = g_value_get_double (value);
       break;
+    case PROP_HEIGHT:
+      priv->height = g_value_get_double (value);
+      break;
     case PROP_TEXT:
       g_free (text_data->text);
       text_data->text = g_value_dup_string (value);
@@ -351,6 +409,7 @@
 {
   GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
   GooCanvasText *text = (GooCanvasText*) object;
+  GooCanvasTextPrivate *priv = goo_canvas_text_get_private (text);
 
   if (simple->model)
     {
@@ -358,7 +417,7 @@
       return;
     }
 
-  goo_canvas_text_set_common_property (object, text->text_data, prop_id,
+  goo_canvas_text_set_common_property (object, text->text_data, priv, prop_id,
 				       value, pspec);
   goo_canvas_item_simple_changed (simple, TRUE);
 }
@@ -534,6 +593,7 @@
 			 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.
@@ -546,6 +606,10 @@
 					  text->layout_width, cr,
 					  &simple->bounds, NULL, NULL);
   g_object_unref (layout);
+
+  /* If the height is set, use that. */
+  if (priv->height > 0.0)
+    simple->bounds.y2 = simple->bounds.y1 + priv->height;
 }
 
 
@@ -573,6 +637,7 @@
 {
   GooCanvasItemSimpleData *simple_data = simple->simple_data;
   GooCanvasText *text = (GooCanvasText*) simple;
+  GooCanvasTextPrivate *priv = goo_canvas_text_get_private (text);
   PangoLayout *layout;
   GooCanvasBounds bounds;
   PangoLayoutIter *iter;
@@ -591,6 +656,10 @@
       && goo_canvas_text_is_unpainted (simple_data->style))
     return FALSE;
 
+  /* Check if the point is outside the clipped height. */
+  if (priv->height > 0.0 && y > priv->height)
+    return FALSE;
+
   layout = goo_canvas_text_create_layout (simple_data, text->text_data,
 					  text->layout_width, cr, &bounds,
 					  &origin_x, &origin_y);
@@ -643,6 +712,7 @@
 		       const GooCanvasBounds *bounds)
 {
   GooCanvasText *text = (GooCanvasText*) simple;
+  GooCanvasTextPrivate *priv = goo_canvas_text_get_private (text);
   PangoLayout *layout;
   GooCanvasBounds layout_bounds;
   gdouble origin_x, origin_y;
@@ -658,8 +728,18 @@
 					  text->layout_width, 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_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 +752,7 @@
   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 +773,23 @@
   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,
-					  &simple->bounds, NULL, NULL);
-  g_object_unref (layout);
+  if (priv->height < 0.0)
+    {
+     /* Create layout with given width. */
+      layout = goo_canvas_text_create_layout (simple_data, text->text_data,
+					      text->layout_width, cr,
+					      &simple->bounds, NULL, NULL);
+      g_object_unref (layout);
+
+      height = simple->bounds.y2 - simple->bounds.y1;
+    }
+  else
+    {
+      height = priv->height;
+    }
 
-  /* Convert to the parent's coordinate space. As above,  we only need to
+  /* 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;
   if (simple_data->transform)
     height *= simple_data->transform->yy;
 
@@ -782,6 +871,8 @@
   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 +933,8 @@
 {
   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 +947,14 @@
 static void
 goo_canvas_text_model_init (GooCanvasTextModel *tmodel)
 {
+  GooCanvasTextPrivate *priv = GOO_CANVAS_TEXT_MODEL_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;
 }
 
 
@@ -951,9 +1048,10 @@
 				    GParamSpec           *pspec)
 {
   GooCanvasTextModel *tmodel = (GooCanvasTextModel*) object;
+  GooCanvasTextPrivate *priv = GOO_CANVAS_TEXT_MODEL_GET_PRIVATE (tmodel);
 
-  goo_canvas_text_get_common_property (object, &tmodel->text_data, prop_id,
-				       value, pspec);
+  goo_canvas_text_get_common_property (object, &tmodel->text_data, priv,
+				       prop_id, value, pspec);
 }
 
 
@@ -964,9 +1062,10 @@
 				    GParamSpec           *pspec)
 {
   GooCanvasTextModel *tmodel = (GooCanvasTextModel*) object;
+  GooCanvasTextPrivate *priv = GOO_CANVAS_TEXT_MODEL_GET_PRIVATE (tmodel);
 
-  goo_canvas_text_set_common_property (object, &tmodel->text_data, prop_id,
-				       value, pspec);
+  goo_canvas_text_set_common_property (object, &tmodel->text_data, priv,
+				       prop_id, value, pspec);
   g_signal_emit_by_name (tmodel, "changed", TRUE);
 }
 

Modified: trunk/src/goocanvaswidget.c
==============================================================================
--- trunk/src/goocanvaswidget.c	(original)
+++ trunk/src/goocanvaswidget.c	Mon Jan 12 23:22:22 2009
@@ -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>
@@ -155,6 +159,73 @@
 }
 
 
+/* Returns the anchor position, within the given width. */
+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;
+    }
+}
+
+
+/* Returns the anchor position, within the given height. */
+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;
+    }
+}
+
+
+/* Returns the size to use for the widget, either the item's width & height
+   properties or the widget's own requested width & height. */
+static void
+goo_canvas_widget_get_widget_size (GooCanvasWidget *witem,
+				   gdouble         *width,
+				   gdouble         *height)
+{
+  GtkRequisition requisition;
+
+  if (witem->widget)
+    {
+      /* Get the widget's requested size, if we need it. */
+      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;
+    }
+  else
+    {
+      *width = *height = 0.0;
+    }
+}
+
+
 static void
 goo_canvas_widget_set_widget (GooCanvasWidget *witem,
 			      GtkWidget       *widget)
@@ -349,53 +420,19 @@
 			   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;
@@ -546,6 +583,7 @@
 							G_MAXDOUBLE, -1.0,
 							G_PARAM_READWRITE));
 
+
   g_object_class_install_property (gobject_class, PROP_ANCHOR,
 				   g_param_spec_enum ("anchor",
 						      _("Anchor"),
@@ -557,5 +595,3 @@
   g_object_class_override_property (gobject_class, PROP_VISIBILITY,
 				    "visibility");
 }
-
-



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