From 65ef01d619dd4918dabde33c9a8b195242ec816d Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Thu, 28 May 2020 01:32:24 +0200 Subject: [PATCH] LPE BOOL_OP improvements This long MR fixes mayor issues on LPE boolop pointed in RC, most with @adam.belis in PM. At the end put a list of fixes but I want to remark this MR also provide clipboard functionality to LPE very important in linked item parameter like clone LPE (not used jet) and maybe with further elements with generated along the LPE like rotate copies or measure segments. Also fix the issues on translating linked items, still pending use in clone LPE. -- Done: * Operand is selectable as other item. * Works with groups as operand. * Works with clip-paths on operand. * One parameter removed not need anymore (~hide operand). * Added helper path to show paths. * Cut, paste, duple, stamp fixed. * Alignments with the operand selected. -- Know issues * Simple blur filter in operand is removed (others keeped) * On stamp there is a operand translation while stamping, but the result is OK. * User cant cut/copy operands to clipboard * Operands cant have another LPE bool --- src/extension/internal/cairo-renderer.cpp | 7 +- src/live_effects/effect.cpp | 42 ++ src/live_effects/effect.h | 7 + src/live_effects/lpe-bool.cpp | 429 +++++++++++++++++--- src/live_effects/lpe-bool.h | 48 ++- src/live_effects/lpeobject.cpp | 8 +- src/live_effects/parameter/item.cpp | 31 +- src/live_effects/parameter/item.h | 2 + src/live_effects/parameter/originalitem.cpp | 15 - src/live_effects/parameter/originalitem.h | 3 - src/object-snapper.cpp | 50 ++- src/object/sp-lpe-item.cpp | 8 +- src/selection-chemistry.cpp | 143 ++++++- src/selection-chemistry.h | 3 + src/seltrans.cpp | 14 +- src/ui/clipboard.cpp | 15 +- src/ui/dialog/align-and-distribute.cpp | 28 +- src/ui/dialog/livepatheffect-editor.cpp | 18 +- src/ui/dialog/livepatheffect-editor.h | 1 + 19 files changed, 707 insertions(+), 165 deletions(-) diff --git a/src/extension/internal/cairo-renderer.cpp b/src/extension/internal/cairo-renderer.cpp index c84ab6e3c7..b01eb7ccbc 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 86eda6984c..5816a62855 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 f631646a79..d8610e3ead 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 7681ea9ef9..ff077681c4 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 6e35511c93..856a970cc5 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 f931515fd2..7a17364f5c 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 dbcc45e63c..aab7e9fe84 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 a6a0e36b12..34644519e0 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 f4ad3815fc..eae25540a7 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 26729f6de8..2752adeaeb 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 e78044d1c1..69c1f56c13 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 bde56a06b3..febbbcaa4a 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 100f54896e..c20749d283 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 771066b817..a7e7d709a7 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 96e6b2c94a..99b9663521 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 bd541c0d43..1854138f77 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 da7c007a89..aae8271d5e 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 19598db054..8814018fb3 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 6af7aed72a..daaf998100 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; -- GitLab