diff options
| author | Diederik van Lierop <mail@diedenrezi.nl> | 2011-11-04 21:30:19 +0000 |
|---|---|---|
| committer | Diederik van Lierop <mail@diedenrezi.nl> | 2011-11-04 21:30:19 +0000 |
| commit | 224a99dc216119d34eb3ed13d12f123158acfe3c (patch) | |
| tree | cb012b688f276656612f2994e461fda3622bcd20 /src | |
| parent | Powerstroke: add erasing of knots with ctrl+alt (LPE parameter editing on-can... (diff) | |
| download | inkscape-224a99dc216119d34eb3ed13d12f123158acfe3c.tar.gz inkscape-224a99dc216119d34eb3ed13d12f123158acfe3c.zip | |
1) Cycle to the next-closest-snap-source when pressing tab, if the snap-closest-point-only-option has been activated. Works for the selector tool, but also when scaling/stretching/skewing a selection of nodes in the node tool
2) Cleanup and simplification of the code that finds the closest snapsource
(bzr r10720)
Diffstat (limited to 'src')
| -rw-r--r-- | src/display/snap-indicator.cpp | 2 | ||||
| -rw-r--r-- | src/event-context.cpp | 218 | ||||
| -rw-r--r-- | src/select-context.cpp | 16 | ||||
| -rw-r--r-- | src/seltrans.cpp | 150 | ||||
| -rw-r--r-- | src/seltrans.h | 16 | ||||
| -rw-r--r-- | src/snap-candidate.h | 8 | ||||
| -rw-r--r-- | src/snap.cpp | 53 | ||||
| -rw-r--r-- | src/snap.h | 15 | ||||
| -rw-r--r-- | src/ui/tool/control-point-selection.cpp | 1 | ||||
| -rw-r--r-- | src/ui/tool/control-point.cpp | 27 | ||||
| -rw-r--r-- | src/ui/tool/transform-handle-set.cpp | 147 | ||||
| -rw-r--r-- | src/ui/tool/transform-handle-set.h | 26 |
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 |
