/* dia-handle.c
 * Copyright (C) 2001  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-handle.h"
#include <stdlib.h>
#include <math.h>
#include <libart_lgpl/art_affine.h>
#include "diamarshal.h"
#include "diatypebuiltins.h"
#include "dia-canvas-i18n.h"

#define SPEED_HACK

enum {
	PROP_OWNER = 1,
	PROP_INDEX,
	PROP_POS_I,
	PROP_POS_W,
	PROP_STRENGTH,
	PROP_CONNECT,
	PROP_DISCONNECT,
	PROP_CONNECTED_TO,
	PROP_CONNECTABLE,
	PROP_MOVABLE
};

static gint _dia_handle_size = 9;

static void dia_handle_init		(DiaHandle	 *handle);
static void dia_handle_class_init	(DiaHandleClass	 *klass);
static void dia_handle_set_property	(GObject	*object,
					 guint		 property_id,
					 const GValue	*value,
					 GParamSpec	*pspec);
static void dia_handle_get_property	(GObject	*object,
					 guint		 property_id,
					 GValue		*value,
					 GParamSpec	*pspec);

static void dia_handle_dispose		(GObject *object);
static void dia_handle_finalize		(GObject *object);
static void pos_w_changed		(DiaVariable *variable,
					 DiaHandle *handle);

static GObjectClass *parent_class = NULL;

#define INVARIANT() \
	g_return_if_fail (DIA_IS_HANDLE (handle)); \
	g_return_if_fail (DIA_IS_CANVAS_ITEM (handle->owner))

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

	if (!object_type) {
		static const GTypeInfo object_info = {
			sizeof (DiaHandleClass),
			(GBaseInitFunc) NULL,
			(GBaseFinalizeFunc) NULL,
			(GClassInitFunc) dia_handle_class_init,
			(GClassFinalizeFunc) NULL,
			(gconstpointer) NULL, /* class_data */
			sizeof (DiaHandle),
			(guint16) 0, /* n_preallocs */
			(GInstanceInitFunc) dia_handle_init,
		};

		object_type = g_type_register_static (G_TYPE_OBJECT,
						      "DiaHandle",
						      &object_info, 0);
	}

	return object_type;
}

static void
dia_handle_class_init (DiaHandleClass *klass)
{
	GObjectClass *object_class;
  
	object_class = (GObjectClass*) klass;

	parent_class = g_type_class_peek_parent (klass);

	object_class->dispose = dia_handle_dispose;
	object_class->finalize = dia_handle_finalize;
	object_class->set_property = dia_handle_set_property;
	object_class->get_property = dia_handle_get_property;
	
	g_object_class_install_property (object_class,
					 PROP_OWNER,
					 g_param_spec_object ("owner",
						_("Owner"),
						_("Item owning the handle."),
						DIA_TYPE_CANVAS_ITEM,
						G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_INDEX,
					 g_param_spec_int ("index",
						_("Index"),
						_("The position of the handle in its item."),
						0, G_MAXINT, 0,
						G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_POS_I,
					 g_param_spec_boxed ("pos_i",
						_("Position, item relative"),
						_("Item relative position."),
						DIA_TYPE_POINT,
						G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_POS_W,
					 g_param_spec_boxed ("pos_w",
						_("Position, world relative"),
						_("Items world position."),
						DIA_TYPE_POINT,
						G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_STRENGTH,
					 g_param_spec_enum ("strength",
						_("Strength"),
						_("Strength of the handle."),
						DIA_TYPE_STRENGTH,
						DIA_STRENGTH_WEAK,
						G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_CONNECT,
					 g_param_spec_object ("connect",
						_("Connect"),
						_(""),
						DIA_TYPE_CANVAS_ITEM,
						G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_DISCONNECT,
					 g_param_spec_object ("disconnect",
						_(""),
						_(""),
						DIA_TYPE_CANVAS_ITEM,
						G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_CONNECTED_TO,
					 g_param_spec_object ("connected_to",
						_("Item connected to the handle"),
						_("Item connected to the handle"),
						DIA_TYPE_CANVAS_ITEM,
						G_PARAM_READABLE));
	g_object_class_install_property (object_class,
					 PROP_CONNECTABLE,
					 g_param_spec_boolean ("connectable",
						_("Connectable"),
						_("Handle can be connected to other items."),
						FALSE,
						G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_MOVABLE,
					 g_param_spec_boolean ("movable",
						_("Movable"),
						_("Handle can be moved independend from the owner."),
						TRUE,
						G_PARAM_READWRITE));
	
}


static void
dia_handle_init (DiaHandle *handle)
{
	handle->need_update_w2i = FALSE;
	handle->movable = FALSE;
	handle->connectable = FALSE;

	handle->owner = NULL;

	handle->pos_i.x = dia_variable_new ();
	handle->pos_i.y = dia_variable_new ();

	handle->pos_w.x = dia_variable_new ();
	g_signal_connect (G_OBJECT (handle->pos_w.x), "changed",
			  G_CALLBACK (pos_w_changed), handle);

	handle->pos_w.y = dia_variable_new ();
	g_signal_connect (G_OBJECT (handle->pos_w.y), "changed",
			  G_CALLBACK (pos_w_changed), handle);


	handle->connected_to = NULL;

	handle->constraints = NULL;
}

static void
dia_handle_set_property (GObject *object, guint property_id,
			 const GValue *value, GParamSpec *pspec)
{
	DiaHandle *handle = DIA_HANDLE (object);
	DiaCanvasItem *owner;
	DiaPoint *pos;
	DiaCanvasItem *item;

	switch (property_id) {
	case PROP_OWNER:
		/* The main purpose of this property is for the undo scheme */

		owner = (DiaCanvasItem*) g_value_get_object (value);

		/* Owner can be set once and unset again:
		 *  if handle->owner ==> owner == NULL
		 *  if handle->owner == NULL ==> owner != NULL or owner == NULL
		 */
		if (handle->owner && owner)
			g_error (_("DiaHandle::owner can only be set once!"));

		if (handle->owner && handle->owner->canvas) {
			dia_canvas_preserve_property (handle->owner->canvas,
						      G_OBJECT (handle),
						      "pos_i");
			dia_canvas_preserve_property (handle->owner->canvas,
						      G_OBJECT (handle),
						      "index");
			dia_canvas_preserve_property (handle->owner->canvas,
						      G_OBJECT (handle),
						      "owner");
			dia_canvas_item_request_update (handle->owner);
		} else if (owner && owner->canvas) {
			/* Preserving property owner will allow us to
			 * remove the handle when UNDO is pressed. */
			dia_canvas_preserve_property (owner->canvas,
						      G_OBJECT (handle),
						      "owner");
			dia_canvas_item_request_update (owner);
		}

		/* This is for undo/redo actions: */
		/* Do one of two things: remove the handle from the canvas item
		 * or add it to the canvas item. */
		if (handle->owner != NULL) {
			handle->owner->handles = g_list_remove (handle->owner->handles, handle);
			g_object_unref (handle);
			handle->owner = NULL;
		} else if (owner) {
			handle->owner = owner;
			owner->handles = g_list_append (owner->handles,
							handle);
			g_object_ref (handle);
		}
		break;
	case PROP_INDEX:
		if (handle->owner) {
			gint index = g_value_get_int (value);
			g_return_if_fail (index > 0 && index < g_list_length (handle->owner->handles));

			if (handle->owner->canvas)
				dia_canvas_preserve_property (handle->owner->canvas,
							      G_OBJECT (handle),
							      "index");

			handle->owner->handles = g_list_remove (handle->owner->handles, handle);
			handle->owner->handles = g_list_insert (handle->owner->handles,
								handle, index);
			
		}
		break;
	case PROP_POS_I:
		if (handle->owner && handle->owner->canvas)
			dia_canvas_preserve_property (handle->owner->canvas,
						      G_OBJECT (handle),
						      "pos_i");
		pos = g_value_get_boxed (value);
		g_object_freeze_notify (object);
		dia_handle_set_pos_i (handle, pos->x, pos->y);
		g_object_thaw_notify (object);
		break;
	case PROP_POS_W:
		if (handle->owner && handle->owner->canvas)
			dia_canvas_preserve_property (handle->owner->canvas,
						      G_OBJECT (handle),
						      "pos_w");
		pos = g_value_get_boxed (value);
		g_object_freeze_notify (object);
		dia_handle_set_pos_w (handle, pos->x, pos->y);
		g_object_thaw_notify (object);
		break;
	case PROP_STRENGTH:
		if (handle->owner && handle->owner->canvas)
			dia_canvas_preserve_property (handle->owner->canvas,
						      G_OBJECT (handle),
						      "strength");
		g_object_freeze_notify (object);
		dia_handle_set_strength (handle, g_value_get_enum (value));
		g_object_thaw_notify (object);
		break;
	case PROP_CONNECT:
		if (g_value_get_object (value))
			dia_canvas_item_connect (DIA_CANVAS_ITEM (g_value_get_object (value)), handle);
		break;
	case PROP_DISCONNECT:
		if (g_value_get_object (value))
			dia_canvas_item_disconnect (DIA_CANVAS_ITEM (g_value_get_object (value)), handle);
		break;
	case PROP_CONNECTABLE:
		if (handle->owner && handle->owner->canvas)
			dia_canvas_preserve_property (handle->owner->canvas,
						      G_OBJECT (handle),
						      "connectable");
		handle->connectable = g_value_get_boolean (value);
		if (handle->connectable)
			handle->movable = TRUE;
		break;
	case PROP_MOVABLE:
		if (handle->owner && handle->owner->canvas)
			dia_canvas_preserve_property (handle->owner->canvas,
						      G_OBJECT (handle),
						      "movable");
		handle->movable = g_value_get_boolean (value);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
		break;
	}
}

static void
dia_handle_get_property (GObject *object, guint property_id,
			 GValue *value, GParamSpec *pspec)
{
	DiaHandle *handle = DIA_HANDLE (object);

	switch (property_id) {
	case PROP_OWNER:
		g_value_set_object (value, handle->owner);
		break;
	case PROP_INDEX:
		g_value_set_int (value, g_list_index (handle->owner->handles, handle));
		break;
	case PROP_POS_I: {
			DiaPoint p;
			dia_handle_get_pos_i (handle, &p.x, &p.y);
			g_value_set_boxed (value, &p);
		}
		break;
	case PROP_POS_W: {
			DiaPoint p;
			dia_handle_get_pos_w (handle, &p.x, &p.y);
			g_value_set_boxed (value, &p);
		}
		break;
	case PROP_STRENGTH:
		g_value_set_enum (value,
				  dia_variable_get_strength (handle->pos_w.x));
		break;
	case PROP_CONNECT:
	case PROP_DISCONNECT:
	case PROP_CONNECTED_TO:
		g_value_set_object (value, handle->connected_to);
		break;
	case PROP_CONNECTABLE:
		g_value_set_boolean (value, handle->connectable);
		break;
	case PROP_MOVABLE:
		g_value_set_boolean (value, handle->movable);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
	}
}

static void
dia_handle_dispose (GObject *object)
{
	DiaHandle *handle = (DiaHandle*) object;

	//g_message (__FUNCTION__": %x DISPOSING!!!", (int) object);
	dia_handle_remove_all_constraints (handle);
}

static void
dia_handle_finalize (GObject *object)
{
	DiaHandle *handle = (DiaHandle*) object;

	g_object_unref (handle->pos_i.x);
	g_object_unref (handle->pos_i.y);

	g_object_unref (handle->pos_w.x);
	g_object_unref (handle->pos_w.y);

	parent_class->finalize (object);
}


static void
pos_w_changed (DiaVariable *variable, DiaHandle *handle)
{
	dia_handle_request_update_w2i (handle);
	dia_canvas_item_request_update (handle->owner);
}


/**
 * dia_handle_new:
 * @owner: #DiaCanvasItem owning the handle.
 *
 * Create a new handle on position (0.0, 0.0). 
 * The handle is owner by @owner.
 *
 * Return value: A borrowed reference to the newly created handle.
 **/
DiaHandle*
dia_handle_new (DiaCanvasItem *owner)
{
	DiaHandle *handle;
       
	g_return_val_if_fail (DIA_IS_CANVAS_ITEM (owner), NULL);
	
	//handle = g_object_new (DIA_TYPE_HANDLE, NULL);
	handle = g_object_new (DIA_TYPE_HANDLE, "owner", owner, NULL);

	//handle->owner = owner;
	//owner->handles = g_list_append (owner->handles, handle);

	// Unref, since g_object_new increates the refcount of our handle,
	// but owner already has the initial reference.
	g_object_unref (handle);
	return handle;
}


/**
 * dia_handle_set_strength:
 * @handle: 
 * @strength: 
 *
 * Set the strength of the handle. A strong handle is less likely to move
 * than a weak handle.
 **/
void
dia_handle_set_strength (DiaHandle *handle, DiaStrength strength)
{
	INVARIANT();

	g_return_if_fail (DIA_IS_HANDLE (handle));

	dia_variable_set_strength (handle->pos_w.x, strength);
	dia_variable_set_strength (handle->pos_w.y, strength);
}

/**
 * dia_handle_new_with_pos:
 * @owner: 
 * @x: 
 * @y: 
 *
 * Create a new handle on position (@x, @y).
 *
 * Return value: 
 **/
DiaHandle*
dia_handle_new_with_pos (DiaCanvasItem *owner, gdouble x, gdouble y)
{
	DiaHandle *handle = dia_handle_new (owner);

	dia_handle_set_pos_i (handle, x, y);

	return handle;
}


/**
 * dia_handle_get_pos_i:
 * @handle: 
 * @x: 
 * @y: 
 *
 * Get the item relative position of the handle. If the handle needs a
 * world->item update, that is done first.
 **/
void
dia_handle_get_pos_i (DiaHandle *handle, gdouble *x, gdouble *y)
{
	INVARIANT();

	if (handle->need_update_w2i)
		dia_handle_update_w2i (handle);

	*x = dia_variable_get_value (handle->pos_i.x);
	*y = dia_variable_get_value (handle->pos_i.y);
}

/**
 * dia_handle_get_pos_w:
 * @handle: 
 * @x: 
 * @y: 
 *
 * Retrieve the world coordinates of a handle.
 **/
void
dia_handle_get_pos_w (DiaHandle *handle, gdouble *x, gdouble *y)
{
	INVARIANT();

	*x = dia_variable_get_value (handle->pos_w.x);
	*y = dia_variable_get_value (handle->pos_w.y);
}

/**
 * dia_handle_set_pos_i:
 * @handle: 
 * @x: 
 * @y: 
 *
 * Set a new position in item relative coordinates and request for an update.
 **/
void
dia_handle_set_pos_i (DiaHandle *handle, gdouble x, gdouble y)
{
	gdouble affine[6];

	INVARIANT();

	dia_canvas_item_affine_i2w (handle->owner, affine);

	dia_handle_set_pos_i_affine (handle, x, y, affine);
}

/**
 * dia_handle_set_pos_i_affine:
 * @handle: 
 * @x: new x position, in item coordinates.
 * @y: new y position, in item coordinates.
 * @affine: Item to world transforation matrix.
 *
 * Set the position of the handle, in item relative coordinates.
 **/
void
dia_handle_set_pos_i_affine (DiaHandle *handle, gdouble x, gdouble y,
			     const gdouble affine[6])
{
	INVARIANT();

	dia_variable_set_value (handle->pos_i.x, x);
	dia_variable_set_value (handle->pos_i.y, y);

	g_object_notify (G_OBJECT (handle), "pos_i");
	dia_handle_update_i2w_affine (handle, affine);
}

/**
 * dia_handle_update_i2w_affine:
 * @handle: 
 * @affine: item to world transformation matrix.
 *
 * Sync the item and world coordinates by changing the world coordinates.
 **/
void
dia_handle_update_i2w_affine (DiaHandle *handle, const gdouble affine[6])
{
	register gdouble x, y;

	INVARIANT ();
	g_return_if_fail (affine != NULL);

	x = dia_variable_get_value (handle->pos_i.x);
	y = dia_variable_get_value (handle->pos_i.y);

	if ((affine[0] == 1.0) && (affine[1] == 0.0)
	    && (affine[2] == 0.0) && (affine[3] == 1.0)) {
		dia_variable_set_value (handle->pos_w.x, x + affine[4]);
		dia_variable_set_value (handle->pos_w.y, y + affine[5]);
	} else {
		dia_variable_set_value (handle->pos_w.x,
					x * affine[0] + y * affine[2] + affine[4]);
		dia_variable_set_value (handle->pos_w.y,
					x * affine[1] + y * affine[3] + affine[5]);
	}
	g_object_notify (G_OBJECT (handle), "pos_w");
	dia_canvas_item_request_update (handle->owner);
}


/**
 * dia_handle_set_pos_w:
 * @handle: 
 * @x: 
 * @y: 
 *
 * Assign @handle a new position in world coordinates. All items that requested
 * a item to world update will be updated also.
 **/
void
dia_handle_set_pos_w (DiaHandle *handle, gdouble x, gdouble y)
{
	INVARIANT();

	dia_variable_set_value (handle->pos_w.x, x);
	dia_variable_set_value (handle->pos_w.y, y);

	g_object_notify (G_OBJECT (handle), "pos_w");
	dia_handle_request_update_w2i (handle);
}


/**
 * dia_handle_distance_i:
 * @handle: 
 * @x: 
 * @y: 
 *
 * Calculate the distance from (@x, @y) to the handle. Note that the handle
 * is internally represented as a point rather than a rectangle (it is
 * visualized as a rectangle).
 *
 * Return value: The calculated distance.
 **/
gdouble
dia_handle_distance_i (DiaHandle *handle, gdouble x, gdouble y)
{
	DiaPoint p1, p2;

	g_return_val_if_fail (DIA_IS_HANDLE (handle), G_MAXDOUBLE);
	g_return_val_if_fail (DIA_IS_CANVAS_ITEM (handle->owner), G_MAXDOUBLE);

	p1.x = x;
	p1.y = y;
	dia_handle_get_pos_i (handle, &p2.x, &p2.y);

	return dia_distance_point_point_manhattan (&p1, &p2);
}


/**
 * dia_handle_distance_w:
 * @handle: 
 * @x: x position relative to the root canvas item.
 * @y: y position relative to the root canvas item.
 *
 * Calculate the distance from one point to the handle, using manhattan
 * calculation.
 *
 * Return value: The distance.
 **/
gdouble
dia_handle_distance_w (DiaHandle *handle, gdouble x, gdouble y)
{
	DiaPoint p1, p2;

	g_return_val_if_fail (DIA_IS_HANDLE (handle), G_MAXDOUBLE);
	g_return_val_if_fail (DIA_IS_CANVAS_ITEM (handle->owner), G_MAXDOUBLE);

	p1.x = x;
	p1.y = y;
	dia_handle_get_pos_w (handle, &p2.x, &p2.y);

	return dia_distance_point_point_manhattan (&p1, &p2);
}


/**
 * dia_handle_request_update_w2i:
 * @handle: 
 *
 * Request a world to item synchronization.
 **/
void
dia_handle_request_update_w2i (DiaHandle *handle)
{
	INVARIANT ();

	handle->need_update_w2i = TRUE;
	dia_canvas_item_request_update (handle->owner);
}


/**
 * dia_handle_update_w2i:
 * @handle: 
 *
 * Update the item relative coordinate of @handle.
 **/
void
dia_handle_update_w2i (DiaHandle *handle)
{
	gdouble a[6];

	INVARIANT();

	dia_canvas_item_affine_w2i (handle->owner, a);
	dia_handle_update_w2i_affine (handle, a);
}

/**
 * dia_handle_update_w2i_affine:
 * @handle: 
 * @affine: 
 *
 * As dia_handle_update_w2i(), but also a transformation matrix is provided.
 **/
void
dia_handle_update_w2i_affine (DiaHandle *handle, const gdouble affine[6])
{
	gdouble x, y;

	INVARIANT();

	x = dia_variable_get_value (handle->pos_w.x);
	y = dia_variable_get_value (handle->pos_w.y);

	if ((affine[0] == 1.0) && (affine[1] == 0.0)
	    && (affine[2] == 0.0) && (affine[3] == 1.0)) {
		dia_variable_set_value (handle->pos_i.x, x + affine[4]);
		dia_variable_set_value (handle->pos_i.y, y + affine[5]);
	} else {
		dia_variable_set_value (handle->pos_i.x,
				x * affine[0] + y * affine[2] + affine[4]);
		dia_variable_set_value (handle->pos_i.y,
				x * affine[1] + y * affine[3] + affine[5]);
	}

	handle->need_update_w2i = FALSE;
}


/**
 * dia_handle_size:
 *
 * Return the size of a handle (currently 9).
 *
 * Return value: 
 **/
gint
dia_handle_size (void)
{
	return _dia_handle_size;
}


/**
 * dia_handle_add_constraint:
 * @handle: 
 * @c: 
 *
 * Add constraint @c to the handle. The constraint should be used to connect
 * the handle to another object than its owner.
 * 
 * The constraint will automatically be removed if the handle is disconnected.
 **/
void
dia_handle_add_constraint (DiaHandle *handle, DiaConstraint *c)
{
	INVARIANT();
	if (!handle->owner->canvas)
		return;

	/* TODO: Preserve property constraints */
	g_object_ref (c);
	handle->constraints = g_slist_prepend (handle->constraints, c);
	dia_canvas_add_constraint (handle->owner->canvas, c);
}


/**
 * dia_handle_add_point_constraint:
 * @handle: 
 * @host: the handle that wants to connect to us
 *
 **/
void
dia_handle_add_point_constraint (DiaHandle *handle, DiaHandle *host)
{
	DiaConstraint *conx, *cony;

	g_return_if_fail (DIA_IS_HANDLE (handle));
	g_return_if_fail (DIA_IS_HANDLE (host));

	conx = dia_constraint_new ();
	cony = dia_constraint_new ();

	dia_constraint_add (conx, handle->pos_w.x, 1);
	dia_constraint_add (conx, host->pos_w.x, -1.0);
	dia_constraint_add (cony, host->pos_w.y, 1);
	dia_constraint_add (cony, handle->pos_w.y, -1.0); 

	dia_handle_add_constraint (host, conx);
	dia_handle_add_constraint (host, cony);

	g_object_unref (conx);
	g_object_unref (cony);
}

#define C_EPSILON  (0.0001)
/**
 * dia_handle_add_line_constraint:
 * @begin: 
 * @end: 
 * @middle: 
 *
 * Create constraints for @middle. @middle should be between @begin and @end.
 * The constraints will let @middle stay on the line. If @begin or @end moves,
 * @middle is kept there.
 **/
void
dia_handle_add_line_constraint (DiaHandle *begin,
				DiaHandle *end,
				DiaHandle *middle)
{
	DiaConstraint *conx, *cony;
	gdouble bx, ex, mx;
	gdouble by, ey, my;

	g_return_if_fail (DIA_IS_HANDLE (begin));
	g_return_if_fail (DIA_IS_HANDLE (end));
	g_return_if_fail (DIA_IS_HANDLE (middle));

	dia_handle_get_pos_w (begin, &bx, &by);
	dia_handle_get_pos_w (end, &ex, &ey);
	dia_handle_get_pos_w (middle, &mx, &my);

	conx = dia_constraint_new ();
	cony = dia_constraint_new ();

	if ((fabs (bx - mx) < C_EPSILON) && (fabs (by - my) < C_EPSILON)) {
		/* Handle connects on begin point: */
		dia_constraint_add (conx, begin->pos_w.x, 1.0);
		dia_constraint_add (conx, middle->pos_w.x, -1.0);
		dia_constraint_add (cony, begin->pos_w.y, 1.0);
		dia_constraint_add (cony, middle->pos_w.y, -1.0);
	} else if ((fabs (ex - mx) < C_EPSILON)
		   && (fabs (ey - my) < C_EPSILON)) {
		/* Connect to the end-handle position */
		dia_constraint_add (conx, end->pos_w.x, 1.0);
		dia_constraint_add (conx, middle->pos_w.x, -1.0);
		dia_constraint_add (cony, end->pos_w.y, 1.0);
		dia_constraint_add (cony, middle->pos_w.y, -1.0);
	} else {
		/* c = (middle - begin) / (end - middle)
		 * (middle - begin) = (end - middle) * c
		 * ==> middle - begin - end * c + middle * c = 0
		 * ==> middle * (1 + c) - begin - end * c = 0
		 *
		 * NOTE: By adding the @middle variable first, this variable
		 * will be changed as the first variable when constraints
		 * are solved.
		 */
		register gdouble c = 0.0;
		if ((fabs (bx - ex) < C_EPSILON)
		    && (fabs (ey - my) > C_EPSILON)) {
			c = (my - by) / (ey - my);
		} else if (fabs (ex - mx) > C_EPSILON) {
			c = (mx - bx) / (ex - mx);
		}
		dia_constraint_add (conx, middle->pos_w.x, c + 1);
		dia_constraint_add (conx, begin->pos_w.x, -1.0);
		dia_constraint_add (conx, end->pos_w.x, -c);
		dia_constraint_add (cony, middle->pos_w.y, c + 1);
		dia_constraint_add (cony, begin->pos_w.y, -1.0);
		dia_constraint_add (cony, end->pos_w.y, -c);
	}
	dia_handle_add_constraint (middle, conx);
	g_object_unref (conx);
	dia_handle_add_constraint (middle, cony);
	g_object_unref (cony);
}

/**
 * dia_handle_remove_constraint:
 * @handle: 
 * @c: 
 *
 * remove @c from the handle. If @c does not exist in @handle's context,
 * a warning is issued.
 **/
void
dia_handle_remove_constraint (DiaHandle *handle, DiaConstraint *c)
{
	INVARIANT();

	g_return_if_fail (g_slist_find (handle->constraints, c));
	g_assert (handle->owner->canvas != NULL);

	/* TODO: Preserve property constraints */
	dia_canvas_remove_constraint (handle->owner->canvas, c);
	handle->constraints = g_slist_remove (handle->constraints, c);
	g_object_unref (c);
}

/**
 * dia_handle_remove_all_constraints:
 * @handle: 
 *
 * Remove all constraints from @handle. This is typically done if a
 * #DiaCanvasItem is disconnected from another DiaCanvasItem.
 **/
void
dia_handle_remove_all_constraints (DiaHandle *handle)
{
	INVARIANT();

	while (handle->constraints) {
		dia_handle_remove_constraint (handle,
					      handle->constraints->data);
	}
}

