diff --git a/po/POTFILES.src.in b/po/POTFILES.src.in
index cd486f3ce14f73b0fc6e07553a60e7f5e7cc1237..f228e327434d1c261591de6799f679ac9d2f8b1b 100644
--- a/po/POTFILES.src.in
+++ b/po/POTFILES.src.in
@@ -126,6 +126,7 @@
../src/live_effects/lpe-bspline.cpp
../src/live_effects/lpe-circle_with_radius.cpp
../src/live_effects/lpe-clone-original.cpp
+../src/live_effects/lpe-connector.cpp
../src/live_effects/lpe-constructgrid.cpp
../src/live_effects/lpe-copy_rotate.cpp
../src/live_effects/lpe-curvestitch.cpp
@@ -169,6 +170,7 @@
../src/live_effects/lpe-vonkoch.cpp
../src/live_effects/parameter/bool.cpp
../src/live_effects/parameter/colorpicker.cpp
+../src/live_effects/parameter/connectorpointarray.cpp
../src/live_effects/parameter/enum.h
../src/live_effects/parameter/fontbutton.cpp
../src/live_effects/parameter/item.cpp
diff --git a/share/icons/Tango/scalable/actions/lpe-connector.svg b/share/icons/Tango/scalable/actions/lpe-connector.svg
new file mode 100644
index 0000000000000000000000000000000000000000..6590154fc89bbcd2552398d02a72db42a5be4a2d
--- /dev/null
+++ b/share/icons/Tango/scalable/actions/lpe-connector.svg
@@ -0,0 +1,152 @@
+
+
diff --git a/share/icons/hicolor/scalable/actions/lpe-connector.svg b/share/icons/hicolor/scalable/actions/lpe-connector.svg
new file mode 100644
index 0000000000000000000000000000000000000000..6590154fc89bbcd2552398d02a72db42a5be4a2d
--- /dev/null
+++ b/share/icons/hicolor/scalable/actions/lpe-connector.svg
@@ -0,0 +1,152 @@
+
+
diff --git a/share/icons/hicolor/symbolic/actions/lpe-connector-symbolic.svg b/share/icons/hicolor/symbolic/actions/lpe-connector-symbolic.svg
new file mode 100644
index 0000000000000000000000000000000000000000..33d144763954adc2d7688efab46a573d0b93cb72
--- /dev/null
+++ b/share/icons/hicolor/symbolic/actions/lpe-connector-symbolic.svg
@@ -0,0 +1,152 @@
+
+
diff --git a/share/icons/hicolor/symbolic/actions/lpe-connector-symbolic.svg.svg b/share/icons/hicolor/symbolic/actions/lpe-connector-symbolic.svg.svg
new file mode 100644
index 0000000000000000000000000000000000000000..6ebae50c4721c536b7a5333396e732c570f97a92
--- /dev/null
+++ b/share/icons/hicolor/symbolic/actions/lpe-connector-symbolic.svg.svg
@@ -0,0 +1,150 @@
+
+
diff --git a/share/icons/multicolor/symbolic/actions/lpe-connector-symbolic.svg b/share/icons/multicolor/symbolic/actions/lpe-connector-symbolic.svg
new file mode 100644
index 0000000000000000000000000000000000000000..e51a258331928f96b3a762b5f82825c5b0ea8828
--- /dev/null
+++ b/share/icons/multicolor/symbolic/actions/lpe-connector-symbolic.svg
@@ -0,0 +1,157 @@
+
+
diff --git a/src/attributes.cpp b/src/attributes.cpp
index 531bc16edb516937a3556a4ef99a4a631b3f91e8..d8ede579caec6a08d5e3e1f6bcf1521dc9bca63d 100644
--- a/src/attributes.cpp
+++ b/src/attributes.cpp
@@ -143,6 +143,7 @@ static SPStyleProp const props[] = {
/* (Note: XML representation of connectors may change in future.) */
{SPAttr::CONNECTOR_TYPE, "inkscape:connector-type"},
{SPAttr::CONNECTOR_CURVATURE, "inkscape:connector-curvature"},
+ {SPAttr::CONNECTOR_CONTINUOUS, "inkscape:connector-continuous"},
{SPAttr::INKSCAPE_CONNECTOR_SPACING, "inkscape:connector-spacing"},
{SPAttr::CONNECTION_START, "inkscape:connection-start"},
{SPAttr::CONNECTION_END, "inkscape:connection-end"},
diff --git a/src/attributes.h b/src/attributes.h
index 95553b4ae535ae41ed4c354bdc47bbf5fa17f14a..65ef218629e8dd9e7d33a983d8962d2098b7bafd 100644
--- a/src/attributes.h
+++ b/src/attributes.h
@@ -142,6 +142,7 @@ enum class SPAttr {
INKSCAPE_ORIGINAL_D,
CONNECTOR_TYPE,
CONNECTOR_CURVATURE,
+ CONNECTOR_CONTINUOUS,
INKSCAPE_CONNECTOR_SPACING,
CONNECTION_START,
CONNECTION_END,
diff --git a/src/conn-avoid-ref.cpp b/src/conn-avoid-ref.cpp
index ec64cfa8fbbe44ce9a4a6fb7cbefaf9ce3838009..701a1a3758527a8e96bbbb5a520ffc2480315a3a 100644
--- a/src/conn-avoid-ref.cpp
+++ b/src/conn-avoid-ref.cpp
@@ -11,31 +11,26 @@
* Released under GNU GPL v2+, read the file 'COPYING' for more information.
*/
+#include "conn-avoid-ref.h"
#include
-#include
#include
+#include
#include "2geom/convex-hull.h"
#include "2geom/line.h"
-
-#include "conn-avoid-ref.h"
+#include "3rdparty/adaptagrams/libavoid/router.h"
+#include "3rdparty/adaptagrams/libavoid/shape.h"
#include "desktop.h"
+#include "display/curve.h"
#include "document-undo.h"
#include "document.h"
#include "inkscape.h"
-#include "verbs.h"
-
-#include "display/curve.h"
-
-#include "3rdparty/adaptagrams/libavoid/router.h"
-#include "3rdparty/adaptagrams/libavoid/shape.h"
-
+#include "object/sp-conn-router.h"
#include "object/sp-namedview.h"
#include "object/sp-shape.h"
-
#include "svg/stringstream.h"
-
+#include "verbs.h"
#include "xml/node.h"
using Inkscape::DocumentUndo;
@@ -61,10 +56,10 @@ SPAvoidRef::~SPAvoidRef()
// If the document is being destroyed then the router instance
// and the ShapeRefs will have been destroyed with it.
- Router *router = item->document->getRouter();
+ SPConnRouter *router = item->document->getRouter();
if (shapeRef && router) {
- router->deleteShape(shapeRef);
+ router->getAvoidRouter()->deleteShape(shapeRef);
}
shapeRef = nullptr;
}
@@ -103,7 +98,7 @@ void SPAvoidRef::handleSettingChange()
}
setting = new_setting;
- Router *router = item->document->getRouter();
+ Router *router = item->document->getRouter()->getAvoidRouter();
_transformed_connection.disconnect();
if (new_setting) {
@@ -135,7 +130,7 @@ std::vector SPAvoidRef::getAttachedShapes(const unsigned int type)
Avoid::IntList shapes;
GQuark shapeId = g_quark_from_string(item->getId());
- item->document->getRouter()->attachedShapes(shapes, shapeId, type);
+ item->document->getRouter()->getAvoidRouter()->attachedShapes(shapes, shapeId, type);
Avoid::IntList::iterator finish = shapes.end();
for (Avoid::IntList::iterator i = shapes.begin(); i != finish; ++i) {
@@ -159,7 +154,7 @@ std::vector SPAvoidRef::getAttachedConnectors(const unsigned int type)
Avoid::IntList conns;
GQuark shapeId = g_quark_from_string(item->getId());
- item->document->getRouter()->attachedConns(conns, shapeId, type);
+ item->document->getRouter()->getAvoidRouter()->attachedConns(conns, shapeId, type);
Avoid::IntList::iterator finish = conns.end();
for (Avoid::IntList::iterator i = conns.begin(); i != finish; ++i) {
@@ -364,7 +359,7 @@ void avoid_item_move(Geom::Affine const */*mp*/, SPItem *moved_item)
Avoid::ShapeRef *shapeRef = moved_item->getAvoidRef().shapeRef;
g_assert(shapeRef);
- Router *router = moved_item->document->getRouter();
+ Router *router = moved_item->document->getRouter()->getAvoidRouter();
Avoid::Polygon poly = avoid_item_poly(moved_item);
if (!poly.empty()) {
router->moveShape(shapeRef, poly);
diff --git a/src/display/drawing.cpp b/src/display/drawing.cpp
index bf203c898502d22f95ef665ca76d6c0d1553b08a..6f955e6d8ecdaf2e282d84386c62d09c59587aaa 100644
--- a/src/display/drawing.cpp
+++ b/src/display/drawing.cpp
@@ -11,9 +11,11 @@
* Released under GNU GPL v2+, read the file 'COPYING' for more information.
*/
+#include "display/drawing.h"
+
#include
-#include "display/drawing.h"
+#include "display/control/canvas-item-drawing.h"
#include "nr-filter-gaussian.h"
#include "nr-filter-types.h"
@@ -165,7 +167,13 @@ void
Drawing::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset)
{
if (_root) {
- _root->update(area, ctx, flags, reset);
+ // TODO fix why UpdateContext change on translation
+ // This change fix it but maybe need a deep fix
+ if (_canvas_item_drawing) {
+ _root->update(area, _canvas_item_drawing->get_context(), flags, reset);
+ } else {
+ _root->update(area, ctx, flags, reset);
+ }
}
if ((flags & DrawingItem::STATE_CACHE) || (flags & DrawingItem::STATE_ALL)) {
// process the updated cache scores
diff --git a/src/document.cpp b/src/document.cpp
index 5a9548e9535341f5f6873dd8d1d724d799565220..ad9b08bbbdee6d0cfa55218b13b9653554998de7 100644
--- a/src/document.cpp
+++ b/src/document.cpp
@@ -37,43 +37,36 @@
#define noSP_DOCUMENT_DEBUG_IDLE
#define noSP_DOCUMENT_DEBUG_UNDO
-#include
-#include
-#include
-
#include <2geom/transforms.h>
+#include
+#include
+#include
+#include "3rdparty/adaptagrams/libavoid/router.h"
+#include "3rdparty/libcroco/cr-parser.h"
+#include "3rdparty/libcroco/cr-sel-eng.h"
+#include "3rdparty/libcroco/cr-selector.h"
#include "desktop.h"
-#include "io/dir-util.h"
+#include "display/drawing.h"
#include "document-undo.h"
#include "file.h"
#include "id-clash.h"
#include "inkscape-version.h"
-#include "inkscape.h"
#include "inkscape-window.h"
-#include "profile-manager.h"
-#include "rdf.h"
-
-#include "actions/actions-canvas-snapping.h"
-
-#include "display/drawing.h"
-
-#include "3rdparty/adaptagrams/libavoid/router.h"
-
-#include "3rdparty/libcroco/cr-parser.h"
-#include "3rdparty/libcroco/cr-sel-eng.h"
-#include "3rdparty/libcroco/cr-selector.h"
-
+#include "inkscape.h"
+#include "io/dir-util.h"
#include "live_effects/lpeobject.h"
#include "object/persp3d.h"
+#include "object/sp-conn-router.h"
#include "object/sp-defs.h"
#include "object/sp-factory.h"
#include "object/sp-namedview.h"
#include "object/sp-root.h"
#include "object/sp-symbol.h"
-
+#include "profile-manager.h"
+#include "rdf.h"
+#include "actions/actions-canvas-snapping.h"
#include "widgets/desktop-widget.h"
-
#include "xml/croco-node-iface.h"
#include "xml/rebase-hrefs.h"
@@ -108,7 +101,7 @@ SPDocument::SPDocument() :
document_name(nullptr),
actionkey(),
profileManager(nullptr), // deferred until after other initialization
- router(new Avoid::Router(Avoid::PolyLineRouting|Avoid::OrthogonalRouting)),
+ router(new SPConnRouter(Avoid::PolyLineRouting|Avoid::OrthogonalRouting)),
oldSignalsConnected(false),
current_persp3d(nullptr),
current_persp3d_impl(nullptr),
@@ -124,7 +117,7 @@ SPDocument::SPDocument() :
// Penalise libavoid for choosing paths with needless extra segments.
// This results in much better looking orthogonal connector paths.
- router->setRoutingPenalty(Avoid::segmentPenalty);
+ router->getAvoidRouter()->setRoutingPenalty(Avoid::segmentPenalty);
_serial = next_serial++;
@@ -1239,7 +1232,7 @@ gint SPDocument::ensureUpToDate()
// changed objects and provide new routings. This may cause some objects
// to be modified, hence the second update pass.
if (pass == 1) {
- router->processTransaction();
+ router->getAvoidRouter()->processTransaction();
}
}
@@ -1273,7 +1266,7 @@ SPDocument::rerouting_handler()
// Process any queued movement actions and determine new routings for
// object-avoiding connectors. Callbacks will be used to update and
// redraw affected connectors.
- router->processTransaction();
+ router->getAvoidRouter()->processTransaction();
// We don't need to handle rerouting again until there are further
// diagram updates.
@@ -1428,6 +1421,43 @@ static SPItem *find_item_at_point(std::deque *nodes, unsigned int dkey,
return seen;
}
+/**
+Returns the all items (in z-order) from the descendants of group (recursively) which
+is at the point p, or NULL if none. Honors into_groups on whether to recurse into
+non-layer groups or not. Honors take_insensitive on whether to return insensitive
+items. If upto != NULL, then if item upto is encountered (at any level), stops searching
+upwards in z-order and returns what it has found so far (i.e. the found item is
+guaranteed to be lower than upto). Requires a list of nodes built by
+build_flat_item_list.
+ */
+static std::vector find_items_at_point(std::deque *nodes, unsigned int dkey, Geom::Point const &p,
+ SPItem *upto = nullptr)
+{
+ std::vector items;
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ gdouble delta = prefs->getDouble("/options/cursortolerance/value", 1.0);
+
+ SPItem *child;
+ bool seen_upto = (!upto);
+ for (auto node : *nodes) {
+ child = node;
+ if (!seen_upto) {
+ if (child == upto)
+ seen_upto = true;
+ continue;
+ }
+ Inkscape::DrawingItem *arenaitem = child->get_arenaitem(dkey);
+ if (arenaitem) {
+ arenaitem->drawing().update();
+ if (arenaitem->pick(p, delta, 1) != nullptr) {
+ items.push_back(child);
+ }
+ }
+ }
+
+ return items;
+}
+
/**
Returns the topmost non-layer group from the descendants of group which is at point
p, or NULL if none. Recurses into layers but not into groups.
@@ -1493,7 +1523,7 @@ std::vector SPDocument::getItemsPartiallyInBox(unsigned int dkey, Geom:
std::vector x;
return find_items_in_area(x, SP_GROUP(this->root), dkey, box, overlaps, take_hidden, take_insensitive, take_groups, enter_groups);
}
-
+// this add top items till limit in a list of points
std::vector SPDocument::getItemsAtPoints(unsigned const key, std::vector points, bool all_layers, size_t limit) const
{
std::vector items;
@@ -1538,6 +1568,51 @@ std::vector SPDocument::getItemsAtPoints(unsigned const key, std::vecto
return items;
}
+// this add all items till limit in a point
+std::vector SPDocument::getItemsAtPoint(unsigned const key, Geom::Point point, bool all_layers,
+ size_t limit) const
+{
+ std::vector items;
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+
+ // When picking along the path, we don't want small objects close together
+ // (such as hatching strokes) to obscure each other by their deltas,
+ // so we temporarily set delta to a small value
+ gdouble saved_delta = prefs->getDouble("/options/cursortolerance/value", 1.0);
+ prefs->setDouble("/options/cursortolerance/value", 0.25);
+
+ // Cache a flattened SVG DOM to speed up selection.
+ if (!_node_cache_valid) {
+ _node_cache.clear();
+ build_flat_item_list(key, SP_GROUP(this->root), true);
+ _node_cache_valid = true;
+ }
+ SPObject *current_layer = nullptr;
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ Inkscape::LayerModel *layer_model = nullptr;
+ if (desktop) {
+ current_layer = desktop->currentLayer();
+ layer_model = desktop->layers;
+ }
+ size_t item_counter = 0;
+ std::vector tmp = find_items_at_point(&_node_cache, key, point);
+ for (auto item : tmp) {
+ if (all_layers || (layer_model && layer_model->layerForObject(item) == current_layer)) {
+ items.push_back(item);
+ item_counter++;
+ // limit 0 = no limit
+ if (item_counter == limit) {
+ prefs->setDouble("/options/cursortolerance/value", saved_delta);
+ return items;
+ }
+ }
+ }
+
+ // and now we restore it back
+ prefs->setDouble("/options/cursortolerance/value", saved_delta);
+
+ return items;
+}
SPItem *SPDocument::getItemAtPoint( unsigned const key, Geom::Point const &p,
bool const into_groups, SPItem *upto) const
diff --git a/src/document.h b/src/document.h
index f6145ce80725a2d099e430f7fa4d922cb9f4e210..6328f229c7f5e6eda3f96d8404f7ab5d9d612205 100644
--- a/src/document.h
+++ b/src/document.h
@@ -63,8 +63,6 @@ extern bool sp_no_convert_text_baseline_spacing;
// look in 0.92+.
extern bool sp_do_not_fix_pre_92;
-
-
namespace Avoid {
class Router;
}
@@ -73,6 +71,7 @@ class SPItem;
class SPObject;
class SPGroup;
class SPRoot;
+class SPConnRouter;
namespace Inkscape {
class Selection;
@@ -168,9 +167,8 @@ public:
// Document structure -----------------
Inkscape::ProfileManager* getProfileManager() const { return profileManager; }
- Avoid::Router* getRouter() const { return router; }
+ SPConnRouter *getRouter() const { return router; }
-
/** Returns our SPRoot */
SPRoot *getRoot() { return root; }
SPRoot const *getRoot() const { return root; }
@@ -249,6 +247,8 @@ public:
std::vector getItemsPartiallyInBox(unsigned int dkey, Geom::Rect const &box, bool take_hidden = false, bool take_insensitive = false, bool take_groups = true, bool enter_groups = false) const;
SPItem *getItemAtPoint(unsigned int key, Geom::Point const &p, bool into_groups, SPItem *upto = nullptr) const;
std::vector getItemsAtPoints(unsigned const key, std::vector points, bool all_layers = true, size_t limit = 0) const ;
+ std::vector getItemsAtPoint(unsigned const key, Geom::Point point, bool all_layers = true,
+ size_t limit = 0) const;
SPItem *getGroupAtPoint(unsigned int key, Geom::Point const &p) const;
/**
@@ -296,7 +296,7 @@ private:
// Document ------------------------------
Inkscape::ProfileManager* profileManager; // Color profile.
- Avoid::Router *router; // Instance of the connector router
+ SPConnRouter *router; // Instance of the connector router
// Document status -----------------------
diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt
index 939e7ee4439f70835c37c1f8f834aa0084147351..5f5bedaed7d9a95f285f5a2238485cfbd8bc917f 100644
--- a/src/live_effects/CMakeLists.txt
+++ b/src/live_effects/CMakeLists.txt
@@ -12,6 +12,7 @@ set(live_effects_SRC
lpe-transform_2pts.cpp
lpe-circle_with_radius.cpp
lpe-clone-original.cpp
+ lpe-connector.cpp
lpe-constructgrid.cpp
lpe-copy_rotate.cpp
lpe-curvestitch.cpp
@@ -68,6 +69,7 @@ set(live_effects_SRC
parameter/array.cpp
parameter/bool.cpp
+ parameter/connectorpointarray.cpp
parameter/colorpicker.cpp
parameter/hidden.cpp
parameter/item-reference.cpp
@@ -107,6 +109,7 @@ set(live_effects_SRC
lpe-transform_2pts.h
lpe-circle_with_radius.h
lpe-clone-original.h
+ lpe-connector.h
lpe-constructgrid.h
lpe-copy_rotate.h
lpe-curvestitch.h
@@ -164,6 +167,7 @@ set(live_effects_SRC
parameter/array.h
parameter/bool.h
+ parameter/connectorpointarray.h
parameter/colorpicker.h
parameter/hidden.h
parameter/enum.h
diff --git a/src/live_effects/effect-enum.h b/src/live_effects/effect-enum.h
index 166e4d3fa98d213a85bbec3d405916b4c9864e25..8e1394c8016d5749acfea6262833be4d1c007ac4 100644
--- a/src/live_effects/effect-enum.h
+++ b/src/live_effects/effect-enum.h
@@ -16,7 +16,8 @@ namespace Inkscape {
namespace LivePathEffect {
//Please fill in the same order than in effect.cpp:98
-enum EffectType {
+enum EffectType
+{
BEND_PATH = 0,
GEARS,
PATTERN_ALONG_PATH,
@@ -65,6 +66,9 @@ enum EffectType {
PARALLEL,
PERP_BISECTOR,
TANGENT_TO_CURVE,
+ SLICE,
+ CONNECTOR,
+ // PUT NEW LPE BEFORE EXPERIMENTAL
DOEFFECTSTACK_TEST,
DYNASTROKE,
LATTICE,
@@ -72,7 +76,6 @@ enum EffectType {
RECURSIVE_SKELETON,
TEXT_LABEL,
EMBRODERY_STITCH,
- SLICE,
INVALID_LPE // This must be last (I made it such that it is not needed anymore I think..., Don't trust on it being
// last. - johan)
};
diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp
index 4863a62a1973642f221bf41850c445edd463b2d3..6e03edf23088021ff14cf15547bf41cb4239fe34 100644
--- a/src/live_effects/effect.cpp
+++ b/src/live_effects/effect.cpp
@@ -13,6 +13,12 @@
//#define LPE_ENABLE_TEST_EFFECTS //uncomment for toy effects
// include effects:
+#include
+#include
+#include
+#include
+
+#include "display/curve.h"
#include "live_effects/lpe-angle_bisector.h"
#include "live_effects/lpe-attach-path.h"
#include "live_effects/lpe-bendpath.h"
@@ -22,6 +28,7 @@
#include "live_effects/lpe-circle_3pts.h"
#include "live_effects/lpe-circle_with_radius.h"
#include "live_effects/lpe-clone-original.h"
+#include "live_effects/lpe-connector.h"
#include "live_effects/lpe-constructgrid.h"
#include "live_effects/lpe-copy_rotate.h"
#include "live_effects/lpe-curvestitch.h"
@@ -69,28 +76,18 @@
#include "live_effects/lpe-text_label.h"
#include "live_effects/lpe-transform_2pts.h"
#include "live_effects/lpe-vonkoch.h"
-
#include "live_effects/lpeobject.h"
-
-#include "xml/node-event-vector.h"
-#include "xml/sp-css-attr.h"
-
-#include "display/curve.h"
#include "message-stack.h"
+#include "object/sp-defs.h"
+#include "object/sp-root.h"
+#include "object/sp-shape.h"
#include "path-chemistry.h"
#include "ui/icon-loader.h"
#include "ui/tools-switch.h"
#include "ui/tools/node-tool.h"
#include "ui/tools/pen-tool.h"
-
-#include "object/sp-defs.h"
-#include "object/sp-root.h"
-#include "object/sp-shape.h"
-
-#include
-#include
-#include
-#include
+#include "xml/node-event-vector.h"
+#include "xml/sp-css-attr.h"
namespace Inkscape {
@@ -676,7 +673,7 @@ const EnumEffectData LPETypeData[] = {
true ,//on_group
false ,//on_image
false ,//on_text
- true ,//experimental
+ true ,//experimental
},
{
CIRCLE_WITH_RADIUS,
@@ -690,7 +687,7 @@ const EnumEffectData LPETypeData[] = {
true ,//on_group
false ,//on_image
false ,//on_text
- true ,//experimental
+ true ,//experimental
},
{
CIRCLE_3PTS,
@@ -704,7 +701,7 @@ const EnumEffectData LPETypeData[] = {
true ,//on_group
false ,//on_image
false ,//on_text
- true ,//experimental
+ true ,//experimental
},
{
EXTRUDE,
@@ -718,7 +715,7 @@ const EnumEffectData LPETypeData[] = {
true ,//on_group
false ,//on_image
false ,//on_text
- true ,//experimental
+ true ,//experimental
},
{
LINE_SEGMENT,
@@ -732,7 +729,7 @@ const EnumEffectData LPETypeData[] = {
true ,//on_group
false ,//on_image
false ,//on_text
- true ,//experimental
+ true ,//experimental
},
{
PARALLEL,
@@ -746,7 +743,7 @@ const EnumEffectData LPETypeData[] = {
true ,//on_group
false ,//on_image
false ,//on_text
- true ,//experimental
+ true ,//experimental
},
{
PERP_BISECTOR,
@@ -760,7 +757,7 @@ const EnumEffectData LPETypeData[] = {
true ,//on_group
false ,//on_image
false ,//on_text
- true ,//experimental
+ true ,//experimental
},
{
TANGENT_TO_CURVE,
@@ -774,7 +771,7 @@ const EnumEffectData LPETypeData[] = {
true ,//on_group
false ,//on_image
false ,//on_text
- true ,//experimental
+ true ,//experimental
},
/* 1.1 */
{
@@ -786,7 +783,21 @@ const EnumEffectData LPETypeData[] = {
N_("Slices the item into parts. It can also be applied multiple times.") ,//description
true ,//on_path
true ,//on_shape
- true ,//on_group
+ true ,//on_group
+ false ,//on_image
+ false ,//on_text
+ false ,//experimental
+ },
+ {
+ CONNECTOR,
+ N_("Connector") ,//label
+ "connector" ,//key
+ "lpe-connector" ,//icon
+ "Connector" ,//untranslated name
+ N_("You can add this LPE to groups to add target points to it or to paths to add crossing gaps.") ,//description
+ false ,//on_path
+ false ,//on_shape
+ true ,//on_group
false ,//on_image
false ,//on_text
false ,//experimental
@@ -804,7 +815,7 @@ const EnumEffectData LPETypeData[] = {
true ,//on_group
false ,//on_image
false ,//on_text
- true ,//experimental
+ true ,//experimental
},
{
DYNASTROKE,
@@ -818,7 +829,7 @@ const EnumEffectData LPETypeData[] = {
true ,//on_group
false ,//on_image
false ,//on_text
- true ,//experimental
+ true ,//experimental
},
{
LATTICE,
@@ -832,7 +843,7 @@ const EnumEffectData LPETypeData[] = {
true ,//on_group
false ,//on_image
false ,//on_text
- true ,//experimental
+ true ,//experimental
},
{
PATH_LENGTH,
@@ -846,7 +857,7 @@ const EnumEffectData LPETypeData[] = {
true ,//on_group
false ,//on_image
false ,//on_text
- true ,//experimental
+ true ,//experimental
},
{
RECURSIVE_SKELETON,
@@ -860,7 +871,7 @@ const EnumEffectData LPETypeData[] = {
true ,//on_group
false ,//on_image
false ,//on_text
- true ,//experimental
+ true ,//experimental
},
{
TEXT_LABEL,
@@ -874,7 +885,7 @@ const EnumEffectData LPETypeData[] = {
true ,//on_group
false ,//on_image
false ,//on_text
- true ,//experimental
+ true ,//experimental
},
{
EMBRODERY_STITCH,
@@ -1082,6 +1093,9 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
case SLICE:
neweffect = static_cast(new LPESlice(lpeobj));
break;
+ case CONNECTOR:
+ neweffect = static_cast(new LPEConnector(lpeobj));
+ break;
default:
g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr);
neweffect = nullptr;
@@ -1549,6 +1563,18 @@ Effect::update_helperpath() {
}
}
+/**
+ * Get selection
+ */
+Inkscape::Selection *Effect::getSelection()
+{
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (desktop) {
+ return desktop->getSelection();
+ }
+ return nullptr;
+}
+
/**
* This *creates* a new widget, management of deletion should be done by the caller
*/
diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h
index 2b68661347b7e1fe050c636dd72ba0154e7acc0d..60dbe227ddb10a696dec5be7a8adf091e39cc921 100644
--- a/src/live_effects/effect.h
+++ b/src/live_effects/effect.h
@@ -8,15 +8,16 @@
* Released under GNU GPL v2+, read the file 'COPYING' for more information.
*/
-#include "effect-enum.h"
-#include "parameter/bool.h"
-#include "parameter/hidden.h"
-#include "ui/widget/registry.h"
#include <2geom/forward.h>
#include
#include
#include
+#include "effect-enum.h"
+#include "parameter/bool.h"
+#include "parameter/hidden.h"
+#include "selection.h"
+#include "ui/widget/registry.h"
#define LPE_CONVERSION_TOLERANCE 0.01 // FIXME: find good solution for this.
@@ -122,6 +123,7 @@ public:
void addHandles(KnotHolder *knotholder, SPItem *item);
std::vector getCanvasIndicators(SPLPEItem const* lpeitem);
void update_helperpath();
+ Inkscape::Selection *getSelection();
bool has_exception;
inline bool providesOwnFlashPaths() const {
diff --git a/src/live_effects/lpe-clone-original.cpp b/src/live_effects/lpe-clone-original.cpp
index 6c357ba34ba48b5d0d4b442dafe4695539ab0a4d..c118da0b83d1e14717f6ae1e7079ff45607d74cf 100644
--- a/src/live_effects/lpe-clone-original.cpp
+++ b/src/live_effects/lpe-clone-original.cpp
@@ -6,23 +6,22 @@
*/
#include "live_effects/lpe-clone-original.h"
-#include "live_effects/lpe-spiro.h"
+
+#include "display/curve.h"
#include "live_effects/lpe-bspline.h"
-#include "live_effects/lpeobject.h"
+#include "live_effects/lpe-spiro.h"
#include "live_effects/lpeobject-reference.h"
-#include "display/curve.h"
-#include "svg/path-string.h"
-#include "svg/svg.h"
-
-#include "ui/tools-switch.h"
-#include "ui/tools/node-tool.h"
+#include "live_effects/lpeobject.h"
#include "object/sp-clippath.h"
#include "object/sp-mask.h"
#include "object/sp-path.h"
#include "object/sp-shape.h"
+#include "object/sp-symbol.h"
#include "object/sp-text.h"
-#include "display/curve.h"
-
+#include "svg/path-string.h"
+#include "svg/svg.h"
+#include "ui/tools-switch.h"
+#include "ui/tools/node-tool.h"
#include "xml/sp-css-attr.h"
// TODO due to internal breakage in glibmm headers, this must be last:
@@ -156,13 +155,45 @@ LPECloneOriginal::cloneAttrbutes(SPObject *origin, SPObject *dest, const gchar *
index++;
}
}
+ SPSymbol *symbol_origin = dynamic_cast(origin);
+ group_dest = dynamic_cast(dest);
+ if (symbol_origin && group_dest && symbol_origin->getItemCount() != group_dest->getItemCount()) {
+ sp_lpe_item_enable_path_effects(sp_lpe_item, false);
+ std::vector childs = symbol_origin->childList(true);
+ std::vector childsdest = group_dest->childList(true);
+ for (auto &child : childsdest) {
+ child->deleteObject(true);
+ }
+ for (auto &child : childs) {
+ Inkscape::XML::Document *xml_doc = document->getReprDoc();
+ Inkscape::XML::Node *dup = child->getRepr()->duplicate(xml_doc);
+ dest->getRepr()->appendChild(dup);
+ Inkscape::GC::release(dup);
+ }
+ sp_lpe_item_enable_path_effects(sp_lpe_item, true);
+ }
+ if (symbol_origin && group_dest) {
+ std::vector childs = group_origin->childList(true);
+ size_t index = 0;
+ for (auto &child : childs) {
+ SPObject *dest_child = group_dest->nthChild(index);
+ cloneAttrbutes(child, dest_child, attributes, css_properties, init);
+ index++;
+ }
+ }
//Attributes
SPShape * shape_origin = dynamic_cast(origin);
SPShape * shape_dest = dynamic_cast(dest);
SPItem * item_origin = dynamic_cast(origin);
SPItem * item_dest = dynamic_cast(dest);
- SPMask * mask_origin = dynamic_cast(item_origin->getMaskObject());
- SPMask * mask_dest = dynamic_cast(item_dest->getMaskObject());
+ SPMask *mask_origin = nullptr;
+ SPMask *mask_dest = nullptr;
+ if (item_origin) {
+ mask_origin = dynamic_cast(item_origin->getMaskObject());
+ }
+ if (item_dest) {
+ mask_dest = dynamic_cast(item_dest->getMaskObject());
+ }
if(mask_origin && mask_dest) {
std::vector mask_list = mask_origin->childList(true);
std::vector mask_list_dest = mask_dest->childList(true);
@@ -175,9 +206,15 @@ LPECloneOriginal::cloneAttrbutes(SPObject *origin, SPObject *dest, const gchar *
}
}
}
-
- SPClipPath *clippath_origin = SP_ITEM(origin)->getClipObject();
- SPClipPath *clippath_dest = SP_ITEM(dest)->getClipObject();
+
+ SPClipPath *clippath_origin = nullptr;
+ SPClipPath *clippath_dest = nullptr;
+ if (item_origin) {
+ clippath_origin = dynamic_cast(item_origin->getClipObject());
+ }
+ if (item_dest) {
+ clippath_dest = dynamic_cast(item_dest->getClipObject());
+ }
if(clippath_origin && clippath_dest) {
std::vector clippath_list = clippath_origin->childList(true);
std::vector clippath_list_dest = clippath_dest->childList(true);
@@ -190,6 +227,40 @@ LPECloneOriginal::cloneAttrbutes(SPObject *origin, SPObject *dest, const gchar *
}
}
}
+ SPCSSAttr *css_origin = sp_repr_css_attr_new();
+ sp_repr_css_attr_add_from_string(css_origin, origin->getRepr()->attribute("style"));
+ SPCSSAttr *css_dest = sp_repr_css_attr_new();
+ sp_repr_css_attr_add_from_string(css_dest, dest->getRepr()->attribute("style"));
+ if (init) {
+ css_dest = css_origin;
+ }
+ gchar **styleattarray = g_strsplit(old_css_properties.c_str(), ",", 0);
+ gchar **styleiter = styleattarray;
+ while (*styleiter != nullptr) {
+ const char *attribute = (*styleiter);
+ if (strlen(attribute)) {
+ sp_repr_css_set_property(css_dest, attribute, nullptr);
+ }
+ styleiter++;
+ }
+ styleattarray = g_strsplit(css_properties, ",", 0);
+ styleiter = styleattarray;
+ while (*styleiter != nullptr) {
+ const char *attribute = (*styleiter);
+ if (strlen(attribute)) {
+ const char *origin_attribute = sp_repr_css_property(css_origin, attribute, "");
+ if (!strlen(origin_attribute)) { //==0
+ sp_repr_css_set_property(css_dest, attribute, nullptr);
+ } else {
+ sp_repr_css_set_property(css_dest, attribute, origin_attribute);
+ }
+ }
+ styleiter++;
+ }
+ g_strfreev(styleattarray);
+ Glib::ustring css_str;
+ sp_repr_css_write_string(css_dest, css_str);
+ dest->getRepr()->setAttributeOrRemoveIfEmpty("style", css_str);
gchar ** attarray = g_strsplit(old_attributes.c_str(), ",", 0);
gchar ** iter = attarray;
while (*iter != nullptr) {
@@ -203,6 +274,7 @@ LPECloneOriginal::cloneAttrbutes(SPObject *origin, SPObject *dest, const gchar *
iter = attarray;
while (*iter != nullptr) {
const char* attribute = (*iter);
+ std::cout << attribute << std::endl;
if (strlen(attribute) && shape_dest && shape_origin) {
if (std::strcmp(attribute, "d") == 0) {
std::unique_ptr c;
@@ -252,40 +324,6 @@ LPECloneOriginal::cloneAttrbutes(SPObject *origin, SPObject *dest, const gchar *
dest->getRepr()->setAttribute("transform", origin->getRepr()->attribute("transform"));
}
g_strfreev (attarray);
- SPCSSAttr *css_origin = sp_repr_css_attr_new();
- sp_repr_css_attr_add_from_string(css_origin, origin->getRepr()->attribute("style"));
- SPCSSAttr *css_dest = sp_repr_css_attr_new();
- sp_repr_css_attr_add_from_string(css_dest, dest->getRepr()->attribute("style"));
- if (init) {
- css_dest = css_origin;
- }
- gchar ** styleattarray = g_strsplit(old_css_properties.c_str(), ",", 0);
- gchar ** styleiter = styleattarray;
- while (*styleiter != nullptr) {
- const char* attribute = (*styleiter);
- if (strlen(attribute)) {
- sp_repr_css_set_property (css_dest, attribute, nullptr);
- }
- styleiter++;
- }
- styleattarray = g_strsplit(css_properties, ",", 0);
- styleiter = styleattarray;
- while (*styleiter != nullptr) {
- const char* attribute = (*styleiter);
- if (strlen(attribute)) {
- const char* origin_attribute = sp_repr_css_property(css_origin, attribute, "");
- if (!strlen(origin_attribute)) { //==0
- sp_repr_css_set_property (css_dest, attribute, nullptr);
- } else {
- sp_repr_css_set_property (css_dest, attribute, origin_attribute);
- }
- }
- styleiter++;
- }
- g_strfreev (styleattarray);
- Glib::ustring css_str;
- sp_repr_css_write_string(css_dest,css_str);
- dest->getRepr()->setAttributeOrRemoveIfEmpty("style", css_str);
}
void
@@ -302,6 +340,7 @@ LPECloneOriginal::doBeforeEffect (SPLPEItem const* lpeitem){
}
SPText *text_origin = dynamic_cast(orig);
SPGroup *group_origin = dynamic_cast(orig);
+ SPSymbol *symbol_origin = dynamic_cast(orig);
SPItem *dest = dynamic_cast(sp_lpe_item);
const gchar * id = orig->getId();
bool init = g_strcmp0(id, linked) != 0 && !is_load;
@@ -323,6 +362,9 @@ LPECloneOriginal::doBeforeEffect (SPLPEItem const* lpeitem){
if (attr.size() && attributes_str.empty()) {
attr.erase (attr.size()-1, 1);
}
+ if (symbol_origin) {
+ attr += ",style";
+ }
auto css_properties_str = css_properties.param_getSVGValue();
Glib::ustring style_attr = "";
if (style_attr.size() && css_properties_str.empty()) {
@@ -330,10 +372,10 @@ LPECloneOriginal::doBeforeEffect (SPLPEItem const* lpeitem){
}
style_attr += css_properties_str + ",";
cloneAttrbutes(orig, dest, attr.c_str(), style_attr.c_str(), init);
- if (!group_origin && linkeditem.last_transform.isTranslation()) {
+ if (!group_origin && !symbol_origin && linkeditem.last_transform.isTranslation()) {
Geom::Affine orig = sp_lpe_item->transform;
sp_lpe_item->transform *= orig.inverse() * linkeditem.last_transform.inverse() * orig;
- linkeditem.last_transform = Geom::identity();
+ linkeditem.last_transform = Geom::identity();
}
old_css_properties = css_properties.param_getSVGValue();
old_attributes = attributes.param_getSVGValue();
@@ -384,6 +426,21 @@ LPECloneOriginal::doEffect (SPCurve * curve)
}
}
+void LPECloneOriginal::doAfterEffect(SPLPEItem const *lpeitem, SPCurve *curve)
+{
+ if (linkeditem.linksToItem()) {
+ SPItem *orig = dynamic_cast(linkeditem.getObject());
+ if (!orig) {
+ return;
+ }
+ SPSymbol *symbol_origin = dynamic_cast(orig);
+ if (symbol_origin) {
+ sp_lpe_item->removeAllPathEffects(true);
+ Effect::createAndApply(CONNECTOR, sp_lpe_item->document, sp_lpe_item);
+ }
+ }
+}
+
} // namespace LivePathEffect
} /* namespace Inkscape */
diff --git a/src/live_effects/lpe-clone-original.h b/src/live_effects/lpe-clone-original.h
index 0ef479f3d0a0e0916c60fc1dcfe6e384c6fbbae2..8793b6a4f7ea77f5176446c34fd1b539fa859d3c 100644
--- a/src/live_effects/lpe-clone-original.h
+++ b/src/live_effects/lpe-clone-original.h
@@ -28,6 +28,7 @@ public:
~LPECloneOriginal() override;
void doEffect (SPCurve * curve) override;
void doBeforeEffect (SPLPEItem const* lpeitem) override;
+ void doAfterEffect(SPLPEItem const *lpeitem, SPCurve *curve) override;
void cloneAttrbutes(SPObject *origin, SPObject *dest, const gchar * attributes, const gchar * css_properties, bool init);
void modified(SPObject */*obj*/, guint /*flags*/);
Gtk::Widget *newWidget() override;
diff --git a/src/live_effects/lpe-connector.cpp b/src/live_effects/lpe-connector.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..03f69fd8be80255d97f0c83d4b8d0bded94efb27
--- /dev/null
+++ b/src/live_effects/lpe-connector.cpp
@@ -0,0 +1,343 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** \file
+ * LPE implementation: slices a path with respect to a given line.
+ */
+/*
+ * Authors:
+ * Jabiertxof
+ *
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/lpe-connector.h"
+
+#include "display/curve.h"
+#include "document-undo.h"
+#include "live_effects/lpeobject.h"
+#include "object/sp-conn-end.h"
+#include "object/sp-conn-router.h"
+#include "object/sp-item-group.h"
+#include "object/sp-path.h"
+
+
+// TODO due to internal breakage in glibmm headers, this must be last:
+#include
+
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+LPEConnector::LPEConnector(LivePathEffectObject *lpeobject) :
+ Effect(lpeobject),
+ connector_points(_("Connector points"), _("Connector Points"), "connector_points", &wr, this),
+ crossing_gap(_("Crossing gap:"), _("Gap between crossings"), "crossing_gap", &wr, this, 5.0),
+ extrem_gap(_("Extrem gap:"), _("Extrem gap"), "extrem_gap", &wr, this, 0.0),
+ nocrossone(_("Keep one crossing unchanged"), _("Dont cross gap in one connector"), "nocrossone", &wr, this, true),
+ crossing_markers(_("Split connector on crossings"), _("Split connector on crossings to allow middle markers"), "crossing_markers", &wr, this, false),
+ linkstyle(_("One style"), _("Keep one style in all connectors"), "linkstyle", &wr, this, true)
+{
+ registerParameter(&connector_points);
+ registerParameter(&crossing_gap);
+ registerParameter(&extrem_gap);
+ registerParameter(&nocrossone);
+ registerParameter(&crossing_markers);
+ registerParameter(&linkstyle);
+ crossing_gap.param_set_range(0, Geom::infinity());
+ crossing_gap.param_set_increments(1, 5);
+ crossing_gap.param_set_digits(4);
+ extrem_gap.param_set_range(0, Geom::infinity());
+ extrem_gap.param_set_increments(1, 5);
+ extrem_gap.param_set_digits(4);
+}
+
+LPEConnector::~LPEConnector() = default;
+
+Gtk::Widget *LPEConnector::newWidget()
+{
+ // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
+ Gtk::VBox *vbox = Gtk::manage(new Gtk::VBox(Effect::newWidget()));
+ vbox->set_border_width(5);
+ vbox->set_homogeneous(false);
+ vbox->set_spacing(6);
+ SPGroup *group = dynamic_cast(sp_lpe_item);
+ if (!group) {
+ std::vector::iterator it = param_vector.begin();
+ while (it != param_vector.end()) {
+ if ((*it)->widget_is_visible) {
+ Parameter *param = *it;
+ if (param->param_key != "connector_points") {
+ Gtk::Widget *widg = dynamic_cast(param->param_newWidget());
+ Glib::ustring *tip = param->param_getTooltip();
+ if (widg) {
+ vbox->pack_start(*widg, true, true, 2);
+ if (tip) {
+ widg->set_tooltip_text(*tip);
+ } else {
+ widg->set_tooltip_text("");
+ widg->set_has_tooltip(false);
+ }
+ }
+ }
+ }
+ ++it;
+ }
+ if (Gtk::Widget *widg = defaultParamSet()) {
+ vbox->pack_start(*widg, true, true, 2);
+ }
+ }
+ return dynamic_cast(vbox);
+}
+
+void LPEConnector::doOnApply(SPLPEItem const *lpeitem)
+{
+ if (!sp_lpe_item) {
+ return;
+ }
+ SPGroup *group = dynamic_cast(sp_lpe_item);
+ if (group) {
+ std::vector points;
+ for (auto &child : group->children) {
+ if (child.getAttribute("inkscape:connector")) {
+ SPPath *path = dynamic_cast(&child);
+ if (path) {
+ auto c = path->curve();
+ if (c) {
+ auto p = c->first_point();
+ if (p) {
+ points.push_back(*p);
+ }
+ }
+ }
+ }
+ }
+ connector_points.param_set_and_write_new_value(points);
+ } else {
+ if (sp_lpe_item->countLPEOfType(CONNECTOR) > 1) {
+ sp_lpe_item->removeCurrentPathEffect(false);
+ return;
+ }
+ SPConnRouter *router = getSPDoc()->getRouter();
+ Glib::ustring lpeid = "";
+ if (router && router->get_lpe() == "") {
+ router->set_lpe(Glib::ustring(this->getLPEObj()->getId()));
+ } else {
+ lpeid = sp_lpe_item->getAttribute("inkscape:path-effect");
+ sp_lpe_item->setAttribute("inkscape:path-effect", (Glib::ustring("#") + router->get_lpe()).c_str());
+ }
+ SPPath *path = dynamic_cast(sp_lpe_item);
+ sp_conn_redraw_path(path);
+ if (SPObject *elemref = getSPDoc()->getObjectById(lpeid.c_str())) {
+ elemref->deleteObject(true);
+ }
+ }
+}
+
+void LPEConnector::doOnRemove(SPLPEItem const * /*lpeitem*/)
+{
+ // set "keep paths" hook on sp-lpe-item.cpp
+ if (keep_paths) {
+ processObjects(LPE_TO_OBJECTS);
+ items.clear();
+ return;
+ }
+ processObjects(LPE_ERASE);
+}
+
+void LPEConnector::doBeforeEffect(SPLPEItem const *lpeitem)
+{
+ SPGroup *group = dynamic_cast(sp_lpe_item);
+ if (group) {
+ original_bbox(lpeitem, false, true);
+ Geom::Point center(boundingbox_X.middle(), boundingbox_Y.middle());
+ std::vector points = connector_points.data();
+ Inkscape::XML::Document *xml_doc = getSPDoc()->getReprDoc();
+ if (points.empty()) {
+ Glib::ustring id = "connector-0-";
+ id += this->lpeobj->getId();
+ items.clear();
+ items.push_back(id);
+ Inkscape::XML::Node *resultnode = xml_doc->createElement("svg:path");
+ resultnode->setAttribute("inkscape:connector", "true");
+ Inkscape::SVGOStringStream os;
+ os << "M " << center << " l 0 0";
+ resultnode->setAttribute("d", os.str().c_str());
+ resultnode->setAttribute("id", id.c_str());
+ points.push_back(center);
+ connector_points.param_set_and_write_new_value(points);
+ } else {
+ std::vector points = connector_points.data();
+ Inkscape::SVGOStringStream os;
+ size_t prevsize = items.size();
+ items.clear();
+ SPGroup *group = dynamic_cast(sp_lpe_item);
+ unsigned int i;
+ for (i = 0; i < points.size(); ++i) {
+ Glib::ustring id = "connector-";
+ id += Glib::ustring::format(i);
+ id += "-";
+ id += this->lpeobj->getId();
+ items.push_back(id);
+ SPObject *elemref = getSPDoc()->getObjectById(id.c_str());
+ Inkscape::XML::Node *phantom = nullptr;
+ if (elemref) {
+ phantom = elemref->getRepr();
+ } else {
+ phantom = xml_doc->createElement("svg:path");
+ phantom->setAttribute("inkscape:connector", "true");
+ phantom->setAttribute("id", id.c_str());
+ elemref = group->appendChildRepr(phantom);
+ Inkscape::GC::release(phantom);
+ }
+ Inkscape::SVGOStringStream os;
+ os << "M " << points[i] << " l 0 0";
+ phantom->setAttribute("d", os.str().c_str());
+ }
+ while (i < prevsize) {
+ Glib::ustring id = "connector-";
+ id += Glib::ustring::format(i);
+ id += "-";
+ id += this->lpeobj->getId();
+ items.push_back(id);
+ SPObject *elemref = getSPDoc()->getObjectById(id.c_str());
+ if (elemref) {
+ elemref->deleteObject(true);
+ }
+ ++i;
+ }
+ }
+ } else {
+ if (crossing_markers && crossing_gap > 0) {
+ crossing_gap.param_set_value(0);
+ crossing_gap.write_to_SVG();
+ }
+ SPConnRouter *router = getSPDoc()->getRouter();
+ if (router) {
+ router->set_lpe(Glib::ustring(this->getLPEObj()->getId()));
+ if (linkstyle) {
+ router->apply_style(sp_lpe_item->getAttribute("style"));
+ }
+ if (router->get_lpe() != "" &&
+ (Glib::ustring("#") + router->get_lpe()) != sp_lpe_item->getAttribute("inkscape:path-effect")) {
+ sp_lpe_item->setAttribute("inkscape:path-effect", (Glib::ustring("#") + router->get_lpe()).c_str());
+ }
+ }
+ }
+}
+
+bool sp_reorder_along(Geom::Point a, Geom::Point b, Geom::Point c)
+{
+ return Geom::distance(a, c) < Geom::distance(b, c);
+}
+
+Geom::PathVector LPEConnector::doEffect_path(Geom::PathVector const &path_in)
+{
+ if (path_in.empty()) {
+ return path_in;
+ }
+ Geom::PathVector path_out = path_in;
+ SPPath *path = dynamic_cast(sp_lpe_item);
+ SPGroup *group = dynamic_cast(sp_lpe_item);
+ if (group) {
+ return path_out;
+ } else if (path) {
+ SPConnRouter *router = getSPDoc()->getRouter();
+ std::vector points = connector_points.data();
+ Geom::Affine i2d = sp_lpe_item->i2doc_affine();
+ Geom::Path &pathin = path_out[0];
+ Geom::Coord sizeopen = pathin.size_open();
+ if (extrem_gap) {
+ double t = 0;
+ double length_part = pathin.front().length();
+ if (length_part && length_part > extrem_gap) {
+ t = extrem_gap / length_part;
+ }
+ double t1 = 0;
+ length_part = pathin.back_open().length();
+ if (length_part && length_part > extrem_gap) {
+ t1 = 1 - (extrem_gap / length_part);
+ }
+ if (t && t1) {
+ pathin = pathin.portion(t, (sizeopen - 1) + t1);
+ }
+ }
+ std::vector pointsclean;
+ for (auto point : points) {
+ point *= i2d.inverse();
+ Geom::Coord distance;
+ boost::optional pos = path_out.nearestTime(point, &distance);
+ if (distance < 0.01 && pos) {
+ pointsclean.push_back(point);
+ }
+ }
+ sort(pointsclean.begin(), pointsclean.end(), bind(sp_reorder_along, _1, _2, path_out.initialPoint()));
+ for (auto point : pointsclean) {
+ Geom::Coord distance;
+ boost::optional pos = path_out.nearestTime(point, &distance);
+ if (distance < 0.01 && pos) {
+ if (nocrossone && router && router->pairs_at_point(point).first &&
+ router->pairs_at_point(point).first->_path == sp_lpe_item) {
+ continue;
+ }
+ if (path->connEndPair.getContinuous()) {
+ //continue;
+ }
+ Geom::Path &pathin = path_out[(*pos).path_index];
+ Geom::Coord sizeopen = pathin.size_open();
+ Geom::Path startpath = pathin.portion(0, (*pos).curve_index + (*pos).t);
+ Geom::Path endpath = pathin.portion((*pos).curve_index + (*pos).t, sizeopen);
+ if (crossing_markers) {
+ startpath.append(endpath);
+ path_out.push_back(startpath);
+ path_out.erase(path_out.begin() + (*pos).path_index);
+ continue;
+ }
+ if (crossing_gap == 0) {
+ continue;
+ }
+ double t = 0;
+ double length_part = startpath.back_open().length();
+ if (length_part && length_part > crossing_gap) {
+ t = 1 - (crossing_gap / length_part);
+ if (t > 0) {
+ startpath = startpath.portion(0, t);
+ } else {
+ t = 0;
+ }
+ }
+ double t2 = 0;
+ length_part = endpath.back_open().length();
+ if (length_part && length_part > crossing_gap) {
+ t2 = crossing_gap / length_part;
+ if (t2 < endpath.size_open()) {
+ endpath = endpath.portion(t2, endpath.size_open());
+ } else {
+ t2 = 0;
+ }
+ }
+ if (t) {
+ path_out.push_back(startpath);
+ }
+ if (t2 && t) {
+ path_out.push_back(endpath);
+ }
+ path_out.erase(path_out.begin() + (*pos).path_index);
+ }
+ }
+ }
+ return path_out;
+}
+
+} // namespace LivePathEffect
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/lpe-connector.h b/src/live_effects/lpe-connector.h
new file mode 100644
index 0000000000000000000000000000000000000000..3e4170a6021795134abb366652e11153b75d72f7
--- /dev/null
+++ b/src/live_effects/lpe-connector.h
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LPE_CONNECTOR_H
+#define INKSCAPE_LPE_CONNECTOR_H
+
+/** \file
+ * LPE implementation: mirrors a path with respect to a given line.
+ */
+/*
+ * Authors:
+ * Maximilian Albert
+ * Johan Engelen
+ * Jabiertxof
+ *
+ * Copyright (C) Johan Engelen 2007
+ * Copyright (C) Maximilin Albert 2008
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/effect.h"
+#include "live_effects/lpegroupbbox.h"
+#include "live_effects/lpeobject-reference.h"
+#include "live_effects/lpeobject.h"
+#include "live_effects/parameter/bool.h"
+#include "live_effects/parameter/connectorpointarray.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+class LPEConnector
+ : public Effect
+ , GroupBBoxEffect
+{
+public:
+ LPEConnector(LivePathEffectObject *lpeobject);
+ ~LPEConnector() override;
+ void doOnApply(SPLPEItem const *lpeitem) override;
+ void doBeforeEffect(SPLPEItem const *lpeitem) override;
+ Gtk::Widget *newWidget() override;
+ void doOnRemove(SPLPEItem const * /*lpeitem*/) override;
+ Geom::PathVector doEffect_path(Geom::PathVector const &path_in) override;
+ ConnectorPointArrayParam connector_points;
+
+private:
+ ScalarParam crossing_gap;
+ ScalarParam extrem_gap;
+ BoolParam crossing_markers;
+ BoolParam nocrossone;
+ BoolParam linkstyle;
+ LPEConnector(const LPEConnector &) = delete;
+ LPEConnector &operator=(const LPEConnector &) = delete;
+};
+
+} // namespace LivePathEffect
+} // namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/connectorpointarray.cpp b/src/live_effects/parameter/connectorpointarray.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..38954c07eb3b78f44ab6339eb7955993c9bd3c48
--- /dev/null
+++ b/src/live_effects/parameter/connectorpointarray.cpp
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Johan Engelen 2007
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "connectorpointarray.h"
+
+#include
+
+#include "live_effects/effect.h"
+#include "live_effects/lpe-connector.h"
+#include "ui/knot/knot-holder.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+ConnectorPointArrayParam::ConnectorPointArrayParam(const Glib::ustring &label, const Glib::ustring &tip,
+ const Glib::ustring &key, Inkscape::UI::Widget::Registry *wr,
+ Effect *effect)
+ : ArrayParam(label, tip, key, wr, effect, 0)
+{
+ knot_shape = Inkscape::CANVAS_ITEM_CTRL_SHAPE_DIAMOND;
+ knot_mode = Inkscape::CANVAS_ITEM_CTRL_MODE_XOR;
+ knot_color = 0xff88ff00;
+}
+
+ConnectorPointArrayParam::~ConnectorPointArrayParam() = default;
+
+Gtk::Widget *ConnectorPointArrayParam::param_newWidget()
+{
+ return nullptr;
+}
+
+void ConnectorPointArrayParam::set_oncanvas_looks(Inkscape::CanvasItemCtrlShape shape,
+ Inkscape::CanvasItemCtrlMode mode, guint32 color)
+{
+ knot_shape = shape;
+ knot_mode = mode;
+ knot_color = color;
+}
+
+ConnectorPointArrayParamKnotHolderEntity::ConnectorPointArrayParamKnotHolderEntity(ConnectorPointArrayParam *p,
+ unsigned int index)
+ : _pparam(p)
+ , _index(index)
+{}
+
+void ConnectorPointArrayParamKnotHolderEntity::knot_set(Geom::Point const &p, Geom::Point const & /*origin*/,
+ guint state)
+{
+ using namespace Geom;
+
+ if (!valid_index(_index)) {
+ return;
+ }
+ Geom::Point s = snap_knot_position(p, state);
+ _pparam->_vector.at(_index) = s;
+ sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
+}
+
+Geom::Point ConnectorPointArrayParamKnotHolderEntity::knot_get() const
+{
+ using namespace Geom;
+
+ if (!valid_index(_index)) {
+ return Geom::Point(Geom::infinity(), Geom::infinity());
+ }
+ return _pparam->_vector.at(_index);
+}
+
+void ConnectorPointArrayParamKnotHolderEntity::knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin,
+ guint state)
+{
+ _pparam->param_effect->refresh_widgets = true;
+ _pparam->write_to_SVG();
+}
+
+void ConnectorPointArrayParamKnotHolderEntity::knot_click(guint state)
+{
+ if (state & GDK_CONTROL_MASK) {
+ if (state & GDK_MOD1_MASK) {
+ // delete the clicked knot
+ std::vector &vec = _pparam->_vector;
+ if (vec.size() > 1) { // Force don't remove last knot
+ vec.erase(vec.begin() + _index);
+ _pparam->param_set_and_write_new_value(vec);
+ // shift knots down one index
+ for (auto &ent : parent_holder->entity) {
+ ConnectorPointArrayParamKnotHolderEntity *pspa_ent =
+ dynamic_cast(ent);
+ if (pspa_ent && pspa_ent->_pparam == this->_pparam) { // check if the knotentity belongs to this
+ // connectorpointarray parameter
+ if (pspa_ent->_index > this->_index) {
+ --pspa_ent->_index;
+ }
+ }
+ };
+ // temporary hide, when knotholder were recreated it finally drop
+ this->knot->hide();
+ }
+ return;
+ } else {
+ // add a knot to XML
+ std::vector &vec = _pparam->_vector;
+ vec.insert(vec.begin() + _index, 1, vec.at(_index)); // this clicked knot is duplicated
+ _pparam->param_set_and_write_new_value(vec);
+
+ // shift knots up one index
+ for (auto &ent : parent_holder->entity) {
+ ConnectorPointArrayParamKnotHolderEntity *pspa_ent =
+ dynamic_cast(ent);
+ if (pspa_ent && pspa_ent->_pparam == this->_pparam) { // check if the knotentity belongs to this
+ // connectorpointarray parameter
+ if (pspa_ent->_index > this->_index) {
+ ++pspa_ent->_index;
+ }
+ }
+ };
+ // add knot to knotholder
+ ConnectorPointArrayParamKnotHolderEntity *e =
+ new ConnectorPointArrayParamKnotHolderEntity(_pparam, _index + 1);
+ e->create(this->desktop, this->item, parent_holder, Inkscape::CANVAS_ITEM_CTRL_TYPE_LPE, "LPE:Connector",
+ _("Connector control point: drag to move the connector point. Ctrl+click adds a "
+ "control point, Ctrl+Alt+click deletes it"),
+ _pparam->knot_color);
+ parent_holder->add(e);
+ }
+ }
+}
+
+void ConnectorPointArrayParam::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item)
+{
+ for (unsigned int i = 0; i < _vector.size(); ++i) {
+ ConnectorPointArrayParamKnotHolderEntity *e = new ConnectorPointArrayParamKnotHolderEntity(this, i);
+ e->create(nullptr, item, knotholder, Inkscape::CANVAS_ITEM_CTRL_TYPE_LPE, "LPE:Connector",
+ _("Connector control point: drag to move the connector point. Ctrl+click adds a "
+ "control point, Ctrl+Alt+click deletes it"),
+ knot_color);
+ knotholder->add(e);
+ }
+}
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/connectorpointarray.h b/src/live_effects/parameter/connectorpointarray.h
new file mode 100644
index 0000000000000000000000000000000000000000..efe0fe05123f2475e84c54124116e3a1a2a35bff
--- /dev/null
+++ b/src/live_effects/parameter/connectorpointarray.h
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_CONNECTOR_POINT_ARRAY_H
+#define INKSCAPE_LIVEPATHEFFECT_CONNECTOR_POINT_ARRAY_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Copyright (C) Johan Engelen 2007
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <2geom/point.h>
+#include
+
+#include "live_effects/parameter/array.h"
+#include "ui/knot/knot-holder-entity.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class ConnectorPointArrayParam : public ArrayParam
+{
+public:
+ ConnectorPointArrayParam(const Glib::ustring &label, const Glib::ustring &tip, const Glib::ustring &key,
+ Inkscape::UI::Widget::Registry *wr, Effect *effect);
+ ~ConnectorPointArrayParam() override;
+
+ ConnectorPointArrayParam(const ConnectorPointArrayParam &) = delete;
+ ConnectorPointArrayParam &operator=(const ConnectorPointArrayParam &) = delete;
+
+ Gtk::Widget *param_newWidget() override;
+
+ void set_oncanvas_looks(Inkscape::CanvasItemCtrlShape shape, Inkscape::CanvasItemCtrlMode mode, guint32 color);
+
+ bool providesKnotHolderEntities() const override { return true; }
+ void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) override;
+ friend class ConnectorPointArrayParamKnotHolderEntity;
+
+private:
+ Inkscape::CanvasItemCtrlShape knot_shape = Inkscape::CANVAS_ITEM_CTRL_SHAPE_DIAMOND;
+ Inkscape::CanvasItemCtrlMode knot_mode = Inkscape::CANVAS_ITEM_CTRL_MODE_XOR;
+ guint32 knot_color;
+};
+
+class ConnectorPointArrayParamKnotHolderEntity : public KnotHolderEntity
+{
+public:
+ ConnectorPointArrayParamKnotHolderEntity(ConnectorPointArrayParam *p, unsigned int index);
+ ~ConnectorPointArrayParamKnotHolderEntity() override = default;
+
+ void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) override;
+ void knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state) override;
+ Geom::Point knot_get() const override;
+ void knot_click(guint state) override;
+
+ /** Checks whether the index falls within the size of the parameter's vector */
+ bool valid_index(unsigned int index) const { return (_pparam->_vector.size() > index); };
+
+private:
+ ConnectorPointArrayParam *_pparam;
+ unsigned int _index;
+};
+
+} // namespace LivePathEffect
+
+} // namespace Inkscape
+
+#endif
diff --git a/src/object/CMakeLists.txt b/src/object/CMakeLists.txt
index e44d56f5815c4ffb3b5901bba582fd6b3ea9d789..4a19efb8be457da2646bbcee123d3e3e381e3055 100644
--- a/src/object/CMakeLists.txt
+++ b/src/object/CMakeLists.txt
@@ -12,6 +12,7 @@ set(object_SRC
sp-clippath.cpp
sp-conn-end-pair.cpp
sp-conn-end.cpp
+ sp-conn-router.cpp
sp-defs.cpp
sp-desc.cpp
sp-dimensions.cpp
@@ -97,6 +98,7 @@ set(object_SRC
sp-clippath.h
sp-conn-end-pair.h
sp-conn-end.h
+ sp-conn-router.h
sp-defs.h
sp-desc.h
sp-dimensions.h
diff --git a/src/object/object-set.h b/src/object/object-set.h
index 7844237f4e6f5cdc0ca42dd93a89a04548bc3c38..566c414e881a029db57393f7812eaa16d5c869c9 100644
--- a/src/object/object-set.h
+++ b/src/object/object-set.h
@@ -368,7 +368,8 @@ public:
void removeLPESRecursive(bool keep_paths);
void relink();
void cloneOriginal();
- void cloneOriginalPathLPE(bool allow_transforms = false);
+ void cloneOriginalPathLPE(bool allow_transforms = false, SPObject *parent = nullptr,
+ size_t pos = Glib::ustring::npos);
Inkscape::XML::Node* group();
void popFromGroup();
void ungroup();
diff --git a/src/object/sp-conn-end-pair.cpp b/src/object/sp-conn-end-pair.cpp
index c6aa18a48592a1dffdc838034ba03e85f596b56a..3b52dcc8ef2f72ad50abad9ad97a47e5e6b7cafc 100644
--- a/src/object/sp-conn-end-pair.cpp
+++ b/src/object/sp-conn-end-pair.cpp
@@ -13,26 +13,30 @@
*/
#include
-#include
#include
+#include
+#include "3rdparty/adaptagrams/libavoid/router.h"
#include "attributes.h"
-#include "sp-conn-end.h"
-#include "uri.h"
#include "display/curve.h"
-#include "xml/repr.h"
-#include "sp-path.h"
-#include "sp-use.h"
-#include "3rdparty/adaptagrams/libavoid/router.h"
#include "document.h"
+#include "inkscape.h"
+#include "live_effects/effect-enum.h"
+#include "live_effects/lpe-connector.h"
+#include "sp-conn-end.h"
+#include "sp-conn-router.h"
#include "sp-item-group.h"
-
+#include "sp-path.h"
+#include "sp-use.h"
+#include "uri.h"
+#include "xml/repr.h"
SPConnEndPair::SPConnEndPair(SPPath *const owner)
: _path(owner)
, _connRef(nullptr)
, _connType(SP_CONNECTOR_NOAVOID)
, _connCurvature(0.0)
+ , _connContinuous(true)
, _transformed_connection()
{
for (unsigned handle_ix = 0; handle_ix <= 1; ++handle_ix) {
@@ -50,6 +54,13 @@ SPConnEndPair::~SPConnEndPair()
delete handle_ix;
handle_ix = nullptr;
}
+ SPDocument *document = _path->document;
+ if (document) {
+ SPConnRouter *router = document->getRouter();
+ if (router) {
+ router->removeConnEndPair(this);
+ }
+ }
}
void SPConnEndPair::release()
@@ -70,6 +81,7 @@ void SPConnEndPair::release()
if (_connRef && routerInstanceExists) {
_connRef->router()->deleteConnector(_connRef);
+ _path->document->getRouter()->release();
}
_connRef = nullptr;
@@ -84,6 +96,7 @@ void sp_conn_end_pair_build(SPObject *object)
object->readAttr(SPAttr::CONNECTION_END);
object->readAttr(SPAttr::CONNECTION_END_POINT);
object->readAttr(SPAttr::CONNECTOR_CURVATURE);
+ object->readAttr(SPAttr::CONNECTOR_CONTINUOUS);
}
@@ -95,6 +108,24 @@ static void avoid_conn_transformed(Geom::Affine const */*mp*/, SPItem *moved_ite
}
}
+void SPConnEndPair::crossings(const bool optimisedForConnectorType, const bool fromRouter)
+{
+ auto router = _path->document->getRouter();
+ if (router) {
+ if (!fromRouter) {
+ std::vector points = router->crossings();
+ SPLPEItem *lpeitem = dynamic_cast(_path);
+ if (lpeitem) {
+ Inkscape::LivePathEffect::LPEConnector *lpe = dynamic_cast(
+ lpeitem->getPathEffectOfType(Inkscape::LivePathEffect::CONNECTOR));
+ if (lpe) {
+ lpe->connector_points.param_set_and_write_new_value(points);
+ }
+ }
+ _path->document->getRouter()->reroute_router(this);
+ }
+ }
+}
void SPConnEndPair::setAttr(const SPAttr key, gchar const *const value)
{
@@ -105,11 +136,12 @@ void SPConnEndPair::setAttr(const SPAttr key, gchar const *const value)
if (!_connRef) {
_connType = new_conn_type;
- Avoid::Router *router = _path->document->getRouter();
+ Avoid::Router *router = _path->document->getRouter()->getAvoidRouter();
_connRef = new Avoid::ConnRef(router);
_connRef->setRoutingType(new_conn_type == SP_CONNECTOR_POLYLINE ?
Avoid::ConnType_PolyLine : Avoid::ConnType_Orthogonal);
_transformed_connection = _path->connectTransformed(sigc::ptr_fun(&avoid_conn_transformed));
+ _path->document->getRouter()->addConnEndPair(this);
} else if (new_conn_type != _connType) {
_connType = new_conn_type;
_connRef->setRoutingType(new_conn_type == SP_CONNECTOR_POLYLINE ?
@@ -120,6 +152,7 @@ void SPConnEndPair::setAttr(const SPAttr key, gchar const *const value)
_connType = SP_CONNECTOR_NOAVOID;
if (_connRef) {
+ _path->document->getRouter()->removeConnEndPair(this);
_connRef->router()->deleteConnector(_connRef);
_connRef = nullptr;
_transformed_connection.disconnect();
@@ -135,6 +168,15 @@ void SPConnEndPair::setAttr(const SPAttr key, gchar const *const value)
}
}
break;
+ case SPAttr::CONNECTOR_CONTINUOUS:
+ if (value) {
+ _connContinuous = value == "true"?true:false;
+ if (_connRef && _connRef->isInitialised()) {
+ // Redraw the connector, but only if it has been initialised.
+ sp_conn_reroute_path(_path);
+ }
+ }
+ break;
case SPAttr::CONNECTION_START:
this->_connEnd[0]->setAttacherHref(value);
break;
@@ -241,6 +283,11 @@ gdouble SPConnEndPair::getCurvature() const
return _connCurvature;
}
+gboolean SPConnEndPair::getContinuous() const
+{
+ return _connContinuous;
+}
+
SPConnEnd** SPConnEndPair::getConnEnds()
{
return _connEnd;
@@ -367,13 +414,20 @@ bool SPConnEndPair::reroutePathFromLibavoid()
// Do nothing
return false;
}
-
- SPCurve *curve = _path->curve();
-
+ SPLPEItem *lpeitem = dynamic_cast(_path);
+ SPCurve *curve = nullptr;
+ if (lpeitem && lpeitem->hasPathEffectOfType(Inkscape::LivePathEffect::EffectType::CONNECTOR)) {
+ curve = const_cast(_path->curveForEdit());
+ } else {
+ curve = _path->curve();
+ }
recreateCurve(curve, _connRef, _connCurvature);
Geom::Affine doc2item = _path->i2doc_affine().inverse();
curve->transform(doc2item);
+ /* if (lpeitem && lpeitem->hasPathEffectOfType(Inkscape::LivePathEffect::EffectType::CONNECTOR)) {
+ _path->setCurveBeforeLPE(std::move(curve));
+ } */
return true;
}
diff --git a/src/object/sp-conn-end-pair.h b/src/object/sp-conn-end-pair.h
index 2391123504859069b876ebd8be217be291012a91..c1830a22d89922cab6b42e88e7f64df9705b0a88 100644
--- a/src/object/sp-conn-end-pair.h
+++ b/src/object/sp-conn-end-pair.h
@@ -16,10 +16,19 @@
#include
#include
-#include "3rdparty/adaptagrams/libavoid/connector.h"
#include "attributes.h"
+namespace Avoid {
+class ConnRef;
+}
+
+namespace Inkscape {
+namespace LivePathEffect {
+class LPEConnector;
+}
+} // namespace Inkscape
+class SPConnRouter;
class SPConnEnd;
class SPCurve;
class SPPath;
@@ -45,11 +54,13 @@ public:
void getAttachedItems(SPItem *[2]) const;
void getEndpoints(Geom::Point endPts[]) const;
double getCurvature() const;
+ gboolean getContinuous() const;
SPConnEnd** getConnEnds();
bool isOrthogonal() const;
friend void recreateCurve(SPCurve *curve, Avoid::ConnRef *connRef, double curvature);
void tellLibavoidNewEndpoints(bool const processTransaction = false);
bool reroutePathFromLibavoid();
+ void crossings(const bool optimisedForConnectorType, const bool fromRouter);
void makePathInvalid();
void update();
bool isAutoRoutingConn();
@@ -67,9 +78,12 @@ private:
int _connType;
double _connCurvature;
+ bool _connContinuous;
// A sigc connection for transformed signal.
sigc::connection _transformed_connection;
+ friend class SPConnRouter;
+ friend class Inkscape::LivePathEffect::LPEConnector;
};
diff --git a/src/object/sp-conn-end.cpp b/src/object/sp-conn-end.cpp
index a419ae1784cf663dd2f1aae24acaa76e6611970c..f96ee194fb04737a074dfada4e50abcb41102db4 100644
--- a/src/object/sp-conn-end.cpp
+++ b/src/object/sp-conn-end.cpp
@@ -10,18 +10,18 @@
#include "sp-conn-end.h"
#include
-#include
#include
+#include
+#include "2geom/path-intersection.h"
#include "bad-uri-exception.h"
#include "display/curve.h"
-#include "xml/repr.h"
-#include "sp-path.h"
-#include "uri.h"
#include "document.h"
+#include "live_effects/effect-enum.h"
#include "sp-item-group.h"
-#include "2geom/path-intersection.h"
-
+#include "sp-path.h"
+#include "uri.h"
+#include "xml/repr.h"
static void change_endpts(SPCurve *const curve, double const endPos[2]);
@@ -107,7 +107,13 @@ static bool try_get_intersect_point_with_item(SPPath* conn, SPItem* item,
const bool at_start, double& intersect_pos)
{
// Copy the curve and apply transformations up to common ancestor.
- auto conn_curve = conn->curve()->copy();
+ SPLPEItem *lpeitem = dynamic_cast(conn);
+ std::unique_ptr conn_curve;
+ if (lpeitem && lpeitem->hasPathEffectOfType(Inkscape::LivePathEffect::EffectType::CONNECTOR)) {
+ conn_curve = conn->curveBeforeLPE()->copy();
+ } else {
+ conn_curve = conn->curve()->copy();
+ }
conn_curve->transform(conn_transform);
Geom::PathVector conn_pv = conn_curve->get_pathvector();
@@ -137,8 +143,8 @@ static bool try_get_intersect_point_with_item(SPPath* conn, SPItem* item,
return result;
}
-
-static void sp_conn_get_route_and_redraw(SPPath *const path, const bool updatePathRepr = true)
+static void sp_conn_get_route_and_redraw(SPPath *const path, const bool updatePathRepr = true,
+ const bool fromRouter = false)
{
// Get the new route around obstacles.
bool rerouted = path->connEndPair.reroutePathFromLibavoid();
@@ -154,7 +160,14 @@ static void sp_conn_get_route_and_redraw(SPPath *const path, const bool updatePa
// Set sensible values in case there the connector ends are not
// attached to any shapes.
- Geom::PathVector conn_pv = path->curve()->get_pathvector();
+ SPLPEItem *lpeitem = dynamic_cast(path);
+ SPCurve *conn_curve = nullptr;
+ if (lpeitem && lpeitem->hasPathEffectOfType(Inkscape::LivePathEffect::EffectType::CONNECTOR)) {
+ conn_curve = path->curveForEdit();
+ } else {
+ conn_curve = path->curve();
+ }
+ Geom::PathVector conn_pv = conn_curve->get_pathvector();
double endPos[2] = { 0.0, static_cast(conn_pv[0].size()) };
for (unsigned h = 0; h < 2; ++h) {
@@ -165,7 +178,8 @@ static void sp_conn_get_route_and_redraw(SPPath *const path, const bool updatePa
(h == 0), endPos[h]);
}
}
- change_endpts(path->curve(), endPos);
+ change_endpts(conn_curve, endPos);
+ path->connEndPair.crossings(true, fromRouter);
if (updatePathRepr) {
path->updateRepr();
path->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
@@ -202,9 +216,9 @@ void sp_conn_reroute_path_immediate(SPPath *const path)
sp_conn_get_route_and_redraw(path, updatePathRepr);
}
-void sp_conn_redraw_path(SPPath *const path)
+void sp_conn_redraw_path(SPPath *const path, const bool fromRouter)
{
- sp_conn_get_route_and_redraw(path);
+ sp_conn_get_route_and_redraw(path, true, fromRouter);
}
diff --git a/src/object/sp-conn-end.h b/src/object/sp-conn-end.h
index 1779a5b9b695b31eee22280b0710f9d2c4cbfc47..961578926c2130453482487d02762315b382b68c 100644
--- a/src/object/sp-conn-end.h
+++ b/src/object/sp-conn-end.h
@@ -57,7 +57,7 @@ void sp_conn_end_href_changed(SPObject *old_ref, SPObject *ref,
SPConnEnd *connEnd, SPPath *path, unsigned const handle_ix);
void sp_conn_reroute_path(SPPath *const path);
void sp_conn_reroute_path_immediate(SPPath *const path);
-void sp_conn_redraw_path(SPPath *const path);
+void sp_conn_redraw_path(SPPath *const path, const bool fromRouter = false);
void sp_conn_end_detach(SPObject *const owner, unsigned const handle_ix);
diff --git a/src/object/sp-conn-router.cpp b/src/object/sp-conn-router.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..36813bcca3393f96be6a3564b4499d090df87ebe
--- /dev/null
+++ b/src/object/sp-conn-router.cpp
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * A class for handling basic routing.
+ *
+ * Authors:
+ * Jabier Arraiza
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "sp-conn-router.h"
+
+#include "3rdparty/adaptagrams/libavoid/router.h"
+#include "display/curve.h"
+#include "sp-conn-end-pair.h"
+#include "sp-conn-end.h"
+#include "sp-path.h"
+
+SPConnRouter::SPConnRouter(const unsigned int flags)
+ : _router(new Avoid::Router(flags))
+ , _lpe("")
+{}
+
+SPConnRouter::~SPConnRouter()
+{
+ _conn_endpairs.clear();
+}
+
+void SPConnRouter::release()
+{
+ _conn_endpairs.clear();
+}
+
+void SPConnRouter::set_lpe(Glib::ustring lpe)
+{
+ _lpe = lpe;
+}
+
+size_t SPConnRouter::size()
+{
+ return _conn_endpairs.size();
+}
+
+void SPConnRouter::addConnEndPair(SPConnEndPair *cnnpair)
+{
+ _conn_endpairs.insert(cnnpair);
+}
+
+void SPConnRouter::removeConnEndPair(SPConnEndPair *cnnpair)
+{
+ std::set::iterator itr = _conn_endpairs.find(cnnpair);
+ if (itr != _conn_endpairs.end()) {
+ _conn_endpairs.erase(itr);
+ }
+}
+
+void SPConnRouter::reroute_router(SPConnEndPair *origin)
+{
+ for (auto cnnpair : _conn_endpairs) {
+ if (cnnpair != origin) {
+ sp_conn_redraw_path(cnnpair->_path, true);
+ }
+ }
+}
+
+void SPConnRouter::apply_style(Glib::ustring style)
+{
+ for (auto cnnpair : _conn_endpairs) {
+ if (cnnpair->_path->getAttribute("style") && cnnpair->_path->getAttribute("style") != style) {
+ cnnpair->_path->setAttribute("style", style);
+ }
+ }
+}
+
+std::pair> SPConnRouter::pairs_at_point(Geom::Point p)
+{
+ SPConnEndPair *first = nullptr;
+ std::vector crossings;
+ for (auto cnnpair : _conn_endpairs) {
+ Geom::Coord distance;
+ boost::optional pos = cnnpair->_path->curve()->get_pathvector().nearestTime(p, &distance);
+ if (distance < 0.01 && pos) {
+ if (!first) {
+ first = cnnpair;
+ }
+ crossings.push_back(cnnpair);
+ }
+ }
+ return std::make_pair(first, crossings);
+}
+
+std::vector SPConnRouter::crossings(const bool optimisedForConnectorType)
+{
+ std::vector points;
+ Avoid::Router *router = getAvoidRouter();
+ if (router) {
+ Avoid::PointSet crossingPoints;
+ Avoid::ConnRefList::iterator fin = router->connRefs.end();
+ for (Avoid::ConnRefList::iterator i = router->connRefs.begin(); i != fin; ++i) {
+ Avoid::Polygon iRoute = (*i)->displayRoute();
+ Avoid::ConnRefList::iterator j = i;
+ for (++j; j != fin; ++j) {
+ // Determine if this pair overlap
+ Avoid::Polygon jRoute = (*j)->displayRoute();
+ Avoid::ConnRef *iConn = (optimisedForConnectorType) ? *i : nullptr;
+ Avoid::ConnRef *jConn = (optimisedForConnectorType) ? *j : nullptr;
+ Avoid::ConnectorCrossings cross(iRoute, true, jRoute, iConn, jConn);
+ cross.crossingPoints = &crossingPoints;
+ cross.checkForBranchingSegments = true;
+ for (size_t jInd = 1; jInd < jRoute.size(); ++jInd) {
+ const bool finalSegment = ((jInd + 1) == jRoute.size());
+
+ // Normal crossings aren't counted if we pass the pointers
+ // for the connectors, so don't pass them.
+ cross.countForSegment(jInd, finalSegment);
+ }
+ }
+ }
+ for (auto crossp : crossingPoints) {
+ Geom::Point point(crossp.x, crossp.y);
+ points.push_back(point);
+ }
+ }
+ return points;
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/object/sp-conn-router.h b/src/object/sp-conn-router.h
new file mode 100644
index 0000000000000000000000000000000000000000..aee4626b101871fbda3bff0e9fc8e877263c95e5
--- /dev/null
+++ b/src/object/sp-conn-router.h
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef SEEN_SP_CONN_ROUTER
+#define SEEN_SP_CONN_ROUTER
+
+/*
+ * A class for handling a connector router.
+ *
+ * Authors:
+ * Jabier Arraiza
+ *
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include
+#include
+#include
+
+#include "2geom/point.h"
+
+namespace Avoid {
+class Router;
+}
+
+class SPPath;
+class SPConnEndPair;
+class SPConnEnd;
+
+class SPConnRouter
+{
+public:
+ SPConnRouter(const unsigned int flags);
+ ~SPConnRouter();
+ void release();
+ size_t size();
+ Glib::ustring get_lpe() const { return _lpe; }
+ void set_lpe(Glib::ustring lpe);
+ void addConnEndPair(SPConnEndPair *cnnpair);
+ void removeConnEndPair(SPConnEndPair *cnnpair);
+ void reroute_router(SPConnEndPair *origin);
+ void apply_style(Glib::ustring style);
+ std::vector crossings(const bool optimisedForConnectorType = true);
+ std::pair> pairs_at_point(Geom::Point p);
+ Avoid::Router *getAvoidRouter() const { return _router; }
+
+private:
+ Glib::ustring _lpe;
+ std::set _conn_endpairs;
+ Avoid::Router *_router;
+};
+
+#endif /* !SEEN_SP_CONN_ROUTER */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/object/sp-lpe-item.cpp b/src/object/sp-lpe-item.cpp
index 0e86471845e38e46d59aaffc8557a18d757ecacf..7ec6c876a7ec3591fb3c7386f45709a23c64a9e1 100755
--- a/src/object/sp-lpe-item.cpp
+++ b/src/object/sp-lpe-item.cpp
@@ -698,7 +698,7 @@ void SPLPEItem::removeCurrentPathEffect(bool keep_paths)
void SPLPEItem::removeAllPathEffects(bool keep_paths)
{
if (keep_paths) {
- if (path_effect_list->empty()) {
+ if (!path_effect_list || path_effect_list->empty()) {
return;
}
}
@@ -771,7 +771,7 @@ void SPLPEItem::upCurrentPathEffect()
/** used for shapes so they can see if they should also disable shape calculation and read from d= */
bool SPLPEItem::hasBrokenPathEffect() const
{
- if (path_effect_list->empty()) {
+ if (!path_effect_list || path_effect_list->empty()) {
return false;
}
@@ -789,7 +789,7 @@ bool SPLPEItem::hasBrokenPathEffect() const
bool SPLPEItem::hasPathEffectOfType(int const type, bool is_ready) const
{
- if (path_effect_list->empty()) {
+ if (!path_effect_list || path_effect_list->empty()) {
return false;
}
@@ -1239,7 +1239,7 @@ Inkscape::LivePathEffect::Effect* SPLPEItem::getNextLPE(Inkscape::LivePathEffect
size_t SPLPEItem::countLPEOfType(int const type, bool inc_hidden, bool is_ready) const
{
size_t counter = 0;
- if (path_effect_list->empty()) {
+ if (!path_effect_list || path_effect_list->empty()) {
return counter;
}
@@ -1340,7 +1340,10 @@ bool SPLPEItem::forkPathEffectsIfNecessary(unsigned int nr_of_allowed_users, boo
// Clones of the LPEItem will increase the refcount of the lpeobjects.
// Therefore, nr_of_allowed_users should be increased with the number of clones (i.e. refs to the lpeitem)
nr_of_allowed_users += this->hrefcount;
-
+ SPPath *path = dynamic_cast(this);
+ if (path && path->getAttribute("inkscape:connector-type")) {
+ return false;
+ }
std::vector old_lpeobjs, new_lpeobjs;
PathEffectList effect_list = this->getEffectList();
for (auto & it : effect_list)
diff --git a/src/object/sp-path.cpp b/src/object/sp-path.cpp
index 879fb4c85cb897804f418ad2749aa6079d17fded..94efa219f02f35ad94c9f3a300040533874f79ba 100644
--- a/src/object/sp-path.cpp
+++ b/src/object/sp-path.cpp
@@ -269,6 +269,7 @@ void SPPath::set(SPAttr key, const gchar* value) {
case SPAttr::CONNECTOR_TYPE:
case SPAttr::CONNECTOR_CURVATURE:
+ case SPAttr::CONNECTOR_CONTINUOUS:
case SPAttr::CONNECTION_START:
case SPAttr::CONNECTION_END:
case SPAttr::CONNECTION_START_POINT:
diff --git a/src/object/sp-shape.cpp b/src/object/sp-shape.cpp
index 1d5df5ea3769cd8f549e48119916896ed78494a7..68c830767631592e71cdaca647be91a43bd8c511 100644
--- a/src/object/sp-shape.cpp
+++ b/src/object/sp-shape.cpp
@@ -1157,6 +1157,17 @@ SPCurve const *SPShape::curveForEdit() const
return curve();
}
+/**
+ * Return a borrowed pointer of the curve for edit
+ */
+SPCurve *SPShape::curveForEdit()
+{
+ if (_curve_before_lpe) {
+ return _curve_before_lpe.get();
+ }
+ return curve();
+}
+
void SPShape::snappoints(std::vector &p, Inkscape::SnapPreferences const *snapprefs) const {
if (this->_curve == nullptr) {
return;
diff --git a/src/object/sp-shape.h b/src/object/sp-shape.h
index fd39213099e7a1d9319ad808b4e339fbe8def6df..47c8b0e7ce835c599665696ac4d51af979ead073 100644
--- a/src/object/sp-shape.h
+++ b/src/object/sp-shape.h
@@ -43,6 +43,7 @@ public:
~SPShape() override;
SPCurve *curve();
+ SPCurve *curveForEdit();
SPCurve const *curve() const;
SPCurve const *curveBeforeLPE() const;
SPCurve const *curveForEdit() const;
diff --git a/src/path-chemistry.cpp b/src/path-chemistry.cpp
index e1f90d1beee307e1b545edfa017913e9333ab968..5b71c34574acadc43a76c9dbefd78edffe3a5c25 100644
--- a/src/path-chemistry.cpp
+++ b/src/path-chemistry.cpp
@@ -401,6 +401,7 @@ sp_item_list_to_curves(const std::vector &items, std::vector&
item->removeAttribute("inkscape:connection-end-point");
item->removeAttribute("inkscape:connector-type");
item->removeAttribute("inkscape:connector-curvature");
+ item->removeAttribute("inkscape:connector-continuous");
did = true;
}
continue; // already a path, and no path effect
diff --git a/src/preferences-skeleton.h b/src/preferences-skeleton.h
index 32ac0432f39432a6a8472d327e96fbecc53d9105..52e00ceac93156621c60840a0ce656455dbbf943 100644
--- a/src/preferences-skeleton.h
+++ b/src/preferences-skeleton.h
@@ -139,7 +139,7 @@ static char const preferences_skeleton[] =
-
+
diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp
index ac76c43dbcd7e7c549021a8a03afaab8c4593f16..7fd8f1750394b5fe9253d98508c6830c5437a193 100644
--- a/src/selection-chemistry.cpp
+++ b/src/selection-chemistry.cpp
@@ -2950,7 +2950,7 @@ void ObjectSet::cloneOriginal()
/**
* This applies the Fill Between Many LPE, and has it refer to the selection.
*/
-void ObjectSet::cloneOriginalPathLPE(bool allow_transforms)
+void ObjectSet::cloneOriginalPathLPE(bool allow_transforms, SPObject *parent, size_t pos)
{
Inkscape::SVGOStringStream os;
@@ -2958,7 +2958,7 @@ void ObjectSet::cloneOriginalPathLPE(bool allow_transforms)
auto items_= items();
bool multiple = false;
for (auto i=items_.begin();i!=items_.end();++i){
- if (SP_IS_SHAPE(*i) || SP_IS_TEXT(*i) || SP_IS_GROUP(*i)) {
+ if (SP_IS_SHAPE(*i) || SP_IS_TEXT(*i) || SP_IS_GROUP(*i) || SP_IS_SYMBOL(*i)) {
if (firstItem) {
os << "|";
multiple = true;
@@ -2970,7 +2970,10 @@ void ObjectSet::cloneOriginalPathLPE(bool allow_transforms)
}
if (firstItem) {
Inkscape::XML::Document *xml_doc = document()->getReprDoc();
- SPObject *parent = firstItem->parent;
+ if (!parent) {
+ parent = firstItem->parent;
+ pos = firstItem->getPosition();
+ }
// create the LPE
Inkscape::XML::Node *lpe_repr = xml_doc->createElement("inkscape:path-effect");
if (multiple) {
@@ -2990,23 +2993,37 @@ void ObjectSet::cloneOriginalPathLPE(bool allow_transforms)
Inkscape::GC::release(lpe_repr);
Inkscape::XML::Node* clone = nullptr;
SPGroup *firstgroup = dynamic_cast(firstItem);
+ SPSymbol *firstsymbol = dynamic_cast(firstItem);
if (firstgroup) {
if (!multiple) {
clone = firstgroup->getRepr()->duplicate(xml_doc);
}
+ } else if (firstsymbol) {
+ if (!multiple) {
+ clone = firstsymbol->getRepr()->duplicate(xml_doc);
+ }
} else {
// create the new path
clone = xml_doc->createElement("svg:path");
clone->setAttribute("d", "M 0 0");
-
}
if (clone) {
// add the new clone to the top of the original's parent
- parent->appendChildRepr(clone);
+ if (pos != Glib::ustring::npos) {
+ Inkscape::XML::Node *prev = nullptr;
+ if (pos) {
+ prev = parent->nthChild(pos - 1)->getRepr();
+ }
+ parent->addChild(clone, prev);
+ } else {
+ parent->appendChildRepr(clone);
+ }
// select the new object:
set(clone);
Inkscape::GC::release(clone);
- SPObject *clone_obj = document()->getObjectById(clone->attribute("id"));
+ const gchar *id = clone->attribute("id");
+ unSymbol();
+ SPObject *clone_obj = document()->getObjectById(id);
SPLPEItem *clone_lpeitem = dynamic_cast(clone_obj);
if (clone_lpeitem) {
clone_lpeitem->addPathEffect(lpe_id_href, false);
diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp
index d25cf9e315e54384f883c7678cf79768b58bca83..fe6ced4a65ebf0497937925d76d92610e39239de 100644
--- a/src/ui/dialog/inkscape-preferences.cpp
+++ b/src/ui/dialog/inkscape-preferences.cpp
@@ -767,7 +767,7 @@ void InkscapePreferences::AddNewObjectsStyle(DialogPage &p, Glib::ustring const
else
p.add_group_header( _("Style of new objects"));
PrefRadioButton* current = Gtk::manage( new PrefRadioButton);
- current->init ( _("Last used style"), prefs_path + "/usecurrent", 1, true, nullptr);
+ current->init(_("Last used style"), prefs_path + "/usecurrent", 1, true, current);
p.add_line( true, "", *current, "",
_("Apply the style you last set on an object"));
@@ -1018,6 +1018,7 @@ void InkscapePreferences::initPageTools()
//Connector
this->AddSelcueCheckbox(_page_connector, "/tools/connector", true);
+ this->AddNewObjectsStyle(_page_connector, "/tools/connector");
_page_connector.add_line(false, "", _connector_ignore_text, "",
_("If on, connector attachment points will not be shown for text objects"));
diff --git a/src/ui/dialog/livepatheffect-add.cpp b/src/ui/dialog/livepatheffect-add.cpp
index e1c8003cec01ca3c1b76c4cbb16580505cc6744e..35e03b16a4e91a10f188494a7b9911f4bd03d69c 100644
--- a/src/ui/dialog/livepatheffect-add.cpp
+++ b/src/ui/dialog/livepatheffect-add.cpp
@@ -544,6 +544,10 @@ bool LivePathEffectAdd::on_filter(Gtk::FlowBoxChild *child)
disable = true;
} else if (_item_type == "shape" && !converter.get_on_shape(data->id)) {
disable = true;
+ } else if (_item_type == "connector" && data->id == Inkscape::LivePathEffect::CONNECTOR) {
+ disable = false;
+ } else if (_item_type == "connector" && data->id != Inkscape::LivePathEffect::CONNECTOR) {
+ disable = true;
} else if (_item_type == "path" && !converter.get_on_path(data->id)) {
disable = true;
}
@@ -933,6 +937,8 @@ void LivePathEffectAdd::show(SPDesktop *desktop)
dial._item_type = "";
if (group) {
dial._item_type = "group";
+ } else if (path && path->getAttribute("inkscape:connector-type")) {
+ dial._item_type = "connector";
} else if (path) {
dial._item_type = "path";
} else if (shape) {
diff --git a/src/ui/dialog/livepatheffect-editor.cpp b/src/ui/dialog/livepatheffect-editor.cpp
index 8a225706937a932209abd77cf62669c6e8178b88..68297c12a0f977e3bfd330c628de8e6046304260 100644
--- a/src/ui/dialog/livepatheffect-editor.cpp
+++ b/src/ui/dialog/livepatheffect-editor.cpp
@@ -20,27 +20,25 @@
#include "desktop.h"
#include "document-undo.h"
#include "document.h"
-#include "inkscape.h"
-#include "livepatheffect-add.h"
-#include "path-chemistry.h"
-#include "selection-chemistry.h"
-#include "verbs.h"
-
#include "helper/action.h"
-#include "ui/icon-loader.h"
-
+#include "inkscape.h"
#include "live_effects/effect.h"
#include "live_effects/lpeobject-reference.h"
#include "live_effects/lpeobject.h"
-
+#include "livepatheffect-add.h"
#include "object/sp-item-group.h"
#include "object/sp-path.h"
-#include "object/sp-use.h"
+#include "object/sp-symbol.h"
#include "object/sp-text.h"
-
+#include "object/sp-use.h"
+#include "path-chemistry.h"
+#include "selection-chemistry.h"
+#include "ui/icon-loader.h"
#include "ui/icon-names.h"
+#include "ui/tools-switch.h"
#include "ui/tools/node-tool.h"
#include "ui/widget/imagetoggler.h"
+#include "verbs.h"
namespace Inkscape {
namespace UI {
@@ -319,6 +317,7 @@ LivePathEffectEditor::onSelectionChanged(Inkscape::Selection *sel)
SPItem *orig = use->get_original();
if ( dynamic_cast(orig) ||
dynamic_cast(orig) ||
+ dynamic_cast(orig) ||
dynamic_cast(orig) )
{
// Note that an SP_USE cannot have an LPE applied, so we only need to worry about the "add effect" case.
@@ -426,6 +425,7 @@ LivePathEffectEditor::onAdd()
if ( sel && !sel->isEmpty() ) {
SPItem *item = sel->singleItem();
if (item) {
+ tools_switch(current_desktop, TOOLS_SELECT);
if ( dynamic_cast(item) ) {
// show effectlist dialog
using Inkscape::UI::Dialog::LivePathEffectAdd;
@@ -457,14 +457,18 @@ LivePathEffectEditor::onAdd()
// convert to path, apply CLONE_ORIGINAL LPE, link it to the cloned path
// test whether linked object is supported by the CLONE_ORIGINAL LPE
+
SPItem *orig = use->get_original();
if ( dynamic_cast(orig) ||
dynamic_cast(orig) ||
+ dynamic_cast(orig) ||
dynamic_cast(orig) )
{
+ // allow use symbols and position in sampe index
+ SPObject *parent = item->parent;
+ size_t pos = item->getPosition();
// select original
sel->set(orig);
-
// delete clone but remember its id and transform
gchar *id = g_strdup(item->getRepr()->attribute("id"));
gchar *transform = g_strdup(item->getRepr()->attribute("transform"));
@@ -472,7 +476,7 @@ LivePathEffectEditor::onAdd()
item = nullptr;
// run sp_selection_clone_original_path_lpe
- sel->cloneOriginalPathLPE(true);
+ sel->cloneOriginalPathLPE(true, parent, pos);
SPItem *new_item = sel->singleItem();
// Check that the cloning was successful. We don't want to change the ID of the original referenced path!
diff --git a/src/ui/dialog/livepatheffect-editor.cpp.orig b/src/ui/dialog/livepatheffect-editor.cpp.orig
new file mode 100644
index 0000000000000000000000000000000000000000..bb09fe2626951731c1ae7234d1ad65cc7ffda9ea
--- /dev/null
+++ b/src/ui/dialog/livepatheffect-editor.cpp.orig
@@ -0,0 +1,650 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * @file
+ * Live Path Effect editing dialog - implementation.
+ */
+/* Authors:
+ * Johan Engelen
+ * Steren Giannini
+ * Bastien Bouclet
+ * Abhishek Sharma
+ *
+ * Copyright (C) 2007 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "livepatheffect-editor.h"
+
+#include
+
+#include "desktop.h"
+#include "document-undo.h"
+#include "document.h"
+#include "inkscape.h"
+#include "livepatheffect-add.h"
+#include "path-chemistry.h"
+#include "selection-chemistry.h"
+#include "verbs.h"
+
+#include "helper/action.h"
+#include "ui/icon-loader.h"
+
+#include "live_effects/effect.h"
+#include "live_effects/lpeobject-reference.h"
+#include "live_effects/lpeobject.h"
+
+#include "object/sp-item-group.h"
+#include "object/sp-path.h"
+#include "object/sp-use.h"
+#include "object/sp-symbol.h"
+#include "object/sp-text.h"
+
+#include "ui/icon-names.h"
+#include "ui/tools-switch.h"
+#include "ui/tools/node-tool.h"
+#include "ui/widget/imagetoggler.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+
+/*####################
+ * Callback functions
+ */
+
+
+void lpeeditor_selection_changed (Inkscape::Selection * selection, gpointer data)
+{
+ LivePathEffectEditor *lpeeditor = static_cast(data);
+ lpeeditor->selection_changed_lock = true;
+ lpeeditor->lpe_list_locked = false;
+ lpeeditor->onSelectionChanged(selection);
+ lpeeditor->_on_button_release(nullptr); //to force update widgets
+ lpeeditor->selection_changed_lock = false;
+}
+
+void lpeeditor_selection_modified (Inkscape::Selection * selection, guint /*flags*/, gpointer data)
+{
+
+ LivePathEffectEditor *lpeeditor = static_cast(data);
+ lpeeditor->lpe_list_locked = false;
+ lpeeditor->onSelectionChanged(selection);
+}
+
+static void lpe_style_button(Gtk::Button& btn, char const* iconName)
+{
+ GtkWidget *child = sp_get_icon_image(iconName, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_widget_show( child );
+ btn.add(*Gtk::manage(Glib::wrap(child)));
+ btn.set_relief(Gtk::RELIEF_NONE);
+}
+
+
+/*
+ * LivePathEffectEditor
+ *
+ * TRANSLATORS: this dialog is accessible via menu Path - Path Effect Editor...
+ *
+ */
+
+LivePathEffectEditor::LivePathEffectEditor()
+ : DialogBase("/dialogs/livepatheffect", SP_VERB_DIALOG_LIVE_PATH_EFFECT)
+ , lpe_list_locked(false)
+ , effectwidget(nullptr)
+ , status_label("", Gtk::ALIGN_CENTER)
+ , effectcontrol_frame("")
+ , button_add()
+ , button_remove()
+ , button_up()
+ , button_down()
+ , current_desktop(nullptr)
+ , current_lpeitem(nullptr)
+ , current_lperef(nullptr)
+ , effectcontrol_vbox(Gtk::ORIENTATION_VERTICAL)
+ , effectlist_vbox(Gtk::ORIENTATION_VERTICAL)
+ , effectapplication_hbox(Gtk::ORIENTATION_HORIZONTAL, 4)
+{
+ set_spacing(4);
+
+ //Add the TreeView, inside a ScrolledWindow, with the button underneath:
+ scrolled_window.add(effectlist_view);
+ scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+ scrolled_window.set_shadow_type(Gtk::SHADOW_IN);
+ scrolled_window.set_size_request(210, 70);
+
+ effectcontrol_vbox.set_spacing(4);
+
+ effectlist_vbox.pack_start(scrolled_window, Gtk::PACK_EXPAND_WIDGET);
+ effectlist_vbox.pack_end(toolbar_hbox, Gtk::PACK_SHRINK);
+ effectcontrol_eventbox.add_events(Gdk::BUTTON_RELEASE_MASK);
+ effectcontrol_eventbox.signal_button_release_event().connect(sigc::mem_fun(*this, &LivePathEffectEditor::_on_button_release) );
+ effectcontrol_eventbox.add(effectcontrol_vbox);
+ effectcontrol_frame.add(effectcontrol_eventbox);
+
+ button_add.set_tooltip_text(_("Add path effect"));
+ lpe_style_button(button_add, INKSCAPE_ICON("list-add"));
+ button_add.set_relief(Gtk::RELIEF_NONE);
+
+ button_remove.set_tooltip_text(_("Delete current path effect"));
+ lpe_style_button(button_remove, INKSCAPE_ICON("list-remove"));
+ button_remove.set_relief(Gtk::RELIEF_NONE);
+
+ button_up.set_tooltip_text(_("Raise the current path effect"));
+ lpe_style_button(button_up, INKSCAPE_ICON("go-up"));
+ button_up.set_relief(Gtk::RELIEF_NONE);
+
+ button_down.set_tooltip_text(_("Lower the current path effect"));
+ lpe_style_button(button_down, INKSCAPE_ICON("go-down"));
+ button_down.set_relief(Gtk::RELIEF_NONE);
+
+ // Add toolbar items to toolbar
+ toolbar_hbox.set_layout (Gtk::BUTTONBOX_END);
+ toolbar_hbox.add( button_add );
+ toolbar_hbox.set_child_secondary( button_add , true);
+ toolbar_hbox.add( button_remove );
+ toolbar_hbox.set_child_secondary( button_remove , true);
+ toolbar_hbox.add( button_up );
+ toolbar_hbox.add( button_down );
+
+ //Create the Tree model:
+ effectlist_store = Gtk::ListStore::create(columns);
+ effectlist_view.set_model(effectlist_store);
+ effectlist_view.set_headers_visible(false);
+
+ // Handle tree selections
+ effectlist_selection = effectlist_view.get_selection();
+ effectlist_selection->signal_changed().connect( sigc::mem_fun(*this, &LivePathEffectEditor::on_effect_selection_changed) );
+
+ //Add the visibility icon column:
+ Inkscape::UI::Widget::ImageToggler *eyeRenderer = Gtk::manage( new Inkscape::UI::Widget::ImageToggler(
+ INKSCAPE_ICON("object-visible"), INKSCAPE_ICON("object-hidden")) );
+ int visibleColNum = effectlist_view.append_column("is_visible", *eyeRenderer) - 1;
+ eyeRenderer->signal_toggled().connect( sigc::mem_fun(*this, &LivePathEffectEditor::on_visibility_toggled) );
+ eyeRenderer->property_activatable() = true;
+ Gtk::TreeViewColumn* col = effectlist_view.get_column(visibleColNum);
+ if ( col ) {
+ col->add_attribute( eyeRenderer->property_active(), columns.col_visible );
+ }
+
+ //Add the effect name column:
+ effectlist_view.append_column("Effect", columns.col_name);
+
+ pack_start(effectlist_vbox, true, true);
+ pack_start(status_label, false, false);
+ pack_start(effectcontrol_frame, false, false);
+
+ effectcontrol_frame.hide();
+ selection_changed_lock = false;
+ // connect callback functions to buttons
+ button_add.signal_clicked().connect(sigc::mem_fun(*this, &LivePathEffectEditor::onAdd));
+ button_remove.signal_clicked().connect(sigc::mem_fun(*this, &LivePathEffectEditor::onRemove));
+ button_up.signal_clicked().connect(sigc::mem_fun(*this, &LivePathEffectEditor::onUp));
+ button_down.signal_clicked().connect(sigc::mem_fun(*this, &LivePathEffectEditor::onDown));
+
+ update();
+ show_all_children();
+}
+
+LivePathEffectEditor::~LivePathEffectEditor()
+{
+ if (effectwidget) {
+ effectcontrol_vbox.remove(*effectwidget);
+ delete effectwidget;
+ effectwidget = nullptr;
+ }
+
+ if (current_desktop) {
+ selection_changed_connection.disconnect();
+ selection_modified_connection.disconnect();
+ }
+}
+
+bool LivePathEffectEditor::_on_button_release(GdkEventButton* button_event) {
+ Glib::RefPtr sel = effectlist_view.get_selection();
+ if (sel->count_selected_rows () == 0) {
+ return true;
+ }
+ Gtk::TreeModel::iterator it = sel->get_selected();
+ LivePathEffect::LPEObjectReference * lperef = (*it)[columns.lperef];
+ if (lperef && current_lpeitem && current_lperef != lperef) {
+ if (lperef->getObject()) {
+ LivePathEffect::Effect * effect = lperef->lpeobject->get_lpe();
+ if (effect) {
+ effect->refresh_widgets = true;
+ showParams(*effect);
+ }
+ }
+ }
+ return true;
+}
+
+void
+LivePathEffectEditor::showParams(LivePathEffect::Effect& effect)
+{
+ if (effectwidget && !effect.refresh_widgets) {
+ return;
+ }
+ if (effectwidget) {
+ effectcontrol_vbox.remove(*effectwidget);
+ delete effectwidget;
+ effectwidget = nullptr;
+ }
+ effectwidget = effect.newWidget();
+ effectcontrol_frame.set_label(effect.getName());
+ effectcontrol_vbox.pack_start(*effectwidget, true, true);
+
+ button_remove.show();
+ status_label.hide();
+ effectcontrol_frame.show();
+ effectcontrol_vbox.show_all_children();
+ // fixme: add resizing of dialog
+ effect.refresh_widgets = false;
+}
+
+void
+LivePathEffectEditor::selectInList(LivePathEffect::Effect* effect)
+{
+ Gtk::TreeNodeChildren chi = effectlist_view.get_model()->children();
+ for (Gtk::TreeIter ci = chi.begin() ; ci != chi.end(); ci++) {
+ if (ci->get_value(columns.lperef)->lpeobject->get_lpe() == effect && effectlist_view.get_selection())
+ effectlist_view.get_selection()->select(ci);
+ }
+}
+
+
+void
+LivePathEffectEditor::showText(Glib::ustring const &str)
+{
+ if (effectwidget) {
+ effectcontrol_vbox.remove(*effectwidget);
+ delete effectwidget;
+ effectwidget = nullptr;
+ }
+
+ status_label.show();
+ status_label.set_label(str);
+
+ effectcontrol_frame.hide();
+
+ // fixme: do resizing of dialog ?
+}
+
+void
+LivePathEffectEditor::set_sensitize_all(bool sensitive)
+{
+ //combo_effecttype.set_sensitive(sensitive);
+ button_add.set_sensitive(sensitive);
+ button_remove.set_sensitive(sensitive);
+ effectlist_view.set_sensitive(sensitive);
+ button_up.set_sensitive(sensitive);
+ button_down.set_sensitive(sensitive);
+}
+
+void
+LivePathEffectEditor::onSelectionChanged(Inkscape::Selection *sel)
+{
+ if (lpe_list_locked) {
+ // this was triggered by selecting a row in the list, so skip reloading
+ lpe_list_locked = false;
+ return;
+ }
+ current_lpeitem = nullptr;
+ effectlist_store->clear();
+
+ if ( sel && !sel->isEmpty() ) {
+ SPItem *item = sel->singleItem();
+ if ( item ) {
+ SPLPEItem *lpeitem = dynamic_cast(item);
+ if ( lpeitem ) {
+ effect_list_reload(lpeitem);
+ current_lpeitem = lpeitem;
+ set_sensitize_all(true);
+ if ( lpeitem->hasPathEffect() ) {
+ Inkscape::LivePathEffect::Effect *lpe = lpeitem->getCurrentLPE();
+ if (lpe) {
+ showParams(*lpe);
+ lpe_list_locked = true;
+ selectInList(lpe);
+ } else {
+ showText(_("Unknown effect is applied"));
+ }
+ } else {
+ showText(_("Click button to add an effect"));
+ button_remove.set_sensitive(false);
+ button_up.set_sensitive(false);
+ button_down.set_sensitive(false);
+ }
+ } else {
+ SPUse *use = dynamic_cast(item);
+ if ( use ) {
+ // test whether linked object is supported by the CLONE_ORIGINAL LPE
+ SPItem *orig = use->get_original();
+ if ( dynamic_cast(orig) ||
+ dynamic_cast(orig) ||
+ dynamic_cast(orig) ||
+ dynamic_cast(orig) )
+ {
+ // Note that an SP_USE cannot have an LPE applied, so we only need to worry about the "add effect" case.
+ set_sensitize_all(true);
+ showText(_("Click add button to convert clone"));
+ button_remove.set_sensitive(false);
+ button_up.set_sensitive(false);
+ button_down.set_sensitive(false);
+ } else {
+ showText(_("Select a path or shape"));
+ set_sensitize_all(false);
+ }
+ } else {
+ showText(_("Select a path or shape"));
+ set_sensitize_all(false);
+ }
+ }
+ } else {
+ showText(_("Only one item can be selected"));
+ set_sensitize_all(false);
+ }
+ } else {
+ showText(_("Select a path or shape"));
+ set_sensitize_all(false);
+ }
+}
+
+/*
+ * First clears the effectlist_store, then appends all effects from the effectlist.
+ */
+void
+LivePathEffectEditor::effect_list_reload(SPLPEItem *lpeitem)
+{
+ effectlist_store->clear();
+
+ PathEffectList effectlist = lpeitem->getEffectList();
+ PathEffectList::iterator it;
+ for( it = effectlist.begin() ; it!=effectlist.end(); ++it)
+ {
+ if ( !(*it)->lpeobject ) {
+ continue;
+ }
+
+ if ((*it)->lpeobject->get_lpe()) {
+ Gtk::TreeModel::Row row = *(effectlist_store->append());
+ row[columns.col_name] = (*it)->lpeobject->get_lpe()->getName();
+ row[columns.lperef] = *it;
+ row[columns.col_visible] = (*it)->lpeobject->get_lpe()->isVisible();
+ } else {
+ Gtk::TreeModel::Row row = *(effectlist_store->append());
+ row[columns.col_name] = _("Unknown effect");
+ row[columns.lperef] = *it;
+ row[columns.col_visible] = false;
+ }
+ }
+}
+
+
+void
+LivePathEffectEditor::setDesktop(SPDesktop *desktop)
+{
+ if ( desktop == current_desktop ) {
+ return;
+ }
+
+ if (current_desktop) {
+ selection_changed_connection.disconnect();
+ selection_modified_connection.disconnect();
+ }
+
+ lpe_list_locked = false;
+ current_desktop = desktop;
+ if (desktop) {
+ Inkscape::Selection *selection = desktop->getSelection();
+ selection_changed_connection = selection->connectChanged(
+ sigc::bind (sigc::ptr_fun(&lpeeditor_selection_changed), this ) );
+ selection_modified_connection = selection->connectModified(
+ sigc::bind (sigc::ptr_fun(&lpeeditor_selection_modified), this ) );
+
+ onSelectionChanged(selection);
+ } else {
+ onSelectionChanged(nullptr);
+ }
+}
+
+void LivePathEffectEditor::update()
+{
+ if (!_app) {
+ std::cerr << "LivePathEffectEditor::update(): _app is null" << std::endl;
+ return;
+ }
+
+ setDesktop(getDesktop());
+}
+
+/*########################################################################
+# BUTTON CLICK HANDLERS (callbacks)
+########################################################################*/
+
+// TODO: factor out the effect applying code which can be called from anywhere. (selection-chemistry.cpp also needs it)
+void
+LivePathEffectEditor::onAdd()
+{
+ Inkscape::Selection *sel = _getSelection();
+ if ( sel && !sel->isEmpty() ) {
+ SPItem *item = sel->singleItem();
+ if (item) {
+ tools_switch(current_desktop, TOOLS_SELECT);
+ if ( dynamic_cast(item) ) {
+ // show effectlist dialog
+ using Inkscape::UI::Dialog::LivePathEffectAdd;
+ LivePathEffectAdd::show(current_desktop);
+ if ( !LivePathEffectAdd::isApplied()) {
+ return;
+ }
+
+ SPDocument *doc = current_desktop->doc();
+
+ const LivePathEffect::EnumEffectData *data =
+ LivePathEffectAdd::getActiveData();
+ if (!data) {
+ return;
+ }
+ item = sel->singleItem(); // get new item
+
+ LivePathEffect::Effect::createAndApply(data->key.c_str(), doc, item);
+
+ DocumentUndo::done(doc, SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Create and apply path effect"));
+
+ lpe_list_locked = false;
+ onSelectionChanged(sel);
+ } else {
+ SPUse *use = dynamic_cast(item);
+ if ( use ) {
+ // item is a clone. do not show effectlist dialog.
+ // convert to path, apply CLONE_ORIGINAL LPE, link it to the cloned path
+
+ // test whether linked object is supported by the CLONE_ORIGINAL LPE
+
+ SPItem *orig = use->get_original();
+ if ( dynamic_cast(orig) ||
+ dynamic_cast(orig) ||
+ dynamic_cast(orig) ||
+ dynamic_cast(orig) )
+ {
+ // allow use symbols and position in sampe index
+ SPObject *parent = item->parent;
+ size_t pos = item->getPosition();
+ // select original
+ sel->set(orig);
+ // delete clone but remember its id and transform
+ gchar *id = g_strdup(item->getRepr()->attribute("id"));
+ gchar *transform = g_strdup(item->getRepr()->attribute("transform"));
+ item->deleteObject(false);
+ item = nullptr;
+
+ // run sp_selection_clone_original_path_lpe
+ sel->cloneOriginalPathLPE(true, parent, pos);
+
+ SPItem *new_item = sel->singleItem();
+ // Check that the cloning was successful. We don't want to change the ID of the original referenced path!
+ if (new_item && (new_item != orig)) {
+ new_item->setAttribute("id", id);
+ new_item->setAttribute("transform", transform);
+ }
+ g_free(id);
+ g_free(transform);
+
+ /// \todo Add the LPE stack of the original path?
+
+ SPDocument *doc = current_desktop->doc();
+ DocumentUndo::done(doc, SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Create and apply Clone original path effect"));
+
+ lpe_list_locked = false;
+ onSelectionChanged(sel);
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+LivePathEffectEditor::onRemove()
+{
+ Inkscape::Selection *sel = _getSelection();
+ if ( sel && !sel->isEmpty() ) {
+ SPItem *item = sel->singleItem();
+ SPLPEItem *lpeitem = dynamic_cast(item);
+ if ( lpeitem ) {
+ sp_lpe_item_update_patheffect(lpeitem, false, false);
+ lpeitem->removeCurrentPathEffect(false);
+ current_lperef = nullptr;
+ DocumentUndo::done( current_desktop->getDocument(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Remove path effect") );
+ lpe_list_locked = false;
+ onSelectionChanged(sel);
+ }
+ }
+
+}
+
+void LivePathEffectEditor::onUp()
+{
+ Inkscape::Selection *sel = _getSelection();
+ if ( sel && !sel->isEmpty() ) {
+ SPItem *item = sel->singleItem();
+ SPLPEItem *lpeitem = dynamic_cast(item);
+ if ( lpeitem ) {
+ Inkscape::LivePathEffect::Effect *lpe = lpeitem->getCurrentLPE();
+ lpeitem->upCurrentPathEffect();
+ DocumentUndo::done( current_desktop->getDocument(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Move path effect up") );
+
+ effect_list_reload(lpeitem);
+ if (lpe) {
+ showParams(*lpe);
+ lpe_list_locked = true;
+ selectInList(lpe);
+ }
+ }
+ }
+}
+
+void LivePathEffectEditor::onDown()
+{
+ Inkscape::Selection *sel = _getSelection();
+ if ( sel && !sel->isEmpty() ) {
+ SPItem *item = sel->singleItem();
+ SPLPEItem *lpeitem = dynamic_cast(item);
+ if ( lpeitem ) {
+ Inkscape::LivePathEffect::Effect *lpe = lpeitem->getCurrentLPE();
+ lpeitem->downCurrentPathEffect();
+
+ DocumentUndo::done( current_desktop->getDocument(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Move path effect down") );
+ effect_list_reload(lpeitem);
+ if (lpe) {
+ showParams(*lpe);
+ lpe_list_locked = true;
+ selectInList(lpe);
+ }
+ }
+ }
+}
+
+void LivePathEffectEditor::on_effect_selection_changed()
+{
+ Glib::RefPtr sel = effectlist_view.get_selection();
+ if (sel->count_selected_rows () == 0) {
+ button_remove.set_sensitive(false);
+ return;
+ }
+ button_remove.set_sensitive(true);
+ Gtk::TreeModel::iterator it = sel->get_selected();
+ LivePathEffect::LPEObjectReference * lperef = (*it)[columns.lperef];
+
+ if (lperef && current_lpeitem && current_lperef != lperef) {
+ // The last condition ignore Gtk::TreeModel may occasionally be changed emitted when nothing has happened
+ if (lperef->getObject()) {
+ lpe_list_locked = true; // prevent reload of the list which would lose selection
+ current_lpeitem->setCurrentPathEffect(lperef);
+ current_lperef = lperef;
+ LivePathEffect::Effect * effect = lperef->lpeobject->get_lpe();
+ if (effect) {
+ effect->refresh_widgets = true;
+ showParams(*effect);
+ // To reload knots and helper paths
+ Inkscape::Selection *sel = _getSelection();
+ if ( sel && !sel->isEmpty() && !selection_changed_lock) {
+ SPLPEItem *lpeitem = dynamic_cast(sel->singleItem());
+ if (lpeitem) {
+ sel->clear();
+ sel->add(lpeitem);
+ Inkscape::UI::Tools::sp_update_helperpath(current_desktop);
+ }
+ }
+ }
+ }
+ }
+}
+
+void LivePathEffectEditor::on_visibility_toggled( Glib::ustring const& str )
+{
+
+ Gtk::TreeModel::Children::iterator iter = effectlist_view.get_model()->get_iter(str);
+ Gtk::TreeModel::Row row = *iter;
+
+ LivePathEffect::LPEObjectReference * lpeobjref = row[columns.lperef];
+
+ if ( lpeobjref && lpeobjref->lpeobject->get_lpe() ) {
+ bool newValue = !row[columns.col_visible];
+ row[columns.col_visible] = newValue;
+ /* FIXME: this explicit writing to SVG is wrong. The lpe_item should have a method to disable/enable an effect within its stack.
+ * So one can call: lpe_item->setActive(lpeobjref->lpeobject); */
+ lpeobjref->lpeobject->get_lpe()->getRepr()->setAttribute("is_visible", newValue ? "true" : "false");
+ Inkscape::Selection *sel = _getSelection();
+ if ( sel && !sel->isEmpty() ) {
+ SPItem *item = sel->singleItem();
+ SPLPEItem *lpeitem = dynamic_cast(item);
+ if ( lpeitem ) {
+ lpeobjref->lpeobject->get_lpe()->doOnVisibilityToggled(lpeitem);
+ }
+ }
+ DocumentUndo::done( current_desktop->getDocument(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ newValue ? _("Activate path effect") : _("Deactivate path effect"));
+ }
+}
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/ui/knot/knot-holder.h b/src/ui/knot/knot-holder.h
index 397179b9651031c7dc9d05390a400a4952e04e12..b3842f84bca7b4750b8b20c81a8b9d27e85a9436 100644
--- a/src/ui/knot/knot-holder.h
+++ b/src/ui/knot/knot-holder.h
@@ -32,6 +32,7 @@ class Node;
}
namespace LivePathEffect {
class PowerStrokePointArrayParamKnotHolderEntity;
+class ConnectorPointArrayParamKnotHolderEntity;
class SatellitesArrayParam;
class FilletChamferKnotHolderEntity;
}
@@ -76,6 +77,7 @@ public:
friend class Inkscape::UI::ShapeEditor; // FIXME why?
friend class Inkscape::LivePathEffect::SatellitesArrayParam; // why?
friend class Inkscape::LivePathEffect::PowerStrokePointArrayParamKnotHolderEntity; // why?
+ friend class Inkscape::LivePathEffect::ConnectorPointArrayParamKnotHolderEntity; // why?
friend class Inkscape::LivePathEffect::FilletChamferKnotHolderEntity; // why?
protected:
diff --git a/src/ui/tools/connector-tool.cpp b/src/ui/tools/connector-tool.cpp
index 5dba80ba23628d78922f812e5e161685c2a4362e..ff84b2868e9a099459283281420d1ee915702926 100644
--- a/src/ui/tools/connector-tool.cpp
+++ b/src/ui/tools/connector-tool.cpp
@@ -67,46 +67,40 @@
*
*/
-#include
-#include
+#include "ui/tools/connector-tool.h"
+#include
+#include
#include
#include
-#include
+#include
+#include "3rdparty/adaptagrams/libavoid/router.h"
#include "context-fns.h"
#include "desktop-style.h"
#include "desktop.h"
+#include "display/control/canvas-item-bpath.h"
+#include "display/control/canvas-item-ctrl.h"
+#include "display/curve.h"
#include "document-undo.h"
#include "document.h"
#include "inkscape.h"
#include "message-context.h"
#include "message-stack.h"
-#include "selection.h"
-#include "snap.h"
-#include "verbs.h"
-
-#include "display/control/canvas-item-bpath.h"
-#include "display/control/canvas-item-ctrl.h"
-#include "display/curve.h"
-
-#include "3rdparty/adaptagrams/libavoid/router.h"
-
#include "object/sp-conn-end.h"
+#include "object/sp-conn-router.h"
#include "object/sp-flowtext.h"
#include "object/sp-namedview.h"
#include "object/sp-path.h"
+#include "object/sp-symbol.h"
#include "object/sp-text.h"
#include "object/sp-use.h"
-#include "object/sp-symbol.h"
-
-#include "ui/knot/knot.h"
-#include "ui/widget/canvas.h" // Enter events
-
+#include "selection.h"
+#include "snap.h"
#include "svg/svg.h"
-
-#include "ui/tools/connector-tool.h"
-
+#include "ui/knot/knot.h"
+#include "ui/widget/canvas.h" // Enter events
+#include "verbs.h"
#include "xml/node-event-vector.h"
using Inkscape::DocumentUndo;
@@ -513,7 +507,6 @@ bool ConnectorTool::_handleButtonPress(GdkEventButton const &bevent)
m.unSetup();
}
this->_setInitialPoint(p);
-
}
this->state = SP_CONNECTOR_CONTEXT_DRAGGING;
ret = true;
@@ -819,7 +812,7 @@ void ConnectorTool::_setSubsequentPoint(Geom::Point const p)
Avoid::Point dst(d[Geom::X], d[Geom::Y]);
if (!this->newConnRef) {
- Avoid::Router *router = desktop->getDocument()->getRouter();
+ Avoid::Router *router = desktop->getDocument()->getRouter()->getAvoidRouter();
this->newConnRef = new Avoid::ConnRef(router);
this->newConnRef->setEndpoint(Avoid::VertID::src, src);
if (this->isOrthogonal)
@@ -1100,8 +1093,11 @@ void ConnectorTool::_activeShapeAddKnot(SPItem* item, SPItem* subitem)
void ConnectorTool::_setActiveShape(SPItem *item)
{
- g_assert(item != nullptr );
-
+ // We can get into this situation so remove the assert and do a check
+ // g_assert(item != nullptr );
+ if (!item) {
+ return;
+ }
if (this->active_shape != item) {
// The active shape has changed
// Rebuild everything
diff --git a/testfiles/src/attributes-test.cpp b/testfiles/src/attributes-test.cpp
index 759c05792382e2a15d10a6972fa0a4d9673e7110..cc039246d309927c65bc5248016debc9c21206d3 100644
--- a/testfiles/src/attributes-test.cpp
+++ b/testfiles/src/attributes-test.cpp
@@ -394,6 +394,7 @@ std::vector getKnownAttrs()
AttributeInfo("inkscape:connection-start-point", true),
AttributeInfo("inkscape:connector-avoid", true),
AttributeInfo("inkscape:connector-curvature", true),
+ AttributeInfo("inkscape:connector-continuous", true),
AttributeInfo("inkscape:connector-spacing", true),
AttributeInfo("inkscape:connector-type", true),
AttributeInfo("inkscape:corner0", true),