diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:24:48 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:24:48 +0000 |
commit | cca66b9ec4e494c1d919bff0f71a820d8afab1fa (patch) | |
tree | 146f39ded1c938019e1ed42d30923c2ac9e86789 /src/ui/tools/spiral-tool.cpp | |
parent | Initial commit. (diff) | |
download | inkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.tar.xz inkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.zip |
Adding upstream version 1.2.2.upstream/1.2.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/ui/tools/spiral-tool.cpp')
-rw-r--r-- | src/ui/tools/spiral-tool.cpp | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/src/ui/tools/spiral-tool.cpp b/src/ui/tools/spiral-tool.cpp new file mode 100644 index 0000000..f79eab7 --- /dev/null +++ b/src/ui/tools/spiral-tool.cpp @@ -0,0 +1,411 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Spiral drawing context + * + * Authors: + * Mitsuru Oka + * Lauris Kaplinski <lauris@kaplinski.com> + * bulia byak <buliabyak@users.sf.net> + * Jon A. Cruz <jon@joncruz.org> + * Abhishek Sharma + * + * Copyright (C) 1999-2001 Lauris Kaplinski + * Copyright (C) 2001-2002 Mitsuru Oka + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include <cstring> +#include <string> + +#include <gdk/gdkkeysyms.h> +#include <glibmm/i18n.h> + +#include "spiral-tool.h" + +#include "context-fns.h" +#include "desktop-style.h" +#include "desktop.h" +#include "document-undo.h" +#include "document.h" +#include "message-context.h" +#include "selection.h" + +#include "include/macros.h" + +#include "object/sp-namedview.h" +#include "object/sp-spiral.h" + +#include "ui/icon-names.h" +#include "ui/shape-editor.h" + +#include "xml/node-event-vector.h" + +using Inkscape::DocumentUndo; + +namespace Inkscape { +namespace UI { +namespace Tools { + +SpiralTool::SpiralTool(SPDesktop *desktop) + : ToolBase(desktop, "/tools/shapes/spiral", "spiral.svg") + , spiral(nullptr) + , revo(3) + , exp(1) + , t0(0) +{ + sp_event_context_read(this, "expansion"); + sp_event_context_read(this, "revolution"); + sp_event_context_read(this, "t0"); + + this->shape_editor = new ShapeEditor(desktop); + + SPItem *item = desktop->getSelection()->singleItem(); + if (item) { + this->shape_editor->set_item(item); + } + + Inkscape::Selection *selection = desktop->getSelection(); + this->sel_changed_connection.disconnect(); + + this->sel_changed_connection = selection->connectChanged(sigc::mem_fun(this, &SpiralTool::selection_changed)); + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + if (prefs->getBool("/tools/shapes/selcue")) { + this->enableSelectionCue(); + } + + if (prefs->getBool("/tools/shapes/gradientdrag")) { + this->enableGrDrag(); + } +} + +SpiralTool::~SpiralTool() { + ungrabCanvasEvents(); + + this->finishItem(); + this->sel_changed_connection.disconnect(); + + this->enableGrDrag(false); + + delete this->shape_editor; + this->shape_editor = nullptr; + + /* fixme: This is necessary because we do not grab */ + if (this->spiral) { + this->finishItem(); + } +} + +/** + * Callback that processes the "changed" signal on the selection; + * destroys old and creates new knotholder. + */ +void SpiralTool::selection_changed(Inkscape::Selection *selection) { + this->shape_editor->unset_item(); + this->shape_editor->set_item(selection->singleItem()); +} + + +void SpiralTool::set(const Inkscape::Preferences::Entry& val) { + Glib::ustring name = val.getEntryName(); + + if (name == "expansion") { + this->exp = CLAMP(val.getDouble(), 0.0, 1000.0); + } else if (name == "revolution") { + this->revo = CLAMP(val.getDouble(3.0), 0.05, 40.0); + } else if (name == "t0") { + this->t0 = CLAMP(val.getDouble(), 0.0, 0.999); + } +} + +bool SpiralTool::root_handler(GdkEvent* event) { + static gboolean dragging; + + Inkscape::Selection *selection = _desktop->getSelection(); + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + this->tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); + + gint ret = FALSE; + + switch (event->type) { + case GDK_BUTTON_PRESS: + if (event->button.button == 1) { + dragging = TRUE; + + this->center = this->setup_for_drag_start(event); + + SnapManager &m = _desktop->namedview->snap_manager; + m.setup(_desktop); + m.freeSnapReturnByRef(this->center, Inkscape::SNAPSOURCE_NODE_HANDLE); + m.unSetup(); + + grabCanvasEvents(); + ret = TRUE; + } + break; + + case GDK_MOTION_NOTIFY: + if (dragging && (event->motion.state & GDK_BUTTON1_MASK)) { + if ( this->within_tolerance + && ( abs( (gint) event->motion.x - this->xp ) < this->tolerance ) + && ( abs( (gint) event->motion.y - this->yp ) < this->tolerance ) ) { + break; // do not drag if we're within tolerance from origin + } + // Once the user has moved farther than tolerance from the original location + // (indicating they intend to draw, not click), then always process the + // motion notify coordinates as given (no snapping back to origin) + this->within_tolerance = false; + + Geom::Point const motion_w(event->motion.x, event->motion.y); + Geom::Point motion_dt(_desktop->w2d(motion_w)); + + SnapManager &m = _desktop->namedview->snap_manager; + m.setup(_desktop, true, this->spiral); + m.freeSnapReturnByRef(motion_dt, Inkscape::SNAPSOURCE_NODE_HANDLE); + m.unSetup(); + + this->drag(motion_dt, event->motion.state); + + gobble_motion_events(GDK_BUTTON1_MASK); + + ret = TRUE; + } else if (!this->sp_event_context_knot_mouseover()) { + SnapManager &m = _desktop->namedview->snap_manager; + m.setup(_desktop); + Geom::Point const motion_w(event->motion.x, event->motion.y); + Geom::Point motion_dt(_desktop->w2d(motion_w)); + m.preSnap(Inkscape::SnapCandidatePoint(motion_dt, Inkscape::SNAPSOURCE_NODE_HANDLE)); + m.unSetup(); + } + break; + + case GDK_BUTTON_RELEASE: + this->xp = this->yp = 0; + if (event->button.button == 1) { + dragging = FALSE; + this->discard_delayed_snap_event(); + + if (!this->within_tolerance) { + // we've been dragging, finish the spiral + this->finishItem(); + } else if (this->item_to_select) { + // no dragging, select clicked item if any + if (event->button.state & GDK_SHIFT_MASK) { + selection->toggle(this->item_to_select); + } else { + selection->set(this->item_to_select); + } + } else { + // click in an empty space + selection->clear(); + } + + this->item_to_select = nullptr; + ret = TRUE; + ungrabCanvasEvents(); + } + break; + + case GDK_KEY_PRESS: + switch (get_latin_keyval(&event->key)) { + case GDK_KEY_Alt_R: + case GDK_KEY_Control_L: + case GDK_KEY_Control_R: + case GDK_KEY_Shift_L: + case GDK_KEY_Shift_R: + case GDK_KEY_Meta_L: // Meta is when you press Shift+Alt (at least on my machine) + case GDK_KEY_Meta_R: + sp_event_show_modifier_tip(this->defaultMessageContext(), event, + _("<b>Ctrl</b>: snap angle"), + nullptr, + _("<b>Alt</b>: lock spiral radius")); + break; + + case GDK_KEY_x: + case GDK_KEY_X: + if (MOD__ALT_ONLY(event)) { + _desktop->setToolboxFocusTo("spiral-revolutions"); + ret = TRUE; + } + break; + + case GDK_KEY_Escape: + if (dragging) { + dragging = false; + this->discard_delayed_snap_event(); + // if drawing, cancel, otherwise pass it up for deselecting + this->cancel(); + ret = TRUE; + } + break; + + case GDK_KEY_space: + if (dragging) { + ungrabCanvasEvents(); + dragging = false; + this->discard_delayed_snap_event(); + + if (!this->within_tolerance) { + // we've been dragging, finish the spiral + finishItem(); + } + // do not return true, so that space would work switching to selector + } + break; + + case GDK_KEY_Delete: + case GDK_KEY_KP_Delete: + case GDK_KEY_BackSpace: + ret = this->deleteSelectedDrag(MOD__CTRL_ONLY(event)); + break; + + default: + break; + } + break; + + case GDK_KEY_RELEASE: + switch (get_latin_keyval(&event->key)) { + case GDK_KEY_Alt_L: + case GDK_KEY_Alt_R: + case GDK_KEY_Control_L: + case GDK_KEY_Control_R: + case GDK_KEY_Shift_L: + case GDK_KEY_Shift_R: + case GDK_KEY_Meta_L: // Meta is when you press Shift+Alt + case GDK_KEY_Meta_R: + this->defaultMessageContext()->clear(); + break; + + default: + break; + } + break; + + default: + break; + } + + if (!ret) { + ret = ToolBase::root_handler(event); + } + + return ret; +} + +void SpiralTool::drag(Geom::Point const &p, guint state) { + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); + + if (!this->spiral) { + if (Inkscape::have_viable_layer(_desktop, defaultMessageContext()) == false) { + return; + } + + // Create object + Inkscape::XML::Document *xml_doc = _desktop->doc()->getReprDoc(); + Inkscape::XML::Node *repr = xml_doc->createElement("svg:path"); + repr->setAttribute("sodipodi:type", "spiral"); + + // Set style + sp_desktop_apply_style_tool(_desktop, repr, "/tools/shapes/spiral", false); + + this->spiral = SP_SPIRAL(currentLayer()->appendChildRepr(repr)); + Inkscape::GC::release(repr); + this->spiral->transform = currentLayer()->i2doc_affine().inverse(); + this->spiral->updateRepr(); + } + + SnapManager &m = _desktop->namedview->snap_manager; + m.setup(_desktop, true, this->spiral); + Geom::Point pt2g = p; + m.freeSnapReturnByRef(pt2g, Inkscape::SNAPSOURCE_NODE_HANDLE); + m.unSetup(); + Geom::Point const p0 = _desktop->dt2doc(this->center); + Geom::Point const p1 = _desktop->dt2doc(pt2g); + + Geom::Point const delta = p1 - p0; + gdouble const rad = Geom::L2(delta); + + // Start angle calculated from end angle and number of revolutions. + gdouble arg = Geom::atan2(delta) - 2.0*M_PI * spiral->revo; + + if (state & GDK_CONTROL_MASK) { + /* Snap start angle */ + double snaps_radian = M_PI/snaps; + arg = std::round(arg/snaps_radian) * snaps_radian; + } + + /* Fixme: these parameters should be got from dialog box */ + this->spiral->setPosition(p0[Geom::X], p0[Geom::Y], + /*expansion*/ this->exp, + /*revolution*/ this->revo, + rad, arg, + /*t0*/ this->t0); + + /* status text */ + Inkscape::Util::Quantity q = Inkscape::Util::Quantity(rad, "px"); + Glib::ustring rads = q.string(_desktop->namedview->display_units); + this->message_context->setF(Inkscape::IMMEDIATE_MESSAGE, + _("<b>Spiral</b>: radius %s, angle %.2f°; with <b>Ctrl</b> to snap angle"), + rads.c_str(), arg * 180/M_PI + 360*spiral->revo); +} + +void SpiralTool::finishItem() { + this->message_context->clear(); + + if (this->spiral != nullptr) { + if (this->spiral->rad == 0) { + this->cancel(); // Don't allow the creating of zero sized spiral, for example when the start and and point snap to the snap grid point + return; + } + + spiral->set_shape(); + spiral->updateRepr(SP_OBJECT_WRITE_EXT); + // compensate stroke scaling couldn't be done in doWriteTransform + double const expansion = spiral->transform.descrim(); + spiral->doWriteTransform(spiral->transform, nullptr, true); + spiral->adjust_stroke_width_recursive(expansion); + + _desktop->getSelection()->set(this->spiral); + DocumentUndo::done(_desktop->getDocument(), _("Create spiral"), INKSCAPE_ICON("draw-spiral")); + + this->spiral = nullptr; + } +} + +void SpiralTool::cancel() { + _desktop->getSelection()->clear(); + ungrabCanvasEvents(); + + if (this->spiral != nullptr) { + this->spiral->deleteObject(); + this->spiral = nullptr; + } + + this->within_tolerance = false; + this->xp = 0; + this->yp = 0; + this->item_to_select = nullptr; + + DocumentUndo::cancel(_desktop->getDocument()); +} + +} +} +} + +/* + 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 : |