Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
box3d-tool.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * 3D box drawing context
4 *
5 * Author:
6 * Lauris Kaplinski <lauris@kaplinski.com>
7 * bulia byak <buliabyak@users.sf.net>
8 * Jon A. Cruz <jon@joncruz.org>
9 * Abhishek Sharma
10 *
11 * Copyright (C) 2007 Maximilian Albert <Anhalter42@gmx.de>
12 * Copyright (C) 2006 Johan Engelen <johan@shouraizou.nl>
13 * Copyright (C) 2000-2005 authors
14 * Copyright (C) 2000-2001 Ximian, Inc.
15 *
16 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
17 */
18
19#include <gdk/gdkkeysyms.h>
20#include <glibmm/i18n.h>
21
22#include "context-fns.h"
23#include "desktop.h"
24#include "document-undo.h"
25#include "document.h"
26#include "message-context.h"
27#include "perspective-line.h"
28#include "selection.h"
29
30#include "object/box3d-side.h"
31#include "object/box3d.h"
32#include "object/sp-defs.h"
33#include "object/sp-namedview.h"
34
35#include "ui/icon-names.h"
36#include "ui/shape-editor.h"
37#include "ui/tools/box3d-tool.h"
39
41
42namespace Inkscape::UI::Tools {
43
45 : ToolBase(desktop, "/tools/shapes/3dbox", "box.svg")
46{
48
49 if (auto item = desktop->getSelection()->singleItem()) {
51 }
52
54 sigc::mem_fun(*this, &Box3dTool::selection_changed)
55 );
56
57 _vpdrag = std::make_unique<Box3D::VPDrag>(desktop->getDocument());
58
59 auto prefs = Preferences::get();
60
61 if (prefs->getBool("/tools/shapes/selcue")) {
63 }
64
65 if (prefs->getBool("/tools/shapes/gradientdrag")) {
67 }
68}
69
71{
73 finishItem();
74
75 enableGrDrag(false);
76
77 delete shape_editor;
78 shape_editor = nullptr;
79}
80
86{
88 shape_editor->set_item(selection->singleItem());
89
90 if (selection->perspList().size() == 1) {
91 // selecting a single box changes the current perspective
92 _desktop->doc()->setCurrentPersp3D(selection->perspList().front());
93 }
94}
95
96/* Create a default perspective in document defs if none is present (which can happen, among other
97 * circumstances, after 'vacuum defs' or when a pre-0.46 file is opened).
98 */
99static void ensure_persp_in_defs(SPDocument *document)
100{
101 auto defs = document->getDefs();
102
103 for (auto const &child : defs->children) {
104 if (is<Persp3D>(&child)) {
105 return;
106 }
107 }
108
110}
111
113{
114 if (event.type() == EventType::BUTTON_PRESS) {
115 auto &button_event = static_cast<ButtonPressEvent const &>(event);
116 if (button_event.num_press == 1 && button_event.button == 1) {
117 setup_for_drag_start(button_event);
118 }
119 }
120
121 return ToolBase::item_handler(item, event);
122}
123
125{
126 auto document = _desktop->getDocument();
127 auto const y_dir = _desktop->yaxisdir();
128 auto selection = _desktop->getSelection();
129
130 auto prefs = Preferences::get();
131 int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
132 tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
133
134 auto cur_persp = document->getCurrentPersp3D();
135
136 bool ret = false;
137
138 inspect_event(event,
139 [&] (ButtonPressEvent const &event) {
140 if (event.num_press != 1 || event.button != 1) {
141 return;
142 }
143
144 auto const button_w = event.pos;
145 auto button_dt = _desktop->w2d(button_w);
146
147 saveDragOrigin(button_w);
148
149 // remember clicked box3d, *not* disregarding groups (since a 3D box is a group), honoring Alt
150 item_to_select = sp_event_context_find_item(_desktop, button_w, event.modifiers & GDK_ALT_MASK, event.modifiers & GDK_CONTROL_MASK);
151
152 dragging = true;
153
154 auto &m = _desktop->getNamedView()->snap_manager;
155 m.setup(_desktop, true, box3d.get());
156 m.freeSnapReturnByRef(button_dt, Inkscape::SNAPSOURCE_NODE_HANDLE);
157 m.unSetup();
158 center = button_dt;
159
160 drag_origin = button_dt;
161 drag_ptB = button_dt;
162 drag_ptC = button_dt;
163
164 // This can happen after saving when the last remaining perspective was purged and must be recreated.
165 if (!cur_persp) {
166 ensure_persp_in_defs(document);
167 cur_persp = document->getCurrentPersp3D();
168 }
169
170 // Projective preimages of clicked point under current perspective.
171 drag_origin_proj = cur_persp->perspective_impl->tmat.preimage(button_dt, 0, Proj::Z);
175 drag_ptC_proj[Proj::Z] = 0.25;
176
178 ret = true;
179 },
180
181 [&] (MotionEvent const &event) {
182 if (dragging && event.modifiers & GDK_BUTTON1_MASK) {
183 if (!cur_persp) {
184 // Can happen if perspective is deleted while dragging, e.g. on document closure.
185 ret = true;
186 return;
187 }
188
189 if (!checkDragMoved(event.pos)) {
190 return;
191 }
192
193 auto const motion_w = event.pos;
194 auto motion_dt = _desktop->w2d(motion_w);
195
196 auto &m = _desktop->getNamedView()->snap_manager;
197 m.setup(_desktop, true, box3d.get());
198 m.freeSnapReturnByRef(motion_dt, Inkscape::SNAPSOURCE_NODE_HANDLE);
199 ctrl_dragged = event.modifiers & GDK_CONTROL_MASK;
200
201 if (event.modifiers & GDK_SHIFT_MASK && box3d) {
202 // once shift is pressed, set extruded
203 extruded = true;
204 }
205
206 if (!extruded) {
207 drag_ptB = motion_dt;
208 drag_ptC = motion_dt;
209
210 drag_ptB_proj = cur_persp->perspective_impl->tmat.preimage(motion_dt, 0, Proj::Z);
213 drag_ptC_proj[Proj::Z] = 0.25;
214 } else {
215 // Without Ctrl, motion of the extruded corner is constrained to the
216 // perspective line from drag_ptB to vanishing point Y.
217 if (!ctrl_dragged) {
218 // snapping
219 auto pline = Box3D::PerspectiveLine(drag_ptB, Proj::Z, document->getCurrentPersp3D());
220 drag_ptC = pline.closest_to(motion_dt);
221
223 drag_ptC_proj = cur_persp->perspective_impl->tmat.preimage(drag_ptC, drag_ptB_proj[Proj::X], Proj::X);
224 } else {
225 drag_ptC = motion_dt;
226
228 drag_ptC_proj = cur_persp->perspective_impl->tmat.preimage(motion_dt, drag_ptB_proj[Proj::X], Proj::X);
229 }
230
231 m.freeSnapReturnByRef(drag_ptC, Inkscape::SNAPSOURCE_NODE_HANDLE);
232 }
233
234 m.unSetup();
235
236 drag();
237
238 ret = true;
239 } else if (!sp_event_context_knot_mouseover()) {
240 auto &m = _desktop->getNamedView()->snap_manager;
241 m.setup(_desktop);
242
243 auto const motion_w = event.pos;
244 auto motion_dt = _desktop->w2d(motion_w);
246 m.unSetup();
247 }
248 },
249
250 [&] (ButtonReleaseEvent const &event) {
251 xyp = {};
252
253 if (event.button != 1) {
254 return;
255 }
256
257 dragging = false;
259
260 if (!within_tolerance) {
261 // we've been dragging (or switched tools if !box3d), finish the box
262 if (box3d) {
263 // update while creating inside a LPE group
264 sp_lpe_item_update_patheffect(this->box3d.get(), true, true);
265 _desktop->getSelection()->set(box3d.get()); // Updating the selection will send signals to the box3d-toolbar ...
266 }
267 finishItem(); // .. but finishItem() will be called from the destructor too and shall NOT fire such signals!
268 } else if (item_to_select) {
269 // no dragging, select clicked box3d if any
270 if (event.modifiers & GDK_SHIFT_MASK) {
271 selection->toggle(item_to_select);
272 } else {
273 selection->set(item_to_select);
274 }
275 } else {
276 // click in an empty space
277 selection->clear();
278 }
279
280 item_to_select = nullptr;
281 ret = true;
283 },
284
285 [&] (KeyPressEvent const &event) {
286 switch (get_latin_keyval(event)) {
287 case GDK_KEY_Up:
288 case GDK_KEY_Down:
289 case GDK_KEY_KP_Up:
290 case GDK_KEY_KP_Down:
291 // prevent the zoom field from activation
292 if (!mod_ctrl_only(event)) {
293 ret = true;
294 }
295 break;
296
297 case GDK_KEY_bracketright:
298 document->getCurrentPersp3D()->rotate_VP (Proj::X, 180 / snaps * y_dir, mod_alt(event));
299 DocumentUndo::done(document, _("Change perspective (angle of PLs)"), INKSCAPE_ICON("draw-cuboid"));
300 ret = true;
301 break;
302
303 case GDK_KEY_bracketleft:
304 document->getCurrentPersp3D()->rotate_VP (Proj::X, -180 / snaps * y_dir, mod_alt(event));
305 DocumentUndo::done(document, _("Change perspective (angle of PLs)"), INKSCAPE_ICON("draw-cuboid"));
306 ret = true;
307 break;
308
309 case GDK_KEY_parenright:
310 document->getCurrentPersp3D()->rotate_VP (Proj::Y, 180 / snaps * y_dir, mod_alt(event));
311 DocumentUndo::done(document, _("Change perspective (angle of PLs)"), INKSCAPE_ICON("draw-cuboid"));
312 ret = true;
313 break;
314
315 case GDK_KEY_parenleft:
316 document->getCurrentPersp3D()->rotate_VP (Proj::Y, -180 / snaps * y_dir, mod_alt(event));
317 DocumentUndo::done(document, _("Change perspective (angle of PLs)"), INKSCAPE_ICON("draw-cuboid"));
318 ret = true;
319 break;
320
321 case GDK_KEY_braceright:
322 document->getCurrentPersp3D()->rotate_VP (Proj::Z, 180 / snaps * y_dir, mod_alt(event));
323 DocumentUndo::done(document, _("Change perspective (angle of PLs)"), INKSCAPE_ICON("draw-cuboid"));
324 ret = true;
325 break;
326
327 case GDK_KEY_braceleft:
328 document->getCurrentPersp3D()->rotate_VP (Proj::Z, -180 / snaps * y_dir, mod_alt(event));
329 DocumentUndo::done(document, _("Change perspective (angle of PLs)"), INKSCAPE_ICON("draw-cuboid"));
330 ret = true;
331 break;
332
333 case GDK_KEY_g:
334 case GDK_KEY_G:
335 if (mod_shift_only(event)) {
337 ret = true;
338 }
339 break;
340
341 case GDK_KEY_p:
342 case GDK_KEY_P:
343 if (mod_shift_only(event)) {
344 if (document->getCurrentPersp3D()) {
345 document->getCurrentPersp3D()->print_debugging_info();
346 }
347 ret = true;
348 }
349 break;
350
351 case GDK_KEY_x:
352 case GDK_KEY_X:
353 if (mod_alt_only(event)) {
354 _desktop->setToolboxFocusTo("box3d-angle-x");
355 ret = true;
356 }
357 if (mod_shift_only(event)) {
358 Persp3D::toggle_VPs(selection->perspList(), Proj::X);
359 _vpdrag->updateLines(); // FIXME: Shouldn't this be done automatically?
360 ret = true;
361 }
362 break;
363
364 case GDK_KEY_y:
365 case GDK_KEY_Y:
366 if (mod_shift_only(event)) {
367 Persp3D::toggle_VPs(selection->perspList(), Proj::Y);
368 _vpdrag->updateLines(); // FIXME: Shouldn't this be done automatically?
369 ret = true;
370 }
371 break;
372
373 case GDK_KEY_z:
374 case GDK_KEY_Z:
375 if (mod_shift_only(event)) {
376 Persp3D::toggle_VPs(selection->perspList(), Proj::Z);
377 _vpdrag->updateLines(); // FIXME: Shouldn't this be done automatically?
378 ret = true;
379 }
380 break;
381
382 case GDK_KEY_Escape:
383 if (dragging) {
384 dragging = false;
386 // if drawing, cancel, otherwise pass it up for deselecting
387 cancel();
388 ret = true;
389 }
390 break;
391
392 case GDK_KEY_space:
393 if (dragging) {
395 dragging = false;
397 if (!within_tolerance) {
398 // we've been dragging (or switched tools if !box3d), finish the box
399 if (box3d) {
400 _desktop->getSelection()->set(box3d.get()); // Updating the selection will send signals to the box3d-toolbar ...
401 }
402 finishItem(); // .. but finishItem() will be called from the destructor too and shall NOT fire such signals!
403 }
404 // do not return true, so that space would work switching to selector
405 }
406 break;
407
408 case GDK_KEY_Delete:
409 case GDK_KEY_KP_Delete:
410 case GDK_KEY_BackSpace:
411 ret = deleteSelectedDrag(mod_ctrl_only(event));
412 break;
413
414 default:
415 break;
416 }
417 },
418
419 [&] (CanvasEvent const &event) {}
420 );
421
422 return ret || ToolBase::root_handler(event);
423}
424
426{
427 if (!box3d) {
429 return;
430 }
431
432 // Create object
433 auto newbox3d = SPBox3D::createBox3D(currentLayer());
434
435 // Set style
436 _desktop->applyCurrentOrToolStyle(newbox3d, "/tools/shapes/3dbox", false);
437
438 box3d = newbox3d;
439
440 // TODO: Incorporate this in box3d-side.cpp!
441 for (int i = 0; i < 6; ++i) {
442 auto side = Box3DSide::createBox3DSide(newbox3d);
443
444 auto [plane, front_or_rear] = Box3D::int_to_face(i);
445
446 plane = Box3D::is_plane(plane) ? plane : Box3D::orth_plane_or_axis(plane);
447 side->dir1 = Box3D::extract_first_axis_direction(plane);
448 side->dir2 = Box3D::extract_second_axis_direction(plane);
449 side->front_or_rear = front_or_rear;
450
451 // Set style
452 auto prefs = Preferences::get();
453
454 Glib::ustring descr = "/desktop/";
455 descr += side->axes_string();
456 descr += "/style";
457
458 Glib::ustring cur_style = prefs->getString(descr);
459
460 bool use_current = prefs->getBool("/tools/shapes/3dbox/usecurrent", false);
461
462 if (use_current && !cur_style.empty()) {
463 // use last used style
464 side->setAttribute("style", cur_style);
465 } else {
466 // use default style
467 auto tool_path = Glib::ustring::compose("/tools/shapes/3dbox/%1", side->axes_string());
468 _desktop->applyCurrentOrToolStyle(side, tool_path, false);
469 }
470
471 side->updateRepr(); // calls Box3DSide::write() and updates, e.g., the axes string description
472 }
473
475 box3d->updateRepr();
476
477 // TODO: It would be nice to show the VPs during dragging, but since there is no selection
478 // at this point (only after finishing the box), we must do this "manually"
479 // _vpdrag->updateDraggers();
480 }
481
484
486
487 // we need to call this from here (instead of from SPBox3D::position_set(), for example)
488 // because z-order setting must not interfere with display updates during undo/redo.
490
492
493 // status text
494 message_context->setF(Inkscape::NORMAL_MESSAGE, "%s", _("<b>3D Box</b>; with <b>Shift</b> to extrude along the Z axis"));
495}
496
498{
499 message_context->clear();
500 ctrl_dragged = false;
501 extruded = false;
502
503 if (box3d) {
507 2) {
508 this->cancel(); // Don't allow the creation of zero sized 3d boxes
509 return;
510 }
511 auto doc = _desktop->getDocument();
512
513 if (!doc || !doc->getCurrentPersp3D()) {
514 return;
515 }
516
519
520 box3d->updateRepr();
521
523
524 DocumentUndo::done(_desktop->getDocument(), _("Create 3D box"), INKSCAPE_ICON("draw-cuboid"));
525
526 box3d = nullptr;
527 }
528}
529
531{
534
535 if (box3d) {
537 }
538
539 this->within_tolerance = false;
540 xyp = {};
541 this->item_to_select = nullptr;
542
544}
545
546} // namespace Inkscape::UI::Tools
547
548/*
549 Local Variables:
550 mode:c++
551 c-file-style:"stroustrup"
552 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
553 indent-tabs-mode:nil
554 fill-column:99
555 End:
556*/
557// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
static Box3DSide * createBox3DSide(SPBox3D *box)
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
static void cancel(SPDocument *document)
void clear()
Unselects all selected objects.
SPItem * singleItem()
Returns a single selected item.
std::list< Persp3D * > const perspList()
Returns a list of all perspectives which have a 3D box in the current selection.
static Preferences * get()
Access the singleton Preferences object.
T * get() const
Definition weakptr.h:26
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
void set(XML::Node *repr)
Set the selection to an XML node's SPObject.
Definition selection.h:118
sigc::connection connectChanged(sigc::slot< void(Selection *)> slot)
Connects a slot to be notified of selection changes.
Definition selection.h:179
Class to store data for points which are snap candidates, either as a source or as a target.
void set_item(SPItem *item)
void unset_item(bool keep_knotholder=false)
bool extruded
whether shift-dragging already occurred (i.e. the box is already extruded)
Definition box3d-tool.h:70
bool item_handler(SPItem *item, CanvasEvent const &event) override
Handles item specific events.
sigc::scoped_connection sel_changed_connection
Definition box3d-tool.h:72
bool root_handler(CanvasEvent const &event) override
Geom::Point drag_origin
save three corners while dragging: 1) the starting point (already done by the event_context) 2) drag_...
Definition box3d-tool.h:61
Box3dTool(SPDesktop *desktop)
bool ctrl_dragged
whether we are ctrl-dragging
Definition box3d-tool.h:69
std::unique_ptr< Box3D::VPDrag > _vpdrag
Definition box3d-tool.h:44
SPWeakPtr< SPBox3D > box3d
Definition box3d-tool.h:50
void selection_changed(Selection *selection)
Callback that processes the "changed" signal on the selection; destroys old and creates new knotholde...
Base class for Event processors.
Definition tool-base.h:107
bool sp_event_context_knot_mouseover() const
Returns true if we're hovering above a knot (needed because we don't want to pre-snap in that case).
void ungrabCanvasEvents()
Ungrab events from the Canvas Catchall.
void grabCanvasEvents(EventMask mask=EventType::KEY_PRESS|EventType::BUTTON_RELEASE|EventType::MOTION|EventType::BUTTON_PRESS)
Grab events from the Canvas Catchall.
SPGroup * currentLayer() const
bool within_tolerance
are we still within tolerance of origin
Definition tool-base.h:148
bool dragging
are we dragging?
Definition tool-base.h:146
std::unique_ptr< MessageContext > message_context
Definition tool-base.h:193
void setup_for_drag_start(ButtonPressEvent const &ev)
SPItem * item_to_select
the item where mouse_press occurred, to be selected if this is a click not drag
Definition tool-base.h:152
Geom::IntPoint xyp
where drag started
Definition tool-base.h:145
virtual bool root_handler(CanvasEvent const &event)
void saveDragOrigin(Geom::Point const &pos)
bool checkDragMoved(Geom::Point const &pos)
Analyse the current position and return true once it has moved farther than tolerance from the drag o...
virtual bool item_handler(SPItem *item, CanvasEvent const &event)
Handles item specific events.
void enableGrDrag(bool enable=true)
void enableSelectionCue(bool enable=true)
Enables/disables the ToolBase's SelCue.
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
bool deleteSelectedDrag(bool just_one)
Delete a selected GrDrag point.
static void toggle_VPs(std::list< Persp3D * >, Proj::Axis axis)
Definition persp3d.cpp:358
static Persp3D * create_xml_element(SPDocument *document)
Definition persp3d.cpp:203
void normalize()
Definition proj_pt.cpp:91
static SPBox3D * createBox3D(SPItem *parent)
Create a SPBox3D and append it to the parent.
Definition box3d.cpp:419
Proj::Pt3 orig_corner0
Definition box3d.h:41
void check_for_swapped_coords()
Definition box3d.cpp:1194
void set_z_orders()
Definition box3d.cpp:1077
void position_set()
Definition box3d.cpp:241
Proj::Pt3 orig_corner7
Definition box3d.h:42
void relabel_corners()
Definition box3d.cpp:1161
To do: update description of desktop.
Definition desktop.h:149
SPDocument * getDocument() const
Definition desktop.h:189
void setToolboxFocusTo(char const *label)
Definition desktop.cpp:1128
SPNamedView * getNamedView() const
Definition desktop.h:191
Inkscape::Selection * getSelection() const
Definition desktop.h:188
double yaxisdir() const
Definition desktop.h:426
SPDocument * doc() const
Definition desktop.h:159
void applyCurrentOrToolStyle(SPObject *obj, Glib::ustring const &tool_path, bool with_text)
Apply the desktop's current style or the tool style to the object.
Definition desktop.cpp:1110
Geom::Affine const & w2d() const
Transformation from window to desktop coordinates (zoom/rotate).
Definition desktop.h:416
Typed SVG document implementation.
Definition document.h:101
void setCurrentPersp3D(Persp3D *const persp)
Definition document.cpp:269
SPDefs * getDefs()
Return the main defs object for the document.
Definition document.cpp:246
Base class for visual SVG elements.
Definition sp-item.h:109
SnapManager snap_manager
Inkscape::XML::Node * updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT)
Updates the object's repr based on the object's state.
void deleteObject(bool propagate, bool propagate_descendants)
Deletes an object, unparenting it from its parent.
void setup(SPDesktop const *desktop, bool snapindicator=true, SPObject const *item_to_ignore=nullptr, std::vector< Inkscape::SnapCandidatePoint > *unselected_nodes=nullptr)
Convenience shortcut when there is only one item to ignore.
Definition snap.cpp:663
Editable view implementation.
TODO: insert short description here.
Macro for icon names used in Inkscape.
SPItem * item
Interface for locally managing a current status message.
std::pair< Box3D::Axis, Box3D::FrontOrRear > int_to_face(unsigned id)
Definition axis-manip.h:169
Box3D::Axis orth_plane_or_axis(Box3D::Axis axis)
Definition axis-manip.h:231
Box3D::Axis extract_first_axis_direction(Box3D::Axis dirs)
Definition axis-manip.h:221
Box3D::Axis extract_second_axis_direction(Box3D::Axis dirs)
Definition axis-manip.h:227
bool is_plane(Box3D::Axis plane)
Definition axis-manip.h:200
SPItem * sp_event_context_find_item(SPDesktop *desktop, Geom::Point const &p, bool select_under, bool into_groups)
Returns item at point p in desktop.
static void ensure_persp_in_defs(SPDocument *document)
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.
bool mod_alt(unsigned modifiers)
void inspect_event(E &&event, Fs... funcs)
Perform pattern-matching on a CanvasEvent.
@ SNAPSOURCE_NODE_HANDLE
Definition snap-enums.h:43
bool mod_ctrl_only(unsigned modifiers)
bool mod_shift_only(unsigned modifiers)
bool mod_alt_only(unsigned modifiers)
@ NORMAL_MESSAGE
Definition message.h:26
bool have_viable_layer(SPDesktop *desktop, MessageContext *message)
Check to see if the current layer is both unhidden and unlocked.
Ocnode * child[8]
Definition quantize.cpp:33
Inkscape::ShapeEditor This is a container class which contains a knotholder for shapes.
void sp_lpe_item_update_patheffect(SPLPEItem *lpeitem, bool wholetree, bool write, bool with_satellites)
Calls any registered handlers for the update_patheffect action.
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.
virtual EventType type() const =0
Return the dynamic type of the CanvasEvent.
A key has been pressed.
Movement of the mouse pointer.
SPDesktop * desktop