/*
 * DiaCanvasLine
 *
 * This is LGPL'ed code.
 */

#include "dia-canvas-line.h"
#include <math.h>
#include "dia-handle.h"
#include "dia-shape.h"
#include "diatypebuiltins.h"
#include "dia-canvas-i18n.h"


#define NUM_ARROW_POINTS 4

enum {
	PROP_LINE_WIDTH = 1,
	PROP_COLOR,
	PROP_CAP,
	PROP_JOIN,
	PROP_CYCLIC,
	PROP_ORTHOGONAL,
	PROP_HORIZONTAL,
	PROP_ADD_POINT,
	PROP_ADD_SEGMENT,
	PROP_DEL_SEGMENT,
	PROP_DASH,
	PROP_HEAD_POS,
	PROP_TAIL_POS,
	PROP_HAS_HEAD,
	PROP_HEAD_A,
	PROP_HEAD_B,
	PROP_HEAD_C,
	PROP_HEAD_D,
	PROP_HEAD_COLOR,
	PROP_HEAD_FILL_COLOR,
	PROP_HAS_TAIL,
	PROP_TAIL_A,
	PROP_TAIL_B,
	PROP_TAIL_C,
	PROP_TAIL_D,
	PROP_TAIL_COLOR,
	PROP_TAIL_FILL_COLOR
};

#define get_connected_handles(handle) \
	g_object_steal_qdata(G_OBJECT(handle), q_connected_handles)
#define set_connected_handles(handle, list) \
	g_object_set_qdata_full(G_OBJECT(handle), q_connected_handles, \
			        list, dia_canvas_line_destroy_connected_handles)

static void dia_canvas_line_class_init (DiaCanvasLineClass *klass);
static void dia_canvas_line_init (DiaCanvasLine *item);
static void dia_canvas_line_set_property (GObject *object,
					  guint property_id,
					  const GValue *value,
					  GParamSpec *pspec);
static void dia_canvas_line_get_property (GObject *object,
					  guint property_id,
					  GValue *value,
					  GParamSpec *pspec);
static void dia_canvas_line_dispose		(GObject *object);
static void dia_canvas_line_update (DiaCanvasItem *item, gdouble affine[6]);
static gboolean dia_canvas_line_get_shape_iter	(DiaCanvasItem *item,
						 DiaCanvasIter *iter);
static gboolean dia_canvas_line_shape_next	(DiaCanvasItem *item,
						 DiaCanvasIter *iter);
static DiaShape* dia_canvas_line_shape_value	(DiaCanvasItem *item,
						 DiaCanvasIter *iter);
static gdouble dia_canvas_line_point (DiaCanvasItem *item,
				      gdouble x, gdouble y);
static void dia_canvas_line_handle_motion (DiaCanvasItem *item,
					   DiaHandle *handle,
					   gdouble *wx, gdouble *wy,
					   DiaEventMask mask);
static gdouble dia_canvas_line_glue (DiaCanvasItem *item, DiaHandle *handle,
				     gdouble *x, gdouble *y);
static gboolean dia_canvas_line_connect		(DiaCanvasItem *item,
						 DiaHandle *handle);
static gboolean dia_canvas_line_disconnect	(DiaCanvasItem *item,
						 DiaHandle *handle);

static void dia_canvas_line_parent_notify	(DiaCanvasLine *line);
static void dia_canvas_line_destroy_connected_handles (gpointer data);

static void add_ortho_constraints		(DiaCanvasLine *line);
static void remove_ortho_constraints		(DiaCanvasLine *line);

static DiaCanvasItemClass *parent_class = NULL;

/* Quark for othogonal constraints. */
static GQuark q_ortho_constraint = 0;

/* A list of handles that are connected to a specific line segment.
 * The list is of type GSList* and is connected to the last handle of a
 * segment (this means that handle 0 will never have a list of connected
 * handles). This list is used in case the user creates or deletes a line
 * segment. The line is able to create new constraints for the connected
 * handles (note that the handle->owner::connect() function is not used here.
 */
static GQuark q_connected_handles = 0;

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

	if (!object_type) {
		static const GTypeInfo object_info = {
			sizeof (DiaCanvasLineClass),
			(GBaseInitFunc) NULL,
			(GBaseFinalizeFunc) NULL,
			(GClassInitFunc) dia_canvas_line_class_init,
			(GClassFinalizeFunc) NULL,
			(gconstpointer) NULL, /* class_data */
			sizeof (DiaCanvasLine),
			(guint16) 0, /* n_preallocs */
			(GInstanceInitFunc) dia_canvas_line_init,
		};

		object_type = g_type_register_static (DIA_TYPE_CANVAS_ITEM,
						      "DiaCanvasLine",
						      &object_info, 0);
		q_ortho_constraint = g_quark_from_static_string ("DiaCanvasLine::ortho_constraint");
		q_connected_handles = g_quark_from_static_string ("DiaCanvasLine::connected_handles");
	}

	return object_type;
}


