Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
path-chemistry.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Here are handlers for modifying selections, specific to paths
4 *
5 * Authors:
6 * Lauris Kaplinski <lauris@kaplinski.com>
7 * bulia byak <buliabyak@users.sf.net>
8 * Jasper van de Gronde <th.v.d.gronde@hccnet.nl>
9 * Jon A. Cruz <jon@joncruz.org>
10 * Abhishek Sharma
11 *
12 * Copyright (C) 1999-2008 Authors
13 * Copyright (C) 2001-2002 Ximian, Inc.
14 *
15 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
16 */
17
18#include <cstring>
19#include <string>
20#include <boost/range/adaptor/reversed.hpp>
21#include <glibmm/i18n.h>
22
23#include "desktop.h"
24#include "document-undo.h"
25#include "document.h"
27#include "message-stack.h"
28#include "path-chemistry.h"
29#include "text-editing.h"
30
31#include "display/curve.h"
32
33#include "object/box3d.h"
34#include "object/object-set.h"
35#include "object/sp-flowtext.h"
36#include "object/sp-path.h"
37#include "object/sp-root.h"
38#include "object/sp-text.h"
39#include "style.h"
40
41#include "ui/icon-names.h"
42
43#include "svg/svg.h"
44
45#include "xml/repr.h"
46
49
50static void sp_degroup_list_recursive(std::vector<SPItem*> &out, SPItem *item)
51{
52 if (auto group = cast<SPGroup>(item)) {
53 for (auto &child : group->children) {
54 if (auto childitem = cast<SPItem>(&child)) {
55 sp_degroup_list_recursive(out, childitem);
56 }
57 }
58 } else {
59 out.emplace_back(item);
60 }
61}
62
64static std::vector<SPItem*> sp_degroup_list(std::vector<SPItem*> const &items)
65{
66 std::vector<SPItem*> out;
67 for (auto item : items) {
69 }
70 return out;
71}
72
73void ObjectSet::combine(bool skip_undo, bool silent)
74{
75 auto doc = document();
76 auto items_copy = std::vector<SPItem*>(items().begin(), items().end());
77
78 if (items_copy.empty()) {
79 if (desktop() && !silent) {
80 desktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to combine."));
81 }
82 return;
83 }
84
85 if (desktop()) {
86 if (!silent) {
87 desktop()->messageStack()->flash(Inkscape::IMMEDIATE_MESSAGE, _("Combining paths..."));
88 }
89 // set "busy" cursor
91 }
92
93 items_copy = sp_degroup_list(items_copy); // descend into any groups in selection
94
95 std::vector<SPItem*> to_paths;
96 for (auto item : boost::adaptors::reverse(items_copy)) {
97 if (!is<SPPath>(item) && !is<SPGroup>(item)) {
98 to_paths.emplace_back(item);
99 }
100 }
101 std::vector<Inkscape::XML::Node*> converted;
102 bool did = sp_item_list_to_curves(to_paths, items_copy, converted);
103 for (auto node : converted) {
104 items_copy.emplace_back(static_cast<SPItem*>(doc->getObjectByRepr(node)));
105 }
106
107 items_copy = sp_degroup_list(items_copy); // converting to path may have added more groups, descend again
108
109 std::sort(items_copy.begin(), items_copy.end(), [] (auto a, auto b) {
110 return sp_repr_compare_position(a->getRepr(), b->getRepr()) < 0;
111 });
112 assert(!items_copy.empty()); // cannot be empty because of check at top of function
113
114 // remember the position, id, transform and style of the topmost path, they will be assigned to the combined one
115 int position = 0;
116 char const *transform = nullptr;
117 char const *path_effect = nullptr;
118
120 SPItem *first = nullptr;
121 Inkscape::XML::Node *parent = nullptr;
122
123 if (did) {
124 clear();
125 }
126
127 for (auto item : boost::adaptors::reverse(items_copy)) {
128 auto path = cast<SPPath>(item);
129 if (!path) {
130 continue;
131 }
132
133 if (!did) {
134 clear();
135 did = true;
136 }
137
138 auto c = *path->curveForEdit();
139 if (!first) { // this is the topmost path
140 first = item;
141 parent = first->getRepr()->parent();
142 position = first->getRepr()->position();
143 transform = first->getRepr()->attribute("transform");
144 // FIXME: merge styles of combined objects instead of using the first one's style
145 path_effect = first->getRepr()->attribute("inkscape:path-effect");
146 //c->transform(item->transform);
147 curve = std::move(c);
148 } else {
149 c.transform(item->getRelativeTransform(first));
150 curve.append(std::move(c));
151
152 // reduce position only if the same parent
153 if (item->getRepr()->parent() == parent) {
154 position--;
155 }
156 // delete the object for real, so that its clones can take appropriate action
158 }
159 }
160
161 if (did) {
162 Inkscape::XML::Document *xml_doc = doc->getReprDoc();
163 Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");
164
166
167 // delete the topmost.
168 first->deleteObject(false);
169
170 // restore id, transform, path effect, and style
171 if (transform) {
172 repr->setAttribute("transform", transform);
173 }
174
175 repr->setAttribute("inkscape:path-effect", path_effect);
176
177 // set path data corresponding to new curve
178 auto dstring = sp_svg_write_path(curve.get_pathvector());
179 if (path_effect) {
180 repr->setAttribute("inkscape:original-d", dstring);
181 } else {
182 repr->setAttribute("d", dstring);
183 }
184
185 // add the new group to the parent of the topmost
186 // move to the position of the topmost, reduced by the number of deleted items
187 parent->addChildAtPos(repr, position > 0 ? position : 0);
188
189 if (!skip_undo) {
190 DocumentUndo::done(doc, _("Combine"), INKSCAPE_ICON("path-combine"));
191 }
192 set(repr);
193
195
196 } else if (desktop() && !silent) {
197 desktop()->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("<b>No path(s)</b> to combine in the selection."));
198 }
199
200 if (desktop()) {
202 }
203}
204
205void
206ObjectSet::breakApart(bool skip_undo, bool overlapping, bool silent)
207{
208 if (isEmpty()) {
209 if(desktop() && !silent)
210 desktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>path(s)</b> to break apart."));
211 return;
212 }
213 if(desktop()){
214 if (!silent) {
215 desktop()->messageStack()->flash(Inkscape::IMMEDIATE_MESSAGE, _("Breaking apart paths..."));
216 }
217 // set "busy" cursor
219 }
220
221 bool did = false;
222
223 std::vector<SPItem*> itemlist(items().begin(), items().end());
224 for (auto item : itemlist){
225
226 auto path = cast<SPPath>(item);
227 if (!path) {
228 continue;
229 }
230
231 if (!path->curveForEdit()) {
232 continue;
233 }
234 auto pathv = path->curveForEdit()->get_pathvector();
235
236 did = true;
237
239 gint pos = item->getRepr()->position();
240 char const *id = item->getRepr()->attribute("id");
241 auto const fill_rule = item->style->fill_rule.computed;
242
243 // XML Tree being used directly here while it shouldn't be...
244 gchar *style = g_strdup(item->getRepr()->attribute("style"));
245 // XML Tree being used directly here while it shouldn't be...
246 gchar *path_effect = g_strdup(item->getRepr()->attribute("inkscape:path-effect"));
247 Geom::Affine transform = path->transform;
248 // it's going to resurrect as one of the pieces, so we delete without advertisement
250 item->deleteObject(false);
251
252 auto list = Inkscape::split_non_intersecting_paths(std::move(pathv),
253 overlapping ? fill_justDont : to_livarot(fill_rule));
254
255 std::vector<Inkscape::XML::Node*> reprs;
256 for (auto const &curve : list) {
257
258 Inkscape::XML::Node *repr = parent->document()->createElement("svg:path");
259 repr->setAttribute("style", style);
260
261 repr->setAttribute("inkscape:path-effect", path_effect);
262
263 auto str = sp_svg_write_path(curve);
264 if (path_effect)
265 repr->setAttribute("inkscape:original-d", str);
266 else
267 repr->setAttribute("d", str);
268 repr->setAttributeOrRemoveIfEmpty("transform", sp_svg_transform_write(transform));
269
270 // add the new repr to the parent
271 // move to the saved position
272 parent->addChildAtPos(repr, pos);
273 SPLPEItem *lpeitem = nullptr;
274 if (path_effect && (( lpeitem = cast<SPLPEItem>(document->getObjectByRepr(repr)) )) ) {
275 lpeitem->forkPathEffectsIfNecessary(1);
276 }
277 // if it's the first one, restore id
278 if (&curve == &list.front())
279 repr->setAttribute("id", id);
280
281 reprs.push_back(repr);
282
284 }
285 setReprList(reprs);
286
287 g_free(style);
288 g_free(path_effect);
289 }
290
291 if (desktop()) {
293 }
294
295 if (did) {
296 if ( !skip_undo ) {
297 DocumentUndo::done(document(), _("Break apart"), INKSCAPE_ICON("path-break-apart"));
298 }
299 } else if (desktop() && !silent) {
300 desktop()->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("<b>No path(s)</b> to break apart in the selection."));
301 }
302}
303
304void ObjectSet::toCurves(bool skip_undo, bool clonesjustunlink)
305{
306 if (isEmpty()) {
307 if (desktop())
308 desktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to convert to path."));
309 return;
310 }
311
312 bool did = false;
313 if (desktop()) {
314 desktop()->messageStack()->flash(Inkscape::IMMEDIATE_MESSAGE, _("Converting objects to paths..."));
315 // set "busy" cursor
317 }
318 if (!clonesjustunlink) {
319 unlinkRecursive(true, false, true);
320 }
321 std::vector<SPItem*> selected(items().begin(), items().end());
322 std::vector<Inkscape::XML::Node*> to_select;
323 std::vector<SPItem*> items(selected);
324
325 did = sp_item_list_to_curves(items, selected, to_select);
326 if (did) {
327 setReprList(to_select);
328 addList(selected);
329 }
330 if (clonesjustunlink) {
331 unlinkRecursive(true, false, true);
332 }
333
334 if (desktop()) {
336 }
337 if (did && !skip_undo) {
338 DocumentUndo::done(document(), _("Object to path"), INKSCAPE_ICON("object-to-path"));
339 } else {
340 if(desktop())
341 desktop()->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("<b>No objects</b> to convert to path in the selection."));
342 return;
343 }
344}
345
348{
349
350 if (isEmpty()) {
351 return;
352 }
353 unlinkRecursive(true);
354 std::vector<SPItem*> selected(items().begin(), items().end());
355 std::vector<Inkscape::XML::Node*> to_select;
356 clear();
357 std::vector<SPItem*> items(selected);
358
359
360 sp_item_list_to_curves(items, selected, to_select, true);
361
362 setReprList(to_select);
363 addList(selected);
364}
365
366static void collect_object_items(SPObject *object, std::vector<SPItem*> &items)
367{
368 for (auto &child : object->children) {
369 if (auto child_item = cast<SPItem>(&child)) {
370 items.push_back(child_item);
371 } else {
373 }
374 }
375}
376
377bool
378sp_item_list_to_curves(const std::vector<SPItem*> &items, std::vector<SPItem*>& selected, std::vector<Inkscape::XML::Node*> &to_select, bool skip_all_lpeitems)
379{
380 bool did = false;
381 for (auto item : items){
382 g_assert(item != nullptr);
383 SPDocument *document = item->document;
384
385 auto group = cast<SPGroup>(item);
386 if ( skip_all_lpeitems &&
387 cast<SPLPEItem>(item) &&
388 !group ) // also convert objects in an SPGroup when skip_all_lpeitems is set.
389 {
390 continue;
391 }
392
393 if (auto box = cast<SPBox3D>(item)) {
394 // convert 3D box to ordinary group of paths; replace the old element in 'selected' with the new group
395 Inkscape::XML::Node *repr = box->convert_to_group()->getRepr();
396
397 if (repr) {
398 to_select.insert(to_select.begin(),repr);
399 did = true;
400 selected.erase(remove(selected.begin(), selected.end(), item), selected.end());
401 }
402
403 continue;
404 }
405 // remember id
406 char const *id = item->getRepr()->attribute("id");
407
408 auto lpeitem = cast<SPLPEItem>(item);
409 if (lpeitem && lpeitem->hasPathEffect()) {
410 lpeitem->removeAllPathEffects(true);
411 SPObject *elemref = document->getObjectById(id);
412 if (elemref != item) {
413 selected.erase(remove(selected.begin(), selected.end(), item), selected.end());
414 did = true;
415 if (elemref) {
416 //If the LPE item is a shape is converted to a path so we need to reupdate the item
417 item = cast<SPItem>(elemref);
418 selected.push_back(item);
419 } else {
420 // item deleted. Possibly because original-d value has no segments
421 continue;
422 }
423 } else if (!lpeitem->hasPathEffect()) {
424 did = true;
425 }
426 }
427
428 if (is<SPPath>(item)) {
429 // remove connector attributes
430 if (item->getAttribute("inkscape:connector-type") != nullptr) {
431 item->removeAttribute("inkscape:connection-start");
432 item->removeAttribute("inkscape:connection-start-point");
433 item->removeAttribute("inkscape:connection-end");
434 item->removeAttribute("inkscape:connection-end-point");
435 item->removeAttribute("inkscape:connector-type");
436 item->removeAttribute("inkscape:connector-curvature");
437 did = true;
438 }
439 continue; // already a path, and no path effect
440 }
441
442 if (group) {
443 std::vector<SPItem*> item_list;
444 // This convoluted system allows SPItem's in defs to be collected too
445 collect_object_items(group, item_list);
446 std::vector<Inkscape::XML::Node*> item_to_select;
447 std::vector<SPItem*> item_selected;
448
449 if (sp_item_list_to_curves(item_list, item_selected, item_to_select))
450 did = true;
451
452
453 continue;
454 }
455
457 if (!repr)
458 continue;
459
460 did = true;
461 selected.erase(remove(selected.begin(), selected.end(), item), selected.end());
462
463 // remember the position of the item
464 gint pos = item->getRepr()->position();
465 // remember parent
467 // remember class
468 char const *class_attr = item->getRepr()->attribute("class");
469
470 // It's going to resurrect, so we delete without notifying listeners.
471 item->deleteObject(false);
472
473 // restore id
474 repr->setAttribute("id", id);
475 // restore class
476 repr->setAttribute("class", class_attr);
477 // add the new repr to the parent
478 parent->addChildAtPos(repr, pos);
479
480 /* Buglet: We don't re-add the (new version of the) object to the selection of any other
481 * desktops where it was previously selected. */
482 to_select.insert(to_select.begin(),repr);
484 }
485
486 return did;
487}
488
489void list_text_items_recursive(SPItem *root, std::vector<SPItem *> &items)
490{
491 for (auto &child : root->children) {
492 auto item = cast<SPItem>(&child);
493 if (is<SPText>(item) || is<SPFlowtext>(item)) {
494 items.push_back(item);
495 }
496 if (is<SPGroup>(item)) {
498 }
499 }
500}
501
506{
507 std::vector<SPItem *> items;
508 doc->ensureUpToDate();
509
511 for (auto item : items) {
513 }
514
515 std::vector<SPItem *> selected; // Not used
516 std::vector<Inkscape::XML::Node *> to_select; // Not used
517
518 sp_item_list_to_curves(items, selected, to_select);
519}
520
523{
524 if (!item)
525 return nullptr;
526
528
529 if (is<SPText>(item) || is<SPFlowtext>(item)) {
530
531 // Special treatment for text: convert each glyph to separate path, then group the paths
532 auto layout = te_get_layout(item);
533
534 // Save original text for accessibility.
535 Glib::ustring original_text = sp_te_get_string_multiline(item, layout->begin(), layout->end());
536
537 SPObject *prev_parent = nullptr;
538 std::vector<std::pair<Geom::PathVector, SPStyle *>> curves;
539
540 Inkscape::Text::Layout::iterator iter = layout->begin();
541 do {
542 Inkscape::Text::Layout::iterator iter_next = iter;
543 iter_next.nextGlyph(); // iter_next is one glyph ahead from iter
544 if (iter == iter_next)
545 break;
546
547 /* This glyph's style */
548 SPObject *pos_obj = nullptr;
549 layout->getSourceOfCharacter(iter, &pos_obj);
550 if (!pos_obj) // no source for glyph, abort
551 break;
552 while (is<SPString>(pos_obj) && pos_obj->parent) {
553 pos_obj = pos_obj->parent; // SPStrings don't have style
554 }
555
556 // get path from iter to iter_next:
557 auto curve = layout->convertToCurves(iter, iter_next);
558 iter = iter_next; // shift to next glyph
559 if (curve.is_empty()) { // whitespace glyph?
560 continue;
561 }
562
563 // Create a new path for each span to group glyphs into
564 // which preserves styles such as paint-order
565 if (!prev_parent || prev_parent != pos_obj) {
566 // Record the style for the dying tspan tree (see sp_style_merge_from_dying_parent in style.cpp)
567 auto style = pos_obj->style;
568 for (auto sp = pos_obj->parent; sp && sp != item; sp = sp->parent) {
569 style->merge(sp->style);
570 }
571 curves.emplace_back(curve.get_pathvector(), style);
572 } else {
573 for (auto &path : curve.get_pathvector()) {
574 curves.back().first.push_back(path);
575 }
576 }
577
578 prev_parent = pos_obj;
579 if (iter == layout->end())
580 break;
581
582 } while (true);
583
584 if (curves.empty())
585 return nullptr;
586
587 Inkscape::XML::Node *result = curves.size() > 1 ? xml_doc->createElement("svg:g") : nullptr;
588 SPStyle *result_style = new SPStyle(item->document);
589
590 for (auto &[pathv, style] : curves) {
591 Glib::ustring glyph_style = style->writeIfDiff(item->style);
592 auto new_path = xml_doc->createElement("svg:path");
593 new_path->setAttributeOrRemoveIfEmpty("style", glyph_style);
594 new_path->setAttribute("d", sp_svg_write_path(pathv));
595 if (curves.size() == 1) {
596 result = new_path;
597 result_style->merge(style);
598 } else {
599 result->appendChild(new_path);
600 Inkscape::GC::release(new_path);
601 }
602 }
603
604 result_style->merge(item->style);
605 Glib::ustring css = result_style->writeIfDiff(item->parent ? item->parent->style : nullptr);
606 delete result_style;
607
609 result->setAttributeOrRemoveIfEmpty("style", css);
610 result->setAttributeOrRemoveIfEmpty("transform", item->getRepr()->attribute("transform"));
611
612 if (!original_text.empty()) {
613 result->setAttribute("aria-label", original_text);
614 }
615 return result;
616 }
617
619
620 if (auto shape = cast<SPShape>(item); shape && shape->curveForEdit()) {
621 curve = *shape->curveForEdit();
622 } else {
623 return nullptr;
624 }
625
626 // Prevent empty paths from being added to the document
627 // otherwise we end up with zomby markup in the SVG file
628 if(curve.is_empty()) {
629 return nullptr;
630 }
631
632 Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");
633
635
636 /* Transformation */
637 repr->setAttribute("transform", item->getRepr()->attribute("transform"));
638
639 /* Style */
640 Glib::ustring style_str =
641 item->style->writeIfDiff(item->parent ? item->parent->style : nullptr); // TODO investigate possibility
642 repr->setAttributeOrRemoveIfEmpty("style", style_str);
643
644 /* Definition */
645 repr->setAttribute("d", sp_svg_write_path(curve.get_pathvector()));
646 return repr;
647}
648
649
650void
652{
653 if (isEmpty()) {
654 if(desktop())
655 desktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>path(s)</b> to reverse."));
656 return;
657 }
658
659
660 // set "busy" cursor
661 if(desktop()){
663 desktop()->messageStack()->flash(Inkscape::IMMEDIATE_MESSAGE, _("Reversing paths..."));
664 }
665
666 bool did = false;
667
668 for (auto i = items().begin(); i != items().end(); ++i){
669
670 auto path = cast<SPPath>(*i);
671 if (!path) {
672 continue;
673 }
674
675 did = true;
676
677 auto str = sp_svg_write_path(path->curveForEdit()->get_pathvector().reversed());
678 if ( path->hasPathEffectRecursive() ) {
679 path->setAttribute("inkscape:original-d", str);
680 } else {
681 path->setAttribute("d", str);
682 }
683
684 // reverse nodetypes order (Bug #179866)
685 gchar *nodetypes = g_strdup(path->getRepr()->attribute("sodipodi:nodetypes"));
686 if ( nodetypes ) {
687 path->setAttribute("sodipodi:nodetypes", g_strreverse(nodetypes));
688 g_free(nodetypes);
689 }
690
691 path->update_patheffect(false);
692 }
693 if(desktop())
695
696 if (did) {
697 DocumentUndo::done(document(), _("Reverse path"), INKSCAPE_ICON("path-reverse"));
698 } else {
699 if(desktop())
700 desktop()->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("<b>No paths</b> to reverse in the selection."));
701 }
702}
703
704
713 Inkscape::XML::Node *dest, //
714 Inkscape::XML::Node const *src)
715{
716 static char const *const keys[] = {
717 // core
718 "id",
719
720 // clip & mask
721 "clip-path",
722 "mask",
723
724 // style
725 "style",
726 "class",
727
728 // inkscape
729 "inkscape:highlight-color",
730 "inkscape:label",
731 "inkscape:transform-center-x",
732 "inkscape:transform-center-y",
733
734 // interactivity
735 "onclick",
736 "onmouseover",
737 "onmouseout",
738 "onmousedown",
739 "onmouseup",
740 "onmousemove",
741 "onfocusin",
742 "onfocusout",
743 "onload",
744 };
745
746 for (auto *key : keys) {
747 auto *value = src->attribute(key);
748 if (value) {
749 dest->setAttribute(key, value);
750 }
751 }
752}
753
754
765 Inkscape::XML::Node *dest, //
766 Inkscape::XML::Node const *src)
767{
768 static std::set<std::string> const names{
769 // descriptive elements
770 "svg:title",
771 "svg:desc",
772 };
773
774 for (const auto *child = src->firstChild(); child != nullptr; child = child->next()) {
775 // check if this child should be copied
776 if (!(child->type() == Inkscape::XML::NodeType::COMMENT_NODE || //
777 (child->name() && names.count(child->name())))) {
778 continue;
779 }
780
781 auto dchild = child->duplicate(dest->document());
782 dest->appendChild(dchild);
783 dchild->release();
784 }
785}
786
787
805 Inkscape::XML::Node *dest, //
806 Inkscape::XML::Node const *src)
807{
809 ink_copy_generic_children(dest, src);
810}
811
812
813/*
814 Local Variables:
815 mode:c++
816 c-file-style:"stroustrup"
817 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
818 indent-tabs-mode:nil
819 fill-column:99
820 End:
821*/
822// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
FillRule to_livarot(SPWindRule fill_rule)
Definition LivarotDefs.h:96
@ fill_justDont
Definition LivarotDefs.h:72
3x3 matrix representing an affine transformation.
Definition affine.h:70
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
MessageId flash(MessageType type, char const *message)
Temporarily pushes a message onto the stack.
SPDesktop * desktop()
Returns the desktop the selection is bound to.
Definition object-set.h:390
void breakApart(bool skip_undo=false, bool overlapping=true, bool silent=false)
SPItemRange items()
Returns a range of selected SPItems.
Definition object-set.h:255
void setReprList(std::vector< XML::Node * > const &list)
Selects the objects with the same IDs as those in list.
void clear()
Unselects all selected objects.
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:326
void toLPEItems()
Converts the selected items to LPEItems if they are not already so; e.g.
bool isEmpty()
Returns true if no items are selected.
SPDocument * document()
Returns the document the selection is bound to.
Definition object-set.h:397
void toCurves(bool skip_undo=false, bool clonesjustunlink=false)
void combine(bool skip_undo=false, bool silent=false)
bool unlinkRecursive(const bool skip_undo=false, const bool force=false, const bool silent=false)
Recursively unlink any clones present in the current selection, including clones which are used to cl...
Holds a position within the glyph output of Layout.
Definition Layout-TNG.h:981
Interface for refcounted XML nodes.
Definition node.h:80
virtual Node * parent()=0
Get the parent of this node.
virtual void appendChild(Node *child)=0
Append a node as the last child of this node.
void setAttributeOrRemoveIfEmpty(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:167
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
virtual Node * firstChild()=0
Get the first child of this node.
virtual unsigned position() const =0
Get the index of this node in parent's child order.
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
virtual Document * document()=0
Get the node's associated document.
Wrapper around a Geom::PathVector object.
Definition curve.h:28
Inkscape::MessageStack * messageStack() const
Definition desktop.h:160
void clearWaitingCursor()
Definition desktop.cpp:1166
void setWaitingCursor()
Definition desktop.cpp:1155
Typed SVG document implementation.
Definition document.h:101
SPRoot * getRoot()
Returns our SPRoot.
Definition document.h:200
SPObject * getObjectById(std::string const &id) const
int ensureUpToDate(unsigned int object_modified_tag=0)
Repeatedly works on getting the document updated, since sometimes it takes more than one pass to get ...
SPObject * getObjectByRepr(Inkscape::XML::Node *repr) const
Base class for visual SVG elements.
Definition sp-item.h:109
Geom::Affine getRelativeTransform(SPObject const *obj) const
Definition sp-item.cpp:1828
bool forkPathEffectsIfNecessary(unsigned int nr_of_allowed_users=1, bool recursive=true, bool force=false)
Check all effects in the stack if they are used by other items, and fork them if so.
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
void removeAttribute(char const *key)
SPDocument * document
Definition sp-object.h:188
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Definition sp-object.h:248
SPObject * parent
Definition sp-object.h:189
void deleteObject(bool propagate, bool propagate_descendants)
Deletes an object, unparenting it from its parent.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
char const * getAttribute(char const *name) const
An SVG style object.
Definition style.h:45
void merge(SPStyle const *parent)
Combine style and parent style specifications into a single style specification that preserves (as mu...
Definition style.cpp:843
Glib::ustring writeIfDiff(SPStyle const *base) const
Get CSS string for set properties which are different from the given base style.
Definition style.cpp:799
T< SPAttr::FILL_RULE, SPIEnum< SPWindRule > > fill_rule
fill-rule: 0 nonzero, 1 evenodd
Definition style.h:244
RootCluster root
std::shared_ptr< Css const > css
Css & result
double c[8][4]
Editable view implementation.
static char const *const parent
Definition dir-util.cpp:70
TODO: insert short description here.
unsigned int guint32
Macro for icon names used in Inkscape.
SPItem * item
Inkscape::XML::Node * node
Geom::Point end
Raw stack of active status messages.
static R & release(R &r)
Decrements the reference count of a anchored object.
@ COMMENT_NODE
Comment node, e.g. <!– some comment –>.
void copy_object_properties(XML::Node *dest, XML::Node const *src)
Copy generic object properties, like:
std::vector< Geom::PathVector > split_non_intersecting_paths(Geom::PathVector &&paths, FillRule fill_rule)
Split a pathvector into its connected components when filled using the given fill rule.
@ ERROR_MESSAGE
Definition message.h:29
@ IMMEDIATE_MESSAGE
Definition message.h:27
@ WARNING_MESSAGE
Definition message.h:28
void convert_text_to_curves(SPDocument *)
Convert all text in the document to path, in-place.
static cairo_user_data_key_t key
void list_text_items_recursive(SPItem *root, std::vector< SPItem * > &items)
static void ink_copy_generic_attributes(Inkscape::XML::Node *dest, Inkscape::XML::Node const *src)
Copy generic attributes, like those from the "Object Properties" dialog, but also style and transform...
static std::vector< SPItem * > sp_degroup_list(std::vector< SPItem * > const &items)
Replace all groups in the list with their member objects, recursively.
static void collect_object_items(SPObject *object, std::vector< SPItem * > &items)
static void sp_degroup_list_recursive(std::vector< SPItem * > &out, SPItem *item)
Inkscape::XML::Node * sp_selected_item_to_curved_repr(SPItem *item, guint32)
bool sp_item_list_to_curves(const std::vector< SPItem * > &items, std::vector< SPItem * > &selected, std::vector< Inkscape::XML::Node * > &to_select, bool skip_all_lpeitems)
static void ink_copy_generic_children(Inkscape::XML::Node *dest, Inkscape::XML::Node const *src)
Copy generic child elements, like those from the "Object Properties" dialog (title and description) b...
Ocnode * child[8]
Definition quantize.cpp:33
C facade to Inkscape::XML::Node.
GList * items
void remove(std::vector< T > &vec, T const &val)
Definition sanitize.cpp:94
TODO: insert short description here.
SPRoot: SVG <svg> implementation.
Interface for XML documents.
Definition document.h:43
virtual Node * createElement(char const *name)=0
Definition curve.h:24
SPStyle - a style object for SPItem objects.
std::string sp_svg_transform_write(Geom::Affine const &transform)
static void sp_svg_write_path(Inkscape::SVG::PathString &str, Geom::Path const &p, bool normalize=false)
Definition svg-path.cpp:109
void te_update_layout_now_recursive(SPItem *item)
Glib::ustring sp_te_get_string_multiline(SPItem const *text)
Gets a text-only representation of the given text or flowroot object, replacing line break elements w...
Inkscape::Text::Layout const * te_get_layout(SPItem const *item)