Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
sp-item-group.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * SVG <g> implementation
4 *
5 * Authors:
6 * Lauris Kaplinski <lauris@kaplinski.com>
7 * bulia byak <buliabyak@users.sf.net>
8 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
9 * Jon A. Cruz <jon@joncruz.org>
10 * Abhishek Sharma
11 *
12 * Copyright (C) 1999-2006 authors
13 * Copyright (C) 2000-2001 Ximian, Inc.
14 *
15 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
16 */
17
18#include <cstring>
19#include <glibmm/i18n.h>
20#include <string>
21
22#include "attributes.h"
23#include "document-undo.h"
24#include "document.h"
25#include "enums.h"
26#include "preferences.h"
27#include "style.h"
28
29#include "box3d.h"
30#include "object-set.h"
31#include "sp-clippath.h"
32#include "sp-defs.h"
33#include "sp-desc.h"
34#include "sp-flowtext.h"
35#include "sp-mask.h"
36#include "sp-offset.h"
37#include "sp-root.h"
38#include "sp-switch.h"
39#include "sp-textpath.h"
40#include "sp-title.h"
41#include "sp-use.h"
42
43#include "display/curve.h"
45#include "live_effects/effect.h"
49#include "svg/svg.h"
50#include "xml/repr.h"
51
53
54static void sp_group_perform_patheffect(SPGroup *group, SPGroup *top_group, Inkscape::LivePathEffect::Effect *lpe, bool write);
55
57 _insert_bottom(false),
58 _layer_mode(SPGroup::GROUP)
59{
60}
61
62SPGroup::~SPGroup() = default;
63
69
71 if (this->_layer_mode == SPGroup::LAYER) {
72 this->document->removeResource("layer", this);
73 }
74
76}
77
80
81 SPObject *last_child = this->lastChild();
82 if (last_child && last_child->getRepr() == child) {
83 // optimization for the common special case where the child is being added at the end
84 auto item = cast<SPItem>(last_child);
85 if ( item ) {
86 /* TODO: this should be moved into SPItem somehow */
87 for (auto &v : views) {
88 auto ac = item->invoke_show(v.drawingitem->drawing(), v.key, v.flags);
89 if (ac) {
90 v.drawingitem->appendChild(ac);
91 }
92 }
93 }
94 } else { // general case
95 auto item = cast<SPItem>(get_child_by_repr(child));
96 if ( item ) {
97 /* TODO: this should be moved into SPItem somehow */
98 unsigned position = item->pos_in_parent();
99
100 for (auto &v : views) {
101 auto ac = item->invoke_show (v.drawingitem->drawing(), v.key, v.flags);
102 if (ac) {
103 v.drawingitem->prependChild(ac);
104 ac->setZOrder(position);
105 }
106 }
107 }
108 }
109 this->requestModified(SP_OBJECT_MODIFIED_FLAG);
110}
111
112/* fixme: hide (Lauris) */
113
116 if (this->hasPathEffectRecursive()) {
117 sp_lpe_item_update_patheffect(this, true, true);
118 this->requestModified(SP_OBJECT_MODIFIED_FLAG);
119 }
120}
121
123{
124 SPLPEItem::order_changed(child, old_ref, new_ref);
125
126 auto item = cast<SPItem>(get_child_by_repr(child));
127 if ( item ) {
128 /* TODO: this should be moved into SPItem somehow */
129 unsigned position = item->pos_in_parent();
130 for (auto &v : item->views) {
131 v.drawingitem->setZOrder(position);
132 }
133 }
134
135 this->requestModified(SP_OBJECT_MODIFIED_FLAG);
136}
137
138void SPGroup::update(SPCtx *ctx, unsigned int flags) {
139 // std::cout << "SPGroup::update(): " << (getId()?getId():"null") << std::endl;
140 SPItemCtx *ictx, cctx;
141
142 ictx = (SPItemCtx *) ctx;
143 cctx = *ictx;
144
145 unsigned childflags = flags;
146
147 if (flags & SP_OBJECT_MODIFIED_FLAG) {
148 childflags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
149 }
150 childflags &= SP_OBJECT_MODIFIED_CASCADE;
151 std::vector<SPObject*> l=this->childList(true, SPObject::ActionUpdate);
152 for(auto child : l){
153 if (childflags || (child->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
154 auto item = cast<SPItem>(child);
155 if (item) {
156 cctx.i2doc = item->transform * ictx->i2doc;
157 cctx.i2vp = item->transform * ictx->i2vp;
158 child->updateDisplay((SPCtx *)&cctx, childflags);
159 } else {
160 child->updateDisplay(ctx, childflags);
161 }
162 }
163
165 }
166
167 // For a group, we need to update ourselves *after* updating children.
168 // this is because the group might contain shapes such as rect or ellipse,
169 // which recompute their equivalent path (a.k.a curve) in the update callback,
170 // and this is in turn used when computing bbox.
171 SPLPEItem::update(ctx, flags);
172
173 if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
174 for (auto &v : views) {
175 auto group = cast<Inkscape::DrawingGroup>(v.drawingitem.get());
176 if (parent) {
178 }
179 group->setStyle(style, context_style);
180 }
181 }
182}
183
184void SPGroup::modified(guint flags) {
185 //std::cout << "SPGroup::modified(): " << (getId()?getId():"null") << std::endl;
186 SPLPEItem::modified(flags);
187 if (flags & SP_OBJECT_MODIFIED_FLAG) {
188 flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
189 }
190
191 flags &= SP_OBJECT_MODIFIED_CASCADE;
192
193 if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
194 for (auto &v : views) {
195 auto group = cast<Inkscape::DrawingGroup>(v.drawingitem.get());
196 group->setStyle(this->style);
197 }
198 }
199
200 std::vector<SPObject*> l=this->childList(true);
201 for(auto child : l){
202 if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
203 child->emitModified(flags);
204 }
205
207 }
208}
209
211 if (flags & SP_OBJECT_WRITE_BUILD) {
212 std::vector<Inkscape::XML::Node *> l;
213
214 if (!repr) {
215 if (is<SPSwitch>(this)) {
216 repr = xml_doc->createElement("svg:switch");
217 } else {
218 repr = xml_doc->createElement("svg:g");
219 }
220 }
221
222 for (auto& child: children) {
223 if (!is<SPTitle>(&child) && !is<SPDesc>(&child)) {
224 Inkscape::XML::Node *crepr = child.updateRepr(xml_doc, nullptr, flags);
225
226 if (crepr) {
227 l.push_back(crepr);
228 }
229 }
230 }
231 for (auto i=l.rbegin();i!=l.rend();++i) {
232 repr->addChild(*i, nullptr);
234 }
235 } else {
236 for (auto& child: children) {
237 if (!is<SPTitle>(&child) && !is<SPDesc>(&child)) {
238 child.updateRepr(flags);
239 }
240 }
241 }
242
243 if ( flags & SP_OBJECT_WRITE_EXT ) {
244 const char *value;
245 if ( _layer_mode == SPGroup::LAYER ) {
246 value = "layer";
247 } else if ( _layer_mode == SPGroup::MASK_HELPER ) {
248 value = "maskhelper";
249 } else if ( flags & SP_OBJECT_WRITE_ALL ) {
250 value = "group";
251 } else {
252 value = nullptr;
253 }
254
255 repr->setAttribute("inkscape:groupmode", value);
256 }
257
258 SPLPEItem::write(xml_doc, repr, flags);
259
260 return repr;
261}
262
264{
266
267 // TODO CPPIFY: replace this const_cast later
268 std::vector<SPObject*> l = const_cast<SPGroup*>(this)->childList(false, SPObject::ActionBBox);
269 for(auto o : l){
270 auto item = cast<SPItem>(o);
271 if (item && !item->isHidden()) {
273 bbox |= item->bounds(bboxtype, ct);
274 }
275 }
276
277 return bbox;
278}
279
281 for(auto& child: children){
282 SPObject *o = &child;
283 auto item = cast<SPItem>(o);
284 if (item) {
285 item->invoke_print(ctx);
286 }
287 }
288}
289
290const char *SPGroup::typeName() const {
291 switch (_layer_mode) {
292 case SPGroup::LAYER:
293 return "layer";
295 case SPGroup::GROUP:
296 default:
297 return "group";
298 }
299}
300
301const char *SPGroup::displayName() const {
302 switch (_layer_mode) {
303 case SPGroup::LAYER:
304 return _("Layer");
306 return _("Mask Helper");
307 case SPGroup::GROUP:
308 default:
309 return C_("Noun", "Group");
310 }
311}
312
313gchar *SPGroup::description() const {
314 gint len = this->getItemCount();
315 return g_strdup_printf(
316 ngettext("of <b>%d</b> object", "of <b>%d</b> objects", len), len);
317}
318
319void SPGroup::set(SPAttr key, gchar const* value) {
320 switch (key) {
322 if ( value && !strcmp(value, "layer") ) {
324 } else if ( value && !strcmp(value, "maskhelper") ) {
326 } else {
328 }
329 break;
330
331 default:
332 SPLPEItem::set(key, value);
333 break;
334 }
335}
336
337Inkscape::DrawingItem *SPGroup::show (Inkscape::Drawing &drawing, unsigned int key, unsigned int flags) {
338 // std::cout << "SPGroup::set_visible(true): " << (getId()?getId():"null") << std::endl;
340
341 ai = new Inkscape::DrawingGroup(drawing);
343 if( this->parent ) {
344 this->context_style = this->parent->context_style;
345 }
346 ai->setStyle(this->style, this->context_style);
347
348 this->_showChildren(drawing, ai, key, flags);
349 return ai;
350}
351
352void SPGroup::hide (unsigned int key) {
353 std::vector<SPObject*> l=this->childList(false, SPObject::ActionShow);
354 for(auto o : l){
355 auto item = cast<SPItem>(o);
356 if (item) {
358 }
359 }
360
361// SPLPEItem::onHide(key);
362}
363
364std::vector<SPItem*> SPGroup::item_list()
365{
366 std::vector<SPItem *> ret;
367 for (auto& child: children) {
368 if (auto item = cast<SPItem>(const_cast<SPObject *>(&child))) {
369 ret.push_back(item);
370 }
371 }
372 return ret;
373}
374
375void SPGroup::snappoints(std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) const {
376 for (auto& o: children)
377 {
378 SPItem const *item = cast<SPItem>(&o);
379 if (item) {
380 item->getSnappoints(p, snapprefs);
381 }
382 }
383}
384
399static void _ungroup_compensate_source_transform(SPItem *item, SPItem const *const expected_source,
400 Geom::Affine const &source_transform)
401{
402 if (!item || item->cloned) {
403 return;
404 }
405
406 SPItem *source = nullptr;
407 SPText *item_text = nullptr;
408 SPOffset *item_offset = nullptr;
409 SPUse *item_use = nullptr;
410 auto lpeitemclone = cast<SPLPEItem>(item);
411
412 bool override = false;
413 if ((item_offset = cast<SPOffset>(item))) {
414 source = sp_offset_get_source(item_offset);
415 } else if ((item_text = cast<SPText>(item))) {
416 source = item_text->get_first_shape_dependency();
417 } else if (auto textpath = cast<SPTextPath>(item)) {
418 item_text = cast<SPText>(textpath->parent);
419 if (!item_text)
420 return;
421 item = item_text;
422 source = sp_textpath_get_path_item(textpath);
423 } else if ((item_use = cast<SPUse>(item))) {
424 source = item_use->get_original();
425 } else if (lpeitemclone && lpeitemclone->hasPathEffectOfType(Inkscape::LivePathEffect::CLONE_ORIGINAL)) {
426 override = true;
427 }
428
429 if (source != expected_source && !override) {
430 return;
431 }
432
433 // FIXME: constructing a transform that would fully preserve the appearance of a
434 // textpath if it is ungrouped with its path seems to be impossible in general
435 // case. E.g. if the group was squeezed, to keep the ungrouped textpath squeezed
436 // as well, we'll need to relink it to some "virtual" path which is inversely
437 // stretched relative to the actual path, and then squeeze the textpath back so it
438 // would both fit the actual path _and_ be squeezed as before. It's a bummer.
439
440 auto const adv = item->transform.inverse() * source_transform * item->transform;
441 double const scale = source_transform.descrim();
442
443 if (item_text) {
444 item_text->_adjustFontsizeRecursive(item_text, scale);
445 } else if (item_offset) {
446 item_offset->rad *= scale;
447 } else if (item_use) {
448 item->transform = Geom::Translate(item_use->x.computed, item_use->y.computed) * item->transform;
449 item_use->x = 0;
450 item_use->y = 0;
451 }
452
453 if (!item_use) {
458 }
459
460 item->transform = source_transform.inverse() * item->transform;
461 item->updateRepr();
462}
463
465{
466 // copy the list because the original may get invalidated
467 auto hrefListCopy = parent->hrefList;
468
469 for (auto *cobj : hrefListCopy) {
470 _ungroup_compensate_source_transform(cast<SPItem>(cobj), parent, g);
471 }
472}
473
474/*
475 * Get bbox of clip/mask if is a rect to fix PDF import issues
476 */
478 auto shape = cast<SPShape>(object);
479 Geom::OptRect bbox_clip = Geom::OptRect();
480 if (shape) {
481 auto curve = shape->curve();
482 if (curve) {
483 Geom::PathVector pv = curve->get_pathvector();
484 std::vector<Geom::Point> nodes = pv.nodes();
485 if (pv.size() == 1 && nodes.size() == 4) {
486 if (Geom::are_near(nodes[0][Geom::X],nodes[3][Geom::X]) &&
487 Geom::are_near(nodes[1][Geom::X],nodes[2][Geom::X]) &&
488 Geom::are_near(nodes[0][Geom::Y],nodes[1][Geom::Y]) &&
489 Geom::are_near(nodes[2][Geom::Y],nodes[3][Geom::Y]))
490 {
491 bbox_clip = shape->visualBounds();
492 bbox_clip->expandBy(1);
493 }
494 }
495 }
496 }
497 return bbox_clip;
498}
499
500/*
501 * Get clip and item has the same path, PDF fix
502 */
504 auto shape = cast<SPShape>(item);
505 auto shape_clip = cast<SPShape>(clip);
506 bool equal = false;
507 if (shape && shape_clip) {
508 auto filter = shape->style->getFilter();
509 auto stroke = shape->style->getFillOrStroke(false);
510 if (!filter && (!stroke || stroke->isNone())) {
511 auto curve = shape->curve();
512 auto curve_clip = shape_clip->curve();
513 if (curve && curve_clip) {
514 equal = curve->is_similar(curve_clip, 0.01);
515 }
516 }
517 }
518 return equal;
519}
520
527static void unlink_clones_of(SPGroup *group)
528{
529 // copy the list because the original may get invalidated
530 auto hrefListCopy = group->hrefList;
531
532 for (auto *cobj : hrefListCopy) {
533 if (auto clone = cast<SPUse>(cobj)) {
534 clone->unlink();
535 }
536 }
537}
538
539void
540sp_item_group_ungroup (SPGroup *group, std::vector<SPItem*> &children)
541{
542 g_return_if_fail (group != nullptr);
543
544 SPDocument *doc = group->document;
545 SPRoot *root = doc->getRoot();
546 SPObject *defs = root->defs;
547 auto const prefs = Inkscape::Preferences::get();
548 // TODO handle better ungrouping
549 // now is used in clipmask LPE on remove
550 prefs->setBool("/options/onungroup", true);
551
552 Inkscape::XML::Node *grepr = group->getRepr();
553
554 g_return_if_fail (!strcmp (grepr->name(), "svg:g")
555 || !strcmp (grepr->name(), "svg:a")
556 || !strcmp (grepr->name(), "svg:switch")
557 || !strcmp (grepr->name(), "svg:svg"));
558
559 // this converts the gradient/pattern fill/stroke on the group, if any, to userSpaceOnUse
561
562 auto pitem = cast<SPItem>(group->parent);
563 g_assert(pitem);
564 Inkscape::XML::Node *prepr = pitem->getRepr();
565
566 {
567 auto box = cast<SPBox3D>(group);
568 if (box) {
569 group = box->convert_to_group();
570 }
571 }
572
573 group->removeAllPathEffects(false);
574 bool maskonungroup = prefs->getBool("/options/maskobject/maskonungroup", true);
575 bool topmost = prefs->getBool("/options/maskobject/topmost", true);
576 int grouping = prefs->getInt("/options/maskobject/grouping", PREFS_MASKOBJECT_GROUPING_NONE);
577 SPObject *clip = nullptr;
578 SPObject *mask = nullptr;
579 if (maskonungroup) {
580 Inkscape::ObjectSet tmp_clip_set(doc);
581 tmp_clip_set.add(group);
582 Inkscape::ObjectSet tmp_mask_set(doc);
583 tmp_mask_set.add(group);
584 auto *clip_obj = group->getClipObject();
585 auto *mask_obj = group->getMaskObject();
586 prefs->setBool("/options/maskobject/topmost", true);
587 prefs->setInt("/options/maskobject/grouping", PREFS_MASKOBJECT_GROUPING_NONE);
588 if (clip_obj) {
589 tmp_clip_set.unsetMask(true, false, true);
590 tmp_clip_set.remove(group);
591 clip = tmp_clip_set.singleItem();
592 // TODO: handle multiple children of a <clipPath>. Currently,
593 // ObjectSet::setMask() can only set a single item as clip.
594 }
595 if (mask_obj) {
596 tmp_mask_set.unsetMask(false, false, true);
597 tmp_mask_set.remove(group);
598 tmp_mask_set.group();
599 mask = tmp_mask_set.singleItem();
600 }
601 }
602 /* Step 1 - generate lists of children objects */
603 std::vector<Inkscape::XML::Node *> items;
604 std::vector<Inkscape::XML::Node *> objects;
605 Geom::Affine const g = i2anc_affine(group, group->parent);
606
607 // Unlink clones of group before modifying any transforms
608 unlink_clones_of(group);
609
610 if (!g.isIdentity()) {
611 for (auto &child : group->children) {
612 if (auto citem = cast<SPItem>(&child)) {
613 auto lpeitem = cast<SPLPEItem>(citem);
614 if (lpeitem) {
615 for (auto lpe :
616 lpeitem->getPathEffectsOfType(Inkscape::LivePathEffect::EffectType::CLONE_ORIGINAL)) {
617 auto clonelpe = dynamic_cast<Inkscape::LivePathEffect::LPECloneOriginal*>(lpe);
618 if (clonelpe) {
619 SPObject *linked = clonelpe->linkeditem.getObject();
620 if (linked) {
621 bool breakparent = false;
622 for (auto &child2 : group->children) {
623 if (cast<SPItem>(&child2) == linked) {
624 _ungroup_compensate_source_transform(citem, cast<SPItem>(linked), g);
625 breakparent = true;
626 break;
627 }
628 }
629 if (breakparent) {
630 break;
631 }
632 }
633 }
634 }
635 }
637 }
638 }
639 }
640
641 for (auto& child: group->children) {
642 auto citem = cast<SPItem>(&child);
643 if (citem) {
644 /* Merging of style */
645 // this converts the gradient/pattern fill/stroke, if any, to userSpaceOnUse; we need to do
646 // it here _before_ the new transform is set, so as to use the pre-transform bbox
647 citem->adjust_paint_recursive(Geom::identity(), Geom::identity());
648
649 child.style->merge( group->style );
650 /*
651 * fixme: We currently make no allowance for the case where child is cloned
652 * and the group has any style settings.
653 *
654 * (This should never occur with documents created solely with the current
655 * version of inkscape without using the XML editor: we usually apply group
656 * style changes to children rather than to the group itself.)
657 *
658 * If the group has no style settings, then style->merge() should be a no-op. Otherwise
659 * (i.e. if we change the child's style to compensate for its parent going away)
660 * then those changes will typically be reflected in any clones of child,
661 * whereas we'd prefer for Ungroup not to affect the visual appearance.
662 *
663 * The only way of preserving styling appearance in general is for child to
664 * be put into a new group -- a somewhat surprising response to an Ungroup
665 * command. We could add a new groupmode:transparent that would mostly
666 * hide the existence of such groups from the user (i.e. editing behaves as
667 * if the transparent group's children weren't in a group), though that's
668 * extra complication & maintenance burden and this case is rare.
669 */
670
671 // Merging transform
672 citem->transform *= g;
673
674 child.updateRepr();
675
676 Inkscape::XML::Node *nrepr = child.getRepr()->duplicate(prepr->document());
677 items.push_back(nrepr);
678
679 } else {
680 Inkscape::XML::Node *nrepr = child.getRepr()->duplicate(prepr->document());
681 objects.push_back(nrepr);
682 }
683 }
684
685 /* Step 2 - clear group */
686 // remember the position of the group
687 auto insert_after = group->getRepr()->prev();
688
689 // the group is leaving forever, no heir, clones should take note; its children however are going to reemerge
690 group->deleteObject(true, false);
691
692 /* Step 3 - add nonitems */
693 if (!objects.empty()) {
694 Inkscape::XML::Node *last_def = defs->getRepr()->lastChild();
695 for (auto i=objects.rbegin();i!=objects.rend();++i) {
696 Inkscape::XML::Node *repr = *i;
697 if (!sp_repr_is_meta_element(repr)) {
698 defs->getRepr()->addChild(repr, last_def);
699 }
701 }
702 }
703 Inkscape::ObjectSet result_mask_set(doc);
704 Inkscape::ObjectSet result_clip_set(doc);
705 Geom::OptRect bbox_clip = Geom::OptRect();
706 if (clip) { // if !maskonungroup is always null
707 bbox_clip = bbox_on_rect_clip(clip);
708 }
709 /* Step 4 - add items */
710 std::vector<SPLPEItem *> lpeitems;
711 for (auto *repr : items) {
712 // add item
713 prepr->addChild(repr, insert_after);
714 insert_after = repr;
715
716 // fill in the children list if non-null
717 SPItem *item = static_cast<SPItem *>(doc->getObjectByRepr(repr));
718 auto lpeitem = cast<SPLPEItem>(item);
719 if (item) {
720 if (lpeitem) {
721 lpeitems.push_back(lpeitem);
722 sp_lpe_item_enable_path_effects(lpeitem, false);
723 children.insert(children.begin(), item);
724 } else {
725 item->doWriteTransform(item->transform, nullptr, false);
726 children.insert(children.begin(), item);
727 item->requestModified(SP_OBJECT_MODIFIED_FLAG);
728 }
729 } else {
730 g_assert_not_reached();
731 }
732
734 if (!lpeitem && clip && item) { // if !maskonungroup is always null
735 Geom::OptRect bbox_item = item->visualBounds();
736 if (bbox_item && !equal_clip(item, clip)) {
737 if (!bbox_clip || !(*bbox_clip).contains(*bbox_item)) {
738 result_clip_set.add(item);
739 }
740 }
741 }
742 if (mask && item) {
743 result_mask_set.add(item);
744 }
745 }
746
747 if (mask) {
748 result_mask_set.add(mask);
749 result_mask_set.setMask(false, false, false);
750 mask->deleteObject(true, false);
751 }
752 for (auto lpeitem : lpeitems) {
753 sp_lpe_item_enable_path_effects(lpeitem, true);
754 lpeitem->doWriteTransform(lpeitem->transform, nullptr, false);
755 lpeitem->requestModified(SP_OBJECT_MODIFIED_FLAG);
756 if (clip && lpeitem) { // if !maskonungroup is always null
757 Geom::OptRect bbox_item = lpeitem->visualBounds();
758 if (bbox_item && !equal_clip(lpeitem, clip)) {
759 if (!bbox_clip || !(*bbox_clip).contains(*bbox_item)) {
760 result_clip_set.add(lpeitem);
761 }
762 }
763 }
764 }
765 if (clip) { // if !maskonungroup is always null
766 if (result_clip_set.size()) {
767 result_clip_set.add(clip);
768 result_clip_set.setMask(true, false, false);
769 }
770 clip->deleteObject(true, false);
771 }
772 prefs->setBool("/options/maskobject/topmost", topmost);
773 prefs->setInt("/options/maskobject/grouping", grouping);
774 prefs->setBool("/options/onungroup", false);
775}
776
777/*
778 * some API for list aspect of SPGroup
779 */
780
782{
783 SPObject *child = (ref) ? ref->getNext() : group->firstChild();
784 while ( child && strcmp(child->getRepr()->name(), name) ) {
785 child = child->getNext();
786 }
787 return child;
788}
789
791 if ( _layer_mode != mode ) {
792 if ( mode == LAYER ) {
793 this->document->addResource("layer", this);
794 } else if ( _layer_mode == LAYER ) {
795 this->document->removeResource("layer", this);
796 }
799 }
800}
801
803 std::map<unsigned int, LayerMode>::const_iterator iter;
804 iter = _display_modes.find(dkey);
805 if ( iter != _display_modes.end() ) {
806 return (*iter).second;
807 } else {
808 return GROUP;
809 }
810}
811
812void SPGroup::setInsertBottom(bool insertbottom) {
813 if ( _insert_bottom != insertbottom) {
814 _insert_bottom = insertbottom;
815 }
816}
817
819 if ( layerDisplayMode(dkey) != mode ) {
820 _display_modes[dkey] = mode;
821 _updateLayerMode(dkey);
822 }
823}
824
825void SPGroup::_updateLayerMode(unsigned int display_key) {
826 for (auto &v : views) {
827 if (!display_key || v.key == display_key) {
828 if (auto g = cast<Inkscape::DrawingGroup>(v.drawingitem.get())) {
829 g->setPickChildren(effectiveLayerMode(v.key) == SPGroup::LAYER);
830 }
831 }
832 }
833}
834
836{
837 if (hasChildren()) {
838 for (auto &o: children) {
839 if (auto item = cast<SPItem>(&o)) {
840 item->move_rel(tr);
841 }
842 }
843 }
844}
845
846// Recursively (or not) scale child items around a point
847void SPGroup::scaleChildItemsRec(Geom::Scale const &sc, Geom::Point const &p, bool noRecurse)
848{
849 if ( hasChildren() ) {
850 for (auto& o: children) {
851 if ( auto defs = cast<SPDefs>(&o) ) { // select symbols from defs, ignore clips, masks, patterns
852 for (auto& defschild: defs->children) {
853 auto defsgroup = cast<SPGroup>(&defschild);
854 if (defsgroup)
855 defsgroup->scaleChildItemsRec(sc, p, false);
856 }
857 } else if (auto item = cast<SPItem>(&o)) {
858 auto group = cast<SPGroup>(item);
859 if (group && !is<SPBox3D>(item)) {
860 /* Using recursion breaks clipping because transforms are applied
861 in coordinates for draws but nothing in defs is changed
862 instead change the transform on the entire group, and the transform
863 is applied after any references to clipping paths. However NOT using
864 recursion apparently breaks as of r13544 other parts of Inkscape
865 involved with showing/modifying units. So offer both for use
866 in different contexts.
867 */
868 if(noRecurse) {
869 // used for EMF import
870 Geom::Translate const s(p);
871 Geom::Affine final = s.inverse() * sc * s;
872 Geom::Affine tAff = item->i2dt_affine() * final;
873 item->set_i2d_affine(tAff);
874 tAff = item->transform;
875 // Eliminate common rounding error affecting EMF/WMF input.
876 // When the rounding error persists it converts the simple
877 // transform=scale() to transform=matrix().
878 if(std::abs(tAff[4]) < 1.0e-5 && std::abs(tAff[5]) < 1.0e-5){
879 tAff[4] = 0.0;
880 tAff[5] = 0.0;
881 }
882 item->doWriteTransform(tAff, nullptr, true);
883 } else {
884 // used for other import
885 SPItem *sub_item = nullptr;
886 if (item->getClipObject()) {
887 sub_item = cast<SPItem>(item->getClipObject()->firstChild());
888 }
889 if (sub_item != nullptr) {
890 sub_item->doWriteTransform(sub_item->transform*sc, nullptr, true);
891 }
892 sub_item = nullptr;
893 if (item->getMaskObject()) {
894 sub_item = cast<SPItem>(item->getMaskObject()->firstChild());
895 }
896 if (sub_item != nullptr) {
897 sub_item->doWriteTransform(sub_item->transform*sc, nullptr, true);
898 }
899 item->doWriteTransform(sc.inverse()*item->transform*sc, nullptr, true);
900 group->scaleChildItemsRec(sc, p, false);
901 }
902 } else {
903// Geom::OptRect bbox = item->desktopVisualBounds();
904// if (bbox) { // test not needed, this was causing a failure to scale <circle> and <rect> in the clipboard, see LP Bug 1365451
905 // Scale item
906 Geom::Translate const s(p);
907 Geom::Affine final = s.inverse() * sc * s;
908
909 gchar const *conn_type = nullptr;
910 auto text_item = cast<SPText>(item);
911 bool is_text_path = text_item && text_item->firstChild() && cast<SPTextPath>(text_item->firstChild());
912 if (is_text_path) {
913 text_item->optimizeTextpathText();
914 } else {
915 auto flowText = cast<SPFlowtext>(item);
916 if (flowText) {
917 flowText->optimizeScaledText();
918 } else {
919 auto box = cast<SPBox3D>(item);
920 if (box) {
921 // Force recalculation from perspective
922 box->position_set();
923 } else if (item->getAttribute("inkscape:connector-type") != nullptr
924 && (item->getAttribute("inkscape:connection-start") == nullptr
925 || item->getAttribute("inkscape:connection-end") == nullptr)) {
926 // Remove and store connector type for transform if disconnected
927 conn_type = item->getAttribute("inkscape:connector-type");
928 item->removeAttribute("inkscape:connector-type");
929 }
930 }
931 }
932
933 if (is_text_path && !item->transform.isIdentity()) {
934 // Save and reset current transform
937 // Apply scale
939 item->doWriteTransform(item->transform, nullptr, true);
940 // Scale translation and restore original transform
941 tmp[4] *= sc[0];
942 tmp[5] *= sc[1];
943 item->doWriteTransform(tmp, nullptr, true);
944 } else if (is<SPUse>(item)) {
945 // calculate the matrix we need to apply to the clone
946 // to cancel its induced transform from its original
947 Geom::Affine move = final.inverse() * item->transform * final;
948 item->doWriteTransform(move, &move, true);
949 } else {
950 item->doWriteTransform(item->transform*sc, nullptr, true);
951 }
952
953 if (conn_type != nullptr) {
954 item->setAttribute("inkscape:connector-type", conn_type);
955 }
956
957 if (item->isCenterSet() && !(final.isTranslation() || final.isIdentity())) {
958 item->scaleCenter(sc); // All coordinates have been scaled, so also the center must be scaled
959 item->updateRepr();
960 }
961// }
962 }
963 }
964 }
965 }
966}
967
969 gint len = 0;
970 for (auto& child: children) {
971 if (is<SPItem>(&child)) {
972 len++;
973 }
974 }
975
976 return len;
977}
978
979void SPGroup::_showChildren (Inkscape::Drawing &drawing, Inkscape::DrawingItem *ai, unsigned int key, unsigned int flags) {
980 Inkscape::DrawingItem *ac = nullptr;
981 std::vector<SPObject*> l=this->childList(false, SPObject::ActionShow);
982 for(auto o : l){
983 auto child = cast<SPItem>(o);
984 if (child) {
985 ac = child->invoke_show (drawing, key, flags);
986 if (ac) {
987 ai->appendChild(ac);
988 }
989 }
990 }
991}
992
993std::vector<SPItem*> SPGroup::get_expanded(std::vector<SPItem*> const &items)
994{
995 std::vector<SPItem*> result;
996
997 for (auto item : items) {
998 if (auto group = cast<SPGroup>(item)) {
999 auto sub = get_expanded(group->item_list());
1000 result.insert(result.end(), sub.begin(), sub.end());
1001 } else {
1002 result.emplace_back(item);
1003 }
1004 }
1005
1006 return result;
1007}
1008
1010#ifdef GROUP_VERBOSE
1011 g_message("sp_group_update_patheffect: %p\n", lpeitem);
1012#endif
1013 for (auto sub_item : item_list()) {
1014 if (sub_item) {
1015 // don't need lpe version < 1 (issue only reply on lower LPE on nested LPEs
1016 // this doesn't happen because it's done at very first stage
1017 // we need to be sure performed to inform lpe original bounds ok,
1018 // if not original_bbox function fail on update groups
1019 auto sub_shape = cast<SPShape>(sub_item);
1020 if (sub_shape && sub_shape->hasPathEffectRecursive()) {
1021 sub_shape->bbox_vis_cache_is_valid = false;
1022 sub_shape->bbox_geom_cache_is_valid = false;
1023 }
1024 auto lpe_item = cast<SPLPEItem>(sub_item);
1025 if (lpe_item) {
1026 lpe_item->update_patheffect(write);
1027 }
1028 }
1029 }
1030
1031 // avoid update lpe in each selection
1032 // must be set also to non effect items (satellites or parents)
1033 lpe_initialized = true;
1034 if (hasPathEffect() && pathEffectsEnabled()) {
1035 if (!document->getRoot()->inkscape.getVersion().isInsideRangeInclusive({0, 1}, {0, 92})) {
1037 }
1039 for (auto &lperef : path_effect_list) {
1040 LivePathEffectObject *lpeobj = lperef->lpeobject;
1041 if (lpeobj) {
1043 if (lpe && lpe->isVisible()) {
1044 lpeobj->get_lpe()->doBeforeEffect_impl(this);
1045 sp_group_perform_patheffect(this, this, lpe, write);
1046 lpeobj->get_lpe()->doAfterEffect_impl(this, nullptr);
1047 }
1048 }
1049 }
1050 }
1051}
1052
1053static void
1055{
1056 std::vector<SPItem*> const item_list = group->item_list();
1057 for (auto sub_item : item_list) {
1058 auto sub_group = cast<SPGroup>(sub_item);
1059 if (sub_group) {
1060 sp_group_perform_patheffect(sub_group, top_group, lpe, write);
1061 } else {
1062 auto sub_shape = cast<SPShape>(sub_item);
1063 //auto sub_path = cast<SPPath>(sub_item);
1064 auto clipmaskto = sub_item;
1065 if (clipmaskto) {
1066 top_group->applyToClipPath(clipmaskto, lpe);
1067 top_group->applyToMask(clipmaskto, lpe);
1068 }
1069 if (sub_shape) {
1070 bool success = false;
1071 // only run LPEs when the shape has a curve defined
1072 if (sub_shape->curve()) {
1073 auto c = *sub_shape->curve();
1074 lpe->pathvector_before_effect = c.get_pathvector();
1075 c.transform(i2anc_affine(sub_shape, top_group));
1076 sub_shape->setCurveInsync(&c);
1077 success = top_group->performOnePathEffect(&c, sub_shape, lpe);
1078 c.transform(i2anc_affine(sub_shape, top_group).inverse());
1079 Inkscape::XML::Node *repr = sub_item->getRepr();
1080 if (success) {
1081 sub_shape->setCurveInsync(&c);
1082 if (lpe->lpeversion.param_getSVGValue() != "0") { // we are on 1 or up
1083 sub_shape->bbox_vis_cache_is_valid = false;
1084 sub_shape->bbox_geom_cache_is_valid = false;
1085 }
1086 lpe->pathvector_after_effect = c.get_pathvector();
1087 if (write) {
1089#ifdef GROUP_VERBOSE
1090 g_message("sp_group_perform_patheffect writes 'd' attribute");
1091#endif
1092 }
1093 }
1094 }
1095 }
1096 }
1097 }
1098 auto clipmaskto = group;
1099 if (clipmaskto) {
1100 top_group->applyToClipPath(clipmaskto, lpe);
1101 top_group->applyToMask(clipmaskto, lpe);
1102 }
1103}
1104
1105// This is really a UI feature and doesn't belong here.
1106
1107// A list of default highlight colours to use when one isn't set.
1108std::vector<guint32> default_highlights;
1109
1114 // Parent must not be a layer (root, or similar) and this group must also be a layer
1116 char const * oid = defaultLabel();
1117 if (oid && *oid) {
1118 // Color based on the last few bits of the label or object id.
1119 return Inkscape::Colors::Color(default_highlights[oid[(strlen(oid) - 1)] % default_highlights.size()]);
1120 }
1121 }
1122 return SPItem::highlight_color();
1123}
1124
1125void set_default_highlight_colors(std::vector<guint32> colors) {
1126 std::swap(default_highlights, colors);
1127}
1128
1130{
1131 for (auto item : item_list()) {
1133 }
1134 removeAttribute("transform");
1135}
1136
1137/*
1138 Local Variables:
1139 mode:c++
1140 c-file-style:"stroustrup"
1141 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1142 indent-tabs-mode:nil
1143 fill-column:99
1144 End:
1145*/
1146// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
double scale
Definition aa.cpp:228
Lookup dictionary for attributes/properties.
SPAttr
Definition attributes.h:27
@ INKSCAPE_GROUPMODE
3x3 matrix representing an affine transformation.
Definition affine.h:70
Coord descrim() const
Calculate the descriminant.
Definition affine.cpp:434
bool isIdentity(Coord eps=EPSILON) const
Check whether this matrix is an identity matrix.
Definition affine.cpp:109
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
bool contains(CRect const &r) const
Check whether the rectangle includes all points in the given rectangle.
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Sequence of subpaths.
Definition pathvector.h:122
size_type size() const
Get the number of paths in the vector.
Definition pathvector.h:147
std::vector< Point > nodes() const
Two-dimensional point that doubles as a vector.
Definition point.h:66
Scaling from the origin.
Definition transforms.h:150
Scale inverse() const
Definition transforms.h:172
Translation by a vector.
Definition transforms.h:115
Translate inverse() const
Get the inverse translation.
Definition transforms.h:133
void setPickChildren(bool)
Set whether the group returns children from pick calls.
SVG drawing item for display.
void appendChild(DrawingItem *item)
virtual void setStyle(SPStyle const *style, SPStyle const *context_style=nullptr)
Process information related to the new style.
void doBeforeEffect_impl(SPLPEItem const *lpeitem)
Definition effect.cpp:1550
void doAfterEffect_impl(SPLPEItem const *lpeitem, SPCurve *curve)
Definition effect.cpp:1483
Geom::PathVector pathvector_before_effect
Definition effect.h:174
Geom::PathVector pathvector_after_effect
Definition effect.h:175
Glib::ustring param_getSVGValue() const override
Definition hidden.cpp:53
Inkscape::XML::Node * group(bool is_anchor=false)
bool remove(SPObject *object)
Removes an item from the set of selected objects.
void unsetMask(const bool apply_clip_path, const bool delete_helper_group, bool remove_original)
bool add(SPObject *object, bool nosignal=false)
Add an SPObject to the set of selected objects.
int size()
Returns size of the selection.
SPItem * singleItem()
Returns a single selected item.
void setMask(bool apply_clip_path, bool apply_to_layer, bool remove_original)
Creates a mask or clipPath from selection.
static Preferences * get()
Access the singleton Preferences object.
Storing of snapping preferences.
Interface for refcounted XML nodes.
Definition node.h:80
virtual Node * prev()=0
virtual void addChild(Node *child, Node *after)=0
Insert another node as a child of this node.
virtual char const * name() const =0
Get the name of the element node.
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
virtual Node * duplicate(Document *doc) const =0
Create a duplicate of this node.
virtual Document * document()=0
Get the node's associated document.
virtual Node * lastChild()=0
Get the last child of this node.
Inkscape::LivePathEffect::Effect * get_lpe()
Definition lpeobject.h:51
SVGLength y
SVGLength x
Typed SVG document implementation.
Definition document.h:103
bool removeResource(char const *key, SPObject *object)
SPRoot * getRoot()
Returns our SPRoot.
Definition document.h:202
bool addResource(char const *key, SPObject *object)
SPObject * getObjectByRepr(Inkscape::XML::Node *repr) const
void release() override
void setLayerMode(LayerMode mode)
void setInsertBottom(bool insertbottom)
void scaleChildItemsRec(Geom::Scale const &sc, Geom::Point const &p, bool noRecurse)
void order_changed(Inkscape::XML::Node *child, Inkscape::XML::Node *old_ref, Inkscape::XML::Node *new_ref) override
int getItemCount() const
void removeTransformsRecursively(SPObject const *root) override
Inkscape::DrawingItem * show(Inkscape::Drawing &drawing, unsigned int key, unsigned int flags) override
~SPGroup() override
void remove_child(Inkscape::XML::Node *child) override
void print(SPPrintContext *ctx) override
Geom::OptRect bbox(Geom::Affine const &transform, SPItem::BBoxType bboxtype) const override
LayerMode layerDisplayMode(unsigned int display_key) const
char * description() const override
void setLayerDisplayMode(unsigned int display_key, LayerMode mode)
LayerMode _layer_mode
std::map< unsigned int, LayerMode > _display_modes
void translateChildItems(Geom::Translate const &tr)
Inkscape::XML::Node * write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, unsigned int flags) override
void set(SPAttr key, char const *value) override
const char * typeName() const override
The item's type name, not node tag name.
static std::vector< SPItem * > get_expanded(std::vector< SPItem * > const &items)
Return the result of recursively ungrouping all groups in items.
void build(SPDocument *document, Inkscape::XML::Node *repr) override
Inkscape::Colors::Color highlight_color() const override
Generate a highlight colour if one isn't set and return it.
void _updateLayerMode(unsigned int display_key=0)
std::vector< SPItem * > item_list()
void snappoints(std::vector< Inkscape::SnapCandidatePoint > &p, Inkscape::SnapPreferences const *snapprefs) const override
void child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref) override
void update_patheffect(bool write) override
void modified(unsigned int flags) override
bool _insert_bottom
const char * displayName() const override
The item's type name as a translated human string.
LayerMode effectiveLayerMode(unsigned int display_key) const
void hide(unsigned int key) override
void update(SPCtx *ctx, unsigned int flags) override
virtual void _showChildren(Inkscape::Drawing &drawing, Inkscape::DrawingItem *ai, unsigned int key, unsigned int flags)
Base class for visual SVG elements.
Definition sp-item.h:109
void set_i2d_affine(Geom::Affine const &transform)
Definition sp-item.cpp:1827
Geom::Affine i2dt_affine() const
Returns the transformation from item to desktop coords.
Definition sp-item.cpp:1821
Geom::OptRect bounds(BBoxType type, Geom::Affine const &transform=Geom::identity()) const
Definition sp-item.cpp:995
virtual Inkscape::Colors::Color highlight_color() const
Definition sp-item.cpp:277
Inkscape::DrawingItem * invoke_show(Inkscape::Drawing &drawing, unsigned int key, unsigned int flags)
Definition sp-item.cpp:1269
SPMask * getMaskObject() const
Definition sp-item.cpp:177
bool isHidden() const
Definition sp-item.cpp:235
void invoke_print(SPPrintContext *ctx)
Definition sp-item.cpp:1158
void adjust_stroke_width_recursive(double ex)
Recursively scale stroke width in item and its children by expansion.
Definition sp-item.cpp:1515
Geom::Affine transform
Definition sp-item.h:138
void adjust_paint_recursive(Geom::Affine advertized_transform, Geom::Affine t_ancestors, PaintServerType type=GRADIENT)
Recursively compensate pattern or gradient transform.
Definition sp-item.cpp:1564
void invoke_hide(unsigned int key)
Definition sp-item.cpp:1320
std::optional< Inkscape::Colors::Color > _highlightColor
Definition sp-item.h:409
void getSnappoints(std::vector< Inkscape::SnapCandidatePoint > &p, Inkscape::SnapPreferences const *snapprefs=nullptr) const
Definition sp-item.cpp:1105
void move_rel(Geom::Translate const &tr)
Definition sp-item.cpp:1951
void scaleCenter(Geom::Scale const &sc)
Definition sp-item.cpp:406
virtual void removeTransformsRecursively(SPObject const *root)
Definition sp-item.cpp:1625
bool isCenterSet() const
Definition sp-item.cpp:372
@ GRADIENT
Definition sp-item.h:121
@ HATCH
Definition sp-item.h:121
@ PATTERN
Definition sp-item.h:121
Geom::OptRect visualBounds(Geom::Affine const &transform=Geom::identity(), bool wfilter=true, bool wclip=true, bool wmask=true) const
Get item's visual bounding box in this item's coordinate system.
Definition sp-item.cpp:925
unsigned int pos_in_parent() const
Definition sp-item.cpp:1076
void doWriteTransform(Geom::Affine const &transform, Geom::Affine const *adv=nullptr, bool compensate=true)
Set a new transform on an object.
Definition sp-item.cpp:1658
std::vector< SPItemView > views
Definition sp-item.h:163
SPClipPath * getClipObject() const
Definition sp-item.cpp:102
void remove_child(Inkscape::XML::Node *child) override
void release() override
void resetClipPathAndMaskLPE(bool fromrecurse=false)
void applyToMask(SPItem *to, Inkscape::LivePathEffect::Effect *lpe=nullptr)
bool performOnePathEffect(SPCurve *curve, SPShape *current, Inkscape::LivePathEffect::Effect *lpe, bool is_clip_or_mask=false)
returns true when LPE was successful.
bool hasPathEffect() const
void applyToClipPath(SPItem *to, Inkscape::LivePathEffect::Effect *lpe=nullptr)
bool lpe_initialized
void child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref) override
SPLPEItem * removeAllPathEffects(bool keep_paths, bool recursive=false)
If keep_path is true, the item should not be updated, effectively 'flattening' the LPE.
bool hasPathEffectRecursive() const
bool pathEffectsEnabled() const
PathEffectList * path_effect_list
Definition sp-lpe-item.h:62
void update(SPCtx *ctx, unsigned int flags) override
void build(SPDocument *doc, Inkscape::XML::Node *repr) override
void modified(unsigned int flags) override
Inkscape::XML::Node * write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, unsigned int flags) override
void set(SPAttr key, char const *value) override
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
SPObject * getNext()
Inkscape::XML::Node * repr
Definition sp-object.h:193
void setAttribute(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
void requestModified(unsigned int flags)
Requests that a modification notification signal be emitted later (e.g.
void removeAttribute(char const *key)
std::vector< SPObject * > childList(bool add_ref, Action action=ActionGeneral)
Retrieves the children as a std vector object, optionally ref'ing the children in the process,...
SPObject * lastChild()
Definition sp-object.h:318
SPObject * get_child_by_repr(Inkscape::XML::Node *repr)
Return object's child whose node pointer equals repr.
SPDocument * document
Definition sp-object.h:188
SPObject * firstChild()
Definition sp-object.h:315
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Definition sp-object.h:248
char const * defaultLabel() const
Returns a default label property for this object.
SPObject * parent
Definition sp-object.h:189
SPStyle * context_style
Represents the style that should be used to resolve 'context-fill' and 'context-stroke'.
Definition sp-object.h:253
Inkscape::XML::Node * updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT)
Updates the object's repr based on the object's state.
void readAttr(char const *key)
Read value of key attribute from XML node into object.
bool hasChildren() const
Definition sp-object.h:313
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
unsigned int cloned
Definition sp-object.h:180
std::list< SPObject * > hrefList
Definition sp-object.h:197
@ ActionUpdate
Definition sp-object.h:324
virtual void order_changed(Inkscape::XML::Node *child, Inkscape::XML::Node *old_repr, Inkscape::XML::Node *new_repr)
ChildrenList children
Definition sp-object.h:907
SPOffset class.
Definition sp-offset.h:50
float rad
offset radius
Definition sp-offset.h:58
<svg> element
Definition sp-root.h:33
struct SPRoot::@40 inkscape
SPItem * get_first_shape_dependency()
Get the first shape reference which affects the position and layout of this text item.
Definition sp-text.cpp:1210
static void _adjustFontsizeRecursive(SPItem *item, double ex, bool is_root=true)
Definition sp-text.cpp:924
Definition sp-use.h:25
SPItem * get_original() const
Definition sp-use.cpp:830
float computed
Definition svg-length.h:50
RootCluster root
Css & result
double c[8][4]
static char const *const parent
Definition dir-util.cpp:70
TODO: insert short description here.
Group belonging to an SVG drawing element.
@ PREFS_MASKOBJECT_GROUPING_NONE
Definition enums.h:111
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
SPItem * item
Affine identity()
Create an identity matrix.
Definition affine.h:210
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
static R & release(R &r)
Decrements the reference count of a anchored object.
static T clip(T const &v, T const &a, T const &b)
static cairo_user_data_key_t key
int mode
Singleton class to access the preferences file in a convenient way.
Ocnode * child[8]
Definition quantize.cpp:33
Ocnode ** ref
Definition quantize.cpp:32
bool sp_repr_is_meta_element(const Inkscape::XML::Node *node)
Determine if the node is a 'title', 'desc' or 'metadata' element.
C facade to Inkscape::XML::Node.
GList * items
auto len
Definition safe-printf.h:21
TODO: insert short description here.
static void _ungroup_compensate_source_transform(SPItem *item, SPItem const *const expected_source, Geom::Affine const &source_transform)
Helper function for ungrouping.
static void sp_group_perform_patheffect(SPGroup *group, SPGroup *top_group, Inkscape::LivePathEffect::Effect *lpe, bool write)
SPObject * sp_item_group_get_child_by_name(SPGroup *group, SPObject *ref, const gchar *name)
Geom::OptRect bbox_on_rect_clip(SPObject *object)
void set_default_highlight_colors(std::vector< guint32 > colors)
void sp_item_group_ungroup_handle_clones(SPItem *parent, Geom::Affine const g)
finds clones of a child of the group going out of the group; and inverse the group transform on its c...
void sp_item_group_ungroup(SPGroup *group, std::vector< SPItem * > &children)
std::vector< guint32 > default_highlights
bool equal_clip(SPItem *item, SPObject *clip)
static void unlink_clones_of(SPGroup *group)
Unlink all clones of the group.
bool SP_IS_LAYER(SPObject const *obj)
Geom::Affine i2anc_affine(SPObject const *object, SPObject const *ancestor)
Definition sp-item.cpp:1787
void sp_lpe_item_update_patheffect(SPLPEItem *lpeitem, bool wholetree, bool write, bool with_satellites)
Calls any registered handlers for the update_patheffect action.
void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable)
std::list< PathEffectSharedPtr > PathEffectList
Definition sp-lpe-item.h:46
SPObject * sp_object_unref(SPObject *object, SPObject *owner)
Decrease reference count of object, with possible debugging and finalization.
SPItem * sp_offset_get_source(SPOffset *offset)
SPRoot: SVG <svg> implementation.
TODO: insert short description here.
SPItem * sp_textpath_get_path_item(SPTextPath const *tp)
Definition sp-tspan.cpp:463
Interface for XML documents.
Definition document.h:43
virtual Node * createElement(char const *name)=0
Unused.
Definition sp-object.h:94
Contains transformations to document/viewport and the viewport size.
Definition sp-item.h:92
Geom::Affine i2doc
Item to document transformation.
Definition sp-item.h:94
Geom::Affine i2vp
Item to viewport transformation.
Definition sp-item.h:100
Definition curve.h:24
SPStyle - a style object for SPItem objects.
static void sp_svg_write_path(Inkscape::SVG::PathString &str, Geom::Path const &p, bool normalize=false)
Definition svg-path.cpp:109
Glib::ustring name
Definition toolbars.cpp:55