Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
sp-lpe-item.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
5/*
6 * Authors:
7 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
8 * Bastien Bouclet <bgkweb@gmail.com>
9 * Abhishek Sharma
10 *
11 * Copyright (C) 2008 authors
12 *
13 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
14 */
15
16#include <algorithm>
17#include <iterator>
18#include <sstream>
19#include <type_traits>
20#include <utility>
21#include <glibmm/i18n.h>
22#include <sigc++/adaptors/bind.h>
23
24#include "bad-uri-exception.h"
25#include "attributes.h"
26#include "desktop.h"
27#include "display/curve.h"
28#include "inkscape.h"
29#include "live_effects/effect.h"
33#include "message-stack.h"
34#include "preferences.h"
35#include "sp-clippath.h"
36#include "sp-ellipse.h"
37#include "sp-spiral.h"
38#include "sp-star.h"
39#include "sp-item-group.h"
40#include "sp-mask.h"
41#include "sp-path.h"
42#include "sp-root.h"
43#include "sp-symbol.h"
44#include "svg/svg.h"
45
46/* LPEItem base class */
47
48static void lpeobject_ref_modified(SPObject *href, guint flags, SPLPEItem *lpeitem);
50static SPLPEItem * sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem, bool keep_paths, bool force = false, bool is_clip_mask = false);
51
52typedef std::list<std::string> HRefList;
53static std::string patheffectlist_svg_string(PathEffectList const & list);
54static std::string hreflist_svg_string(HRefList const & list);
55
56namespace {
57
58void clear_path_effect_list(PathEffectList* const l) {
59 auto it = l->begin();
60 while ( it != l->end()) {
61 (*it)->unlink();
62 it = l->erase(it);
63 }
64}
65
66} // unnamed namespace
67
69 : SPItem()
70 , path_effects_enabled(1)
71 , path_effect_list(new PathEffectList())
72 , current_path_effect(nullptr)
73 , lpe_helperpaths()
74{
75}
76
77SPLPEItem::~SPLPEItem() = default;
78
84
86 // disconnect all modified listeners:
88
89 clear_path_effect_list(this->path_effect_list);
90
91 // delete the list itself
92 delete this->path_effect_list;
93 this->path_effect_list = nullptr;
94
96}
97
98void SPLPEItem::set(SPAttr key, gchar const* value) {
99 switch (key) {
101 this->current_path_effect = nullptr;
102
103 // Disable the path effects while populating the LPE list
105
106 // disconnect all modified listeners:
108
109 clear_path_effect_list(this->path_effect_list);
110
111 // Parse the contents of "value" to rebuild the path effect reference list
112 if ( value ) {
113 std::istringstream iss(value);
114 std::string href;
115
116 while (std::getline(iss, href, ';'))
117 {
118 auto path_effect_ref = std::make_shared<Inkscape::LivePathEffect::LPEObjectReference>(this);
119
120 try {
121 path_effect_ref->link(href.c_str());
122 } catch (Inkscape::BadURIException const &e) {
123 g_warning("BadURIException when trying to find LPE: %s", e.what());
124 path_effect_ref->unlink();
125 continue;
126 }
127
128 if ( path_effect_ref->lpeobject && path_effect_ref->lpeobject->get_lpe() ) {
129 // connect modified-listener
130 lpe_modified_connection_list.emplace_back(
131 path_effect_ref->lpeobject->connectModified(sigc::bind(&lpeobject_ref_modified, this)));
132 } else {
133 // on clipboard we fix refs so in middle time of the operation, in LPE with multiples path
134 // effects can result middle updata and fire a warning, so we silent it
135 if (!isOnClipboard()) {
136 // something has gone wrong in finding the right patheffect.
137 g_warning("Unknown LPE type specified, LPE stack effectively disabled");
138 // keep the effect in the lpestack, so the whole stack is effectively disabled but
139 // maintained
140 }
141 }
142
143 this->path_effect_list->push_back(std::move(path_effect_ref));
144 }
145 }
146
148 break;
149
150 default:
151 SPItem::set(key, value);
152 break;
153 }
154}
155
156void SPLPEItem::update(SPCtx* ctx, unsigned int flags) {
157 SPItem::update(ctx, flags);
158
159 // update the helperpaths of all LPEs applied to the item
160 // TODO: re-add for the new node tool
161}
162
163void SPLPEItem::modified(unsigned int flags) {
164 //stop update when modified and make the effect update on the LPE transform method if the effect require it
165 //if (is<SPGroup>(this) && (flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_USER_MODIFIED_FLAG_B)) {
166 // sp_lpe_item_update_patheffect(this, true, false);
167 //}
168}
169
171 if (flags & SP_OBJECT_WRITE_EXT) {
172 if ( hasPathEffect() ) {
174 } else {
175 repr->removeAttribute("inkscape:path-effect");
176 }
177 }
178
179 SPItem::write(xml_doc, repr, flags);
180
181 return repr;
182}
183
188{
190 Inkscape::XML::Node *clipnode = sp_repr_lookup_name(root, "inkscape:clipboard", 1);
191 return clipnode != nullptr;
192}
193
195 auto p = cast<SPLPEItem>(parent);
196 return (p && p->onsymbol) || is<SPSymbol>(this);
197}
202
203 if (!curve) {
204 return false;
205 }
206
207 if (this->hasPathEffect() && this->pathEffectsEnabled()) {
209 auto const path_effect_list_size = path_effect_list.size();
210 for (auto &lperef : path_effect_list) {
211 LivePathEffectObject *lpeobj = lperef->lpeobject;
212 if (!lpeobj) {
216 g_warning("SPLPEItem::performPathEffect - NULL lpeobj in list!");
217 return false;
218 }
219
221 if (!lpe || !performOnePathEffect(curve, current, lpe, is_clip_or_mask)) {
222 return false;
223 }
224 auto hreflist = lpeobj->hrefList;
225 if (hreflist.size()) { // lpe can be removed on perform (eg: clone lpe on copy)
226 if (path_effect_list_size != this->path_effect_list->size()) {
227 break;
228 }
229 }
230 }
231 }
232 return true;
233}
234
239 if (!lpe) {
243 g_warning("SPLPEItem::performPathEffect - lpeobj with invalid lpe in the stack!");
244 return false;
245 }
246 if (document->isSeeking()) {
247 lpe->refresh_widgets = true;
248 }
249 if (lpe->isVisible()) {
250 if (lpe->acceptsNumClicks() > 0 && !lpe->isReady()) {
251 // if the effect expects mouse input before being applied and the input is not finished
252 // yet, we don't alter the path
253 return false;
254 }
255 //if is not clip or mask or LPE apply to clip and mask
256 if (!is_clip_or_mask || lpe->apply_to_clippath_and_mask) {
257 // Uncomment to get updates
258 // g_debug("LPE running:: %s",Inkscape::LivePathEffect::LPETypeConverter.get_key(lpe->effectType()).c_str());
260 if (!is<SPGroup>(this)) {
261 lpe->pathvector_before_effect = curve->get_pathvector();
262 }
263 // To Calculate BBox on shapes and nested LPE
264 current->setCurveInsync(curve);
265 // Groups have their doBeforeEffect called elsewhere
266 if (lpe->lpeversion.param_getSVGValue() != "0") { // we are on 1 or up
267 current->bbox_vis_cache_is_valid = false;
268 current->bbox_geom_cache_is_valid = false;
269 }
270 auto group = cast<SPGroup>(this);
271 if (!group && !is_clip_or_mask) {
272 lpe->doBeforeEffect_impl(this);
273 }
274
275 try {
276 lpe->doEffect(curve);
277 lpe->has_exception = false;
278 }
279
280 catch (std::exception & e) {
281 g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what());
282 if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->messageStack()) {
283 SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
284 _("An exception occurred during execution of the Path Effect.") );
285 }
286 lpe->doOnException(this);
287 return false;
288 }
289
290 if (!group) {
291 // To have processed the shape to doAfterEffect
292 current->setCurveInsync(curve);
293 if (curve) {
294 lpe->pathvector_after_effect = curve->get_pathvector();
295 }
296 lpe->doAfterEffect_impl(this, curve);
297 }
298 }
299 }
300 return true;
301}
302
307{
308 if (is<SPGroup>(this)) {
309 return false;
310 }
311
312 if (is<SPSpiral>(this) && !this->transform.isUniformScale()) {
313 return false;
314 }
315 if (is<SPStar>(this) && !this->transform.isUniformScale()) {
316 return false;
317 }
318 auto* mask_path = this->getMaskObject();
319 if(mask_path) {
320 return false;
321 }
322 auto* clip_path = this->getClipObject();
323 if(clip_path) {
324 return false;
325 }
327 for (auto &lperef : path_effect_list) {
328 if (!lperef) {
329 continue;
330 }
331 LivePathEffectObject *lpeobj = lperef->lpeobject;
332 if (lpeobj) {
334 if (lpe) {
335 if (dynamic_cast<Inkscape::LivePathEffect::LPEMeasureSegments*>(lpe) ||
336 dynamic_cast<Inkscape::LivePathEffect::LPELattice2*>(lpe))
337 {
338 return false;
339 }
340 }
341 }
342 }
343
344 if (unoptimized()) {
345 return false;
346 }
347
349 return !prefs->getBool("/options/preservetransform/value", false);
350}
351
356{
357 if (!pathEffectsEnabled())
358 return;
359
361 for (auto &lperef : path_effect_list) {
362 if (!lperef) {
363 continue;
364 }
365 LivePathEffectObject *lpeobj = lperef->lpeobject;
366 if (lpeobj) {
368 if (lpe && !lpe->is_load) {
369 lpe->transform_multiply_impl(postmul, this);
370 }
371 }
372 }
373}
374
375// CPPIFY: make pure virtual
376void SPLPEItem::update_patheffect(bool /*write*/) {
377 //throw;
378}
379
383void
384sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write, bool with_satellites)
385{
386#ifdef SHAPE_VERBOSE
387 g_message("sp_lpe_item_update_patheffect: %p\n", lpeitem);
388#endif
389 g_return_if_fail (lpeitem != nullptr);
390
391 // Do not check for LPE item to allow LPE work on clips/mask
392 if (!lpeitem->pathEffectsEnabled())
393 return;
394
395 SPLPEItem *top = nullptr;
396
397 if (wholetree) {
398 SPLPEItem *prev_parent = lpeitem;
399 auto parent = cast<SPLPEItem>(prev_parent->parent);
400 while (parent && parent->hasPathEffectRecursive()) {
401 prev_parent = parent;
402 parent = cast<SPLPEItem>(prev_parent->parent);
403 }
404 top = prev_parent;
405 }
406 else {
407 top = lpeitem;
408 }
409 top->update_patheffect(write);
410 if (with_satellites) {
411 lpeitem->update_satellites();
412 }
413}
414
418static void
419lpeobject_ref_modified(SPObject */*href*/, guint flags, SPLPEItem *lpeitem)
420{
421#ifdef SHAPE_VERBOSE
422 g_message("lpeobject_ref_modified");
423#endif
424 if (!lpeitem->document->isSeeking() && flags != 29 && flags != 253 && !(flags & SP_OBJECT_STYLESHEET_MODIFIED_FLAG))
425 {
426 sp_lpe_item_update_patheffect(lpeitem, false, true, true);
427 }
428}
429
430static void
432{
433 g_return_if_fail(lpeitem != nullptr);
434
435 SPClipPath *clip_path = lpeitem->getClipObject();
436 if(clip_path) {
437 std::vector<SPObject*> clip_path_list = clip_path->childList(true);
438 for (auto iter : clip_path_list) {
439 auto clip_data = cast<SPLPEItem>(iter);
441 sp_object_unref(iter);
442 }
443 }
444
445 SPMask *mask_path = lpeitem->getMaskObject();
446 if(mask_path) {
447 std::vector<SPObject*> mask_path_list = mask_path->childList(true);
448 for (auto iter : mask_path_list) {
449 auto mask_data = cast<SPLPEItem>(iter);
451 sp_object_unref(iter);
452 }
453 }
454 if (is<SPGroup>(lpeitem)) {
455 std::vector<SPItem*> item_list = cast<SPGroup>(lpeitem)->item_list();
456 for (auto subitem : item_list) {
457 if (is<SPLPEItem>(subitem)) {
458 sp_lpe_item_create_original_path_recursive(cast<SPLPEItem>(subitem));
459 }
460 }
461 } else if (auto path = cast<SPPath>(lpeitem)) {
462 if (!path->getAttribute("inkscape:original-d") ) {
463 if (gchar const * value = path->getAttribute("d")) {
464 path->setAttribute("inkscape:original-d", value);
465 }
466 }
467 } else if (auto shape = cast<SPShape>(lpeitem)) {
468 if (!shape->curveBeforeLPE()) {
469 shape->setCurveBeforeLPE(shape->curve());
470 }
471 }
472}
473
474static SPLPEItem *
475sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem, bool keep_paths, bool force, bool is_clip_mask)
476{
477 if (!lpeitem) {
478 return nullptr;
479 }
480 auto group = cast<SPGroup>(lpeitem);
481 auto shape = cast<SPShape>(lpeitem);
482 auto path = cast<SPPath>(lpeitem);
483 SPClipPath *clip_path = lpeitem->getClipObject();
484 if(clip_path) {
485 std::vector<SPObject*> clip_path_list = clip_path->childList(true);
486 for (auto iter : clip_path_list) {
487 auto clip_data = cast<SPLPEItem>(iter);
488 if (clip_data) {
489 sp_lpe_item_cleanup_original_path_recursive(clip_data, keep_paths, lpeitem && !lpeitem->hasPathEffectRecursive(), true);
490 }
491 sp_object_unref(iter);
492 }
493 }
494
495 SPMask *mask_path = lpeitem->getMaskObject();
496 if(mask_path) {
497 std::vector<SPObject*> mask_path_list = mask_path->childList(true);
498 for (auto iter : mask_path_list) {
499 auto mask_data = cast<SPLPEItem>(iter);
500 if (mask_data) {
501 sp_lpe_item_cleanup_original_path_recursive(mask_data, keep_paths, lpeitem && !lpeitem->hasPathEffectRecursive(), true);
502 }
503 sp_object_unref(iter);
504 }
505 }
506
507 if (group) {
508 std::vector<SPItem*> item_list = cast<SPGroup>(lpeitem)->item_list();
509 for (auto iter : item_list) {
510 if (auto subitem = cast<SPLPEItem>(iter)) {
511 if (auto shape = cast<SPShape>(iter)) {
512 if (gchar const * value = shape->getAttribute("d")) {
513 shape->setCurve(SPCurve(sp_svg_read_pathv(value)));
514 }
515 }
517 }
518 }
519 } else if (path) {
520 Inkscape::XML::Node *repr = lpeitem->getRepr();
521 if (repr->attribute("inkscape:original-d") &&
522 !lpeitem->hasPathEffectRecursive() &&
523 (!is_clip_mask ||
524 ( is_clip_mask && force)))
525 {
526 if (!keep_paths) {
527 repr->setAttribute("d", repr->attribute("inkscape:original-d"));
528 }
529 repr->removeAttribute("inkscape:original-d");
530 path->setCurveBeforeLPE(nullptr);
531 if (!(shape->curve()->get_segment_count())) {
532 repr->parent()->removeChild(repr);
533 }
534 } else {
535 if (!keep_paths) {
536 sp_lpe_item_update_patheffect(lpeitem, true, true);
537 }
538 }
539 } else if (shape) {
540 Inkscape::XML::Node *repr = lpeitem->getRepr();
541 SPCurve const *c_lpe = shape->curve();
542 Glib::ustring d_str;
543 if (c_lpe) {
544 d_str = sp_svg_write_path(c_lpe->get_pathvector());
545 } else if (shape->getAttribute("d")) {
546 d_str = shape->getAttribute("d");
547 } else {
548 return lpeitem;
549 }
550 if (!lpeitem->hasPathEffectRecursive() &&
551 (!is_clip_mask ||
552 ( is_clip_mask && force)))
553 {
554 if (!keep_paths) {
555 repr->removeAttribute("d");
556 shape->setCurveBeforeLPE(nullptr);
557 } else {
558 const char * id = repr->attribute("id");
559 const char * style = repr->attribute("style");
560 // remember the position of the item
561 gint pos = shape->getRepr()->position();
562 // remember parent
563 Inkscape::XML::Node *parent = shape->getRepr()->parent();
564 // remember class
565 char const *class_attr = shape->getRepr()->attribute("class");
566 // remember title
567 gchar *title = shape->title();
568 // remember description
569 gchar *desc = shape->desc();
570 // remember transformation
571 gchar const *transform_str = shape->getRepr()->attribute("transform");
572 // Mask
573 gchar const *mask_str = (gchar *) shape->getRepr()->attribute("mask");
574 // Clip path
575 gchar const *clip_str = (gchar *) shape->getRepr()->attribute("clip-path");
576
577 /* Rotation center */
578 gchar const *transform_center_x = shape->getRepr()->attribute("inkscape:transform-center-x");
579 gchar const *transform_center_y = shape->getRepr()->attribute("inkscape:transform-center-y");
580
581 // It's going to resurrect, so we delete without notifying listeners.
582 SPDocument * doc = shape->document;
583 shape->deleteObject(false);
584 Inkscape::XML::Document *xml_doc = doc->getReprDoc();
585 Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");
586 // restore id
587 repr->setAttribute("id", id);
588 // restore class
589 repr->setAttribute("class", class_attr);
590 // restore transform
591 repr->setAttribute("transform", transform_str);
592 // restore clip
593 repr->setAttribute("clip-path", clip_str);
594 // restore mask
595 repr->setAttribute("mask", mask_str);
596 // restore transform_center_x
597 repr->setAttribute("inkscape:transform-center-x", transform_center_x);
598 // restore transform_center_y
599 repr->setAttribute("inkscape:transform-center-y", transform_center_y);
600 //restore d
601 repr->setAttribute("d", d_str);
602 //restore style
603 repr->setAttribute("style", style);
604 // add the new repr to the parent
605 parent->appendChild(repr);
606 SPObject* newObj = doc->getObjectByRepr(repr);
607 if (title && newObj) {
608 newObj->setTitle(title);
609 g_free(title);
610 }
611 if (desc && newObj) {
612 newObj->setDesc(desc);
613 g_free(desc);
614 }
615 // move to the saved position
616 repr->setPosition(pos > 0 ? pos : 0);
618 lpeitem = cast<SPLPEItem>(newObj);
619 }
620 } else {
621 if (!keep_paths) {
622 sp_lpe_item_update_patheffect(lpeitem, true, true);
623 }
624 }
625 }
626 if (lpeitem->getRepr() && !lpeitem->getAttribute("inkscape:path-effect") && lpeitem->path_effect_list) {
627 clear_path_effect_list(lpeitem->path_effect_list);
628 }
629 return lpeitem;
630}
631
632
633
634void SPLPEItem::addPathEffect(std::string value, bool reset)
635{
636 if (!value.empty()) {
637 // Apply the path effects here because in the casse of a group, lpe->resetDefaults
638 // needs that all the subitems have their effects applied
639 auto group = cast<SPGroup>(this);
640 if (group) {
641 sp_lpe_item_update_patheffect(this, false, true);
642 }
643 // Disable the path effects while preparing the new lpe
645
646 // Add the new reference to the list of LPE references
647 HRefList hreflist;
648 for (auto const &it : *this->path_effect_list) {
649 hreflist.emplace_back(it->lpeobject_href);
650 }
651 hreflist.push_back(std::move(value));
652
653 this->setAttributeOrRemoveIfEmpty("inkscape:path-effect", hreflist_svg_string(hreflist));
654 // Make sure that ellipse is stored as <svg:path>
655 if( is<SPGenericEllipse>(this)) {
656 cast<SPGenericEllipse>(this)->write( this->getRepr()->document(), this->getRepr(), SP_OBJECT_WRITE_EXT );
657 }
658 // make sure there is an original-d for paths!!!
660
661 LivePathEffectObject *lpeobj = this->path_effect_list->back()->lpeobject;
662 if (lpeobj && lpeobj->get_lpe()) {
664 // Ask the path effect to reset itself if it doesn't have parameters yet
665 if (reset) {
666 // has to be called when all the subitems have their lpes applied
667 lpe->resetDefaults(this);
668 }
669 // perform this once when the effect is applied
670 lpe->doOnApply_impl(this);
671 }
672
673 //Enable the path effects now that everything is ready to apply the new path effect
675
676 // Apply the path effect
677 sp_lpe_item_update_patheffect(this, true, true);
678 }
679}
680
682{
683 const gchar * repr_id = new_lpeobj->getRepr()->attribute("id");
684 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
685 this->addPathEffect(hrefstr, false);
686 g_free(hrefstr);
687}
688
693{
694 auto const lperef = this->getCurrentLPEReference();
695 if (!lperef) {
696 return nullptr;
697 }
698 if (Inkscape::LivePathEffect::Effect* effect_ = this->getCurrentLPE()) {
699 effect_->keep_paths = keep_paths;
700 effect_->on_remove_all = false;
701 if (effect_->getHolderRemove()) {
702 this->deleteObject(true);
703 return nullptr;
704 }
705 effect_->doOnRemove_impl(this);
706 }
707 this->path_effect_list->remove(lperef); //current lpe ref is always our 'own' pointer from the path_effect_list
708 this->setAttributeOrRemoveIfEmpty("inkscape:path-effect", patheffectlist_svg_string(*this->path_effect_list));
709 if (!keep_paths) {
710 // Make sure that ellipse is stored as <svg:circle> or <svg:ellipse> if possible.
711 if (auto ell = cast<SPGenericEllipse>(this)) {
712 ell->write(getRepr()->document(), getRepr(), SP_OBJECT_WRITE_EXT);
713 }
714 }
715 return sp_lpe_item_cleanup_original_path_recursive(this, keep_paths);
716}
717
721SPLPEItem * SPLPEItem::removeAllPathEffects(bool keep_paths, bool recursive)
722{
723 if (recursive) {
724 auto grp = cast<SPGroup>(this);
725 if (grp) {
726 std::vector<SPItem *> item_list = grp->item_list();
727 for (auto iter : item_list) {
728 sp_object_ref(iter);
729 }
730 for (auto iter : item_list) {
731 auto subitem = cast<SPLPEItem>(iter);
732 if (subitem && subitem->document) {
733 subitem->removeAllPathEffects(keep_paths, recursive);
734 }
735 }
736 for (auto iter : item_list) {
737 sp_object_unref(iter);
738 }
739 }
740 }
741 if (!hasPathEffect()) {
742 return nullptr;
743 }
744 if (keep_paths) {
745 if (path_effect_list->empty()) {
746 return nullptr;
747 }
748 }
749 PathEffectList a_path_effect_list(*path_effect_list);
750 for (auto &lperef : a_path_effect_list) {
751 if (!lperef) {
752 continue;
753 }
754 LivePathEffectObject *lpeobj = lperef->lpeobject;
755 if (lpeobj) {
757 if (lpe) {
758 lpe->keep_paths = keep_paths;
759 lpe->on_remove_all = true;
760 if (lpe->getHolderRemove()) {
761 this->deleteObject(true);
762 return nullptr;
763 }
764 lpe->doOnRemove_impl(this);
765 }
766 }
767 // this allow to keep references and propely delete satellites
768 path_effect_list->remove(lperef);
769 }
770 this->removeAttribute("inkscape:path-effect");
771 if (!keep_paths) {
772 // Make sure that ellipse is stored as <svg:circle> or <svg:ellipse> if possible.
773 if (auto ell = cast<SPGenericEllipse>(this)) {
774 ell->write(getRepr()->document(), getRepr(), SP_OBJECT_WRITE_EXT);
775 }
776 }
777 // SPItem can be changed on remove all LPE items (Shape to Path) We return generated item
778 return sp_lpe_item_cleanup_original_path_recursive(this, keep_paths);
779}
780
782{
783 auto const lperef = getCurrentLPEReference();
784 if (!lperef)
785 return;
786
787 PathEffectList new_list = *this->path_effect_list;
788 auto const cur_it = std::find(new_list.begin(), new_list.end(), lperef);
789 if (cur_it != new_list.cend()) {
790 auto down_it = cur_it;
791 ++down_it;
792 if (down_it != new_list.end()) { // perhaps current effect is already last effect
793 std::iter_swap(cur_it, down_it);
794 }
795 }
796
797 this->setAttributeOrRemoveIfEmpty("inkscape:path-effect", patheffectlist_svg_string(new_list));
798
800}
801
803{
804 auto const lperef = getCurrentLPEReference();
805 if (!lperef)
806 return;
807
808 HRefList hreflist;
809 auto const cur_it = std::find(this->path_effect_list->cbegin(), this->path_effect_list->cend(), lperef);
811 for (auto it = this->path_effect_list->cbegin(); it != this->path_effect_list->cend(); ++it) {
812 hreflist.emplace_back((*it)->lpeobject_href);
813 LivePathEffectObject *lpeobj = (*it)->lpeobject;
814 if (it == cur_it) {
815 auto *duple = lpeobj->fork_private_if_necessary(0);
816 hreflist.push_back(std::string{"#"} += duple->getId());
817 }
818 }
819 this->setAttributeOrRemoveIfEmpty("inkscape:path-effect", hreflist_svg_string(hreflist));
820
822 update_satellites(true);
823}
824
826{
827 auto const lperef = getCurrentLPEReference();
828 if (!lperef)
829 return nullptr;
830
831 HRefList hreflist;
832 HRefList hreflist2;
833 auto const cur_it = std::find(this->path_effect_list->cbegin(), this->path_effect_list->cend(), lperef);
835 bool done = false;
836 for (auto it = this->path_effect_list->cbegin(); it != this->path_effect_list->cend(); ++it) {
837 if (done) {
838 hreflist2.emplace_back((*it)->lpeobject_href);
839 } else {
840 hreflist.emplace_back((*it)->lpeobject_href);
841 }
842 if (it == cur_it) {
843 done = true;
844 }
845 }
846 this->setAttributeOrRemoveIfEmpty("inkscape:path-effect", hreflist_svg_string(hreflist));
847
849 sp_lpe_item_update_patheffect(this, true, true);
850
851 auto lpeitem = removeAllPathEffects(true);
852 if ( hreflist2.size()) {
853 sp_lpe_item_enable_path_effects(lpeitem, false);
854 lpeitem->setAttributeOrRemoveIfEmpty("inkscape:path-effect", hreflist_svg_string(hreflist2));
856 sp_lpe_item_enable_path_effects(lpeitem, true);
857 sp_lpe_item_update_patheffect(lpeitem, true, true);
858 lpeitem->update_satellites(true);
859 }
860 return lpeitem;
861}
862
864{
866 if (!lpe)
867 return;
868
869 bool exist = false;
870 for (auto &lperef : path_effect_list) {
871 if (lperef->lpeobject == lpe->getLPEObj()) {
872 setCurrentPathEffect(lperef);
873 exist = true;
874 break;
875 }
876 }
877 if (exist) {
878 // this function is called only with FILLET_CHAMFER if do with holderRemove LPE`s (clones LPE) need to rework
879 removeCurrentPathEffect(keep_paths);
880 } else {
881 g_warning("LPE dont exist to remove");
882 }
883}
884
885void SPLPEItem::movePathEffect(gint origin, gint dest, bool select_moved)
886{
887 PathEffectList new_list = *this->path_effect_list;
888 auto lpe = getCurrentLPE();
889 if (!lpe)
890 return;
891
892 LivePathEffectObject *lpeobj = lpe->getLPEObj();
893 if (lpeobj) {
894 auto const nlpe = new_list.size();
895 if (!nlpe ||
896 origin == dest ||
897 origin > nlpe -1 ||
898 dest > nlpe -1)
899 {
900 return;
901 }
902 gint selectme = 0;
903 auto insertme = new_list.begin();
904 auto insertto = new_list.begin();
905 std::advance(insertme, origin);
906 if (origin > dest) {
907 std::advance(insertto, dest);
908 selectme = dest;
909 } else {
910 std::advance(insertto, dest + 1);
911 selectme = dest + 1;
912 }
913 new_list.insert(insertto, *insertme);
914 auto removeme = new_list.begin();
915 if (origin > dest) {
916 std::advance(removeme, origin + 1);
917 } else {
918 std::advance(removeme, origin);
919 selectme = dest;
920 }
921 new_list.erase(removeme);
922 this->setAttributeOrRemoveIfEmpty("inkscape:path-effect", patheffectlist_svg_string(new_list));
924 auto select = this->path_effect_list->begin();
925 std::advance(select, selectme);
926 if (select_moved) {
927 setCurrentPathEffect(*select);
928 } else {
930 for (auto &lperef : path_effect_list) {
931 if (lperef->lpeobject == lpeobj) {
932 setCurrentPathEffect(lperef);
933 break;
934 }
935 }
936 }
937 }
938}
939
940
942{
943 auto const lperef = getCurrentLPEReference();
944 if (!lperef)
945 return;
946
947 PathEffectList new_list = *this->path_effect_list;
948 auto const cur_it = std::find(new_list.begin(), new_list.end(), lperef);
949 if (cur_it != new_list.end() && cur_it != new_list.begin()) {
950 auto up_it = cur_it;
951 --up_it;
952 std::iter_swap(cur_it, up_it);
953 }
954
955 this->setAttributeOrRemoveIfEmpty("inkscape:path-effect", patheffectlist_svg_string(new_list));
956
958}
959
960void
962 if (path_effect_list->empty()) {
963 return;
964 }
965 auto grp = cast<SPGroup>(this);
966 if (recursive && grp) {
967 std::vector<SPItem *> item_list = grp->item_list();
968 for (auto iter : item_list) {
969 auto subitem = cast<SPLPEItem>(iter);
970 if (subitem) {
971 subitem->update_satellites(recursive);
972 }
973 }
974 }
975
976 // go through the list; if some are unknown or invalid, return true
978 for (auto &lperef : path_effect_list) {
979 LivePathEffectObject *lpeobj = lperef->lpeobject;
980 if (lpeobj) {
981 if (auto *lpe = lpeobj->get_lpe()) {
982 lpe->update_satellites();
983 }
984 }
985 }
986}
987
990{
991 if (path_effect_list->empty()) {
992 return false;
993 }
994
995 // go through the list; if some are unknown or invalid, return true
997 for (auto &lperef : path_effect_list) {
998 LivePathEffectObject *lpeobj = lperef->lpeobject;
999 if (!lpeobj || !lpeobj->get_lpe()) {
1000 return true;
1001 }
1002 }
1003
1004 return false;
1005}
1006
1007bool SPLPEItem::hasPathEffectOfTypeRecursive(int const type, bool is_ready) const
1008{
1009 auto parent_lpe_item = cast<SPLPEItem>(parent);
1010 if (parent_lpe_item) {
1011 return hasPathEffectOfType(type, is_ready) || parent_lpe_item->hasPathEffectOfTypeRecursive(type, is_ready);
1012 } else {
1013 return hasPathEffectOfType(type, is_ready);
1014 }
1015}
1016
1017bool SPLPEItem::hasPathEffectOfType(int const type, bool is_ready) const
1018{
1019 if (path_effect_list->empty()) {
1020 return false;
1021 }
1022
1023 for (auto const &it : *path_effect_list) {
1024 auto const lpeobj = it->lpeobject;
1025 if (lpeobj) {
1026 Inkscape::LivePathEffect::Effect const* lpe = lpeobj->get_lpe();
1027 if (lpe && (lpe->effectType() == type)) {
1028 if (is_ready || lpe->isReady()) {
1029 return true;
1030 }
1031 }
1032 }
1033 }
1034
1035 return false;
1036}
1037
1042{
1043 if (shape->hasPathEffectRecursive()) {
1044 return true;
1045 }
1046 if (!path_effect_list || path_effect_list->empty()) {
1047 return false;
1048 }
1049
1051 for (auto &lperef : path_effect_list) {
1052 LivePathEffectObject *lpeobj = lperef->lpeobject;
1053 if (!lpeobj) {
1054 continue;
1055 }
1057 if (lpe && lpe->apply_to_clippath_and_mask) {
1058 return true;
1059 }
1060 }
1061 return false;
1062}
1063
1068{
1069 auto parent_lpe_item = cast<SPLPEItem>(parent);
1070 if (parent_lpe_item) {
1071 return hasPathEffectOnClipOrMask(shape) || parent_lpe_item->hasPathEffectOnClipOrMaskRecursive(shape);
1072 }
1073 else {
1074 return hasPathEffectOnClipOrMask(shape);
1075 }
1076}
1077
1079{
1080 if (!path_effect_list || path_effect_list->empty()) {
1081 return false;
1082 }
1083
1084 // go through the list; if some are unknown or invalid, we are not an LPE item!
1086 for (auto &lperef : path_effect_list) {
1087 LivePathEffectObject *lpeobj = lperef->lpeobject;
1088 if (!lpeobj || !lpeobj->get_lpe()) {
1089 return false;
1090 }
1091 }
1092
1093 return true;
1094}
1095
1097{
1098 auto parent_lpe_item = cast<SPLPEItem>(parent);
1099 if (parent_lpe_item) {
1100 return hasPathEffect() || parent_lpe_item->hasPathEffectRecursive();
1101 }
1102 else {
1103 return hasPathEffect();
1104 }
1105}
1106
1111{
1112 auto parent_lpe_item = cast<SPLPEItem>(parent);
1113 if (parent_lpe_item && !hasPathEffectRecursive()) {
1114 return hasPathEffect() ? parent_lpe_item : this;
1115 } else {
1116 return parent_lpe_item ? parent_lpe_item->getTopPathEffect() : this;
1117 }
1118}
1119
1120void
1122{
1123 if (fromrecurse) {
1124 auto group = cast<SPGroup>(this);
1125 auto shape = cast<SPShape>(this);
1126 if (group) {
1127 std::vector<SPItem*> item_list = group->item_list();
1128 for (auto iter2 : item_list) {
1129 auto subitem = cast<SPLPEItem>(iter2);
1130 if (subitem) {
1131 subitem->resetClipPathAndMaskLPE(true);
1132 }
1133 }
1134 } else if (shape) {
1135 shape->setCurveInsync(shape->curveForEdit());
1137 shape->removeAttribute("inkscape:original-d");
1138 shape->setCurveBeforeLPE(nullptr);
1139 } else {
1140 // make sure there is an original-d for paths!!!
1142 }
1143 }
1144 return;
1145 }
1146 SPClipPath *clip_path = this->getClipObject();
1147 if(clip_path) {
1148 std::vector<SPObject*> clip_path_list = clip_path->childList(true);
1149 for (auto iter : clip_path_list) {
1150 auto group = cast<SPGroup>(iter);
1151 auto shape = cast<SPShape>(iter);
1152 if (group) {
1153 std::vector<SPItem*> item_list = group->item_list();
1154 for (auto iter2 : item_list) {
1155 auto subitem = cast<SPLPEItem>(iter2);
1156 if (subitem) {
1157 subitem->resetClipPathAndMaskLPE(true);
1158 }
1159 }
1160 } else if (shape) {
1161 shape->setCurveInsync(shape->curveForEdit());
1163 shape->removeAttribute("inkscape:original-d");
1164 shape->setCurveBeforeLPE(nullptr);
1165 } else {
1166 // make sure there is an original-d for paths!!!
1168 }
1169 }
1170 sp_object_unref(iter);
1171 }
1172 }
1173 SPMask *mask = this->getMaskObject();
1174 if(mask) {
1175 std::vector<SPObject*> mask_list = mask->childList(true);
1176 for (auto iter : mask_list) {
1177 auto group = cast<SPGroup>(iter);
1178 auto shape = cast<SPShape>(iter);
1179 if (group) {
1180 std::vector<SPItem*> item_list = group->item_list();
1181 for (auto iter2 : item_list) {
1182 auto subitem = cast<SPLPEItem>(iter2);
1183 if (subitem) {
1184 subitem->resetClipPathAndMaskLPE(true);
1185 }
1186 }
1187 } else if (shape) {
1188 shape->setCurveInsync(shape->curveForEdit());
1190 shape->removeAttribute("inkscape:original-d");
1191 shape->setCurveBeforeLPE(nullptr);
1192 } else {
1193 // make sure there is an original-d for paths!!!
1195 }
1196 }
1197 sp_object_unref(iter);
1198 }
1199 }
1200}
1201
1202void
1204{
1205 if (lpe && !lpe->apply_to_clippath_and_mask) {
1206 return;
1207 }
1208 SPClipPath *clip_path = to->getClipObject();
1209 if(clip_path) {
1210 std::vector<SPObject*> clip_path_list = clip_path->childList(true);
1211 for (auto clip_data : clip_path_list) {
1212 applyToClipPathOrMask(cast<SPItem>(clip_data), to, lpe);
1213 sp_object_unref(clip_data);
1214 }
1215 }
1216}
1217
1218void
1220{
1221 if (lpe && !lpe->apply_to_clippath_and_mask) {
1222 return;
1223 }
1224 SPMask *mask = to->getMaskObject();
1225 if(mask) {
1226 std::vector<SPObject*> mask_list = mask->childList(true);
1227 for (auto mask_data : mask_list) {
1228 applyToClipPathOrMask(cast<SPItem>(mask_data), to, lpe);
1229 sp_object_unref(mask_data);
1230 }
1231 }
1232}
1233
1234void
1236{
1237 auto group = cast<SPGroup>(clip_mask);
1238 auto shape = cast<SPShape>(clip_mask);
1239 SPRoot *root = this->document->getRoot();
1240 if (group) {
1241 std::vector<SPItem*> item_list = group->item_list();
1242 for (auto subitem : item_list) {
1243 applyToClipPathOrMask(subitem, to, lpe);
1244 }
1245 } else if (shape) {
1246 if (root->inkscape.getVersion().isInsideRangeInclusive({0, 1}, {0, 92})) {
1247 shape->removeAttribute("inkscape:original-d");
1248 } else {
1249 if (shape->curve()) {
1250 auto c = *shape->curve();
1251 bool success = false;
1252 try {
1253 if (lpe) {
1254 success = this->performOnePathEffect(&c, shape, lpe, true);
1255 } else {
1256 success = this->performPathEffect(&c, shape, true);
1257 }
1258 } catch (std::exception & e) {
1259 g_warning("Exception during LPE execution. \n %s", e.what());
1260 if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->messageStack()) {
1261 SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
1262 _("An exception occurred during execution of the Path Effect.") );
1263 }
1264 success = false;
1265 }
1266 if (success) {
1267 auto str = sp_svg_write_path(c.get_pathvector());
1268 shape->setCurveInsync(std::move(c));
1269 shape->setAttribute("d", str);
1270 } else {
1271 // LPE was unsuccessful or doeffect stack return null.. Read the old 'd'-attribute.
1272 if (gchar const * value = shape->getAttribute("d")) {
1273 shape->setCurve(SPCurve(sp_svg_read_pathv(value)));
1274 }
1275 }
1276 shape->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1277 }
1278 }
1279 }
1280}
1281
1283{
1284 return const_cast<Inkscape::LivePathEffect::Effect *>(
1285 std::as_const(*this).getFirstPathEffectOfType(type));
1286}
1287
1289{
1290 for (const auto & i : *path_effect_list) {
1291 LivePathEffectObject const *lpeobj = i->lpeobject;
1292 if (lpeobj) {
1293 Inkscape::LivePathEffect::Effect const *lpe = lpeobj->get_lpe();
1294 if (lpe && (lpe->effectType() == type)) {
1295 return lpe;
1296 }
1297 }
1298 }
1299 return nullptr;
1300}
1301
1302template <bool as_const>
1303auto getPathEffectsOfTypeImpl(PathEffectList const &path_effect_list, std::optional<int> const type)
1304{
1306 using Ptr = std::add_pointer_t<std::conditional_t<as_const, std::add_const_t<Effect>, Effect>>;
1307 std::vector<Ptr> effects;
1308 if (!type) effects.reserve(path_effect_list.size());
1309 for (auto const &lperef : path_effect_list) {
1310 if (auto const lpeobj = lperef->lpeobject) {
1311 if (auto const lpe = lpeobj->get_lpe(); lpe && (!type || lpe->effectType() == *type)) {
1312 effects.push_back(lpe);
1313 }
1314 }
1315 }
1316 return effects;
1317}
1318
1319// TODO: Do these really need to copy the member vector to a new one? Why? And we should not shadow
1320
1321std::vector<Inkscape::LivePathEffect::Effect *> SPLPEItem::getPathEffectsOfType(int type)
1322{
1324 return getPathEffectsOfTypeImpl<false>(path_effect_list, type);
1325}
1326
1327std::vector<Inkscape::LivePathEffect::Effect const *> SPLPEItem::getPathEffectsOfType(int type) const
1328{
1330 return getPathEffectsOfTypeImpl<true>(path_effect_list, type);
1331}
1332
1333std::vector<Inkscape::LivePathEffect::Effect *> SPLPEItem::getPathEffects()
1334{
1336 return getPathEffectsOfTypeImpl<false>(path_effect_list, std::nullopt);
1337}
1338
1339std::vector<Inkscape::LivePathEffect::Effect const *> SPLPEItem::getPathEffects() const
1340{
1342 return getPathEffectsOfTypeImpl<true>(path_effect_list, std::nullopt);
1343}
1344
1346{
1347 auto const lperef = this->getCurrentLPEReference();
1348 if (lperef && lperef->lpeobject && lperef->lpeobject->get_lpe()) {
1349 lperef->lpeobject->get_lpe()->editNextParamOncanvas(this, dt);
1350 }
1351}
1352
1355
1356 if (this->hasPathEffectRecursive()) {
1357 SPObject *ochild = this->get_child_by_repr(child);
1358
1359 if ( ochild && is<SPLPEItem>(ochild) ) {
1360 sp_lpe_item_create_original_path_recursive(cast<SPLPEItem>(ochild));
1361 }
1362 }
1363}
1365 SPObject *ochild = this->get_child_by_repr(child);
1366 if (ochild && is<SPLPEItem>(ochild) && cast<SPLPEItem>(ochild)->hasPathEffectRecursive()) {
1367 // we not need to update item because keep paths is false
1368 sp_lpe_item_cleanup_original_path_recursive(cast<SPLPEItem>(ochild), false);
1369 }
1370
1372}
1373
1374static std::string patheffectlist_svg_string(PathEffectList const & list)
1375{
1376 HRefList hreflist;
1377
1378 for (auto const &it : list) {
1379 hreflist.emplace_back(it->lpeobject_href);
1380 }
1381
1382 return hreflist_svg_string(hreflist);
1383}
1384
1392static std::string hreflist_svg_string(HRefList const & list)
1393{
1394 std::string r;
1395 bool semicolon_first = false;
1396
1397 for (auto const &it : list) {
1398 if (semicolon_first) {
1399 r += ';';
1400 }
1401
1402 semicolon_first = true;
1403
1404 r += it;
1405 }
1406
1407 return r;
1408}
1409
1410// Return a copy of the effect list
1415
1416// Return a copy of the effect list
1418{
1419 return *path_effect_list;
1420}
1421
1424{
1425 PathEffectSharedPtr prev = nullptr;
1426 for (auto & it : *path_effect_list) {
1427 if (it->lpeobject_repr == lperef->lpeobject_repr) {
1428 break;
1429 }
1430 prev = it;
1431 }
1432 return prev;
1433}
1434
1437{
1438 bool match = false;
1439 for (auto & it : *path_effect_list) {
1440 if (match) {
1441 return it;
1442 }
1443 if (it->lpeobject_repr == lperef->lpeobject_repr) {
1444 match = true;
1445 }
1446 }
1447 return nullptr;
1448}
1449
1452{
1453 return path_effect_list->back();
1454}
1455
1456std::size_t
1458{
1459 std::size_t counter = 0;
1460 for (auto & it : *path_effect_list) {
1461 if (it->lpeobject_repr == lperef->lpeobject_repr) {
1462 return counter;
1463 }
1464 counter++;
1465 }
1466 return Glib::ustring::npos;
1467}
1468
1470{
1471 if (!this->current_path_effect && !this->path_effect_list->empty()) {
1473 }
1474 if (this->path_effect_list->empty()) {
1475 current_path_effect = nullptr;
1476 }
1477 return current_path_effect;
1478}
1479
1481{
1482 auto const lperef = getCurrentLPEReference();
1483
1484 if (lperef && lperef->lpeobject)
1485 return lperef->lpeobject->get_lpe();
1486 else
1487 return nullptr;
1488}
1489
1491{
1492 Inkscape::LivePathEffect::Effect* prev = nullptr;
1493 for (auto & it : *path_effect_list) {
1494 if (it->lpeobject == lpe->getLPEObj()) {
1495 break;
1496 }
1497 prev = it->lpeobject->get_lpe();
1498 }
1499 return prev;
1500}
1501
1503{
1504 bool match = false;
1505 for (auto & it : *path_effect_list) {
1506 if (match) {
1507 return it->lpeobject->get_lpe();
1508 }
1509 if (it->lpeobject == lpe->getLPEObj()) {
1510 match = true;
1511 }
1512 }
1513 return nullptr;
1514}
1515
1517{
1518 Inkscape::LivePathEffect::Effect* last = nullptr;
1519 for (auto & it : *path_effect_list) {
1520 last = it->lpeobject->get_lpe();
1521 }
1522 return last;
1523}
1524
1525std::size_t
1526SPLPEItem::countLPEOfType(int const type, bool const inc_hidden, bool const is_ready) const
1527{
1528 std::size_t counter = 0;
1529 if (path_effect_list->empty()) {
1530 return counter;
1531 }
1532
1533 for (auto const &it : *path_effect_list) {
1534 auto const lpeobj = it->lpeobject;
1535 if (lpeobj) {
1536 Inkscape::LivePathEffect::Effect const* lpe = lpeobj->get_lpe();
1537 if (lpe && (lpe->effectType() == type) && (lpe->is_visible || inc_hidden)) {
1538 if (is_ready || lpe->isReady()) {
1539 counter++;
1540 }
1541 }
1542 }
1543 }
1544
1545 return counter;
1546}
1547
1548std::size_t
1550{
1551 std::size_t counter = 0;
1552 for (auto & it : *path_effect_list) {
1553 if (it->lpeobject == lpe->getLPEObj()) {
1554 return counter;
1555 }
1556 counter++;
1557 }
1558 return Glib::ustring::npos;
1559}
1560
1562{
1563 for (auto & it : *path_effect_list) {
1564 if (it->lpeobject_repr == lperef->lpeobject_repr) {
1565 this->current_path_effect = it; // current_path_effect should always be a pointer from the path_effect_list !
1566 return true;
1567 }
1568 }
1569
1570 return false;
1571}
1572
1574{
1575 for (auto & it : *path_effect_list) {
1576 if (it->lpeobject_repr == lopeobj->getRepr()) {
1577 this->current_path_effect = it; // current_path_effect should always be a pointer from the path_effect_list !
1578 return true;
1579 }
1580 }
1581
1582 return false;
1583}
1584
1585std::vector<SPObject *> SPLPEItem::get_satellites(bool force, bool recursive, bool onchilds)
1586{
1587 std::vector<SPObject *> satellites;
1588 if (onchilds) {
1589 auto group = cast<SPGroup>(this);
1590 if (group) {
1591 std::vector<SPItem*> item_list = group->item_list();
1592 for (auto child:item_list) {
1593 auto lpechild = cast<SPLPEItem>(child);
1594 if (lpechild) {
1595 std::vector<SPObject *> tmp = lpechild->get_satellites(force, recursive);
1596 satellites.insert( satellites.end(), tmp.begin(), tmp.end() );
1597 }
1598 }
1599 }
1600 }
1601 for (auto &it : *path_effect_list) {
1602 LivePathEffectObject *lpeobj = it->lpeobject;
1603 if (lpeobj) {
1605 if (lpe) {
1606 std::vector<SPObject *> tmp = lpe->effect_get_satellites(force);
1607 satellites.insert(satellites.begin(), tmp.begin(), tmp.end());
1608 }
1609 }
1610 }
1611 if (recursive) {
1612 std::vector<SPObject *> allsatellites;
1613 for (auto satellite : satellites) {
1614 SPLPEItem *lpeitem = nullptr;
1615 if ( satellite && ( lpeitem = cast<SPLPEItem>(satellite) )) {
1616 std::vector<SPObject *> tmp = lpeitem->get_satellites(force, recursive);
1617 allsatellites.insert(allsatellites.begin(), tmp.begin(), tmp.end());
1618 }
1619 }
1620 satellites.insert(satellites.begin(), allsatellites.begin(), allsatellites.end());
1621 }
1622 return satellites;
1623}
1624
1629void SPLPEItem::replacePathEffects( std::vector<LivePathEffectObject const *> const &old_lpeobjs,
1630 std::vector<LivePathEffectObject const *> const &new_lpeobjs )
1631{
1632 HRefList hreflist;
1633 for (auto const &it : *this->path_effect_list) {
1634 auto const current_lpeobj = it->lpeobject;
1635 auto const found_it = std::find(old_lpeobjs.cbegin(), old_lpeobjs.cend(), current_lpeobj);
1636 if (found_it != old_lpeobjs.cend()) {
1637 auto const found_index = std::distance(old_lpeobjs.cbegin(), found_it);
1638 const gchar * repr_id = new_lpeobjs[found_index]->getRepr()->attribute("id");
1639 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
1640 hreflist.emplace_back(hrefstr);
1641 g_free(hrefstr);
1642 } else {
1643 hreflist.emplace_back(it->lpeobject_href);
1644 }
1645 }
1646
1647 this->setAttributeOrRemoveIfEmpty("inkscape:path-effect", hreflist_svg_string(hreflist));
1648}
1649
1656bool SPLPEItem::forkPathEffectsIfNecessary(unsigned int nr_of_allowed_users, bool recursive, bool force)
1657{
1658 bool forked = false;
1659 auto group = cast<SPGroup>(this);
1660 if (group && recursive) {
1661 std::vector<SPItem*> item_list = group->item_list();
1662 for (auto child:item_list) {
1663 auto lpeitem = cast<SPLPEItem>(child);
1664 if (lpeitem && lpeitem->forkPathEffectsIfNecessary(nr_of_allowed_users, recursive)) {
1665 forked = true;
1666 }
1667 }
1668 }
1669
1670 if ( this->hasPathEffect() ) {
1671 // If one of the path effects is used by 2 or more items, fork it
1672 // so that each object has its own independent copy of the effect.
1673 // Note: replacing path effects messes up the path effect list
1674
1675 // Clones of the LPEItem will increase the refcount of the lpeobjects.
1676 // Therefore, nr_of_allowed_users should be increased with the number of clones (i.e. refs to the lpeitem)
1677 // is not well handled forker because is based in hrefcount
1678 // to handle clones and this can be wrong with other references
1679 // for this I add a new parameter to allow force fork
1680 nr_of_allowed_users += this->hrefcount;
1681 if (force) {
1682 nr_of_allowed_users = 1;
1683 }
1684 std::vector<LivePathEffectObject const*> old_lpeobjs, new_lpeobjs;
1685 std::vector<LivePathEffectObject *> upd_lpeobjs;
1686 PathEffectList effect_list = this->getEffectList();
1687 for (auto & it : effect_list)
1688 {
1689 LivePathEffectObject *lpeobj = it->lpeobject;
1690 if (lpeobj) {
1691 LivePathEffectObject *forked_lpeobj = lpeobj->fork_private_if_necessary(nr_of_allowed_users);
1692 if (forked_lpeobj && forked_lpeobj != lpeobj) {
1693 forked = true;
1694 forked_lpeobj->get_lpe()->is_load = true;
1695 forked_lpeobj->get_lpe()->sp_lpe_item = this;
1696 old_lpeobjs.push_back(lpeobj);
1697 new_lpeobjs.push_back(forked_lpeobj);
1698 upd_lpeobjs.push_back(forked_lpeobj);
1699 }
1700 }
1701 }
1702
1703 if (forked) {
1704 this->replacePathEffects(old_lpeobjs, new_lpeobjs);
1705 for (auto &forked_lpeobj : upd_lpeobjs) {
1706 forked_lpeobj->get_lpe()->read_from_SVG();
1707 }
1708 }
1709 }
1710
1711 return forked;
1712}
1713
1714// Enable or disable the path effects of the item.
1716{
1717 if (enable) {
1718 lpeitem->path_effects_enabled++;
1719 }
1720 else {
1721 lpeitem->path_effects_enabled--;
1722 }
1723}
1724
1725// Are the path effects enabled on this item ?
1727{
1728 return !onsymbol && path_effects_enabled > 0;
1729}
1730
1731/*
1732 Local Variables:
1733 mode:c++
1734 c-file-style:"stroustrup"
1735 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1736 indent-tabs-mode:nil
1737 fill-column:99
1738 End:
1739*/
1740// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Point origin
Definition aa.cpp:227
Lookup dictionary for attributes/properties.
SPAttr
Definition attributes.h:27
@ INKSCAPE_PATH_EFFECT
TODO: insert short description here.
3x3 matrix representing an affine transformation.
Definition affine.h:70
bool isUniformScale(Coord eps=EPSILON) const
Check whether this matrix represents pure uniform scaling.
Definition affine.cpp:174
void doBeforeEffect_impl(SPLPEItem const *lpeitem)
Definition effect.cpp:1550
virtual void resetDefaults(SPItem const *item)
Sets all parameters to their default values and writes them to SVG.
Definition effect.cpp:2014
void transform_multiply_impl(Geom::Affine const &postmul, SPLPEItem *)
Definition effect.cpp:1227
std::vector< SPObject * > effect_get_satellites(bool force=true)
Definition effect.cpp:1588
virtual bool getHolderRemove()
Definition effect.h:183
void doAfterEffect_impl(SPLPEItem const *lpeitem, SPCurve *curve)
Definition effect.cpp:1483
virtual void doOnException(SPLPEItem const *lpeitem)
Definition effect.cpp:1463
Geom::PathVector pathvector_before_effect
Definition effect.h:174
Glib::ustring getName() const
Definition effect.cpp:1173
EffectType effectType() const
Definition effect.cpp:1182
Geom::PathVector pathvector_after_effect
Definition effect.h:175
virtual void doEffect(SPCurve *curve)
Definition effect.cpp:1616
void doOnRemove_impl(SPLPEItem const *lpeitem)
Definition effect.cpp:1491
static int acceptsNumClicks(EffectType type)
Definition effect.cpp:917
void doOnApply_impl(SPLPEItem const *lpeitem)
Definition effect.cpp:1532
void setCurrentShape(SPShape *shape)
Definition effect.h:113
LivePathEffectObject * getLPEObj()
Definition effect.h:151
Glib::ustring param_getSVGValue() const override
Definition hidden.cpp:53
Preference storage class.
Definition preferences.h:66
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
static Preferences * get()
Access the singleton Preferences object.
Interface for refcounted XML nodes.
Definition node.h:80
virtual Node * parent()=0
Get the parent of this node.
virtual void setPosition(int pos)=0
Set the position of this node in parent's child order.
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 char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
void removeAttribute(Inkscape::Util::const_char_ptr key)
Remove an attribute of this node.
Definition node.h:280
virtual void removeChild(Node *child)=0
Remove a child of this node.
Inkscape::LivePathEffect::Effect * get_lpe()
Definition lpeobject.h:51
LivePathEffectObject * fork_private_if_necessary(unsigned int nr_of_allowed_users=1)
If this has other users, create a new private duplicate and return it returns 'this' when no forking ...
Wrapper around a Geom::PathVector object.
Definition curve.h:26
Geom::PathVector const & get_pathvector() const
Definition curve.cpp:52
To do: update description of desktop.
Definition desktop.h:149
Typed SVG document implementation.
Definition document.h:103
bool isSeeking() const
Definition document.h:361
SPRoot * getRoot()
Returns our SPRoot.
Definition document.h:202
Inkscape::XML::Node * getReprRoot()
Definition document.h:208
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
Definition document.h:213
SPObject * getObjectByRepr(Inkscape::XML::Node *repr) const
std::vector< SPItem * > item_list()
Base class for visual SVG elements.
Definition sp-item.h:109
void update(SPCtx *ctx, unsigned int flags) override
Definition sp-item.cpp:787
Inkscape::XML::Node * write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, unsigned int flags) override
Definition sp-item.cpp:858
void set(SPAttr key, char const *value) override
Definition sp-item.cpp:566
SPMask * getMaskObject() const
Definition sp-item.cpp:177
void release() override
Definition sp-item.cpp:542
Geom::Affine transform
Definition sp-item.h:138
bool unoptimized()
Definition sp-item.cpp:1652
void build(SPDocument *document, Inkscape::XML::Node *repr) override
Definition sp-item.cpp:519
SPClipPath * getClipObject() const
Definition sp-item.cpp:102
bool onsymbol
bool isOnClipboard()
The lpeitem is on clipboard.
void remove_child(Inkscape::XML::Node *child) override
std::vector< Inkscape::LivePathEffect::Effect * > getPathEffectsOfType(int type)
void notifyTransform(Geom::Affine const &postmul)
notify tranbsform applied to a LPE
void release() override
void duplicateCurrentPathEffect()
void downCurrentPathEffect()
Inkscape::LivePathEffect::Effect * getCurrentLPE()
void resetClipPathAndMaskLPE(bool fromrecurse=false)
PathEffectSharedPtr getNextLPEReference(PathEffectSharedPtr const &lperef)
std::vector< Inkscape::LivePathEffect::Effect * > getPathEffects()
void applyToMask(SPItem *to, Inkscape::LivePathEffect::Effect *lpe=nullptr)
void upCurrentPathEffect()
bool performOnePathEffect(SPCurve *curve, SPShape *current, Inkscape::LivePathEffect::Effect *lpe, bool is_clip_or_mask=false)
returns true when LPE was successful.
bool hasBrokenPathEffect() const
used for shapes so they can see if they should also disable shape calculation and read from d=
Inkscape::LivePathEffect::Effect * getFirstPathEffectOfType(int type)
bool hasPathEffect() const
void applyToClipPath(SPItem *to, Inkscape::LivePathEffect::Effect *lpe=nullptr)
SPLPEItem const * getTopPathEffect() const
returns top most LPE item with LPE
PathEffectSharedPtr current_path_effect
Definition sp-lpe-item.h:63
void movePathEffect(gint origin, gint dest, bool select_moved=false)
PathEffectSharedPtr getLastLPEReference()
std::list< sigc::scoped_connection > lpe_modified_connection_list
Definition sp-lpe-item.h:50
std::vector< SPObject * > get_satellites(bool force=true, bool recursive=false, bool onchilds=false)
void replacePathEffects(std::vector< LivePathEffectObject const * > const &old_lpeobjs, std::vector< LivePathEffectObject const * > const &new_lpeobjs)
Writes a new "inkscape:path-effect" string to xml, where the old_lpeobjects are substituted by the ne...
SPLPEItem * removeCurrentPathEffect(bool keep_paths)
If keep_path is true, the item should not be updated, effectively 'flattening' the LPE.
void update_satellites(bool recursive=true)
bool hasPathEffectOnClipOrMaskRecursive(SPLPEItem *shape) const
returns true when any LPE apply to clip or mask.
std::size_t getLPEReferenceIndex(PathEffectSharedPtr const &lperef) const
std::size_t getLPEIndex(Inkscape::LivePathEffect::Effect *lpe) const
bool hasPathEffectOfTypeRecursive(int const type, bool is_ready=true) const
std::size_t countLPEOfType(int const type, bool inc_hidden=true, bool is_ready=true) const
PathEffectList getEffectList()
void child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref) override
bool hasPathEffectOfType(int const type, bool is_ready=true) const
SPLPEItem * removeAllPathEffects(bool keep_paths, bool recursive=false)
If keep_path is true, the item should not be updated, effectively 'flattening' the LPE.
SPLPEItem * flattenCurrentPathEffect()
void removePathEffect(Inkscape::LivePathEffect::Effect *lpe, bool keep_paths)
int path_effects_enabled
Definition sp-lpe-item.h:60
bool hasPathEffectRecursive() const
void addPathEffect(std::string value, bool reset)
void applyToClipPathOrMask(SPItem *clip_mask, SPItem *to, Inkscape::LivePathEffect::Effect *lpe=nullptr)
Inkscape::LivePathEffect::Effect * getLastLPE()
bool pathEffectsEnabled() const
virtual void update_patheffect(bool write)
PathEffectList * path_effect_list
Definition sp-lpe-item.h:62
void update(SPCtx *ctx, unsigned int flags) override
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.
void build(SPDocument *doc, Inkscape::XML::Node *repr) override
bool performPathEffect(SPCurve *curve, SPShape *current, bool is_clip_or_mask=false)
returns true when LPE was successful.
void modified(unsigned int flags) override
bool hasPathEffectOnClipOrMask(SPLPEItem *shape) const
returns true when any LPE apply to clip or mask.
PathEffectSharedPtr getCurrentLPEReference()
~SPLPEItem() override
Inkscape::XML::Node * write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, unsigned int flags) override
PathEffectSharedPtr getPrevLPEReference(PathEffectSharedPtr const &lperef)
Inkscape::LivePathEffect::Effect * getPrevLPE(Inkscape::LivePathEffect::Effect *lpe)
bool optimizeTransforms()
returns false when LPE write unoptimiced
bool isOnSymbol() const
void editNextParamOncanvas(SPDesktop *dt)
void set(SPAttr key, char const *value) override
Inkscape::LivePathEffect::Effect * getNextLPE(Inkscape::LivePathEffect::Effect *lpe)
bool setCurrentPathEffect(PathEffectSharedPtr const &lperef)
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
Inkscape::XML::Node * repr
Definition sp-object.h:193
void setAttributeOrRemoveIfEmpty(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
Definition sp-object.h:785
void removeAttribute(char const *key)
bool setDesc(char const *desc, bool verbatim=false)
Sets the description of this object.
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 * get_child_by_repr(Inkscape::XML::Node *repr)
Return object's child whose node pointer equals repr.
SPDocument * document
Definition sp-object.h:188
virtual void remove_child(Inkscape::XML::Node *child)
bool setTitle(char const *title, bool verbatim=false)
Sets the title of this object.
SPObject * parent
Definition sp-object.h:189
void readAttr(char const *key)
Read value of key attribute from XML node into object.
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.
virtual void child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
char const * getAttribute(char const *name) const
std::list< SPObject * > hrefList
Definition sp-object.h:197
unsigned int hrefcount
Definition sp-object.h:186
<svg> element
Definition sp-root.h:33
Base class for shapes, including <path> element.
Definition sp-shape.h:38
RootCluster root
double c[8][4]
Editable view implementation.
static char const *const current
Definition dir-util.cpp:71
static char const *const parent
Definition dir-util.cpp:70
LPE <lattice2> implementation, see lpe-lattice2.cpp.
Raw stack of active status messages.
static R & release(R &r)
Decrements the reference count of a anchored object.
@ WARNING_MESSAGE
Definition message.h:28
static cairo_user_data_key_t key
static gint counter
Definition box3d.cpp:39
Singleton class to access the preferences file in a convenient way.
Ocnode * child[8]
Definition quantize.cpp:33
Ocnode ** ref
Definition quantize.cpp:32
Inkscape::XML::Node const * sp_repr_lookup_name(Inkscape::XML::Node const *repr, gchar const *name, gint maxdepth)
static SPLPEItem * sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem, bool keep_paths, bool force=false, bool is_clip_mask=false)
std::list< std::string > HRefList
void sp_lpe_item_update_patheffect(SPLPEItem *lpeitem, bool wholetree, bool write, bool with_satellites)
Calls any registered handlers for the update_patheffect action.
static void sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem)
void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable)
static void lpeobject_ref_modified(SPObject *href, guint flags, SPLPEItem *lpeitem)
Gets called when any of the lpestack's lpeobject repr contents change: i.e.
auto getPathEffectsOfTypeImpl(PathEffectList const &path_effect_list, std::optional< int > const type)
static std::string hreflist_svg_string(HRefList const &list)
THE function that should be used to generate any patheffectlist string.
static std::string patheffectlist_svg_string(PathEffectList const &list)
std::list< PathEffectSharedPtr > PathEffectList
Definition sp-lpe-item.h:46
void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable)
void sp_lpe_item_update_patheffect(SPLPEItem *lpeitem, bool wholetree, bool write, bool with_satellites=false)
Calls any registered handlers for the update_patheffect action.
std::shared_ptr< Inkscape::LivePathEffect::LPEObjectReference > PathEffectSharedPtr
Definition sp-lpe-item.h:45
SPObject * sp_object_unref(SPObject *object, SPObject *owner)
Decrease reference count of object, with possible debugging and finalization.
SPObject * sp_object_ref(SPObject *object, SPObject *owner)
Increase reference count of object, with possible debugging.
SPRoot: SVG <svg> implementation.
Interface for XML documents.
Definition document.h:43
virtual Node * createElement(char const *name)=0
Unused.
Definition sp-object.h:94
Definition curve.h:24
Geom::PathVector sp_svg_read_pathv(char const *str)
Definition svg-path.cpp:37
static void sp_svg_write_path(Inkscape::SVG::PathString &str, Geom::Path const &p, bool normalize=false)
Definition svg-path.cpp:109