Code

c4425add69e85c283e794dee4bec0312e7530545
[inkscape.git] / src / sp-conn-end-pair.cpp
1 /*
2  * A class for handling connector endpoint movement and libavoid interaction.
3  *
4  * Authors:
5  *   Peter Moulder <pmoulder@mail.csse.monash.edu.au>
6  *   Michael Wybrow <mjwybrow@users.sourceforge.net>
7  *
8  *    * Copyright (C) 2004-2005 Monash University
9  *
10  * Released under GNU GPL, read the file 'COPYING' for more information
11  */
13 #include "attributes.h"
14 #include "sp-conn-end.h"
15 #include "uri.h"
16 #include "display/curve.h"
17 #include "xml/repr.h"
18 #include "sp-path.h"
19 #include "libavoid/vertices.h"
20 #include "libavoid/router.h"
21 #include "document.h"
24 SPConnEndPair::SPConnEndPair(SPPath *const owner)
25     : _invalid_path_connection()
26     , _path(owner)
27     , _connRef(NULL)
28     , _connType(SP_CONNECTOR_NOAVOID)
29     , _transformed_connection()
30 {
31     for (unsigned handle_ix = 0; handle_ix <= 1; ++handle_ix) {
32         this->_connEnd[handle_ix] = new SPConnEnd(SP_OBJECT(owner));
33         this->_connEnd[handle_ix]->_changed_connection
34             = this->_connEnd[handle_ix]->ref.changedSignal()
35             .connect(sigc::bind(sigc::ptr_fun(sp_conn_end_href_changed),
36                                 this->_connEnd[handle_ix], owner, handle_ix));
37     }
38 }
40 SPConnEndPair::~SPConnEndPair()
41 {
42     for (unsigned handle_ix = 0; handle_ix < 2; ++handle_ix) {
43         delete this->_connEnd[handle_ix];
44         this->_connEnd[handle_ix] = NULL;
45     }
46     if (_connRef) {
47         _connRef->removeFromGraph();
48         delete _connRef;
49         _connRef = NULL;
50     }
52     _invalid_path_connection.disconnect();
53     _transformed_connection.disconnect();
54 }
56 void
57 SPConnEndPair::release()
58 {
59     for (unsigned handle_ix = 0; handle_ix < 2; ++handle_ix) {
60         this->_connEnd[handle_ix]->_changed_connection.disconnect();
61         this->_connEnd[handle_ix]->_delete_connection.disconnect();
62         this->_connEnd[handle_ix]->_transformed_connection.disconnect();
63         g_free(this->_connEnd[handle_ix]->href);
64         this->_connEnd[handle_ix]->href = NULL;
65         this->_connEnd[handle_ix]->ref.detach();
66     }
67 }
69 void
70 sp_conn_end_pair_build(SPObject *object)
71 {
72     sp_object_read_attr(object, "inkscape:connector-type");
73     sp_object_read_attr(object, "inkscape:connection-start");
74     sp_object_read_attr(object, "inkscape:connection-end");
75 }
78 static void
79 avoid_conn_move(NR::Matrix const *mp, SPItem *moved_item)
80 {
81     // Detach from objects if attached.
82     sp_conn_end_detach(moved_item, 0);
83     sp_conn_end_detach(moved_item, 1);
84     // Reroute connector
85     SPPath *path = SP_PATH(moved_item);
86     path->connEndPair.makePathInvalid();
87     sp_conn_adjust_invalid_path(path);
88 }
91 void
92 SPConnEndPair::setAttr(unsigned const key, gchar const *const value)
93 {
94     if (key == SP_ATTR_CONNECTOR_TYPE) {
95         if (value && (strcmp(value, "polyline") == 0)) {
96             _connType = SP_CONNECTOR_POLYLINE;
98             Avoid::Router *router = _path->document->router;
99             GQuark itemID = g_quark_from_string(SP_OBJECT(_path)->id);
100             _connRef = new Avoid::ConnRef(router, itemID);
101             _invalid_path_connection = connectInvalidPath(
102                     sigc::ptr_fun(&sp_conn_adjust_invalid_path));
103             _transformed_connection = _path->connectTransformed(
104                     sigc::ptr_fun(&avoid_conn_move));
105         }
106         else {
107             _connType = SP_CONNECTOR_NOAVOID;
109             if (_connRef) {
110                 _connRef->removeFromGraph();
111                 delete _connRef;
112                 _connRef = NULL;
113                 _invalid_path_connection.disconnect();
114                 _transformed_connection.disconnect();
115             }
116         }
117         return;
119     }
121     unsigned const handle_ix = key - SP_ATTR_CONNECTION_START;
122     g_assert( handle_ix <= 1 );
123     this->_connEnd[handle_ix]->setAttacherHref(value);
126 void
127 SPConnEndPair::writeRepr(Inkscape::XML::Node *const repr) const
129     for (unsigned handle_ix = 0; handle_ix < 2; ++handle_ix) {
130         if (this->_connEnd[handle_ix]->ref.getURI()) {
131             char const * const attr_strs[] = {"inkscape:connection-start",
132                                               "inkscape:connection-end"};
133             gchar *uri_string = this->_connEnd[handle_ix]->ref.getURI()->toString();
134             repr->setAttribute(attr_strs[handle_ix], uri_string);
135             g_free(uri_string);
136         }
137     }
140 void
141 SPConnEndPair::getAttachedItems(SPItem *h2attItem[2]) const {
142     for (unsigned h = 0; h < 2; ++h) {
143         h2attItem[h] = this->_connEnd[h]->ref.getObject();
144     }
147 void
148 SPConnEndPair::getEndpoints(NR::Point endPts[]) const {
149     SPCurve *curve = _path->curve;
150     SPItem *h2attItem[2];
151     getAttachedItems(h2attItem);
153     for (unsigned h = 0; h < 2; ++h) {
154         if ( h2attItem[h] ) {
155             NR::Rect const bbox = h2attItem[h]->invokeBbox(sp_item_i2doc_affine(h2attItem[h]));
156             endPts[h] = bbox.midpoint();
157         }
158         else
159         {
160             if (h == 0) {
161                 endPts[h] = sp_curve_first_point(curve);
162             }
163             else {
164                 endPts[h] = sp_curve_last_point(curve);
165             }
166         }
167     }
170 sigc::connection
171 SPConnEndPair::connectInvalidPath(sigc::slot<void, SPPath *> slot)
173     return _invalid_path_signal.connect(slot);
176 static void emitPathInvalidationNotification(void *ptr)
178     // We emit a signal here rather than just calling the reroute function
179     // since this allows all the movement action computation to happen,
180     // then all connectors (that require it) will be rerouted.  Otherwise,
181     // one connector could get rerouted several times as a result of
182     // dragging a couple of shapes.
184     SPPath *path = SP_PATH(ptr);
185     path->connEndPair._invalid_path_signal.emit(path);
188 void
189 SPConnEndPair::rerouteFromManipulation(void)
191     _connRef->makePathInvalid();
192     sp_conn_adjust_path(_path);
195 void
196 SPConnEndPair::reroute(void)
198     sp_conn_adjust_path(_path);
201 // Called from sp_path_update to initialise the endpoints.
202 void
203 SPConnEndPair::update(void)
205     if (_connType != SP_CONNECTOR_NOAVOID) {
206         g_assert(_connRef != NULL);
207         if (!(_connRef->isInitialised())) {
208             NR::Point endPt[2];
209             getEndpoints(endPt);
211             Avoid::Point src = { endPt[0][NR::X], endPt[0][NR::Y] };
212             Avoid::Point dst = { endPt[1][NR::X], endPt[1][NR::Y] };
214             _connRef->lateSetup(src, dst);
215             _connRef->setCallback(&emitPathInvalidationNotification, _path);
216         }
217         // Store the ID of the objects attached to the connector.
218         storeIds();
219     }
223 void SPConnEndPair::storeIds(void)
225     if (_connEnd[0]->href) {
226         // href begins with a '#' which we don't want.
227         const char *startId = _connEnd[0]->href + 1;
228         GQuark itemId = g_quark_from_string(startId);
229         _connRef->setEndPointId(Avoid::VertID::src, itemId);
230     }
231     else {
232         _connRef->setEndPointId(Avoid::VertID::src, 0);
233     }
234     if (_connEnd[1]->href) {
235         // href begins with a '#' which we don't want.
236         const char *endId = _connEnd[1]->href + 1;
237         GQuark itemId = g_quark_from_string(endId);
238         _connRef->setEndPointId(Avoid::VertID::tar, itemId);
239     }
240     else {
241         _connRef->setEndPointId(Avoid::VertID::tar, 0);
242     }
246 bool
247 SPConnEndPair::isAutoRoutingConn(void)
249     if (_connType != SP_CONNECTOR_NOAVOID) {
250         return true;
251     }
252     return false;
255 void
256 SPConnEndPair::makePathInvalid(void)
258     _connRef->makePathInvalid();
261 void
262 SPConnEndPair::reroutePath(void)
264     if (!isAutoRoutingConn()) {
265         // Do nothing
266         return;
267     }
269     SPCurve *curve = _path->curve;
271     NR::Point endPt[2];
272     getEndpoints(endPt);
274     Avoid::Point src = { endPt[0][NR::X], endPt[0][NR::Y] };
275     Avoid::Point dst = { endPt[1][NR::X], endPt[1][NR::Y] };
277     _connRef->updateEndPoint(Avoid::VertID::src, src);
278     _connRef->updateEndPoint(Avoid::VertID::tar, dst);
280     _connRef->generatePath(src, dst);
282     Avoid::PolyLine route = _connRef->route();
283     _connRef->calcRouteDist();
285     sp_curve_reset(curve);
286     sp_curve_moveto(curve, endPt[0]);
288     for (int i = 1; i < route.pn; ++i) {
289         NR::Point p(route.ps[i].x, route.ps[i].y);
290         sp_curve_lineto(curve, p);
291     }
294 /*
295   Local Variables:
296   mode:c++
297   c-file-style:"stroustrup"
298   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
299   indent-tabs-mode:nil
300   fill-column:99
301   End:
302 */
303 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :