Code

* src/conn-avoid-ref.cpp: Include the "xml/simple-node" header, rather than
[inkscape.git] / src / conn-avoid-ref.cpp
1 /*
2  * A class for handling shape interaction with libavoid.
3  *
4  * Authors:
5  *   Michael Wybrow <mjwybrow@users.sourceforge.net>
6  *
7  * Copyright (C) 2005 Michael Wybrow
8  *
9  * Released under GNU GPL, read the file 'COPYING' for more information
10  */
13 #include <cstring>
14 #include <string>
16 #include "sp-item.h"
17 #include "conn-avoid-ref.h"
18 #include "libnr/nr-rect-ops.h"
19 #include "libavoid/polyutil.h"
20 #include "libavoid/router.h"
21 #include "libavoid/connector.h"
22 #include "xml/simple-node.h"
23 #include "document.h"
24 #include "prefs-utils.h"
26 #include "desktop.h"
27 #include "desktop-handles.h"
28 #include "sp-namedview.h"
29 #include "inkscape.h"
32 using Avoid::Router;
34 static Avoid::Polygn avoid_item_poly(SPItem const *item);
37 SPAvoidRef::SPAvoidRef(SPItem *spitem)
38     : shapeRef(NULL)
39     , item(spitem)
40     , setting(false)
41     , new_setting(false)
42     , _transformed_connection()
43 {
44 }
47 SPAvoidRef::~SPAvoidRef()
48 {
49     _transformed_connection.disconnect();
50     if (shapeRef) {
51         Router *router = shapeRef->router();
52         // shapeRef is finalised by delShape,
53         // so no memory is lost here.
54         router->delShape(shapeRef);
55         shapeRef = NULL;
56     }
57 }
60 void SPAvoidRef::setAvoid(char const *value)
61 {
62     if (SP_OBJECT_IS_CLONED(item)) {
63         // Don't keep avoidance information for cloned objects.
64         return;
65     }
66     new_setting = false;
67     if (value && (strcmp(value, "true") == 0)) {
68         new_setting = true;
69     }
70 }
73 void SPAvoidRef::handleSettingChange(void)
74 {
75     SPDesktop *desktop = inkscape_active_desktop();
76     if (desktop == NULL) {
77         return;
78     }
79     if (sp_desktop_document(desktop) != item->document) {
80         // We don't want to go any further if the active desktop's document
81         // isn't the same as the document that this item is part of.  This
82         // case can happen if a new document is loaded from the file chooser
83         // or via the recent file menu.  In this case, we can end up here
84         // as a rersult of a sp_document_ensure_up_to_date performed on a
85         // document not yet attached to the active desktop.
86         return;
87     }
89     if (new_setting == setting) {
90         // Don't need to make any changes
91         return;
92     }
93     setting = new_setting;
95     Router *router = item->document->router;
96     
97     _transformed_connection.disconnect();
98     if (new_setting) {
99         Avoid::Polygn poly = avoid_item_poly(item);
100         if (poly.pn > 0) {
101             _transformed_connection = item->connectTransformed(
102                     sigc::ptr_fun(&avoid_item_move));
104             const char *id = SP_OBJECT_REPR(item)->attribute("id");
105             g_assert(id != NULL);
106             
107             // Get a unique ID for the item.
108             GQuark itemID = g_quark_from_string(id);
110             shapeRef = new Avoid::ShapeRef(router, itemID, poly);
111             Avoid::freePoly(poly);
112         
113             router->addShape(shapeRef);
114         }
115     }
116     else
117     {
118         g_assert(shapeRef);
119         
120         // shapeRef is finalised by delShape,
121         // so no memory is lost here.
122         router->delShape(shapeRef);
123         shapeRef = NULL;
124     }
128 GSList *SPAvoidRef::getAttachedShapes(const unsigned int type)
130     GSList *list = NULL;
132     Avoid::IntList shapes;
133     GQuark shapeId = g_quark_from_string(item->id);
134     item->document->router->attachedShapes(shapes, shapeId, type);
135     
136     Avoid::IntList::iterator finish = shapes.end();
137     for (Avoid::IntList::iterator i = shapes.begin(); i != finish; ++i) {
138         const gchar *connId = g_quark_to_string(*i);
139         SPObject *obj = item->document->getObjectById(connId);
140         if (obj == NULL) {
141             g_warning("getAttachedShapes: Object with id=\"%s\" is not "
142                     "found. Skipping.", connId);
143             continue;
144         }
145         SPItem *shapeItem = SP_ITEM(obj);
146         list = g_slist_prepend(list, shapeItem);
147     }
148     return list;
152 GSList *SPAvoidRef::getAttachedConnectors(const unsigned int type)
154     GSList *list = NULL;
156     Avoid::IntList conns;
157     GQuark shapeId = g_quark_from_string(item->id);
158     item->document->router->attachedConns(conns, shapeId, type);
159     
160     Avoid::IntList::iterator finish = conns.end();
161     for (Avoid::IntList::iterator i = conns.begin(); i != finish; ++i) {
162         const gchar *connId = g_quark_to_string(*i);
163         SPObject *obj = item->document->getObjectById(connId);
164         if (obj == NULL) {
165             g_warning("getAttachedConnectors: Object with id=\"%s\" is not "
166                     "found. Skipping.", connId);
167             continue;
168         }
169         SPItem *connItem = SP_ITEM(obj);
170         list = g_slist_prepend(list, connItem);
171     }
172     return list;
176 static Avoid::Polygn avoid_item_poly(SPItem const *item)
178     SPDesktop *desktop = inkscape_active_desktop();
179     g_assert(desktop != NULL);
181     Avoid::Polygn poly;
183     // TODO: The right way to do this is to return the convex hull of
184     //       the object, or an approximation in the case of a rounded
185     //       object.  Specific SPItems will need to have a new
186     //       function that returns points for the convex hull.
187     //       For some objects it is enough to feed the snappoints to
188     //       some convex hull code, though not NR::ConvexHull as this
189     //       only keeps the bounding box of the convex hull currently.
191     // TODO: SPItem::getBounds gives the wrong result for some objects
192     //       that have internal representations that are updated later
193     //       by the sp_*_update functions, e.g., text.
194     sp_document_ensure_up_to_date(item->document);
195     
196     NR::Maybe<NR::Rect> rHull = item->getBounds(sp_item_i2doc_affine(item));
197     if (!rHull) {
198         return Avoid::newPoly(0);
199     }
201     double spacing = desktop->namedview->connector_spacing;
203     // Add a little buffer around the edge of each object.
204     NR::Rect rExpandedHull = NR::expand(*rHull, -spacing); 
205     poly = Avoid::newPoly(4);
207     for (unsigned n = 0; n < 4; ++n) {
208         NR::Point hullPoint = rExpandedHull.corner(n);
209         poly.ps[n].x = hullPoint[NR::X];
210         poly.ps[n].y = hullPoint[NR::Y];
211     }
213     return poly;
217 GSList *get_avoided_items(GSList *list, SPObject *from, SPDesktop *desktop, 
218         bool initialised)
220     for (SPObject *child = sp_object_first_child(SP_OBJECT(from)) ;
221             child != NULL; child = SP_OBJECT_NEXT(child) ) {
222         if (SP_IS_ITEM(child) &&
223             !desktop->isLayer(SP_ITEM(child)) &&
224             !SP_ITEM(child)->isLocked() && 
225             !desktop->itemIsHidden(SP_ITEM(child)) &&
226             (!initialised || SP_ITEM(child)->avoidRef->shapeRef)
227             )
228         {
229             list = g_slist_prepend (list, SP_ITEM(child));
230         }
232         if (SP_IS_ITEM(child) && desktop->isLayer(SP_ITEM(child))) {
233             list = get_avoided_items(list, child, desktop, initialised);
234         }
235     }
237     return list;
241 void avoid_item_move(NR::Matrix const */*mp*/, SPItem *moved_item)
243     Avoid::ShapeRef *shapeRef = moved_item->avoidRef->shapeRef;
244     g_assert(shapeRef);
246     Router *router = moved_item->document->router;
247     Avoid::Polygn poly = avoid_item_poly(moved_item);
248     if (poly.pn > 0) {
249         router->moveShape(shapeRef, &poly);
250         Avoid::freePoly(poly);
251     }
255 void init_avoided_shape_geometry(SPDesktop *desktop)
257     // Don't count this as changes to the document,
258     // it is basically just llate initialisation.
259     SPDocument *document = sp_desktop_document(desktop);
260     bool saved = sp_document_get_undo_sensitive(document);
261     sp_document_set_undo_sensitive(document, false);
262     
263     bool initialised = false;
264     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop,
265             initialised);
267     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
268         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
269         item->avoidRef->handleSettingChange();
270     }
272     if (items) {
273         g_slist_free(items);
274     }
275     sp_document_set_undo_sensitive(document, saved);
279 /*
280   Local Variables:
281   mode:c++
282   c-file-style:"stroustrup"
283   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
284   indent-tabs-mode:nil
285   fill-column:99
286   End:
287 */
288 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :