Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
knot.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * SPKnot implementation
4 *
5 * Authors:
6 * Lauris Kaplinski <lauris@kaplinski.com>
7 * bulia byak <buliabyak@users.sf.net>
8 * Abhishek Sharma
9 *
10 * Copyright (C) 1999-2005 authors
11 * Copyright (C) 2001-2002 Ximian, Inc.
12 *
13 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
14 */
15
16#include "knot.h"
17
18#include <utility>
19#include <gdk/gdkkeysyms.h>
20#include <glibmm/i18n.h>
21
22#include "desktop.h"
23#include "document-undo.h"
24#include "knot-ptr.h"
25#include "message-context.h"
26#include "message-stack.h"
27
29#include "ui/tools/tool-base.h"
30#include "ui/tools/node-tool.h"
31#include "ui/widget/canvas.h" // autoscroll
33
36
37static constexpr auto KNOT_EVENT_MASK =
38 EventType::BUTTON_PRESS |
39 EventType::BUTTON_RELEASE |
40 EventType::MOTION |
41 EventType::KEY_PRESS |
42 EventType::KEY_RELEASE;
43
44static auto const nograbenv = getenv("INKSCAPE_NO_GRAB");
45static auto const nograb = nograbenv && *nograbenv && *nograbenv != '0';
46
48{
49 if (--knot->ref_count < 1) {
50 delete knot;
51 }
52}
53
54SPKnot::SPKnot(SPDesktop *desktop, char const *tip, Inkscape::CanvasItemCtrlType type, Glib::ustring const &name)
56{
57 if (tip) {
58 _tip = tip;
59 }
60
61 image[SP_KNOT_STATE_NORMAL] = nullptr;
65
66 ctrl = make_canvasitem<Inkscape::CanvasItemCtrl>(desktop->getCanvasControls(), type); // Shape, mode set
67 ctrl->set_name("CanvasItemCtrl:Knot:" + name);
68
69 _event_connection = ctrl->connect_event(sigc::mem_fun(*this, &SPKnot::eventHandler));
70
72}
73
75{
76 // Make sure the knot is not grabbed, as it's destructing can be deferred causing
77 // issues like https://gitlab.com/inkscape/inkscape/-/issues/4239
78 ctrl->ungrab();
79 ctrl.reset();
80
81 // FIXME: cannot snap to destroyed knot (lp:1309050)
82 // this->desktop->getTool()->discard_delayed_snap_event();
84}
85
86void SPKnot::startDragging(Geom::Point const &p, Geom::IntPoint const &xy, uint32_t etime)
87{
88 // save drag origin
89 xyp = xy;
90 within_tolerance = true;
91
92 this->grabbed_rel_pos = p - this->pos;
93 this->drag_origin = this->pos;
94
95 if (!nograb && ctrl) {
97 }
98 this->setFlag(SP_KNOT_GRABBED, true);
99
100 grabbed = true;
101}
102
103void SPKnot::selectKnot(bool select)
104{
105 setFlag(SP_KNOT_SELECTED, select);
106}
107
109{
110 // Run client universal event handler, if present.
111 if (event_signal.emit(this, event)) {
112 return true;
113 }
114
115 bool key_press_event_unconsumed = false;
116
117 SPKnot::ref(this);
118
119 auto prefs = Inkscape::Preferences::get();
120 tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
121
122 bool consumed = false;
123
124 inspect_event(event,
125 [&] (Inkscape::ButtonPressEvent const &event) {
126 if (event.button != 1) {
127 return;
128 }
129 if (event.num_press == 2) {
130 doubleclicked_signal.emit(this, event.modifiers);
131 grabbed = false;
132 moved = false;
133 consumed = true;
134 } else if (event.num_press == 1 && desktop && desktop->getTool() && !desktop->getTool()->is_space_panning()) {
135 auto const p = desktop->w2d(event.pos);
136 startDragging(p, event.pos.floor(), event.time);
137 mousedown_signal.emit(this, event.modifiers);
138 consumed = true;
139 }
140 },
141
142 [&] (Inkscape::ButtonReleaseEvent const &event) {
143 if (event.button == 1 &&
144 desktop &&
145 desktop->getTool() &&
147 {
148 // If we have any pending snap event, then invoke it now
150 pressure = 0;
151
152 if (transform_escaped) {
153 transform_escaped = false;
154 consumed = true;
155 } else {
156 setFlag(SP_KNOT_GRABBED, false);
157
158 if (!nograb && ctrl) {
159 ctrl->ungrab();
160 }
161
162 if (moved) {
164 ungrabbed_signal.emit(this, event.modifiers);
165 } else {
166 click_signal.emit(this, event.modifiers);
167 }
168
169 grabbed = false;
170 moved = false;
171 consumed = true;
172 }
173 }
175 },
176
177 [&] (Inkscape::MotionEvent const &event) {
178 if (!(event.modifiers & GDK_BUTTON1_MASK) && flags & SP_KNOT_DRAGGING) {
179 pressure = 0;
180
181 if (transform_escaped) {
182 transform_escaped = false;
183 consumed = true;
184 } else {
185 setFlag(SP_KNOT_GRABBED, false);
186
187 if (!nograb && ctrl) {
188 ctrl->ungrab();
189 }
190
191 if (moved) {
193 ungrabbed_signal.emit(this, event.modifiers);
194 } else {
195 click_signal.emit(this, event.modifiers);
196 }
197
198 grabbed = false;
199 moved = false;
200 consumed = true;
202 }
203 } else if (grabbed && desktop && desktop->getTool() &&
205 {
206 consumed = true;
207
208 if (within_tolerance && Geom::LInfty(event.pos.floor() - xyp) < tolerance) {
209 return; // do not drag if we're within tolerance from origin
210 }
211
212 // Once the user has moved farther than tolerance from the original location
213 // (indicating they intend to move the object, not click), then always process the
214 // motion notify coordinates as given (no snapping back to origin)
215 within_tolerance = false;
216
217 if (event.extinput.pressure) {
218 pressure = std::clamp(*event.extinput.pressure, 0.0, 1.0);
219 } else {
220 pressure = 0.5;
221 }
222
223 if (!moved) {
225 grabbed_signal.emit(this, event.modifiers);
226 }
227
230 moved = true;
231 }
232 },
233
234 [&] (Inkscape::EnterEvent const &event) {
236 setFlag(SP_KNOT_GRABBED, false);
237
238 if (!_tip.empty() && desktop && desktop->getTool()) {
240 }
242
243 grabbed = false;
244 moved = false;
245 consumed = true;
246 },
247
248 [&] (Inkscape::LeaveEvent const &event) {
250 setFlag(SP_KNOT_GRABBED, false);
251
252 if (!_tip.empty() && desktop && desktop->getTool()) {
254 }
256
257 grabbed = false;
258 moved = false;
259 consumed = true;
260 },
261
262 [&] (Inkscape::KeyPressEvent const &event) {
263 // keybindings for knot
265 case GDK_KEY_Escape:
266 setFlag(SP_KNOT_GRABBED, false);
267
268 if (!nograb && ctrl) {
269 ctrl->ungrab();
270 }
271
272 if (moved) {
274
275 ungrabbed_signal.emit(this, event.modifiers);
276
277 DocumentUndo::undo(desktop->getDocument());
278 desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Node or handle drag canceled."));
279 transform_escaped = true;
280 consumed = true;
281 }
282
283 grabbed = false;
284 moved = false;
285
287 break;
288 default:
289 consumed = false;
290 key_press_event_unconsumed = true;
291 break;
292 }
293 },
294
295 [&] (Inkscape::CanvasEvent const &event) {}
296 );
297
298 SPKnot::unref(this);
299
300 if (key_press_event_unconsumed) {
301 return false; // e.g. in case "%" was pressed to toggle snapping, or Q for quick zoom (while dragging a handle)
302 } else {
303 return consumed || grabbed;
304 }
305}
306
308{
309 auto const motion_w = event.pos;
310 auto const motion_dt = desktop->w2d(motion_w);
311 auto p = motion_dt - grabbed_rel_pos;
312
313 requestPosition(p, event.modifiers);
315 desktop->set_coordinate_status(pos); // display the coordinate of knot, not cursor - they may be different!
316
317 if (event.modifiers & GDK_BUTTON1_MASK) {
319 }
320}
321
323{
325}
326
328{
329 setFlag(SP_KNOT_VISIBLE, false);
330}
331
332void SPKnot::requestPosition(Geom::Point const &p, unsigned state)
333{
334 bool done = request_signal.emit(this, &const_cast<Geom::Point&>(p), state);
335
336 // If user did not complete, we simply move knot to new position.
337 if (!done) {
338 setPosition(p, state);
339 }
340}
341
342void SPKnot::setPosition(Geom::Point const &p, unsigned state)
343{
344 pos = p;
345
346 if (ctrl) {
347 ctrl->set_position(p);
348 }
349
350 moved_signal.emit(this, p, state);
351}
352
354{
355 pos = p;
356
357 if (ctrl) {
358 ctrl->set_position(p);
359 }
360}
361
362void SPKnot::setFlag(guint flag, bool set) {
363 if (set) {
364 this->flags |= flag;
365 } else {
366 this->flags &= ~flag;
367 }
368
369 switch (flag) {
370 case SP_KNOT_VISIBLE:
371 if (ctrl) {
372 ctrl->set_visible(set);
373 }
374 break;
376 case SP_KNOT_DRAGGING:
377 case SP_KNOT_SELECTED:
378 this->_setCtrlState();
379 break;
380 case SP_KNOT_GRABBED:
381 break;
382 default:
383 g_assert_not_reached();
384 break;
385 }
386}
387
388// TODO: Look at removing this and setting ctrl parameters directly.
390
391 if (ctrl) {
392 if (size_set) {
393 ctrl->set_size(_size);
394 }
395 ctrl->set_angle(angle);
396 ctrl->set_anchor(anchor);
397 }
398
400}
401
403 if (ctrl) {
404 ctrl->set_normal(this->flags & SP_KNOT_SELECTED);
405 if (this->flags & SP_KNOT_DRAGGING) {
406 ctrl->set_click();
407 } else if (this->flags & SP_KNOT_MOUSEOVER) {
408 ctrl->set_hover();
409 }
410 }
411}
412
417
418void SPKnot::setAnchor(guint i) {
419 anchor = (SPAnchorType) i;
420}
421
422void SPKnot::setAngle(double i) {
423 angle = i;
424}
425
426void SPKnot::setImage(guchar* normal, guchar* mouseover, guchar* dragging, guchar* selected) {
427 image[SP_KNOT_STATE_NORMAL] = normal;
428 image[SP_KNOT_STATE_MOUSEOVER] = mouseover;
429 image[SP_KNOT_STATE_DRAGGING] = dragging;
430 image[SP_KNOT_STATE_SELECTED] = selected;
431}
432
433void SPKnot::setCursor(SPKnotStateType type, Glib::RefPtr<Gdk::Cursor> cursor)
434{
435 _cursors[type] = std::move(cursor);
436}
437
438void SPKnot::setTip(Glib::ustring &&tip)
439{
440 _tip = std::move(tip);
441}
442
443/*
444 Local Variables:
445 mode:c++
446 c-file-style:"stroustrup"
447 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
448 indent-tabs-mode:nil
449 fill-column:99
450 End:
451*/
452// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
Inkscape canvas widget.
Two-dimensional point with integer coordinates.
Definition int-point.h:57
Two-dimensional point that doubles as a vector.
Definition point.h:66
void set(MessageType type, char const *message)
pushes a message on the stack, replacing our old message
void clear()
removes our current message from the stack
MessageId flash(MessageType type, char const *message)
Temporarily pushes a message onto the stack.
static Preferences * get()
Access the singleton Preferences object.
bool is_space_panning() const
True if we're panning with the space bar.
Definition tool-base.h:191
void snap_delay_handler(gpointer item, gpointer item2, MotionEvent const &event, DelayedSnapEvent::Origin origin)
Analyses the current event, calculates the mouse speed, turns snapping off (temporarily) if the mouse...
void use_cursor(Glib::RefPtr< Gdk::Cursor > cursor)
Set the cursor to this specific one, don't remember it.
void process_delayed_snap_event()
When the delayed snap event timer expires, this method will be called and will re-inject the last mot...
void discard_delayed_snap_event()
If a delayed snap event has been scheduled, this function will cancel it.
MessageContext * defaultMessageContext() const
Definition tool-base.h:123
To do: update description of desktop.
Definition desktop.h:149
Inkscape::UI::Widget::Canvas * getCanvas() const
Definition desktop.h:190
Inkscape::CanvasItemGroup * getCanvasControls() const
Definition desktop.h:196
SPDocument * getDocument() const
Definition desktop.h:189
Inkscape::MessageStack * messageStack() const
Definition desktop.h:160
Inkscape::UI::Tools::ToolBase * getTool() const
Definition desktop.h:187
void set_coordinate_status(Geom::Point const &p)
Sets the coordinate status to a given point.
Definition desktop.cpp:331
Geom::Affine const & w2d() const
Transformation from window to desktop coordinates (zoom/rotate).
Definition desktop.h:416
Desktop-bound visual control object.
Definition knot.h:51
bool grabbed
Definition knot.h:74
static void ref(SPKnot *knot)
Definition knot.h:175
Geom::Point drag_origin
Origin of drag.
Definition knot.h:71
sigc::signal< void(SPKnot *, unsigned int)> ungrabbed_signal
Definition knot.h:97
void _setCtrlState()
Set knot control state (dragging/mouseover/normal).
Definition knot.cpp:402
unsigned char * image[SP_KNOT_VISIBLE_STATES]
Definition knot.h:84
int ref_count
Definition knot.h:181
sigc::signal< void(SPKnot *, Geom::Point const &, unsigned int)> moved_signal
Definition knot.h:98
sigc::signal< void(SPKnot *, unsigned int)> click_signal
Definition knot.h:93
void setPosition(Geom::Point const &p, unsigned int state)
Move knot to new position and emits "moved" signal.
Definition knot.cpp:342
unsigned int flags
Definition knot.h:63
void handler_request_position(Inkscape::MotionEvent const &event)
Definition knot.cpp:307
Geom::Point pos
Our desktop coordinates.
Definition knot.h:69
void setImage(unsigned char *normal, unsigned char *mouseover, unsigned char *dragging, unsigned char *selected)
Definition knot.cpp:426
void setSize(Inkscape::HandleSize size)
Definition knot.cpp:413
int tolerance
Definition knot.h:77
sigc::signal< void(SPKnot *, unsigned int)> grabbed_signal
Definition knot.h:96
Geom::IntPoint xyp
Where drag started.
Definition knot.h:76
void setAngle(double i)
Definition knot.cpp:422
sigc::signal< bool(SPKnot *, Geom::Point *, unsigned int)> request_signal
Definition knot.h:101
bool transform_escaped
Definition knot.h:79
SPKnot(SPDesktop *desktop, char const *tip, Inkscape::CanvasItemCtrlType type, Glib::ustring const &name="unknown")
Definition knot.cpp:54
CanvasItemPtr< Inkscape::CanvasItemCtrl > ctrl
Our CanvasItemCtrl.
Definition knot.h:60
sigc::signal< bool(SPKnot *, Inkscape::CanvasEvent const &)> event_signal
Definition knot.h:99
static void unref(SPKnot *knot)
Definition knot.cpp:47
void hide()
Hide knot on its canvas.
Definition knot.cpp:327
bool moved
Definition knot.h:75
virtual ~SPKnot()
Definition knot.cpp:74
Inkscape::HandleSize _size
Always square.
Definition knot.h:65
void setAnchor(unsigned int i)
Definition knot.cpp:418
sigc::scoped_connection _event_connection
Definition knot.h:87
double angle
Angle of mesh handle.
Definition knot.h:67
void show()
Show knot on its canvas.
Definition knot.cpp:322
void setTip(Glib::ustring &&tip)
Definition knot.cpp:438
void requestPosition(Geom::Point const &pos, unsigned int state)
Request or set new position for knot.
Definition knot.cpp:332
bool size_set
Use default size unless explicitly set.
Definition knot.h:66
SPAnchorType anchor
Anchor.
Definition knot.h:72
double pressure
The tablet pen pressure when the knot is being dragged.
Definition knot.h:89
void setFlag(unsigned int flag, bool set)
Set flag in knot, with side effects.
Definition knot.cpp:362
bool within_tolerance
Definition knot.h:78
Glib::RefPtr< Gdk::Cursor > _cursors[SP_KNOT_VISIBLE_STATES]
Definition knot.h:85
Geom::Point grabbed_rel_pos
Grabbed relative position.
Definition knot.h:70
void updateCtrl()
Update knot's control state.
Definition knot.cpp:389
Glib::ustring _tip
Definition knot.h:179
SPDesktop * desktop
Desktop we are on.
Definition knot.h:59
void moveto(Geom::Point const &p)
Move knot to new position, without emitting a MOVED signal.
Definition knot.cpp:353
void selectKnot(bool select)
Select knot.
Definition knot.cpp:103
bool eventHandler(Inkscape::CanvasEvent const &event)
Event handler (from CanvasItems).
Definition knot.cpp:108
void setCursor(SPKnotStateType type, Glib::RefPtr< Gdk::Cursor > cursor)
Definition knot.cpp:433
void startDragging(Geom::Point const &p, Geom::IntPoint const &xy, uint32_t etime)
Update knot for dragging and tell canvas an item was grabbed.
Definition knot.cpp:86
Geom::IntPoint size
Editable view implementation.
TODO: insert short description here.
SPAnchorType
Definition enums.h:18
SPKnotStateType
Definition knot-enums.h:19
@ SP_KNOT_STATE_NORMAL
Definition knot-enums.h:20
@ SP_KNOT_STATE_DRAGGING
Definition knot-enums.h:22
@ SP_KNOT_STATE_SELECTED
Definition knot-enums.h:23
@ SP_KNOT_STATE_MOUSEOVER
Definition knot-enums.h:21
@ SP_KNOT_SELECTED
Definition knot-enums.h:34
@ SP_KNOT_MOUSEOVER
Definition knot-enums.h:31
@ SP_KNOT_VISIBLE
Definition knot-enums.h:30
@ SP_KNOT_GRABBED
Definition knot-enums.h:33
@ SP_KNOT_DRAGGING
Definition knot-enums.h:32
void knot_created_callback(void *knot)
Definition knot-ptr.cpp:23
void knot_deleted_callback(void *knot)
Definition knot-ptr.cpp:17
TODO: insert short description here.
static auto const nograbenv
Definition knot.cpp:44
static constexpr auto KNOT_EVENT_MASK
Definition knot.cpp:37
static auto const nograb
Definition knot.cpp:45
Declarations for SPKnot: Desktop-bound visual control object.
Interface for locally managing a current status message.
Raw stack of active status messages.
Coord LInfty(Point const &p)
void gobble_motion_events(unsigned mask)
Definition tool-base.h:244
unsigned get_latin_keyval(GtkEventControllerKey const *const controller, unsigned const keyval, unsigned const keycode, GdkModifierType const state, unsigned *consumed_modifiers)
Return the keyval corresponding to the event controller key in Latin group.
void sp_update_helperpath(SPDesktop *desktop)
EventType
The type of a CanvasEvent.
Definition enums.h:22
@ NORMAL_MESSAGE
Definition message.h:26
New node tool with support for multiple path editing.
A mouse button (left/right/middle) is pressed.
A mouse button (left/right/middle) is released.
Abstract base class for events.
unsigned modifiers
The modifiers mask immediately before the event.
The pointer has entered a widget or item.
A key has been pressed.
The pointer has exited a widget or item.
Movement of the mouse pointer.
SPDesktop * desktop
Glib::ustring name
Definition toolbars.cpp:55