Inkscape
Vector Graphics Editor
selection.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Per-desktop selection container
4 *
5 * Authors:
6 * Lauris Kaplinski <lauris@kaplinski.com>
7 * MenTaLguY <mental@rydia.net>
8 * bulia byak <buliabyak@users.sf.net>
9 * Andrius R. <knutux@gmail.com>
10 * Abhishek Sharma
11 * Adrian Boguszewski
12 *
13 * Copyright (C) 2016 Adrian Boguszewski
14 * Copyright (C) 2006 Andrius R.
15 * Copyright (C) 2004-2005 MenTaLguY
16 * Copyright (C) 1999-2002 Lauris Kaplinski
17 * Copyright (C) 2001-2002 Ximian, Inc.
18 *
19 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
20 */
21
22#include "selection.h"
23
24#include <cmath>
25
26#include "desktop.h"
27#include "document-undo.h"
28#include "document.h"
29#include "inkscape.h"
30#include "layer-manager.h"
31#include "page-manager.h"
32
33#include "object/sp-defs.h"
34#include "object/sp-page.h"
35#include "object/sp-path.h"
36#include "object/sp-shape.h"
39#include "ui/tools/node-tool.h"
40
41static constexpr auto SP_SELECTION_UPDATE_PRIORITY = G_PRIORITY_HIGH_IDLE + 1;
42
43namespace Inkscape {
44
46 ObjectSet(desktop),
47 _selection_context(nullptr),
48 _flags(0),
49 _idle(0),
50 anchor_x(0.0),
51 anchor_y(0.0)
52{
53}
54
56 ObjectSet(document),
57 _selection_context(nullptr),
58 _flags(0),
59 _idle(0),
60 anchor_x(0.0),
61 anchor_y(0.0)
62{
63}
64
66 if (_idle) {
67 g_source_remove(_idle);
68 _idle = 0;
69 }
70}
71
72/* Handler for selected objects "modified" signal */
73
74void Selection::_schedule_modified(SPObject */*obj*/, guint flags) {
75 if (!this->_idle) {
76 /* Request handling to be run in _idle loop */
77 this->_idle = g_idle_add_full(SP_SELECTION_UPDATE_PRIORITY, GSourceFunc(&Selection::_emit_modified), this, nullptr);
78 }
79
80 /* Collect all flags */
81 this->_flags |= flags;
82}
83
85{
86 /* force new handler to be created if requested before we return */
87 selection->_idle = 0;
88 guint flags = selection->_flags;
89 selection->_flags = 0;
90
91 selection->_emitModified(flags);
92
93 /* drop this handler */
94 return FALSE;
95}
96
97void Selection::_emitModified(guint flags)
98{
99 for (auto it = _modified_signals.begin(); it != _modified_signals.end(); ) {
100 if (it->empty()) {
101 it = _modified_signals.erase(it);
102 } else {
103 it->emit(this, flags);
104 ++it;
105 }
106 }
107
108 if (!_desktop || isEmpty()) {
109 return;
110 }
111
112 auto &pm = _desktop->getDocument()->getPageManager();
113
114 // If the selected items have been moved to a new page...
115 if (auto item = singleItem()) {
116 pm.selectPage(item, false);
117 } else {
118 SPPage *page = pm.getPageFor(firstItem(), true);
119 for (auto this_item : this->items()) {
120 if (page != pm.getPageFor(this_item, true)) {
121 return;
122 }
123 }
124 pm.selectPage(page);
125 }
126}
127
128void Selection::_emitChanged(bool persist_selection_context/* = false */) {
130 if (persist_selection_context) {
131 if (nullptr == _selection_context) {
135 }
136 } else {
138 }
139
143 if (_document && _desktop) {
144 if (auto item = singleItem()) {
145 if (_change_layer) {
146 auto layer = _desktop->layerManager().layerForObject(item);
147 if (layer && layer != _selection_context) {
149 }
150 }
151 if (_change_page) {
152 // This could be more complex if we want to be smarter.
154 }
155 }
157 }
158
159 for (auto it = _changed_signals.begin(); it != _changed_signals.end(); ) {
160 if (it->empty()) {
161 it = _changed_signals.erase(it);
162 } else {
163 it->emit(this);
164 ++it;
165 }
166 }
167}
168
170{
171 if (nullptr == _selection_context || _selection_context != obj)
172 return;
173
175
177 _selection_context = nullptr;
178}
179
181 if (nullptr != _selection_context)
182 return _selection_context;
184}
185
186std::vector<Inkscape::SnapCandidatePoint> Selection::getSnapPoints(SnapPreferences const *snapprefs) const {
187 std::vector<Inkscape::SnapCandidatePoint> p;
188
189 if (snapprefs != nullptr){
190 SnapPreferences snapprefs_dummy = *snapprefs; // create a local copy of the snapping prefs
191 snapprefs_dummy.setTargetSnappable(Inkscape::SNAPTARGET_ROTATION_CENTER, false); // locally disable snapping to the item center
192 auto items = const_cast<Selection *>(this)->items();
193 for (auto iter = items.begin(); iter != items.end(); ++iter) {
194 SPItem *this_item = *iter;
195 this_item->getSnappoints(p, &snapprefs_dummy);
196
197 //Include the transformation origin for snapping
198 //For a selection or group only the overall center is considered, not for each item individually
200 p.emplace_back(this_item->getCenter(), SNAPSOURCE_ROTATION_CENTER);
201 }
202 }
203 }
204
205 return p;
206}
207
208sigc::connection Selection::connectChanged(sigc::slot<void (Selection *)> slot)
209{
210 if (_changed_signals.empty()) _changed_signals.emplace_back();
211 return _changed_signals.back().connect(std::move(slot));
212}
213
214sigc::connection Selection::connectChangedFirst(sigc::slot<void (Selection *)> slot)
215{
216 return _changed_signals.emplace_front().connect(std::move(slot));
217}
218
219void Selection::setAnchor(double x, double y, bool set)
220{
221 double const epsilon = 1e-12;
222 if (std::fabs(anchor_x - x) > epsilon || std::fabs(anchor_y - y) > epsilon || set != has_anchor) {
223 anchor_x = x;
224 anchor_y = y;
225 has_anchor = set;
226 this->_emitModified(SP_OBJECT_MODIFIED_FLAG);
227 }
228}
229
230sigc::connection Selection::connectModified(sigc::slot<void (Selection *, unsigned)> slot)
231{
232 if (_modified_signals.empty()) _modified_signals.emplace_back();
233 return _modified_signals.back().connect(std::move(slot));
234}
235
236sigc::connection Selection::connectModifiedFirst(sigc::slot<void (Selection *, unsigned)> slot)
237{
238 return _modified_signals.emplace_front().connect(std::move(slot));
239}
240
242 g_return_val_if_fail(repr != nullptr, NULL);
243 SPObject *object = _desktop->getDocument()->getObjectByRepr(repr);
244 assert(object == _desktop->getDocument()->getObjectById(repr->attribute("id")));
245 return object;
246}
247
249 auto items = this->items();
250 std::set<SPObject*> layers;
251 for (auto iter = items.begin(); iter != items.end(); ++iter) {
252 SPObject *layer = _desktop->layerManager().layerForObject(*iter);
253 layers.insert(layer);
254 }
255
256 return layers.size();
257}
258
260 auto items = this->items();
261 std::set<SPObject*> parents;
262 for (auto iter = items.begin(); iter != items.end(); ++iter) {
263 SPObject *parent = (*iter)->parent;
264 parents.insert(parent);
265 }
266 return parents.size();
267}
268
270 _modified_connections[object] = object->connectModified(sigc::mem_fun(*this, &Selection::_schedule_modified));
271}
272
274 _modified_connections.erase(object);
275}
276
277void
279 _selected_ids.clear();
280 _seldata.clear();
281 params.clear();
282}
283
284void
286{
287 SPDesktop *desktop = this->desktop();
288 Inkscape::UI::Tools::NodeTool *tool = nullptr;
289 if (desktop) {
290 if (auto nt = dynamic_cast<Inkscape::UI::Tools::NodeTool*>(desktop->getTool())) {
291 tool = nt;
292 }
293 }
294
295 emptyBackup();
296
297 for (auto const * const item : items()) {
298 auto id = item->getId();
299 if (!id) continue;
300
301 std::string selected_id;
302 selected_id += "--id=";
303 selected_id += id;
304 params.push_back(std::move(selected_id));
305
306 _selected_ids.emplace_back(std::move(id));
307 }
308
309 if (!tool) return;
310
311 for (auto const point : tool->_selected_nodes->_points_list) {
312 auto const node = dynamic_cast<Inkscape::UI::Node const *>(point);
313 if (!node) continue;
314
315 auto const &nodeList = node->nodeList();
316 auto const &subpathList = nodeList.subpathList();
317
318 int sp = 0;
319 bool found_sp = false;
320 for (auto i = subpathList.begin(), e = subpathList.end(); i != e; ++i, ++sp) {
321 if (&**i == &nodeList) {
322 found_sp = true;
323 break;
324 }
325 }
326
327 int nl = 0;
328 bool found_nl = false;
329 for (auto j = nodeList.begin(), e = nodeList.end(); j != e; ++j, ++nl) {
330 if (&*j == node){
331 found_nl = true;
332 break;
333 }
334 }
335
336 if (!(found_nl && found_sp)) {
337 g_warning("Something went wrong while trying to pass selected nodes to extension. Please report a bug.");
338 return;
339 }
340
341 auto id = subpathList.pm().item()->getId();
342 params.push_back(Glib::ustring::compose("--selected-nodes=%1:%2:%3", id, sp, nl));
343 _seldata.emplace_back(std::move(id), std::make_pair(sp, nl));
344 }
345}
346
347void
349{
350 SPDesktop *desktop = this->desktop();
351 SPDocument *document = SP_ACTIVE_DOCUMENT;
352 SPDefs * defs = document->getDefs();
353 Inkscape::UI::Tools::NodeTool *tool = nullptr;
354 if (desktop) {
355 if (auto nt = dynamic_cast<Inkscape::UI::Tools::NodeTool*>(desktop->getTool())) {
356 tool = nt;
357 }
358 }
359
360 // update selection
361 std::vector<SPItem *> new_selection;
362 for (auto const &selected_id : _selected_ids) {
363 auto const item = cast<SPItem>(document->getObjectById(selected_id.c_str()));
364 if (item && !defs->isAncestorOf(item)) {
365 new_selection.push_back(item);
366 }
367 }
368 clear();
369 add(new_selection.begin(), new_selection.end());
370 new_selection.clear();
371
372 if (!tool) return;
373
374 auto const cps = tool->_selected_nodes;
375 cps->selectAll();
376 auto const point = !cps->_points_list.empty() ? cps->_points_list.front() : nullptr;
377 cps->clear();
378 if (!point) return;
379
380 auto const node = dynamic_cast<Inkscape::UI::Node const *>(point);
381 if (!node) return;
382
383 auto const &sp = node->nodeList().subpathList();
384 for (auto & l : _seldata) {
385 int sp_count = 0;
386 for (auto j = sp.begin(); j != sp.end(); ++j, ++sp_count) {
387 if (sp_count != l.second.first) continue;
388
389 int nt_count = 0;
390 for (auto k = (*j)->begin(); k != (*j)->end(); ++k, ++nt_count) {
391 if (nt_count == l.second.second) {
392 cps->insert(k.ptr());
393 break;
394 }
395 }
396 break;
397 }
398 }
399}
400
401} // namespace Inkscape
402
403/*
404 Local Variables:
405 mode:c++
406 c-file-style:"stroustrup"
407 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
408 indent-tabs-mode:nil
409 fill-column:99
410 End:
411*/
412// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
uint64_t page
Definition: canvas.cpp:172
static void resetKey(SPDocument *document)
SPObject * layerForObject(SPObject *object)
Return layer that contains object.
void setCurrentLayer(SPObject *object, bool clear=false)
Sets the current layer of the desktop.
SPGroup * currentLayer() const
Returns current top layer.
SPDesktop * desktop()
Returns the desktop the selection is bound to.
Definition: object-set.h:400
GC::soft_ptr< SPDocument > _document
Definition: object-set.h:546
SPItemRange items()
Returns a range of selected SPItems.
Definition: object-set.h:267
GC::soft_ptr< SPDesktop > _desktop
Definition: object-set.h:545
void clear()
Unselects all selected objects.
Definition: object-set.cpp:122
virtual void _emitChanged(bool persist_selection_context=false)
Definition: object-set.cpp:88
bool isEmpty()
Returns true if no items are selected.
Definition: object-set.cpp:227
SPDocument * document()
Returns the document the selection is bound to.
Definition: object-set.h:407
SPItem * firstItem() const
Returns the first selected item, returns nullptr if no items selected.
Definition: object-set.cpp:246
SPItem * singleItem()
Returns a single selected item.
Definition: object-set.cpp:235
bool selectPage(SPPage *page)
Set the given page as the selected page.
The set of selected SPObjects for a given document and layer model.
Definition: selection.h:55
SPObject * _selection_context
Definition: selection.h:233
unsigned int _idle
Definition: selection.h:235
void _releaseSignals(SPObject *object) override
Definition: selection.cpp:273
unsigned int _flags
Definition: selection.h:234
void add(XML::Node *repr)
Add an XML node's SPObject to the set of selected objects.
Definition: selection.h:89
void setBackup()
Set a backup of current selection and store it also to be command line readable by extension system.
Definition: selection.cpp:285
std::vector< Inkscape::SnapCandidatePoint > getSnapPoints(SnapPreferences const *snapprefs) const
Compute the list of points in the selection that are to be considered for snapping from.
Definition: selection.cpp:186
auto_connection _context_release_connection
Definition: selection.h:241
void _releaseContext(SPObject *obj)
Releases an active layer object that is being removed.
Definition: selection.cpp:169
void setAnchor(double x, double y, bool set=true)
Set the anchor point of the selection, used for telling it how transforms should be anchored against.
Definition: selection.cpp:219
SPObject * activeContext()
Returns active layer for selection (currentLayer or its parent).
Definition: selection.cpp:180
void set(XML::Node *repr)
Set the selection to an XML node's SPObject.
Definition: selection.h:100
std::list< sigc::signal< void(Selection *, unsigned int)> > _modified_signals
Definition: selection.h:244
std::vector< std::pair< std::string, std::pair< int, int > > > _seldata
Definition: selection.h:238
void _connectSignals(SPObject *object) override
Definition: selection.cpp:269
size_t numberOfLayers()
Returns the number of layers in which there are selected objects.
Definition: selection.cpp:248
void _emitChanged(bool persist_selection_context=false) override
Issues changed selection signal.
Definition: selection.cpp:128
std::unordered_map< SPObject *, auto_connection > _modified_connections
Definition: selection.h:240
static int _emit_modified(Selection *selection)
Issues modification notification signals.
Definition: selection.cpp:84
sigc::connection connectModifiedFirst(sigc::slot< void(Selection *, unsigned)> slot)
Definition: selection.cpp:236
std::list< std::string > params
Here store a paramlist when set backup.
Definition: selection.h:206
std::list< sigc::signal< void(Selection *)> > _changed_signals
Definition: selection.h:243
void _emitModified(unsigned int flags)
Issues modified selection signal.
Definition: selection.cpp:97
std::vector< std::string > _selected_ids
Definition: selection.h:239
size_t numberOfParents()
Returns the number of parents to which the selected objects belong.
Definition: selection.cpp:259
sigc::connection connectChangedFirst(sigc::slot< void(Selection *)> slot)
Definition: selection.cpp:214
void _schedule_modified(SPObject *obj, unsigned int flags)
Schedules an item modification signal to be sent.
Definition: selection.cpp:74
sigc::connection connectChanged(sigc::slot< void(Selection *)> slot)
Connects a slot to be notified of selection changes.
Definition: selection.cpp:208
Selection(SPDesktop *desktop)
Constructs an selection object, bound to a particular layer model.
Definition: selection.cpp:45
void restoreBackup()
Restore a selection from a existing backup.
Definition: selection.cpp:348
~Selection() override
Definition: selection.cpp:65
SPObject * _objectForXMLNode(XML::Node *repr) const
returns the SPObject corresponding to an xml node (if any).
Definition: selection.cpp:241
void emptyBackup()
Clear backup of current selection.
Definition: selection.cpp:278
sigc::connection connectModified(sigc::slot< void(Selection *, unsigned)> slot)
Connects a slot to be notified of selected object modifications.
Definition: selection.cpp:230
Storing of snapping preferences.
void setTargetSnappable(Inkscape::SnapTargetType const target, bool enabled)
bool isTargetSnappable(Inkscape::SnapTargetType const target) const
std::list< SelectableControlPoint * > _points_list
void selectAll()
Select all points that this selection can contain.
Inkscape::UI::ControlPointSelection * _selected_nodes
Definition: node-tool.h:50
Interface for refcounted XML nodes.
Definition: node.h:80
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
Definition: sp-defs.h:19
To do: update description of desktop.
Definition: desktop.h:150
SPDocument * getDocument() const
Definition: desktop.h:187
Inkscape::UI::Tools::ToolBase * getTool() const
Definition: desktop.h:185
Inkscape::LayerManager & layerManager()
Definition: desktop.h:300
Typed SVG document implementation.
Definition: document.h:106
SPObject * getObjectById(std::string const &id) const
Definition: document.cpp:1182
Inkscape::PageManager & getPageManager()
Definition: document.h:176
SPDefs * getDefs()
Return the main defs object for the document.
Definition: document.cpp:259
SPObject * getObjectByRepr(Inkscape::XML::Node *repr) const
Definition: document.cpp:1358
Base class for visual SVG elements.
Definition: sp-item.h:107
Geom::Point getCenter() const
Definition: sp-item.cpp:301
void getSnappoints(std::vector< Inkscape::SnapCandidatePoint > &p, Inkscape::SnapPreferences const *snapprefs=nullptr) const
Definition: sp-item.cpp:1028
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition: sp-object.h:146
char const * getId() const
Returns the objects current ID string.
Definition: sp-object.cpp:206
sigc::connection connectRelease(sigc::slot< void(SPObject *)> slot)
Connects to the release request signal.
Definition: sp-object.h:223
bool isAncestorOf(SPObject const *object) const
True if object is non-NULL and this is some in/direct parent of object.
Definition: sp-object.cpp:322
Definition: sp-page.h:31
Control point selection - stores a set of control points and applies transformations to them.
Editable view implementation.
TODO: insert short description here.
SPItem * item
Definition: imagemagick.cpp:43
Inkscape::XML::Node * node
Definition: imagemagick.cpp:39
const double epsilon
Definition: axis-manip.h:56
D2< T > compose(D2< T > const &a, T const &b)
Definition: d2.h:405
CMYK to sRGB conversion routines.
@ SNAPSOURCE_ROTATION_CENTER
Definition: snap-enums.h:52
@ SNAPTARGET_ROTATION_CENTER
Definition: snap-enums.h:117
New node tool with support for multiple path editing.
Path manipulator - a component that edits a single path on-canvas.
Ocnode * parent
Definition: quantize.cpp:31
static constexpr auto SP_SELECTION_UPDATE_PRIORITY
Definition: selection.cpp:41
SPObject * sp_object_unref(SPObject *object, SPObject *owner)
Decrease reference count of object, with possible debugging and finalization.
Definition: sp-object.cpp:252
SPObject * sp_object_ref(SPObject *object, SPObject *owner)
Increase reference count of object, with possible debugging.
Definition: sp-object.cpp:241
SPPage – a page object.