From bcff6d0c1905aafd905bcba36cce583210c4fb7a Mon Sep 17 00:00:00 2001 From: Ian Bruce Date: Sun, 29 Jul 2018 05:31:57 +0000 Subject: [PATCH 1/3] This patch fixes the behaviour of control-handle locking during conversion to smooth and symmetric path nodes, to conform to what is described in the keyboard reference and the manual. https://inkscape.org/en/doc/keys092.html#idm2176 http://tavmjong.free.fr/INKSCAPE/MANUAL/html/Paths-Editing.html See for complete details. --- src/ui/tool/node.cpp | 132 +++++++++++++++++++++++++++---------------- 1 file changed, 84 insertions(+), 48 deletions(-) diff --git a/src/ui/tool/node.cpp b/src/ui/tool/node.cpp index 9223b44def..a3c590ccf5 100644 --- a/src/ui/tool/node.cpp +++ b/src/ui/tool/node.cpp @@ -158,9 +158,8 @@ void Handle::move(Geom::Point const &new_pos) break; } } - // If the segment between the handle and the node - // in its direction becomes linear and there are smooth nodes - // at its ends, make their handles colinear with the segment + // If the segment between the handle and the node in its direction becomes linear, + // and there are smooth nodes at its ends, make their handles collinear with the segment. if (towards && towards_second->isDegenerate()) { if (node_towards->type() == NODE_SMOOTH) { towards->setDirection(*_parent, *node_towards); @@ -171,7 +170,7 @@ void Handle::move(Geom::Point const &new_pos) } setPosition(new_pos); - //move the handler and its opposite the same proportion + // move the handle and its opposite the same proportion if(_pm()._isBSpline()){ setPosition(_pm()._bsplineHandleReposition(this, false)); bspline_weight = _pm()._bsplineHandlePosition(this, false); @@ -189,7 +188,7 @@ void Handle::move(Geom::Point const &new_pos) / Geom::L2sq(direction)) * direction; setRelativePos(new_delta); - //move the handler and its opposite the same proportion + // move the handle and its opposite the same proportion if(_pm()._isBSpline()){ setPosition(_pm()._bsplineHandleReposition(this, false)); bspline_weight = _pm()._bsplineHandlePosition(this, false); @@ -204,8 +203,8 @@ void Handle::move(Geom::Point const &new_pos) _parent->setType(NODE_SMOOTH, false); // fall through - auto nodes degrade into smooth nodes case NODE_SMOOTH: { - /* for smooth nodes, we need to rotate the other handle so that it's colinear - * with the dragged one while conserving length. */ + // for smooth nodes, we need to rotate the opposite handle + // so that it's collinear with the dragged one, while conserving length. other->setDirection(new_pos, *_parent); } break; case NODE_SYMMETRIC: @@ -216,7 +215,7 @@ void Handle::move(Geom::Point const &new_pos) } setPosition(new_pos); - // moves the handler and its opposite the same proportion + // move the handle and its opposite the same proportion if(_pm()._isBSpline()){ setPosition(_pm()._bsplineHandleReposition(this, false)); bspline_weight = _pm()._bsplineHandlePosition(this, false); @@ -280,37 +279,75 @@ bool Handle::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEven switch (event->type) { case GDK_KEY_PRESS: + switch (shortcut_key(event->key)) { case GDK_KEY_s: case GDK_KEY_S: + + /* if Shift+S is pressed while hovering over a cusp node handle, + hold the handle in place; otherwise, process normally. + this handle is guaranteed not to be degenerate. */ + if (held_only_shift(event->key) && _parent->_type == NODE_CUSP) { - // when Shift+S is pressed when hovering over a handle belonging to a cusp node, - // hold this handle in place; otherwise process normally - // this handle is guaranteed not to be degenerate - other()->move(_parent->position() - (position() - _parent->position())); + + // make opposite handle collinear, + // but preserve length, unless degenerate + if (other()->isDegenerate()) + other()->setRelativePos(-relativePos()); + else + other()->setDirection(-relativePos()); _parent->setType(NODE_SMOOTH, false); - _parent->_pm().update(); // magic triple combo to add undo event - _parent->_pm().writeXML(); + + // update display + _parent->_pm().update(); + + // update undo history _parent->_pm()._commit(_("Change node type")); + + return true; + } + break; + + case GDK_KEY_y: + case GDK_KEY_Y: + + /* if Shift+Y is pressed while hovering over a cusp, smooth, or auto node handle, + hold the handle in place; otherwise, process normally. + this handle is guaranteed not to be degenerate. */ + + if (held_only_shift(event->key) && (_parent->_type == NODE_CUSP || + _parent->_type == NODE_SMOOTH || + _parent->_type == NODE_AUTO)) { + + // make opposite handle collinear, and of equal length + other()->setRelativePos(-relativePos()); + _parent->setType(NODE_SYMMETRIC, false); + + // update display + _parent->_pm().update(); + + // update undo history + _parent->_pm()._commit(_("Change node type")); + return true; } break; - default: break; } break; - // new double click event to set the handlers of a node to the default proportion, DEFAULT_START_POWER% + case GDK_2BUTTON_PRESS: + + // double-click event to set the handles of a node + // to the position specified by DEFAULT_START_POWER handle_2button_press(); break; - - default: break; } return ControlPoint::_eventHandler(event_context, event); } -//this function moves the handler and its opposite to the default proportion of DEFAULT_START_POWER +// this function moves the handle and its opposite to the position specified by DEFAULT_START_POWER void Handle::handle_2button_press(){ if(_pm()._isBSpline()){ setPosition(_pm()._bsplineHandleReposition(this, DEFAULT_START_POWER)); @@ -367,7 +404,7 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) ctrl_constraint = Inkscape::Snapper::SnapConstraint(parent_pos, parent_pos - perp_pos); } new_pos = result; - // moves the handler and its opposite in X fixed positions depending on parameter "steps with control" + // move the handle and its opposite in X fixed positions depending on parameter "steps with control" // by default in live BSpline if(_pm()._isBSpline()){ setPosition(new_pos); @@ -377,7 +414,7 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) } std::vector unselected; - //if the snap adjustment is activated and it is not bspline + // if the snap adjustment is activated and it is not BSpline if (snap && !_pm()._isBSpline()) { ControlPointSelection::Set &nodes = _parent->_selection.allPoints(); for (ControlPointSelection::Set::iterator i = nodes.begin(); i != nodes.end(); ++i) { @@ -418,7 +455,7 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) other()->setPosition(_saved_other_pos); } } - //if it is bspline but SHIFT or CONTROL are not pressed it fixes it in the original position + // if it is BSpline, but SHIFT or CONTROL are not pressed, fix it in the original position if(_pm()._isBSpline() && !held_shift(*event) && !held_control(*event)){ new_pos=_last_drag_origin(); } @@ -480,8 +517,9 @@ static double snap_increment_degrees() { Glib::ustring Handle::_getTip(unsigned state) const { char const *more; - // a trick to mark as bspline if the node has no strength, we are going to use it later - // to show the appropriate messages. We cannot do it in any different way because the function is constant + /* a trick to mark as BSpline if the node has no strength; + we are going to use it later to show the appropriate messages. + we cannot do it in any different way because the function is constant. */ Handle *h = const_cast(this); bool isBSpline = _pm()._isBSpline(); bool can_shift_rotate = _parent->type() == NODE_CUSP && !other()->isDegenerate(); @@ -640,11 +678,11 @@ void Node::move(Geom::Point const &new_pos) _front.setPosition(_front.position() + delta); _back.setPosition(_back.position() + delta); - // if the node has a smooth handle after a line segment, it should be kept colinear + // if the node has a smooth handle after a line segment, it should be kept collinear // with the segment _fixNeighbors(old_pos, new_pos); - // move the affected handlers. First the node ones, later the adjoining ones. + // move the affected handles. First the node ones, later the adjoining ones. if(_pm()._isBSpline()){ _front.setPosition(_pm()._bsplineHandleReposition(this->front(),nodeWeight)); _back.setPosition(_pm()._bsplineHandleReposition(this->back(),nodeWeight)); @@ -685,7 +723,7 @@ void Node::transform(Geom::Affine const &m) * but smooth nodes at ends of linear segments and auto nodes need special treatment */ _fixNeighbors(old_pos, position()); - // move the involved handlers, first the node ones, later the adjoining ones + // move the involved handles. First the node ones, later the adjoining ones. if(_pm()._isBSpline()){ _front.setPosition(_pm()._bsplineHandleReposition(this->front(), nodeWeight)); _back.setPosition(_pm()._bsplineHandleReposition(this->back(), nodeWeight)); @@ -708,10 +746,10 @@ Geom::Rect Node::bounds() const void Node::_fixNeighbors(Geom::Point const &old_pos, Geom::Point const &new_pos) { - /* This method restores handle invariants for neighboring nodes, - * and invariants that are based on positions of those nodes for this one. */ + // This method restores handle invariants for neighboring nodes, + // and invariants that are based on positions of those nodes for this one. - /* Fix auto handles */ + // Fix auto handles if (_type == NODE_AUTO) _updateAutoHandles(); if (old_pos != new_pos) { if (_next() && _next()->_type == NODE_AUTO) _next()->_updateAutoHandles(); @@ -719,8 +757,8 @@ void Node::_fixNeighbors(Geom::Point const &old_pos, Geom::Point const &new_pos) } /* Fix smooth handles at the ends of linear segments. - * Rotate the appropriate handle to be colinear with the segment. - * If there is a smooth node at the other end of the segment, rotate it too. */ + Rotate the appropriate handle to be collinear with the segment. + If there is a smooth node at the other end of the segment, rotate it too. */ Handle *handle, *other_handle; Node *other; if (_is_line_segment(this, _next())) { @@ -744,17 +782,16 @@ void Node::_fixNeighbors(Geom::Point const &old_pos, Geom::Point const &new_pos) void Node::_updateAutoHandles() { - // Recompute the position of automatic handles. - // For endnodes, retract both handles. (It's only possible to create an end auto node - // through the XML editor.) + // Recompute the position of automatic handles. For endnodes, retract both handles. + // (It's only possible to create an end auto node through the XML editor.) if (isEndNode()) { _front.retract(); _back.retract(); return; } - // Auto nodes automatically adjust their handles to give an appearance of smoothness, - // no matter what their surroundings are. + // auto nodes automatically adjust their handles to give + // an appearance of smoothness, no matter what their surroundings are. Geom::Point vec_next = _next()->position() - position(); Geom::Point vec_prev = _prev()->position() - position(); double len_next = vec_next.length(), len_prev = vec_prev.length(); @@ -816,7 +853,7 @@ void Node::setType(NodeType type, bool update_handles) case NODE_SMOOTH: { // ignore attempts to make smooth endnodes. if (isEndNode()) return; - // rotate handles to be colinear + // rotate handles to be collinear // for degenerate nodes set positions like auto handles bool prev_line = _is_line_segment(_prev(), this); bool next_line = _is_line_segment(this, _next()); @@ -834,9 +871,8 @@ void Node::setType(NodeType type, bool update_handles) } else if (isDegenerate()) { _updateAutoHandles(); } else if (_front.isDegenerate()) { - // if the front handle is degenerate and this...next is a line segment, - // make back colinear; otherwise pull out the other handle - // to 1/3 of distance to prev + // if the front handle is degenerate and next path segment is a line, make back collinear; + // otherwise, pull out the other handle to 1/3 of distance to prev. if (next_line) { _back.setDirection(*_next(), *this); } else if (_prev()) { @@ -851,10 +887,10 @@ void Node::setType(NodeType type, bool update_handles) _back.setRelativePos(Geom::distance(_next()->position(), position()) / 3 * dir); } } else { - // both handles are extended. make colinear while keeping length - // first make back colinear with the vector front ---> back, - // then make front colinear with back ---> node - // (not back ---> front because back's position was changed in the first call) + /* both handles are extended. make collinear while keeping length. + first make back collinear with the vector front ---> back, + then make front collinear with back ---> node. + (not back ---> front, because back's position was changed in the first call) */ _back.setDirection(_front, _back); _front.setDirection(_back, *this); } @@ -883,8 +919,8 @@ void Node::setType(NodeType type, bool update_handles) break; default: break; } - /* in node type changes, about bspline traces, we can maintain them with NO_POWER power in border mode, - or we give them the default power in curve mode */ + // in node type changes, for BSpline traces, we can either maintain them + // with NO_POWER power in border mode, or give them the default power in curve mode. if(_pm()._isBSpline()){ double weight = NO_POWER; if(_pm()._bsplineHandlePosition(this->front()) != NO_POWER ){ @@ -1140,7 +1176,7 @@ void Node::_setState(State state) case STATE_CLICKED: mgr.setActive(_canvas_item, true); mgr.setPrelight(_canvas_item, false); - //this shows the handlers when selecting the nodes + // show the handles when selecting the nodes if(_pm()._isBSpline()){ this->front()->setPosition(_pm()._bsplineHandleReposition(this->front())); this->back()->setPosition(_pm()._bsplineHandleReposition(this->back())); -- GitLab From efcdd82838762a24175aba4e923545d041842031 Mon Sep 17 00:00:00 2001 From: Ian Bruce Date: Sun, 29 Jul 2018 06:59:43 +0000 Subject: [PATCH 2/3] This patch alters the tip information displayed during path node editing, to more accurately describe the actions available for the control that the mouse is currently hovering over. This patch should be applied on top of my first commit, bcff6d0c. Unlike that, this one does not alter Inkscape's functionality in any way, but presumably will require translation, which is why they have been separated. Full details are available at . --- src/ui/tool/node.cpp | 297 +++++++++++++++++++++++++++++++------------ 1 file changed, 215 insertions(+), 82 deletions(-) diff --git a/src/ui/tool/node.cpp b/src/ui/tool/node.cpp index a3c590ccf5..4f2c42f7c8 100644 --- a/src/ui/tool/node.cpp +++ b/src/ui/tool/node.cpp @@ -265,13 +265,24 @@ void Handle::setDirection(Geom::Point const &dir) char const *Handle::handle_type_to_localized_string(NodeType type) { + char const *s = ""; + switch(type) { - case NODE_CUSP: return _("Cusp node handle"); - case NODE_SMOOTH: return _("Smooth node handle"); - case NODE_SYMMETRIC: return _("Symmetric node handle"); - case NODE_AUTO: return _("Auto-smooth node handle"); - default: return ""; + case NODE_CUSP: + s = _("Corner node handle"); + break; + case NODE_SMOOTH: + s = _("Smooth node handle"); + break; + case NODE_SYMMETRIC: + s = _("Symmetric node handle"); + break; + case NODE_AUTO: + s = _("Auto-smooth node handle"); + break; } + + return (s); } bool Handle::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *event) @@ -514,79 +525,147 @@ static double snap_increment_degrees() { return 180.0 / snaps; } +#define DRAG_TO_SHAPE "drag to shape the path" +#define HOVER_TO_LOCK "hover to lock" +#define MAKE_SMOOTH "Shift+S to make smooth" +#define MAKE_SYMMETRIC "Shift+Y to make symmetric" +#define META_KEYS "(more: %s)" + Glib::ustring Handle::_getTip(unsigned state) const { - char const *more; /* a trick to mark as BSpline if the node has no strength; we are going to use it later to show the appropriate messages. we cannot do it in any different way because the function is constant. */ Handle *h = const_cast(this); bool isBSpline = _pm()._isBSpline(); bool can_shift_rotate = _parent->type() == NODE_CUSP && !other()->isDegenerate(); - if (can_shift_rotate && !isBSpline) { - more = C_("Path handle tip", "more: Shift, Ctrl, Alt"); - } else if(isBSpline){ - more = C_("Path handle tip", "more: Ctrl"); - }else { - more = C_("Path handle tip", "more: Ctrl, Alt"); - } + Glib::ustring s = C_("Path handle tip", + "node control handle"); // not expected + if (state_held_alt(state) && !isBSpline) { if (state_held_control(state)) { if (state_held_shift(state) && can_shift_rotate) { - return format_tip(C_("Path handle tip", - "Shift+Ctrl+Alt: preserve length and snap rotation angle to %g° " - "increments while rotating both handles"), + s = format_tip(C_("Path handle tip", + "Shift+Ctrl+Alt: " + "preserve length and snap rotation angle to %g° increments, " + "and rotate both handles"), snap_increment_degrees()); - } else { - return format_tip(C_("Path handle tip", - "Ctrl+Alt: preserve length and snap rotation angle to %g° increments"), + } + else { + s = format_tip(C_("Path handle tip", + "Ctrl+Alt: " + "preserve length and snap rotation angle to %g° increments"), snap_increment_degrees()); } - } else { + } + else { if (state_held_shift(state) && can_shift_rotate) { - return C_("Path handle tip", + s = C_("Path handle tip", "Shift+Alt: preserve handle length and rotate both handles"); - } else { - return C_("Path handle tip", + } + else { + s = C_("Path handle tip", "Alt: preserve handle length while dragging"); } } - } else { + } + else { if (state_held_control(state)) { if (state_held_shift(state) && can_shift_rotate && !isBSpline) { - return format_tip(C_("Path handle tip", - "Shift+Ctrl: snap rotation angle to %g° increments and rotate both handles"), + s = format_tip(C_("Path handle tip", + "Shift+Ctrl: " + "snap rotation angle to %g° increments, and rotate both handles"), snap_increment_degrees()); - } else if(isBSpline){ - return format_tip(C_("Path handle tip", - "Ctrl: Snap handle to steps defined in BSpline Live Path Effect")); - }else{ - return format_tip(C_("Path handle tip", - "Ctrl: snap rotation angle to %g° increments, click to retract"), + } + else if (isBSpline) { + s = C_("Path handle tip", + "Ctrl: " + "Snap handle to steps defined in BSpline Live Path Effect"); + } + else { + s = format_tip(C_("Path handle tip", + "Ctrl: " + "snap rotation angle to %g° increments, click to retract"), snap_increment_degrees()); } - } else if (state_held_shift(state) && can_shift_rotate && !isBSpline) { - return C_("Path hande tip", + } + else if (state_held_shift(state) && can_shift_rotate && !isBSpline) { + s = C_("Path handle tip", "Shift: rotate both handles by the same angle"); - } else if(state_held_shift(state) && isBSpline){ - return C_("Path hande tip", + } + else if (state_held_shift(state) && isBSpline) { + s = C_("Path handle tip", "Shift: move handle"); } - } + else { + char const *handletype = handle_type_to_localized_string(_parent->_type); + char const *more; - switch (_parent->type()) { - case NODE_AUTO: - return format_tip(C_("Path handle tip", - "Auto node handle: drag to convert to smooth node (%s)"), more); - default: - if(!isBSpline){ - return format_tip(C_("Path handle tip", - "Auto node handle: drag to convert to smooth node (%s)"), more); - }else{ - return format_tip(C_("Path handle tip", - "BSpline node handle: Shift to drag, double click to reset (%s). %g power"),more,_pm()._bsplineHandlePosition(h)); + if (can_shift_rotate && !isBSpline) { + more = C_("Path handle tip", + "Shift, Ctrl, Alt"); + } + else if (isBSpline) { + more = C_("Path handle tip", + "Ctrl"); + } + else { + more = C_("Path handle tip", + "Ctrl, Alt"); + } + + if (_parent->type() == NODE_CUSP) { + s = format_tip(C_("Path handle tip", + "%s: " + DRAG_TO_SHAPE ", " + HOVER_TO_LOCK ", " + MAKE_SMOOTH ", " + MAKE_SYMMETRIC ". " + META_KEYS), + handletype, more); + } + else if (_parent->type() == NODE_SMOOTH) { + s = format_tip(C_("Path handle tip", + "%s: " + DRAG_TO_SHAPE ", " + HOVER_TO_LOCK ", " + MAKE_SYMMETRIC ". " + META_KEYS), + handletype, more); + } + else if (_parent->type() == NODE_AUTO) { + s = format_tip(C_("Path handle tip", + "%s: " + "drag to make smooth, " + HOVER_TO_LOCK ", " + MAKE_SYMMETRIC ". " + META_KEYS), + handletype, more); + } + else if (_parent->type() == NODE_SYMMETRIC) { + s = format_tip(C_("Path handle tip", + "%s: " + DRAG_TO_SHAPE ". " + META_KEYS), + handletype, more); + } + else if (isBSpline) { + double power = _pm()._bsplineHandlePosition(h); + s = format_tip(C_("Path handle tip", + "BSpline node handle (%.3g power): " + "Shift-drag to move, " + "double-click to reset. " + "(more: %s)"), + power, more); + } + else { + s = C_("Path handle tip", + "unknown node handle"); // not expected + } } } + + return (s); } Glib::ustring Handle::_getDragTip(GdkEventMotion */*event*/) const @@ -1434,58 +1513,101 @@ Node *Node::nodeAwayFrom(Handle *h) return nullptr; } +#define CLICK_TO_SELECT "click to select only this node" +#define CLICK_TO_TOGGLE "click to toggle scale/rotation handles" +#define SHIFT_CTRL_ALT "(more: Shift, Ctrl, Alt)" + Glib::ustring Node::_getTip(unsigned state) const { bool isBSpline = _pm()._isBSpline(); Handle *h = const_cast(&_front); + Glib::ustring s = C_("Path node tip", + "node handle"); // not expected + if (state_held_shift(state)) { - bool can_drag_out = (_next() && _front.isDegenerate()) || (_prev() && _back.isDegenerate()); + bool can_drag_out = (_next() && _front.isDegenerate()) || + (_prev() && _back.isDegenerate()); + if (can_drag_out) { /*if (state_held_control(state)) { - return format_tip(C_("Path node tip", + s = format_tip(C_("Path node tip", "Shift+Ctrl: drag out a handle and snap its angle " "to %f° increments"), snap_increment_degrees()); }*/ - return C_("Path node tip", + s = C_("Path node tip", "Shift: drag out a handle, click to toggle selection"); } - return C_("Path node tip", "Shift: click to toggle selection"); + else { + s = C_("Path node tip", + "Shift: click to toggle selection"); + } } - if (state_held_control(state)) { + else if (state_held_control(state)) { if (state_held_alt(state)) { - return C_("Path node tip", "Ctrl+Alt: move along handle lines, click to delete node"); + s = C_("Path node tip", + "Ctrl+Alt: move along handle lines, click to delete node"); } - return C_("Path node tip", + else { + s = C_("Path node tip", "Ctrl: move along axes, click to change node type"); + } } - if (state_held_alt(state)) { - return C_("Path node tip", "Alt: sculpt nodes"); + else if (state_held_alt(state)) { + s = C_("Path node tip", + "Alt: sculpt nodes"); } - // No modifiers: assemble tip from node type - char const *nodetype = node_type_to_localized_string(_type); - double power = _pm()._bsplineHandlePosition(h); - if (_selection.transformHandlesEnabled() && selected()) { - if (_selection.size() == 1 && !isBSpline) { - return format_tip(C_("Path node tip", - "%s: drag to shape the path (more: Shift, Ctrl, Alt)"), nodetype); - }else if(_selection.size() == 1){ - return format_tip(C_("Path node tip", - "BSpline node: drag to shape the path (more: Shift, Ctrl, Alt). %g power"), power); + else { // No modifiers: assemble tip from node type + char const *nodetype = node_type_to_localized_string(_type); + double power = _pm()._bsplineHandlePosition(h); + + if (_selection.transformHandlesEnabled() && selected()) { + if (_selection.size() == 1) { + if (!isBSpline) { + s = format_tip(C_("Path node tip", + "%s: " + DRAG_TO_SHAPE ". " + SHIFT_CTRL_ALT), + nodetype); + } + else { + s = format_tip(C_("Path node tip", + "BSpline node (%.3g power): " + DRAG_TO_SHAPE ". " + SHIFT_CTRL_ALT), + power); + } + } + else { + s = format_tip(C_("Path node tip", + "%s: " + DRAG_TO_SHAPE ", " + CLICK_TO_TOGGLE ". " + SHIFT_CTRL_ALT), + nodetype); + } + } + else if (!isBSpline) { + s = format_tip(C_("Path node tip", + "%s: " + DRAG_TO_SHAPE ", " + CLICK_TO_SELECT ". " + SHIFT_CTRL_ALT), + nodetype); + } + else { + s = format_tip(C_("Path node tip", + "BSpline node (%.3g power): " + DRAG_TO_SHAPE ", " + CLICK_TO_SELECT ". " + SHIFT_CTRL_ALT), + power); } - return format_tip(C_("Path node tip", - "%s: drag to shape the path, click to toggle scale/rotation handles (more: Shift, Ctrl, Alt)"), nodetype); - } - if (!isBSpline) { - return format_tip(C_("Path node tip", - "%s: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt)"), nodetype); - }else{ - return format_tip(C_("Path node tip", - "BSpline node: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt). %g power"), power); - } + + return (s); } Glib::ustring Node::_getDragTip(GdkEventMotion */*event*/) const @@ -1502,13 +1624,24 @@ Glib::ustring Node::_getDragTip(GdkEventMotion */*event*/) const char const *Node::node_type_to_localized_string(NodeType type) { + char const *s = ""; + switch (type) { - case NODE_CUSP: return _("Cusp node"); - case NODE_SMOOTH: return _("Smooth node"); - case NODE_SYMMETRIC: return _("Symmetric node"); - case NODE_AUTO: return _("Auto-smooth node"); - default: return ""; + case NODE_CUSP: + s = _("Corner node"); + break; + case NODE_SMOOTH: + s = _("Smooth node"); + break; + case NODE_SYMMETRIC: + s = _("Symmetric node"); + break; + case NODE_AUTO: + s = _("Auto-smooth node"); + break; } + + return (s); } bool Node::_is_line_segment(Node *first, Node *second) -- GitLab From 910a423e9d6531ae29014c14e48235feee5e63a5 Mon Sep 17 00:00:00 2001 From: Ian Bruce Date: Sun, 29 Jul 2018 07:56:35 +0000 Subject: [PATCH 3/3] temporary fix for bug where undo/redo does not restore node-type changes This is not intended to be a permanent patch, but to point out the actual source of a problem where undo/redo does not work for node-type changes. It works by disabling the function of the PathManipulator::_nodetypesKey() method, to match some breakage which has apparently occurred somewhere else. Presumably, somebody would know where that was. Once that is located and fixed, this patch will be unnecessary, except for a minor code cleanup. At that point, the functional change would itself constitute a bug, instead of a temporary fix. Full details are available at . --- src/ui/tool/path-manipulator.cpp | 33 +++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp index 2369a75d41..d49e9bfdc5 100644 --- a/src/ui/tool/path-manipulator.cpp +++ b/src/ui/tool/path-manipulator.cpp @@ -192,20 +192,23 @@ void PathManipulator::writeXML() { if (!_live_outline) _updateOutline(); + _setGeometry(); - if (!_path) return; - _observer->block(); - if (!empty()) { - _path->updateRepr(); - _getXMLNode()->setAttribute(_nodetypesKey().data(), _createTypeString().data()); - } else { - // this manipulator will have to be destroyed right after this call - _getXMLNode()->removeObserver(*_observer); - _path->deleteObject(true, true); - _path = nullptr; + if (_path) { + _observer->block(); + if (!empty()) { + _path->updateRepr(); + _getXMLNode()->setAttribute(_nodetypesKey().data(), _createTypeString().data()); + } + else { + // this manipulator will have to be destroyed right after this call + _getXMLNode()->removeObserver(*_observer); + _path->deleteObject(true, true); + _path = nullptr; + } + _observer->unblock(); } - _observer->unblock(); } /** Remove all nodes from the path. */ @@ -1526,8 +1529,12 @@ void PathManipulator::_setGeometry() /** Figure out in what attribute to store the nodetype string. */ Glib::ustring PathManipulator::_nodetypesKey() { - if (_lpe_key.empty()) return "sodipodi:nodetypes"; - return _lpe_key + "-nodetypes"; + // temporary fix + // if (_lpe_key.empty()) + if (false) + return ("sodipodi:nodetypes"); + else + return (_lpe_key + "-nodetypes"); } /** Return the XML node we are editing. -- GitLab