summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/display/snap-indicator.cpp2
-rw-r--r--src/event-context.cpp218
-rw-r--r--src/select-context.cpp16
-rw-r--r--src/seltrans.cpp150
-rw-r--r--src/seltrans.h16
-rw-r--r--src/snap-candidate.h8
-rw-r--r--src/snap.cpp53
-rw-r--r--src/snap.h15
-rw-r--r--src/ui/tool/control-point-selection.cpp1
-rw-r--r--src/ui/tool/control-point.cpp27
-rw-r--r--src/ui/tool/transform-handle-set.cpp147
-rw-r--r--src/ui/tool/transform-handle-set.h26
12 files changed, 387 insertions, 292 deletions
diff --git a/src/display/snap-indicator.cpp b/src/display/snap-indicator.cpp
index 9b61d4a4d..3b9bb57e1 100644
--- a/src/display/snap-indicator.cpp
+++ b/src/display/snap-indicator.cpp
@@ -310,7 +310,7 @@ SnapIndicator::set_new_snapsource(Inkscape::SnapCandidatePoint const &p)
{
remove_snapsource();
- g_assert(_desktop != NULL);
+ g_assert(_desktop != NULL); // If this fails, then likely setup() has not been called on the snap manager (see snap.cpp -> setup())
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
bool value = prefs->getBool("/options/snapindicator/value", true);
diff --git a/src/event-context.cpp b/src/event-context.cpp
index 4f938bde5..7dc52bea0 100644
--- a/src/event-context.cpp
+++ b/src/event-context.cpp
@@ -541,117 +541,117 @@ static gint sp_event_context_private_root_handler(
// in the editing window). So we resteal them back and run our regular shortcut
// invoker on them.
unsigned int shortcut;
- case GDK_Tab:
- case GDK_ISO_Left_Tab:
- case GDK_F1:
- shortcut = get_group0_keyval(&event->key);
- if (event->key.state & GDK_SHIFT_MASK)
- shortcut |= SP_SHORTCUT_SHIFT_MASK;
- if (event->key.state & GDK_CONTROL_MASK)
- shortcut |= SP_SHORTCUT_CONTROL_MASK;
- if (event->key.state & GDK_MOD1_MASK)
- shortcut |= SP_SHORTCUT_ALT_MASK;
- ret = sp_shortcut_invoke(shortcut, desktop);
- break;
+ case GDK_Tab:
+ case GDK_ISO_Left_Tab:
+ case GDK_F1:
+ shortcut = get_group0_keyval(&event->key);
+ if (event->key.state & GDK_SHIFT_MASK)
+ shortcut |= SP_SHORTCUT_SHIFT_MASK;
+ if (event->key.state & GDK_CONTROL_MASK)
+ shortcut |= SP_SHORTCUT_CONTROL_MASK;
+ if (event->key.state & GDK_MOD1_MASK)
+ shortcut |= SP_SHORTCUT_ALT_MASK;
+ ret = sp_shortcut_invoke(shortcut, desktop);
+ break;
- case GDK_D:
- case GDK_d:
- if (!MOD__SHIFT && !MOD__CTRL && !MOD__ALT) {
- sp_toggle_dropper(desktop);
- ret = TRUE;
- }
- break;
- case GDK_Q:
- case GDK_q:
- if (desktop->quick_zoomed()) {
- ret = TRUE;
- }
- if (!MOD__SHIFT && !MOD__CTRL && !MOD__ALT) {
- desktop->zoom_quick(true);
- ret = TRUE;
- }
- break;
- case GDK_W:
- case GDK_w:
- case GDK_F4:
- /* Close view */
- if (MOD__CTRL_ONLY) {
- sp_ui_close_view(NULL);
- ret = TRUE;
- }
- break;
- case GDK_Left: // Ctrl Left
- case GDK_KP_Left:
- case GDK_KP_4:
- if (MOD__CTRL_ONLY) {
- int i = (int) floor(key_scroll * accelerate_scroll(event,
- acceleration, sp_desktop_canvas(desktop)));
- gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
- event_context->desktop->scroll_world(i, 0);
- ret = TRUE;
- }
- break;
- case GDK_Up: // Ctrl Up
- case GDK_KP_Up:
- case GDK_KP_8:
- if (MOD__CTRL_ONLY) {
- int i = (int) floor(key_scroll * accelerate_scroll(event,
- acceleration, sp_desktop_canvas(desktop)));
- gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
- event_context->desktop->scroll_world(0, i);
- ret = TRUE;
- }
- break;
- case GDK_Right: // Ctrl Right
- case GDK_KP_Right:
- case GDK_KP_6:
- if (MOD__CTRL_ONLY) {
- int i = (int) floor(key_scroll * accelerate_scroll(event,
- acceleration, sp_desktop_canvas(desktop)));
- gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
- event_context->desktop->scroll_world(-i, 0);
- ret = TRUE;
- }
- break;
- case GDK_Down: // Ctrl Down
- case GDK_KP_Down:
- case GDK_KP_2:
- if (MOD__CTRL_ONLY) {
- int i = (int) floor(key_scroll * accelerate_scroll(event,
- acceleration, sp_desktop_canvas(desktop)));
- gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
- event_context->desktop->scroll_world(0, -i);
- ret = TRUE;
- }
- break;
- case GDK_F10:
- if (MOD__SHIFT_ONLY) {
- sp_event_root_menu_popup(desktop, NULL, event);
- ret = TRUE;
- }
- break;
- case GDK_space:
- if (prefs->getBool("/options/spacepans/value")) {
- event_context->space_panning = true;
- event_context->_message_context->set(Inkscape::INFORMATION_MESSAGE,
- _("<b>Space+mouse drag</b> to pan canvas"));
- ret = TRUE;
- } else {
- sp_toggle_selector(desktop);
- ret = TRUE;
- }
- break;
- case GDK_z:
- case GDK_Z:
- if (MOD__ALT_ONLY) {
- desktop->zoom_grab_focus();
- ret = TRUE;
- }
- break;
- default:
- break;
+ case GDK_D:
+ case GDK_d:
+ if (!MOD__SHIFT && !MOD__CTRL && !MOD__ALT) {
+ sp_toggle_dropper(desktop);
+ ret = TRUE;
+ }
+ break;
+ case GDK_Q:
+ case GDK_q:
+ if (desktop->quick_zoomed()) {
+ ret = TRUE;
+ }
+ if (!MOD__SHIFT && !MOD__CTRL && !MOD__ALT) {
+ desktop->zoom_quick(true);
+ ret = TRUE;
+ }
+ break;
+ case GDK_W:
+ case GDK_w:
+ case GDK_F4:
+ /* Close view */
+ if (MOD__CTRL_ONLY) {
+ sp_ui_close_view(NULL);
+ ret = TRUE;
+ }
+ break;
+ case GDK_Left: // Ctrl Left
+ case GDK_KP_Left:
+ case GDK_KP_4:
+ if (MOD__CTRL_ONLY) {
+ int i = (int) floor(key_scroll * accelerate_scroll(event,
+ acceleration, sp_desktop_canvas(desktop)));
+ gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
+ event_context->desktop->scroll_world(i, 0);
+ ret = TRUE;
+ }
+ break;
+ case GDK_Up: // Ctrl Up
+ case GDK_KP_Up:
+ case GDK_KP_8:
+ if (MOD__CTRL_ONLY) {
+ int i = (int) floor(key_scroll * accelerate_scroll(event,
+ acceleration, sp_desktop_canvas(desktop)));
+ gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
+ event_context->desktop->scroll_world(0, i);
+ ret = TRUE;
+ }
+ break;
+ case GDK_Right: // Ctrl Right
+ case GDK_KP_Right:
+ case GDK_KP_6:
+ if (MOD__CTRL_ONLY) {
+ int i = (int) floor(key_scroll * accelerate_scroll(event,
+ acceleration, sp_desktop_canvas(desktop)));
+ gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
+ event_context->desktop->scroll_world(-i, 0);
+ ret = TRUE;
+ }
+ break;
+ case GDK_Down: // Ctrl Down
+ case GDK_KP_Down:
+ case GDK_KP_2:
+ if (MOD__CTRL_ONLY) {
+ int i = (int) floor(key_scroll * accelerate_scroll(event,
+ acceleration, sp_desktop_canvas(desktop)));
+ gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
+ event_context->desktop->scroll_world(0, -i);
+ ret = TRUE;
+ }
+ break;
+ case GDK_F10:
+ if (MOD__SHIFT_ONLY) {
+ sp_event_root_menu_popup(desktop, NULL, event);
+ ret = TRUE;
+ }
+ break;
+ case GDK_space:
+ if (prefs->getBool("/options/spacepans/value")) {
+ event_context->space_panning = true;
+ event_context->_message_context->set(Inkscape::INFORMATION_MESSAGE,
+ _("<b>Space+mouse drag</b> to pan canvas"));
+ ret = TRUE;
+ } else {
+ sp_toggle_selector(desktop);
+ ret = TRUE;
+ }
+ break;
+ case GDK_z:
+ case GDK_Z:
+ if (MOD__ALT_ONLY) {
+ desktop->zoom_grab_focus();
+ ret = TRUE;
+ }
+ break;
+ default:
+ break;
+ }
}
- }
break;
case GDK_KEY_RELEASE:
switch (get_group0_keyval(&event->key)) {
diff --git a/src/select-context.cpp b/src/select-context.cpp
index 99ad35124..3803b7d07 100644
--- a/src/select-context.cpp
+++ b/src/select-context.cpp
@@ -398,6 +398,16 @@ sp_select_context_item_handler(SPEventContext *event_context, SPItem *item, GdkE
seltrans->stamp();
ret = TRUE;
}
+ } else if (get_group0_keyval (&event->key) == GDK_Tab) {
+ if (sc->dragging && sc->grabbed) {
+ seltrans->getNextClosestPoint(false);
+ ret = TRUE;
+ }
+ } else if (get_group0_keyval (&event->key) == GDK_ISO_Left_Tab) {
+ if (sc->dragging && sc->grabbed) {
+ seltrans->getNextClosestPoint(true);
+ ret = TRUE;
+ }
}
break;
@@ -531,8 +541,8 @@ sp_select_context_root_handler(SPEventContext *event_context, GdkEvent *event)
case GDK_MOTION_NOTIFY:
{
- tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
- if (event->motion.state & GDK_BUTTON1_MASK && !event_context->space_panning) {
+ tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
+ if ((event->motion.state & GDK_BUTTON1_MASK) && !event_context->space_panning) {
Geom::Point const motion_pt(event->motion.x, event->motion.y);
Geom::Point const p(desktop->w2d(motion_pt));
@@ -549,7 +559,7 @@ sp_select_context_root_handler(SPEventContext *event_context, GdkEvent *event)
if (sc->button_press_ctrl || (sc->button_press_alt && !sc->button_press_shift && !selection->isEmpty())) {
// if it's not click and ctrl or alt was pressed (the latter with some selection
// but not with shift) we want to drag rather than rubberband
- sc->dragging = TRUE;
+ sc->dragging = TRUE;
gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(desktop))->window, CursorSelectDragging);
sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 5);
diff --git a/src/seltrans.cpp b/src/seltrans.cpp
index 2d09f393e..4889f7dc1 100644
--- a/src/seltrans.cpp
+++ b/src/seltrans.cpp
@@ -163,6 +163,8 @@ Inkscape::SelTrans::SelTrans(SPDesktop *desktop) :
_sel_modified_connection = _selection->connectModified(
sigc::mem_fun(*this, &Inkscape::SelTrans::_selModified)
);
+
+ _all_snap_sources_iter = _all_snap_sources_sorted.end();
}
Inkscape::SelTrans::~SelTrans()
@@ -296,13 +298,10 @@ void Inkscape::SelTrans::grab(Geom::Point const &p, gdouble x, gdouble y, bool s
std::vector<Inkscape::SnapCandidatePoint> snap_points_hull = selection->getSnapPointsConvexHull(&m.snapprefs);
if (_snap_points.size() > 200) {
/* Snapping a huge number of nodes will take way too long, so limit the number of snappable nodes
- An average user would rarely ever try to snap such a large number of nodes anyway, because
- (s)he could hardly discern which node would be snapping */
- if (prefs->getBool("/options/snapclosestonly/value", false)) {
- m.keepClosestPointOnly(_snap_points, p);
- } else {
- _snap_points = snap_points_hull;
- }
+ A typical user would rarely ever try to snap such a large number of nodes anyway, because
+ (s)he would hardly be able to discern which node would be snapping */
+ _snap_points = snap_points_hull;
+ //}
// Unfortunately, by now we will have lost the font-baseline snappoints :-(
}
@@ -321,26 +320,27 @@ void Inkscape::SelTrans::grab(Geom::Point const &p, gdouble x, gdouble y, bool s
}
_bbox_points.clear();
- _bbox_points_for_translating.clear();
// Collect the bounding box's corners and midpoints for each selected item
if (m.snapprefs.getSnapModeBBox()) {
bool c = m.snapprefs.isTargetSnappable(SNAPTARGET_BBOX_CORNER);
bool mp = m.snapprefs.isTargetSnappable(SNAPTARGET_BBOX_MIDPOINT);
bool emp = m.snapprefs.isTargetSnappable(SNAPTARGET_BBOX_EDGE_MIDPOINT);
- // Preferably we'd use the bbox of each selected item, instead of the bbox of the selection as a whole; for translations
+ // 1) Preferably we'd use the bbox of each selected item, instead of the bbox of the selection as a whole; for translations
// this is easy to do, but when snapping the visual bbox while scaling we will have to compensate for the scaling of the
// stroke width. (see get_scale_transform_for_stroke()). This however is currently only implemented for a single bbox.
- // That's why we have both _bbox_points_for_translating and _bbox_points.
- getBBoxPoints(selection->bounds(_snap_bbox_type), &_bbox_points, false, c, emp, mp);
- if (((_items.size() > 0) && (_items.size() < 50)) || prefs->getBool("/options/snapclosestonly/value", false)) {
- // More than 50 items will produce at least 200 bbox points, which might make Inkscape crawl
- // (see the comment a few lines above). In that case we will use the bbox of the selection as a whole
+ // 2) More than 50 items will produce at least 200 bbox points, which might make Inkscape crawl
+ // (see the comment a few lines above). In that case we will use the bbox of the selection as a whole
+ bool c1 = (_items.size() > 0) && (_items.size() < 50);
+ bool c2 = prefs->getBool("/options/snapclosestonly/value", false);
+ if (translating && (c1 || c2)) {
+ // Get the bounding box points for each item in the selection
for (unsigned i = 0; i < _items.size(); i++) {
Geom::OptRect b = _items[i]->desktopBounds(_snap_bbox_type);
- getBBoxPoints(b, &_bbox_points_for_translating, false, c, emp, mp);
+ getBBoxPoints(b, &_bbox_points, false, c, emp, mp);
}
} else {
- _bbox_points_for_translating = _bbox_points; // use the bbox points of the selection as a whole
+ // Only get the bounding box points of the selection as a whole
+ getBBoxPoints(selection->bounds(_snap_bbox_type), &_bbox_points, false, c, emp, mp);
}
}
@@ -357,54 +357,10 @@ void Inkscape::SelTrans::grab(Geom::Point const &p, gdouble x, gdouble y, bool s
}
// When snapping the node closest to the mouse pointer is absolutely preferred over the closest snap
- // (i.e. when weight == 1), then we will not even try to snap to other points and discard those other
- // points immediately.
+ // (i.e. when weight == 1), then we will not even try to snap to other points and disregard those other points
if (prefs->getBool("/options/snapclosestonly/value", false)) {
- if (m.snapprefs.getSnapModeNode() || m.snapprefs.getSnapModeOthers() || m.snapprefs.getSnapModeDatums()) {
- m.keepClosestPointOnly(_snap_points, p);
- } else {
- _snap_points.clear(); // don't keep any point
- }
-
- if (m.snapprefs.getSnapModeBBox()) {
- m.keepClosestPointOnly(_bbox_points, p);
- m.keepClosestPointOnly(_bbox_points_for_translating, p);
- } else {
- _bbox_points.clear(); // don't keep any point
- _bbox_points_for_translating.clear();
- }
-
- // Each of the three vectors of snappoints now contains either one snappoint or none at all.
- if (_snap_points.size() > 1 || _bbox_points.size() > 1 || _bbox_points_for_translating.size() > 1) {
- g_warning("Incorrect assumption encountered while finding the snap source; nothing serious, but please report to Diederik");
- }
-
- // Now let's reduce this to a single closest snappoint
- Geom::Coord dsp = _snap_points.size() == 1 ? Geom::L2((_snap_points.at(0)).getPoint() - p) : Geom::infinity();
- Geom::Coord dbbp = _bbox_points.size() == 1 ? Geom::L2((_bbox_points.at(0)).getPoint() - p) : Geom::infinity();
- Geom::Coord dbbpft = _bbox_points_for_translating.size() == 1 ? Geom::L2((_bbox_points_for_translating.at(0)).getPoint() - p) : Geom::infinity();
-
- if (translating) {
- _bbox_points.clear();
- if (dsp > dbbpft) {
- _snap_points.clear();
- } else {
- _bbox_points_for_translating.clear();
- }
- } else {
- _bbox_points_for_translating.clear();
- if (dsp > dbbp) {
- _snap_points.clear();
- } else {
- _bbox_points.clear();
- }
- }
-
- if ((_snap_points.size() + _bbox_points.size() + _bbox_points_for_translating.size()) > 1) {
- g_warning("Checking number of snap sources failed; nothing serious, but please report to Diederik");
- }
-
+ _keepClosestPointOnly(p, translating);
}
if ((x != -1) && (y != -1)) {
@@ -1476,7 +1432,7 @@ void Inkscape::SelTrans::moveTo(Geom::Point const &xy, guint state)
// Therefore we will have to set the point through which the constraint-line runs
// individually for each point to be snapped; this will be handled however by _snapTransformed()
Geom::Point cvec; cvec[dim] = 1.;
- s.push_back(m.constrainedSnapTranslate(_bbox_points_for_translating,
+ s.push_back(m.constrainedSnapTranslate(_bbox_points,
_point,
Inkscape::Snapper::SnapConstraint(cvec),
dxy));
@@ -1493,7 +1449,7 @@ void Inkscape::SelTrans::moveTo(Geom::Point const &xy, guint state)
g_get_current_time(&starttime); */
/* Snap to things with no constraint */
- s.push_back(m.freeSnapTranslate(_bbox_points_for_translating, _point, dxy));
+ s.push_back(m.freeSnapTranslate(_bbox_points, _point, dxy));
s.push_back(m.freeSnapTranslate(_snap_points, _point, dxy));
/*g_get_current_time(&endtime);
@@ -1643,6 +1599,72 @@ Geom::Point Inkscape::SelTrans::_calcAbsAffineGeom(Geom::Scale const geom_scale)
return _calcAbsAffineDefault(geom_scale); // this is bogus, but we must return _something_
}
+void Inkscape::SelTrans::_keepClosestPointOnly(Geom::Point const &p, bool const translating)
+{
+ SnapManager const &m = _desktop->namedview->snap_manager;
+
+ if (!(m.snapprefs.getSnapModeNode() || m.snapprefs.getSnapModeOthers() || m.snapprefs.getSnapModeDatums())) {
+ _snap_points.clear();
+ }
+
+ if (!m.snapprefs.getSnapModeBBox()) {
+ _bbox_points.clear();
+ }
+
+ _all_snap_sources_sorted = _snap_points;
+ _all_snap_sources_sorted.insert(_all_snap_sources_sorted.end(), _bbox_points.begin(), _bbox_points.end());
+
+ // Calculate and store the distance to the reference point for each snap candidate point
+ for(std::vector<Inkscape::SnapCandidatePoint>::iterator i = _all_snap_sources_sorted.begin(); i != _all_snap_sources_sorted.end(); ++i) {
+ (*i).setDistance(Geom::L2((*i).getPoint() - p));
+ }
+
+ // Sort them ascending, using the distance calculated above as the single criteria
+ std::sort(_all_snap_sources_sorted.begin(), _all_snap_sources_sorted.end());
+
+ // Now get the closest snap source
+ _snap_points.clear();
+ _bbox_points.clear();
+ if (!_all_snap_sources_sorted.empty()) {
+ _all_snap_sources_iter = _all_snap_sources_sorted.begin();
+ if (_all_snap_sources_sorted.front().getSourceType() & SNAPSOURCE_BBOX_CATEGORY) {
+ _bbox_points.push_back(_all_snap_sources_sorted.front());
+ } else {
+ _snap_points.push_back(_all_snap_sources_sorted.front());
+ }
+ }
+
+}
+
+void Inkscape::SelTrans::getNextClosestPoint(bool reverse)
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ if (prefs->getBool("/options/snapclosestonly/value", false)) {
+ if (!_all_snap_sources_sorted.empty()) {
+ if (reverse) { // Shift-tab will find a closer point
+ if (_all_snap_sources_iter == _all_snap_sources_sorted.begin()) {
+ _all_snap_sources_iter = _all_snap_sources_sorted.end();
+ }
+ --_all_snap_sources_iter;
+ } else { // Tab will find a point further away
+ ++_all_snap_sources_iter;
+ if (_all_snap_sources_iter == _all_snap_sources_sorted.end()) {
+ _all_snap_sources_iter = _all_snap_sources_sorted.begin();
+ }
+ }
+
+ _snap_points.clear();
+ _bbox_points.clear();
+
+ if ((*_all_snap_sources_iter).getSourceType() & SNAPSOURCE_BBOX_CATEGORY) {
+ _bbox_points.push_back(*_all_snap_sources_iter);
+ } else {
+ _snap_points.push_back(*_all_snap_sources_iter);
+ }
+ }
+ }
+}
+
/*
Local Variables:
mode:c++
diff --git a/src/seltrans.h b/src/seltrans.h
index 3a5fa006e..58764c48e 100644
--- a/src/seltrans.h
+++ b/src/seltrans.h
@@ -89,9 +89,11 @@ public:
bool isGrabbed() {
return _grabbed;
}
- bool centerIsVisible() {
- return ( _chandle && SP_KNOT_IS_VISIBLE (_chandle) );
- }
+ bool centerIsVisible() {
+ return ( _chandle && SP_KNOT_IS_VISIBLE (_chandle) );
+ }
+
+ void getNextClosestPoint(bool reverse);
private:
void _updateHandles();
@@ -103,7 +105,7 @@ private:
Geom::Point _getGeomHandlePos(Geom::Point const &visual_handle_pos);
Geom::Point _calcAbsAffineDefault(Geom::Scale const default_scale);
Geom::Point _calcAbsAffineGeom(Geom::Scale const geom_scale);
- void _display_snapsource();
+ void _keepClosestPointOnly(Geom::Point const &p, bool const translating);
enum State {
STATE_SCALE, //scale or stretch
@@ -118,9 +120,9 @@ private:
std::vector<Geom::Point> _items_centers;
std::vector<Inkscape::SnapCandidatePoint> _snap_points;
- std::vector<Inkscape::SnapCandidatePoint> _bbox_points; // the bbox point of the selection as a whole, i.e. max. 4 corners plus optionally some midpoints
- std::vector<Inkscape::SnapCandidatePoint> _bbox_points_for_translating; // the bbox points of each selected item, only to be used for translating
-
+ std::vector<Inkscape::SnapCandidatePoint> _bbox_points;
+ std::vector<Inkscape::SnapCandidatePoint> _all_snap_sources_sorted;
+ std::vector<Inkscape::SnapCandidatePoint>::iterator _all_snap_sources_iter;
Inkscape::SelCue _selcue;
Inkscape::Selection *_selection;
diff --git a/src/snap-candidate.h b/src/snap-candidate.h
index 5302b49c9..857a2c2a4 100644
--- a/src/snap-candidate.h
+++ b/src/snap-candidate.h
@@ -71,6 +71,9 @@ public:
inline Inkscape::SnapTargetType getTargetType() const {return _target_type;}
inline long getSourceNum() const {return _source_num;}
void setSourceNum(long num) {_source_num = num;}
+ void setDistance(Geom::Coord dist) {_dist = dist;}
+ Geom::Coord getDistance() { return _dist;}
+ bool operator <(const SnapCandidatePoint other) const { return _dist < other._dist; } // Needed for sorting the SnapCandidatePoints
inline Geom::OptRect const getTargetBBox() const {return _target_bbox;}
boost::optional<Geom::Point> const & getStartingPoint() const {return _line_starting_point;}
@@ -93,6 +96,9 @@ private:
// If this is a target and it belongs to a bounding box, e.g. when the target type is
// SNAPTARGET_BBOX_EDGE_MIDPOINT, then _target_bbox stores the relevant bounding box
Geom::OptRect _target_bbox;
+
+ // For finding the snap candidate closest to the mouse pointer
+ Geom::Coord _dist;
};
class SnapCandidateItem
@@ -127,7 +133,5 @@ public:
bool currently_being_edited; // true for the path that's currently being edited in the node tool (if any)
};
-
} // end of namespace Inkscape
-
#endif /* !SEEN_SNAP_CANDIDATE_H */
diff --git a/src/snap.cpp b/src/snap.cpp
index 5f4872bba..853268b4b 100644
--- a/src/snap.cpp
+++ b/src/snap.cpp
@@ -736,7 +736,7 @@ Inkscape::SnappedPoint SnapManager::freeSnapTranslate(std::vector<Inkscape::Snap
Inkscape::SnappedPoint result = _snapTransformed(p, pointer, false, Geom::Point(0,0), TRANSLATE, tr, Geom::Point(0,0), Geom::X, false);
if (p.size() == 1) {
- _displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
+ displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
}
return result;
@@ -750,7 +750,7 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapTranslate(std::vector<Inkscap
Inkscape::SnappedPoint result = _snapTransformed(p, pointer, true, constraint, TRANSLATE, tr, Geom::Point(0,0), Geom::X, false);
if (p.size() == 1) {
- _displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
+ displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
}
return result;
@@ -765,7 +765,7 @@ Inkscape::SnappedPoint SnapManager::freeSnapScale(std::vector<Inkscape::SnapCand
Inkscape::SnappedPoint result = _snapTransformed(p, pointer, false, Geom::Point(0,0), SCALE, Geom::Point(s[Geom::X], s[Geom::Y]), o, Geom::X, false);
if (p.size() == 1) {
- _displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
+ displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
}
return result;
@@ -781,7 +781,7 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapScale(std::vector<Inkscape::S
Inkscape::SnappedPoint result = _snapTransformed(p, pointer, true, Geom::Point(0,0), SCALE, Geom::Point(s[Geom::X], s[Geom::Y]), o, Geom::X, true);
if (p.size() == 1) {
- _displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
+ displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
}
return result;
@@ -797,7 +797,7 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapStretch(std::vector<Inkscape:
Inkscape::SnappedPoint result = _snapTransformed(p, pointer, true, Geom::Point(0,0), STRETCH, Geom::Point(s, s), o, d, u);
if (p.size() == 1) {
- _displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
+ displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
}
return result;
@@ -824,7 +824,7 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapSkew(std::vector<Inkscape::Sn
Inkscape::SnappedPoint result = _snapTransformed(p, pointer, true, constraint, SKEW, s, o, d, false);
if (p.size() == 1) {
- _displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
+ displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
}
return result;
@@ -844,7 +844,7 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapRotate(std::vector<Inkscape::
Inkscape::SnappedPoint result = _snapTransformed(p, pointer, true, Geom::Point(0,0), ROTATE, Geom::Point(angle, angle), o, Geom::X, false);
if (p.size() == 1) {
- _displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
+ displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
}
return result;
@@ -1109,14 +1109,19 @@ Geom::Point SnapManager::_transformPoint(Inkscape::SnapCandidatePoint const &p,
return transformed;
}
-void SnapManager::_displaySnapsource(Inkscape::SnapCandidatePoint const &p) const {
-
+/**
+ * Mark the location of the snap source (not the snap target!) on the canvas by drawing a symbol.
+ *
+ * @param point_type Category of points to which the source point belongs: node, guide or bounding box
+ * @param p The transformed position of the source point, paired with an identifier of the type of the snap source.
+ */
+void SnapManager::displaySnapsource(Inkscape::SnapCandidatePoint const &p) const {
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
if (prefs->getBool("/options/snapclosestonly/value")) {
Inkscape::SnapSourceType t = p.getSourceType();
bool p_is_a_node = t & Inkscape::SNAPSOURCE_NODE_CATEGORY;
bool p_is_a_bbox = t & Inkscape::SNAPSOURCE_BBOX_CATEGORY;
- bool p_is_other = t & Inkscape::SNAPSOURCE_OTHERS_CATEGORY || t & Inkscape::SNAPSOURCE_DATUMS_CATEGORY;
+ bool p_is_other = (t & Inkscape::SNAPSOURCE_OTHERS_CATEGORY) || (t & Inkscape::SNAPSOURCE_DATUMS_CATEGORY);
g_assert(_desktop != NULL);
if (snapprefs.getSnapEnabledGlobally() && (p_is_other || (p_is_a_node && snapprefs.getSnapModeNode()) || (p_is_a_bbox && snapprefs.getSnapModeBBox()))) {
@@ -1126,34 +1131,6 @@ void SnapManager::_displaySnapsource(Inkscape::SnapCandidatePoint const &p) cons
}
}
}
-
-void SnapManager::keepClosestPointOnly(std::vector<Inkscape::SnapCandidatePoint> &points, const Geom::Point &reference) const
-{
- if (points.size() == 0) {
- return;
- }
-
- if (points.size() == 1) {
- points.front().setSourceNum(-1); // Just in case
- return;
- }
-
- Inkscape::SnapCandidatePoint closest_point = Inkscape::SnapCandidatePoint(Geom::Point(Geom::infinity(), Geom::infinity()), Inkscape::SNAPSOURCE_UNDEFINED, Inkscape::SNAPTARGET_UNDEFINED);
- Geom::Coord closest_dist = Geom::infinity();
-
- for(std::vector<Inkscape::SnapCandidatePoint>::const_iterator i = points.begin(); i != points.end(); i++) {
- Geom::Coord dist = Geom::L2((*i).getPoint() - reference);
- if (i == points.begin() || dist < closest_dist) {
- closest_point = *i;
- closest_dist = dist;
- }
- }
-
- closest_point.setSourceNum(-1);
- points.clear();
- points.push_back(closest_point);
-}
-
/*
Local Variables:
mode:c++
diff --git a/src/snap.h b/src/snap.h
index fffbbdf6a..3eb4c2b7a 100644
--- a/src/snap.h
+++ b/src/snap.h
@@ -476,7 +476,13 @@ public:
*/
Inkscape::SnappedPoint findBestSnap(Inkscape::SnapCandidatePoint const &p, IntermSnapResults const &isr, bool constrained, bool allowOffScreen = false) const;
- void keepClosestPointOnly(std::vector<Inkscape::SnapCandidatePoint> &points, const Geom::Point &reference) const;
+ /**
+ * Mark the location of the snap source (not the snap target!) on the canvas by drawing a symbol.
+ *
+ * @param point_type Category of points to which the source point belongs: node, guide or bounding box.
+ * @param p The transformed position of the source point, paired with an identifier of the type of the snap source.
+ */
+ void displaySnapsource(Inkscape::SnapCandidatePoint const &p) const;
protected:
SPNamedView const *_named_view;
@@ -543,13 +549,6 @@ private:
Geom::Dim2 const dim,
bool const uniform) const;
- /**
- * Mark the location of the snap source (not the snap target!) on the canvas by drawing a symbol.
- *
- * @param point_type Category of points to which the source point belongs: node, guide or bounding box.
- * @param p The transformed position of the source point, paired with an identifier of the type of the snap source.
- */
- void _displaySnapsource(Inkscape::SnapCandidatePoint const &p) const;
};
#endif // !SEEN_SNAP_H
diff --git a/src/ui/tool/control-point-selection.cpp b/src/ui/tool/control-point-selection.cpp
index 1a1aee47c..308359c33 100644
--- a/src/ui/tool/control-point-selection.cpp
+++ b/src/ui/tool/control-point-selection.cpp
@@ -675,7 +675,6 @@ void ControlPointSelection::setOriginalPoints()
}
}
-
} // namespace UI
} // namespace Inkscape
diff --git a/src/ui/tool/control-point.cpp b/src/ui/tool/control-point.cpp
index 81cb53f6f..79d70d453 100644
--- a/src/ui/tool/control-point.cpp
+++ b/src/ui/tool/control-point.cpp
@@ -22,6 +22,7 @@
#include "preferences.h"
#include "ui/tool/control-point.h"
#include "ui/tool/event-utils.h"
+#include "ui/tool/transform-handle-set.h"
namespace Inkscape {
namespace UI {
@@ -437,6 +438,32 @@ bool ControlPoint::_eventHandler(GdkEvent *event)
// update tips on modifier state change
// TODO add ESC keybinding as drag cancel
case GDK_KEY_PRESS:
+ switch (get_group0_keyval(&event->key))
+ {
+ case GDK_Tab:
+ {// Downcast from ControlPoint to TransformHandle, if possible
+ // This is an ugly hack; we should have the transform handle intercept the keystrokes itself
+ TransformHandle *th = dynamic_cast<TransformHandle*>(this);
+ if (th) {
+ th->getNextClosestPoint(false);
+ return true;
+ }
+ break;
+ }
+ case GDK_ISO_Left_Tab:
+ {// Downcast from ControlPoint to TransformHandle, if possible
+ // This is an ugly hack; we should have the transform handle intercept the keystrokes itself
+ TransformHandle *th = dynamic_cast<TransformHandle*>(this);
+ if (th) {
+ th->getNextClosestPoint(true);
+ return true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ // Do not break here, to allow for updating tooltips and such
case GDK_KEY_RELEASE:
if (mouseovered_point != this) return false;
if (_drag_initiated) {
diff --git a/src/ui/tool/transform-handle-set.cpp b/src/ui/tool/transform-handle-set.cpp
index 58f064b9a..7a12f4fbd 100644
--- a/src/ui/tool/transform-handle-set.cpp
+++ b/src/ui/tool/transform-handle-set.cpp
@@ -84,72 +84,101 @@ ControlPoint::ColorSet center_cset = {
} // anonymous namespace
/** Base class for node transform handles to simplify implementation */
-class TransformHandle : public ControlPoint {
-public:
- TransformHandle(TransformHandleSet &th, Gtk::AnchorType anchor, Glib::RefPtr<Gdk::Pixbuf> pb)
- : ControlPoint(th._desktop, Geom::Point(), anchor, pb, &thandle_cset,
- th._transform_handle_group)
- , _th(th)
- {
- setVisible(false);
+TransformHandle::TransformHandle(TransformHandleSet &th, Gtk::AnchorType anchor, Glib::RefPtr<Gdk::Pixbuf> pb)
+ : ControlPoint(th._desktop, Geom::Point(), anchor, pb, &thandle_cset,
+ th._transform_handle_group)
+ , _th(th)
+{
+ setVisible(false);
+}
+
+void TransformHandle::getNextClosestPoint(bool reverse)
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ if (prefs->getBool("/options/snapclosestonly/value", false)) {
+ if (!_all_snap_sources_sorted.empty()) {
+ if (reverse) { // Shift-tab will find a closer point
+ if (_all_snap_sources_iter == _all_snap_sources_sorted.begin()) {
+ _all_snap_sources_iter = _all_snap_sources_sorted.end();
+ }
+ --_all_snap_sources_iter;
+ } else { // Tab will find a point further away
+ ++_all_snap_sources_iter;
+ if (_all_snap_sources_iter == _all_snap_sources_sorted.end()) {
+ _all_snap_sources_iter = _all_snap_sources_sorted.begin();
+ }
+ }
+
+ _snap_points.clear();
+ _snap_points.push_back(*_all_snap_sources_iter);
+
+ }
}
-protected:
- virtual void startTransform() {}
- virtual void endTransform() {}
- virtual Geom::Affine computeTransform(Geom::Point const &pos, GdkEventMotion *event) = 0;
- virtual CommitEvent getCommitEvent() = 0;
+}
- Geom::Affine _last_transform;
- Geom::Point _origin;
- TransformHandleSet &_th;
- std::vector<Inkscape::SnapCandidatePoint> _snap_points;
- std::vector<Inkscape::SnapCandidatePoint> _unselected_points;
+bool TransformHandle::grabbed(GdkEventMotion *)
+{
+ _origin = position();
+ _last_transform.setIdentity();
+ startTransform();
-private:
- virtual bool grabbed(GdkEventMotion *) {
- _origin = position();
- _last_transform.setIdentity();
- startTransform();
-
- _th._setActiveHandle(this);
- _cset = &invisible_cset;
- _setState(_state);
-
- // Collect the snap-candidates, one for each selected node. These will be stored in the _snap_points vector.
- SnapManager &m = _th._desktop->namedview->snap_manager;
- InkNodeTool *nt = INK_NODE_TOOL(_th._desktop->event_context);
- ControlPointSelection *selection = nt->_selected_nodes.get();
-
- selection->setOriginalPoints();
- selection->getOriginalPoints(_snap_points);
- selection->getUnselectedPoints(_unselected_points);
-
- Inkscape::Preferences *prefs = Inkscape::Preferences::get();
- if (prefs->getBool("/options/snapclosestonly/value", false)) {
- m.keepClosestPointOnly(_snap_points, _origin);
+ _th._setActiveHandle(this);
+ _cset = &invisible_cset;
+ _setState(_state);
+
+ // Collect the snap-candidates, one for each selected node. These will be stored in the _snap_points vector.
+ InkNodeTool *nt = INK_NODE_TOOL(_th._desktop->event_context);
+ ControlPointSelection *selection = nt->_selected_nodes.get();
+
+ selection->setOriginalPoints();
+ selection->getOriginalPoints(_snap_points);
+ selection->getUnselectedPoints(_unselected_points);
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ if (prefs->getBool("/options/snapclosestonly/value", false)) {
+ // Find the closest snap source candidate
+ _all_snap_sources_sorted = _snap_points;
+
+ // Calculate and store the distance to the reference point for each snap candidate point
+ for(std::vector<Inkscape::SnapCandidatePoint>::iterator i = _all_snap_sources_sorted.begin(); i != _all_snap_sources_sorted.end(); ++i) {
+ (*i).setDistance(Geom::L2((*i).getPoint() - _origin));
}
- return false;
- }
- virtual void dragged(Geom::Point &new_pos, GdkEventMotion *event)
- {
- Geom::Affine t = computeTransform(new_pos, event);
- // protect against degeneracies
- if (t.isSingular()) return;
- Geom::Affine incr = _last_transform.inverse() * t;
- if (incr.isSingular()) return;
- _th.signal_transform.emit(incr);
- _last_transform = t;
- }
- virtual void ungrabbed(GdkEventButton *) {
+ // Sort them ascending, using the distance calculated above as the single criteria
+ std::sort(_all_snap_sources_sorted.begin(), _all_snap_sources_sorted.end());
+
+ // Now get the closest snap source
_snap_points.clear();
- _th._clearActiveHandle();
- _cset = &thandle_cset;
- _setState(_state);
- endTransform();
- _th.signal_commit.emit(getCommitEvent());
+ if (!_all_snap_sources_sorted.empty()) {
+ _all_snap_sources_iter = _all_snap_sources_sorted.begin();
+ _snap_points.push_back(_all_snap_sources_sorted.front());
+ }
}
-};
+
+ return false;
+}
+
+void TransformHandle::dragged(Geom::Point &new_pos, GdkEventMotion *event)
+{
+ Geom::Affine t = computeTransform(new_pos, event);
+ // protect against degeneracies
+ if (t.isSingular()) return;
+ Geom::Affine incr = _last_transform.inverse() * t;
+ if (incr.isSingular()) return;
+ _th.signal_transform.emit(incr);
+ _last_transform = t;
+}
+
+void TransformHandle::ungrabbed(GdkEventButton *)
+{
+ _snap_points.clear();
+ _th._clearActiveHandle();
+ _cset = &thandle_cset;
+ _setState(_state);
+ endTransform();
+ _th.signal_commit.emit(getCommitEvent());
+}
+
class ScaleHandle : public TransformHandle {
public:
diff --git a/src/ui/tool/transform-handle-set.h b/src/ui/tool/transform-handle-set.h
index 0557b1278..8ce7011c5 100644
--- a/src/ui/tool/transform-handle-set.h
+++ b/src/ui/tool/transform-handle-set.h
@@ -78,6 +78,32 @@ private:
friend class RotationCenter;
};
+/** Base class for node transform handles to simplify implementation */
+class TransformHandle : public ControlPoint {
+public:
+ TransformHandle(TransformHandleSet &th, Gtk::AnchorType anchor, Glib::RefPtr<Gdk::Pixbuf> pb);
+ void getNextClosestPoint(bool reverse);
+
+protected:
+ virtual void startTransform() {}
+ virtual void endTransform() {}
+ virtual Geom::Affine computeTransform(Geom::Point const &pos, GdkEventMotion *event) = 0;
+ virtual CommitEvent getCommitEvent() = 0;
+
+ Geom::Affine _last_transform;
+ Geom::Point _origin;
+ TransformHandleSet &_th;
+ std::vector<Inkscape::SnapCandidatePoint> _snap_points;
+ std::vector<Inkscape::SnapCandidatePoint> _unselected_points;
+ std::vector<Inkscape::SnapCandidatePoint> _all_snap_sources_sorted;
+ std::vector<Inkscape::SnapCandidatePoint>::iterator _all_snap_sources_iter;
+
+private:
+ virtual bool grabbed(GdkEventMotion *);
+ virtual void dragged(Geom::Point &new_pos, GdkEventMotion *event);
+ virtual void ungrabbed(GdkEventButton *);
+};
+
} // namespace UI
} // namespace Inkscape