static void
dia_canvas_line_class_init (DiaCanvasLineClass *klass)
{
	GObjectClass *object_class;
	DiaCanvasItemClass *item_class;
	
	object_class = (GObjectClass*) klass;
	item_class = DIA_CANVAS_ITEM_CLASS (klass);
	
	parent_class = g_type_class_peek_parent (klass);

	object_class->dispose = dia_canvas_line_dispose;
	object_class->set_property = dia_canvas_line_set_property;
	object_class->get_property = dia_canvas_line_get_property;

	item_class->update = dia_canvas_line_update;
	item_class->get_shape_iter = dia_canvas_line_get_shape_iter;
	item_class->shape_next = dia_canvas_line_shape_next;
	item_class->shape_value = dia_canvas_line_shape_value;
	item_class->point = dia_canvas_line_point;
	item_class->handle_motion = dia_canvas_line_handle_motion;
	item_class->glue = dia_canvas_line_glue;
	item_class->connect = dia_canvas_line_connect;
	item_class->disconnect = dia_canvas_line_disconnect;

	g_object_class_install_property (object_class, PROP_LINE_WIDTH,
					 g_param_spec_double ("line_width",
						 _("Line width"),
						 _("Width of the line"),
						 0.0, G_MAXDOUBLE, 1.0,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_COLOR,
					 g_param_spec_ulong ("color",
						 _("Color"),
						 _("Color of the line in RGBA"),
						 0, G_MAXULONG, 0,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_CAP,
					 g_param_spec_enum ("cap",
						 _("Cap style"),
						 _("Style of the line ends"),
						 DIA_TYPE_CAP_STYLE,
						 DIA_CAP_BUTT,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_JOIN,
					 g_param_spec_enum ("join",
						 _("Join style"),
						 _("Style of the line's corners"),
						 DIA_TYPE_JOIN_STYLE,
						 DIA_JOIN_MITER,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_CYCLIC,
					 g_param_spec_boolean ("cyclic",
						 _("Cyclic"),
						 _("Line is cyclic, a polygon"),
						 FALSE,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_ORTHOGONAL,
					 g_param_spec_boolean ("orthogonal",
						 _("Orthogonal"),
						 _("Add contains to the line points to make them orthogonal (horizontal and vertical)."),
						 FALSE,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_HORIZONTAL,
					 g_param_spec_boolean ("horizontal",
						 _("Horizontal"),
						 _("Make the first segment of the line horizontal. This should be used in conjuncion with the 'orthogonal' option."),
						 FALSE,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_ADD_POINT,
					 g_param_spec_boxed ("add_point",
						 _("Add point"),
						 _("Point to add to the line"),
						 DIA_TYPE_POINT,
						 G_PARAM_WRITABLE));
	g_object_class_install_property (object_class, PROP_ADD_SEGMENT,
					 g_param_spec_uint ("add_segment",
						 _("Add segment"),
						 _("Add a segment by splitting the given segment into two."),
						 0, G_MAXUINT, 0,
						 G_PARAM_WRITABLE));
	g_object_class_install_property (object_class, PROP_DEL_SEGMENT,
					 g_param_spec_uint ("del_segment",
						 _("Delete segment"),
						 _("Delete a segment, first segment is indexed with 0."),
						 0, G_MAXUINT, 0,
						 G_PARAM_WRITABLE));
	g_object_class_install_property (object_class, PROP_DASH,
					 g_param_spec_boxed ("dash",
						 _("Dash"),
						 _("Dash style, of type DiaDashStyle."),
						 DIA_TYPE_DASH_STYLE,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_HEAD_POS,
					 g_param_spec_boxed ("head_pos",
						 _("Head point position"),
						 _(""),
						 DIA_TYPE_POINT,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_TAIL_POS,
					 g_param_spec_boxed ("tail_pos",
						 _("Tail point position"),
						 _(""),
						 DIA_TYPE_POINT,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_HAS_HEAD,
					 g_param_spec_boolean ("has_head",
						 _("Has Head"),
						 _("The line should have an arrow on the beginning of the line. The arrow is descripbed by 'head_[abcd]'."),
						 FALSE,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_HAS_TAIL,
					 g_param_spec_boolean ("has_tail",
						 _("Has Tail"),
						 _("The line should have an arrow on the end of the line. The arrow is descripbed by 'tail_[abcd]'."),
						 FALSE,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_HEAD_COLOR,
					 g_param_spec_ulong ("head_color",
						 _("Head color"),
						 _("Color of the head arrow in RGBA"),
						 0, G_MAXULONG, 0,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_TAIL_COLOR,
					 g_param_spec_ulong ("tail_color",
						 _("Tail color"),
						 _("Color of the end arrow in RGBA"),
						 0, G_MAXULONG, 0,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_HEAD_FILL_COLOR,
					 g_param_spec_ulong ("head_fill_color",
						 _("Head fill color"),
						 _("Color of the line in RGBA"),
						 0, G_MAXULONG, 0,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_TAIL_FILL_COLOR,
					 g_param_spec_ulong ("tail_fill_color",
						 _("Tail fill color"),
						 _("Color of the line in RGBA"),
						 0, G_MAXULONG, 0,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_HEAD_A,
					 g_param_spec_double ("head_a",
						 _("Head A"),
						 _(""),
						 0.0, G_MAXDOUBLE, 0.0,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_HEAD_B,
					 g_param_spec_double ("head_b",
						 _("Head B"),
						 _(""),
						 0.0, G_MAXDOUBLE, 0.0,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_HEAD_C,
					 g_param_spec_double ("head_c",
						 _("Head C"),
						 _(""),
						 0.0, G_MAXDOUBLE, 0.0,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_HEAD_D,
					 g_param_spec_double ("head_d",
						 _("Head D"),
						 _(""),
						 0.0, G_MAXDOUBLE, 0.0,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_TAIL_A,
					 g_param_spec_double ("tail_a",
						 _("Tail A"),
						 _(""),
						 0.0, G_MAXDOUBLE, 0.0,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_TAIL_B,
					 g_param_spec_double ("tail_b",
						 _("Tail B"),
						 _(""),
						 0.0, G_MAXDOUBLE, 0.0,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_TAIL_C,
					 g_param_spec_double ("tail_c",
						 _("Tail C"),
						 _(""),
						 0.0, G_MAXDOUBLE, 0.0,
						 G_PARAM_READWRITE));
	g_object_class_install_property (object_class, PROP_TAIL_D,
					 g_param_spec_double ("tail_d",
						 _("Tail D"),
						 _(""),
						 0.0, G_MAXDOUBLE, 0.0,
						 G_PARAM_READWRITE));
}


static void
dia_canvas_line_init (DiaCanvasLine *item)
{
	int i;

	item->color = DIA_COLOR (0, 0, 0);
	item->line_width = 2.0;
	item->cap = DIA_CAP_ROUND;
	item->join = DIA_JOIN_ROUND;
	item->cyclic = FALSE;
	item->orthogonal = FALSE;
	item->horizontal = TRUE;
	item->n_dash = 0;
	item->dash = NULL;
	item->has_head = FALSE;
	item->has_tail = FALSE;
	item->head_a = 0.0;
	item->head_b = 0.0;
	item->head_c = 0.0;
	item->head_d = 0.0;
	item->tail_a = 0.0;
	item->tail_b = 0.0;
	item->tail_c = 0.0;
	item->tail_d = 0.0;
	item->head_color = DIA_COLOR (0, 0, 0);
	item->tail_color = DIA_COLOR (0, 0, 0);
	item->head_fill_color = DIA_COLOR (0, 0, 0);
	item->tail_fill_color = DIA_COLOR (0, 0, 0);
	item->line = dia_shape_new (DIA_SHAPE_PATH);
	item->head_arrow = dia_shape_new (DIA_SHAPE_PATH);
	item->tail_arrow = dia_shape_new (DIA_SHAPE_PATH);

	/* A line should have at least two points: */
	for (i = 0; i < 2; i++) {
		g_object_new (DIA_TYPE_HANDLE,
				"owner", item,
				"movable", TRUE,
				"connectable", TRUE,
				NULL);
	}

	/* Set a signal that updates a few variables when the parent changes: */
	g_signal_connect (G_OBJECT (item), "notify::parent",
			  G_CALLBACK (dia_canvas_line_parent_notify), NULL);
}


static void
dia_canvas_line_set_property (GObject *object, guint property_id,
			      const GValue *value, GParamSpec *pspec)
{
	DiaCanvasItem *item = (DiaCanvasItem*) object;
	DiaCanvasLine *line = (DiaCanvasLine*) object;

	switch (property_id) {
	case PROP_LINE_WIDTH:
		dia_canvas_item_preserve_property (item, "line_width");
		line->line_width = g_value_get_double (value);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (line));
		break;
	case PROP_COLOR:
		dia_canvas_item_preserve_property (item, "color");
		line->color = (DiaColor) g_value_get_ulong (value);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (line));
		break;
	case PROP_CAP:
		dia_canvas_item_preserve_property (item, "cap");
		line->cap = (DiaCapStyle) g_value_get_enum (value);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (line));
		break;
	case PROP_JOIN:
		dia_canvas_item_preserve_property (item, "join");
		line->join = (DiaJoinStyle) g_value_get_enum (value);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (line));
		break;
	case PROP_CYCLIC:
		dia_canvas_item_preserve_property (item, "cyclic");
		line->cyclic = g_value_get_boolean (value);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (line));
		break;
	case PROP_ORTHOGONAL:
		dia_canvas_item_preserve_property (item, "orthogonal");
		if (!item->canvas) {
			line->orthogonal = g_value_get_boolean (value);
			break;
		}
		if (!line->orthogonal) {
			GList *l;
			for (l = item->handles; l != NULL; l = l->next)
				dia_canvas_preserve_property (item->canvas,
							      l->data, "pos_i");
		}
		line->orthogonal = g_value_get_boolean (value);
		if (line->orthogonal) {
			add_ortho_constraints (line);
		} else {
			remove_ortho_constraints (line);
		}
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (line));
		break;
	case PROP_HORIZONTAL:
		dia_canvas_item_preserve_property (item, "horizontal");
		if (!item->canvas) {
			line->horizontal = g_value_get_boolean (value);
			break;
		}
		if (line->orthogonal) {
			GList *l;
			for (l = item->handles; l != NULL; l = l->next)
				dia_canvas_preserve_property (item->canvas,
							      l->data, "pos_i");
		}
		line->horizontal = g_value_get_boolean (value);
		if (line->orthogonal) {
			add_ortho_constraints (line);
		}
		dia_canvas_item_request_update (item);
		break;
	case PROP_ADD_POINT: {
			DiaPoint *p = g_value_get_boxed (value);
			DiaHandle *h;

			/* Add constraints when needed: */
			if (item->canvas && line->orthogonal) {
				GList *l;
				dia_canvas_item_preserve_property (item,
								"orthogonal");
				for (l = item->handles; l != NULL; l = l->next)
					dia_canvas_preserve_property (item->canvas,
							l->data, "pos_i");
			}

			if (g_list_length (item->handles) > 1) {
				h = g_list_last (item->handles)->data;
				g_object_set (h, "connectable", FALSE, NULL);;
				if (item->canvas && h->connected_to)
					dia_canvas_item_disconnect (h->connected_to, h);
			}

			h = g_object_new (DIA_TYPE_HANDLE,
					  "owner", item,
					  "pos_i", p,
					  "movable", TRUE,
					  "connectable", TRUE,
					  NULL);
			if (line->orthogonal)
				add_ortho_constraints (line);
		}
		dia_canvas_item_request_update (item);
		break;
	case PROP_ADD_SEGMENT: {
			guint segment;
			GSList *handles_conn = NULL, *hc;
			DiaPoint p1, p2, pnew;
			DiaHandle *h1, *h2, *hnew;

			segment = g_value_get_uint(value);
			if ((segment < 0)
			    && (segment > g_list_length (item->handles) - 2)) {
				g_warning (_("The new segment should be between 0 and %d inclusive (was %d)"), g_list_length (item->handles) - 2, segment);
				break;
			}

			/* Store original handle positions. */
			if (item->canvas && line->orthogonal) {
				GList *l;
				dia_canvas_item_preserve_property (item,
								"orthogonal");
				for (l = item->handles; l != NULL; l = l->next)
					dia_canvas_preserve_property (item->canvas,
							l->data, "pos_i");
			}

			h1 = g_list_nth_data (item->handles, segment);
			h2 = g_list_nth_data (item->handles, segment + 1);
			dia_handle_get_pos_i (h1, &p1.x, &p1.y);
			dia_handle_get_pos_i (h2, &p2.x, &p2.y);

			/* Create the new handle and put it into place */
			pnew.x = (p1.x + p2.x) / 2.0;
			pnew.y = (p1.y + p2.y) / 2.0;
			hnew = g_object_new (DIA_TYPE_HANDLE,
					     "owner", item,
					     "pos_i", &pnew,
					     "movable", TRUE,
					     "connectable", FALSE);

			/* Move the handle between h1 and h2: */
			item->handles = g_list_remove (item->handles, hnew);
			item->handles = g_list_insert (item->handles,
						       hnew, segment + 1);

			/* Find all connected handles: */
			handles_conn = get_connected_handles (h2);

			/* Reconnect handles connected to h2. We should figure
			 * out if they should be added to h2 or hnew. */
			for (hc = handles_conn; hc != NULL; hc = hc->next) {
				DiaHandle *handle = hc->data;
				gdouble dist1, dist2;
				DiaPoint hpos, pos1, pos2;
				DiaHandle *hbegin, *hend;

				dia_handle_remove_all_constraints (handle);
				dia_handle_get_pos_w (handle,
						      &hpos.x, &hpos.y);
				dia_canvas_item_affine_point_w2i (item,
							&hpos.x, &hpos.y);

				dist1 = dia_distance_line_point (&p1, &pnew,
						 &hpos, line->line_width,
						 DIA_CAP_ROUND, &pos1);
				dist2 = dia_distance_line_point (&pnew, &p2,
						 &hpos, line->line_width,
						 DIA_CAP_ROUND, &pos2);
				if (dist1 < dist2) {
					hbegin = h1;
					hend = hnew;
				} else {
					hbegin = hnew;
					hend = h2;
				}
				/* Make the handle move undoable. */
				dia_canvas_preserve_property_last (item->canvas,
							      G_OBJECT (handle),
							      "pos_i");
				dia_canvas_preserve_property_last (item->canvas,
							      G_OBJECT (handle),
							      "connect");
				dia_handle_add_line_constraint (hbegin, hend, handle);
				/* Store the newly connected handle */
				handles_conn = get_connected_handles (hend);
				handles_conn = g_slist_prepend (handles_conn,
								g_object_ref (handle));
				set_connected_handles (hend, handles_conn);
			}

			if (line->orthogonal)
				add_ortho_constraints (line);
		}
		dia_canvas_item_request_update (item);
		break;
	case PROP_DEL_SEGMENT: {
			DiaHandle *h1, *h2, *h3;
			DiaPoint p1, p2, p3, pnew;
			GSList *handles_conn = NULL, *hc;
			guint segment;

			if (g_list_length (item->handles) < 3) {
				g_warning (_("Not enough line segments."));
				break;
			}
			segment = g_value_get_uint(value);

			/* Store original handle positions. */
			if (line->orthogonal) {
				GList *l;
				dia_canvas_item_preserve_property (item,
								"orthogonal");
				for (l = item->handles; l != NULL; l = l->next)
					dia_canvas_preserve_property (item->canvas,
							l->data, "pos_i");
			}

			h1 = g_list_nth_data (item->handles, segment);
			h2 = g_list_nth_data (item->handles, segment + 1);
			h3 = g_list_nth_data (item->handles, segment + 2);
			
			/* We always try to remove a handle in the middle. */
			if (!h3 && segment > 0) {
				h3 = h2;
				h2 = h1;
				h1 = g_list_nth_data (item->handles,
						      segment - 1);
			}

			g_assert (h3);

			if (line->orthogonal)
				remove_ortho_constraints (line);

			dia_handle_get_pos_i (h1, &p1.x, &p1.y);
			dia_handle_get_pos_i (h3, &p3.x, &p3.y);

			/* Also collect the connected handles from
			 * 'segment + 1'. */
			handles_conn = g_slist_concat (get_connected_handles (h2),
						       get_connected_handles (h3));
			set_connected_handles (h3, handles_conn);

			/* Remove the constraints from all handles in the
			 * 'handles_conn' list. We create new constraints
			 * between 'h1 and 'h3'. Be sure this behaves the
			 * same as the _connect() function.
			 * We can not do a simple "connect", cause then the
			 * handles are connected to the closest line segment.
			 * This might not be segment h1-h3... */
			for (hc = handles_conn; hc != NULL; hc = hc->next) {
				DiaHandle *handle = hc->data;
				dia_handle_get_pos_w (handle, &p2.x, &p2.y);
				dia_canvas_item_affine_point_w2i (item,
								  &p2.x, &p2.y);

				dia_handle_remove_all_constraints (handle);
				dia_distance_line_point (&p1, &p3, &p2,
							 line->line_width,
							 DIA_CAP_ROUND, &pnew);
				dia_canvas_item_affine_point_i2w (item,
								  &pnew.x,
								  &pnew.y);
				/* Make the handle move undoable. */
				dia_canvas_preserve_property_last (item->canvas,
							      G_OBJECT (handle),
							      "pos_i");
				dia_canvas_preserve_property_last (item->canvas,
							      G_OBJECT (handle),
							      "connect");
				dia_handle_set_pos_w (handle, pnew.x, pnew.y);
				dia_handle_add_line_constraint (h1, h3, handle);
			}

			g_object_set (G_OBJECT (h2), "owner", NULL, NULL);

			if (line->orthogonal)
				add_ortho_constraints (line);
		}
		dia_canvas_item_request_update (item);
		break;
	case PROP_DASH: {
			DiaDashStyle *dash;
			dash = (DiaDashStyle*) g_value_get_boxed(value);
			g_free (line->dash);
			line->dash = NULL;
			line->n_dash = 0;
			if (dash != NULL) {
				line->n_dash = dash->n_dash;
				if (line->n_dash == 0)
					break;
				line->dash = g_new (gdouble, line->n_dash);
				memcpy (line->dash, dash->dash,
					sizeof (gdouble) * line->n_dash);
			}
		}
		dia_canvas_item_request_update (item);
		break;
	case PROP_HEAD_POS: {
			DiaPoint *pos;
			DiaHandle *handle;
			dia_canvas_item_preserve_property (item, "head_pos");
			pos = g_value_get_boxed (value);
			handle = g_list_first (item->handles)->data;
			dia_handle_set_pos_i (handle, pos->x, pos->y);
		}
		break;
	case PROP_TAIL_POS: {
			DiaPoint *pos;
			DiaHandle *handle;
			dia_canvas_item_preserve_property (item, "tail_pos");
			pos = g_value_get_boxed (value);
			handle = g_list_last (item->handles)->data;
			dia_handle_set_pos_i (handle, pos->x, pos->y);
		}
		break;
	case PROP_HAS_HEAD:
		dia_canvas_item_preserve_property (item, "has_head");
		line->has_head = g_value_get_boolean(value);
		dia_canvas_item_request_update (item);
		break;
	case PROP_HAS_TAIL:
		dia_canvas_item_preserve_property (item, "has_tail");
		line->has_tail = g_value_get_boolean(value);
		dia_canvas_item_request_update (item);
		break;
	case PROP_HEAD_A:
		dia_canvas_item_preserve_property (item, "head_a");
		line->head_a = g_value_get_double(value);
		dia_canvas_item_request_update (item);
		break;
	case PROP_HEAD_B:
		dia_canvas_item_preserve_property (item, "head_b");
		line->head_b = g_value_get_double(value);
		dia_canvas_item_request_update (item);
		break;
	case PROP_HEAD_C:
		dia_canvas_item_preserve_property (item, "head_c");
		line->head_c = g_value_get_double(value);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (line));
		break;
	case PROP_HEAD_D:
		dia_canvas_item_preserve_property (item, "head_d");
		line->head_d = g_value_get_double(value);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (line));
		break;
	case PROP_TAIL_A:
		dia_canvas_item_preserve_property (item, "tail_a");
		line->tail_a = g_value_get_double(value);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (line));
		break;
	case PROP_TAIL_B:
		dia_canvas_item_preserve_property (item, "tail_b");
		line->tail_b = g_value_get_double(value);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (line));
		break;
	case PROP_TAIL_C:
		dia_canvas_item_preserve_property (item, "tail_c");
		line->tail_c = g_value_get_double(value);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (line));
		break;
	case PROP_TAIL_D:
		dia_canvas_item_preserve_property (item, "tail_d");
		line->tail_d = g_value_get_double(value);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (line));
		break;
	case PROP_HEAD_COLOR:
		dia_canvas_item_preserve_property (item, "head_color");
		line->head_color = g_value_get_ulong(value);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (line));
		break;
	case PROP_TAIL_COLOR:
		dia_canvas_item_preserve_property (item, "tail_color");
		line->tail_color = g_value_get_ulong(value);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (line));
		break;
	case PROP_HEAD_FILL_COLOR:
		dia_canvas_item_preserve_property (item, "head_fill_color");
		line->head_fill_color = g_value_get_ulong(value);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (line));
		break;
	case PROP_TAIL_FILL_COLOR:
		dia_canvas_item_preserve_property (item, "tail_fill_color");
		line->tail_fill_color = g_value_get_ulong(value);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (line));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
	}
}

static void
dia_canvas_line_get_property (GObject *object, guint property_id,
			      GValue *value, GParamSpec *pspec)
{
	switch (property_id) {
	case PROP_LINE_WIDTH:
		g_value_set_double(value, DIA_CANVAS_LINE (object)->line_width);
		break;
	case PROP_COLOR:
		g_value_set_ulong(value, DIA_CANVAS_LINE (object)->color);
		break;
	case PROP_CAP:
		g_value_set_enum(value, DIA_CANVAS_LINE (object)->cap);
		break;
	case PROP_JOIN:
		g_value_set_enum(value, DIA_CANVAS_LINE (object)->join);
		break;
	case PROP_CYCLIC:
		g_value_set_boolean(value, DIA_CANVAS_LINE (object)->cyclic);
		break;
	case PROP_ORTHOGONAL:
		g_value_set_boolean(value, DIA_CANVAS_LINE (object)->orthogonal);
		break;
	case PROP_HORIZONTAL:
		g_value_set_boolean(value, DIA_CANVAS_LINE (object)->horizontal);
		break;
	case PROP_HEAD_POS: {
			DiaPoint pos;
			DiaHandle *handle = g_list_first (DIA_CANVAS_ITEM (object)->handles)->data;
			dia_handle_get_pos_i (handle, &pos.x, &pos.y);
			g_value_set_boxed (value, &pos);
		}
		break;
	case PROP_TAIL_POS: {
			DiaPoint pos;
			DiaHandle *handle = g_list_last (DIA_CANVAS_ITEM (object)->handles)->data;
			dia_handle_get_pos_i (handle, &pos.x, &pos.y);
			g_value_set_boxed (value, &pos);
		}
		break;
	case PROP_HAS_HEAD:
		g_value_set_boolean(value, DIA_CANVAS_LINE (object)->has_head);
		break;
	case PROP_HAS_TAIL:
		g_value_set_boolean(value, DIA_CANVAS_LINE (object)->has_tail);
		break;
	case PROP_HEAD_A:
		g_value_set_double(value, DIA_CANVAS_LINE (object)->head_a);
		break;
	case PROP_HEAD_B:
		g_value_set_double(value, DIA_CANVAS_LINE (object)->head_b);
		break;
	case PROP_HEAD_C:
		g_value_set_double(value, DIA_CANVAS_LINE (object)->head_c);
		break;
	case PROP_HEAD_D:
		g_value_set_double(value, DIA_CANVAS_LINE (object)->head_d);
		break;
	case PROP_TAIL_A:
		g_value_set_double(value, DIA_CANVAS_LINE (object)->tail_a);
		break;
	case PROP_TAIL_B:
		g_value_set_double(value, DIA_CANVAS_LINE (object)->tail_b);
		break;
	case PROP_TAIL_C:
		g_value_set_double(value, DIA_CANVAS_LINE (object)->tail_c);
		break;
	case PROP_TAIL_D:
		g_value_set_double(value, DIA_CANVAS_LINE (object)->tail_d);
		break;
	case PROP_HEAD_COLOR:
		g_value_set_ulong(value, DIA_CANVAS_LINE (object)->head_color);
		break;
	case PROP_TAIL_COLOR:
		g_value_set_ulong(value, DIA_CANVAS_LINE (object)->tail_color);
		break;
	case PROP_HEAD_FILL_COLOR:
		g_value_set_ulong(value, DIA_CANVAS_LINE (object)->head_fill_color);
		break;
	case PROP_TAIL_FILL_COLOR:
		g_value_set_ulong(value, DIA_CANVAS_LINE (object)->tail_fill_color);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
	}
}

static void
dia_canvas_line_dispose (GObject *object)
{
	DiaCanvasLine *line = (DiaCanvasLine*) object;

	/*for (h = DIA_CANVAS_ITEM (object)->handles; h != NULL; h = h->next) {
		handles_conn = g_object_get_qdata (object, q_connected_handles);
		g_object_set_qdata (object, q_connected_handles, NULL);
		g_slist_free (handles_conn);
	} */

	if (line->line) {
		dia_shape_free (line->line);
		line->line = NULL;
		dia_shape_free (line->head_arrow);
		line->head_arrow = NULL;
		dia_shape_free (line->tail_arrow);
		line->tail_arrow = NULL;
	}
	remove_ortho_constraints (DIA_CANVAS_LINE (object));
	
	G_OBJECT_CLASS (parent_class)->dispose (object);
}

/**
 * create_arrow:
 * @shape_a: Distance from tip of arrowhead to the center point.
 * @shape_b: Distance from tip of arrowhead to trailing point,
 *	     measured along the shaft.
 * @shape_c: Distance of trailing point from the shaft (bottom half).
 * @shape_d: Distance of trailing point from the shaft (top half).
 * @point: Last bot one point on the line.
 * @end_point: INOUT Last point on the line. This point will be adjusted
 *		wrt the arrow.
 * @p: OUT Array of NUM_ARROW_POINTS points. will contain the arrow.
 *
 * Create a GnomeCanvasLine like arrow shape for the line. @end_point is
 * adjusted to attach to the beginpoint of the arrow.
 *		       p3|a|
 *		      p0/\  d
 * (point)=============<  > p2
 * 			\/  c
 * 		       | b|p1
 **/
static void
create_arrow (gdouble shape_a, gdouble shape_b,
	      gdouble shape_c, gdouble shape_d,
	      DiaPoint *point, DiaPoint *end_point,
	      DiaPoint p[NUM_ARROW_POINTS])
{
	double dx, dy, length;
	double sin_theta, cos_theta;

	p[2].x = end_point->x;
	p[2].y = end_point->y;

	dx = p[2].x - point->x;
	dy = p[2].y - point->y;
	length = sqrt (dx * dx + dy * dy);
	if (length < 1e-10) {
		sin_theta = cos_theta = 0.0;
	} else {
		sin_theta = dy / length;
		cos_theta = dx / length;
	}

	p[0].x = p[2].x - shape_a * cos_theta;
	p[0].y = p[2].y - shape_a * sin_theta;

	p[1].x = p[2].x - shape_b * cos_theta + shape_c * sin_theta;
	p[1].y = p[2].y - shape_b * sin_theta - shape_c * cos_theta;

	p[3].x = p[2].x - shape_b * cos_theta - shape_d * sin_theta;
	p[3].y = p[2].y - shape_b * sin_theta + shape_d * cos_theta;

	/* Move the first point towards the second so that the corners at
	 * the end of the line are inside the arrowhead. */
	*end_point = p[0];
}


static void
dia_canvas_line_update (DiaCanvasItem *item, gdouble affine[6])
{
	DiaCanvasLine *line = DIA_CANVAS_LINE (item);
	DiaPoint *points, *p;
	gint len, i;
	gdouble half_lw = line->line_width / 2.0;
	GList *l;

        if (DIA_CANVAS_ITEM_CLASS (parent_class)->update)
		DIA_CANVAS_ITEM_CLASS (parent_class)->update (item, affine);

	len = g_list_length (item->handles);

	/* NB. allocated on stack! no free()! */
	points = g_newa (DiaPoint, len);
	p = points;

	/* First the bounding box. This also fills the array of points
	 * needed for the Path shape. */
	if (item->handles) {
		dia_handle_get_pos_i (item->handles->data, &p->x, &p->y);
		item->bounds.left = item->bounds.right = p->x;
		item->bounds.top = item->bounds.bottom = p->y;
		p++;
		for (l = item->handles->next; l != NULL; l = l->next, p++) {
			dia_handle_get_pos_i (l->data, &p->x, &p->y);
			dia_rectangle_add_point (&item->bounds, p);
		}
	}

	/* Arrows */
	if (line->has_head) {
		DiaPoint arrow[NUM_ARROW_POINTS];
		create_arrow(line->head_a, line->head_b,
			     line->head_c, line->head_d,
			     &points[1], &points[0] /* endpoint */, arrow);
		/* Border */
		dia_shape_polygon (line->head_arrow, NUM_ARROW_POINTS, arrow);
		dia_shape_path_set_line_width (line->head_arrow, line->line_width);
		dia_shape_path_set_join (line->head_arrow, line->join);
		dia_shape_path_set_cap (line->head_arrow, line->cap);
		dia_shape_set_color (line->head_arrow, line->head_color);
		dia_shape_path_set_fill (line->head_arrow, DIA_FILL_SOLID);
		dia_shape_path_set_fill_color (line->head_arrow, line->head_fill_color);
		dia_shape_path_set_cyclic(line->head_arrow, TRUE);
		/* Extend the bounding box by the arrow head. */
		for (i = 0; i < NUM_ARROW_POINTS; i++)
			dia_rectangle_add_point (&item->bounds, &arrow[i]);
	} else {
		dia_shape_set_color (line->head_arrow, DIA_COLOR_A(0,0,0,0));
	}

	if (line->has_tail) {
		DiaPoint arrow[NUM_ARROW_POINTS];
		create_arrow(line->tail_a, line->tail_b,
			     line->tail_c, line->tail_d,
			     &points[len - 2], &points[len - 1], arrow);
		/* Border */
		dia_shape_polygon (line->tail_arrow, NUM_ARROW_POINTS, arrow);
		dia_shape_path_set_line_width (line->tail_arrow, line->line_width);
		dia_shape_path_set_join (line->tail_arrow, line->join);
		dia_shape_path_set_cap (line->tail_arrow, line->cap);
		dia_shape_set_color (line->tail_arrow, line->tail_color);
		dia_shape_path_set_fill (line->tail_arrow, DIA_FILL_SOLID);
		dia_shape_path_set_fill_color (line->tail_arrow, line->tail_fill_color);
		dia_shape_path_set_cyclic (line->tail_arrow, TRUE);
		/* Extend the bounding box by the arrow head. */
		for (i = 0; i < NUM_ARROW_POINTS; i++)
			dia_rectangle_add_point (&item->bounds, &arrow[i]);
	} else {
		dia_shape_set_color (line->tail_arrow, DIA_COLOR_A(0,0,0,0));
	}

	/* The line. */
	dia_shape_polyline (line->line, len, points);
	dia_shape_path_set_line_width (line->line, line->line_width);
	dia_shape_path_set_dash (line->line, 0.0, line->n_dash, line->dash);
	dia_shape_set_color (line->line, line->color);
	dia_shape_path_set_join (line->line, line->join);
	dia_shape_path_set_cap (line->line, line->cap);
	dia_shape_path_set_cyclic (line->line, line->cyclic);
	dia_shape_request_update (line->line);

	//g_free (points);

	/* Update bounds (+/- .5 * linewidth) */
	item->bounds.left   -= half_lw;
	item->bounds.top    -= half_lw;
	item->bounds.right  += half_lw;
	item->bounds.bottom += half_lw;
}

static gboolean
dia_canvas_line_get_shape_iter (DiaCanvasItem *item, DiaCanvasIter *iter)
{
	iter->data[0] = DIA_CANVAS_LINE (item)->line;
	return TRUE;
}

static gboolean
dia_canvas_line_shape_next (DiaCanvasItem *item, DiaCanvasIter *iter)
{
	DiaCanvasLine *line = (DiaCanvasLine*) item;
	DiaShape *shape = iter->data[0];

	if (shape == line->line) {
		if (line->has_head)
			iter->data[0] = line->head_arrow;
		else if (line->has_tail)
			iter->data[0] = line->tail_arrow;
		else
			iter->data[0] = NULL;
	} else if (shape == line->head_arrow && line->has_tail)
		iter->data[0] = line->tail_arrow;
	else 
		iter->data[0] = NULL;

	return (iter->data[0] != NULL ? TRUE : FALSE);
}

static DiaShape*
dia_canvas_line_shape_value (DiaCanvasItem *item, DiaCanvasIter *iter)
{
	return iter->data[0];
}

static gdouble
calc_closest_point (DiaCanvasItem *item, gdouble x, gdouble y,
		    gint *segment, DiaPoint *closest)
{
	DiaCanvasLine *line = (DiaCanvasLine*) item;
	gdouble d, dist = G_MAXDOUBLE;
	gint s;
	DiaPoint c;
	DiaPoint p1, p2;
	DiaPoint cursor;
	GList *l;

	if (! item->handles)
		return G_MAXDOUBLE;

	cursor.x = x;
	cursor.y = y;
	
	l = item->handles;
	dia_handle_get_pos_i ((DiaHandle*) l->data, &p1.x, &p1.y);
	l = l->next;
	for (s = 1; l != NULL; l = l->next, s++) {
		p2 = p1;
		dia_handle_get_pos_i ((DiaHandle*) l->data, &p1.x, &p1.y);
		d = dia_distance_line_point (&p1, &p2, &cursor,
					     line->line_width, DIA_CAP_ROUND,
					     &c);
		if (d < dist) {
			dist = d;
			if (closest)
				*closest = c;
			if (segment)
				*segment = s;
		}
	}

	return dist;
}

static gdouble
dia_canvas_line_point (DiaCanvasItem *item, gdouble x, gdouble y)
{
	return calc_closest_point (item, x, y, NULL, NULL);
}

static void
dia_canvas_line_handle_motion (DiaCanvasItem *item, DiaHandle *handle,
			       gdouble *wx, gdouble *wy, DiaEventMask mask)
{
	DiaCanvasLine *line = (DiaCanvasLine*) item;

	if (!item->canvas)
		return;

	/* For orthogonal lines save all handle positions, since the
	 * ortho-constraints might mess up the undo-result.
	 * We also save the connected_to item, so it can be applied to the
	 * right place. */
	if (line->orthogonal) {
		GList *l;
		for (l = item->handles; l != NULL; l = l->next)
			dia_canvas_preserve_property (item->canvas,
						      l->data, "pos_i");
	} else
		dia_canvas_preserve_property (item->canvas,
					      G_OBJECT (handle), "pos_i");
}


static gdouble
dia_canvas_line_glue (DiaCanvasItem *item, DiaHandle *handle,
		      gdouble *x, gdouble *y)
{
	DiaPoint p1, p2;
	gdouble ix, iy;

	ix = *x;
	iy = *y;

	dia_canvas_item_affine_point_w2i (item, &ix, &iy);

	calc_closest_point (item, ix, iy, NULL, &p1);

	dia_canvas_item_affine_point_i2w (item, &p1.x, &p1.y);

	p2.x = *x;
	p2.y = *y;
	*x = p1.x;
	*y = p1.y;

	return dia_distance_point_point (&p1, &p2);
}

static gboolean
dia_canvas_line_connect (DiaCanvasItem *item, DiaHandle *handle)
{
	DiaHandle *hbegin, *hend;
	gdouble x, y;
	gint segment;
	DiaPoint closest;
	GSList *handles_conn;

	if (!parent_class->connect (item, handle))
		return FALSE;

	/* If the handle was previously connected to the same object, find
	 * its 'q_connected_handles' item and remove it from it. */
	if (handle->connected_to == item) {
		GList *hl;
		for (hl = item->handles; hl != NULL; hl = hl->next) {
			handles_conn = get_connected_handles (hl->data);
			if (!g_slist_find (handles_conn, handle)) {
				handles_conn = g_slist_remove (handles_conn,
							       handle);
				g_object_unref(handle);
				set_connected_handles (hl->data, handles_conn);
				break;
			}
		}
	}
	dia_handle_get_pos_w (handle, &x, &y);
	dia_canvas_item_affine_point_w2i (item, &x, &y);

	calc_closest_point (item, x, y, &segment, &closest);

	dia_canvas_item_affine_point_i2w (item, &closest.x, &closest.y);
	dia_handle_set_pos_w (handle, closest.x, closest.y);

	hbegin = g_list_nth_data (item->handles, segment - 1);
	hend = g_list_nth_data (item->handles, segment);

	g_assert (hbegin != NULL);
	g_assert (hend != NULL);

	dia_handle_remove_all_constraints (handle);

	dia_handle_add_line_constraint (hbegin, hend, handle);

	handles_conn = get_connected_handles (hend);
	handles_conn = g_slist_prepend (handles_conn, g_object_ref (handle));
	set_connected_handles (hend, handles_conn);
	return TRUE;
}

static gboolean
dia_canvas_line_disconnect (DiaCanvasItem *item, DiaHandle *handle)
{
	gboolean result;

	result = parent_class->disconnect (item, handle);
	if (result) {
		GSList *handles_conn;
		GList *h;
		for (h = item->handles; h != NULL; h = h->next) {
			handles_conn = get_connected_handles (h->data);
			if (g_slist_find (handles_conn, handle)) {
				handles_conn = g_slist_remove (handles_conn,
							       handle);
				g_object_unref (handle);
				set_connected_handles (h->data, handles_conn);
				break;
			}
		}
	}
	return result;
}

static void
dia_canvas_line_parent_notify (DiaCanvasLine *line)
{
	if (DIA_CANVAS_ITEM (line)->canvas) {
		if (line->orthogonal)
			add_ortho_constraints (line);
	}
}

static void
dia_canvas_line_destroy_connected_handles (gpointer data)
{
	GSList *l;
	for (l = data; l != NULL; l = l->next)
		g_object_unref (l->data);
	g_slist_free(data);
}

/**
 * add_ortho_constraints:
 * @line: 
 *
 * Add constraints to ensure that a line will behave itself as an orthogonal
 * line (line segments can only be horizontal or vertical).
 *
 * We have to take special care of the line ends, since those can be connected
 * to another element. The order in which the equations are build is important.
 **/
static void
add_ortho_constraints (DiaCanvasLine *line)
{
	GList *l;
	gboolean horiz = line->horizontal;
	DiaConstraint *con = NULL;

	if (!DIA_CANVAS_ITEM (line)->canvas)
		return;
	for (l = line->item.handles; l && l->next; l = l->next) {
		/* Remove the old constraint first... */
		con = g_object_steal_qdata (G_OBJECT (l->data),
					    q_ortho_constraint);
		if (con) {
			dia_canvas_remove_constraint (DIA_CANVAS_ITEM (line)->canvas,
						      con);
			g_object_unref (con);
		}

		/* Next, add a new constraint... For the first segment, invert
		 * the order in which the constraint is build, this will
		 * ensure that the line will become orthogonal, even if the
		 * line end is connected. */
		con = dia_constraint_new ();
		if (horiz) {
			if (l == line->item.handles) {
				dia_constraint_add (con,
						    DIA_HANDLE (l->next->data)->pos_w.y,
						    1.0);
				dia_constraint_add (con,
						    DIA_HANDLE (l->data)->pos_w.y,
						    -1.0);
			} else {
				dia_constraint_add (con,
						    DIA_HANDLE (l->data)->pos_w.y,
						    -1.0);
				dia_constraint_add (con,
						    DIA_HANDLE (l->next->data)->pos_w.y,
						    1.0);
			}
		} else {
			if (l == line->item.handles) {
				dia_constraint_add (con,
						    DIA_HANDLE (l->next->data)->pos_w.x,
						    1.0);
				dia_constraint_add (con,
						    DIA_HANDLE (l->data)->pos_w.x,
						    -1.0);
			} else {
				dia_constraint_add (con,
						    DIA_HANDLE (l->data)->pos_w.x,
						    -1.0);
				dia_constraint_add (con,
						    DIA_HANDLE (l->next->data)->pos_w.x,
						    1.0);
			}
		}
		dia_canvas_add_constraint (DIA_CANVAS_ITEM (line)->canvas, con);
		/* the qdata takes over our reference to the constraint. */
		g_object_set_qdata_full (G_OBJECT (l->data), q_ortho_constraint,
					 con, g_object_unref);
		horiz = !horiz;
	}
}

static void
remove_ortho_constraints (DiaCanvasLine *line)
{
	GList *l;
	DiaConstraint *con;

	if (!DIA_CANVAS_ITEM (line)->canvas)
		return;

	for (l = line->item.handles; l && l->next; l = l->next) {
		con = g_object_steal_qdata (G_OBJECT (l->data),
					    q_ortho_constraint);
		if (con) {
			dia_canvas_remove_constraint (DIA_CANVAS_ITEM (line)->canvas,
						      con);
			g_object_unref (con);
		}
	}
}

/**
 * dia_canvas_line_get_closest_segment:
 * @line: 
 * @x: 
 * @y: 
 *
 * Get the segment that is closest to the point described by (@x, @y).
 * @x and @y should be in item coordinates.
 *
 * Return value: 
 **/
gint
dia_canvas_line_get_closest_segment (DiaCanvasLine *line, gdouble x, gdouble y)
{
	gint segment = -1;

	g_return_val_if_fail (DIA_IS_CANVAS_LINE (line), -1);

	calc_closest_point ((DiaCanvasItem*) line, x, y, &segment, NULL);

	return segment - 1;
}

