index c4425add69e85c283e794dee4bec0312e7530545..0a5a6d7bdde9e9fb72ae144ba76ab29c7e0355c5 100644 (file)
--- a/src/sp-conn-end-pair.cpp
+++ b/src/sp-conn-end-pair.cpp
* Authors:
* Peter Moulder <pmoulder@mail.csse.monash.edu.au>
* Michael Wybrow <mjwybrow@users.sourceforge.net>
+ * Abhishek Sharma
*
* * Copyright (C) 2004-2005 Monash University
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
+#include <cstring>
+#include <string>
+#include <iostream>
+#include <glibmm/stringutils.h>
+
#include "attributes.h"
#include "sp-conn-end.h"
#include "uri.h"
#include "libavoid/vertices.h"
#include "libavoid/router.h"
#include "document.h"
+#include "sp-item-group.h"
SPConnEndPair::SPConnEndPair(SPPath *const owner)
- : _invalid_path_connection()
- , _path(owner)
+ : _path(owner)
, _connRef(NULL)
, _connType(SP_CONNECTOR_NOAVOID)
+ , _connCurvature(0.0)
, _transformed_connection()
{
for (unsigned handle_ix = 0; handle_ix <= 1; ++handle_ix) {
delete this->_connEnd[handle_ix];
this->_connEnd[handle_ix] = NULL;
}
- if (_connRef) {
- _connRef->removeFromGraph();
- delete _connRef;
- _connRef = NULL;
- }
-
- _invalid_path_connection.disconnect();
- _transformed_connection.disconnect();
}
void
this->_connEnd[handle_ix]->href = NULL;
this->_connEnd[handle_ix]->ref.detach();
}
+
+ // If the document is being destroyed then the router instance
+ // and the ConnRefs will have been destroyed with it.
+ const bool routerInstanceExists = (_path->document->router != NULL);
+
+ if (_connRef && routerInstanceExists) {
+ _connRef->removeFromGraph();
+ delete _connRef;
+ }
+ _connRef = NULL;
+
+ _transformed_connection.disconnect();
}
void
sp_conn_end_pair_build(SPObject *object)
{
- sp_object_read_attr(object, "inkscape:connector-type");
- sp_object_read_attr(object, "inkscape:connection-start");
- sp_object_read_attr(object, "inkscape:connection-end");
+ object->readAttr( "inkscape:connector-type" );
+ object->readAttr( "inkscape:connection-start" );
+ object->readAttr( "inkscape:connection-start-point" );
+ object->readAttr( "inkscape:connection-end" );
+ object->readAttr( "inkscape:connection-end-point" );
+ object->readAttr( "inkscape:connector-curvature" );
}
static void
-avoid_conn_move(NR::Matrix const *mp, SPItem *moved_item)
+avoid_conn_transformed(Geom::Matrix const */*mp*/, SPItem *moved_item)
{
- // Detach from objects if attached.
- sp_conn_end_detach(moved_item, 0);
- sp_conn_end_detach(moved_item, 1);
- // Reroute connector
SPPath *path = SP_PATH(moved_item);
- path->connEndPair.makePathInvalid();
- sp_conn_adjust_invalid_path(path);
+ if (path->connEndPair.isAutoRoutingConn()) {
+ path->connEndPair.tellLibavoidNewEndpoints();
+ }
}
void
SPConnEndPair::setAttr(unsigned const key, gchar const *const value)
{
- if (key == SP_ATTR_CONNECTOR_TYPE) {
- if (value && (strcmp(value, "polyline") == 0)) {
- _connType = SP_CONNECTOR_POLYLINE;
-
- Avoid::Router *router = _path->document->router;
- GQuark itemID = g_quark_from_string(SP_OBJECT(_path)->id);
- _connRef = new Avoid::ConnRef(router, itemID);
- _invalid_path_connection = connectInvalidPath(
- sigc::ptr_fun(&sp_conn_adjust_invalid_path));
- _transformed_connection = _path->connectTransformed(
- sigc::ptr_fun(&avoid_conn_move));
- }
- else {
- _connType = SP_CONNECTOR_NOAVOID;
-
- if (_connRef) {
- _connRef->removeFromGraph();
- delete _connRef;
- _connRef = NULL;
- _invalid_path_connection.disconnect();
- _transformed_connection.disconnect();
+ switch (key)
+ {
+ case SP_ATTR_CONNECTOR_TYPE:
+ if (value && (strcmp(value, "polyline") == 0 || strcmp(value, "orthogonal") == 0)) {
+ int newconnType = strcmp(value, "polyline") ? SP_CONNECTOR_ORTHOGONAL : SP_CONNECTOR_POLYLINE;
+
+ if (!_connRef)
+ {
+ _connType = newconnType;
+ Avoid::Router *router = _path->document->router;
+ GQuark itemID = g_quark_from_string(_path->getId());
+ _connRef = new Avoid::ConnRef(router, itemID);
+ switch (newconnType)
+ {
+ case SP_CONNECTOR_POLYLINE:
+ _connRef->setRoutingType(Avoid::ConnType_PolyLine);
+ break;
+ case SP_CONNECTOR_ORTHOGONAL:
+ _connRef->setRoutingType(Avoid::ConnType_Orthogonal);
+ }
+ _transformed_connection = _path->connectTransformed(
+ sigc::ptr_fun(&avoid_conn_transformed));
+ }
+ else
+ if (newconnType != _connType)
+ {
+ _connType = newconnType;
+ switch (newconnType)
+ {
+ case SP_CONNECTOR_POLYLINE:
+ _connRef->setRoutingType(Avoid::ConnType_PolyLine);
+ break;
+ case SP_CONNECTOR_ORTHOGONAL:
+ _connRef->setRoutingType(Avoid::ConnType_Orthogonal);
+ }
+ sp_conn_reroute_path(_path);
+ }
}
- }
- return;
-
+ else {
+ _connType = SP_CONNECTOR_NOAVOID;
+
+ if (_connRef) {
+ _connRef->removeFromGraph();
+ delete _connRef;
+ _connRef = NULL;
+ _transformed_connection.disconnect();
+ }
+ }
+ break;
+ case SP_ATTR_CONNECTOR_CURVATURE:
+ if (value) {
+ _connCurvature = g_strtod(value, NULL);
+ if (_connRef && _connRef->isInitialised()) {
+ // Redraw the connector, but only if it has been initialised.
+ sp_conn_reroute_path(_path);
+ }
+ }
+ break;
+ case SP_ATTR_CONNECTION_START:
+ case SP_ATTR_CONNECTION_END:
+ this->_connEnd[(key == SP_ATTR_CONNECTION_START ? 0 : 1)]->setAttacherHref(value, _path);
+ break;
+ case SP_ATTR_CONNECTION_START_POINT:
+ case SP_ATTR_CONNECTION_END_POINT:
+ this->_connEnd[(key == SP_ATTR_CONNECTION_START_POINT ? 0 : 1)]->setAttacherEndpoint(value, _path);
+ break;
}
- unsigned const handle_ix = key - SP_ATTR_CONNECTION_START;
- g_assert( handle_ix <= 1 );
- this->_connEnd[handle_ix]->setAttacherHref(value);
}
void
SPConnEndPair::writeRepr(Inkscape::XML::Node *const repr) const
{
+ char const * const attr_strs[] = {"inkscape:connection-start", "inkscape:connection-start-point",
+ "inkscape:connection-end", "inkscape:connection-end-point"};
for (unsigned handle_ix = 0; handle_ix < 2; ++handle_ix) {
if (this->_connEnd[handle_ix]->ref.getURI()) {
- char const * const attr_strs[] = {"inkscape:connection-start",
- "inkscape:connection-end"};
- gchar *uri_string = this->_connEnd[handle_ix]->ref.getURI()->toString();
- repr->setAttribute(attr_strs[handle_ix], uri_string);
- g_free(uri_string);
+ repr->setAttribute(attr_strs[2*handle_ix], this->_connEnd[handle_ix]->ref.getURI()->toString());
+ std::ostringstream ostr;
+ ostr<<(this->_connEnd[handle_ix]->type == ConnPointDefault ? "d":"u") <<
+ this->_connEnd[handle_ix]->id;
+ repr->setAttribute(attr_strs[2*handle_ix+1], ostr.str().c_str());
}
}
+ repr->setAttribute("inkscape:connector-curvature", Glib::Ascii::dtostr(_connCurvature).c_str());
+ if (_connType == SP_CONNECTOR_POLYLINE || _connType == SP_CONNECTOR_ORTHOGONAL)
+ repr->setAttribute("inkscape:connector-type", _connType == SP_CONNECTOR_POLYLINE ? "polyline" : "orthogonal" );
}
void
SPConnEndPair::getAttachedItems(SPItem *h2attItem[2]) const {
for (unsigned h = 0; h < 2; ++h) {
h2attItem[h] = this->_connEnd[h]->ref.getObject();
+
+ // Deal with the case of the attached object being an empty group.
+ // A group containing no items does not have a valid bbox, so
+ // causes problems for the auto-routing code. Also, since such a
+ // group no longer has an onscreen representation and can only be
+ // selected through the XML editor, it makes sense just to detach
+ // connectors from them.
+ if (SP_IS_GROUP(h2attItem[h])) {
+ if (SP_GROUP(h2attItem[h])->group->getItemCount() == 0) {
+ // This group is empty, so detach.
+ sp_conn_end_detach(_path, h);
+ h2attItem[h] = NULL;
+ }
+ }
}
}
void
-SPConnEndPair::getEndpoints(NR::Point endPts[]) const {
- SPCurve *curve = _path->curve;
+SPConnEndPair::getEndpoints(Geom::Point endPts[]) const {
+ SPCurve *curve = _path->original_curve ? _path->original_curve : _path->curve;
SPItem *h2attItem[2];
getAttachedItems(h2attItem);
for (unsigned h = 0; h < 2; ++h) {
if ( h2attItem[h] ) {
- NR::Rect const bbox = h2attItem[h]->invokeBbox(sp_item_i2doc_affine(h2attItem[h]));
- endPts[h] = bbox.midpoint();
+ g_assert(h2attItem[h]->avoidRef);
+ endPts[h] = h2attItem[h]->avoidRef->getConnectionPointPos(_connEnd[h]->type, _connEnd[h]->id);
}
else
{
if (h == 0) {
- endPts[h] = sp_curve_first_point(curve);
+ endPts[h] = *(curve->first_point());
}
else {
- endPts[h] = sp_curve_last_point(curve);
+ endPts[h] = *(curve->last_point());
}
}
}
}
-sigc::connection
-SPConnEndPair::connectInvalidPath(sigc::slot<void, SPPath *> slot)
-{
- return _invalid_path_signal.connect(slot);
+gdouble
+SPConnEndPair::getCurvature(void) const {
+ return _connCurvature;
}
-static void emitPathInvalidationNotification(void *ptr)
+SPConnEnd**
+SPConnEndPair::getConnEnds(void)
{
- // We emit a signal here rather than just calling the reroute function
- // since this allows all the movement action computation to happen,
- // then all connectors (that require it) will be rerouted. Otherwise,
- // one connector could get rerouted several times as a result of
- // dragging a couple of shapes.
+ return _connEnd;
+}
- SPPath *path = SP_PATH(ptr);
- path->connEndPair._invalid_path_signal.emit(path);
+bool
+SPConnEndPair::isOrthogonal(void) const {
+ return _connType == SP_CONNECTOR_ORTHOGONAL;
}
-void
-SPConnEndPair::rerouteFromManipulation(void)
+
+static void redrawConnectorCallback(void *ptr)
{
- _connRef->makePathInvalid();
- sp_conn_adjust_path(_path);
+ SPPath *path = SP_PATH(ptr);
+ if (path->document == NULL) {
+ // This can happen when the document is being destroyed.
+ return;
+ }
+ sp_conn_redraw_path(path);
}
void
-SPConnEndPair::reroute(void)
+SPConnEndPair::rerouteFromManipulation(void)
{
- sp_conn_adjust_path(_path);
+ sp_conn_reroute_path_immediate(_path);
}
+
// Called from sp_path_update to initialise the endpoints.
void
SPConnEndPair::update(void)
if (_connType != SP_CONNECTOR_NOAVOID) {
g_assert(_connRef != NULL);
if (!(_connRef->isInitialised())) {
- NR::Point endPt[2];
+ Geom::Point endPt[2];
getEndpoints(endPt);
- Avoid::Point src = { endPt[0][NR::X], endPt[0][NR::Y] };
- Avoid::Point dst = { endPt[1][NR::X], endPt[1][NR::Y] };
+ Avoid::Point src(endPt[0][Geom::X], endPt[0][Geom::Y]);
+ Avoid::Point dst(endPt[1][Geom::X], endPt[1][Geom::Y]);
- _connRef->lateSetup(src, dst);
- _connRef->setCallback(&emitPathInvalidationNotification, _path);
+ _connRef->setEndpoints(src, dst);
+ _connRef->setCallback(&redrawConnectorCallback, _path);
}
// Store the ID of the objects attached to the connector.
storeIds();
_connRef->makePathInvalid();
}
+
+// Redraws the curve along the recalculated route
+// Straight or curved
+void recreateCurve(SPCurve *curve, Avoid::ConnRef *connRef, const gdouble curvature)
+{
+ bool straight = curvature<1e-3;
+
+ Avoid::PolyLine route = connRef->displayRoute();
+ if (!straight)
+ route = route.curvedPolyline(curvature);
+ connRef->calcRouteDist();
+
+ curve->reset();
+
+ curve->moveto( Geom::Point(route.ps[0].x, route.ps[0].y) );
+ int pn = route.size();
+ for (int i = 1; i < pn; ++i) {
+ Geom::Point p(route.ps[i].x, route.ps[i].y);
+ if (straight) {
+ curve->lineto( p );
+ }
+ else {
+ switch (route.ts[i]) {
+ case 'M':
+ curve->moveto( p );
+ break;
+ case 'L':
+ curve->lineto( p );
+ break;
+ case 'C':
+ g_assert( i+2<pn );
+ curve->curveto( p, Geom::Point(route.ps[i+1].x, route.ps[i+1].y),
+ Geom::Point(route.ps[i+2].x, route.ps[i+2].y) );
+ i+=2;
+ break;
+ }
+ }
+ }
+}
+
+
void
-SPConnEndPair::reroutePath(void)
+SPConnEndPair::tellLibavoidNewEndpoints(const bool processTransaction)
{
if (!isAutoRoutingConn()) {
// Do nothing
return;
}
+ makePathInvalid();
- SPCurve *curve = _path->curve;
-
- NR::Point endPt[2];
+ Geom::Point endPt[2];
getEndpoints(endPt);
- Avoid::Point src = { endPt[0][NR::X], endPt[0][NR::Y] };
- Avoid::Point dst = { endPt[1][NR::X], endPt[1][NR::Y] };
+ Avoid::Point src(endPt[0][Geom::X], endPt[0][Geom::Y]);
+ Avoid::Point dst(endPt[1][Geom::X], endPt[1][Geom::Y]);
- _connRef->updateEndPoint(Avoid::VertID::src, src);
- _connRef->updateEndPoint(Avoid::VertID::tar, dst);
+ _connRef->setEndpoints(src, dst);
+ if (processTransaction)
+ {
+ _connRef->router()->processTransaction();
+ }
+ return;
+}
- _connRef->generatePath(src, dst);
- Avoid::PolyLine route = _connRef->route();
- _connRef->calcRouteDist();
+bool
+SPConnEndPair::reroutePathFromLibavoid(void)
+{
+ if (!isAutoRoutingConn()) {
+ // Do nothing
+ return false;
+ }
- sp_curve_reset(curve);
- sp_curve_moveto(curve, endPt[0]);
+ SPCurve *curve = _path->original_curve ?_path->original_curve : _path->curve;
- for (int i = 1; i < route.pn; ++i) {
- NR::Point p(route.ps[i].x, route.ps[i].y);
- sp_curve_lineto(curve, p);
- }
+ recreateCurve( curve, _connRef, _connCurvature );
+
+ Geom::Matrix doc2item = SP_ITEM(_path)->i2doc_affine().inverse();
+ curve->transform(doc2item);
+
+ return true;
}
+
/*
Local Variables:
mode:c++