Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
actions-selection.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Gio::Actions for to change selection, tied to the application and without GUI.
4 *
5 * Copyright (C) 2018 Tavmjong Bah
6 *
7 * The contents of this file may be used under the GNU General Public License Version 2 or later.
8 *
9 */
10
11#include <iostream>
12
13#include <giomm.h> // Not <gtkmm.h>! To eventually allow a headless version!
14#include <glibmm/i18n.h>
15
16#include "actions-selection.h"
17#include "actions-helper.h"
18
19#include "document.h"
21#include "selection.h" // Selection
22
23#include "object/sp-root.h" // select_all: document->getRoot();
24#include "object/sp-item-group.h" // select_all
25
26void
28{
29 SPDocument* document = nullptr;
30 Inkscape::Selection* selection = nullptr;
31 if (!get_document_and_selection(app, &document, &selection)) {
32 return;
33 }
34 selection->clear();
35}
36
37void
38select_by_id(Glib::ustring ids, InkscapeApplication* app)
39{
40 SPDocument* document = nullptr;
41 Inkscape::Selection* selection = nullptr;
42 if (!get_document_and_selection(app, &document, &selection)) {
43 return;
44 }
45
46 auto tokens = Glib::Regex::split_simple("\\s*,\\s*", ids);
47 for (auto id : tokens) {
48 SPObject* object = document->getObjectById(id);
49 if (object) {
50 selection->add(object);
51 } else {
52 show_output(Glib::ustring("select_by_id: Did not find object with id: ") + id.raw());
53 }
54 }
55}
56
57void
58unselect_by_id(Glib::ustring ids, InkscapeApplication* app)
59{
60 SPDocument* document = nullptr;
61 Inkscape::Selection* selection = nullptr;
62 if (!get_document_and_selection(app, &document, &selection)) {
63 return;
64 }
65
66 auto tokens = Glib::Regex::split_simple("\\s*,\\s*", ids);
67 for (auto id : tokens) {
68 SPObject* object = document->getObjectById(id);
69 if (object) {
70 selection->remove(object);
71 } else {
72 show_output(Glib::ustring("unselect_by_id: Did not find object with id: ") + id.raw());
73 }
74 }
75}
76
77void
78select_by_class(Glib::ustring klass, InkscapeApplication* app)
79{
80 SPDocument* document = nullptr;
81 Inkscape::Selection* selection = nullptr;
82 if (!get_document_and_selection(app, &document, &selection)) {
83 return;
84 }
85
86 auto objects = document->getObjectsByClass(klass);
87 selection->add(objects.begin(), objects.end());
88}
89
90void
91select_by_element(Glib::ustring element, InkscapeApplication* app)
92{
93 SPDocument* document = nullptr;
94 Inkscape::Selection* selection = nullptr;
95 if (!get_document_and_selection(app, &document, &selection)) {
96 return;
97 }
98 auto objects = document->getObjectsByElement(element);
99 selection->add(objects.begin(), objects.end());
100}
101
102void
103select_by_selector(Glib::ustring selector, InkscapeApplication* app)
104{
105 SPDocument* document = nullptr;
106 Inkscape::Selection* selection = nullptr;
107 if (!get_document_and_selection(app, &document, &selection)) {
108 return;
109 }
110
111 auto objects = document->getObjectsBySelector(selector);
112 selection->add(objects.begin(), objects.end());
113}
114
115
116// Helper
117void
118get_all_items_recursive(std::vector<SPObject *> &objects, SPObject *object, Glib::ustring &condition)
119{
120 for (auto &o : object->childList(false)) {
121 if (is<SPItem>(o)) {
122 auto group = cast<SPGroup>(o);
123 if (condition == "layers") {
124 if (group && group->layerMode() == SPGroup::LAYER) {
125 objects.emplace_back(o);
126 continue; // Layers cannot contain layers.
127 }
128 } else if (condition == "no-layers") {
129 if (group && group->layerMode() == SPGroup::LAYER) {
130 // recurse one level
131 } else {
132 objects.emplace_back(o);
133 continue;
134 }
135 } else if (condition == "groups") {
136 if (group) {
137 objects.emplace_back(o);
138 }
139 } else if (condition == "all") {
140 objects.emplace_back(o);
141 } else {
142 // no-groups, default
143 if (!group) {
144 objects.emplace_back(o);
145 continue; // Non-groups cannot contain items.
146 }
147 }
148 get_all_items_recursive(objects, o, condition);
149 }
150 }
151}
152
153
154/*
155 * 'layers': All layers.
156 * 'groups': All groups (including layers).
157 * 'no-layers': All top level objects in all layers (matches GUI "Select All in All Layers").
158 * 'no-groups': All objects other than groups (and layers).
159 * 'all': All objects including groups and their descendents.
160 *
161 * Note: GUI "Select All" requires knowledge of selected layer, which is a desktop property.
162 */
163void
164select_all(Glib::ustring condition, InkscapeApplication* app)
165{
166 if (condition != "" && condition != "layers" && condition != "no-layers" &&
167 condition != "groups" && condition != "no-groups" && condition != "all") {
168 show_output( "select_all: allowed options are '', 'all', 'layers', 'no-layers', 'groups', and 'no-groups'" );
169 return;
170 }
171
172 SPDocument* document = nullptr;
173 Inkscape::Selection* selection = nullptr;
174 if (!get_document_and_selection(app, &document, &selection)) {
175 return;
176 }
177
178 std::vector<SPObject *> objects;
179 get_all_items_recursive(objects, document->getRoot(), condition);
180
181 selection->setList(objects);
182}
183
184void
185select_invert(Glib::ustring condition, InkscapeApplication* app)
186{
187 if (condition != "" && condition != "layers" && condition != "no-layers" &&
188 condition != "groups" && condition != "no-groups" && condition != "all") {
189 show_output( "select_all: allowed options are '', 'all', 'layers', 'no-layers', 'groups', and 'no-groups'" );
190 return;
191 }
192
193 SPDocument* document = nullptr;
194 Inkscape::Selection* selection = nullptr;
195 if (!get_document_and_selection(app, &document, &selection)) {
196 return;
197 }
198
199 // Find all objects that match condition.
200 std::vector<SPObject *> objects;
201 get_all_items_recursive(objects, document->getRoot(), condition);
202
203 // Get current selection.
204 std::vector<SPObject *> current(selection->items().begin(), selection->items().end());
205
206 // Remove current selection from object vector (using "erase remove_if idiom").
207 objects.erase(
208 std::remove_if(std::begin(objects), std::end(objects), [&current](const SPObject *x)
209 {
210 return (std::find(current.begin(), current.end(), x) != current.end());
211 }), objects.end());
212
213 // Set selection to object vector.
214 selection->setList(objects);
215}
216
217// Debug... print selected items
218void
220{
221 SPDocument* document = nullptr;
222 Inkscape::Selection* selection = nullptr;
223 if (!get_document_and_selection(app, &document, &selection)) {
224 return;
225 }
226
227 for (auto obj : selection->objects()) {
228 std::stringstream buffer;
229 buffer << *obj;
230 show_output(buffer.str(), false);
231 }
232}
233
234const Glib::ustring SECTION = NC_("Action Section", "Select");
235
236std::vector<std::vector<Glib::ustring>> raw_data_selection =
237{
238 // clang-format offs
239 {"app.select-clear", N_("Clear Selection"), SECTION, N_("Clear selection")},
240 {"app.select", N_("Select"), SECTION, N_("Select by ID (deprecated)")},
241 {"app.unselect", N_("Deselect"), SECTION, N_("Deselect by ID (deprecated)")},
242 {"app.select-by-id", N_("Select by ID"), SECTION, N_("Select by ID")},
243 {"app.unselect-by-id", N_("Deselect by ID"), SECTION, N_("Deselect by ID")},
244 {"app.select-by-class", N_("Select by Class"), SECTION, N_("Select by class")},
245 {"app.select-by-element", N_("Select by Element"), SECTION, N_("Select by SVG element (e.g. 'rect')")},
246 {"app.select-by-selector", N_("Select by Selector"), SECTION, N_("Select by CSS selector")},
247 {"app.select-all", N_("Select All Objects"), SECTION, N_("Select all; options: 'all' (every object including groups), 'layers', 'no-layers' (top level objects in layers), 'groups' (all groups including layers), 'no-groups' (all objects other than groups and layers, default)")},
248 {"app.select-list", N_("List Selection"), SECTION, N_("Print a list of objects in current selection")},
249 // clang-format on
250};
251
252void
254{
255 auto *gapp = app->gio_app();
256
257 // clang-format off
258 gapp->add_action( "select-clear", sigc::bind(sigc::ptr_fun(&select_clear), app) );
259 gapp->add_action_radio_string( "select", sigc::bind(sigc::ptr_fun(&select_by_id), app), "null"); // Backwards compatible.
260 gapp->add_action_radio_string( "unselect", sigc::bind(sigc::ptr_fun(&unselect_by_id), app), "null"); // Match select.
261 gapp->add_action_radio_string( "select-by-id", sigc::bind(sigc::ptr_fun(&select_by_id), app), "null");
262 gapp->add_action_radio_string( "unselect-by-id", sigc::bind(sigc::ptr_fun(&unselect_by_id), app), "null");
263 gapp->add_action_radio_string( "select-by-class", sigc::bind(sigc::ptr_fun(&select_by_class), app), "null");
264 gapp->add_action_radio_string( "select-by-element", sigc::bind(sigc::ptr_fun(&select_by_element), app), "null");
265 gapp->add_action_radio_string( "select-by-selector", sigc::bind(sigc::ptr_fun(&select_by_selector), app), "null");
266 gapp->add_action_radio_string( "select-all", sigc::bind(sigc::ptr_fun(&select_all), app), "null");
267 gapp->add_action_radio_string( "select-invert", sigc::bind(sigc::ptr_fun(&select_invert), app), "null");
268 gapp->add_action( "select-list", sigc::bind(sigc::ptr_fun(&select_list), app) );
269 // clang-format on
270
272}
273
274
275/*
276 Local Variables:
277 mode:c++
278 c-file-style:"stroustrup"
279 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
280 indent-tabs-mode:nil
281 fill-column:99
282 End:
283*/
284// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
void show_output(Glib::ustring const &data, bool const is_cerr)
bool get_document_and_selection(InkscapeApplication *app, SPDocument **document, Inkscape::Selection **selection)
void select_by_selector(Glib::ustring selector, InkscapeApplication *app)
void select_by_class(Glib::ustring klass, InkscapeApplication *app)
void select_by_element(Glib::ustring element, InkscapeApplication *app)
void select_list(InkscapeApplication *app)
void add_actions_selection(InkscapeApplication *app)
const Glib::ustring SECTION
void select_invert(Glib::ustring condition, InkscapeApplication *app)
std::vector< std::vector< Glib::ustring > > raw_data_selection
void select_all(Glib::ustring condition, InkscapeApplication *app)
void unselect_by_id(Glib::ustring ids, InkscapeApplication *app)
void get_all_items_recursive(std::vector< SPObject * > &objects, SPObject *object, Glib::ustring &condition)
void select_by_id(Glib::ustring ids, InkscapeApplication *app)
void select_clear(InkscapeApplication *app)
void add_data(std::vector< std::vector< Glib::ustring > > const &raw_data)
InkActionExtraData & get_action_extra_data()
Gio::Application * gio_app()
The Gio application instance, never NULL.
SPItemRange items()
Returns a range of selected SPItems.
Definition object-set.h:230
boost::enable_if< boost::is_base_of< SPObject, T >, void >::type setList(const std::vector< T * > &objs)
Selects exactly the specified objects.
Definition object-set.h:274
void clear()
Unselects all selected objects.
SPObjectRange objects()
Returns the list of selected objects.
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
void add(XML::Node *repr)
Add an XML node's SPObject to the set of selected objects.
Definition selection.h:107
void remove(XML::Node *repr)
Removes an item from the set of selected objects.
Definition selection.h:131
Typed SVG document implementation.
Definition document.h:101
std::vector< SPObject * > getObjectsByClass(Glib::ustring const &klass) const
SPRoot * getRoot()
Returns our SPRoot.
Definition document.h:200
SPObject * getObjectById(std::string const &id) const
std::vector< SPObject * > getObjectsByElement(Glib::ustring const &element, bool custom=false) const
std::vector< SPObject * > getObjectsBySelector(Glib::ustring const &selector) const
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
static char const *const current
Definition dir-util.cpp:71
SPRoot: SVG <svg> implementation.