Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
node-tool.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
6/* Authors:
7 * Krzysztof KosiƄski <tweenk@gmail.com>
8 * Abhishek Sharma
9 *
10 * Copyright (C) 2009 Authors
11 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
12 */
13
14#include <iomanip>
15#include <glib/gi18n.h>
16
17#include "rubberband.h"
18#include "selection-chemistry.h"
19#include "selection.h"
20#include "display/curve.h"
23#include "live_effects/effect.h"
24#include "object/sp-clippath.h"
25#include "object/sp-mask.h"
26#include "object/sp-shape.h"
27#include "ui/knot/knot-holder.h"
28#include "ui/modifiers.h"
29#include "ui/shape-editor.h" // temporary!
33#include "ui/tools/node-tool.h"
36
38
96namespace Inkscape::UI::Tools {
97
99{
101 group->set_name("CanvasItemGroup:NodeTool");
102 return group;
103}
104
106 : ToolBase(desktop, "/tools/nodes", "node.svg")
107{
109
112
113 // Prepare canvas groups for controls. This guarantees correct z-order, so that
114 // for example a dragpoint won't obscure a node
115 data.outline_group = create_control_group(desktop);
116 data.node_data.handle_line_group = new Inkscape::CanvasItemGroup(desktop->getCanvasControls());
117 data.dragpoint_group = create_control_group(desktop);
119 data.node_data.node_group = create_control_group(desktop);
120 data.node_data.handle_group = create_control_group(desktop);
121
122 data.node_data.handle_line_group->set_name("CanvasItemGroup:NodeTool:handle_line_group");
123
125
126 this->_selection_changed_connection.disconnect();
128 selection->connectChanged(sigc::mem_fun(*this, &NodeTool::selection_changed));
129
130 this->_mouseover_changed_connection.disconnect();
133
134 if (this->_transform_handle_group) {
136 }
138
140
141 this->_multipath->signal_coords_changed.connect([this] {
143 });
144
145 _selected_nodes->signal_selection_changed.connect([this] (auto, auto) { update_tip(); });
146
147 // read prefs before adding items to selection to prevent momentarily showing the outline
148 sp_event_context_read(this, "show_handles");
149 sp_event_context_read(this, "show_outline");
150 sp_event_context_read(this, "live_outline");
151 sp_event_context_read(this, "live_objects");
152 sp_event_context_read(this, "show_path_direction");
153 sp_event_context_read(this, "show_transform_handles");
154 sp_event_context_read(this, "single_node_transform_handles");
155 sp_event_context_read(this, "edit_clipping_paths");
156 sp_event_context_read(this, "edit_masks");
157
158 selection_changed(selection);
159 update_tip();
160
161 auto prefs = Preferences::get();
162
163 if (prefs->getBool("/tools/nodes/selcue")) {
164 this->enableSelectionCue();
165 }
166
167 if (prefs->getBool("/tools/nodes/gradientdrag")) {
168 this->enableGrDrag();
169 }
170
171 desktop->emit_control_point_selected(_selected_nodes); // sets the coord entry fields to inactive
173}
174
176{
177 this->_selected_nodes->clear();
178 this->get_rubberband()->stop();
179
180 this->enableGrDrag(false);
181
182 if (this->flash_tempitem) {
184 }
185 for (auto hp : this->_helperpath_tmpitem) {
187 }
188 this->_selection_changed_connection.disconnect();
189 // this->_selection_modified_connection.disconnect();
190 this->_mouseover_changed_connection.disconnect();
191
192 delete this->_multipath;
193 delete this->_selected_nodes;
194
201}
202
207
209{
210 auto prefs = Preferences::get();
211 // This takes care of undo internally
212 _multipath->deleteNodes((NodeDeleteMode)prefs->getInt("/tools/node/delete-mode-default", (int)NodeDeleteMode::automatic));
213}
214
215// show helper paths of the applied LPE, if any
217{
218 if (!desktop) {
219 return;
220 }
221
222 auto nt = dynamic_cast<Tools::NodeTool*>(desktop->getTool());
223 if (!nt) {
224 // We remove this warning and just stop execution
225 // because we are updating helper paths also from LPE dialog so we not unsure the tool used
226 // std::cerr << "sp_update_helperpath called when Node Tool not active!" << std::endl;
227 return;
228 }
229
231 for (auto hp : nt->_helperpath_tmpitem) {
233 }
234 nt->_helperpath_tmpitem.clear();
235 std::vector<SPItem *> vec(selection->items().begin(), selection->items().end());
236 std::vector<std::pair<Geom::PathVector, Geom::Affine>> cs;
237 for (auto item : vec) {
238 auto lpeitem = cast<SPLPEItem>(item);
239 if (lpeitem && lpeitem->hasPathEffectRecursive()) {
240 Inkscape::LivePathEffect::Effect *lpe = lpeitem->getCurrentLPE();
241 if (lpe && lpe->isVisible()/* && lpe->showOrigPath()*/) {
242 std::vector<Geom::Point> selectedNodesPositions;
243 if (nt->_selected_nodes) {
244 Inkscape::UI::ControlPointSelection *selectionNodes = nt->_selected_nodes;
245 for (auto selectionNode : *selectionNodes) {
246 Inkscape::UI::Node *n = dynamic_cast<Inkscape::UI::Node *>(selectionNode);
247 selectedNodesPositions.push_back(n->position());
248 }
249 }
250 lpe->setSelectedNodePoints(selectedNodesPositions);
253 std::vector<Geom::PathVector> cs = lpe->getCanvasIndicators(lpeitem);
254 for (auto &p : cs) {
255 p *= desktop->dt2doc();
257 }
258 if (!c.empty()) {
259 auto helperpath = new Inkscape::CanvasItemBpath(desktop->getCanvasTemp(), c, true);
260 helperpath->set_stroke(0x0000ff9a);
261 helperpath->set_fill(0x0, SP_WIND_RULE_NONZERO); // No fill
262 nt->_helperpath_tmpitem.emplace_back(desktop->add_temporary_canvasitem(helperpath, 0));
263 }
264 }
265 }
266 }
267}
268
270 Glib::ustring entry_name = value.getEntryName();
271
272 if (entry_name == "show_handles") {
273 this->show_handles = value.getBool(true);
274 this->_multipath->showHandles(this->show_handles);
275 } else if (entry_name == "show_outline") {
276 this->show_outline = value.getBool();
277 this->_multipath->showOutline(this->show_outline);
278 } else if (entry_name == "live_outline") {
279 this->live_outline = value.getBool();
281 } else if (entry_name == "live_objects") {
282 this->live_objects = value.getBool();
284 } else if (entry_name == "show_path_direction") {
285 this->show_path_direction = value.getBool();
287 } else if (entry_name == "show_transform_handles") {
288 this->show_transform_handles = value.getBool(true);
291 } else if (entry_name == "single_node_transform_handles") {
295 } else if (entry_name == "edit_clipping_paths") {
296 this->edit_clipping_paths = value.getBool();
298 } else if (entry_name == "edit_masks") {
299 this->edit_masks = value.getBool();
301 } else {
302 ToolBase::set(value);
303 }
304}
305
307static
309 std::set<Inkscape::UI::ShapeRecord> &s)
310{
311 using namespace Inkscape::UI;
312
313 if (!obj) {
314 return;
315 }
316
317 //XML Tree being used directly here while it shouldn't be.
318 if (role != SHAPE_ROLE_NORMAL && (is<SPGroup>(obj) || is<SPObjectGroup>(obj))) {
319 for (auto& c: obj->children) {
320 gather_items(nt, base, &c, role, s);
321 }
322 } else if (auto item = cast<SPItem>(obj)) {
323 ShapeRecord r;
324 r.object = obj;
325 r.role = role;
326
327 // TODO add support for objectBoundingBox
328 if (role != SHAPE_ROLE_NORMAL && base) {
329 r.edit_transform = base->i2doc_affine();
330 }
331
332 if (s.insert(r).second) {
333 // this item was encountered the first time
334 if (nt->edit_clipping_paths) {
336 }
337
338 if (nt->edit_masks) {
340 }
341 }
342 }
343}
344
346{
347 std::set<ShapeRecord> shapes;
348
349 for (auto item : sel->items()) {
350 if (item) {
351 gather_items(this, nullptr, item, SHAPE_ROLE_NORMAL, shapes);
352 }
353 }
354
355 // use multiple ShapeEditors for now, to allow editing many shapes at once
356 // needs to be rethought
357 std::erase_if(_shape_editors, [&] (auto const &i) {
358 ShapeRecord s;
359 s.object = i.first;
360 return !shapes.contains(s);
361 });
362
363 for (auto const &r : shapes) {
364 auto item = cast<SPItem>(r.object);
365 auto [it, inserted] = _shape_editors.try_emplace(item);
366 if (inserted) {
367 auto si = std::make_unique<ShapeEditor>(_desktop, r.edit_transform);
368 si->set_item(item);
369 it->second = std::move(si);
370 }
371 }
372
373 std::vector<SPItem *> vec(sel->items().begin(), sel->items().end());
375 _current_selection = std::move(vec);
376 _multipath->setItems(shapes);
377 update_tip();
379}
380
382{
383 /* things to handle here:
384 * 1. selection of items
385 * 2. passing events to manipulators
386 * 3. some keybindings
387 */
388
389 auto selection = _desktop->getSelection();
390 auto prefs = Preferences::get();
391 auto rubberband = get_rubberband();
392
393 if (!rubberband->isStarted()) {
394 if (_multipath->event(this, event) || _selected_nodes->event(this, event)) {
395 return true;
396 }
397 }
398
399 bool ret = false;
400
401 inspect_event(event,
402 [&] (MotionEvent const &event) {
404 auto over_item = sp_event_context_find_item(_desktop, event.pos, false, true);
405
406 auto const motion_w = event.pos;
407 auto const motion_dt = _desktop->w2d(motion_w);
408
409 if (event.modifiers & GDK_BUTTON1_MASK) {
410 if (rubberband->isStarted()) {
411 rubberband->move(motion_dt);
413 }
414
415 auto touch_path = Modifier::get(Modifiers::Type::SELECT_TOUCH_PATH)->get_label();
416 if (rubberband->getMode() == Rubberband::Mode::TOUCHPATH) {
418 _("<b>Draw over</b> lines to select their nodes; release <b>%s</b> to switch to rubberband selection"), touch_path.c_str());
419 } else {
421 _("<b>Drag around</b> nodes to select them; press <b>%s</b> to switch to box selection"), touch_path.c_str());
422 }
423 ret = true;
424 return;
425 } else if (rubberband->isMoved()) {
426 // Mouse button is up, but rubberband is still kicking.
427 rubberband->stop();
428 }
429
430 auto &m = _desktop->getNamedView()->snap_manager;
431
432 // We will show a pre-snap indication for when the user adds a node through double-clicking
433 // Adding a node will only work when a path has been selected; if that's not the case then snapping is useless
434 if (!_desktop->getSelection()->isEmpty()) {
435 if (!(event.modifiers & GDK_SHIFT_MASK)) {
436 m.setup(_desktop);
438 m.preSnap(scp, true);
439 m.unSetup();
440 }
441 }
442
443 if (over_item && over_item != _last_over) {
444 _last_over = over_item;
445 update_tip(event);
446 }
447 // create pathflash outline
448
449 if (prefs->getBool("/tools/nodes/pathflash_enabled")) {
450 if (over_item == flashed_item) {
451 return;
452 }
453
454 if (!prefs->getBool("/tools/nodes/pathflash_selected") && over_item && selection->includes(over_item)) {
455 return;
456 }
457
458 if (flash_tempitem) {
460 flash_tempitem = nullptr;
461 flashed_item = nullptr;
462 }
463
464 auto shape = cast<SPShape>(over_item);
465 if (!shape) {
466 return; // for now, handle only shapes
467 }
468
469 flashed_item = over_item;
470 if (!shape->curveForEdit()) {
471 return; // break out when curve doesn't exist
472 }
473
474 auto c = *shape->curveForEdit() * over_item->i2dt_affine();
475
476 auto flash = new Inkscape::CanvasItemBpath(_desktop->getCanvasTemp(), c, true);
477 flash->set_stroke(over_item->highlight_color().toRGBA());
478 flash->set_fill(0x0, SP_WIND_RULE_NONZERO); // No fill.
480 _desktop->add_temporary_canvasitem(flash, prefs->getInt("/tools/nodes/pathflash_timeout", 500));
481 }
482 // do not return true, because we need to pass this event to the parent context
483 // otherwise some features cease to work
484 },
485
486 [&] (KeyPressEvent const &event) {
488 rubberband->move(_desktop->point());
489
490 // Unconfigurable shortcuts
491 switch (get_latin_keyval(event)) {
492 case GDK_KEY_Escape: // deselect everything
493 if (_selected_nodes->empty()) {
495 } else {
497 }
498 update_tip(event);
499 ret = true;
500 return;
501
502 case GDK_KEY_a:
503 case GDK_KEY_A:
504 if (mod_ctrl(event) && mod_alt(event)) {
506 // Ctrl+A is handled in selection-chemistry.cpp via verb
507 update_tip(event);
508 ret = true;
509 return;
510 }
511 break;
512
513 case GDK_KEY_h:
514 case GDK_KEY_H:
515 if (mod_ctrl_only(event)) {
516 prefs->setBool("/tools/nodes/show_handles", !show_handles);
517 ret = true;
518 return;
519 }
520 break;
521
522 case GDK_KEY_Tab:
524 ret = true;
525 return;
526 case GDK_KEY_ISO_Left_Tab:
528 ret = true;
529 return;
530
531 default:
532 break;
533 }
534 update_tip(event);
535 },
536
537 [&] (KeyReleaseEvent const &event) {
539 rubberband->move(_desktop->point());
540 update_tip(event);
541 },
542
543 [&] (ButtonPressEvent const &event) {
544 if (event.button != 1) {
545 return;
546 }
547
548 auto const event_pt = event.pos;
549 auto const desktop_pt = _desktop->w2d(event_pt);
550
551 if (event.num_press == 1) {
552
553 if (Modifier::get(Modifiers::Type::SELECT_TOUCH_PATH)->active(event.modifiers)) {
554 rubberband->setMode(Rubberband::Mode::TOUCHPATH);
555 rubberband->setHandle(RUBBERBAND_TOUCHPATH);
556 }
557 rubberband->start(_desktop, desktop_pt, true);
558 ret = true;
559 return;
560
561 } else if (event.num_press == 2) {
562
563 // If the selector received the doubleclick event, then we're at some distance from
564 // the path; otherwise, the doubleclick event would have been received by
565 // CurveDragPoint
566 if (!(event.modifiers & GDK_SHIFT_MASK)) {
567 // The first click of the double click will have cleared the path selection, because
568 // we clicked aside of the path. We need to undo this on double click
569 auto selection = _desktop->getSelection();
570 selection->addList(_previous_selection);
571
572 // The selection has been restored, and the signal selection_changed has been emitted,
573 // which has again forced a restore of the _mmap variable of the MultiPathManipulator (this->_multipath)
574 // Now we can insert the new nodes as if nothing has happened!
575 _multipath->insertNode(desktop_pt);
576 ret = true;
577 return;
578 }
579
580 }
581 },
582
583 [&] (ButtonReleaseEvent const &event) {
584 if (event.button != 1) {
585 return;
586 }
587
588 if (rubberband->isStarted() && rubberband->isMoved()) {
589 select_area(rubberband->getPath(), event);
590 } else {
591 select_point(event);
592 }
593 rubberband->stop();
594 ret = true;
595 return;
596 },
597
598 [&] (CanvasEvent const &event) {}
599 );
600
601 return ret || ToolBase::root_handler(event);
602}
603
605{
606 if (ToolBase::item_handler(item, event)) {
607 return true;
608 }
609
610 bool ret = false;
611
612 // Node shape editors are handled differently than shape tools
613 inspect_event(event,
614 [&] (ButtonPressEvent const &event) {
615 if (event.num_press != 1 || event.button != 1) {
616 return;
617 }
618 for (auto &se : _shape_editors) {
619 // This allows users to select an arbitary position in a pattern to edit on canvas.
620 if (auto &knotholder = se.second->knotholder) {
621 auto const point = event.pos;
622
623 // This allows us to dive into groups and find what the real item is
624 if (_desktop->getItemAtPoint(point, true) != knotholder->getItem()) {
625 continue;
626 }
627
628 ret |= knotholder->set_item_clickpos(_desktop->w2d(point) * _desktop->dt2doc());
629 }
630 }
631 },
632 [&] (CanvasEvent const &event) {}
633 );
634
635 return ret;
636}
637
639{
640 if (event.type() == EventType::KEY_PRESS || event.type() == EventType::KEY_RELEASE) {
641 auto modifiers_change = event.modifiersChange();
642
643 if (modifiers_change == 0) {
644 return;
645 }
646
647 auto modifiers_after = event.modifiers ^ modifiers_change;
648
649 if (mod_shift(modifiers_after)) {
650 if (_last_over) {
652 C_("Node tool tip", "<b>Shift</b>: drag to add nodes to the selection, "
653 "click to toggle object selection"));
654 } else {
656 C_("Node tool tip", "<b>Shift</b>: drag to add nodes to the selection"));
657 }
658
659 return;
660 }
661 }
662
663 update_tip();
664}
665
667{
668 unsigned sz = _selected_nodes->size();
669 unsigned total = _selected_nodes->allPoints().size();
670
671 if (sz != 0) {
672 // TRANSLATORS: %1 is the number of nodes selected. %2 is the total number of nodes.
673 auto nodestring = Glib::ustring::compose(
674 ngettext("<b>%1 of %2</b> node selected.", "<b>%1 of %2</b> nodes selected.", total),
675 sz, total);
676
677 if (sz == 2) {
678 // if there are only two nodes selected, display the angle
679 // of a line going through them relative to the X axis.
681 std::vector<Geom::Point> positions;
682 for (auto selection_node : selection_nodes) {
683 if (selection_node->selected()) {
684 Inkscape::UI::Node *n = dynamic_cast<Inkscape::UI::Node *>(selection_node);
685 positions.push_back(n->position());
686 }
687 }
688 g_assert(positions.size() == 2);
689 const double angle = Geom::deg_from_rad(Geom::Line(positions[0], positions[1]).angle());
690 nodestring += " ";
691 // TRANSLATORS: %1 is an angle in degrees, formatted with two decimal places.
692 nodestring += Glib::ustring::compose(_("Angle: %1°."),
693 Inkscape::ustring::format_classic(std::fixed, std::setprecision(2), angle));
694 }
695
696 if (this->_last_over) {
697 // TRANSLATORS: The %1 below is where the previous "%1 of %2 nodes selected" sentence gets put.
698 auto const dyntip = Glib::ustring::compose(C_("Node tool tip",
699 "%1 Drag to select nodes, click to edit only this object (more: Shift)"),
700 nodestring);
701 this->message_context->set(Inkscape::NORMAL_MESSAGE, dyntip.c_str());
702 } else {
703 // TRANSLATORS: The %1 below is where the previous "%1 of %2 nodes selected" sentence gets put.
704 auto const dyntip = Glib::ustring::compose(C_("Node tool tip",
705 "%1 Drag to select nodes, click to clear the selection"),
706 nodestring);
707 this->message_context->set(Inkscape::NORMAL_MESSAGE, dyntip.c_str());
708 }
709 } else if (!this->_multipath->empty()) {
710 if (this->_last_over) {
711 this->message_context->set(Inkscape::NORMAL_MESSAGE, C_("Node tool tip",
712 "Drag to select nodes, click to edit only this object"));
713 } else {
714 this->message_context->set(Inkscape::NORMAL_MESSAGE, C_("Node tool tip",
715 "Drag to select nodes, click to clear the selection"));
716 }
717 } else {
718 if (this->_last_over) {
719 this->message_context->set(Inkscape::NORMAL_MESSAGE, C_("Node tool tip",
720 "Drag to select objects to edit, click to edit this object (more: Shift)"));
721 } else {
722 this->message_context->set(Inkscape::NORMAL_MESSAGE, C_("Node tool tip",
723 "Drag to select objects to edit"));
724 }
725 }
726}
727
729{
730 if (_multipath->empty()) {
731 // if multipath is empty, select rubberbanded items rather than nodes
732 auto selection = _desktop->getSelection();
733 auto sel_doc = _desktop->dt2doc() * *path.boundsFast();
734 auto items = _desktop->getDocument()->getItemsInBox(_desktop->dkey, sel_doc);
735 selection->setList(items);
736 } else {
737 bool shift = mod_shift(event);
738 bool ctrl = mod_ctrl(event);
739
740 if (!shift) {
741 // A/C. No modifier, selects all nodes, or selects all other nodes.
743 }
744 if (shift && ctrl) {
745 // D. Shift+Ctrl pressed, removes nodes under box from existing selection.
746 _selected_nodes->selectArea(path, true);
747 } else {
748 // A/B/C. Adds nodes under box to existing selection.
750 if (ctrl) {
751 // C. Selects the inverse of all nodes under the box.
753 }
754 }
755 }
756}
757
759{
760 if (event.button != 1) {
761 return;
762 }
763
764 auto selection = _desktop->getSelection();
765
766 auto item_clicked = sp_event_context_find_item(_desktop, event.pos,
767 (event.modifiers & GDK_ALT_MASK) && !(event.modifiers & GDK_CONTROL_MASK), true);
768
769 if (!item_clicked) { // nothing under cursor
770 // if no Shift, deselect
771 // if there are nodes selected, the first click should deselect the nodes
772 // and the second should deselect the items
773 if (!mod_shift(event)) {
774 if (_selected_nodes->empty()) {
775 selection->clear();
776 } else {
778 }
779 }
780 } else {
781 if (mod_shift(event)) {
782 selection->toggle(item_clicked);
783 } else if (!selection->includes(item_clicked)) {
784 selection->set(item_clicked);
785 }
786 }
787}
788
791
792 CurveDragPoint *cdp = dynamic_cast<CurveDragPoint*>(p);
793
794 if (cdp && !this->cursor_drag) {
795 this->set_cursor("node-mouseover.svg");
796 this->cursor_drag = true;
797 } else if (!cdp && this->cursor_drag) {
798 this->set_cursor("node.svg");
799 this->cursor_drag = false;
800 }
801}
802
806
808{
809 auto rubberband = get_rubberband();
810 if (Modifier::get(Modifiers::Type::NODE_REMOVE_FROM)->active(event.modifiersAfter())) {
811 // if Ctrl+Shift is pressed, change rubberband operation to remove
812 rubberband->setOperation(Rubberband::Operation::REMOVE);
813 } else if (Modifier::get(Modifiers::Type::NODE_INVERT)->active(event.modifiersAfter())) {
814 // Ctrl pressed, it's an invert option
815 rubberband->setOperation(Rubberband::Operation::INVERT);
816 } else {
817 // Nothing pressed, switch back to add
818 rubberband->setOperation(Rubberband::Operation::ADD);
819 }
820}
821
822} // namespace Inkscape::UI::Tools
823
824/*
825 Local Variables:
826 mode:c++
827 c-file-style:"stroustrup"
828 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
829 indent-tabs-mode:nil
830 fill-column:99
831 End:
832*/
833// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Infinite line on a plane.
Definition line.h:53
Sequence of subpaths.
Definition pathvector.h:122
Sequence of contiguous curves, aka spline.
Definition path.h:353
OptRect boundsFast() const
Get the approximate bounding box.
Definition path.cpp:348
std::vector< Geom::PathVector > getCanvasIndicators(SPLPEItem const *lpeitem)
Return a vector of PathVectors which contain all canvas indicators for this effect.
Definition effect.cpp:1739
void setSelectedNodePoints(std::vector< Geom::Point > sNP)
Definition effect.cpp:1240
void setF(MessageType type, char const *format,...) G_GNUC_PRINTF(3
pushes a message on the stack using prinf-style formatting, and replacing our old message
A class to represent ways functionality is driven by shift modifiers.
Definition modifiers.h:103
SPItemRange items()
Returns a range of selected SPItems.
Definition object-set.h:230
boost::enable_if< boost::is_base_of< SPObject, T >, void >::type addList(const std::vector< T * > &objs)
Adds the specified objects to selection, without deselecting first.
Definition object-set.h:301
bool isEmpty()
Returns true if no items are selected.
Data type representing a typeless value of a preference.
Glib::ustring getEntryName() const
Get the last component of the preference's path.
bool getBool(bool def=false) const
Interpret the preference as a Boolean value.
static Preferences * get()
Access the singleton Preferences object.
Rubberbanding selector.
Definition rubberband.h:35
static Rubberband * get(SPDesktop *desktop)
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
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.
Group of selected control points.
void selectArea(Geom::Path const &, bool invert=false)
Select all points inside the given rectangle (in desktop coordinates).
sigc::signal< void(std::vector< SelectableControlPoint * >, bool)> signal_selection_changed
void invertSelection()
Unselect all selected points and select all unselected points.
bool event(Inkscape::UI::Tools::ToolBase *tool, CanvasEvent const &event) override
Handle input event. Returns true if handled.
void showTransformHandles(bool v, bool one_node)
void clear()
Remove all points from the selection, making it empty.
void selectAll()
Select all points that this selection can contain.
Draggable point, the workhorse of on-canvas editing.
static sigc::signal< void(ControlPoint *)> signal_mouseover_change
Emitted when the mouseovered point changes.
An invisible point used to drag curves.
Manipulator that manages multiple path manipulators active at the same time.
void setItems(std::set< ShapeRecord > const &)
Change the set of items to edit.
sigc::signal< void()> signal_coords_changed
Emitted whenever the coordinates shown in the status bar need updating.
void setLiveObjects(bool set)
Set live object update status.
void setLiveOutline(bool set)
Set live outline update status.
bool event(Inkscape::UI::Tools::ToolBase *tool, CanvasEvent const &event) override
Handle input event. Returns true if handled.
void deleteNodes()
Delete nodes, use the preference to decide which mode to use.
std::vector< Inkscape::Display::TemporaryItem * > _helperpath_tmpitem
Definition node-tool.h:47
std::vector< SPItem * > _previous_selection
Definition node-tool.h:82
Inkscape::UI::PathSharedData * _path_data
Definition node-tool.h:68
void _updateSelectionColor(CanvasEvent const &event)
void mouseover_changed(Inkscape::UI::ControlPoint *p)
sigc::connection _mouseover_changed_connection
Definition node-tool.h:62
std::vector< SPItem * > _current_selection
Definition node-tool.h:81
void select_point(ButtonReleaseEvent const &event)
Inkscape::UI::ControlPointSelection * _selected_nodes
Definition node-tool.h:45
bool item_handler(SPItem *item, CanvasEvent const &event) override
Handles item specific events.
Inkscape::CanvasItemGroup * _transform_handle_group
Definition node-tool.h:69
void selection_changed(Inkscape::Selection *sel)
NodeTool(SPDesktop *desktop)
std::map< SPItem *, std::unique_ptr< ShapeEditor > > _shape_editors
Definition node-tool.h:48
sigc::connection _selection_changed_connection
Definition node-tool.h:61
Inkscape::Display::TemporaryItem * flash_tempitem
Definition node-tool.h:66
void select_area(Geom::Path const &path, ButtonReleaseEvent const &event)
Inkscape::Rubberband * get_rubberband() const
bool root_handler(CanvasEvent const &event) override
void set(Preferences::Entry const &val) override
Called by our pref_observer if a preference has been changed.
Inkscape::UI::MultiPathManipulator * _multipath
Definition node-tool.h:46
Base class for Event processors.
Definition tool-base.h:95
void set_cursor(std::string filename)
Sets the current cursor to the given filename.
std::unique_ptr< MessageContext > message_context
Definition tool-base.h:181
virtual bool root_handler(CanvasEvent const &event)
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.
virtual void set(Preferences::Entry const &val)
Called by our pref_observer if a preference has been changed.
MessageContext * defaultMessageContext() const
Definition tool-base.h:111
To do: update description of desktop.
Definition desktop.h:149
double current_zoom() const
Definition desktop.h:335
Inkscape::CanvasItemGroup * getCanvasControls() const
Definition desktop.h:196
void emit_control_point_selected(Inkscape::UI::ControlPointSelection *selection)
Definition desktop.cpp:1365
SPDocument * getDocument() const
Definition desktop.h:189
Inkscape::Display::TemporaryItem * add_temporary_canvasitem(Inkscape::CanvasItem *item, int lifetime_msecs, bool move_to_bottom=true)
One should not keep a reference to the SPCanvasItem, the temporary item code will delete the object f...
Definition desktop.cpp:229
unsigned dkey
Definition desktop.h:229
Inkscape::CanvasItemGroup * getCanvasTemp() const
Definition desktop.h:202
Geom::Affine const & dt2doc() const
Definition desktop.cpp:1343
Geom::Point point() const
Returns the mouse point in desktop coordinates; if mouse is outside the canvas, returns the center of...
Definition desktop.cpp:378
SPItem * getItemAtPoint(Geom::Point const &p, bool into_groups, SPItem *upto=nullptr) const
Definition desktop.cpp:352
SPNamedView * getNamedView() const
Definition desktop.h:191
Inkscape::Selection * getSelection() const
Definition desktop.h:188
void remove_temporary_canvasitem(Inkscape::Display::TemporaryItem *tempitem)
It is perfectly safe to call this function while the object has already been deleted due to a timeout...
Definition desktop.cpp:242
Inkscape::UI::Tools::ToolBase * getTool() const
Definition desktop.h:187
Geom::Affine const & w2d() const
Transformation from window to desktop coordinates (zoom/rotate).
Definition desktop.h:416
std::vector< SPItem * > getItemsInBox(unsigned int dkey, Geom::Rect const &box, bool take_hidden=false, bool take_insensitive=false, bool take_groups=true, bool enter_groups=false, bool enter_layers=true) const
Return list of items, contained in box.
Base class for visual SVG elements.
Definition sp-item.h:109
Geom::Affine i2dt_affine() const
Returns the transformation from item to desktop coords.
Definition sp-item.cpp:1829
SPMask * getMaskObject() const
Definition sp-item.cpp:177
Geom::Affine i2doc_affine() const
Returns the accumulated transformation of the item and all its ancestors, including root's viewport.
Definition sp-item.cpp:1824
SPClipPath * getClipObject() const
Definition sp-item.cpp:102
SnapManager snap_manager
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
ChildrenList children
Definition sp-object.h:907
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
Control point selection - stores a set of control points and applies transformations to them.
double c[8][4]
void pathvector_append(Geom::PathVector &to, Geom::PathVector const &pathv, bool use_lineto)
Append pathv to to.
Definition curve.cpp:78
SPItem * item
void shift(T &a, T &b, T const &c)
Multi path manipulator - a tool component that edits multiple paths at once.
void selectNone(SPDesktop *desktop)
static void gather_items(NodeTool *nt, SPItem *base, SPObject *obj, Inkscape::UI::ShapeRole role, std::set< Inkscape::UI::ShapeRecord > &s)
Recursively collect ShapeRecords.
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.
Inkscape::CanvasItemGroup * create_control_group(SPDesktop *desktop)
Definition node-tool.cpp:98
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_event_context_read(ToolBase *tool, char const *key)
Calls virtual set() function of ToolBase.
void sp_update_helperpath(SPDesktop *desktop)
User interface code.
Definition desktop.h:113
ShapeRole
Role of the shape in the drawing - affects outline display and color.
@ SHAPE_ROLE_CLIPPING_PATH
Glib::ustring format_classic(T const &... args)
bool mod_alt(unsigned modifiers)
void inspect_event(E &&event, Fs... funcs)
Perform pattern-matching on a CanvasEvent.
@ SNAPSOURCE_OTHER_HANDLE
Definition snap-enums.h:56
bool mod_ctrl_only(unsigned modifiers)
bool mod_ctrl(unsigned modifiers)
bool mod_shift(unsigned modifiers)
@ NORMAL_MESSAGE
Definition message.h:26
New node tool with support for multiple path editing.
GList * items
Inkscape::ShapeEditor This is a container class which contains a knotholder for shapes.
static const Point data[]
unsigned button
The button that was pressed/released. (Matches GDK_BUTTON_*.)
Geom::Point pos
Location of the cursor, in world coordinates.
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.
unsigned modifiersAfter() const
Get the modifiers mask immediately after the event. (Convenience function.)
virtual EventType type() const =0
Return the dynamic type of the CanvasEvent.
A key has been pressed.
A key has been released.
Movement of the mouse pointer.
Inkscape::CanvasItemGroup * node_group
Definition node.h:56
Inkscape::CanvasItemGroup * handle_group
Definition node.h:57
Inkscape::CanvasItemGroup * handle_line_group
Definition node.h:58
ControlPointSelection * selection
Definition node.h:55
Inkscape::CanvasItemGroup * outline_group
Inkscape::CanvasItemGroup * dragpoint_group
@ SP_WIND_RULE_NONZERO
Definition style-enums.h:24
SPDesktop * desktop