diff --git a/src/extension/internal/cairo-renderer.cpp b/src/extension/internal/cairo-renderer.cpp index c84ab6e3c79428a243ca73a249335efdc908f2d9..b01eb7ccbc9ca4d09681dd3c6e45ad4e6ebba17e 100644 --- a/src/extension/internal/cairo-renderer.cpp +++ b/src/extension/internal/cairo-renderer.cpp @@ -545,7 +545,12 @@ static void sp_item_invoke_render(SPItem *item, CairoRenderContext *ctx) return; } - if(ctx->getFilterToBitmap() && (item->style->filter.set != 0)) { + if (ctx->getFilterToBitmap() && (!item->style || item->style->filter.set != 0)) { + // This is not necesary but for cleanup, the filter hide the item + SPFilter *filt = item->style->getFilter(); + if (filt && g_strcmp0(filt->getId(), "selectable_hidder_filter") == 0) { + return; + } return sp_asbitmap_render(item, ctx); } diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index 86eda6984c678364340330e39e2386821d354ce2..5816a628555629e0cd745d92534ddd0934ade61d 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -1148,6 +1148,34 @@ void Effect::doOnApply (SPLPEItem const*/*lpeitem*/) { } +/** + * Is performed on forked to allow pass volatile data from original LPE to forked one + */ +void Effect::doOnFork(Effect const * /*forkedeffect*/) {} + +/** + * Is performed on stamp selection + */ +void Effect::doOnStamp(SPLPEItem const * /*lpeitem*/) {} +/** + * Is performed a single time previously the item become copied (see bool LPE to add element to selection) + */ +void Effect::doOnCopy(SPLPEItem const * /*lpeitem*/, Inkscape::ObjectSet *set) {} + +/** + * Is performed a single time previously the item become cut (see bool LPE to add element to selection) + */ +void Effect::doOnCut(SPLPEItem const * /*lpeitem*/) {} + +/** + * Is performed a single time previously the item become pasted (see bool LPE to add element to selection) + */ +void Effect::doOnPaste(SPLPEItem const * /*lpeitem*/) {} + +/** + * Is performed a single time when the item become dupled + */ +void Effect::doOnDuple(SPLPEItem const * /*lpeitem*/) {} void Effect::setCurrentZoom(double cZ) @@ -1166,6 +1194,20 @@ Effect::setSelectedNodePoints(std::vector sNP) selectedNodesPoints = sNP; } +/** + * The lpe is on clipboard + */ +bool Effect::isOnClipboard() +{ + SPDocument *document = getSPDoc(); + if (!document) { + return false; + } + Inkscape::XML::Node *root = document->getReprRoot(); + Inkscape::XML::Node *clipnode = sp_repr_lookup_name(root, "inkscape:clipboard", 1); + return clipnode != nullptr; +} + bool Effect::isNodePointSelected(Geom::Point const &nodePoint) const { diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h index f631646a79740ee6daaa3ca1f8c73afc0da22284..d8610e3ead59a2de5caeb236ba7b6d261b80da49 100644 --- a/src/live_effects/effect.h +++ b/src/live_effects/effect.h @@ -74,7 +74,14 @@ public: void setCurrentZoom(double cZ); void setSelectedNodePoints(std::vector sNP); bool isNodePointSelected(Geom::Point const &nodePoint) const; + bool isOnClipboard(); virtual void doOnApply (SPLPEItem const* lpeitem); + virtual void doOnCopy(SPLPEItem const *lpeitem, Inkscape::ObjectSet *set); + virtual void doOnCut(SPLPEItem const *lpeitem); + virtual void doOnPaste(SPLPEItem const *lpeitem); + virtual void doOnDuple(SPLPEItem const *lpeitem); + virtual void doOnStamp(SPLPEItem const *lpeitem); + virtual void doOnFork(Effect const *forkedeffect); virtual void doBeforeEffect (SPLPEItem const* lpeitem); virtual void transform_multiply(Geom::Affine const &postmul, bool set); virtual void doAfterEffect (SPLPEItem const* lpeitem); diff --git a/src/live_effects/lpe-bool.cpp b/src/live_effects/lpe-bool.cpp index 7681ea9ef96f9ba3be3500e39e946310de28bda7..ff077681c49acb89e3f2a0683b101fbbb97a9dbd 100644 --- a/src/live_effects/lpe-bool.cpp +++ b/src/live_effects/lpe-bool.cpp @@ -7,12 +7,12 @@ * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ -#include +#include "live_effects/lpe-bool.h" + +#include #include #include -#include - -#include "live_effects/lpe-bool.h" +#include #include "2geom/affine.h" #include "2geom/bezier-curve.h" @@ -20,17 +20,23 @@ #include "2geom/path.h" #include "2geom/svg-path-parser.h" #include "display/curve.h" -#include "object/sp-shape.h" -#include "svg/svg.h" - #include "helper/geom.h" - +#include "inkscape.h" +#include "selection-chemistry.h" #include "livarot/Path.h" #include "livarot/Shape.h" #include "livarot/path-description.h" - +#include "live_effects/lpeobject.h" +#include "object/sp-clippath.h" +#include "object/sp-defs.h" +#include "object/sp-shape.h" +#include "object/sp-text.h" #include "path/path-boolop.h" #include "path/path-util.h" +#include "snap.h" +#include "style.h" +#include "svg/svg.h" +#include "ui/tools/tool-base.h" namespace Inkscape { namespace LivePathEffect { @@ -38,18 +44,17 @@ namespace LivePathEffect { // Define an extended boolean operation type static const Util::EnumData BoolOpData[LPEBool::bool_op_ex_count] = { - { LPEBool::bool_op_ex_union, N_("union"), "union" }, - { LPEBool::bool_op_ex_inters, N_("intersection"), "inters" }, - { LPEBool::bool_op_ex_diff, N_("difference"), "diff" }, - { LPEBool::bool_op_ex_symdiff, N_("symmetric difference"), "symdiff" }, - { LPEBool::bool_op_ex_cut, N_("division"), "cut" }, + {LPEBool::bool_op_ex_union, N_("union"), "union"}, + {LPEBool::bool_op_ex_inters, N_("intersection"), "inters"}, + {LPEBool::bool_op_ex_diff, N_("difference"), "diff"}, + {LPEBool::bool_op_ex_symdiff, N_("symmetric difference"), "symdiff"}, + {LPEBool::bool_op_ex_cut, N_("division"), "cut"}, // Note on naming of operations: // bool_op_cut is called "Division" in the manu, see sp_selected_path_cut // bool_op_slice is called "Cut path" in the menu, see sp_selected_path_slice - { LPEBool::bool_op_ex_slice, N_("cut"), "slice" }, - // Comneted in 1.0 don't work properly - // { LPEBool::bool_op_ex_slice_inside, N_("cut inside"), "slice-inside" }, - // { LPEBool::bool_op_ex_slice_outside, N_("cut outside"), "slice-outside" }, + {LPEBool::bool_op_ex_slice, N_("cut"), "slice"}, + {LPEBool::bool_op_ex_slice_inside, N_("cut inside"), "slice-inside"}, + {LPEBool::bool_op_ex_slice_outside, N_("cut outside"), "slice-outside"}, }; static const Util::EnumDataConverter BoolOpConverter(BoolOpData, sizeof(BoolOpData) / sizeof(*BoolOpData)); @@ -68,7 +73,6 @@ LPEBool::LPEBool(LivePathEffectObject *lpeobject) , operand_path(_("Operand path:"), _("Operand for the boolean operation"), "operand-path", &wr, this) , bool_operation(_("Operation:"), _("Boolean Operation"), "operation", BoolOpConverter, &wr, this, bool_op_ex_union) , swap_operands(_("Swap operands"), _("Swap operands (useful e.g. for difference)"), "swap-operands", &wr, this) - , hide_linked(_("Hide Linked"), _("Hide linked path"), "hide-linked", &wr, this, true) , rmv_inner( _("Remove inner"), _("For cut operations: remove inner (non-contour) lines of cutting path to avoid invisible extra points"), @@ -77,21 +81,29 @@ LPEBool::LPEBool(LivePathEffectObject *lpeobject) FillTypeConverter, &wr, this, fill_justDont) , fill_type_operand(_("Fill type operand:"), _("Fill type (winding mode) for operand path"), "filltype-operand", FillTypeConverter, &wr, this, fill_justDont) + , filter("Filter", "Previous filter", "filter", &wr, this, "", true) { registerParameter(&operand_path); registerParameter(&bool_operation); registerParameter(&swap_operands); - registerParameter(&hide_linked); registerParameter(&rmv_inner); registerParameter(&fill_type_this); + registerParameter(&filter); registerParameter(&fill_type_operand); show_orig_path = true; + extra_selected = false; + fork_duple = false; + fork_copy = false; + fork_stamp = false; + is_load = true; + prev_affine = Geom::identity(); operand = dynamic_cast(operand_path.getObject()); } LPEBool::~LPEBool() -= default; - +{ + doOnRemove(nullptr); +} bool cmp_cut_position(const Path::cut_position &a, const Path::cut_position &b) { return a.piece == b.piece ? a.t < b.t : a.piece < b.piece; @@ -352,62 +364,326 @@ static fill_typ GetFillTyp(SPItem *item) } } +void LPEBool::add_filter() +{ + if (operand) { + Inkscape::XML::Node *repr = operand->getRepr(); + if (!repr) { + return; + } + SPFilter *filt = operand->style->getFilter(); + if (filt && filt->getId() && strcmp(filt->getId(), "selectable_hidder_filter") != 0) { + filter.param_setValue(filt->getId(), true); + } + if (!filt || (filt->getId() && strcmp(filt->getId(), "selectable_hidder_filter") != 0)) { + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_set_property(css, "filter", "url(#selectable_hidder_filter)"); + sp_repr_css_change(repr, css, "style"); + sp_repr_css_attr_unref(css); + } + } +} + +void LPEBool::remove_filter() +{ + if (operand) { + Inkscape::XML::Node *repr = operand->getRepr(); + if (!repr) { + return; + } + SPFilter *filt = operand->style->getFilter(); + if (filt && (filt->getId() && strcmp(filt->getId(), "selectable_hidder_filter") == 0)) { + SPCSSAttr *css = sp_repr_css_attr_new(); + Glib::ustring filtstr = filter.param_getSVGValue(); + if (filtstr != "") { + Glib::ustring url = "url(#"; + url += filtstr; + url += ")"; + sp_repr_css_set_property(css, "filter", url.c_str()); + // blur is removed when no item using it + /*SPDocument *document = getSPDoc(); + SPObject * filterobj = nullptr; + if((filterobj = document->getObjectById(filtstr))) { + for (auto obj:filterobj->childList(false)) { + if (obj) { + obj->deleteObject(false); + break; + } + } + } */ + filter.param_setValue(""); + } else { + sp_repr_css_unset_property(css, "filter"); + } + sp_repr_css_change(repr, css, "style"); + sp_repr_css_attr_unref(css); + } + } +} + void LPEBool::doBeforeEffect(SPLPEItem const *lpeitem) { - // operand->set_transform(i2anc_affine(sp_lpe_item, sp_lpe_item->parent)); SPDocument *document = getSPDoc(); if (!document) { return; } + _hp.clear(); Inkscape::XML::Document *xml_doc = document->getReprDoc(); + SPObject *elemref = nullptr; + Inkscape::XML::Node *boolfilter = nullptr; + if (!(elemref = document->getObjectById("selectable_hidder_filter"))) { + boolfilter = xml_doc->createElement("svg:filter"); + boolfilter->setAttribute("id", "selectable_hidder_filter"); + boolfilter->setAttribute("width", "1"); + boolfilter->setAttribute("height", "1"); + boolfilter->setAttribute("x", "0"); + boolfilter->setAttribute("y", "0"); + boolfilter->setAttribute("style", "color-interpolation-filters:sRGB;"); + boolfilter->setAttribute("inkscape:label", "LPE boolean visibility"); + /* Create */ + Inkscape::XML::Node *primitive = xml_doc->createElement("svg:feComposite"); + primitive->setAttribute("id", "boolops_hidder_primitive"); + primitive->setAttribute("result", "composite1"); + primitive->setAttribute("operator", "arithmetic"); + primitive->setAttribute("in2", "SourceGraphic"); + primitive->setAttribute("in", "BackgroundImage"); + Inkscape::XML::Node *defs = document->getDefs()->getRepr(); + defs->addChild(boolfilter, nullptr); + Inkscape::GC::release(boolfilter); + boolfilter->addChild(primitive, nullptr); + Inkscape::GC::release(primitive); + } else { + for (auto obj : elemref->childList(false)) { + if (obj && strcmp(obj->getId(), "boolops_hidder_primitive") != 0) { + obj->deleteObject(true); + } + } + } SPItem *current_operand = dynamic_cast(operand_path.getObject()); + SPLPEItem *current_operand_lpeitem = dynamic_cast(operand_path.getObject()); + if (current_operand_lpeitem && + current_operand_lpeitem->hasPathEffectOfType(Inkscape::LivePathEffect::EffectType::BOOL_OP)) { + operand_path.remove_link(); + operand = nullptr; + current_operand = nullptr; + } + if (current_operand && current_operand->getId()) { + if (!(document->getObjectById(current_operand->getId()))) { + operand_path.remove_link(); + operand = nullptr; + current_operand = nullptr; + } + } + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (desktop && + operand && + sp_lpe_item && + desktop->getSelection()->includes(operand) && + desktop->getSelection()->includes(sp_lpe_item)) + { + desktop->getSelection()->remove(operand); + } + if (desktop && operand && fork_duple) { + fork_duple = false; + operand_path.param_fork(); + } + if (desktop && operand && !isOnClipboard() && fork_copy) { + if (extra_selected) { + desktop->selection->remove(operand); + extra_selected = false; + } + fork_copy = false; + } if (!current_operand) { if (operand) { - operand->setHidden(false); + remove_filter(); } operand = nullptr; } + if (current_operand && operand != current_operand) { if (operand) { - operand->setHidden(false); + remove_filter(); } operand = current_operand; + remove_filter(); + if (is_load && sp_lpe_item) { + sp_lpe_item_update_patheffect(sp_lpe_item, true, true); + } + } + if (operand) { + if (is_visible) { + add_filter(); + } else { + remove_filter(); + } + } +} + +void LPEBool::doOnFork(LivePathEffect::Effect const *forkedeffect) +{ + LivePathEffect::Effect *effect = const_cast(forkedeffect); + LPEBool *original = dynamic_cast(effect); + if (original) { + operand = original->operand; + sp_lpe_item = original->sp_lpe_item; + if (original->fork_stamp) { + operand_path.param_fork(); + original->fork_stamp = false; + fork_stamp = true; + } + } +} + +void LPEBool::doOnCopy(SPLPEItem const *lpeitem, Inkscape::ObjectSet *set) +{ + SPDocument *document = getSPDoc(); + if (!document) { + return; } - - if (operand && operand->isHidden() != hide_linked) { - operand->setHidden(hide_linked); - } - - if (operand && operand->parent && sp_lpe_item && sp_lpe_item->parent != operand->parent) { - // TODO: reposition new operand on doOnRemove if keep_paths is false - Inkscape::XML::Node *copy = operand->getRepr()->duplicate(xml_doc); - SPItem *relocated_operand = dynamic_cast(sp_lpe_item->parent->appendChildRepr(copy)); - Inkscape::GC::release(copy); - operand->deleteObject(); - operand = relocated_operand; - if (!g_strcmp0(operand->getRepr()->attribute("sodipodi:type"), "inkscape:box3dside")) { - operand->getRepr()->removeAttribute("sodipodi:type"); - if (operand->getRepr()->attribute("inkscape:box3dsidetype")) { - operand->getRepr()->removeAttribute("inkscape:box3dsidetype"); + fork_copy = true; + if (!isOnClipboard()) { + if (operand) { + if (!set->includes(operand)) { + set->add(operand); + extra_selected = true; } } - Glib::ustring itemid = operand->getId(); - operand_path.linkitem(itemid); } } +void LPEBool::doOnCut(SPLPEItem const *lpeitem) {} + +void LPEBool::doOnPaste(SPLPEItem const *lpeitem) +{ + if (isOnClipboard() && sp_lpe_item) { + operand = dynamic_cast(operand_path.getObject()); + if (operand) { + gchar *tmpid = sp_object_get_unique_id(operand, nullptr); + operand->set(SPAttr::ID, tmpid); + operand->updateRepr(); + Glib::ustring newid = tmpid; + operand_path.linkitem(newid); + + const gchar *patheffectlist = sp_lpe_item->getRepr()->attribute("inkscape:path-effect"); + const gchar *lpeobjid = this->getLPEObj()->getId(); + if (lpeobjid && patheffectlist) { + gchar *oldid = g_strdup_printf("#%s", lpeobjid); + gchar **lpeeffects = g_strsplit(patheffectlist, ";", -1); + Glib::ustring newpatheffectlist; + if (lpeeffects) { + tmpid = sp_object_get_unique_id(this->getLPEObj(), nullptr); + this->getLPEObj()->getRepr()->setAttribute("id", tmpid); + Glib::ustring newid = "#"; + newid += tmpid; + unsigned int i = 0; + while (lpeeffects[i]) { + if (g_strcmp0(lpeeffects[i], oldid) != 0) { + if (!newpatheffectlist.empty()) { + newpatheffectlist += ";"; + } + newpatheffectlist += lpeeffects[i]; + } + i++; + } + if (!newpatheffectlist.empty()) { + newpatheffectlist += ";"; + } + newpatheffectlist += newid; + g_strfreev(lpeeffects); + } + sp_lpe_item->getRepr()->setAttribute("inkscape:path-effect", newpatheffectlist.c_str()); + g_free(oldid); + } + g_free(tmpid); + } + } +} + +void LPEBool::doOnDuple(SPLPEItem const *lpeitem) +{ + fork_duple = true; +} + +void LPEBool::doOnStamp(SPLPEItem const *lpeitem) +{ + fork_stamp = true; +} + void LPEBool::transform_multiply(Geom::Affine const &postmul, bool /*set*/) { - if (operand) { - operand->transform *= sp_item_transform_repr(sp_lpe_item).inverse() * postmul; - if (is_visible) { - operand->setHidden(hide_linked); - } else { - operand->setHidden(false); + if (operand && !isOnClipboard()) { + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (desktop && !desktop->getSelection()->includes(operand)) { + prev_affine = operand->transform * sp_item_transform_repr(sp_lpe_item).inverse() * postmul; + operand->doWriteTransform(prev_affine); } } } +Geom::PathVector LPEBool::get_union(SPObject *object) +{ + Geom::PathVector res; + Geom::PathVector clippv; + SPItem *objitem = dynamic_cast(object); + if (objitem) { + SPObject *clip_path = objitem->getClipObject(); + if (clip_path) { + std::vector clip_path_list = clip_path->childList(true); + if (clip_path_list.size()) { + for (auto clip : clip_path_list) { + SPShape *clipshape = dynamic_cast(clip); + if (clipshape) { + clippv = clipshape->curve()->get_pathvector(); + } + } + } + } + } + SPGroup *group = dynamic_cast(object); + if (group) { + std::vector item_list = sp_item_group_item_list(group); + for (auto iter : item_list) { + if (res.empty()) { + res = get_union(SP_OBJECT(iter)); + } else { + res = sp_pathvector_boolop(res, get_union(SP_OBJECT(iter)), to_bool_op(bool_op_ex_union), fill_oddEven, + fill_oddEven); + } + } + } + SPShape *shape = dynamic_cast(object); + if (shape) { + fill_typ originfill = fill_oddEven; + SPCurve *curve = shape->curve(); + if (curve) { + if (res.empty()) { + res = curve->get_pathvector(); + } else { + res = sp_pathvector_boolop(res, curve->get_pathvector(), to_bool_op(bool_op_ex_union), originfill, + GetFillTyp(shape)); + } + } + originfill = GetFillTyp(shape); + } + SPText *text = dynamic_cast(object); + if (text) { + std::unique_ptr curve = text->getNormalizedBpath(); + if (curve) { + if (res.empty()) { + res = curve->get_pathvector(); + } else { + res = sp_pathvector_boolop(res, curve->get_pathvector(), to_bool_op(bool_op_ex_union), fill_oddEven, + fill_oddEven); + } + } + } + if (!clippv.empty()) { + res = sp_pathvector_boolop(res, clippv, to_bool_op(bool_op_ex_inters), fill_oddEven, fill_oddEven); + } + return res; +} + void LPEBool::doEffect(SPCurve *curve) { Geom::PathVector path_in = curve->get_pathvector(); @@ -416,15 +692,13 @@ void LPEBool::doEffect(SPCurve *curve) operand_path.param_set_default(); return; } - if (operand_path.getObject() && operand_path.linksToPath() && operand) { - + if (operand_path.getObject() && operand) { bool_op_ex op = bool_operation.get_value(); bool swap = swap_operands.get_value(); - Geom::Affine current_affine = sp_item_transform_repr(sp_lpe_item); - Geom::Affine operand_affine = sp_item_transform_repr(operand); - - Geom::PathVector operand_pv = operand_path.get_pathvector(); + Geom::Affine current_affine = sp_lpe_item->transform; + Geom::Affine operand_affine = operand->transform; + Geom::PathVector operand_pv = get_union(operand); if (operand_pv.empty()) { return; } @@ -433,7 +707,9 @@ void LPEBool::doEffect(SPCurve *curve) Geom::PathVector path_a = swap ? path_in : operand_pv; Geom::PathVector path_b = swap ? operand_pv : path_in; - + _hp = path_a; + _hp.insert(_hp.end(), path_b.begin(), path_b.end()); + _hp *= current_affine.inverse(); fill_typ fill_this = fill_type_this.get_value() != fill_justDont ? fill_type_this.get_value() : GetFillTyp(current_shape); fill_typ fill_operand = fill_type_operand.get_value() != fill_justDont ? fill_type_operand.get_value() : GetFillTyp(operand_path.getObject()); @@ -447,17 +723,31 @@ void LPEBool::doEffect(SPCurve *curve) Geom::PathVector path_out; if (op == bool_op_ex_cut) { // we do in two pass because wrong sp_pathvector_boolop diference, in commnet they sugest make this to fix - path_out = sp_pathvector_boolop(path_a, path_b, to_bool_op(bool_op_ex_inters), fill_a, fill_b); - Geom::PathVector path_out_2 = sp_pathvector_boolop(path_b, path_a, to_bool_op(bool_op_ex_diff), fill_b, fill_a); - path_out.insert(path_out.end(), path_out_2.begin(), path_out_2.end()); + Geom::PathVector path_tmp_outside = + sp_pathvector_boolop_slice_intersect(path_a, path_b, false, fill_a, fill_b); + Geom::PathVector path_tmp = sp_pathvector_boolop(path_a, path_b, to_bool_op(op), fill_a, fill_b); + for (auto pathit : path_tmp) { + bool outside = false; + for (auto pathit_out : path_tmp_outside) { + Geom::OptRect outbbox = pathit_out.boundsFast(); + if (outbbox) { + (*outbbox).expandBy(1); + if ((*outbbox).contains(pathit.boundsFast())) { + outside = true; + } + } + } + if (!outside) { + path_out.push_back(pathit); + } + } } else if (op == bool_op_ex_slice) { // For slicing, the bool op is added to the line group which is sliced, not the cut path. This swapped order is correct path_out = sp_pathvector_boolop(path_b, path_a, to_bool_op(op), fill_b, fill_a); - /*} else if (op == bool_op_ex_slice_inside) { + } else if (op == bool_op_ex_slice_inside) { path_out = sp_pathvector_boolop_slice_intersect(path_a, path_b, true, fill_a, fill_b); } else if (op == bool_op_ex_slice_outside) { path_out = sp_pathvector_boolop_slice_intersect(path_a, path_b, false, fill_a, fill_b); - } */ } else { path_out = sp_pathvector_boolop(path_a, path_b, to_bool_op(op), fill_a, fill_b); } @@ -473,18 +763,23 @@ void LPEBool::doEffect(SPCurve *curve) } } +void LPEBool::addCanvasIndicators(SPLPEItem const * /*lpeitem*/, std::vector &hp_vec) +{ + hp_vec.push_back(_hp); +} + void LPEBool::doOnRemove(SPLPEItem const * /*lpeitem*/) { // set "keep paths" hook on sp-lpe-item.cpp SPItem *operand = dynamic_cast(operand_path.getObject()); - if (operand_path.linksToPath() && operand) { + if (operand) { if (keep_paths) { - if (operand->isHidden()) { + if (is_visible) { operand->deleteObject(true); } } else { - if (operand->isHidden()) { - operand->setHidden(false); + if (is_visible) { + remove_filter(); } } } @@ -494,9 +789,9 @@ void LPEBool::doOnRemove(SPLPEItem const * /*lpeitem*/) void LPEBool::doOnVisibilityToggled(SPLPEItem const * /*lpeitem*/) { SPItem *operand = dynamic_cast(operand_path.getObject()); - if (operand_path.linksToPath() && operand) { + if (operand) { if (!is_visible) { - operand->setHidden(false); + remove_filter(); } } } diff --git a/src/live_effects/lpe-bool.h b/src/live_effects/lpe-bool.h index 6e35511c935983dd444d736875393dad7700d583..856a970cc558a1fb26947e6e416475abafd3cde4 100644 --- a/src/live_effects/lpe-bool.h +++ b/src/live_effects/lpe-bool.h @@ -10,12 +10,13 @@ #ifndef INKSCAPE_LPE_BOOL_H #define INKSCAPE_LPE_BOOL_H +#include "livarot/LivarotDefs.h" #include "live_effects/effect.h" -#include "live_effects/parameter/parameter.h" -#include "live_effects/parameter/originalpath.h" #include "live_effects/parameter/bool.h" #include "live_effects/parameter/enum.h" -#include "livarot/LivarotDefs.h" +#include "live_effects/parameter/hidden.h" +#include "live_effects/parameter/originalitem.h" +#include "live_effects/parameter/parameter.h" namespace Inkscape { namespace LivePathEffect { @@ -26,21 +27,30 @@ public: ~LPEBool() override; void doEffect(SPCurve *curve) override; + void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector &hp_vec) override; void doBeforeEffect(SPLPEItem const *lpeitem) override; + void doOnCopy(SPLPEItem const *lpeitem, Inkscape::ObjectSet *set) override; + void doOnCut(SPLPEItem const *lpeitem) override; + void doOnStamp(SPLPEItem const *lpeitem) override; + void doOnPaste(SPLPEItem const *lpeitem) override; + void doOnFork(LivePathEffect::Effect const *forkedeffect) override; + void doOnDuple(SPLPEItem const *lpeitem) override; void transform_multiply(Geom::Affine const &postmul, bool set) override; void doOnVisibilityToggled(SPLPEItem const * /*lpeitem*/) override; void doOnRemove(SPLPEItem const * /*lpeitem*/) override; - - enum bool_op_ex { - bool_op_ex_union = bool_op_union, - bool_op_ex_inters = bool_op_inters, - bool_op_ex_diff = bool_op_diff, - bool_op_ex_symdiff = bool_op_symdiff, - bool_op_ex_cut = bool_op_cut, - bool_op_ex_slice = bool_op_slice, - // Comneted in 1.0 don't work properly - // bool_op_ex_slice_inside, // like bool_op_slice, but leaves only the contour pieces inside of the cut path - // bool_op_ex_slice_outside, // like bool_op_slice, but leaves only the contour pieces outside of the cut path + void add_filter(); + Geom::PathVector get_union(SPObject *object); + void remove_filter(); + enum bool_op_ex + { + bool_op_ex_union = bool_op_union, + bool_op_ex_inters = bool_op_inters, + bool_op_ex_diff = bool_op_diff, + bool_op_ex_symdiff = bool_op_symdiff, + bool_op_ex_cut = bool_op_cut, + bool_op_ex_slice = bool_op_slice, + bool_op_ex_slice_inside, // like bool_op_slice, but leaves only the contour pieces inside of the cut path + bool_op_ex_slice_outside, // like bool_op_slice, but leaves only the contour pieces outside of the cut path bool_op_ex_count }; @@ -54,14 +64,20 @@ private: LPEBool(const LPEBool &) = delete; LPEBool &operator=(const LPEBool &) = delete; - OriginalPathParam operand_path; + OriginalItemParam operand_path; EnumParam bool_operation; EnumParam fill_type_this; EnumParam fill_type_operand; - BoolParam hide_linked; BoolParam swap_operands; BoolParam rmv_inner; SPItem *operand; + HiddenParam filter; + Geom::PathVector _hp; + bool extra_selected; + bool fork_duple; + bool fork_copy; + bool fork_stamp; + Geom::Affine prev_affine; }; }; //namespace LivePathEffect diff --git a/src/live_effects/lpeobject.cpp b/src/live_effects/lpeobject.cpp index f931515fd2d838b6a61f1d4d65480b2fb18c97ea..7a17364f5ca1ec88ad8434d8fba54efd950d3e9f 100644 --- a/src/live_effects/lpeobject.cpp +++ b/src/live_effects/lpeobject.cpp @@ -199,8 +199,14 @@ LivePathEffectObject *LivePathEffectObject::fork_private_if_necessary(unsigned i doc->getDefs()->getRepr()->addChild(dup_repr, nullptr); LivePathEffectObject *lpeobj_new = dynamic_cast(doc->getObjectByRepr(dup_repr)); - Inkscape::GC::release(dup_repr); + // To regenerate ID + sp_object_ref(lpeobj_new, nullptr); + gchar *id = sp_object_get_unique_id(this, nullptr); + lpeobj_new->setAttribute("id", id); + g_free(id); + // Load all volatile vars of forked item + sp_object_unref(lpeobj_new, nullptr); return lpeobj_new; } return this; diff --git a/src/live_effects/parameter/item.cpp b/src/live_effects/parameter/item.cpp index dbcc45e63c388075de23c956c2d643007390f7a3..aab7e9fe84b870433902916b186ad4c3a66e4feb 100644 --- a/src/live_effects/parameter/item.cpp +++ b/src/live_effects/parameter/item.cpp @@ -9,21 +9,19 @@ #include "live_effects/parameter/item.h" #include - #include #include #include "bad-uri-exception.h" -#include "ui/widget/point.h" - -#include "live_effects/effect.h" -#include "svg/svg.h" - #include "desktop.h" #include "inkscape.h" +#include "live_effects/effect.h" +#include "live_effects/lpeobject.h" #include "message-stack.h" #include "selection-chemistry.h" +#include "svg/svg.h" #include "ui/icon-loader.h" +#include "ui/widget/point.h" #include "xml/repr.h" // clipboard support #include "ui/clipboard.h" @@ -72,6 +70,27 @@ ItemParam::param_set_and_write_default() param_write_to_repr(defvalue); } +SPObject *ItemParam::param_fork() +{ + SPObject *newobj = nullptr; + SPDocument *document = param_effect->getSPDoc(); + if (!document) { + return newobj; + } + SPObject *oldobj = ref.getObject(); + + if (oldobj) { + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + Inkscape::XML::Node *fork = oldobj->getRepr()->duplicate(xml_doc); + newobj = oldobj->parent->appendChildRepr(fork); + if (newobj && newobj->getId()) { + Glib::ustring id = newobj->getId(); + linkitem(id); + } + } + return newobj; +} + bool ItemParam::param_readSVGValue(const gchar * strvalue) { diff --git a/src/live_effects/parameter/item.h b/src/live_effects/parameter/item.h index a6a0e36b1200f911976f2267a0705cc24b575e59..34644519e0e0a2316adcffd3fbb5c2979cf9b8d5 100644 --- a/src/live_effects/parameter/item.h +++ b/src/live_effects/parameter/item.h @@ -39,11 +39,13 @@ public: void param_set_default() override; void param_update_default(const gchar * default_value) override; void param_set_and_write_default(); + SPObject *param_fork(); void addCanvasIndicators(SPLPEItem const* lpeitem, std::vector &hp_vec) override; sigc::signal signal_item_pasted; sigc::signal signal_item_changed; void linkitem(Glib::ustring itemid); Geom::Affine last_transform; + friend class LPEBool; bool changed; /* this gets set whenever the path is changed (this is set to true, and then the signal_item_changed signal is emitted). * the user must set it back to false if she wants to use it sensibly */ protected: diff --git a/src/live_effects/parameter/originalitem.cpp b/src/live_effects/parameter/originalitem.cpp index f4ad3815fc4996c8c407029d58523124cdd2c3c7..eae25540a7d36c6c7cc668c38695948305a4dd49 100644 --- a/src/live_effects/parameter/originalitem.cpp +++ b/src/live_effects/parameter/originalitem.cpp @@ -78,21 +78,6 @@ OriginalItemParam::param_newWidget() return dynamic_cast (_widget); } -void -OriginalItemParam::linked_modified_callback(SPObject *linked_obj, guint /*flags*/) -{ - emit_changed(); - SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG); - last_transform = Geom::identity(); -} - -void -OriginalItemParam::linked_transformed_callback(Geom::Affine const * rel_transf, SPItem *moved_item) -{ - last_transform = *rel_transf; - SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG); -} - void OriginalItemParam::on_select_original_button_click() diff --git a/src/live_effects/parameter/originalitem.h b/src/live_effects/parameter/originalitem.h index 26729f6de8dbae85f6046d41a31a254c3d1d9edb..2752adeaebdaaa5afbaabb62f327f09652cc7372 100644 --- a/src/live_effects/parameter/originalitem.h +++ b/src/live_effects/parameter/originalitem.h @@ -30,9 +30,6 @@ public: Gtk::Widget * param_newWidget() override; protected: - void linked_modified_callback(SPObject *linked_obj, guint flags) override; - void linked_transformed_callback(Geom::Affine const *rel_transf, SPItem *moved_item) override; - void on_select_original_button_click(); private: diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index e78044d1c1931b41cde2cef27d76b2553c288152..69c1f56c13e3d1b510dd3b54890efe5309ffe867 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -19,11 +19,10 @@ #include <2geom/path-sink.h> #include "desktop.h" +#include "display/curve.h" #include "document.h" #include "inkscape.h" -#include "preferences.h" -#include "text-editing.h" - +#include "live_effects/effect-enum.h" #include "object/sp-clippath.h" #include "object/sp-flowtext.h" #include "object/sp-image.h" @@ -35,11 +34,11 @@ #include "object/sp-shape.h" #include "object/sp-text.h" #include "object/sp-use.h" - -#include "display/curve.h" -#include "path/path-util.h" // curve_for_item - +#include "path/path-util.h" // curve_for_item +#include "preferences.h" +#include "style.h" #include "svg/svg.h" +#include "text-editing.h" Inkscape::ObjectSnapper::ObjectSnapper(SnapManager *sm, Geom::Coord const d) : Snapper(sm, d) @@ -97,6 +96,43 @@ void Inkscape::ObjectSnapper::_findCandidates(SPObject* parent, g_assert(dt != nullptr); SPItem *item = dynamic_cast(&o); if (item && !(dt->itemIsHidden(item) && !clip_or_mask)) { + // Fix LPE boolops selfsnaping + bool stop = false; + if (item->style) { + SPFilter *filt = item->style->getFilter(); + if (filt && filt->getId() && strcmp(filt->getId(), "selectable_hidder_filter") == 0) { + stop = true; + } + SPLPEItem *lpeitem = dynamic_cast(item); + if (lpeitem && lpeitem->hasPathEffectOfType(Inkscape::LivePathEffect::EffectType::BOOL_OP)) { + stop = true; + } + } + if (stop) { + stop = false; + for (auto skipitem : *it) { + if (skipitem && skipitem->style) { + SPItem *toskip = const_cast(skipitem); + if (toskip) { + SPFilter *filt = toskip->style->getFilter(); + if (filt && filt->getId() && strcmp(filt->getId(), "selectable_hidder_filter") == 0) { + stop = true; + break; + } + + SPLPEItem *lpeitem = dynamic_cast(toskip); + if (!stop && lpeitem && + lpeitem->hasPathEffectOfType(Inkscape::LivePathEffect::EffectType::BOOL_OP)) { + stop = true; + break; + } + } + } + } + if (stop) { + continue; + } + } // Snapping to items in a locked layer is allowed // Don't snap to hidden objects, unless they're a clipped path or a mask /* See if this item is on the ignore list */ diff --git a/src/object/sp-lpe-item.cpp b/src/object/sp-lpe-item.cpp index bde56a06b3ff8124a825862e0a8b614ffd2f37f1..febbbcaa4abb1b74471ae6aa7424b40deac2bd55 100755 --- a/src/object/sp-lpe-item.cpp +++ b/src/object/sp-lpe-item.cpp @@ -109,7 +109,7 @@ void SPLPEItem::set(SPAttr key, gchar const* value) { this->current_path_effect = nullptr; // Disable the path effects while populating the LPE list - sp_lpe_item_enable_path_effects(this, false); + sp_lpe_item_enable_path_effects(this, false); // disconnect all modified listeners: for (auto & mod_it : *this->lpe_modified_connection_list) @@ -327,6 +327,9 @@ bool SPLPEItem::optimizeTransforms() */ void SPLPEItem::notifyTransform(Geom::Affine const &postmul) { + if (!pathEffectsEnabled()) + return; + PathEffectList path_effect_list(*this->path_effect_list); for (auto &lperef : path_effect_list) { if (!lperef) { @@ -1231,9 +1234,10 @@ bool SPLPEItem::forkPathEffectsIfNecessary(unsigned int nr_of_allowed_users, boo LivePathEffectObject *forked_lpeobj = lpeobj->fork_private_if_necessary(nr_of_allowed_users); if (forked_lpeobj && forked_lpeobj != lpeobj) { forked = true; + forked_lpeobj->get_lpe()->is_load = true; + forked_lpeobj->get_lpe()->doOnFork(lpeobj->get_lpe()); old_lpeobjs.push_back(lpeobj); new_lpeobjs.push_back(forked_lpeobj); - forked_lpeobj->get_lpe()->is_load = true; } } } diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index 100f54896e7b9228c0e395b55cff7070d2f9a187..c20749d2833a3dae3e4b00e2e3cb38cf58f3e51e 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -37,33 +37,24 @@ // TODO FIXME: This should be moved into preference repr SPCycleType SP_CYCLING = SP_CYCLE_FOCUS; - #include "context-fns.h" #include "desktop-style.h" #include "desktop.h" -#include "document-undo.h" -#include "gradient-drag.h" -#include "layer-fns.h" -#include "layer-manager.h" -#include "layer-model.h" -#include "message-stack.h" -#include "path-chemistry.h" -#include "selection.h" -#include "text-editing.h" -#include "text-chemistry.h" -#include "verbs.h" - #include "display/cairo-utils.h" #include "display/curve.h" #include "display/sp-canvas.h" - +#include "document-undo.h" +#include "gradient-drag.h" #include "helper/png-write.h" - #include "io/resource.h" - +#include "layer-fns.h" +#include "layer-manager.h" +#include "layer-model.h" #include "live_effects/effect.h" +#include "live_effects/lpeobject-reference.h" +#include "live_effects/lpeobject.h" #include "live_effects/parameter/originalpath.h" - +#include "message-stack.h" #include "object/box3d.h" #include "object/object-set.h" #include "object/persp3d.h" @@ -96,11 +87,13 @@ SPCycleType SP_CYCLING = SP_CYCLE_FOCUS; #include "object/sp-tref.h" #include "object/sp-tspan.h" #include "object/sp-use.h" +#include "path-chemistry.h" +#include "selection.h" #include "style.h" - #include "svg/svg-color.h" #include "svg/svg.h" - +#include "text-chemistry.h" +#include "text-editing.h" #include "ui/clipboard.h" #include "ui/tool/control-point-selection.h" #include "ui/tool/multi-path-manipulator.h" @@ -110,7 +103,7 @@ SPCycleType SP_CYCLING = SP_CYCLE_FOCUS; #include "ui/tools/gradient-tool.h" #include "ui/tools/node-tool.h" #include "ui/tools/text-tool.h" - +#include "verbs.h" #include "xml/rebase-hrefs.h" #include "xml/simple-document.h" @@ -431,6 +424,43 @@ static void add_ids_recursive(std::vector &ids, SPObject *obj) } } +/** + * Call to LPE copy event + * Find a way to pass the function as parameter + */ +void sp_selection_livepatheffect_action(SPLPEItem *lpeitem, Glib::ustring action, Inkscape::ObjectSet *set) +{ + if (lpeitem && lpeitem->hasPathEffect()) { + PathEffectList path_effect_list(*lpeitem->path_effect_list); + std::vector lpelist; + for (auto &lperef : path_effect_list) { + if (!lperef) { + continue; + } + LivePathEffectObject *lpeobj = lperef->lpeobject; + if (lpeobj) { + Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe(); + if (lpe) { + lpelist.push_back(lpe); + } + } + } + for (auto lpe : lpelist) { + if (action == "copy") { + lpe->doOnCopy(lpeitem, set); + } else if (action == "paste") { + lpe->doOnPaste(lpeitem); + } else if (action == "cut") { + lpe->doOnCut(lpeitem); + } else if (action == "duple") { + lpe->doOnDuple(lpeitem); + } else if (action == "stamp") { + lpe->doOnStamp(lpeitem); + } + } + } +} + void ObjectSet::duplicate(bool suppressDone, bool duplicateLayer) { if(duplicateLayer && !desktop() ){ @@ -481,6 +511,14 @@ void ObjectSet::duplicate(bool suppressDone, bool duplicateLayer) std::vector copies; for(auto old_repr : reprs) { + SPItem *old_item = dynamic_cast(doc->getObjectByRepr(old_repr)); + if (old_item) { + SPLPEItem *lpeitem = dynamic_cast(old_item); + if (lpeitem) { + Glib::ustring action = "duple"; + sp_selection_livepatheffect_action(lpeitem, action, this); + } + } Inkscape::XML::Node *parent = old_repr->parent(); Inkscape::XML::Node *copy = old_repr->duplicate(xml_doc); @@ -507,6 +545,7 @@ void ObjectSet::duplicate(bool suppressDone, bool duplicateLayer) SPLPEItem *newLPEObj = dynamic_cast(new_obj); if (newLPEObj) { newLPEObj->forkPathEffectsIfNecessary(1); + sp_lpe_item_update_patheffect(newLPEObj, false, false); } } @@ -1241,6 +1280,35 @@ sp_redo(SPDesktop *desktop, SPDocument *) void ObjectSet::cut() { + // we need to unselect all active operands of bool LPE + auto list = items(); + bool changed = false; + std::vector tounselect; + for (auto itemlist = list.begin(); itemlist != list.end(); ++itemlist) { + SPLPEItem *lpeitem = dynamic_cast(*itemlist); + if (lpeitem && lpeitem->style) { + SPFilter *filt = lpeitem->style->getFilter(); + if (filt && (filt->getId() && strcmp(filt->getId(), "selectable_hidder_filter") == 0)) { + changed = true; + tounselect.push_back(lpeitem); + } + } + } + if (changed) { + for (auto obj : tounselect) { + remove(obj); + } + list = items(); + } + // here we call to main bool lpe that chain to previously unselected items + // in the correct order + for (auto itemlist = list.begin(); itemlist != list.end(); ++itemlist) { + SPLPEItem *lpeitem = dynamic_cast(*itemlist); + if (lpeitem) { + Glib::ustring action = "cut"; + sp_selection_livepatheffect_action(lpeitem, action, this); + } + } copy(); deleteItems(); } @@ -1300,7 +1368,42 @@ take_style_from_item(SPObject *object) void ObjectSet::copy() { Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); + // we need to unselect all active operands of bool LPE + auto list = items(); + bool changed = false; + std::vector tounselect; + for (auto itemlist = list.begin(); itemlist != list.end(); ++itemlist) { + SPLPEItem *lpeitem = dynamic_cast(*itemlist); + if (lpeitem && lpeitem->style) { + SPFilter *filt = lpeitem->style->getFilter(); + if (filt && (filt->getId() && strcmp(filt->getId(), "selectable_hidder_filter") == 0)) { + changed = true; + tounselect.push_back(lpeitem); + } + } + } + if (changed) { + for (auto obj : tounselect) { + remove(obj); + } + list = items(); + } + // here we call to main bool lpe that chain to previously unselected items + // in the correct order + for (auto itemlist = list.begin(); itemlist != list.end(); ++itemlist) { + SPLPEItem *lpeitem = dynamic_cast(*itemlist); + if (lpeitem) { + Glib::ustring action = "copy"; + sp_selection_livepatheffect_action(lpeitem, action, this); + } + } cm->copy(this); + for (auto itemlist = list.begin(); itemlist != list.end(); ++itemlist) { + SPLPEItem *lpeitem = dynamic_cast(*itemlist); + if (lpeitem) { + sp_lpe_item_update_patheffect(lpeitem, false, false); + } + } } void sp_selection_paste(SPDesktop *desktop, bool in_place) diff --git a/src/selection-chemistry.h b/src/selection-chemistry.h index 771066b817fc2c0b7855604eded3118e80669b1a..a7e7d709a767a7ecacd55a16912b377050381e61 100644 --- a/src/selection-chemistry.h +++ b/src/selection-chemistry.h @@ -26,6 +26,7 @@ class SPCSSAttr; class SPDesktop; class SPDocument; class SPItem; +class SPLPEItem; class SPObject; namespace Inkscape { @@ -91,6 +92,8 @@ enum SPSelectStrokeStyleType { void sp_select_same_fill_stroke_style(SPDesktop *desktop, gboolean fill, gboolean strok, gboolean style); void sp_select_same_object_type(SPDesktop *desktop); +void sp_selection_livepatheffect_action(SPLPEItem *lpeitem, Glib::ustring action, Inkscape::ObjectSet *set); + std::vector sp_get_same_style(SPItem *sel, std::vector &src, SPSelectStrokeStyleType type=SP_STYLE_ALL); std::vector sp_get_same_object_type(SPItem *sel, std::vector &src); diff --git a/src/seltrans.cpp b/src/seltrans.cpp index 96e6b2c94a2e2063fda172835dd56ae0da91e3a0..99b96635217cb7c44ad8787e4635c606329453ba 100644 --- a/src/seltrans.cpp +++ b/src/seltrans.cpp @@ -572,17 +572,19 @@ void Inkscape::SelTrans::stamp() new_affine = &original_item->transform; } - copy_item->doWriteTransform(*new_affine); - - if ( copy_item->isCenterSet() && _center ) { - copy_item->setCenter(*_center * _current_relative_affine); - } Inkscape::GC::release(copy_repr); SPLPEItem * lpeitem = dynamic_cast(copy_item); - if(lpeitem && lpeitem->hasPathEffectRecursive()) { + if (lpeitem) { + Glib::ustring action = "stamp"; + sp_selection_livepatheffect_action(lpeitem, action, nullptr); lpeitem->forkPathEffectsIfNecessary(1); sp_lpe_item_update_patheffect(lpeitem, true, true); } + copy_item->doWriteTransform(*new_affine); + + if (copy_item->isCenterSet() && _center) { + copy_item->setCenter(*_center * _current_relative_affine); + } } DocumentUndo::done(_desktop->getDocument(), SP_VERB_CONTEXT_SELECT, _("Stamp")); diff --git a/src/ui/clipboard.cpp b/src/ui/clipboard.cpp index bd541c0d435b994ff734b38c47dffeae3feb3187..1854138f771d638ba2e5ec437c3477ae0142aa54 100644 --- a/src/ui/clipboard.cpp +++ b/src/ui/clipboard.cpp @@ -373,6 +373,18 @@ bool ClipboardManagerImpl::paste(SPDesktop *desktop, bool in_place) return false; } + if (desktop && _clipboardSPDoc) { + for (auto itemref : getElementsOfType(desktop, "*")) { + SPObject *object = _clipboardSPDoc->getObjectById(itemref); + if (object) { + SPLPEItem *lpeitem = dynamic_cast(object); + if (lpeitem) { + Glib::ustring action = "paste"; + sp_selection_livepatheffect_action(lpeitem, action, nullptr); + } + } + } + } Glib::ustring target = _getBestTarget(); // Special cases of clipboard content handling go here @@ -424,7 +436,8 @@ Glib::ustring ClipboardManagerImpl::getFirstObjectID() strcmp(ch->name(), "svg:text") && strcmp(ch->name(), "svg:image") && strcmp(ch->name(), "svg:rect") && - strcmp(ch->name(), "svg:ellipse") + strcmp(ch->name(), "svg:ellipse") && + strcmp(ch->name(), "svg:circle") ) { ch = ch->next(); } diff --git a/src/ui/dialog/align-and-distribute.cpp b/src/ui/dialog/align-and-distribute.cpp index da7c007a89af612d699622bab765f2076cde5212..aae8271d5ec5547628fe5511ef4f88e4e8816d80 100644 --- a/src/ui/dialog/align-and-distribute.cpp +++ b/src/ui/dialog/align-and-distribute.cpp @@ -17,30 +17,25 @@ * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ -#include +#include "align-and-distribute.h" #include <2geom/transforms.h> - +#include #include -#include "align-and-distribute.h" - #include "desktop.h" #include "document-undo.h" #include "document.h" #include "graphlayout.h" #include "inkscape.h" -#include "preferences.h" -#include "removeoverlap.h" -#include "text-editing.h" -#include "unclump.h" -#include "verbs.h" - +#include "live_effects/effect-enum.h" #include "object/sp-flowtext.h" #include "object/sp-item-transform.h" #include "object/sp-root.h" #include "object/sp-text.h" - +#include "preferences.h" +#include "removeoverlap.h" +#include "text-editing.h" #include "ui/icon-loader.h" #include "ui/icon-names.h" #include "ui/tool/control-point-selection.h" @@ -48,6 +43,8 @@ #include "ui/tools-switch.h" #include "ui/tools/node-tool.h" #include "ui/widget/spinbutton.h" +#include "unclump.h" +#include "verbs.h" namespace Inkscape { namespace UI { @@ -122,7 +119,14 @@ void ActionAlign::do_action(SPDesktop *desktop, int index) Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool sel_as_group = prefs->getBool("/dialogs/align/sel-as-groups"); - + // We force unselect operand in bool LPE + auto list = selection->items(); + for (auto itemlist = list.begin(); itemlist != list.end(); ++itemlist) { + SPLPEItem *lpeitem = dynamic_cast(*itemlist); + if (lpeitem && lpeitem->hasPathEffectOfType(Inkscape::LivePathEffect::EffectType::BOOL_OP)) { + sp_lpe_item_update_patheffect(lpeitem, false, false); + } + } std::vector selected(selection->items().begin(), selection->items().end()); if (selected.empty()) return; diff --git a/src/ui/dialog/livepatheffect-editor.cpp b/src/ui/dialog/livepatheffect-editor.cpp index 19598db054468d863756fb28c08622bc87d99079..8814018fb3c8fe8022c96c40dbfaf3ffa75e27e9 100644 --- a/src/ui/dialog/livepatheffect-editor.cpp +++ b/src/ui/dialog/livepatheffect-editor.cpp @@ -55,9 +55,11 @@ namespace Dialog { void lpeeditor_selection_changed (Inkscape::Selection * selection, gpointer data) { LivePathEffectEditor *lpeeditor = static_cast(data); + lpeeditor->selection_changed_lock = true; lpeeditor->lpe_list_locked = false; lpeeditor->onSelectionChanged(selection); lpeeditor->_on_button_release(nullptr); //to force update widgets + lpeeditor->selection_changed_lock = false; } void lpeeditor_selection_modified (Inkscape::Selection * selection, guint /*flags*/, gpointer data) @@ -170,7 +172,7 @@ LivePathEffectEditor::LivePathEffectEditor() contents->pack_start(effectcontrol_frame, false, false); effectcontrol_frame.hide(); - + selection_changed_lock = false; // connect callback functions to buttons button_add.signal_clicked().connect(sigc::mem_fun(*this, &LivePathEffectEditor::onAdd)); button_remove.signal_clicked().connect(sigc::mem_fun(*this, &LivePathEffectEditor::onRemove)); @@ -561,7 +563,7 @@ void LivePathEffectEditor::on_effect_selection_changed() LivePathEffect::LPEObjectReference * lperef = (*it)[columns.lperef]; if (lperef && current_lpeitem && current_lperef != lperef) { - //The last condition ignore Gtk::TreeModel may occasionally be changed emitted when nothing has happened + // The last condition ignore Gtk::TreeModel may occasionally be changed emitted when nothing has happened if (lperef->getObject()) { lpe_list_locked = true; // prevent reload of the list which would lose selection current_lpeitem->setCurrentPathEffect(lperef); @@ -570,14 +572,14 @@ void LivePathEffectEditor::on_effect_selection_changed() if (effect) { effect->refresh_widgets = true; showParams(*effect); - //To reload knots and helper paths + // To reload knots and helper paths Inkscape::Selection *sel = _getSelection(); - if ( sel && !sel->isEmpty() ) { - SPItem *item = sel->singleItem(); - if (item) { + if (sel && !sel->isEmpty() && !selection_changed_lock) { + SPLPEItem *lpeitem = dynamic_cast(sel->singleItem()); + if (lpeitem) { sel->clear(); - sel->add(item); - Inkscape::UI::Tools::sp_update_helperpath(current_desktop); + sel->add(lpeitem); + Inkscape::UI::Tools::sp_update_helperpath(current_desktop); } } } diff --git a/src/ui/dialog/livepatheffect-editor.h b/src/ui/dialog/livepatheffect-editor.h index 6af7aed72adf572f9dd71bd122e0134dc674d66c..daaf99810083af8c22ee1196945ea4f0c6c64e56 100644 --- a/src/ui/dialog/livepatheffect-editor.h +++ b/src/ui/dialog/livepatheffect-editor.h @@ -88,6 +88,7 @@ private: }; bool lpe_list_locked; + bool selection_changed_lock; //Inkscape::UI::Widget::ComboBoxEnum combo_effecttype; Gtk::Widget * effectwidget;