/* 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 #include #include #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, PROP_VISIBLE }; 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 independent from the owner."), TRUE, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_VISIBLE, g_param_spec_boolean ("visible", _("Visible"), _("Handle is visible by user."), TRUE, G_PARAM_READWRITE)); } static void dia_handle_init (DiaHandle *handle) { handle->need_update_w2i = FALSE; handle->movable = FALSE; handle->connectable = FALSE; handle->visible = TRUE; 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; case PROP_VISIBLE: if (handle->owner && handle->owner->canvas) dia_canvas_preserve_property (handle->owner->canvas, G_OBJECT (handle), "visible"); handle->visible = 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; case PROP_VISIBLE: g_value_set_boolean (value, handle->visible); 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 (G_STRLOC": %x DISPOSING!!!", (int) object); dia_handle_remove_all_constraints (handle); if (handle->connected_to) { g_object_unref (handle->connected_to); handle->connected_to = NULL; } } 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; 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); 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); } } /* * UNDO/REDO */ static GSList* dia_g_slist_deep_copy (GSList *l) { GSList *t; GSList *newl; if (!l) return NULL; newl = g_slist_copy (l); for (t = newl; t != NULL; t = g_slist_next (t)) g_object_ref (t->data); return newl; } static void dia_g_slist_deep_free (GSList *l) { GSList *t; for (t = l; t != NULL; t = g_slist_next (t)) g_object_unref (t->data); g_slist_free (l); } typedef struct { DiaUndoAction entry; DiaHandle *handle; /* undo data */ DiaPoint pos_i; DiaCanvasItem *connected_to; GSList *constraints; /* redo data */ DiaPoint redo_pos_i; DiaCanvasItem *redo_connected_to; GSList *redo_constraints; } DiaUndoConnect; static void dia_undo_connect_undo (DiaUndoAction *entry) { DiaUndoConnect *c = (DiaUndoConnect*) entry; DiaHandle *handle = c->handle; GSList *l; //g_message (G_STRLOC); c->redo_pos_i.x = dia_variable_get_value (handle->pos_i.x); c->redo_pos_i.y = dia_variable_get_value (handle->pos_i.y); dia_handle_set_pos_i (handle, c->pos_i.x, c->pos_i.y); if (handle->connected_to) { if (!c->redo_connected_to && handle->connected_to) c->redo_connected_to = g_object_ref (handle->connected_to); //c->redo_connected_to = handle->connected_to; //g_object_ref (handle->connected_to); handle->connected_to->connected_handles = g_list_remove (handle->connected_to->connected_handles, handle); //g_object_unref (handle->connected_to); } //handle->connected_to = c->connected_to; //g_object_ref (c->connected_to); handle->connected_to = g_object_ref (c->connected_to); //g_message(G_STRLOC": handle conn. to %p", handle->connected_to); if (handle->connected_to) { handle->connected_to->connected_handles = g_list_append (handle->connected_to->connected_handles, handle); } //g_message (G_STRLOC": handle set"); if (handle->constraints) { if (!c->redo_constraints) c->redo_constraints = dia_g_slist_deep_copy (handle->constraints); dia_handle_remove_all_constraints (handle); } handle->constraints = dia_g_slist_deep_copy (c->constraints); if (handle->owner && handle->owner->canvas) for (l = handle->constraints; l != NULL; l = g_slist_next (l)) { //g_message (G_STRLOC": adding constraint %p", l->data); dia_canvas_add_constraint (handle->owner->canvas, l->data); } } static void dia_undo_connect_redo (DiaUndoAction *entry) { DiaUndoConnect *c = (DiaUndoConnect*) entry; DiaHandle *handle = c->handle; GSList *l; dia_handle_set_pos_i (handle, c->redo_pos_i.x, c->redo_pos_i.y); if (handle->connected_to) { handle->connected_to->connected_handles = g_list_remove (handle->connected_to->connected_handles, handle); //g_object_unref (handle->connected_to); } handle->connected_to = g_object_ref (c->redo_connected_to); //handle->connected_to = c->redo_connected_to; //g_object_ref (c->redo_connected_to); if (handle->connected_to) { handle->connected_to->connected_handles = g_list_append (handle->connected_to->connected_handles, handle); //g_object_ref (handle); } if (handle->constraints) dia_handle_remove_all_constraints (handle); handle->constraints = dia_g_slist_deep_copy (c->redo_constraints); if (handle->owner && handle->owner->canvas) for (l = handle->constraints; l != NULL; l = g_slist_next (l)) dia_canvas_add_constraint (handle->owner->canvas, l->data); } static void dia_undo_connect_destroy (gpointer entry) { DiaUndoConnect *c = (DiaUndoConnect*) entry; g_object_unref (c->handle); if (c->connected_to) { g_object_unref (c->connected_to); c->connected_to = NULL; } if (c->constraints) { dia_g_slist_deep_free (c->constraints); c->constraints = NULL; } if (c->redo_connected_to) { g_object_unref (c->redo_connected_to); c->redo_connected_to = NULL; } if (c->redo_constraints) { dia_g_slist_deep_free (c->redo_constraints); c->redo_constraints = NULL; } } static DiaUndoAction* dia_undo_connect_new (DiaHandle *handle) { GSList *l; DiaUndoConnect *c = (DiaUndoConnect*) dia_undo_action_new (sizeof (DiaUndoConnect), dia_undo_connect_undo, dia_undo_connect_redo, dia_undo_connect_destroy); //g_message(G_STRLOC": saving handle %p", handle); c->handle = g_object_ref (handle); c->pos_i.x = dia_variable_get_value (handle->pos_i.x); c->pos_i.y = dia_variable_get_value (handle->pos_i.y); if (handle->connected_to) c->connected_to = g_object_ref (handle->connected_to); //c->connected_to = handle->connected_to; //g_object_ref (handle->connected_to); c->constraints = dia_g_slist_deep_copy (handle->constraints); //g_message(G_STRLOC": new DiaUndoConnect: %p, (%f, %f), %p, %p", // handle, c->pos_i.x, c->pos_i.y, c->connected_to, c->constraints); return (DiaUndoAction*) c; } /** * dia_handle_preserve_state: * @handle: * * Save the state of the handle to the DiaUndoManager connected to the * canvas. The state consists of the position and a possible item the handle * is connected to. **/ void dia_handle_preserve_state (DiaHandle *handle) { DiaUndoManager *undo_manager; g_return_if_fail (DIA_IS_HANDLE (handle)); if (!handle->owner || !handle->owner->canvas) return; undo_manager = dia_canvas_get_undo_manager (handle->owner->canvas); dia_undo_manager_add_undo_action (undo_manager, dia_undo_connect_new (handle)); dia_canvas_item_preserve_property(handle->owner, "parent"); }