diff --git a/src/object/object-set.h b/src/object/object-set.h index e688f51dbec2015310bb63df7be4d5252ed7343a..66a3b76b64a78f44333bf8818aba593f0ad01334 100644 --- a/src/object/object-set.h +++ b/src/object/object-set.h @@ -368,7 +368,8 @@ public: * including clones which are used to clip other objects, groups of clones etc. * @return true if anything was unlinked, otherwise false. */ - bool unlinkRecursive(const bool skip_undo = false); + bool unlinkRecursive(const bool skip_undo = false, const bool force = false); + void removeLPESRecursive(bool keep_paths); void relink(); void cloneOriginal(); void cloneOriginalPathLPE(bool allow_transforms = false); diff --git a/src/path-chemistry.cpp b/src/path-chemistry.cpp index c1243f4565ddf98ff582ef3726c8ab7c3828595a..b14b25c83448f4808328955ec03a1a1d3dcbd23e 100644 --- a/src/path-chemistry.cpp +++ b/src/path-chemistry.cpp @@ -305,10 +305,7 @@ void ObjectSet::toCurves(bool skip_undo) // set "busy" cursor desktop()->setWaitingCursor(); } - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - if (prefs->getBool("/options/pathoperationsunlink/value", true)) { - unlinkRecursive(true); - } + unlinkRecursive(true); std::vector selected(items().begin(), items().end()); std::vector to_select; std::vector items(selected); @@ -340,10 +337,7 @@ void ObjectSet::toLPEItems() if (isEmpty()) { return; } - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - if (prefs->getBool("/options/pathoperationsunlink/value", true)) { - unlinkRecursive(true); - } + unlinkRecursive(true); std::vector selected(items().begin(), items().end()); std::vector to_select; clear(); diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index 769e62276be7ff6f21c95aa154b6b831e1f8c395..3ed0e4e563b38e0a70c77ed7e0441ce759513775 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -2790,12 +2790,17 @@ bool ObjectSet::unlink(const bool skip_undo) return unlinked; } -bool ObjectSet::unlinkRecursive(const bool skip_undo) { +bool ObjectSet::unlinkRecursive(const bool skip_undo, const bool force) { if (isEmpty()){ if (desktop()) desktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select clones to unlink.")); return false; } + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool pathoperationsunlink = prefs->getBool("/options/pathoperationsunlink/value", true); + if (!force && !pathoperationsunlink) { + return false; + } bool unlinked = false; ObjectSet tmp_set(document()); std::vector items_(items().begin(), items().end()); @@ -2806,7 +2811,7 @@ bool ObjectSet::unlinkRecursive(const bool skip_undo) { if (SP_IS_GROUP(it)) { std::vector c = it->childList(false); tmp_set.setList(c); - unlinked = tmp_set.unlinkRecursive(true) || unlinked; + unlinked = tmp_set.unlinkRecursive(skip_undo, force) || unlinked; } } if (!unlinked) { @@ -2821,6 +2826,27 @@ bool ObjectSet::unlinkRecursive(const bool skip_undo) { return unlinked; } +void ObjectSet::removeLPESRecursive(bool keep_paths) { + if (isEmpty()){ + return; + } + + ObjectSet tmp_set(document()); + std::vector items_(items().begin(), items().end()); + for (auto& it:items_) { + SPLPEItem *splpeitem = dynamic_cast(it); + SPGroup *spgroup = dynamic_cast(it); + if (spgroup) { + std::vector c = spgroup->childList(false); + tmp_set.setList(c); + tmp_set.removeLPESRecursive(keep_paths); + } else if (splpeitem) { + splpeitem->removeAllPathEffects(keep_paths); + } + } + setList(items_); +} + void ObjectSet::cloneOriginal() { SPItem *item = singleItem(); diff --git a/src/splivarot.cpp b/src/splivarot.cpp index 633a7386b53913bc10d44ac44470f17bb81572fc..a6e75efed74de7c55043bbf0d44e0d26a41ea2c1 100644 --- a/src/splivarot.cpp +++ b/src/splivarot.cpp @@ -838,7 +838,7 @@ BoolOpErrors Inkscape::ObjectSet::pathBoolOp(bool_op bop, const bool skip_undo, } static -void sp_selected_path_outline_add_marker( SPObject *marker_object, Geom::Affine marker_transform, +void sp_selected_path_outline_add_marker( SPItem *context, SPObject *marker_object, Geom::Affine marker_transform, Geom::Scale stroke_scale, Geom::Affine transform, Inkscape::XML::Node *g_repr, Inkscape::XML::Document *xml_doc, SPDocument * doc, SPDesktop *desktop , bool legacy) @@ -864,7 +864,7 @@ void sp_selected_path_outline_add_marker( SPObject *marker_object, Geom::Affine SPItem *marker_item = (SPItem *) doc->getObjectByRepr(m_repr); marker_item->doWriteTransform(tr); if (!legacy) { - sp_item_path_outline(marker_item, desktop, legacy); + sp_item_path_outline(marker_item, desktop, legacy, context); } } } @@ -1121,8 +1121,10 @@ Geom::PathVector* item_outline(SPItem const *item, bool bbox_only) } bool -sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy) +sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy, SPItem *context) { + char const *id = item->getRepr()->attribute("id"); + SPDocument * document = item->document; bool did = false; Inkscape::Selection *selection = desktop->getSelection(); SPDocument * doc = desktop->getDocument(); @@ -1130,6 +1132,12 @@ sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy) SPLPEItem *lpeitem = SP_LPE_ITEM(item); if (lpeitem) { lpeitem->removeAllPathEffects(true); + SPObject *elemref = document->getObjectById(id); + if (elemref && elemref != item) { + // If the LPE item is a shape, it is converted to a path + // so we need to reupdate the item + item = dynamic_cast(elemref); + } } SPGroup *group = dynamic_cast(item); @@ -1172,47 +1180,64 @@ sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy) // remember old stroke style, to be set on fill SPStyle *i_style = item->style; //Stroke - and markers - gchar const *opacity; - gchar const *filter; - // Copying stroke style to fill will fail for properties not defined by style attribute // (i.e., properties defined in style sheet or by attributes). // Stroke - SPCSSAttr *ncss = nullptr; - { - ncss = sp_css_attr_from_style(i_style, SP_STYLE_FLAG_ALWAYS); - gchar const *s_val = sp_repr_css_property(ncss, "stroke", nullptr); - gchar const *s_opac = sp_repr_css_property(ncss, "stroke-opacity", nullptr); - opacity = sp_repr_css_property(ncss, "opacity", nullptr); - filter = sp_repr_css_property(ncss, "filter", nullptr); - sp_repr_css_set_property(ncss, "stroke", "none"); - sp_repr_css_set_property(ncss, "filter", nullptr); - sp_repr_css_set_property(ncss, "opacity", nullptr); - sp_repr_css_set_property(ncss, "stroke-opacity", "1.0"); - sp_repr_css_set_property(ncss, "fill", s_val); - if ( s_opac ) { - sp_repr_css_set_property(ncss, "fill-opacity", s_opac); - } else { - sp_repr_css_set_property(ncss, "fill-opacity", "1.0"); + SPCSSAttr *ncss = sp_css_attr_from_style(i_style, SP_STYLE_FLAG_ALWAYS); + SPCSSAttr *ncsf = sp_css_attr_from_style(i_style, SP_STYLE_FLAG_ALWAYS); + if (context) { + SPStyle *context_style = context->style; + SPCSSAttr *ctxt_style = sp_css_attr_from_style(context_style, SP_STYLE_FLAG_ALWAYS); + // TODO: browsers has diferent behabiours with context on markers + // we need to revisit in the future for best matching + // also dont know if opacity is or want to be included in context + gchar const *s_val = sp_repr_css_property(ctxt_style, "stroke", nullptr); + gchar const *f_val = sp_repr_css_property(ctxt_style, "fill", nullptr); + if (i_style->fill.paintOrigin == SP_CSS_PAINT_ORIGIN_CONTEXT_STROKE || + i_style->fill.paintOrigin == SP_CSS_PAINT_ORIGIN_CONTEXT_FILL) + { + gchar const *fill_value = (i_style->fill.paintOrigin == SP_CSS_PAINT_ORIGIN_CONTEXT_STROKE) ? s_val : f_val; + sp_repr_css_set_property(ncss, "fill", fill_value); + sp_repr_css_set_property(ncsf, "fill", fill_value); + } + if (i_style->stroke.paintOrigin == SP_CSS_PAINT_ORIGIN_CONTEXT_STROKE || + i_style->stroke.paintOrigin == SP_CSS_PAINT_ORIGIN_CONTEXT_FILL) + { + gchar const *stroke_value = (i_style->stroke.paintOrigin == SP_CSS_PAINT_ORIGIN_CONTEXT_FILL) ? f_val : s_val; + sp_repr_css_set_property(ncss, "stroke", stroke_value); + sp_repr_css_set_property(ncsf, "stroke", stroke_value); } - sp_repr_css_unset_property(ncss, "marker-start"); - sp_repr_css_unset_property(ncss, "marker-mid"); - sp_repr_css_unset_property(ncss, "marker-end"); } + gchar const *s_val = sp_repr_css_property(ncss, "stroke", nullptr); + gchar const *s_opac = sp_repr_css_property(ncss, "stroke-opacity", nullptr); + gchar const *f_val = sp_repr_css_property(ncss, "fill", nullptr); + gchar const *opacity = sp_repr_css_property(ncss, "opacity", nullptr); + gchar const *filter = sp_repr_css_property(ncss, "filter", nullptr); + sp_repr_css_set_property(ncss, "stroke", "none"); + sp_repr_css_set_property(ncss, "stroke-width", nullptr); + sp_repr_css_set_property(ncss, "stroke-opacity", "1.0"); + sp_repr_css_set_property(ncss, "filter", nullptr); + sp_repr_css_set_property(ncss, "opacity", nullptr); + sp_repr_css_set_property(ncss, "fill", s_val); + if ( s_opac ) { + sp_repr_css_set_property(ncss, "fill-opacity", s_opac); + } else { + sp_repr_css_set_property(ncss, "fill-opacity", "1.0"); + } + sp_repr_css_unset_property(ncss, "marker-start"); + sp_repr_css_unset_property(ncss, "marker-mid"); + sp_repr_css_unset_property(ncss, "marker-end"); // Fill - SPCSSAttr *ncsf = nullptr; - { - ncsf = sp_css_attr_from_style(i_style, SP_STYLE_FLAG_ALWAYS); - sp_repr_css_set_property(ncsf, "stroke", "none"); - sp_repr_css_set_property(ncsf, "stroke-opacity", "1.0"); - sp_repr_css_set_property(ncsf, "filter", nullptr); - sp_repr_css_set_property(ncsf, "opacity", nullptr); - sp_repr_css_unset_property(ncsf, "marker-start"); - sp_repr_css_unset_property(ncsf, "marker-mid"); - sp_repr_css_unset_property(ncsf, "marker-end"); - } + sp_repr_css_set_property(ncsf, "stroke", "none"); + sp_repr_css_set_property(ncsf, "stroke-opacity", "1.0"); + sp_repr_css_set_property(ncss, "stroke-width", nullptr); + sp_repr_css_set_property(ncsf, "filter", nullptr); + sp_repr_css_set_property(ncsf, "opacity", nullptr); + sp_repr_css_unset_property(ncsf, "marker-start"); + sp_repr_css_unset_property(ncsf, "marker-mid"); + sp_repr_css_unset_property(ncsf, "marker-end"); Geom::Affine const transform(item->transform); float const scale = transform.descrim(); @@ -1313,7 +1338,7 @@ sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy) //The stroke Inkscape::XML::Node *stroke = nullptr; - if( !item->style->stroke.noneSet ){ + if( s_val && !item->style->stroke.noneSet ){ SPDocument * doc = desktop->getDocument(); Inkscape::XML::Document *xml_doc = doc->getReprDoc(); stroke = xml_doc->createElement("svg:path"); @@ -1340,18 +1365,16 @@ sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy) //The fill Inkscape::XML::Node *fill = nullptr; - if (!legacy) { -// gchar const *f_val = sp_repr_css_property(ncsf, "fill", NULL); - if( !item->style->fill.noneSet ){ - fill = xml_doc->createElement("svg:path"); - sp_repr_css_change(fill, ncsf, "style"); + //gchar const *f_val = sp_repr_css_property(ncsf, "fill", NULL); + if(f_val && !item->style->fill.noneSet && !legacy){ + fill = xml_doc->createElement("svg:path"); + sp_repr_css_change(fill, ncsf, "style"); - sp_repr_css_attr_unref(ncsf); + sp_repr_css_attr_unref(ncsf); - gchar *str = sp_svg_write_path( pathv ); - fill->setAttribute("d", str); - g_free(str); - } + gchar *str = sp_svg_write_path( pathv ); + fill->setAttribute("d", str); + g_free(str); } // restore transform SPItem *newitem = (SPItem *) doc->getObjectByRepr(g_repr); @@ -1371,7 +1394,7 @@ sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy) for (int i = 0; i < 2; i++) { // SP_MARKER_LOC and SP_MARKER_LOC_START if ( SPObject *marker_obj = shape->_marker[i] ) { Geom::Affine const m (sp_shape_marker_get_transform_at_start(pathv.front().front())); - sp_selected_path_outline_add_marker( marker_obj, m, + sp_selected_path_outline_add_marker( item, marker_obj, m, Geom::Scale(i_style->stroke_width.computed), transform, markers, xml_doc, doc, desktop, legacy); } @@ -1386,7 +1409,7 @@ sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy) && ! ((path_it == (pathv.end()-1)) && (path_it->size_default() == 0)) ) // if this is the last path and it is a moveto-only, there is no mid marker there { Geom::Affine const m (sp_shape_marker_get_transform_at_start(path_it->front())); - sp_selected_path_outline_add_marker( midmarker_obj, m, + sp_selected_path_outline_add_marker( item, midmarker_obj, m, Geom::Scale(i_style->stroke_width.computed), transform, markers, xml_doc, doc, desktop, legacy); } @@ -1401,7 +1424,7 @@ sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy) * there should be a midpoint marker between last segment and closing straight line segment */ Geom::Affine const m (sp_shape_marker_get_transform(*curve_it1, *curve_it2)); - sp_selected_path_outline_add_marker( midmarker_obj, m, + sp_selected_path_outline_add_marker( item, midmarker_obj, m, Geom::Scale(i_style->stroke_width.computed), transform, markers, xml_doc, doc, desktop, legacy); @@ -1413,7 +1436,7 @@ sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy) if ( path_it != (pathv.end()-1) && !path_it->empty()) { Geom::Curve const &lastcurve = path_it->back_default(); Geom::Affine const m = sp_shape_marker_get_transform_at_end(lastcurve); - sp_selected_path_outline_add_marker( midmarker_obj, m, + sp_selected_path_outline_add_marker( item, midmarker_obj, m, Geom::Scale(i_style->stroke_width.computed), transform, markers, xml_doc, doc, desktop, legacy); } @@ -1432,7 +1455,7 @@ sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy) Geom::Curve const &lastcurve = path_last[index]; Geom::Affine const m = sp_shape_marker_get_transform_at_end(lastcurve); - sp_selected_path_outline_add_marker( marker_obj, m, + sp_selected_path_outline_add_marker( item, marker_obj, m, Geom::Scale(i_style->stroke_width.computed), transform, markers, xml_doc, doc, desktop, legacy); } @@ -1565,11 +1588,11 @@ sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy) } else { item->deleteObject(false); } - Inkscape::GC::release(g_repr); + out->setAttribute("id", id); + Inkscape::GC::release(out); } } } - delete res; delete orig; } @@ -1586,6 +1609,10 @@ sp_selected_path_outline(SPDesktop *desktop, bool legacy) return; } Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + if (prefs->getBool("/options/pathoperationsunlink/value", true)) { + selection->unlinkRecursive(true); + } + bool scale_stroke = prefs->getBool("/options/transform/stroke", true); prefs->setBool("/options/transform/stroke", true); bool did = false; diff --git a/src/splivarot.h b/src/splivarot.h index 2c09b9160f5f77c7466c531a40609fa9ee400d95..ec218ced8396d0d248c9b4561f990c33a0e50a5b 100644 --- a/src/splivarot.h +++ b/src/splivarot.h @@ -42,7 +42,7 @@ void sp_selected_path_create_updating_offset_object_zero (SPDesktop *desktop); // outline of a curve // uses the stroke-width void sp_selected_path_outline (SPDesktop *desktop, bool legacy = false); -bool sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy); +bool sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy, SPItem *context = nullptr); Geom::PathVector* item_outline(SPItem const *item, bool bbox_only = false); // simplifies a path (removes small segments and the like) diff --git a/src/verbs.cpp b/src/verbs.cpp index e0d0b57e16479302318d8494e66b5b848f956959..a1e02f81c94781ade53f30fa43d96202fe49afa1 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1041,7 +1041,7 @@ void EditVerb::perform(SPAction *action, void *data) dt->selection->unlink(); break; case SP_VERB_EDIT_UNLINK_CLONE_RECURSIVE: - dt->selection->unlinkRecursive(); + dt->selection->unlinkRecursive(false, true); break; case SP_VERB_EDIT_RELINK_CLONE: dt->selection->relink(); @@ -1149,27 +1149,33 @@ void SelectionVerb::perform(SPAction *action, void *data) bool handled = true; switch (reinterpret_cast(data)) { case SP_VERB_SELECTION_UNION: - selection->toCurves(true); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); selection->pathUnion(); break; case SP_VERB_SELECTION_INTERSECT: - selection->toCurves(true); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); selection->pathIntersect(); break; case SP_VERB_SELECTION_DIFF: - selection->toCurves(true); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); selection->pathDiff(); break; case SP_VERB_SELECTION_SYMDIFF: - selection->toCurves(true); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); selection->pathSymDiff(); break; case SP_VERB_SELECTION_CUT: - selection->toCurves(true); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); selection->pathCut(); break; case SP_VERB_SELECTION_SLICE: - selection->toCurves(true); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); selection->pathSlice(); break; case SP_VERB_SELECTION_GROW: @@ -1229,7 +1235,7 @@ void SelectionVerb::perform(SPAction *action, void *data) selection->ungroup(); break; case SP_VERB_SELECTION_UNGROUP_POP_SELECTION: - selection->popFromGroup(); + selection->popFromGroup(); break; default: handled = false; @@ -1257,41 +1263,48 @@ void SelectionVerb::perform(SPAction *action, void *data) break; case SP_VERB_SELECTION_OFFSET: - selection->toCurves(true); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); sp_selected_path_offset(dt); break; case SP_VERB_SELECTION_OFFSET_SCREEN: - selection->toCurves(true); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); sp_selected_path_offset_screen(dt, 1); break; case SP_VERB_SELECTION_OFFSET_SCREEN_10: - selection->toCurves(true); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); sp_selected_path_offset_screen(dt, 10); break; case SP_VERB_SELECTION_INSET: - selection->toCurves(true); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); sp_selected_path_inset(dt); break; case SP_VERB_SELECTION_INSET_SCREEN: - selection->toCurves(true); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); sp_selected_path_inset_screen(dt, 1); break; case SP_VERB_SELECTION_INSET_SCREEN_10: - selection->toCurves(true); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); sp_selected_path_inset_screen(dt, 10); break; case SP_VERB_SELECTION_DYNAMIC_OFFSET: - selection->toCurves(true); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); sp_selected_path_create_offset_object_zero(dt); tools_switch(dt, TOOLS_NODES); break; case SP_VERB_SELECTION_LINKED_OFFSET: - selection->toCurves(true); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); sp_selected_path_create_updating_offset_object_zero(dt); tools_switch(dt, TOOLS_NODES); break; case SP_VERB_SELECTION_OUTLINE: - selection->toCurves(true); sp_selected_path_outline(dt); break; case SP_VERB_SELECTION_OUTLINE_LEGACY: @@ -1302,7 +1315,6 @@ void SelectionVerb::perform(SPAction *action, void *data) sp_selected_path_simplify(dt); break; case SP_VERB_SELECTION_REVERSE: - selection->toCurves(true); SelectionHelper::reverse(dt); break; case SP_VERB_SELECTION_TRACE: @@ -1314,11 +1326,10 @@ void SelectionVerb::perform(SPAction *action, void *data) break; case SP_VERB_SELECTION_COMBINE: - selection->toCurves(true); + selection->unlinkRecursive(true); selection->combine(); break; case SP_VERB_SELECTION_BREAK_APART: - selection->toCurves(true); selection->breakApart(); break; case SP_VERB_SELECTION_ARRANGE: