/* dia-canvas-item.c
 * Copyright (C) 2000, 2001  James Henstridge, Arjan Molenaar
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include "dia-canvas-item.h"
#include "dia-canvas-groupable.h"
#include <math.h>
#include <libart_lgpl/art_affine.h>
#include "dia-shape.h"
#include "dia-canvas-i18n.h"
#include "diamarshal.h"
#include "../config.h"

///#define D(msg) G_STMT_START { g_print (G_STRLOC": "); g_print msg; g_print ("\n"); } G_STMT_END
#define D(msg)

enum
{
	NEED_UPDATE,
	Z_ORDER,
	STATE_CHANGED,
	HAS_STATE,
	EVENT,
	MOVE,
	CONNECT,
	DISCONNECT,
	LAST_SIGNAL
};

enum
{
	PROP_PARENT = 1,
	PROP_VISIBLE,
	PROP_CONNECT,
	PROP_DISCONNECT,
	PROP_AFFINE,
	PROP_HANDLES
};


static void dia_canvas_item_init		(DiaCanvasItem	*canvas_item);
static void dia_canvas_item_class_init		(DiaCanvasItemClass *klass);
static void dia_canvas_item_set_property	(GObject	*object,
						 guint		 property_id,
						 const GValue	*value,
						 GParamSpec	*pspec);
static void dia_canvas_item_get_property	(GObject	*object,
						 guint		 property_id,
						 GValue		*value,
						 GParamSpec	*pspec);

static void dia_canvas_item_dispose		(GObject	*object);
static void dia_canvas_item_finalize		(GObject	*object);

static void dia_real_canvas_item_move		(DiaCanvasItem *item,
						 gdouble dx, gdouble dy,
						 gboolean interactive);
static gdouble dia_real_canvas_item_point	(DiaCanvasItem *item,
						 gdouble x, gdouble y);
static void dia_real_canvas_item_update		(DiaCanvasItem	*item,
						 gdouble	 affine[6]);
static gboolean dia_real_canvas_item_connect	(DiaCanvasItem	*item,
						 DiaHandle 	*handle);
static gboolean dia_real_canvas_item_disconnect	(DiaCanvasItem	*item,
						 DiaHandle 	*handle);
static inline void dia_canvas_item_set_state	(DiaCanvasItem	*item,
						 gint		 new_state);
static inline gboolean dia_canvas_item_has_state (DiaCanvasItem	*item,
						  gint		 state);

static GObjectClass *parent_class = NULL;
static guint canvas_item_signals[LAST_SIGNAL] = { 0 };

GType
dia_canvas_item_get_type (void)
{
	static GType object_type = 0;

	if (!object_type) {
		static const GTypeInfo object_info = {
			sizeof (DiaCanvasItemClass),
			(GBaseInitFunc) NULL,
			(GBaseFinalizeFunc) NULL,
			(GClassInitFunc) dia_canvas_item_class_init,
			(GClassFinalizeFunc) NULL,
			(gconstpointer) NULL, /* class_data */
			sizeof (DiaCanvasItem),
			(guint16) 0, /* n_preallocs */
			(GInstanceInitFunc) dia_canvas_item_init,
		};
		object_type = g_type_register_static (G_TYPE_OBJECT,
						      "DiaCanvasItem",
						      &object_info,
						      G_TYPE_FLAG_ABSTRACT);
	}

	return object_type;
}


static void
dia_canvas_item_class_init (DiaCanvasItemClass *klass)
{
	GObjectClass *object_class;

	object_class = (GObjectClass*) klass;

	parent_class = g_type_class_peek_parent (klass);

	object_class->dispose = dia_canvas_item_dispose;
	object_class->finalize = dia_canvas_item_finalize;

	object_class->set_property = dia_canvas_item_set_property;
	object_class->get_property = dia_canvas_item_get_property;

	canvas_item_signals[NEED_UPDATE] = 
	  g_signal_new ("need_update",
			G_TYPE_FROM_CLASS (klass),
			G_SIGNAL_RUN_LAST,
			G_STRUCT_OFFSET (DiaCanvasItemClass, need_update),
			NULL, NULL,
			dia_marshal_VOID__VOID,
			G_TYPE_NONE, 0);
	canvas_item_signals[Z_ORDER] = 
	  g_signal_new ("z_order",
			G_TYPE_FROM_CLASS (klass),
			G_SIGNAL_RUN_LAST,
			G_STRUCT_OFFSET (DiaCanvasItemClass, z_order),
			NULL, NULL,
			dia_marshal_VOID__INT,
			G_TYPE_NONE, 1, G_TYPE_INT);
	canvas_item_signals[STATE_CHANGED] = 
	  g_signal_new ("state_changed",
			G_TYPE_FROM_CLASS (klass),
			G_SIGNAL_RUN_LAST,
			G_STRUCT_OFFSET (DiaCanvasItemClass, state_changed),
			NULL, NULL,
			dia_marshal_VOID__INT,
			G_TYPE_NONE, 1, G_TYPE_INT);
	canvas_item_signals[HAS_STATE] = 
	  g_signal_new ("has_state",
			G_TYPE_FROM_CLASS (klass),
			G_SIGNAL_RUN_LAST,
			G_STRUCT_OFFSET (DiaCanvasItemClass, has_state),
			NULL, NULL,
			dia_marshal_BOOLEAN__INT,
			G_TYPE_BOOLEAN, 1, G_TYPE_INT);
	canvas_item_signals[EVENT] =
	  g_signal_new ("event",
			G_TYPE_FROM_CLASS (klass),
			G_SIGNAL_RUN_LAST,
			G_STRUCT_OFFSET(DiaCanvasItemClass, event),
			NULL, NULL,
			dia_marshal_BOOLEAN__POINTER,
			G_TYPE_BOOLEAN, 1,
			G_TYPE_POINTER);
	canvas_item_signals[MOVE] =
	  g_signal_new ("move",
			G_TYPE_FROM_CLASS (klass),
			G_SIGNAL_RUN_LAST,
			G_STRUCT_OFFSET(DiaCanvasItemClass, move),
			NULL, NULL,
			dia_marshal_VOID__DOUBLE_DOUBLE_BOOLEAN,
			G_TYPE_NONE, 3,
			G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_BOOLEAN);
	canvas_item_signals[CONNECT] =
	  g_signal_new ("connect",
			G_TYPE_FROM_CLASS (klass),
			G_SIGNAL_RUN_LAST,
			G_STRUCT_OFFSET (DiaCanvasItemClass, connect),
			NULL, NULL,
			dia_marshal_BOOLEAN__OBJECT,
			G_TYPE_BOOLEAN, 1,
			DIA_TYPE_HANDLE);
	canvas_item_signals[DISCONNECT] =
	  g_signal_new ("disconnect",
			G_TYPE_FROM_CLASS (klass),
			G_SIGNAL_RUN_LAST,
			G_STRUCT_OFFSET (DiaCanvasItemClass, disconnect),
			NULL, NULL,
			dia_marshal_BOOLEAN__OBJECT,
			G_TYPE_BOOLEAN, 1,
			DIA_TYPE_HANDLE);

	g_object_class_install_property (object_class,
					 PROP_PARENT,
					 g_param_spec_object ("parent",
						 _("Parent item"),
					 _("The parent group of this canvas item"),
					 DIA_TYPE_CANVAS_ITEM,
					 G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_VISIBLE,
					 g_param_spec_boolean ("visible",
						 _("Visible"),
					 _("Whether the canvas item is visible"),
					 TRUE,
					 G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_CONNECT,
					 g_param_spec_object ("connect",
						 _("Connect"),
					 _("Connect a handle to this object"),
					 DIA_TYPE_HANDLE,
					 G_PARAM_WRITABLE));
	g_object_class_install_property (object_class,
					 PROP_DISCONNECT,
					 g_param_spec_object ("disconnect",
						 _("Disconnect"),
					 _("Disconnect a handle from this object"),
					 DIA_TYPE_HANDLE,
					 G_PARAM_WRITABLE));
	g_object_class_install_property (object_class,
					 PROP_AFFINE,
					 g_param_spec_boxed ("affine",
						 _("Affine"),
					 _("Set a new transformation matrix for the object"),
					 DIA_TYPE_AFFINE,
					 G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_HANDLES,
					 g_param_spec_boxed ("handles",
						 _("GList of handles"),
					 _("List of handles, used internaly"),
					 DIA_TYPE_CANVAS_ITEM_HANDLES,
					 G_PARAM_READWRITE));

	/* Callbacks */
	klass->update = dia_real_canvas_item_update;
	klass->point = dia_real_canvas_item_point;
	klass->move = dia_real_canvas_item_move;
	klass->handle_motion = NULL;
	klass->glue = NULL;
	klass->get_shape_iter = NULL;
	klass->shape_next = NULL;
	klass->shape_value = NULL;

	/* Signals. */
	klass->connect = dia_real_canvas_item_connect;
	klass->disconnect = dia_real_canvas_item_disconnect;
}


static void
dia_canvas_item_init (DiaCanvasItem *canvas_item)
{
	canvas_item->canvas = NULL;
	canvas_item->parent = NULL;
	canvas_item->bounds.top = canvas_item->bounds.bottom
		= canvas_item->bounds.left = canvas_item->bounds.right = 0.0;
	canvas_item->handles = NULL;

	/* DIA_NEED_UPDATE should not be set, since it may confuse
	 * dia_canvas_item_request_update() (this function returns if the
	 * flag is already set). */
	DIA_SET_FLAGS (canvas_item, DIA_VISIBLE | DIA_INTERACTIVE);
	
	art_affine_identity (canvas_item->affine);
}

static void
dia_canvas_item_set_property (GObject *object, guint property_id,
			      const GValue *value, GParamSpec *pspec)
{
	DiaCanvasItem *item = DIA_CANVAS_ITEM (object);
	DiaCanvasItem *parent;

	switch (property_id) {
	case PROP_PARENT:
		parent = (DiaCanvasItem*) g_value_get_object (value);
		dia_canvas_item_set_parent (item, parent);
		break;
	case PROP_VISIBLE:
		dia_canvas_item_preserve_property (item, "visible");
		if (g_value_get_boolean (value))
			dia_canvas_item_visible (item);
		else
			dia_canvas_item_invisible (item);
		dia_canvas_item_request_update (item);
		break;
	case PROP_CONNECT:
		dia_canvas_item_connect (item, g_value_get_object(value));
		break;
	case PROP_DISCONNECT:
		dia_canvas_item_disconnect (item, g_value_get_object(value));
		break;
	case PROP_AFFINE:
		dia_canvas_item_preserve_property (item, "affine");
		if (g_value_get_boxed (value) == NULL) {
			item->affine[0] = 1.0;
			item->affine[1] = 0.0;
			item->affine[2] = 0.0;
			item->affine[3] = 1.0;
		} else {
			memcpy (item->affine, g_value_get_boxed (value),
				sizeof (gdouble) * 6);
		}

		dia_canvas_item_request_update (item);
		dia_canvas_item_update_handles_i2w (item);
		break;
	case PROP_HANDLES: {
		GList *l;

		dia_canvas_item_preserve_property (item, "handles");
		for (l = item->handles; l != NULL; l = l->next) {
			if (item->canvas)
				dia_canvas_preserve_property (item->canvas,
							      l->data, "pos_i");
			g_object_unref (l->data);
		}

		g_list_free (item->handles);
		item->handles = (GList*) g_value_get_boxed (value);
		break;
	}
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
		break;
	}
}

static void
dia_canvas_item_get_property (GObject *object, guint property_id,
			      GValue *value, GParamSpec *pspec)
{
	DiaCanvasItem *item = DIA_CANVAS_ITEM (object);

	switch (property_id) {
	case PROP_PARENT:
		if (item->parent)
			g_value_set_object (value, G_OBJECT (item->parent));
		else
			g_value_set_object (value, NULL);
		break;
	case PROP_VISIBLE:
		g_value_set_boolean (value, DIA_CANVAS_ITEM_VISIBLE (item));
		break;
	case PROP_AFFINE:
		g_value_set_boxed (value, item->affine);
		break;
	case PROP_HANDLES:
		g_value_set_boxed (value, item->handles);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
	}
}

static void
dia_canvas_item_dispose (GObject *object)
{
	GList *l;
	DiaCanvasItem *item = (DiaCanvasItem*) object;
	
#ifdef ENABLE_DEBUG
	g_message (G_STRLOC": %s (%p)/%p", G_OBJECT_TYPE_NAME (object), object, item->parent);
#endif

	dia_canvas_item_ungrab (item);

	if (item->parent)
		dia_canvas_item_set_child_of (item, NULL);
		//dia_canvas_groupable_remove_destruction (DIA_CANVAS_GROUPABLE (item->parent), item);


#ifdef ENABLE_DEBUG
	g_assert (item->parent == NULL);
	g_assert (item->canvas == NULL);
	g_assert (item->connected_handles == NULL);
#endif

	// No longer needed since handle disconnection happens the the canvas
	// is removed from the item.
	l = item->handles;
	while (l != NULL) {
		DiaHandle *h = l->data;
		l = l->next;
#ifdef ENABLE_DEBUG
		g_assert (DIA_IS_HANDLE (h));
		g_assert (DIA_HANDLE (h)->connected_to == NULL);
#endif
		g_object_set (G_OBJECT(h), "owner", NULL, NULL);
	}

	G_OBJECT_CLASS (parent_class)->dispose (object);
}

static void
dia_canvas_item_finalize (GObject *object)
{
	GList *l;
	DiaCanvasItem *item = (DiaCanvasItem*) object;

#ifdef ENABLE_DEBUG
	g_message (G_STRLOC": %s (%p)", G_OBJECT_TYPE_NAME (object), object);
#endif
	for (l = item->handles; l != NULL; l = l->next) {
		g_object_unref (l->data);
	}
	g_list_free (item->handles);
	item->handles = NULL;

	parent_class->finalize (object);
}

static void
dia_real_canvas_item_update (DiaCanvasItem *item, gdouble affine[6])
{
	GList *l;
	gdouble inv[6];
	D((" "));

	art_affine_invert (inv, affine);
	for (l = item->handles; l != NULL; l = l->next) {
		if (((DiaHandle*)l->data)->need_update_w2i)
			dia_handle_update_w2i_affine (l->data, inv);
	}

	g_signal_emit (item, canvas_item_signals[NEED_UPDATE], 0);

	DIA_UNSET_FLAGS (item, DIA_NEED_UPDATE | DIA_UPDATE_ALL);
}

static gdouble
dia_real_canvas_item_point (DiaCanvasItem *canvas_item,
			    gdouble x, gdouble y)
{
	g_return_val_if_fail (canvas_item != NULL, G_MAXDOUBLE);
	g_return_val_if_fail (DIA_IS_CANVAS_ITEM (canvas_item), G_MAXDOUBLE);

	return G_MAXDOUBLE;
}

static void
dia_real_canvas_item_move (DiaCanvasItem *item, gdouble dx, gdouble dy,
			   gboolean interactive)
{
	/* We should not move the root item. */
	if (item->canvas && item->canvas->root == item)
		return; 

	if (item->canvas)
		dia_canvas_item_preserve_property (item, "affine");

	item->affine[4] += dx;
	item->affine[5] += dy;

	/*
	if (item->canvas && item->canvas->snap_to_grid) {
		gdouble x, y;
		x = item->affine[4];
		y = item->affine[5];
		dia_canvas_item_affine_point_i2w (item, &x, &y);
		dia_canvas_snap_to_grid (item->canvas, &x, &y);
		dia_canvas_item_affine_point_w2i (item, &x, &y);
		item->affine[4] = x;
		item->affine[5] = y;
	}
	*/

	dia_canvas_item_update_handles_i2w (item);
	dia_canvas_item_request_update (item);
}

static gboolean
dia_real_canvas_item_connect (DiaCanvasItem *item, DiaHandle *handle)
{
	if (handle->connected_to != item) {
		handle->connected_to = item;
		item->connected_handles = g_list_append (item->connected_handles,
							 handle);
		/* When undoing, we should disconnect this connection... */
		if (item->canvas)
			dia_canvas_preserve_property (item->canvas,
						      G_OBJECT (handle),
						      "disconnect");
	} else {
		/* If we move the handle around on the same item, it will only
		 * do a "connect" in order to set up a new set of constraints.
		 */
		if (item->canvas)
			dia_canvas_preserve_property_last (item->canvas,
							   G_OBJECT (handle),
							   "connect");
	}
	return TRUE;
}

static gboolean
dia_real_canvas_item_disconnect (DiaCanvasItem *item, DiaHandle *handle)
{
	g_assert (handle->connected_to == item);

	/* When undoing, we should do a "connect"... */
	if (item->canvas)
		dia_canvas_preserve_property_last (item->canvas,
						   G_OBJECT (handle),
						   "connect");

	dia_handle_remove_all_constraints (handle);

	handle->connected_to = NULL;
	item->connected_handles = g_list_remove (item->connected_handles, handle);

	return TRUE;
}


/**
 * canvas_destroyed:
 * @data: The canvas item
 * @where_the_object_was: The old canvas object
 *
 * This function is used to break the item -> canvas reference. It conforms
 * to the GWeakNotify prototype.
 **/
static void
canvas_destroyed (gpointer data, GObject *where_the_object_was)
{
	DiaCanvasItem *item = data;
	g_assert (item->canvas == (DiaCanvas*) where_the_object_was);

	// Disconnect all handles:
	dia_canvas_item_disconnect_handles (item);
	// Make sure we lose our (weak) reference to the canvas
	item->canvas = NULL;
}

/**
 * dia_canvas_item_create:
 * @type: Type of the new item, should be a DiaCanvasItem child.
 * @first_arg_name: 
 * @...: 
 *
 * Create a new canvas item. You can assign addional properties to the item.
 *
 * Return value: A newly created canvas item.
 **/
DiaCanvasItem*
dia_canvas_item_create (GType type, const gchar *first_arg_name, ...)
{
	DiaCanvasItem *item = NULL;
	va_list va_args;

	g_return_val_if_fail (g_type_is_a (type, DIA_TYPE_CANVAS_ITEM), NULL);

	if (first_arg_name) {
		va_start (va_args, first_arg_name);
		item = (DiaCanvasItem*) g_object_new_valist (type,
							     first_arg_name,
							     va_args);
		va_end (va_args);
	} else
		item = g_object_new (type, NULL);

	dia_canvas_item_request_update (item);

	/* item is returned, although it will not be refcounted by default. */
	return item;
}


/**
 * dia_canvas_item_set_parent:
 * @item: 
 * @new_parent: new parent or NULL in case of disconnect
 *
 * Set a (new) parent for the @item. 
 **/
void
dia_canvas_item_set_parent (DiaCanvasItem *item, DiaCanvasItem *new_parent)
{
	g_return_if_fail (DIA_IS_CANVAS_ITEM (item));
	if (new_parent)
		g_return_if_fail (DIA_IS_CANVAS_GROUPABLE (new_parent));

	if (item->parent == new_parent)
		return;
	g_object_ref (item);
	if (item->parent != NULL)
		dia_canvas_groupable_remove (DIA_CANVAS_GROUPABLE (item->parent), item);
	if (new_parent && item->parent == NULL) {
		g_assert (DIA_IS_CANVAS_GROUPABLE (new_parent));
		//g_message (__FUNCTION__": setting new parent.");
		dia_canvas_groupable_add (DIA_CANVAS_GROUPABLE (new_parent), item);
	}
	g_object_unref (item);
}


/**
 * dia_canvas_item_set_child_of:
 * @item: 
 * @new_parent: new parent or NULL in case of disconnect
 *
 * Set a (new) parent for the @item. This implies setting the #parent and
 * #canvas fields of #item.
 * This function should only be used in DiaCanvasItem implementations.
 * 
 * The parent should implement the #DiaCanvasGroupable interface. Applications
 * should use dia_canvas_groupable_add() and dia_canvas_groupable_remove() to
 * add and remove items in a group. This function should be used in the
 * #DiaCanvasGroupable::add and #::remove implementations to create the actual
 * parent-child relationship.
 *
 * NOTE: This function is not a substitute for the 'parent' property.
 **/
void
dia_canvas_item_set_child_of (DiaCanvasItem *item, DiaCanvasItem *new_parent)
{
	g_return_if_fail (DIA_IS_CANVAS_ITEM (item));
	if (new_parent)
		g_return_if_fail (DIA_IS_CANVAS_GROUPABLE (new_parent));

	//g_message(G_STRLOC ": Setting new parent to %p (%p)", new_parent, new_parent ? new_parent->canvas : NULL);

	/* Notify is filtered, so only one parent notification will be emited */
	g_object_freeze_notify (G_OBJECT(item));

	/* Disconnect the parent when needed, we store this in the
	 * current canvas */
	if (item->parent && item->parent != new_parent) {
		dia_canvas_item_preserve_property (item, "parent");
		g_object_remove_weak_pointer (G_OBJECT (item->parent),
					      (gpointer) &item->parent);
		item->parent = NULL;
		g_object_notify (G_OBJECT (item), "parent");
	}

	/* Now update the canvas, so we can save undo information */
	if ((!new_parent) || item->canvas != new_parent->canvas) {
		/* Ensure the handles are disconnected */
		dia_canvas_item_disconnect_handles (item);

		if (item->canvas)
			g_object_weak_unref (G_OBJECT (item->canvas),
					     canvas_destroyed, item);

		item->canvas = new_parent ? new_parent->canvas : NULL;
		if (item->canvas)
			g_object_weak_ref (G_OBJECT (item->canvas),
					   canvas_destroyed, item);
	}

	/* Set new parent, saving the old value on the canvas' undo stack */
	if (new_parent && item->parent != new_parent) {
		dia_canvas_item_preserve_property (item, "parent");
		item->parent = new_parent;
		g_object_add_weak_pointer (G_OBJECT (item->parent),
					   (gpointer) &item->parent);
		g_object_notify (G_OBJECT (item), "parent");
	}

	/* Ensure the item is updated well */
	DIA_UNSET_FLAGS (item, DIA_NEED_UPDATE);
	dia_canvas_item_update_handles_i2w (item);
	dia_canvas_item_request_update (item);

	/* If this is a groupable item, also update childs */
	if (DIA_IS_CANVAS_GROUPABLE (item)) {
		DiaCanvasIter iter;
		DiaCanvasItem *child;

		if (dia_canvas_groupable_get_iter (DIA_CANVAS_GROUPABLE (item), &iter)) do {
			child = dia_canvas_groupable_value (DIA_CANVAS_GROUPABLE (item), &iter);
			if (child)
				dia_canvas_item_set_child_of (child, item);
		} while (dia_canvas_groupable_next (DIA_CANVAS_GROUPABLE (item), &iter));
	}
	g_object_thaw_notify (G_OBJECT (item));
}


static void
request_update_for_children (DiaCanvasItem *item)
{
	g_assert (item != NULL);

	if (DIA_CANVAS_ITEM_UPDATE_ALL(item))
		return;

	DIA_SET_FLAGS (item, DIA_NEED_UPDATE | DIA_UPDATE_ALL);

	if (DIA_IS_CANVAS_GROUPABLE (item)) {
		DiaCanvasIter iter;
		if (dia_canvas_groupable_get_iter (DIA_CANVAS_GROUPABLE (item), &iter)) do {
			request_update_for_children (dia_canvas_groupable_value (DIA_CANVAS_GROUPABLE (item), &iter));
		} while (dia_canvas_groupable_next (DIA_CANVAS_GROUPABLE (item), &iter));
	}
}

/**
 * dia_canvas_item_request_update:
 * @item: item that needs an update
 * 
 * Schedule an update for the item. If the item has children, they are
 * scheduled for an update too.
 **/
void
dia_canvas_item_request_update (DiaCanvasItem *item)
{
	DiaCanvasItem *i = item;

	g_return_if_fail (DIA_IS_CANVAS_ITEM (item));
	
	if (!item->canvas)
		return;

	dia_canvas_request_update (item->canvas);

	//g_message (__FUNCTION__": doing item: %p (%d)", i, i ? i->flags & DIA_NEED_UPDATE : -1);

	/* Update the whole tree */
	while (i && !DIA_CANVAS_ITEM_NEED_UPDATE (i)) {
		//g_message(__FUNCTION__": need update: %p", i);
		DIA_SET_FLAGS (i, DIA_NEED_UPDATE);
		i = (DiaCanvasItem*) i->parent;
		//g_message (__FUNCTION__": doing parent: %p (%d)", i, i ? i->flags & DIA_NEED_UPDATE : -1);
	}

	/* update child objects: */
	request_update_for_children (item);
}


/**
 * dia_canvas_item_update_now:
 * @item: Item to update
 *
 * Force an update of the item.
 **/
void
dia_canvas_item_update_now (DiaCanvasItem *item)
{
	gdouble a[6];

	g_return_if_fail (DIA_IS_CANVAS_ITEM (item));
	
	dia_canvas_item_affine_i2w (item, a);

	/* Update the DiaCanvasItem first if it needs to */
	if (DIA_CANVAS_ITEM_NEED_UPDATE (item)) {
		g_assert (DIA_CANVAS_ITEM_GET_CLASS (item)->update);
		DIA_CANVAS_ITEM_GET_CLASS (item)->update (item, a);
	}
}


/**
 * dia_canvas_item_update_child:
 * @item: Canvas item
 * @affine: transformation matrix of @item
 * @child: Canvas item, a child of @item
 *
 * This function can be used in the @item's update to trigger updates of
 * a subitem (@child).
 **/
void
dia_canvas_item_update_child (DiaCanvasItem *item,
			      DiaCanvasItem *child,
			      gdouble affine[6])
{
	g_return_if_fail (DIA_IS_CANVAS_ITEM (item));
	g_return_if_fail (affine != NULL);
	g_return_if_fail (DIA_IS_CANVAS_ITEM (child));
	g_return_if_fail (child->parent == item);

	if (DIA_CANVAS_ITEM_NEED_UPDATE (child)) {
		gdouble a[6];
		g_assert (DIA_CANVAS_ITEM_GET_CLASS (child)->update);
		art_affine_multiply (a, affine, child->affine);
		DIA_CANVAS_ITEM_GET_CLASS (child)->update (child, a);
	}
}


/**
 * dia_canvas_item_affine_i2w:
 * @item: 
 * @affine: OUT 
 *
 * Calculate the affine transformation that is nessesary to convert a point
 * from item coordinates to world coordinates.
 **/
void
dia_canvas_item_affine_i2w (DiaCanvasItem *item, gdouble affine[6])
{
	memcpy (affine, item->affine, 6 * sizeof (gdouble));

	while (item->parent) {
		item = (DiaCanvasItem*) item->parent;
		art_affine_multiply (affine, affine, item->affine);
	}
}

/**
 * dia_canvas_item_affine_w2i:
 * @item: 
 * @affine: OUT
 *
 * Return the affine transformation needed to convert world coordinates to
 * item relative coordinates.
 **/
void
dia_canvas_item_affine_w2i (DiaCanvasItem *item, gdouble affine[6])
{
	gdouble inv[6];

	dia_canvas_item_affine_i2w (item, inv);
	
	art_affine_invert (affine, inv);
}

/**
 * dia_canvas_item_affine_point_w2i:
 * @item: 
 * @x: 
 * @y: 
 *
 * Convert a point (@x, @y) from world coordinates to item relative coordinates.
 **/
void
dia_canvas_item_affine_point_w2i (DiaCanvasItem *item, gdouble *x, gdouble *y)
{
	gdouble a[6];
	gdouble ox, oy;

	dia_canvas_item_affine_w2i (item, a);
	ox = *x;
	oy = *y;
	*x = ox * a[0] + oy * a[2] + a[4];
	*y = ox * a[1] + oy * a[3] + a[5];
}

/**
 * dia_canvas_item_affine_point_i2w:
 * @item: 
 * @x: 
 * @y: 
 *
 * Convert a point (@x, @y) from item to world coordinates.
 **/
void
dia_canvas_item_affine_point_i2w (DiaCanvasItem *item, gdouble *x, gdouble *y)
{
	gdouble a[6];
	gdouble ox, oy;

	dia_canvas_item_affine_i2w (item, a);
	ox = *x;
	oy = *y;
	*x = ox * a[0] + oy * a[2] + a[4];
	*y = ox * a[1] + oy * a[3] + a[5];
}

/**
 * dia_canvas_item_update_handles_i2w:
 * @item: 
 *
 * Sync the world coordinates and the item coordinates for all handles of @item.
 *
 **/
void
dia_canvas_item_update_handles_i2w (DiaCanvasItem *item)
{
	GList *l;
	gdouble affine[6];

	g_return_if_fail (DIA_IS_CANVAS_ITEM (item));

	dia_canvas_item_affine_i2w (item, affine);

	for (l = item->handles; l != NULL; l = l->next) {
		dia_handle_update_i2w_affine (l->data, affine);
	}
}

/**
 * dia_canvas_item_update_handles_w2i:
 * @item: 
 *
 * Sync all handles item coordinates to their world coordinates.
 **/
void
dia_canvas_item_update_handles_w2i (DiaCanvasItem *item)
{
	GList *l;
	gdouble affine[6];

	g_return_if_fail (DIA_IS_CANVAS_ITEM (item));

	dia_canvas_item_affine_w2i (item, affine);

	for (l = item->handles; l != NULL; l = l->next) {
		if (((DiaHandle*)l->data)->need_update_w2i) {
			dia_handle_update_w2i_affine (l->data, affine);
			((DiaHandle *) l->data)->need_update_w2i = FALSE;
		}
	}
}

static inline void
dia_canvas_item_set_state (DiaCanvasItem *item, gint new_state)
{
	g_signal_emit (item, canvas_item_signals[STATE_CHANGED],
			0, new_state);
}

static inline gboolean
dia_canvas_item_has_state (DiaCanvasItem *item, gint state)
{
	gboolean retval = FALSE;

	if (item->canvas && item->canvas->allow_state_requests)
		g_signal_emit (item, canvas_item_signals[HAS_STATE],
			       0, state, &retval);

	return retval;
}

/**
 * dia_canvas_item_select:
 * @item: 
 *
 * Select @item. More than one item can be selected (usualy by holding the
 * SHIFT or CONTROL button while selecting an item).
 * The root item can not be selected.
 **/
void
dia_canvas_item_select (DiaCanvasItem *item)
{
	g_return_if_fail (DIA_IS_CANVAS_ITEM (item));

	if (item->parent)
		dia_canvas_item_set_state (item, DIA_UI_STATE_SELECTED);
}

/**
 * dia_canvas_item_unselect:
 * @item: 
 *
 * Unselect @item.
 **/
void
dia_canvas_item_unselect (DiaCanvasItem *item)
{
	g_return_if_fail (DIA_IS_CANVAS_ITEM (item));

	dia_canvas_item_set_state (item, DIA_UI_STATE_UNSELECTED);
}

gboolean
dia_canvas_item_is_selected (DiaCanvasItem *item)
{
	g_return_val_if_fail (DIA_IS_CANVAS_ITEM (item), FALSE);

	return dia_canvas_item_has_state (item, DIA_UI_STATE_SELECTED);
}

/**
 * dia_canvas_item_focus:
 * @item: 
 *
 * @item becomes the focused item. A focused item is also selected, but it
 * has the privilege of being the last object that was selected.
 **/
void
dia_canvas_item_focus (DiaCanvasItem *item)
{
	g_return_if_fail (DIA_IS_CANVAS_ITEM (item));

	dia_canvas_item_set_state (item, DIA_UI_STATE_FOCUSED);
}

/**
 * dia_canvas_item_unfocus:
 * @item: 
 *
 * Unfocus the focused object.
 **/
void
dia_canvas_item_unfocus (DiaCanvasItem *item)
{
	g_return_if_fail (DIA_IS_CANVAS_ITEM (item));

	dia_canvas_item_set_state (item, DIA_UI_STATE_SELECTED);
}

gboolean
dia_canvas_item_is_focused (DiaCanvasItem *item)
{
	g_return_val_if_fail (DIA_IS_CANVAS_ITEM (item), FALSE);

	return dia_canvas_item_has_state (item, DIA_UI_STATE_FOCUSED);
}

/**
 * dia_canvas_item_grab:
 * @item: 
 *
 * Make @item the grabbed item. The grabbed item is also selected and also
 * has the focus. A grabbed item will recieve all events send to the canvas,
 * even if the event is not even near @item.
 **/
void
dia_canvas_item_grab (DiaCanvasItem *item)
{
	g_return_if_fail (DIA_IS_CANVAS_ITEM (item));

	dia_canvas_item_set_state (item, DIA_UI_STATE_GRABBED);
}

/**
 * dia_canvas_item_ungrab:
 * @item: 
 *
 * Ungrab the item. Inverse of dia_canvas_grab(). Does set the item's state
 * to focused.
 **/
void
dia_canvas_item_ungrab (DiaCanvasItem *item)
{
	g_return_if_fail (DIA_IS_CANVAS_ITEM (item));

	dia_canvas_item_set_state (item, DIA_UI_STATE_FOCUSED);
}

gboolean
dia_canvas_item_is_grabbed (DiaCanvasItem *item)
{
	g_return_val_if_fail (DIA_IS_CANVAS_ITEM (item), FALSE);

	return dia_canvas_item_has_state (item, DIA_UI_STATE_GRABBED);
}


/**
 * dia_canvas_item_visible:
 * @item: 
 *
 * Make @item visible.
 **/
void
dia_canvas_item_visible (DiaCanvasItem *item)
{
	g_return_if_fail (DIA_IS_CANVAS_ITEM (item));

	dia_canvas_item_preserve_property (item, "visible");
	DIA_SET_FLAGS (item, DIA_VISIBLE);
	dia_canvas_item_set_state (item, DIA_UI_STATE_UNCHANGED);
}

/**
 * dia_canvas_item_invisible:
 * @item: 
 *
 * Make @item invisible.
 **/
void
dia_canvas_item_invisible (DiaCanvasItem *item)
{
	g_return_if_fail (DIA_IS_CANVAS_ITEM (item));

	dia_canvas_item_preserve_property (item, "visible");

	DIA_UNSET_FLAGS (item, DIA_VISIBLE);
	
	dia_canvas_item_set_state (item, DIA_UI_STATE_UNCHANGED);
}

gboolean
dia_canvas_item_is_visible (DiaCanvasItem *item)
{
	return ((DIA_CANVAS_ITEM_FLAGS (item) & DIA_VISIBLE) != 0);
}

/**
 * dia_canvas_item_bb_affine:
 * @item: 
 * @affine: 
 * @x1: OUT
 * @y1: OUT
 * @x2: OUT
 * @y2: OUT
 *
 * Calculate the bounding box of @item after a @affine transformation.
 **/
void
dia_canvas_item_bb_affine (DiaCanvasItem* item, gdouble affine[6],
			   gdouble *x1, gdouble *y1, gdouble *x2, gdouble *y2)
{
#if 1
	ArtDRect r;

	art_drect_affine_transform (&r, (ArtDRect*) &item->bounds, affine);
	*x1 = r.x0;
	*y1 = r.y0;
	*x2 = r.x1;
	*y2 = r.y1;
#else
	gdouble vals[8];

	vals[0] = item->bounds.left * affine[0];
	vals[1] = item->bounds.right * affine[0];
	vals[2] = item->bounds.left * affine[1];
	vals[3] = item->bounds.right * affine[1];
	vals[4] = item->bounds.top * affine[2];
	vals[5] = item->bounds.bottom * affine[2];
	vals[6] = item->bounds.top * affine[3];
	vals[7] = item->bounds.bottom * affine[3];

	*x1 = MIN (vals[0], vals[1]) + MIN (vals[4], vals[5]) + affine[4];
	*y1 = MIN (vals[2], vals[3]) + MIN (vals[6], vals[7]) + affine[5];
	*x2 = MAX (vals[0], vals[1]) + MAX (vals[4], vals[5]) + affine[4];
	*y2 = MAX (vals[2], vals[3]) + MAX (vals[6], vals[7]) + affine[5];
#endif
}

/**
 * dia_canvas_item_connect:
 * @item: 
 * @handle: 
 *
 * Connect @handle to @item. @Item should not be the owner of @handle.
 * If the "connect" signal returns FALSE, the object will not be connected.
 * 
 * During the connection process, a position for @handle is calculated and
 * the handle is moved to that position (so it is not nessesary to do a glue()
 * before connecting).
 *
 * Return value: TRUE on success, FALSE otherwise.
 **/
gboolean
dia_canvas_item_connect (DiaCanvasItem *item, DiaHandle *handle)
{
	gint result = FALSE;

	g_return_val_if_fail (DIA_IS_CANVAS_ITEM (item), FALSE);
	g_return_val_if_fail (DIA_IS_HANDLE (handle), FALSE);
	g_return_val_if_fail (handle->connectable, FALSE);
	g_return_val_if_fail (handle->owner != item, FALSE);

	g_signal_emit (item, canvas_item_signals[CONNECT],
			0, handle, &result);

	return result;
}

/**
 * dia_canvas_item_disconnect:
 * @item: item the handle is connected too.
 * @handle: handle to be disconnected from @item.
 *
 * Disconnect @handle from @item. @handle->connected_to should be the same
 * object as @item.
 *
 * Return value: TRUE on success, FALSE of failure.
 **/
gboolean
dia_canvas_item_disconnect (DiaCanvasItem *item, DiaHandle *handle)
{
	gint result = FALSE;

	g_return_val_if_fail (DIA_IS_CANVAS_ITEM (item), FALSE);
	g_return_val_if_fail (DIA_IS_HANDLE (handle), FALSE);
	g_return_val_if_fail (handle->connected_to == item, FALSE);
	g_return_val_if_fail (DIA_IS_CANVAS_ITEM (handle->connected_to), FALSE);

	g_signal_emit (item, canvas_item_signals[DISCONNECT],
			0, handle, &result);

	return result;
}

/**
 * dia_canvas_item_disconnect_handles:
 * @item: 
 *
 * Disconnect all handles of @item. Note that this function disconnects all
 * handles that belong to @item, while dia_canvas_item_disconnect() disconnects
 * a handle that is connected to an item!
 *
 * Return value: TRUE if no handles are connected, FALSE otherwise.
 **/
gboolean
dia_canvas_item_disconnect_handles (DiaCanvasItem *item)
{
	gint result = TRUE;
	GList *l, *next;

	/* Disconnect the item's handles */
	for (l = item->handles; l != NULL; l = l->next) {
		if (DIA_HANDLE (l->data)->connected_to)
			result &= dia_canvas_item_disconnect (DIA_HANDLE (l->data)->connected_to, l->data);
	}

	/* Disconnect handles connected to the item */
	l = item->connected_handles;
	while (l != NULL) {
		next = l->next;
		result &= dia_canvas_item_disconnect (item, l->data);
		l = next;
	}

	return result;
}

/**
 * dia_canvas_item_identity:
 * @item: 
 *
 * Remove all transformations from @item.
 **/
void
dia_canvas_item_identity (DiaCanvasItem *item)
{
	gdouble a, b;
	gdouble x1, y1, x2, y2;

	g_return_if_fail (DIA_IS_CANVAS_ITEM(item));

	dia_canvas_item_preserve_property (item, "affine");

	dia_canvas_item_update_handles_w2i (item);

	a = item->bounds.left + (item->bounds.right - item->bounds.left) / 2;
	b = item->bounds.top + (item->bounds.bottom - item->bounds.top) / 2;

	x1 = a * item->affine[0] + b * item->affine[2] + item->affine[4];
	y1 = a * item->affine[1] + b * item->affine[3] + item->affine[5];

	item->affine[0] = 1.0;
	item->affine[1] = 0.0;
	item->affine[2] = 0.0;
	item->affine[3] = 1.0;

	x2 = a * item->affine[0] + b * item->affine[2] + item->affine[4];
	y2 = a * item->affine[1] + b * item->affine[3] + item->affine[5];

	item->affine[4] += x1 - x2;
	item->affine[5] += y1 - y2;

	dia_canvas_item_update_handles_i2w (item);
}

/**
 * dia_canvas_item_scale:
 * @item: 
 * @sx: 
 * @sy: 
 *
 * Scale the object. The objects center of its bounding box will not move.
 **/
void
dia_canvas_item_scale (DiaCanvasItem *item, gdouble sx, gdouble sy)
{
	gdouble affine[6];
	gdouble a, b;
	gdouble x1, y1, x2, y2;

	g_return_if_fail (DIA_IS_CANVAS_ITEM(item));

	dia_canvas_item_preserve_property (item, "affine");

	dia_canvas_item_update_handles_w2i (item);

	a = item->bounds.left + (item->bounds.right - item->bounds.left) / 2;
	b = item->bounds.top + (item->bounds.bottom - item->bounds.top) / 2;

	x1 = a * item->affine[0] + b * item->affine[2] + item->affine[4];
	y1 = a * item->affine[1] + b * item->affine[3] + item->affine[5];

	art_affine_scale (affine, sx, sy);
	art_affine_multiply (item->affine, affine, item->affine);

	x2 = a * item->affine[0] + b * item->affine[2] + item->affine[4];
	y2 = a * item->affine[1] + b * item->affine[3] + item->affine[5];

	item->affine[4] += x1 - x2;
	item->affine[5] += y1 - y2;

	dia_canvas_item_update_handles_i2w (item);
}

/**
 * dia_canvas_item_rotate:
 * @item: 
 * @degrees: 
 *
 * Rotate @item around the center of its bounding box.
 **/
void
dia_canvas_item_rotate (DiaCanvasItem *item, gdouble degrees)
{
	gdouble affine[6];
	gdouble a, b;
	gdouble x1, y1, x2, y2;

	g_return_if_fail (DIA_IS_CANVAS_ITEM(item));

	dia_canvas_item_preserve_property (item, "affine");

	dia_canvas_item_update_handles_w2i (item);

	a = item->bounds.left + (item->bounds.right - item->bounds.left) / 2;
	b = item->bounds.top + (item->bounds.bottom - item->bounds.top) / 2;

	x1 = a * item->affine[0] + b * item->affine[2] + item->affine[4];
	y1 = a * item->affine[1] + b * item->affine[3] + item->affine[5];

	art_affine_rotate (affine, degrees);
	art_affine_multiply (item->affine, affine, item->affine);

	x2 = a * item->affine[0] + b * item->affine[2] + item->affine[4];
	y2 = a * item->affine[1] + b * item->affine[3] + item->affine[5];

	item->affine[4] += x1 - x2;
	item->affine[5] += y1 - y2;

	dia_canvas_item_update_handles_i2w (item);
}

/**
 * dia_canvas_item_shear_x:
 * @item: 
 * @dx: 
 * @dy: 
 *
 * Shear the object.
 **/
void
dia_canvas_item_shear_x (DiaCanvasItem *item, gdouble dx, gdouble dy)
{
	gdouble affine[6];
	gdouble a, b;
	gdouble x1, y1, x2, y2;

	g_return_if_fail (DIA_IS_CANVAS_ITEM(item));

	dia_canvas_item_preserve_property (item, "affine");

	dia_canvas_item_update_handles_w2i (item);

	a = item->bounds.left + (item->bounds.right - item->bounds.left) / 2;
	b = item->bounds.top + (item->bounds.bottom - item->bounds.top) / 2;

	x1 = a * item->affine[0] + b * item->affine[2] + item->affine[4];
	y1 = a * item->affine[1] + b * item->affine[3] + item->affine[5];

	art_affine_identity (affine);
	affine[2] = atan2 (dx, dy + ((item->bounds.bottom - item->bounds.top) / 2));
	art_affine_multiply (item->affine, affine, item->affine);

	x2 = a * item->affine[0] + b * item->affine[2] + item->affine[4];
	y2 = a * item->affine[1] + b * item->affine[3] + item->affine[5];

	item->affine[4] += x1 - x2;
	item->affine[5] += y1 - y2;

	dia_canvas_item_update_handles_i2w (item);
}


/**
 * dia_canvas_item_shear_y:
 * @item: 
 * @dx: 
 * @dy: 
 *
 * Like dia_canvas_item_shear_x(), but now for the y axis.
 **/
void
dia_canvas_item_shear_y (DiaCanvasItem *item, gdouble dx, gdouble dy)
{
	gdouble affine[6];
	gdouble a, b;
	gdouble x1, y1, x2, y2;

	g_return_if_fail (DIA_IS_CANVAS_ITEM(item));

	dia_canvas_item_preserve_property (item, "affine");

	dia_canvas_item_update_handles_w2i (item);

	a = item->bounds.left + (item->bounds.right - item->bounds.left) / 2;
	b = item->bounds.top + (item->bounds.bottom - item->bounds.top) / 2;

	x1 = a * item->affine[0] + b * item->affine[2] + item->affine[4];
	y1 = a * item->affine[1] + b * item->affine[3] + item->affine[5];

	art_affine_identity (affine);
	affine[1] = atan2 (dy, dx + ((item->bounds.right - item->bounds.left) / 2));
	art_affine_multiply (item->affine, affine, item->affine);

	x2 = a * item->affine[0] + b * item->affine[2] + item->affine[4];
	y2 = a * item->affine[1] + b * item->affine[3] + item->affine[5];

	item->affine[4] += x1 - x2;
	item->affine[5] += y1 - y2;

	dia_canvas_item_update_handles_i2w (item);
}

/**
 * dia_canvas_item_flip:
 * @item: 
 * @horz: 
 * @vert: 
 *
 * Flip @item around its horizontal and/or vertical axis.
 **/
void
dia_canvas_item_flip (DiaCanvasItem *item, gboolean horz, gboolean vert)
{
	g_return_if_fail (DIA_IS_CANVAS_ITEM(item));

	dia_canvas_item_preserve_property (item, "affine");

	dia_canvas_item_update_handles_w2i (item);

	art_affine_flip (item->affine, item->affine, horz, vert);

	dia_canvas_item_update_handles_i2w (item);
}

/**
 * dia_canvas_item_move:
 * @item: 
 * @dx: 
 * @dy: 
 *
 * Move @item. Use dia_canvas_item_move_interactive() in event handlers.
 **/
void
dia_canvas_item_move (DiaCanvasItem *item, gdouble dx, gdouble dy)
{
	g_return_if_fail (DIA_IS_CANVAS_ITEM(item));

	g_signal_emit (item, canvas_item_signals[MOVE], 0,
		       dx, dy, FALSE);
}

/**
 * dia_canvas_item_move_interactive:
 * @item: 
 * @dx: 
 * @dy: 
 *
 * Move @item. This function raises the #DiaCanvas::move signal, which is
 * caught by the active #DiaCanvasView. The view will request all selected
 * objects to move.
 *
 * This function should be used in event handlers.
 **/
void
dia_canvas_item_move_interactive (DiaCanvasItem *item, gdouble dx, gdouble dy)
{
	g_return_if_fail (DIA_IS_CANVAS_ITEM(item));

	g_signal_emit (item, canvas_item_signals[MOVE], 0,
		       dx, dy, TRUE);
}

/**
 * dia_canvas_item_expand_bounds:
 * @item: 
 * @d: number to expand the bounding box with
 *
 * Expand the bounding box of @item in all four directions. This is convenient
 * if you have drawn a shape that comes out of the bounding box a little bit.
 **/
void
dia_canvas_item_expand_bounds (DiaCanvasItem *item, gdouble d)
{
	g_return_if_fail (DIA_IS_CANVAS_ITEM (item));

	item->bounds.left -= d;
	item->bounds.right += d;
	item->bounds.top -= d;
	item->bounds.bottom += d;
}

/**
 * dia_canvas_item_get_shape_iter:
 * @item: 
 * @iter: 
 *
 * Return value: The first shape to be drawn.
 **/
gboolean
dia_canvas_item_get_shape_iter (DiaCanvasItem *item, DiaCanvasIter *iter)
{
	DiaCanvasItemClass *class;
	gboolean retval = FALSE;

	g_return_val_if_fail (DIA_IS_CANVAS_ITEM (item), FALSE);
	g_return_val_if_fail (iter != FALSE, FALSE);

	dia_canvas_iter_init(iter);

	class = DIA_CANVAS_ITEM_GET_CLASS (item);

	if (class->get_shape_iter) {
		retval = class->get_shape_iter (item, iter);
		if (!retval)
			dia_canvas_iter_destroy (iter);

	}
	return retval;
}

/**
 * dia_canvas_item_next_shape:
 * @item: 
 * @iter: 
 *
 * Get the next shape to be drawn on the canvas. You should first call
 * dia_canvas_item_get_shape_iter() to
 * obtain the first shape. Use dia_canvas_item_shape_value() to retrieve the
 * shape pointed to by the iterator. If no more shapes are present, the
 * iterator is destroyed. If you quit before the last shape is requested, make
 * sure you call dia_canvas_iter_destroy() yourself.
 *
 * Return value: TRUE if there is a next shape, FALSE otherwise.
 **/
gboolean
dia_canvas_item_shape_next (DiaCanvasItem *item, DiaCanvasIter *iter)
{
	gboolean retval = FALSE;
	DiaCanvasItemClass *class;

	g_return_val_if_fail (DIA_IS_CANVAS_ITEM (item), FALSE);
	g_return_val_if_fail (iter != NULL, FALSE);

	class = DIA_CANVAS_ITEM_GET_CLASS (item);

	if (class->shape_next)
		retval = class->shape_next (item, iter);

	if (!retval)
		dia_canvas_iter_destroy (iter);

	return retval;
}

/**
 * dia_canvas_item_shape_value:
 * @item: 
 * @iter: 
 *
 * Return value: The value that corresponds with the values of the iterator.
 **/
DiaShape*
dia_canvas_item_shape_value (DiaCanvasItem *item, DiaCanvasIter *iter)
{
	DiaCanvasItemClass *class;

	g_return_val_if_fail (DIA_IS_CANVAS_ITEM (item), NULL);
	g_return_val_if_fail (iter != NULL, NULL);

	class = DIA_CANVAS_ITEM_GET_CLASS (item);

	if (class->shape_value)
		return class->shape_value (item, iter);

	return NULL;
}

/**
 * dia_canvas_item_preserve_property:
 * @item: 
 * @property_name: 
 *
 * Shorthand for dia_canvas_preserve_property(). It adds a property to the
 * canvas' undo stack.
 **/
void
dia_canvas_item_preserve_property (DiaCanvasItem *item,
				   const gchar *property_name)
{
	g_return_if_fail (DIA_IS_CANVAS_ITEM (item));
	g_return_if_fail (property_name != NULL);

	if (item->canvas)
		dia_canvas_preserve_property (item->canvas,
					      G_OBJECT (item), property_name);
}

