summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDiederik van Lierop <mail@diedenrezi.nl>2015-05-30 18:27:42 +0000
committerDiederik van Lierop <mail@diedenrezi.nl>2015-05-30 18:27:42 +0000
commitc4c42ebb66d55ca883ed93b2a72d26cd8a759a6d (patch)
tree37b2f5771b29ccd54c1d1428501266d5bba6057f /src
parentUpdating gtest version with newer attributes. (diff)
downloadinkscape-c4c42ebb66d55ca883ed93b2a72d26cd8a759a6d.tar.gz
inkscape-c4c42ebb66d55ca883ed93b2a72d26cd8a759a6d.zip
Snapping in node tool now also works when:
- when double clicking to insert a node on a path - when dragging a part of the path to deform it Fixed bugs: - https://launchpad.net/bugs/1448859 (bzr r14189)
Diffstat (limited to 'src')
-rw-r--r--src/snap-enums.h2
-rw-r--r--src/snap.cpp36
-rw-r--r--src/snap.h11
-rw-r--r--src/ui/tool/control-point.cpp15
-rw-r--r--src/ui/tool/control-point.h6
-rw-r--r--src/ui/tool/curve-drag-point.cpp20
-rw-r--r--src/ui/tool/curve-drag-point.h6
-rw-r--r--src/ui/tool/multi-path-manipulator.cpp8
-rw-r--r--src/ui/tool/multi-path-manipulator.h1
-rw-r--r--src/ui/tool/path-manipulator.cpp36
-rw-r--r--src/ui/tool/path-manipulator.h6
-rw-r--r--src/ui/tool/selector.cpp4
-rw-r--r--src/ui/tool/selector.h1
-rw-r--r--src/ui/tools/node-tool.cpp106
-rw-r--r--src/ui/tools/node-tool.h3
15 files changed, 204 insertions, 57 deletions
diff --git a/src/snap-enums.h b/src/snap-enums.h
index c6ca97402..ab82aaf37 100644
--- a/src/snap-enums.h
+++ b/src/snap-enums.h
@@ -66,7 +66,7 @@ enum SnapTargetType {
SNAPTARGET_NODE_SMOOTH,
SNAPTARGET_NODE_CUSP,
SNAPTARGET_LINE_MIDPOINT,
- SNAPTARGET_PATH,
+ SNAPTARGET_PATH, // If path targets are added here, then also add them to the list in findBestSnap()
SNAPTARGET_PATH_PERPENDICULAR,
SNAPTARGET_PATH_TANGENTIAL,
SNAPTARGET_PATH_INTERSECTION,
diff --git a/src/snap.cpp b/src/snap.cpp
index 30441ca0b..5a308777c 100644
--- a/src/snap.cpp
+++ b/src/snap.cpp
@@ -121,7 +121,8 @@ void SnapManager::freeSnapReturnByRef(Geom::Point &p,
}
Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::SnapCandidatePoint const &p,
- Geom::OptRect const &bbox_to_snap) const
+ Geom::OptRect const &bbox_to_snap,
+ bool to_paths_only) const
{
if (!someSnapperMightSnap()) {
return Inkscape::SnappedPoint(p, Inkscape::SNAPTARGET_UNDEFINED, Geom::infinity(), 0, false, false, false);
@@ -134,16 +135,16 @@ Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::SnapCandidatePoint const
(*i)->freeSnap(isr, p, bbox_to_snap, &_items_to_ignore, _unselected_nodes);
}
- return findBestSnap(p, isr, false);
+ return findBestSnap(p, isr, false, false, to_paths_only);
}
-void SnapManager::preSnap(Inkscape::SnapCandidatePoint const &p)
+void SnapManager::preSnap(Inkscape::SnapCandidatePoint const &p, bool to_paths_only)
{
// setup() must have been called before calling this method!
if (_snapindicator) {
_snapindicator = false; // prevent other methods from drawing a snap indicator; we want to control this here
- Inkscape::SnappedPoint s = freeSnap(p);
+ Inkscape::SnappedPoint s = freeSnap(p, Geom::OptRect(), to_paths_only);
g_assert(_desktop != NULL);
if (s.getSnapped()) {
_desktop->snapindicator->set_new_snaptarget(s, true);
@@ -855,7 +856,8 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapRotate(std::vector<Inkscape::
Inkscape::SnappedPoint SnapManager::findBestSnap(Inkscape::SnapCandidatePoint const &p,
IntermSnapResults const &isr,
bool constrained,
- bool allowOffScreen) const
+ bool allowOffScreen,
+ bool to_path_only) const
{
g_assert(_desktop != NULL);
@@ -966,6 +968,30 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(Inkscape::SnapCandidatePoint co
}
}
+ // Filter out all snap targets that do NOT include a path; this is useful when we try to insert
+ // a node in a path (on doubleclick in the node tool). We don't want to change the shape of the
+ // path, so the snapped point must be on a path, and not e.g. on a grid intersection
+ if (to_path_only) {
+ std::list<Inkscape::SnappedPoint>::iterator i = sp_list.begin();
+
+ while (i != sp_list.end()) {
+ Inkscape::SnapTargetType t = (*i).getTarget();
+ if (t == Inkscape::SNAPTARGET_LINE_MIDPOINT ||
+ t == Inkscape::SNAPTARGET_PATH ||
+ t == Inkscape::SNAPTARGET_PATH_PERPENDICULAR ||
+ t == Inkscape::SNAPTARGET_PATH_TANGENTIAL ||
+ t == Inkscape::SNAPTARGET_PATH_INTERSECTION ||
+ t == Inkscape::SNAPTARGET_PATH_GUIDE_INTERSECTION ||
+ t == Inkscape::SNAPTARGET_PATH_CLIP ||
+ t == Inkscape::SNAPTARGET_PATH_MASK ||
+ t == Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT) {
+ ++i;
+ } else {
+ i = sp_list.erase(i);
+ }
+ }
+ }
+
// now let's see which snapped point gets a thumbs up
Inkscape::SnappedPoint bestSnappedPoint(p.getPoint());
// std::cout << "Finding the best snap..." << std::endl;
diff --git a/src/snap.h b/src/snap.h
index 49bdbfa7c..944c49b3e 100644
--- a/src/snap.h
+++ b/src/snap.h
@@ -192,12 +192,15 @@ public:
*
* @param p Source point to be snapped.
* @param bbox_to_snap Bounding box hulling the set of points, all from the same selection and having the same transformation.
+ * @param to_path_only Only snap to points on a path, such as path intersections with itself or with grids/guides. This is used for
+ * example when adding nodes to a path. We will not snap for example to grid intersections
* @return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
*/
Inkscape::SnappedPoint freeSnap(Inkscape::SnapCandidatePoint const &p,
- Geom::OptRect const &bbox_to_snap = Geom::OptRect() ) const;
+ Geom::OptRect const &bbox_to_snap = Geom::OptRect(),
+ bool to_path_only = false) const;
- void preSnap(Inkscape::SnapCandidatePoint const &p);
+ void preSnap(Inkscape::SnapCandidatePoint const &p, bool to_path_only = false);
/**
* Snap to the closest multiple of a grid pitch.
@@ -473,9 +476,11 @@ public:
* @param isr A structure holding all snap targets that have been found so far.
* @param constrained True if the snap is constrained, e.g. for stretching or for purely horizontal translation.
* @param allowOffScreen If true, then snapping to points which are off the screen is allowed (needed for example when pasting to the grid).
+ * @param to_path_only Only snap to points on a path, such as path intersections with itself or with grids/guides. This is used for
+ * example when adding nodes to a path. We will not snap for example to grid intersections
* @return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
*/
- Inkscape::SnappedPoint findBestSnap(Inkscape::SnapCandidatePoint const &p, IntermSnapResults const &isr, bool constrained, bool allowOffScreen = false) const;
+ Inkscape::SnappedPoint findBestSnap(Inkscape::SnapCandidatePoint const &p, IntermSnapResults const &isr, bool constrained, bool allowOffScreen = false, bool to_paths_only = false) const;
/**
* Mark the location of the snap source (not the snap target!) on the canvas by drawing a symbol.
diff --git a/src/ui/tool/control-point.cpp b/src/ui/tool/control-point.cpp
index bcf5c9fce..636595016 100644
--- a/src/ui/tool/control-point.cpp
+++ b/src/ui/tool/control-point.cpp
@@ -71,7 +71,8 @@ ControlPoint::ControlPoint(SPDesktop *d, Geom::Point const &initial_pos, SPAncho
_cset(cset),
_state(STATE_NORMAL),
_position(initial_pos),
- _lurking(false)
+ _lurking(false),
+ _double_clicked(false)
{
_canvas_item = sp_canvas_item_new(
group ? group : _desktop->getControls(), SP_TYPE_CTRL,
@@ -80,6 +81,7 @@ ControlPoint::ControlPoint(SPDesktop *d, Geom::Point const &initial_pos, SPAncho
"filled", TRUE, "fill_color", _cset.normal.fill,
"stroked", TRUE, "stroke_color", _cset.normal.stroke,
"mode", SP_CTRL_MODE_XOR, NULL);
+
_commonInit();
}
@@ -91,7 +93,8 @@ ControlPoint::ControlPoint(SPDesktop *d, Geom::Point const &initial_pos, SPAncho
_cset(cset),
_state(STATE_NORMAL),
_position(initial_pos),
- _lurking(false)
+ _lurking(false),
+ _double_clicked(false)
{
_canvas_item = ControlManager::getManager().createControl(group ? group : _desktop->getControls(), type);
g_object_set(_canvas_item,
@@ -245,7 +248,8 @@ bool ControlPoint::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, G
static Geom::Point pointer_offset;
// number of last doubleclicked button
static unsigned next_release_doubleclick = 0;
-
+ _double_clicked = false;
+
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
int drag_tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
GdkEventMotion em;
@@ -278,6 +282,7 @@ bool ControlPoint::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, G
Ca = _desktop->canvas;
em = event->motion;
combine_motion_events(Ca, em, 0);
+
if (_event_grab && ! event_context->space_panning) {
_desktop->snapindicator->remove_snaptarget();
bool transferred = false;
@@ -298,6 +303,7 @@ bool ControlPoint::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, G
_drag_initiated = true;
}
}
+
if (!transferred) {
// dragging in progress
Geom::Point new_pos = _desktop->w2d(event_point(event->motion)) + pointer_offset;
@@ -305,7 +311,7 @@ bool ControlPoint::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, G
dragged(new_pos, &em);
move(new_pos);
_updateDragTip(&em); // update dragging tip after moving to new position
-
+
_desktop->scroll_to_point(new_pos);
_desktop->set_coordinate_status(_position);
sp_event_context_snap_delay_handler(event_context, NULL,
@@ -342,6 +348,7 @@ bool ControlPoint::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, G
} else {
// it is the end of a click
if (next_release_doubleclick) {
+ _double_clicked = true;
return doubleclicked(&event->button);
} else {
return clicked(&event->button);
diff --git a/src/ui/tool/control-point.h b/src/ui/tool/control-point.h
index b3ed9545e..4a01b9f21 100644
--- a/src/ui/tool/control-point.h
+++ b/src/ui/tool/control-point.h
@@ -193,6 +193,8 @@ public:
virtual bool _eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *event);
SPDesktop *const _desktop; ///< The desktop this control point resides on.
+ bool doubleClicked() {return _double_clicked;}
+
protected:
struct ColorEntry {
@@ -361,6 +363,8 @@ protected:
/** Events which should be captured when a handle is being dragged. */
static int const _grab_event_mask;
+ static bool _drag_initiated;
+
private:
ControlPoint(ControlPoint const &other);
@@ -397,7 +401,7 @@ private:
static bool _event_grab;
- static bool _drag_initiated;
+ bool _double_clicked;
};
diff --git a/src/ui/tool/curve-drag-point.cpp b/src/ui/tool/curve-drag-point.cpp
index 23640456e..d1756fa2c 100644
--- a/src/ui/tool/curve-drag-point.cpp
+++ b/src/ui/tool/curve-drag-point.cpp
@@ -15,6 +15,8 @@
#include "ui/tool/multi-path-manipulator.h"
#include "ui/tool/path-manipulator.h"
#include "ui/tool/node.h"
+#include "sp-namedview.h"
+#include "snap.h"
namespace Inkscape {
namespace UI {
@@ -77,6 +79,16 @@ void CurveDragPoint::dragged(Geom::Point &new_pos, GdkEventMotion *event)
return;
}
+ if (_drag_initiated && !(event->state & GDK_SHIFT_MASK)) {
+ SnapManager &m = _desktop->namedview->snap_manager;
+ SPItem *path = static_cast<SPItem *>(_pm._path);
+ m.setup(_desktop, true, path); // We will not try to snap to "path" itself
+ Inkscape::SnapCandidatePoint scp(new_pos, Inkscape::SNAPSOURCE_OTHER_HANDLE);
+ Inkscape::SnappedPoint sp = m.freeSnap(scp, Geom::OptRect(), false);
+ new_pos = sp.getPoint();
+ m.unSetup();
+ }
+
// Magic Bezier Drag Equations follow!
// "weight" describes how the influence of the drag should be distributed
// among the handles; 0 = front handle only, 1 = back handle only.
@@ -166,14 +178,8 @@ void CurveDragPoint::_insertNode(bool take_selection)
// Otherwise clicks on the new node would only work after the user moves the mouse a bit.
// PathManipulator will restore visibility when necessary.
setVisible(false);
- NodeList::iterator inserted = _pm.subdivideSegment(first, _t);
- if (take_selection) {
- _pm._selection.clear();
- }
- _pm._selection.insert(inserted.ptr());
- _pm.update(true);
- _pm._commit(_("Add node"));
+ _pm.insertNode(first, _t, take_selection);
}
Glib::ustring CurveDragPoint::_getTip(unsigned state) const
diff --git a/src/ui/tool/curve-drag-point.h b/src/ui/tool/curve-drag-point.h
index ea83978e0..c1d40575f 100644
--- a/src/ui/tool/curve-drag-point.h
+++ b/src/ui/tool/curve-drag-point.h
@@ -34,7 +34,9 @@ public:
CurveDragPoint(PathManipulator &pm);
void setSize(double sz) { _setSize(sz); }
void setTimeValue(double t) { _t = t; }
+ double getTimeValue() { return _t; }
void setIterator(NodeList::iterator i) { first = i; }
+ NodeList::iterator getIterator() { return first; }
virtual bool _eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *event);
protected:
@@ -47,9 +49,6 @@ protected:
virtual bool doubleclicked(GdkEventButton *);
private:
-
- void _insertNode(bool take_selection);
-
double _t;
PathManipulator &_pm;
NodeList::iterator first;
@@ -57,6 +56,7 @@ private:
static bool _drags_stroke;
static bool _segment_was_degenerate;
static Geom::Point _stroke_drag_origin;
+ void _insertNode(bool take_selection);
};
} // namespace UI
diff --git a/src/ui/tool/multi-path-manipulator.cpp b/src/ui/tool/multi-path-manipulator.cpp
index f53cef5f4..46c6246a1 100644
--- a/src/ui/tool/multi-path-manipulator.cpp
+++ b/src/ui/tool/multi-path-manipulator.cpp
@@ -339,6 +339,14 @@ void MultiPathManipulator::insertNodesAtExtrema(ExtremumType extremum)
_done(_("Add extremum nodes"));
}
+void MultiPathManipulator::insertNode(Geom::Point pt)
+{
+ // When double clicking to insert nodes, we might not have a selection of nodes (and we don't need one)
+ // so don't check for "_selection.empty()" here, contrary to the other methods above and below this one
+ invokeForAll(&PathManipulator::insertNode, pt);
+ _done(_("Add nodes"));
+}
+
void MultiPathManipulator::duplicateNodes()
{
if (_selection.empty()) return;
diff --git a/src/ui/tool/multi-path-manipulator.h b/src/ui/tool/multi-path-manipulator.h
index 1bbcdd7ec..c908cede2 100644
--- a/src/ui/tool/multi-path-manipulator.h
+++ b/src/ui/tool/multi-path-manipulator.h
@@ -53,6 +53,7 @@ public:
void insertNodesAtExtrema(ExtremumType extremum);
void insertNodes();
+ void insertNode(Geom::Point pt);
void alertLPE();
void duplicateNodes();
void joinNodes();
diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp
index 6b0c95f68..a772c07c2 100644
--- a/src/ui/tool/path-manipulator.cpp
+++ b/src/ui/tool/path-manipulator.cpp
@@ -174,7 +174,8 @@ bool PathManipulator::event(Inkscape::UI::Tools::ToolBase * /*event_context*/, G
case GDK_MOTION_NOTIFY:
_updateDragPoint(event_point(event->motion));
break;
- default: break;
+ default:
+ break;
}
return false;
}
@@ -275,6 +276,27 @@ void PathManipulator::insertNodes()
}
}
+void PathManipulator::insertNode(Geom::Point pt)
+{
+ Geom::Coord dist = _updateDragPoint(pt);
+ if (dist < 1e-5) { // 1e-6 is too small, as observed occasionally when inserting a node at a snapped intersection of paths
+ insertNode(_dragpoint->getIterator(), _dragpoint->getTimeValue(), true);
+ }
+}
+
+void PathManipulator::insertNode(NodeList::iterator first, double t, bool take_selection)
+{
+ NodeList::iterator inserted = subdivideSegment(first, t);
+ if (take_selection) {
+ _selection.clear();
+ }
+ _selection.insert(inserted.ptr());
+
+ update(true);
+ _commit(_("Add node"));
+}
+
+
static void
add_or_replace_if_extremum(std::vector< std::pair<NodeList::iterator, double> > &vec,
double & extrvalue, double testvalue, NodeList::iterator const& node, double t)
@@ -1643,13 +1665,15 @@ void PathManipulator::_commit(Glib::ustring const &annotation, gchar const *key)
/** Update the position of the curve drag point such that it is over the nearest
* point of the path. */
-void PathManipulator::_updateDragPoint(Geom::Point const &evp)
+Geom::Coord PathManipulator::_updateDragPoint(Geom::Point const &evp)
{
+ Geom::Coord dist = 1e23;
+
Geom::Affine to_desktop = _edit_transform * _i2d_transform;
Geom::PathVector pv = _spcurve->get_pathvector();
boost::optional<Geom::PathVectorPosition> pvp
= Geom::nearestPoint(pv, _desktop->w2d(evp) * to_desktop.inverse());
- if (!pvp) return;
+ if (!pvp) return dist;
Geom::Point nearest_point = _desktop->d2w(pv.at(pvp->path_nr).pointAt(pvp->t) * to_desktop);
double fracpart;
@@ -1657,10 +1681,12 @@ void PathManipulator::_updateDragPoint(Geom::Point const &evp)
for (unsigned i = 0; i < pvp->path_nr; ++i, ++spi) {}
NodeList::iterator first = (*spi)->before(pvp->t, &fracpart);
+ dist = Geom::distance(evp, nearest_point);
+
double stroke_tolerance = _getStrokeTolerance();
if (first && first.next() &&
fracpart != 0.0 &&
- Geom::distance(evp, nearest_point) < stroke_tolerance)
+ dist < stroke_tolerance)
{
_dragpoint->setVisible(true);
_dragpoint->setPosition(_desktop->w2d(nearest_point));
@@ -1670,6 +1696,8 @@ void PathManipulator::_updateDragPoint(Geom::Point const &evp)
} else {
_dragpoint->setVisible(false);
}
+
+ return dist;
}
/// This is called on zoom change to update the direction arrows
diff --git a/src/ui/tool/path-manipulator.h b/src/ui/tool/path-manipulator.h
index 2219af849..4c6f74ba4 100644
--- a/src/ui/tool/path-manipulator.h
+++ b/src/ui/tool/path-manipulator.h
@@ -70,6 +70,8 @@ public:
void insertNodeAtExtremum(ExtremumType extremum);
void insertNodes();
+ void insertNode(Geom::Point);
+ void insertNode(NodeList::iterator first, double t, bool take_selection);
void duplicateNodes();
void weldNodes(NodeList::iterator preserve_pos = NodeList::iterator());
void weldSegments();
@@ -133,7 +135,7 @@ private:
void _removeNodesFromSelection();
void _commit(Glib::ustring const &annotation);
void _commit(Glib::ustring const &annotation, gchar const *key);
- void _updateDragPoint(Geom::Point const &);
+ Geom::Coord _updateDragPoint(Geom::Point const &);
void _updateOutlineOnZoomChange();
double _getStrokeTolerance();
Handle *_chooseHandle(Node *n, int which);
@@ -143,7 +145,7 @@ private:
SPPath *_path; ///< can be an SPPath or an Inkscape::LivePathEffect::Effect !!!
SPCurve *_spcurve; // in item coordinates
SPCanvasItem *_outline;
- CurveDragPoint *_dragpoint; // an invisible control point hoverng over curve
+ CurveDragPoint *_dragpoint; // an invisible control point hovering over curve
PathManipulatorObserver *_observer;
Geom::Affine _d2i_transform; ///< desktop-to-item transform
Geom::Affine _i2d_transform; ///< item-to-desktop transform, inverse of _d2i_transform
diff --git a/src/ui/tool/selector.cpp b/src/ui/tool/selector.cpp
index e4e701785..051cb41ae 100644
--- a/src/ui/tool/selector.cpp
+++ b/src/ui/tool/selector.cpp
@@ -129,6 +129,10 @@ bool Selector::event(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *eve
return false;
}
+bool Selector::doubleClicked() {
+ return _dragger->doubleClicked();
+}
+
} // namespace UI
} // namespace Inkscape
diff --git a/src/ui/tool/selector.h b/src/ui/tool/selector.h
index dbe751ede..bd8d3e1aa 100644
--- a/src/ui/tool/selector.h
+++ b/src/ui/tool/selector.h
@@ -29,6 +29,7 @@ public:
Selector(SPDesktop *d);
virtual ~Selector();
virtual bool event(Inkscape::UI::Tools::ToolBase *, GdkEvent *);
+ virtual bool doubleClicked();
sigc::signal<void, Geom::Rect const &, GdkEventButton*> signal_area;
sigc::signal<void, Geom::Point const &, GdkEventButton*> signal_point;
diff --git a/src/ui/tools/node-tool.cpp b/src/ui/tools/node-tool.cpp
index ef00eaa40..761492f41 100644
--- a/src/ui/tools/node-tool.cpp
+++ b/src/ui/tools/node-tool.cpp
@@ -26,6 +26,8 @@
#include "ui/shape-editor.h" // temporary!
#include "live_effects/effect.h"
#include "display/curve.h"
+#include "snap.h"
+#include "sp-namedview.h"
#include "sp-clippath.h"
#include "sp-item-group.h"
#include "sp-mask.h"
@@ -112,7 +114,7 @@ namespace UI {
namespace Tools {
const std::string& NodeTool::getPrefsPath() {
- return NodeTool::prefsPath;
+ return NodeTool::prefsPath;
}
const std::string NodeTool::prefsPath = "/tools/nodes";
@@ -216,7 +218,7 @@ void NodeTool::setup() {
Inkscape::UI::ControlPoint::signal_mouseover_change.connect(sigc::mem_fun(this, &NodeTool::mouseover_changed));
this->_sizeUpdatedConn = ControlManager::getManager().connectCtrlSizeChanged(
- sigc::mem_fun(this, &NodeTool::handleControlUiStyleChange)
+ sigc::mem_fun(this, &NodeTool::handleControlUiStyleChange)
);
this->_selected_nodes = new Inkscape::UI::ControlPointSelection(this->desktop, this->_transform_handle_group);
@@ -236,14 +238,14 @@ void NodeTool::setup() {
);
this->_selected_nodes->signal_selection_changed.connect(
- // Hide both signal parameters and bind the function parameter to 0
- // sigc::signal<void, SelectableControlPoint *, bool>
- // <=>
- // void update_tip(GdkEvent *event)
- sigc::hide(sigc::hide(sigc::bind(
- sigc::mem_fun(this, &NodeTool::update_tip),
- (GdkEvent*)NULL
- )))
+ // Hide both signal parameters and bind the function parameter to 0
+ // sigc::signal<void, SelectableControlPoint *, bool>
+ // <=>
+ // void update_tip(GdkEvent *event)
+ sigc::hide(sigc::hide(sigc::bind(
+ sigc::mem_fun(this, &NodeTool::update_tip),
+ (GdkEvent*)NULL
+ )))
);
this->helperpath_tmpitem = NULL;
@@ -359,7 +361,7 @@ void NodeTool::set(const Inkscape::Preferences::Entry& value) {
this->edit_masks = value.getBool();
this->selection_changed(this->desktop->selection);
} else {
- ToolBase::set(value);
+ ToolBase::set(value);
}
}
@@ -370,7 +372,7 @@ void gather_items(NodeTool *nt, SPItem *base, SPObject *obj, Inkscape::UI::Shape
using namespace Inkscape::UI;
if (!obj) {
- return;
+ return;
}
//XML Tree being used directly here while it shouldn't be.
@@ -446,6 +448,9 @@ void NodeTool::selection_changed(Inkscape::Selection *sel) {
}
}
+ _previous_selection = _current_selection;
+ _current_selection = sel->itemList();
+
this->_multipath->setItems(shapes);
this->update_tip(NULL);
this->desktop->updateNow();
@@ -458,31 +463,47 @@ bool NodeTool::root_handler(GdkEvent* event) {
* 3. some keybindings
*/
using namespace Inkscape::UI; // pull in event helpers
-
+
Inkscape::Selection *selection = desktop->selection;
static Inkscape::Preferences *prefs = Inkscape::Preferences::get();
if (this->_multipath->event(this, event)) {
- return true;
+ return true;
}
if (this->_selector->event(this, event)) {
- return true;
+ return true;
}
if (this->_selected_nodes->event(this, event)) {
- return true;
+ return true;
}
switch (event->type)
{
case GDK_MOTION_NOTIFY: {
- this->update_helperpath();
+ this->update_helperpath();
combine_motion_events(desktop->canvas, event->motion, 0);
this->update_helperpath();
SPItem *over_item = sp_event_context_find_item (desktop, event_point(event->button),
FALSE, TRUE);
+ Geom::Point const motion_w(event->motion.x, event->motion.y);
+ Geom::Point const motion_dt(this->desktop->w2d(motion_w));
+
+ SnapManager &m = this->desktop->namedview->snap_manager;
+
+ // We will show a pre-snap indication for when the user adds a node through double-clicking
+ // Adding a node will only work when a path has been selected; if that's not the case then snapping is useless
+ if (not this->desktop->selection->isEmpty()) {
+ if (!(event->motion.state & GDK_SHIFT_MASK)) {
+ m.setup(this->desktop);
+ Inkscape::SnapCandidatePoint scp(motion_dt, Inkscape::SNAPSOURCE_OTHER_HANDLE);
+ m.preSnap(scp, true);
+ m.unSetup();
+ }
+ }
+
if (over_item != this->_last_over) {
this->_last_over = over_item;
//ink_node_tool_update_tip(nt, event);
@@ -491,11 +512,11 @@ bool NodeTool::root_handler(GdkEvent* event) {
// create pathflash outline
if (prefs->getBool("/tools/nodes/pathflash_enabled")) {
if (over_item == this->flashed_item) {
- break;
+ break;
}
if (!prefs->getBool("/tools/nodes/pathflash_selected") && selection->includes(over_item)) {
- break;
+ break;
}
if (this->flash_tempitem) {
@@ -505,14 +526,14 @@ bool NodeTool::root_handler(GdkEvent* event) {
}
if (!SP_IS_SHAPE(over_item)) {
- break; // for now, handle only shapes
+ break; // for now, handle only shapes
}
this->flashed_item = over_item;
SPCurve *c = SP_SHAPE(over_item)->getCurveBeforeLPE();
if (!c) {
- break; // break out when curve doesn't exist
+ break; // break out when curve doesn't exist
}
c->transform(over_item->i2dt_affine());
@@ -575,11 +596,42 @@ bool NodeTool::root_handler(GdkEvent* event) {
case GDK_KEY_RELEASE:
//ink_node_tool_update_tip(nt, event);
- this->update_tip(event);
+ this->update_tip(event);
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ if (this->_selector->doubleClicked()) {
+ // If the selector received the doubleclick event, then we're at some distance from
+ // the path; otherwise, the doubleclick event would have been received by
+ // CurveDragPoint; we will insert nodes into the path anyway but only if we can snap
+ // to the path. Otherwise the position would not be very well defined.
+ if (!(event->motion.state & GDK_SHIFT_MASK)) {
+ Geom::Point const motion_w(event->motion.x, event->motion.y);
+ Geom::Point const motion_dt(this->desktop->w2d(motion_w));
+
+ SnapManager &m = this->desktop->namedview->snap_manager;
+ m.setup(this->desktop);
+ Inkscape::SnapCandidatePoint scp(motion_dt, Inkscape::SNAPSOURCE_OTHER_HANDLE);
+ Inkscape::SnappedPoint sp = m.freeSnap(scp, Geom::OptRect(), true);
+ m.unSetup();
+
+ if (sp.getSnapped()) {
+ // The first click of the double click will have cleared the path selection, because
+ // we clicked aside of the path. We need to undo this on double click
+ Inkscape::Selection *selection = desktop->getSelection();
+ selection->addList(_previous_selection);
+
+ // The selection has been restored, and the signal selection_changed has been emitted,
+ // which has again forced a restore of the _mmap variable of the MultiPathManipulator (this->_multipath)
+ // Now we can insert the new nodes as if nothing has happened!
+ this->_multipath->insertNode(this->desktop->d2w(sp.getPoint()));
+ }
+ }
+ }
break;
default:
- break;
+ break;
}
return ToolBase::root_handler(event);
@@ -592,7 +644,7 @@ void NodeTool::update_tip(GdkEvent *event) {
unsigned new_state = state_after_event(event);
if (new_state == event->key.state) {
- return;
+ return;
}
if (state_held_shift(new_state)) {
@@ -661,7 +713,7 @@ void NodeTool::select_area(Geom::Rect const &sel, GdkEventButton *event) {
selection->setList(items);
} else {
if (!held_shift(*event)) {
- this->_selected_nodes->clear();
+ this->_selected_nodes->clear();
}
this->_selected_nodes->selectArea(sel);
@@ -672,11 +724,11 @@ void NodeTool::select_point(Geom::Point const &/*sel*/, GdkEventButton *event) {
using namespace Inkscape::UI; // pull in event helpers
if (!event) {
- return;
+ return;
}
if (event->button != 1) {
- return;
+ return;
}
Inkscape::Selection *selection = this->desktop->selection;
diff --git a/src/ui/tools/node-tool.h b/src/ui/tools/node-tool.h
index 20375e869..d5a21e0c6 100644
--- a/src/ui/tools/node-tool.h
+++ b/src/ui/tools/node-tool.h
@@ -85,6 +85,9 @@ private:
bool show_transform_handles;
bool single_node_transform_handles;
+ std::vector<SPItem*> _current_selection;
+ std::vector<SPItem*> _previous_selection;
+
void selection_changed(Inkscape::Selection *sel);
void select_area(Geom::Rect const &sel, GdkEventButton *event);