Code

Merge from fe-moved
[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 "libavoid/polyutil.h"
19 #include "libavoid/router.h"
20 #include "libavoid/connector.h"
21 #include "xml/node.h"
22 #include "document.h"
23 #include "desktop.h"
24 #include "desktop-handles.h"
25 #include "sp-namedview.h"
26 #include "inkscape.h"
29 using Avoid::Router;
31 static Avoid::Polygn avoid_item_poly(SPItem const *item);
34 SPAvoidRef::SPAvoidRef(SPItem *spitem)
35     : shapeRef(NULL)
36     , item(spitem)
37     , setting(false)
38     , new_setting(false)
39     , _transformed_connection()
40 {
41 }
44 SPAvoidRef::~SPAvoidRef()
45 {
46     _transformed_connection.disconnect();
47     if (shapeRef) {
48         Router *router = shapeRef->router();
49         // shapeRef is finalised by delShape,
50         // so no memory is lost here.
51         router->delShape(shapeRef);
52         shapeRef = NULL;
53     }
54 }
57 void SPAvoidRef::setAvoid(char const *value)
58 {
59     if (SP_OBJECT_IS_CLONED(item)) {
60         // Don't keep avoidance information for cloned objects.
61         return;
62     }
63     new_setting = false;
64     if (value && (strcmp(value, "true") == 0)) {
65         new_setting = true;
66     }
67 }
70 void SPAvoidRef::handleSettingChange(void)
71 {
72     SPDesktop *desktop = inkscape_active_desktop();
73     if (desktop == NULL) {
74         return;
75     }
76     if (sp_desktop_document(desktop) != item->document) {
77         // We don't want to go any further if the active desktop's document
78         // isn't the same as the document that this item is part of.  This
79         // case can happen if a new document is loaded from the file chooser
80         // or via the recent file menu.  In this case, we can end up here
81         // as a rersult of a sp_document_ensure_up_to_date performed on a
82         // document not yet attached to the active desktop.
83         return;
84     }
86     if (new_setting == setting) {
87         // Don't need to make any changes
88         return;
89     }
90     setting = new_setting;
92     Router *router = item->document->router;
93     
94     _transformed_connection.disconnect();
95     if (new_setting) {
96         Avoid::Polygn poly = avoid_item_poly(item);
97         if (poly.pn > 0) {
98             _transformed_connection = item->connectTransformed(
99                     sigc::ptr_fun(&avoid_item_move));
101             const char *id = SP_OBJECT_REPR(item)->attribute("id");
102             g_assert(id != NULL);
103             
104             // Get a unique ID for the item.
105             GQuark itemID = g_quark_from_string(id);
107             shapeRef = new Avoid::ShapeRef(router, itemID, poly);
108             Avoid::freePoly(poly);
109         
110             router->addShape(shapeRef);
111         }
112     }
113     else
114     {
115         g_assert(shapeRef);
116         
117         // shapeRef is finalised by delShape,
118         // so no memory is lost here.
119         router->delShape(shapeRef);
120         shapeRef = NULL;
121     }
125 GSList *SPAvoidRef::getAttachedShapes(const unsigned int type)
127     GSList *list = NULL;
129     Avoid::IntList shapes;
130     GQuark shapeId = g_quark_from_string(item->id);
131     item->document->router->attachedShapes(shapes, shapeId, type);
132     
133     Avoid::IntList::iterator finish = shapes.end();
134     for (Avoid::IntList::iterator i = shapes.begin(); i != finish; ++i) {
135         const gchar *connId = g_quark_to_string(*i);
136         SPObject *obj = item->document->getObjectById(connId);
137         if (obj == NULL) {
138             g_warning("getAttachedShapes: Object with id=\"%s\" is not "
139                     "found. Skipping.", connId);
140             continue;
141         }
142         SPItem *shapeItem = SP_ITEM(obj);
143         list = g_slist_prepend(list, shapeItem);
144     }
145     return list;
149 GSList *SPAvoidRef::getAttachedConnectors(const unsigned int type)
151     GSList *list = NULL;
153     Avoid::IntList conns;
154     GQuark shapeId = g_quark_from_string(item->id);
155     item->document->router->attachedConns(conns, shapeId, type);
156     
157     Avoid::IntList::iterator finish = conns.end();
158     for (Avoid::IntList::iterator i = conns.begin(); i != finish; ++i) {
159         const gchar *connId = g_quark_to_string(*i);
160         SPObject *obj = item->document->getObjectById(connId);
161         if (obj == NULL) {
162             g_warning("getAttachedConnectors: Object with id=\"%s\" is not "
163                     "found. Skipping.", connId);
164             continue;
165         }
166         SPItem *connItem = SP_ITEM(obj);
167         list = g_slist_prepend(list, connItem);
168     }
169     return list;
173 static Avoid::Polygn avoid_item_poly(SPItem const *item)
175     SPDesktop *desktop = inkscape_active_desktop();
176     g_assert(desktop != NULL);
178     Avoid::Polygn poly;
180     // TODO: The right way to do this is to return the convex hull of
181     //       the object, or an approximation in the case of a rounded
182     //       object.  Specific SPItems will need to have a new
183     //       function that returns points for the convex hull.
184     //       For some objects it is enough to feed the snappoints to
185     //       some convex hull code, though not NR::ConvexHull as this
186     //       only keeps the bounding box of the convex hull currently.
188     // TODO: SPItem::getBounds gives the wrong result for some objects
189     //       that have internal representations that are updated later
190     //       by the sp_*_update functions, e.g., text.
191     sp_document_ensure_up_to_date(item->document);
192     
193     Geom::OptRect rHull = item->getBounds(sp_item_i2doc_affine(item));
194     if (!rHull) {
195         return Avoid::newPoly(0);
196     }
198     double spacing = desktop->namedview->connector_spacing;
200     // Add a little buffer around the edge of each object.
201     Geom::Rect rExpandedHull = *rHull;
202     rExpandedHull.expandBy(-spacing); 
203     poly = Avoid::newPoly(4);
205     for (unsigned n = 0; n < 4; ++n) {
206         Geom::Point hullPoint = rExpandedHull.corner(n);
207         poly.ps[n].x = hullPoint[Geom::X];
208         poly.ps[n].y = hullPoint[Geom::Y];
209     }
211     return poly;
215 GSList *get_avoided_items(GSList *list, SPObject *from, SPDesktop *desktop, 
216         bool initialised)
218     for (SPObject *child = sp_object_first_child(SP_OBJECT(from)) ;
219             child != NULL; child = SP_OBJECT_NEXT(child) ) {
220         if (SP_IS_ITEM(child) &&
221             !desktop->isLayer(SP_ITEM(child)) &&
222             !SP_ITEM(child)->isLocked() && 
223             !desktop->itemIsHidden(SP_ITEM(child)) &&
224             (!initialised || SP_ITEM(child)->avoidRef->shapeRef)
225             )
226         {
227             list = g_slist_prepend (list, SP_ITEM(child));
228         }
230         if (SP_IS_ITEM(child) && desktop->isLayer(SP_ITEM(child))) {
231             list = get_avoided_items(list, child, desktop, initialised);
232         }
233     }
235     return list;
239 void avoid_item_move(Geom::Matrix const */*mp*/, SPItem *moved_item)
241     Avoid::ShapeRef *shapeRef = moved_item->avoidRef->shapeRef;
242     g_assert(shapeRef);
244     Router *router = moved_item->document->router;
245     Avoid::Polygn poly = avoid_item_poly(moved_item);
246     if (poly.pn > 0) {
247         router->moveShape(shapeRef, &poly);
248         Avoid::freePoly(poly);
249     }
253 void init_avoided_shape_geometry(SPDesktop *desktop)
255     // Don't count this as changes to the document,
256     // it is basically just llate initialisation.
257     SPDocument *document = sp_desktop_document(desktop);
258     bool saved = sp_document_get_undo_sensitive(document);
259     sp_document_set_undo_sensitive(document, false);
260     
261     bool initialised = false;
262     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop,
263             initialised);
265     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
266         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
267         item->avoidRef->handleSettingChange();
268     }
270     if (items) {
271         g_slist_free(items);
272     }
273     sp_document_set_undo_sensitive(document, saved);
277 /*
278   Local Variables:
279   mode:c++
280   c-file-style:"stroustrup"
281   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
282   indent-tabs-mode:nil
283   fill-column:99
284   End:
285 */
286 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :