Code

* src/sp-conn-end-pair.cpp, src/sp-conn-end-pair.h,
[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"
22 SPConnEndPair::SPConnEndPair(SPPath *const owner)
23     : _invalid_path_connection()
24     , _path(owner)
25     , _connRef(NULL)
26     , _connType(SP_CONNECTOR_NOAVOID)
27     , _transformed_connection()
28 {
29     for (unsigned handle_ix = 0; handle_ix <= 1; ++handle_ix) {
30         this->_connEnd[handle_ix] = new SPConnEnd(SP_OBJECT(owner));
31         this->_connEnd[handle_ix]->_changed_connection
32             = this->_connEnd[handle_ix]->ref.changedSignal()
33             .connect(sigc::bind(sigc::ptr_fun(sp_conn_end_href_changed),
34                                 this->_connEnd[handle_ix], owner, handle_ix));
35     }
36 }
38 SPConnEndPair::~SPConnEndPair()
39 {
40     for (unsigned handle_ix = 0; handle_ix < 2; ++handle_ix) {
41         delete this->_connEnd[handle_ix];
42         this->_connEnd[handle_ix] = NULL;
43     }
44     if (_connRef) {
45         _connRef->removeFromGraph();
46         delete _connRef;
47         _connRef = NULL;
48     }
49     
50     _invalid_path_connection.disconnect();
51     _transformed_connection.disconnect();
52 }
54 void
55 SPConnEndPair::release()
56 {
57     for (unsigned handle_ix = 0; handle_ix < 2; ++handle_ix) {
58         this->_connEnd[handle_ix]->_changed_connection.disconnect();
59         this->_connEnd[handle_ix]->_delete_connection.disconnect();
60         this->_connEnd[handle_ix]->_transformed_connection.disconnect();
61         g_free(this->_connEnd[handle_ix]->href);
62         this->_connEnd[handle_ix]->href = NULL;
63         this->_connEnd[handle_ix]->ref.detach();
64     }
65 }
67 void
68 sp_conn_end_pair_build(SPObject *object)
69 {
70     sp_object_read_attr(object, "inkscape:connector-type");
71     sp_object_read_attr(object, "inkscape:connection-start");
72     sp_object_read_attr(object, "inkscape:connection-end");
73 }
76 static void 
77 avoid_conn_move(NR::Matrix const *mp, SPItem *moved_item)
78 {
79     // Detach from objects if attached.
80     sp_conn_end_detach(moved_item, 0);
81     sp_conn_end_detach(moved_item, 1);
82     // Reroute connector
83     SPPath *path = SP_PATH(moved_item);
84     path->connEndPair.makePathInvalid();
85     sp_conn_adjust_invalid_path(path);
86 }
89 void
90 SPConnEndPair::setAttr(unsigned const key, gchar const *const value)
91 {
92     if (key == SP_ATTR_CONNECTOR_TYPE) {
93         if (value && (strcmp(value, "polyline") == 0)) {
94             _connType = SP_CONNECTOR_POLYLINE;
95             
96             GQuark itemID = g_quark_from_string(SP_OBJECT(_path)->id);
97             _connRef = new Avoid::ConnRef(itemID);
98             _invalid_path_connection = connectInvalidPath(
99                     sigc::ptr_fun(&sp_conn_adjust_invalid_path));
100             _transformed_connection = _path->connectTransformed(
101                     sigc::ptr_fun(&avoid_conn_move));
102         }
103         else {
104             _connType = SP_CONNECTOR_NOAVOID;
105             
106             if (_connRef) {
107                 _connRef->removeFromGraph();
108                 delete _connRef;
109                 _connRef = NULL;
110                 _invalid_path_connection.disconnect();
111                 _transformed_connection.disconnect();
112             }
113         }
114         return;
116     }
117     
118     unsigned const handle_ix = key - SP_ATTR_CONNECTION_START;
119     g_assert( handle_ix <= 1 );
120     this->_connEnd[handle_ix]->setAttacherHref(value);
123 void
124 SPConnEndPair::writeRepr(Inkscape::XML::Node *const repr) const
126     for (unsigned handle_ix = 0; handle_ix < 2; ++handle_ix) {
127         if (this->_connEnd[handle_ix]->ref.getURI()) {
128             char const * const attr_strs[] = {"inkscape:connection-start",
129                                               "inkscape:connection-end"};
130             gchar *uri_string = this->_connEnd[handle_ix]->ref.getURI()->toString();
131             repr->setAttribute(attr_strs[handle_ix], uri_string);
132             g_free(uri_string);
133         }
134     }
137 void
138 SPConnEndPair::getAttachedItems(SPItem *h2attItem[2]) const {
139     for (unsigned h = 0; h < 2; ++h) {
140         h2attItem[h] = this->_connEnd[h]->ref.getObject();
141     }
144 void
145 SPConnEndPair::getEndpoints(NR::Point endPts[]) const {
146     SPCurve *curve = _path->curve;
147     SPItem *h2attItem[2];
148     getAttachedItems(h2attItem);
149     
150     for (unsigned h = 0; h < 2; ++h) {
151         if ( h2attItem[h] ) {
152             NR::Rect const bbox = h2attItem[h]->invokeBbox(sp_item_i2doc_affine(h2attItem[h]));
153             endPts[h] = bbox.midpoint();
154         }
155         else 
156         {
157             if (h == 0) {
158                 endPts[h] = sp_curve_first_point(curve);
159             }
160             else {
161                 endPts[h] = sp_curve_last_point(curve);
162             }
163         }
164     }
167 sigc::connection
168 SPConnEndPair::connectInvalidPath(sigc::slot<void, SPPath *> slot)
170     return _invalid_path_signal.connect(slot);
173 static void emitPathInvalidationNotification(void *ptr)
175     // We emit a signal here rather than just calling the reroute function
176     // since this allows all the movement action computation to happen,
177     // then all connectors (that require it) will be rerouted.  Otherwise,
178     // one connector could get rerouted several times as a result of
179     // dragging a couple of shapes.
180    
181     SPPath *path = SP_PATH(ptr);
182     path->connEndPair._invalid_path_signal.emit(path);
185 void
186 SPConnEndPair::rerouteFromManipulation(void)
188     _connRef->makePathInvalid();
189     sp_conn_adjust_path(_path);
192 void
193 SPConnEndPair::reroute(void)
195     sp_conn_adjust_path(_path);
198 // Called from sp_path_update to initialise the endpoints.
199 void
200 SPConnEndPair::update(void)
202     if (_connType != SP_CONNECTOR_NOAVOID) {
203         g_assert(_connRef != NULL);
204         if (!(_connRef->isInitialised())) {
205             NR::Point endPt[2];
206             getEndpoints(endPt);
208             Avoid::Point src = { endPt[0][NR::X], endPt[0][NR::Y] };
209             Avoid::Point dst = { endPt[1][NR::X], endPt[1][NR::Y] };
211             _connRef->lateSetup(src, dst);
212             _connRef->setCallback(&emitPathInvalidationNotification, _path);
213         }
214         // Store the ID of the objects attached to the connector.
215         storeIds();
216     }
220 void SPConnEndPair::storeIds(void)
222     if (_connEnd[0]->href) {
223         GQuark itemId = g_quark_from_string(_connEnd[0]->href);
224         _connRef->setEndPointId(Avoid::VertID::src, itemId);
225     }
226     else {
227         _connRef->setEndPointId(Avoid::VertID::src, 0);
228     }
229     if (_connEnd[1]->href) {
230         GQuark itemId = g_quark_from_string(_connEnd[1]->href);
231         _connRef->setEndPointId(Avoid::VertID::tar, itemId);
232     }
233     else {
234         _connRef->setEndPointId(Avoid::VertID::tar, 0);
235     }
239 bool
240 SPConnEndPair::isAutoRoutingConn(void)
242     if (_connType != SP_CONNECTOR_NOAVOID) {
243         return true;
244     }
245     return false;
247     
248 void
249 SPConnEndPair::makePathInvalid(void)
251     _connRef->makePathInvalid();
254 void
255 SPConnEndPair::reroutePath(void)
257     if (!isAutoRoutingConn()) {
258         // Do nothing
259         return;
260     }
262     SPCurve *curve = _path->curve;
264     NR::Point endPt[2];
265     getEndpoints(endPt);
267     Avoid::Point src = { endPt[0][NR::X], endPt[0][NR::Y] };
268     Avoid::Point dst = { endPt[1][NR::X], endPt[1][NR::Y] };
269     
270     _connRef->updateEndPoint(Avoid::VertID::src, src);
271     _connRef->updateEndPoint(Avoid::VertID::tar, dst);
273     _connRef->generatePath(src, dst);
275     Avoid::PolyLine route = _connRef->route();
276     _connRef->calcRouteDist();
277     
278     sp_curve_reset(curve);
279     sp_curve_moveto(curve, endPt[0]);
281     for (int i = 1; i < route.pn; ++i) {
282         NR::Point p(route.ps[i].x, route.ps[i].y);
283         sp_curve_lineto(curve, p);
284     }
287 /*
288   Local Variables:
289   mode:c++
290   c-file-style:"stroustrup"
291   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
292   indent-tabs-mode:nil
293   fill-column:99
294   End:
295 */
296 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :