/* 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 #include #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_READABLE)); //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; dia_canvas_item_ungrab (item); //g_message (G_STRLOC": disposing %p", item); if (item->parent) dia_canvas_item_set_child_of (item, NULL); #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; 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; dia_canvas_item_update_handles_i2w (item); dia_canvas_item_request_update (item); } static gboolean dia_real_canvas_item_connect (DiaCanvasItem *item, DiaHandle *handle) { dia_handle_preserve_state (handle); if (handle->connected_to != item) { if (handle->connected_to) dia_canvas_item_disconnect (handle->connected_to, handle); handle->connected_to = g_object_ref (item); item->connected_handles = g_list_append (item->connected_handles, handle); } return TRUE; } static gboolean dia_real_canvas_item_disconnect (DiaCanvasItem *item, DiaHandle *handle) { g_assert (handle->connected_to == item); dia_handle_preserve_state (handle); dia_handle_remove_all_constraints (handle); if (handle->connected_to) g_object_unref (handle->connected_to); 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 (G_STRLOC": 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)); if ((!new_parent) || item->canvas != new_parent->canvas) { /* Ensure the handles are disconnected */ dia_canvas_item_disconnect_handles (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); //g_message(G_STRLOC": resetting canvas to %p", item->canvas); } /* 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 (G_STRLOC": 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(G_STRLOC": need update: %p", i); DIA_SET_FLAGS (i, DIA_NEED_UPDATE); i = (DiaCanvasItem*) i->parent; //g_message (G_STRLOC": 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); }