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 }
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;
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;
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 }
118 unsigned const handle_ix = key - SP_ATTR_CONNECTION_START;
119 g_assert( handle_ix <= 1 );
120 this->_connEnd[handle_ix]->setAttacherHref(value);
121 }
123 void
124 SPConnEndPair::writeRepr(Inkscape::XML::Node *const repr) const
125 {
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 }
135 }
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 }
142 }
144 void
145 SPConnEndPair::getEndpoints(NR::Point endPts[]) const {
146 SPCurve *curve = _path->curve;
147 SPItem *h2attItem[2];
148 getAttachedItems(h2attItem);
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 }
165 }
167 sigc::connection
168 SPConnEndPair::connectInvalidPath(sigc::slot<void, SPPath *> slot)
169 {
170 return _invalid_path_signal.connect(slot);
171 }
173 static void emitPathInvalidationNotification(void *ptr)
174 {
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.
181 SPPath *path = SP_PATH(ptr);
182 path->connEndPair._invalid_path_signal.emit(path);
183 }
185 void
186 SPConnEndPair::rerouteFromManipulation(void)
187 {
188 _connRef->makePathInvalid();
189 sp_conn_adjust_path(_path);
190 }
192 void
193 SPConnEndPair::reroute(void)
194 {
195 sp_conn_adjust_path(_path);
196 }
198 // Called from sp_path_update to initialise the endpoints.
199 void
200 SPConnEndPair::update(void)
201 {
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 }
215 }
218 bool
219 SPConnEndPair::isAutoRoutingConn(void)
220 {
221 if (_connType != SP_CONNECTOR_NOAVOID) {
222 return true;
223 }
224 return false;
225 }
227 void
228 SPConnEndPair::makePathInvalid(void)
229 {
230 _connRef->makePathInvalid();
231 }
233 void
234 SPConnEndPair::reroutePath(void)
235 {
236 if (!isAutoRoutingConn()) {
237 // Do nothing
238 return;
239 }
241 SPCurve *curve = _path->curve;
243 NR::Point endPt[2];
244 getEndpoints(endPt);
246 Avoid::Point src = { endPt[0][NR::X], endPt[0][NR::Y] };
247 Avoid::Point dst = { endPt[1][NR::X], endPt[1][NR::Y] };
249 _connRef->updateEndPoint(Avoid::VertID::src, src);
250 _connRef->updateEndPoint(Avoid::VertID::tar, dst);
252 _connRef->generatePath(src, dst);
254 Avoid::PolyLine route = _connRef->route();
255 _connRef->calcRouteDist();
257 sp_curve_reset(curve);
258 sp_curve_moveto(curve, endPt[0]);
260 for (int i = 1; i < route.pn; ++i) {
261 NR::Point p(route.ps[i].x, route.ps[i].y);
262 sp_curve_lineto(curve, p);
263 }
264 }
266 /*
267 Local Variables:
268 mode:c++
269 c-file-style:"stroustrup"
270 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
271 indent-tabs-mode:nil
272 fill-column:99
273 End:
274 */
275 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :