diff --git a/src/pen-context.cpp b/src/pen-context.cpp
index 1762d668c00d3ba7ccffaffe10b47b25b3eacaf8..5b9f6808aa0f3a82ab95bc322a380167836e1270 100644 (file)
--- a/src/pen-context.cpp
+++ b/src/pen-context.cpp
static SPDrawContextClass *pen_parent_class;
static int pen_next_paraxial_direction(const SPPenContext *const pc, Geom::Point const &pt, Geom::Point const &origin, guint state);
-static void pen_set_to_nearest_horiz_vert(const SPPenContext *const pc, Geom::Point &pt, guint const state);
-static Geom::Point pen_get_intermediate_horiz_vert(const SPPenContext *const pc, Geom::Point const &pt, guint const state);
+static void pen_set_to_nearest_horiz_vert(const SPPenContext *const pc, Geom::Point &pt, guint const state, bool snap);
static int pen_last_paraxial_dir = 0; // last used direction in horizontal/vertical mode; 0 = horizontal, 1 = vertical
pc->c1 = NULL;
pc->cl0 = NULL;
pc->cl1 = NULL;
-
+
pc->events_disabled = 0;
pc->num_clicks = 0;
}
static void
-pen_cancel (SPPenContext *const pc)
+pen_cancel (SPPenContext *const pc)
{
pc->num_clicks = 0;
pc->state = SP_PEN_CONTEXT_STOP;
{
SPPenContext *pc = SP_PEN_CONTEXT(ec);
+ sp_event_context_discard_delayed_snap_event(ec);
+
if (pc->npoints != 0) {
pen_cancel (pc);
}
static void
spdc_endpoint_snap(SPPenContext const *const pc, Geom::Point &p, guint const state)
{
- if ((state & GDK_CONTROL_MASK)) { //CTRL enables angular snapping
+ if ((state & GDK_CONTROL_MASK) && !pc->polylines_paraxial) { //CTRL enables angular snapping
if (pc->npoints > 0) {
spdc_endpoint_snap_rotation(pc, p, pc->p[0], state);
}
} else {
- if (!(state & GDK_SHIFT_MASK)) { //SHIFT disables all snapping, except the angular snapping above
- //After all, the user explicitely asked for angular snapping by
- //pressing CTRL
+ // We cannot use shift here to disable snapping because the shift-key is already used
+ // to toggle the paraxial direction; if the user wants to disable snapping (s)he will
+ // have to use the %-key, the menu, or the snap toolbar
+ if ((pc->npoints > 0) && pc->polylines_paraxial) {
+ // snap constrained
+ pen_set_to_nearest_horiz_vert(pc, p, state, true);
+ } else {
+ // snap freely
spdc_endpoint_snap_free(pc, p, state);
}
}
- if (pc->polylines_paraxial) {
- // TODO: must we avoid one of the snaps in the previous case distinction in some situations?
- pen_set_to_nearest_horiz_vert(pc, p, state);
- }
}
/**
@@ -332,7 +334,7 @@ spdc_endpoint_snap_handle(SPPenContext const *const pc, Geom::Point &p, guint co
}
}
-static gint
+static gint
sp_pen_context_item_handler(SPEventContext *ec, SPItem *item, GdkEvent *event)
{
SPPenContext *const pc = SP_PEN_CONTEXT(ec);
@@ -417,7 +419,7 @@ static gint pen_handle_button_press(SPPenContext *const pc, GdkEventButton const
SPDrawContext * const dc = SP_DRAW_CONTEXT(pc);
SPDesktop * const desktop = SP_EVENT_CONTEXT_DESKTOP(dc);
Geom::Point const event_w(bevent.x, bevent.y);
- Geom::Point const event_dt(desktop->w2d(event_w));
+ Geom::Point event_dt(desktop->w2d(event_w));
SPEventContext *event_context = SP_EVENT_CONTEXT(pc);
gint ret = FALSE;
@@ -453,7 +455,7 @@ static gint pen_handle_button_press(SPPenContext *const pc, GdkEventButton const
case SP_PEN_CONTEXT_CLOSE:
break;
case SP_PEN_CONTEXT_STOP:
- /* This is allowed, if we just cancelled curve */
+ /* This is allowed, if we just canceled curve */
pc->state = SP_PEN_CONTEXT_POINT;
break;
default:
@@ -463,14 +465,21 @@ static gint pen_handle_button_press(SPPenContext *const pc, GdkEventButton const
case SP_PEN_CONTEXT_MODE_DRAG:
switch (pc->state) {
case SP_PEN_CONTEXT_STOP:
- /* This is allowed, if we just cancelled curve */
+ /* This is allowed, if we just canceled curve */
case SP_PEN_CONTEXT_POINT:
if (pc->npoints == 0) {
- if (bevent.state & GDK_CONTROL_MASK) {
- spdc_create_single_dot(event_context, event_dt, "/tools/freehand/pen", bevent.state);
- ret = TRUE;
- break;
+ Geom::Point p;
+ if ((bevent.state & GDK_CONTROL_MASK) && (pc->polylines_only || pc->polylines_paraxial)) {
+ p = event_dt;
+ if (!(bevent.state & GDK_SHIFT_MASK)) {
+ SnapManager &m = desktop->namedview->snap_manager;
+ m.setup(desktop);
+ m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE);
+ }
+ spdc_create_single_dot(event_context, p, "/tools/freehand/pen", bevent.state);
+ ret = TRUE;
+ break;
}
// TODO: Perhaps it would be nicer to rearrange the following case
@@ -478,7 +487,6 @@ static gint pen_handle_button_press(SPPenContext *const pc, GdkEventButton const
/* Set start anchor */
pc->sa = anchor;
- Geom::Point p;
if (anchor && !sp_pen_context_has_waiting_LPE(pc)) {
/* Adjust point to anchor if needed; if we have a waiting LPE, we need
a fresh path to be created so don't continue an existing one */
@@ -500,11 +508,7 @@ static gint pen_handle_button_press(SPPenContext *const pc, GdkEventButton const
/* Create green anchor */
p = event_dt;
- if (!pc->polylines_paraxial) {
- // only snap the starting point if we're not in horizontal/vertical mode
- // because otherwise it gets shifted; TODO: why do we snap here at all??
- spdc_endpoint_snap(pc, p, bevent.state);
- }
+ spdc_endpoint_snap(pc, p, bevent.state);
pc->green_anchor = sp_draw_anchor_new(pc, pc->green_curve, TRUE, p);
}
spdc_pen_set_initial_point(pc, p);
@@ -531,9 +535,6 @@ static gint pen_handle_button_press(SPPenContext *const pc, GdkEventButton const
p = event_dt;
spdc_endpoint_snap(pc, p, bevent.state); /* Snap node only if not hitting anchor. */
spdc_pen_set_subsequent_point(pc, p, true);
- if (pc->polylines_only) {
- spdc_pen_finish_segment(pc, p, bevent.state);
- }
}
}
@@ -553,20 +554,22 @@ static gint pen_handle_button_press(SPPenContext *const pc, GdkEventButton const
default:
break;
}
- } else if (bevent.button == 3 || pc->expecting_clicks_for_LPE == 1) { // when the last click for a waiting LPE occurs we want to finish the path
- if (pc->npoints != 0) {
-
- spdc_pen_finish_segment(pc, event_dt, bevent.state);
- if (pc->green_closed) {
- // finishing at the start anchor, close curve
- spdc_pen_finish(pc, TRUE);
- } else {
- // finishing at some other anchor, finish curve but not close
- spdc_pen_finish(pc, FALSE);
- }
-
- ret = TRUE;
+ } else if (pc->expecting_clicks_for_LPE == 1 && pc->npoints != 0) {
+ // when the last click for a waiting LPE occurs we want to finish the path
+ spdc_pen_finish_segment(pc, event_dt, bevent.state);
+ if (pc->green_closed) {
+ // finishing at the start anchor, close curve
+ spdc_pen_finish(pc, TRUE);
+ } else {
+ // finishing at some other anchor, finish curve but not close
+ spdc_pen_finish(pc, FALSE);
}
+
+ ret = TRUE;
+ } else if (bevent.button == 3 && pc->npoints != 0) {
+ // right click - finish path
+ spdc_pen_finish(pc, FALSE);
+ ret = TRUE;
}
if (pc->expecting_clicks_for_LPE > 0) {
// allow scrolling
return FALSE;
}
-
+
if (pc->events_disabled) {
// skip motion events if pen events are disabled
return FALSE;
spdc_endpoint_snap(pc, p, mevent.state);
spdc_pen_set_subsequent_point(pc, p, true);
ret = TRUE;
+ } else if (!sp_event_context_knot_mouseover(pc)) {
+ SnapManager &m = dt->namedview->snap_manager;
+ m.setup(dt);
+ m.preSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_NODE_HANDLE));
}
break;
case SP_PEN_CONTEXT_CONTROL:
@@ -650,10 +657,11 @@ pen_handle_motion_notify(SPPenContext *const pc, GdkEventMotion const &mevent)
if (!anchor) { /* Snap node only if not hitting anchor */
spdc_endpoint_snap(pc, p, mevent.state);
+ spdc_pen_set_subsequent_point(pc, p, true, mevent.state);
+ } else {
+ spdc_pen_set_subsequent_point(pc, anchor->dp, false, mevent.state);
}
- spdc_pen_set_subsequent_point(pc, p, !anchor, mevent.state);
-
if (anchor && !pc->anchor_statusbar) {
pc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Click</b> or <b>click and drag</b> to close and finish the path."));
pc->anchor_statusbar = true;
pc->_message_context->clear();
pc->anchor_statusbar = false;
}
+ if (!sp_event_context_knot_mouseover(pc)) {
+ SnapManager &m = dt->namedview->snap_manager;
+ m.setup(dt);
+ m.preSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_NODE_HANDLE));
+ }
}
break;
case SP_PEN_CONTEXT_CONTROL:
/* This is perfectly valid */
break;
default:
+ if (!sp_event_context_knot_mouseover(pc)) {
+ SnapManager &m = dt->namedview->snap_manager;
+ m.setup(dt);
+ m.preSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_NODE_HANDLE));
+ }
break;
}
break;
ret = TRUE;
break;
case SP_PEN_CONTEXT_STOP:
- /* This is allowed, if we just cancelled curve */
+ /* This is allowed, if we just canceled curve */
pc->state = SP_PEN_CONTEXT_POINT;
ret = TRUE;
break;
@@ -777,10 +795,8 @@ pen_handle_button_release(SPPenContext *const pc, GdkEventButton const &revent)
switch (pc->state) {
case SP_PEN_CONTEXT_POINT:
case SP_PEN_CONTEXT_CONTROL:
- if (!pc->polylines_only) {
- spdc_endpoint_snap(pc, p, revent.state);
- spdc_pen_finish_segment(pc, p, revent.state);
- }
+ spdc_endpoint_snap(pc, p, revent.state);
+ spdc_pen_finish_segment(pc, p, revent.state);
break;
case SP_PEN_CONTEXT_CLOSE:
spdc_endpoint_snap(pc, p, revent.state);
pen_handle_2button_press(SPPenContext *const pc, GdkEventButton const &bevent)
{
gint ret = FALSE;
- if (pc->npoints != 0 && bevent.button != 2) {
+ // only end on LMB double click. Otherwise horizontal scrolling causes ending of the path
+ if (pc->npoints != 0 && bevent.button == 1) {
spdc_pen_finish(pc, FALSE);
ret = TRUE;
}
static gint
pen_handle_key_press(SPPenContext *const pc, GdkEvent *event)
{
+
gint ret = FALSE;
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
gdouble const nudge = prefs->getDoubleLimited("/options/nudgedistance/value", 2, 0, 1000); // in px
}
break;
+/* TODO: this is not yet enabled?? looks like some traces of the Geometry tool
case GDK_P:
case GDK_p:
if (MOD__SHIFT_ONLY) {
ret = TRUE;
}
break;
+*/
case GDK_U:
case GDK_u:
sp_canvas_item_hide(pc->cl1);
pc->state = SP_PEN_CONTEXT_POINT;
spdc_pen_set_subsequent_point(pc, pt, true);
+ pen_last_paraxial_dir = !pen_last_paraxial_dir;
ret = TRUE;
}
break;
* Show the status message for the current line/curve segment.
* This type of message always shows angle/distance as the last
* two parameters ("angle %3.2f°, distance %s").
- */
+ */
static void
spdc_pen_set_angle_distance_status_message(SPPenContext *const pc, Geom::Point const p, int pc_point_to_compare, gchar const *message)
{
@@ -1235,16 +1256,20 @@ spdc_pen_set_subsequent_point(SPPenContext *const pc, Geom::Point const p, bool
bool is_curve;
pc->red_curve->moveto(pc->p[0]);
if (pc->polylines_paraxial && !statusbar) {
- // we are drawing horizontal/vertical lines and hit an anchor; draw an L-shaped path
- Geom::Point intermed = p;
- pen_set_to_nearest_horiz_vert(pc, intermed, status);
- pc->red_curve->lineto(intermed);
+ // we are drawing horizontal/vertical lines and hit an anchor;
+ Geom::Point const origin = pc->p[0];
+ // if the previous point and the anchor are not aligned either horizontally or vertically...
+ if ((abs(p[Geom::X] - origin[Geom::X]) > 1e-9) && (abs(p[Geom::Y] - origin[Geom::Y]) > 1e-9)) {
+ // ...then we should draw an L-shaped path, consisting of two paraxial segments
+ Geom::Point intermed = p;
+ pen_set_to_nearest_horiz_vert(pc, intermed, status, false);
+ pc->red_curve->lineto(intermed);
+ }
pc->red_curve->lineto(p);
is_curve = false;
} else {
// one of the 'regular' modes
- if (pc->p[1] != pc->p[0])
- {
+ if (pc->p[1] != pc->p[0]) {
pc->red_curve->curveto(pc->p[1], p, p);
is_curve = true;
} else {
@@ -1312,6 +1337,7 @@ spdc_pen_finish_segment(SPPenContext *const pc, Geom::Point const p, guint const
if (pc->polylines_paraxial) {
pen_last_paraxial_dir = pen_next_paraxial_direction(pc, p, pc->p[0], state);
}
+
++pc->num_clicks;
if (!pc->red_curve->is_empty()) {
pc->num_clicks = 0;
pen_disable_events(pc);
-
+
SPDesktop *const desktop = pc->desktop;
pc->_message_context->clear();
desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Drawing finished"));
static void
pen_enable_events(SPPenContext *const pc) {
g_return_if_fail(pc->events_disabled != 0);
-
+
pc->events_disabled--;
}
* horizontal or vertical segment; for all subsequent mouse clicks, we use the direction
* orthogonal to the last one; pressing Shift toggles the direction
*/
- if (pc->num_clicks == 0) {
+ // num_clicks is not reliable because spdc_pen_finish_segment is sometimes called too early
+ // (on first mouse release), in which case num_clicks immediately becomes 1.
+ // if (pc->num_clicks == 0) {
+
+ if (pc->green_curve->is_empty()) {
// first mouse click
double dist_h = fabs(pt[Geom::X] - origin[Geom::X]);
double dist_v = fabs(pt[Geom::Y] - origin[Geom::Y]);
}
}
-void pen_set_to_nearest_horiz_vert(const SPPenContext *const pc, Geom::Point &pt, guint const state)
+void pen_set_to_nearest_horiz_vert(const SPPenContext *const pc, Geom::Point &pt, guint const state, bool snap)
{
- Geom::Point const &origin = pc->p[0];
+ Geom::Point const origin = pc->p[0];
int next_dir = pen_next_paraxial_direction(pc, pt, origin, state);
- if (next_dir == 0) {
- // line is forced to be horizontal
- pt[Geom::Y] = origin[Geom::Y];
+ if (!snap) {
+ if (next_dir == 0) {
+ // line is forced to be horizontal
+ pt[Geom::Y] = origin[Geom::Y];
+ } else {
+ // line is forced to be vertical
+ pt[Geom::X] = origin[Geom::X];
+ }
} else {
- // line is forced to be vertical
- pt[Geom::X] = origin[Geom::X];
- }
-}
+ // Create a horizontal or vertical constraint line
+ Inkscape::Snapper::ConstraintLine cl(origin, next_dir ? Geom::Point(0, 1) : Geom::Point(1, 0));
-Geom::Point pen_get_intermediate_horiz_vert(const SPPenContext *const pc, Geom::Point const &pt)
-{
- Geom::Point const &origin = pc->p[0];
+ // Snap along the constraint line; if we didn't snap then still the constraint will be applied
+ SnapManager &m = pc->desktop->namedview->snap_manager;
- if (pen_last_paraxial_dir == 0) {
- return Geom::Point (origin[Geom::X], pt[Geom::Y]);
- } else {
- return Geom::Point (pt[Geom::X], origin[Geom::Y]);
+ Inkscape::Selection *selection = sp_desktop_selection (pc->desktop);
+ // selection->singleItem() is the item that is currently being drawn. This item will not be snapped to (to avoid self-snapping)
+ // TODO: Allow snapping to the stationary parts of the item, and only ignore the last segment
+
+ m.setup(pc->desktop, true, selection->singleItem());
+ m.constrainedSnapReturnByRef(pt, Inkscape::SNAPSOURCE_NODE_HANDLE, cl);
}
}