/* * A class for handling shape interaction with libavoid. * * Authors: * Michael Wybrow <mjwybrow@users.sourceforge.net> * * Copyright (C) 2005 Michael Wybrow * * Released under GNU GPL, read the file 'COPYING' for more information */ #include <cstring> #include <string> #include "sp-item.h" #include "conn-avoid-ref.h" #include "libnr/nr-rect-ops.h" #include "libavoid/polyutil.h" #include "libavoid/router.h" #include "libavoid/connector.h" #include "xml/simple-node.h" #include "document.h" #include "prefs-utils.h" #include "desktop.h" #include "desktop-handles.h" #include "sp-namedview.h" #include "inkscape.h" using Avoid::Router; static Avoid::Polygn avoid_item_poly(SPItem const *item); SPAvoidRef::SPAvoidRef(SPItem *spitem) : shapeRef(NULL) , item(spitem) , setting(false) , new_setting(false) , _transformed_connection() { } SPAvoidRef::~SPAvoidRef() { _transformed_connection.disconnect(); if (shapeRef) { Router *router = shapeRef->router(); // shapeRef is finalised by delShape, // so no memory is lost here. router->delShape(shapeRef); shapeRef = NULL; } } void SPAvoidRef::setAvoid(char const *value) { if (SP_OBJECT_IS_CLONED(item)) { // Don't keep avoidance information for cloned objects. return; } new_setting = false; if (value && (strcmp(value, "true") == 0)) { new_setting = true; } } void SPAvoidRef::handleSettingChange(void) { SPDesktop *desktop = inkscape_active_desktop(); if (desktop == NULL) { return; } if (sp_desktop_document(desktop) != item->document) { // We don't want to go any further if the active desktop's document // isn't the same as the document that this item is part of. This // case can happen if a new document is loaded from the file chooser // or via the recent file menu. In this case, we can end up here // as a rersult of a sp_document_ensure_up_to_date performed on a // document not yet attached to the active desktop. return; } if (new_setting == setting) { // Don't need to make any changes return; } setting = new_setting; Router *router = item->document->router; _transformed_connection.disconnect(); if (new_setting) { Avoid::Polygn poly = avoid_item_poly(item); if (poly.pn > 0) { _transformed_connection = item->connectTransformed( sigc::ptr_fun(&avoid_item_move)); const char *id = SP_OBJECT_REPR(item)->attribute("id"); g_assert(id != NULL); // Get a unique ID for the item. GQuark itemID = g_quark_from_string(id); shapeRef = new Avoid::ShapeRef(router, itemID, poly); Avoid::freePoly(poly); router->addShape(shapeRef); } } else { g_assert(shapeRef); // shapeRef is finalised by delShape, // so no memory is lost here. router->delShape(shapeRef); shapeRef = NULL; } } GSList *SPAvoidRef::getAttachedShapes(const unsigned int type) { GSList *list = NULL; Avoid::IntList shapes; GQuark shapeId = g_quark_from_string(item->id); item->document->router->attachedShapes(shapes, shapeId, type); Avoid::IntList::iterator finish = shapes.end(); for (Avoid::IntList::iterator i = shapes.begin(); i != finish; ++i) { const gchar *connId = g_quark_to_string(*i); SPObject *obj = item->document->getObjectById(connId); if (obj == NULL) { g_warning("getAttachedShapes: Object with id=\"%s\" is not " "found. Skipping.", connId); continue; } SPItem *shapeItem = SP_ITEM(obj); list = g_slist_prepend(list, shapeItem); } return list; } GSList *SPAvoidRef::getAttachedConnectors(const unsigned int type) { GSList *list = NULL; Avoid::IntList conns; GQuark shapeId = g_quark_from_string(item->id); item->document->router->attachedConns(conns, shapeId, type); Avoid::IntList::iterator finish = conns.end(); for (Avoid::IntList::iterator i = conns.begin(); i != finish; ++i) { const gchar *connId = g_quark_to_string(*i); SPObject *obj = item->document->getObjectById(connId); if (obj == NULL) { g_warning("getAttachedConnectors: Object with id=\"%s\" is not " "found. Skipping.", connId); continue; } SPItem *connItem = SP_ITEM(obj); list = g_slist_prepend(list, connItem); } return list; } static Avoid::Polygn avoid_item_poly(SPItem const *item) { SPDesktop *desktop = inkscape_active_desktop(); g_assert(desktop != NULL); Avoid::Polygn poly; // TODO: The right way to do this is to return the convex hull of // the object, or an approximation in the case of a rounded // object. Specific SPItems will need to have a new // function that returns points for the convex hull. // For some objects it is enough to feed the snappoints to // some convex hull code, though not NR::ConvexHull as this // only keeps the bounding box of the convex hull currently. // TODO: SPItem::getBounds gives the wrong result for some objects // that have internal representations that are updated later // by the sp_*_update functions, e.g., text. sp_document_ensure_up_to_date(item->document); NR::Maybe<NR::Rect> rHull = item->getBounds(sp_item_i2doc_affine(item)); if (!rHull) { return Avoid::newPoly(0); } double spacing = desktop->namedview->connector_spacing; // Add a little buffer around the edge of each object. NR::Rect rExpandedHull = NR::expand(*rHull, -spacing); poly = Avoid::newPoly(4); for (unsigned n = 0; n < 4; ++n) { NR::Point hullPoint = rExpandedHull.corner(n); poly.ps[n].x = hullPoint[NR::X]; poly.ps[n].y = hullPoint[NR::Y]; } return poly; } GSList *get_avoided_items(GSList *list, SPObject *from, SPDesktop *desktop, bool initialised) { for (SPObject *child = sp_object_first_child(SP_OBJECT(from)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { if (SP_IS_ITEM(child) && !desktop->isLayer(SP_ITEM(child)) && !SP_ITEM(child)->isLocked() && !desktop->itemIsHidden(SP_ITEM(child)) && (!initialised || SP_ITEM(child)->avoidRef->shapeRef) ) { list = g_slist_prepend (list, SP_ITEM(child)); } if (SP_IS_ITEM(child) && desktop->isLayer(SP_ITEM(child))) { list = get_avoided_items(list, child, desktop, initialised); } } return list; } void avoid_item_move(NR::Matrix const */*mp*/, SPItem *moved_item) { Avoid::ShapeRef *shapeRef = moved_item->avoidRef->shapeRef; g_assert(shapeRef); Router *router = moved_item->document->router; Avoid::Polygn poly = avoid_item_poly(moved_item); if (poly.pn > 0) { router->moveShape(shapeRef, &poly); Avoid::freePoly(poly); } } void init_avoided_shape_geometry(SPDesktop *desktop) { // Don't count this as changes to the document, // it is basically just llate initialisation. SPDocument *document = sp_desktop_document(desktop); bool saved = sp_document_get_undo_sensitive(document); sp_document_set_undo_sensitive(document, false); bool initialised = false; GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop, initialised); for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) { SPItem *item = reinterpret_cast<SPItem *>(iter->data); item->avoidRef->handleSettingChange(); } if (items) { g_slist_free(items); } sp_document_set_undo_sensitive(document, saved); } /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :