diff options
Diffstat (limited to 'src/rubberband.cpp')
-rw-r--r-- | src/rubberband.cpp | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/src/rubberband.cpp b/src/rubberband.cpp new file mode 100644 index 0000000..8050dd0 --- /dev/null +++ b/src/rubberband.cpp @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Rubberbanding selector. + * + * Author: + * Lauris Kaplinski <lauris@kaplinski.com> + * Jon A. Cruz <jon@joncruz.org> + * + * Copyright (C) 1999-2002 Lauris Kaplinski + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "desktop.h" + +#include "rubberband.h" + +#include "2geom/path.h" + +#include "display/curve.h" +#include "display/control/canvas-item-bpath.h" +#include "display/control/canvas-item-rect.h" + +#include "ui/widget/canvas.h" // autoscroll + +Inkscape::Rubberband *Inkscape::Rubberband::_instance = nullptr; + +Inkscape::Rubberband::Rubberband(SPDesktop *dt) + : _desktop(dt) +{ + _touchpath_curve = new SPCurve(); +} + +void Inkscape::Rubberband::delete_canvas_items() +{ + _rect.reset(); + _touchpath.reset(); +} + +Geom::Path Inkscape::Rubberband::getPath() const +{ + g_assert(_started); + if (_mode == RUBBERBAND_MODE_TOUCHPATH) { + return _path * _desktop->w2d(); + } + return Geom::Path(*getRectangle()); +} + +std::vector<Geom::Point> Inkscape::Rubberband::getPoints() const +{ + return _path.nodes(); +} + +void Inkscape::Rubberband::start(SPDesktop *d, Geom::Point const &p, bool tolerance) +{ + _desktop = d; + + _start = p; + _started = true; + _moved = false; + + auto prefs = Inkscape::Preferences::get(); + _tolerance = tolerance ? prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100) : 0.0; + + _touchpath_curve->reset(); + _touchpath_curve->moveto(p); + + _path = Geom::Path(_desktop->d2w(p)); + + delete_canvas_items(); +} + +void Inkscape::Rubberband::stop() +{ + _started = false; + _moved = false; + defaultMode(); // restore the default + + _touchpath_curve->reset(); + _path.clear(); + + delete_canvas_items(); + + resetColor(); +} + +void Inkscape::Rubberband::move(Geom::Point const &p) +{ + if (!_started) + return; + + if (!_moved) { + if (Geom::are_near(_start, p, _tolerance/_desktop->current_zoom())) + return; + } + + _end = p; + _moved = true; + _desktop->getCanvas()->enable_autoscroll(); + _touchpath_curve->lineto(p); + + Geom::Point next = _desktop->d2w(p); + // we want the points to be at most 0.5 screen pixels apart, + // so that we don't lose anything small; + // if they are farther apart, we interpolate more points + auto prev = _path.finalPoint(); + if (Geom::L2(next-prev) > 0.5) { + int subdiv = 2 * (int) round(Geom::L2(next-prev) + 0.5); + for (int i = 1; i <= subdiv; i ++) { + _path.appendNew<Geom::LineSegment>(prev + ((double)i/subdiv) * (next - prev)); + } + } else { + _path.appendNew<Geom::LineSegment>(next); + } + + if (_touchpath) _touchpath->hide(); + if (_rect) _rect->hide(); + + switch (_mode) { + case RUBBERBAND_MODE_RECT: + if (!_rect) { + _rect = make_canvasitem<CanvasItemRect>(_desktop->getCanvasControls()); + _rect->set_stroke(_color.value_or(0x808080ff)); + _rect->set_shadow(0xffffffff, 0); // Not a shadow + _rect->set_dashed(false); + _rect->set_inverted(true); + } + _rect->set_rect(Geom::Rect(_start, _end)); + _rect->show(); + break; + case RUBBERBAND_MODE_TOUCHRECT: + if (!_rect) { + _rect = make_canvasitem<CanvasItemRect>(_desktop->getCanvasControls()); + _rect->set_stroke(_color.value_or(0xff0000ff)); + _rect->set_shadow(0xffffffff, 0); // Not a shadow + _rect->set_dashed(false); + _rect->set_inverted(false); + } + _rect->set_rect(Geom::Rect(_start, _end)); + _rect->show(); + break; + case RUBBERBAND_MODE_TOUCHPATH: + if (!_touchpath) { + _touchpath = make_canvasitem<CanvasItemBpath>(_desktop->getCanvasControls()); // Should be sketch? + _touchpath->set_stroke(_color.value_or(0xff0000ff)); + _touchpath->set_fill(0x0, SP_WIND_RULE_NONZERO); + } + _touchpath->set_bpath(_touchpath_curve); + _touchpath->show(); + break; + default: + break; + } +} + +void Inkscape::Rubberband::setColor(uint32_t color) +{ + _color = color; + + if (_mode == RUBBERBAND_MODE_TOUCHPATH) { + if (_touchpath) { + _touchpath->set_stroke(color); + } + } else { + if (_rect) { + _rect->set_stroke(color); + } + } +} + +void Inkscape::Rubberband::setMode(int mode) +{ + _mode = mode; +} + +/** + * Set the default mode (usually rect or touchrect) + */ +void Inkscape::Rubberband::defaultMode() +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + if (prefs->getBool("/tools/select/touch_box", false)) { + _mode = RUBBERBAND_MODE_TOUCHRECT; + } else { + _mode = RUBBERBAND_MODE_RECT; + } +} + +/** + * @return Rectangle in desktop coordinates + */ +Geom::OptRect Inkscape::Rubberband::getRectangle() const +{ + if (!_started) { + return Geom::OptRect(); + } + + return Geom::Rect(_start, _end); +} + +Inkscape::Rubberband *Inkscape::Rubberband::get(SPDesktop *desktop) +{ + if (!_instance) { + _instance = new Inkscape::Rubberband(desktop); + } + + return _instance; +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : |