Code

Translations. French translation minor update.
[inkscape.git] / src / sp-conn-end.cpp
2 #include <cstring>
3 #include <string>
4 #include <limits>
6 #include "display/curve.h"
7 #include "libnr/nr-matrix-fns.h"
8 #include "xml/repr.h"
9 #include "sp-conn-end.h"
10 #include "sp-path.h"
11 #include "uri.h"
12 #include "document.h"
13 #include "sp-item-group.h"
14 #include "2geom/path.h"
15 #include "2geom/pathvector.h"
16 #include "2geom/path-intersection.h"
19 static void change_endpts(SPCurve *const curve, double const endPos[2]);
21 SPConnEnd::SPConnEnd(SPObject *const owner) :
22     ref(owner),
23     href(NULL),
24     // Default to center connection endpoint
25     type(ConnPointDefault),
26     id(4),
27     _changed_connection(),
28     _delete_connection(),
29     _transformed_connection()
30 {
31 }
33 static SPObject const *
34 get_nearest_common_ancestor(SPObject const *const obj, SPItem const *const objs[2]) {
35     SPObject const *anc_sofar = obj;
36     for (unsigned i = 0; i < 2; ++i) {
37         if ( objs[i] != NULL ) {
38             anc_sofar = anc_sofar->nearestCommonAncestor(objs[i]);
39         }
40     }
41     return anc_sofar;
42 }
45 static bool try_get_intersect_point_with_item_recursive(Geom::PathVector& conn_pv, SPItem* item,
46         const Geom::Matrix& item_transform, double& intersect_pos) {
48     double initial_pos = intersect_pos;
49     // if this is a group...
50     if (SP_IS_GROUP(item)) {
51         SPGroup* group = SP_GROUP(item);
53         // consider all first-order children
54         double child_pos = 0.0;
55         for (GSList const* i = sp_item_group_item_list(group); i != NULL; i = i->next) {
56             SPItem* child_item = SP_ITEM(i->data);
57             try_get_intersect_point_with_item_recursive(conn_pv, child_item,
58                     item_transform * child_item->transform, child_pos);
59             if (intersect_pos < child_pos)
60                 intersect_pos = child_pos;
61         }
62         return intersect_pos != initial_pos;
63     }
65     // if this is not a shape, nothing to be done
66     if (!SP_IS_SHAPE(item)) return false;
68     // make sure it has an associated curve
69     SPCurve* item_curve = SP_SHAPE(item)->getCurve();
70     if (!item_curve) return false;
72     // apply transformations (up to common ancestor)
73     item_curve->transform(item_transform);
75     const Geom::PathVector& curve_pv = item_curve->get_pathvector();
76     Geom::CrossingSet cross = crossings(conn_pv, curve_pv);
77     // iterate over all Crossings
78     for (Geom::CrossingSet::const_iterator i = cross.begin(); i != cross.end(); i++) {
79         const Geom::Crossings& cr = *i;
81         for (Geom::Crossings::const_iterator i = cr.begin(); i != cr.end(); i++) {
82             const Geom::Crossing& cr_pt = *i;
83             if ( intersect_pos < cr_pt.ta)
84                 intersect_pos = cr_pt.ta;
85         }
86     }
88     item_curve->unref();
90     return intersect_pos != initial_pos;
91 }
94 // This function returns the outermost intersection point between the path (a connector)
95 // and the item given.  If the item is a group, then the component items are considered.
96 // The transforms given should be to a common ancestor of both the path and item.
97 //
98 static bool try_get_intersect_point_with_item(SPPath* conn, SPItem* item,
99         const Geom::Matrix& item_transform, const Geom::Matrix& conn_transform,
100         const bool at_start, double& intersect_pos) {
102     // Copy the curve and apply transformations up to common ancestor.
103     SPCurve* conn_curve = conn->curve->copy();
104     conn_curve->transform(conn_transform);
106     Geom::PathVector conn_pv = conn_curve->get_pathvector();
108     // If this is not the starting point, use Geom::Path::reverse() to reverse the path
109     if (!at_start)
110     {
111         // connectors are actually a single path, so consider the first element from a Geom::PathVector
112         conn_pv[0] = conn_pv[0].reverse();
113     }
115     // We start with the intersection point at the beginning of the path
116     intersect_pos = 0.0;
118     // Find the intersection.
119     bool result = try_get_intersect_point_with_item_recursive(conn_pv, item, item_transform, intersect_pos);
121     if (!result)
122         // No intersection point has been found (why?)
123         // just default to connector end
124         intersect_pos = 0;
125     // If not at the starting point, recompute position with respect to original path
126     if (!at_start)
127         intersect_pos = conn_pv[0].size() - intersect_pos;
128     // Free the curve copy.
129     conn_curve->unref();
131     return result;
135 static void
136 sp_conn_get_route_and_redraw(SPPath *const path,
137         const bool updatePathRepr = true)
139     // Get the new route around obstacles.
140     bool rerouted = path->connEndPair.reroutePathFromLibavoid();
141     if (!rerouted) {
142         return;
143     }
145     SPItem *h2attItem[2];
146     path->connEndPair.getAttachedItems(h2attItem);
148     SPItem const *const path_item = SP_ITEM(path);
149     SPObject const *const ancestor = get_nearest_common_ancestor(path_item, h2attItem);
150     Geom::Matrix const path2anc(i2anc_affine(path_item, ancestor));
152     // Set sensible values incase there the connector ends are not
153     // attached to any shapes.
154     Geom::PathVector conn_pv = path->curve->get_pathvector();
155     double endPos[2] = { 0, conn_pv[0].size() };
157     SPConnEnd** _connEnd = path->connEndPair.getConnEnds();
158     for (unsigned h = 0; h < 2; ++h) {
159         if (h2attItem[h] && _connEnd[h]->type == ConnPointDefault && _connEnd[h]->id == ConnPointPosCC) {
160             Geom::Matrix h2i2anc = i2anc_affine(h2attItem[h], ancestor);
161             try_get_intersect_point_with_item(path, h2attItem[h], h2i2anc, path2anc,
162                         (h == 0), endPos[h]);
163         }
164     }
165     change_endpts(path->curve, endPos);
166     if (updatePathRepr) {
167         path->updateRepr();
168         path->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
169     }
173 static void
174 sp_conn_end_shape_move(Geom::Matrix const */*mp*/, SPItem */*moved_item*/,
175                             SPPath *const path)
177     if (path->connEndPair.isAutoRoutingConn()) {
178         path->connEndPair.tellLibavoidNewEndpoints();
179     }
183 void
184 sp_conn_reroute_path(SPPath *const path)
186     if (path->connEndPair.isAutoRoutingConn()) {
187         path->connEndPair.tellLibavoidNewEndpoints();
188     }
192 void
193 sp_conn_reroute_path_immediate(SPPath *const path)
195     if (path->connEndPair.isAutoRoutingConn()) {
196         bool processTransaction = true;
197         path->connEndPair.tellLibavoidNewEndpoints(processTransaction);
198     }
199     // Don't update the path repr or else connector dragging is slowed by
200     // constant update of values to the xml editor, and each step is also
201     // needlessly remembered by undo/redo.
202     bool const updatePathRepr = false;
203     sp_conn_get_route_and_redraw(path, updatePathRepr);
206 void sp_conn_redraw_path(SPPath *const path)
208     sp_conn_get_route_and_redraw(path);
212 static void
213 change_endpts(SPCurve *const curve, double const endPos[2])
215     // Use Geom::Path::portion to cut the curve at the end positions
216     if (endPos[0] > endPos[1])
217     {
218         // Path is "negative", reset the curve and return
219         curve->reset();
220         return;
221     }
222     const Geom::Path& old_path = curve->get_pathvector()[0];
223     Geom::PathVector new_path_vector;
224     new_path_vector.push_back(old_path.portion(endPos[0], endPos[1]));
225     curve->set_pathvector(new_path_vector);
228 static void
229 sp_conn_end_deleted(SPObject *, SPObject *const owner, unsigned const handle_ix)
231     // todo: The first argument is the deleted object, or just NULL if
232     //       called by sp_conn_end_detach.
233     g_return_if_fail(handle_ix < 2);
234     char const * const attr_strs[] = {"inkscape:connection-start", "inkscape:connection-start-point",
235                                       "inkscape:connection-end", "inkscape:connection-end-point"};
236     SP_OBJECT_REPR(owner)->setAttribute(attr_strs[2*handle_ix], NULL);
237     SP_OBJECT_REPR(owner)->setAttribute(attr_strs[2*handle_ix+1], NULL);
238     /* I believe this will trigger sp_conn_end_href_changed. */
241 void
242 sp_conn_end_detach(SPObject *const owner, unsigned const handle_ix)
244     sp_conn_end_deleted(NULL, owner, handle_ix);
247 void
248 SPConnEnd::setAttacherHref(gchar const *value, SPPath* /*path*/)
250     if ( value && href && ( strcmp(value, href) == 0 ) ) {
251         /* No change, do nothing. */
252     } 
253     else 
254     {
255         if (!value)
256         {
257             ref.detach();
258             g_free(href);
259             href = NULL;
260         }
261         else
262         {
263             bool validRef = true;
264             href = g_strdup(value);
265             // Now do the attaching, which emits the changed signal.
266             try {
267                 ref.attach(Inkscape::URI(value));
268             } catch (Inkscape::BadURIException &e) {
269                 /* TODO: Proper error handling as per
270                 * http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing.  (Also needed for
271                 * sp-use.) */
272                 g_warning("%s", e.what());
273                 validRef = false;
274             }
276             if ( !validRef )
277             {
278                 ref.detach();
279                 g_free(href);
280                 href = NULL;
281             }
282         }
283     }
286 void
287 SPConnEnd::setAttacherEndpoint(gchar const *value, SPPath* /*path*/)
289     
290     /* References to the connection points have the following format
291         <t><id>, where t is the type of the point, which
292         can be either "d" for default or "u" for user-defined, and
293         id is the local (inside the item) id of the connection point.
294         In the case of default points id represents the position on the
295         item (i.e. Top-Left, Center-Center, etc.).
296     */
297     
298     bool changed = false;
299     ConnPointType newtype = type;
300     
301     if (!value)
302     {
303         // Default to center endpoint
304         type = ConnPointDefault;
305         id = 4;
306     }
307     else
308     {
309         switch (value[0])
310         {
311             case 'd':
312                 if ( newtype != ConnPointDefault )
313                 {
314                     newtype = ConnPointDefault;
315                     changed = true;
316                 }
317                 break;
318             case 'u':
319                 if ( newtype != ConnPointUserDefined)
320                 {
321                     newtype = ConnPointUserDefined;
322                     changed = true;
323                 }
324                 break;
325             default:
326                 g_warning("Bad reference to a connection point.");
327         }
328         
329         int newid = (int) g_ascii_strtod( value+1, 0 );
330         if ( id != newid )
331         {
332             id = newid;
333             changed = true;
334         }
336         // We have to verify that the reference to the
337         // connection point is a valid one.
338         
339         if ( changed )
340         {
342             // Get the item the connector is attached to
343             SPItem* item = ref.getObject();
344             if ( item )
345             {
346                 if (!item->avoidRef->isValidConnPointId( newtype, newid ) )
347                 {
348                     g_warning("Bad reference to a connection point.");
349                 }
350                 else
351                 {
352                     type = newtype;
353                     id = newid;
354                 }
355     /*          // Update the connector
356                 if (path->connEndPair.isAutoRoutingConn()) {
357                     path->connEndPair.tellLibavoidNewEndpoints();
358                 }
359     */
360             }
361         }
362     }
365 void
366 sp_conn_end_href_changed(SPObject */*old_ref*/, SPObject */*ref*/,
367                          SPConnEnd *connEndPtr, SPPath *const path, unsigned const handle_ix)
369     g_return_if_fail(connEndPtr != NULL);
370     SPConnEnd &connEnd = *connEndPtr;
371     connEnd._delete_connection.disconnect();
372     connEnd._transformed_connection.disconnect();
374     if (connEnd.href) {
375         SPObject *refobj = connEnd.ref.getObject();
376         if (refobj) {
377             connEnd._delete_connection
378                 = SP_OBJECT(refobj)->connectDelete(sigc::bind(sigc::ptr_fun(&sp_conn_end_deleted),
379                                                               SP_OBJECT(path), handle_ix));
380             connEnd._transformed_connection
381                 = SP_ITEM(refobj)->connectTransformed(sigc::bind(sigc::ptr_fun(&sp_conn_end_shape_move),
382                                                                  path));
383         }
384     }
389 /*
390   Local Variables:
391   mode:c++
392   c-file-style:"stroustrup"
393   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
394   indent-tabs-mode:nil
395   fill-column:99
396   End:
397 */
398 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :