Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
lpe-clone-original.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) Johan Engelen 2012 <j.b.c.engelen@alumnus.utwente.nl>
4 *
5 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
6 */
7
9
10#include <cstddef>
11
12#include <glibmm/i18n.h>
13
14#include "lpe-bspline.h"
15#include "lpeobject.h"
16#include "lpeobject-reference.h"
17#include "lpe-spiro.h"
18
19#include "inkscape.h"
20#include "selection.h"
21
23#include "display/curve.h"
25#include "object/sp-clippath.h"
27#include "object/sp-mask.h"
28#include "object/sp-path.h"
29#include "object/sp-shape.h"
30#include "object/sp-text.h"
31#include "object/sp-use.h"
32#include "svg/path-string.h"
33#include "svg/svg.h"
34#include "ui/pack.h"
35#include "ui/tools/node-tool.h"
36#include "util/optstr.h"
37#include "xml/sp-css-attr.h"
38
39namespace Inkscape {
40namespace LivePathEffect {
41
43 { CLM_NONE, N_("Transformation Only"), "none" },
44 { CLM_D, N_("Original Shape + LPEs"), "d" },
45 { CLM_ORIGINALD, N_("Original Shape Only"), "originald" },
46 { CLM_BSPLINESPIRO, N_("Spiro or BSpline Only"), "bsplinespiro" },
47};
49
51 : Effect(lpeobject)
52 , linkeditem(_("Linked Object:"), _("Select the object to use as the source for original data."), "linkeditem", &wr, this)
53 , method(_("Shape"), _("Choose how path geometry is synchronized from the original object: Transformations only, original path data, path with Live Path Effects, or Spiro/BSpline shapes only."), "method", CLMConverter, &wr, this, CLM_D)
54 , allow_transforms(_("Allow Transformations"), _("This allows you to change the position, scale, and rotation of the clone."), "allow_transforms", &wr, this, true)
55 , attributes(_("Attributes"), _("Attributes of the original object that the clone should copy, written as a comma-separated list e.g. 'transform, style, clip-path, X, Y'."),
56 "attributes", &wr, this, "")
57 , css_properties(_("CSS Properties"),
58 _("Specify which CSS properties the clone should copy from the original object, using a comma-separated list (for example: 'fill, filter, opacity')."),
59 "css_properties", &wr, this, "")
60{
61 //0.92 compatibility
62 const gchar *linkedpath = getLPEObj()->getAttribute("linkedpath");
63 if (linkedpath && strcmp(linkedpath, "") != 0){
64 getLPEObj()->setAttribute("linkeditem", linkedpath);
65 getLPEObj()->removeAttribute("linkedpath");
66 getLPEObj()->setAttribute("method", "bsplinespiro");
67 getLPEObj()->setAttribute("allow_transforms", "false");
68 };
69
70 sync = false;
71 linked = "";
72 if (getLPEObj()->getAttribute("linkeditem")) {
73 linked = getLPEObj()->getAttribute("linkeditem");
74 }
82}
83
85
87{
88 // we need to inform when all items are ready to read svg relink clones
89 // previously couldn't be because clones are not ready (load later)
92 return false;
93}
94
95void
97{
98 if (method != CLM_NONE) {
99 sync = true;
102 refresh_widgets = true;
103 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
105 if (desktop && dynamic_cast<Inkscape::UI::Tools::NodeTool *>(desktop->getTool())) {
106 // Why is this switching tools twice? Probably to reinitialize Node Tool.
107 set_active_tool(desktop, "Select");
108 set_active_tool(desktop, "Node");
109 }
110 }
111}
112
113Gtk::Widget *
115{
116 // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
117 auto const vbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 6);
118 vbox->set_margin(5);
119
120 std::vector<Parameter *>::iterator it = param_vector.begin();
121 while (it != param_vector.end()) {
122 if ((*it)->widget_is_visible) {
123 Parameter * param = *it;
124 auto const widg = param->param_newWidget();
125
126 if (widg) {
127 UI::pack_start(*vbox, *widg, true, true, 2);
128
129 if (auto const tip = param->param_getTooltip()) {
130 widg->set_tooltip_markup(*tip);
131 } else {
132 widg->set_tooltip_text("");
133 widg->set_has_tooltip(false);
134 }
135 }
136 }
137 ++it;
138 }
139 auto const sync_button = Gtk::make_managed<Gtk::Button>(Glib::ustring(_("Unlink Shape - Transformation Only")));
140 sync_button->signal_clicked().connect(sigc::mem_fun (*this,&LPECloneOriginal::syncOriginal));
141 UI::pack_start(*vbox, *sync_button, true, true, 2);
142 return vbox;
143}
144
145void
146LPECloneOriginal::cloneAttributes(SPObject *origin, SPObject *dest, const gchar * attributes, const gchar * css_properties, bool init)
147{
148 SPDocument *document = getSPDoc();
149 if (!document || !origin || !dest) {
150 return;
151 }
152 bool root = dest == sp_lpe_item;
153 auto group_origin = cast<SPGroup>(origin);
154 auto group_dest = cast<SPGroup>(dest);
155 if (group_origin && group_dest && group_origin->getItemCount() == group_dest->getItemCount()) {
156 std::vector< SPObject * > childs = group_origin->childList(true);
157 size_t index = 0;
158 for (auto & child : childs) {
159 SPObject *dest_child = group_dest->nthChild(index);
161 index++;
162 }
163 } else if ((!group_origin && group_dest) ||
164 ( group_origin && !group_dest))
165 {
166 g_warning("LPE Clone Original: for this path effect to work properly, the same type and the same number of children are required");
167 return;
168 }
169 //Attributes
170 auto shape_origin = cast<SPShape>(origin);
171 auto shape_dest = cast<SPShape>(dest);
172 auto path_dest = cast<SPPath>(dest);
173
174 gchar ** attarray = g_strsplit(old_attributes.c_str(), ",", 0);
175 gchar ** iter = attarray;
176 while (*iter) {
177 const char *attribute = g_strstrip(*iter);
178 if (strlen(attribute)) {
179 dest->removeAttribute(attribute);
180 }
181 iter++;
182 }
183 g_strfreev(attarray);
184
185 attarray = g_strsplit(attributes, ",", 0);
186 iter = attarray;
187 while (*iter) {
188 const char *attribute = g_strstrip(*iter);
189 if (strlen(attribute) && shape_dest && shape_origin) {
190 if (std::strcmp(attribute, "d") == 0) {
191 std::optional<Geom::PathVector> c;
192 if (method == CLM_BSPLINESPIRO) {
193 c = ptr_to_opt(shape_origin->curveForEdit());
194 auto lpe_item = cast<SPLPEItem>(origin);
195 if (lpe_item) {
196 PathEffectList lpelist = lpe_item->getEffectList();
197 PathEffectList::iterator i;
198 for (i = lpelist.begin(); i != lpelist.end(); ++i) {
199 LivePathEffectObject *lpeobj = (*i)->lpeobject;
200 if (lpeobj) {
202 if (auto bspline = dynamic_cast<Inkscape::LivePathEffect::LPEBSpline *>(lpe)) {
204 LivePathEffect::sp_bspline_do_effect(*c, 0, hp, bspline->uniform);
205 } else if (dynamic_cast<Inkscape::LivePathEffect::LPESpiro *>(lpe)) {
207 }
208 }
209 }
210 }
211 } else if (method == CLM_ORIGINALD) {
212 c = ptr_to_opt(shape_origin->curveForEdit());
213 } else if(method == CLM_D){
214 c = ptr_to_opt(shape_origin->curve());
215 }
216 if (c && method != CLM_NONE) {
217 auto str = sp_svg_write_path(*c);
218 if (sync){
219 if (path_dest) {
220 dest->setAttribute("inkscape:original-d", str);
221 } else {
222 dest->setAttribute("d", str);
223 }
224 }
225 shape_dest->setCurveInsync(std::move(*c));
226 dest->setAttribute("d", str);
227 } else if (method != CLM_NONE) {
228 dest->removeAttribute(attribute);
229 }
230 } else {
231 dest->setAttribute(attribute, origin->getAttribute(attribute));
232 }
233 } else if (strlen(attribute)) {
234 dest->setAttribute(attribute, origin->getAttribute(attribute));
235 }
236 iter++;
237 }
238 if (!allow_transforms || !root) {
239 dest->setAttribute("transform", origin->getAttribute("transform"));
240 dest->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
241 }
242 g_strfreev(attarray);
243
244 SPCSSAttr *css_origin = sp_repr_css_attr_new();
245 sp_repr_css_attr_add_from_string(css_origin, origin->getAttribute("style"));
246 SPCSSAttr *css_dest = sp_repr_css_attr_new();
247 sp_repr_css_attr_add_from_string(css_dest, dest->getAttribute("style"));
248 if (init) {
249 css_dest = css_origin;
250 }
251 gchar ** styleattarray = g_strsplit(old_css_properties.c_str(), ",", 0);
252 gchar ** styleiter = styleattarray;
253 while (*styleiter) {
254 const char *attribute = g_strstrip(*styleiter);
255 if (strlen(attribute)) {
256 sp_repr_css_set_property (css_dest, attribute, nullptr);
257 }
258 styleiter++;
259 }
260 g_strfreev(styleattarray);
261
262 styleattarray = g_strsplit(css_properties, ",", 0);
263 styleiter = styleattarray;
264 while (*styleiter) {
265 const char *attribute = g_strstrip(*styleiter);
266 if (strlen(attribute)) {
267 const char* origin_attribute = sp_repr_css_property(css_origin, attribute, "");
268 if (!strlen(origin_attribute)) { //==0
269 sp_repr_css_set_property (css_dest, attribute, nullptr);
270 } else {
271 sp_repr_css_set_property (css_dest, attribute, origin_attribute);
272 }
273 }
274 styleiter++;
275 }
276 g_strfreev(styleattarray);
277
278 Glib::ustring css_str;
279 sp_repr_css_write_string(css_dest,css_str);
280 dest->setAttributeOrRemoveIfEmpty("style", css_str);
281}
282
283void
285 SPDocument *document = getSPDoc();
286 if (!document) {
287 return;
288 }
289 if (!is_load && !isOnClipboard() && linkeditem.lperef &&
290 linkeditem.lperef->isAttached() && linkeditem.lperef.get()->getObject() == nullptr)
291 {
293 return;
294 }
295 bool init = false;
296 if (!linkeditem.linksToItem()) {
298 init = true;
299 }
300 if (linkeditem.linksToItem()) {
303 sp_lpe_item_update_patheffect(sp_lpe_item, false, false, true);
304 return;
305 }
306 auto orig = cast<SPItem>(linkeditem.getObject());
307 if(!orig) {
308 return;
309 }
310 auto text_origin = cast<SPText>(orig);
311 auto *dest = sp_lpe_item;
312 auto *dest_path = cast<SPPath>(sp_lpe_item);
313 auto *dest_shape = cast<SPShape>(sp_lpe_item);
314 const gchar * id = getLPEObj()->getAttribute("linkeditem");
315 init = init || linked == "" || g_strcmp0(id, linked.c_str()) != 0;
316 /* if (sp_lpe_item->getRepr()->attribute("style")) {
317 init = false;
318 } */
319 Glib::ustring attr = "d,";
320 if (text_origin && dest_shape) {
321 auto curve = text_origin->getNormalizedBpath();
322 if (dest_path) {
323 dest->setAttribute("inkscape:original-d", sp_svg_write_path(curve));
324 }
325 dest_shape->setCurveInsync(curve);
326 dest_shape->setAttribute("d", sp_svg_write_path(curve));
327 attr = "";
328 }
329 if (g_strcmp0(linked.c_str(), id) && !is_load) {
330 dest->setAttribute("transform", nullptr);
331 }
332 original_bbox(lpeitem, false, true);
333 auto attributes_str = attributes.param_getSVGValue();
334 attr += attributes_str + ",";
335 if (attr.size() && attributes_str.empty()) {
336 attr.erase (attr.size()-1, 1);
337 }
338 auto css_properties_str = css_properties.param_getSVGValue();
339 Glib::ustring style_attr = "";
340 if (style_attr.size() && css_properties_str.empty()) {
341 style_attr.erase (style_attr.size()-1, 1);
342 }
343 style_attr += css_properties_str + ",";
344 cloneAttributes(orig, dest, attr.c_str(), style_attr.c_str(), init);
347 sync = false;
348 linked = id;
349 } else {
350 linked = "";
351 }
352}
353
355 // this leave a empty path item but keep clone
356 std::vector<SPLPEItem *> lpeitems = getCurrrentLPEItems();
357 if (!holderRemove && lpeitems.size() == 1 && !keep_paths && !on_remove_all) {
358 if (lpeitems[0] && lpeitems[0]->getAttribute("class")) {
359 Glib::ustring fromclone = sp_lpe_item->getAttribute("class");
360 size_t pos = fromclone.find("fromclone");
361 if (pos != Glib::ustring::npos && !lpeitems[0]->document->isSeeking()) {
362 if (linkeditem.lperef->getObject() && SP_ACTIVE_DESKTOP) {
363 holderRemove = true;
364 return holderRemove;
365 }
366 }
367 }
368 }
369 return false;
370}
371
373{
374 // this leave a empty path item but keep clone
375 if (holderRemove && lpeitem) {
376 if (lpeitem->getAttribute("class")) {
377 Glib::ustring fromclone = lpeitem->getAttribute("class");
378 size_t pos = fromclone.find("fromclone");
379 if (pos != Glib::ustring::npos && !lpeitem->document->isSeeking()) {
380 auto transform_copy = Util::to_opt(sp_lpe_item->getAttribute("transform"));
381 if (SPObject *owner = linkeditem.lperef->getObject()) {
382 auto oset = ObjectSet(lpeitem->document);
383 oset.add(owner);
384 oset.clone(true);
385 if (SPUse *clone = cast<SPUse>(oset.singleItem())) {
386 auto transform_use = clone->get_root_transform();
387 clone->transform *= transform_use.inverse();
388 if (Util::to_cstr(transform_copy)) {
390 sp_svg_transform_read(Util::to_cstr(transform_copy), &item_t);
391 clone->transform *= item_t;
392 }
393 // update use real transform
394 clone->doWriteTransform(clone->transform);
395 clone->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
396 }
397 }
398 }
399 }
400 }
403}
404
406{
407 auto const *current_curve_before = current_shape->curveBeforeLPE();
408 if (!current_curve_before) {
409 syncOriginal();
410 }
411 if (method != CLM_NONE) {
412 auto const *current_curve = current_shape->curve();
413 if (current_curve) {
414 curve = *current_curve;
415 }
416 }
417}
418
419} // namespace LivePathEffect
420} // namespace Inkscape
421
422/*
423 Local Variables:
424 mode:c++
425 c-file-style:"stroustrup"
426 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
427 indent-tabs-mode:nil
428 fill-column:99
429 End:
430*/
431// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
Point origin
Definition aa.cpp:227
void set_active_tool(InkscapeWindow *win, Glib::ustring const &tool)
3x3 matrix representing an affine transformation.
Definition affine.h:70
Sequence of subpaths.
Definition pathvector.h:122
std::vector< Parameter * > param_vector
Definition effect.h:178
void registerParameter(Parameter *param)
Definition effect.cpp:1704
LivePathEffectObject * lpeobj
Definition effect.h:219
bool isOnClipboard()
The lpe is on clipboard.
Definition effect.cpp:1248
std::vector< SPLPEItem * > getCurrrentLPEItems() const
Definition effect.cpp:1187
LivePathEffectObject * getLPEObj()
Definition effect.h:150
void original_bbox(SPLPEItem const *lpeitem, bool absolute=false, bool clip_mask=false, Geom::Affine base_transform=Geom::identity())
void doOnRemove(SPLPEItem const *) override
LPECloneOriginal(LivePathEffectObject *lpeobject)
bool doOnOpen(SPLPEItem const *lpeitem) override
Is performed on load document or revert If the item is fixed legacy return true.
Gtk::Widget * newWidget() override
This creates a managed widget.
void cloneAttributes(SPObject *origin, SPObject *dest, const gchar *attributes, const gchar *css_properties, bool init)
void doBeforeEffect(SPLPEItem const *lpeitem) override
Is performed each time before the effect is updated.
void doEffect(Geom::PathVector &curve) override
Glib::ustring const * param_getTooltip() const
Definition parameter.h:81
virtual Gtk::Widget * param_newWidget()=0
std::shared_ptr< SatelliteReference > lperef
Definition satellite.h:58
Glib::ustring param_getSVGValue() const override
Definition text.cpp:107
Simplified management of enumerations of svg items with UI labels.
Definition enums.h:42
Inkscape::LivePathEffect::Effect * get_lpe()
Definition lpeobject.h:51
To do: update description of desktop.
Definition desktop.h:149
Inkscape::UI::Tools::ToolBase * getTool() const
Definition desktop.h:187
Typed SVG document implementation.
Definition document.h:101
bool isSeeking() const
Definition document.h:359
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
void setAttributeOrRemoveIfEmpty(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
Definition sp-object.h:785
void setAttribute(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
void removeAttribute(char const *key)
SPDocument * document
Definition sp-object.h:188
char const * getAttribute(char const *name) const
SPObject * nthChild(unsigned index)
void requestDisplayUpdate(unsigned int flags)
Queues an deferred update of this object's display.
Geom::PathVector const * curve() const
Return a borrowed pointer to the curve (if any exists) or NULL if there is no curve.
Definition sp-shape.cpp:967
Geom::PathVector const * curveBeforeLPE() const
Return a borrowed pointer of the curve before LPE (if any exists) or NULL if there is no curve.
Definition sp-shape.cpp:975
Definition sp-use.h:25
RootCluster root
double c[8][4]
Geom::Point orig
auto ptr_to_opt(T const &p)
Create a std::optional<T> from a (generalised) pointer to T.
Definition curve.h:90
Affine identity()
Create an identity matrix.
Definition affine.h:210
void sp_bspline_do_effect(Geom::PathVector &curve, double helper_size, Geom::PathVector &hp, bool uniform)
static const Util::EnumData< Clonelpemethod > ClonelpemethodData[]
static const Util::EnumDataConverter< Clonelpemethod > CLMConverter(ClonelpemethodData, CLM_END)
void sp_spiro_do_effect(Geom::PathVector &curve)
Definition lpe-spiro.cpp:29
void pack_start(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the start of box.
Definition pack.cpp:141
auto to_opt(char const *s)
Definition optstr.h:21
auto to_cstr(std::optional< std::string > const &s)
Definition optstr.h:26
Helper class to stream background task notifications as a series of messages.
New node tool with support for multiple path editing.
Helpers for using Gtk::Boxes, encapsulating large changes between GTK3 & GTK4.
Inkscape::SVG::PathString - builder for SVG path strings.
Ocnode * child[8]
Definition quantize.cpp:33
SPCSSAttr * sp_repr_css_attr_new()
Creates an empty SPCSSAttr (a class for manipulating CSS style properties).
Definition repr-css.cpp:67
void sp_repr_css_write_string(SPCSSAttr *css, Glib::ustring &str)
Write a style attribute string from a list of properties stored in an SPCSAttr object.
Definition repr-css.cpp:242
char const * sp_repr_css_property(SPCSSAttr *css, gchar const *name, gchar const *defval)
Returns a character string of the value of a given style property or a default value if the attribute...
Definition repr-css.cpp:147
void sp_repr_css_set_property(SPCSSAttr *css, gchar const *name, gchar const *value)
Set a style property to a new value (e.g.
Definition repr-css.cpp:190
void sp_repr_css_attr_add_from_string(SPCSSAttr *css, gchar const *p)
Use libcroco to parse a string for CSS properties and then merge them into an existing SPCSSAttr.
Definition repr-css.cpp:340
TODO: insert short description here.
SPCSSAttr - interface for CSS Attributes.
void sp_lpe_item_update_patheffect(SPLPEItem *lpeitem, bool wholetree, bool write, bool with_satellites)
Calls any registered handlers for the update_patheffect action.
std::list< PathEffectSharedPtr > PathEffectList
Definition sp-lpe-item.h:45
Simplified management of enumerations of svg items with UI labels.
Definition enums.h:27
Definition curve.h:24
bool sp_svg_transform_read(gchar const *str, Geom::Affine *transform)
static void sp_svg_write_path(Inkscape::SVG::PathString &str, Geom::Path const &p, bool normalize=false)
Definition svg-path.cpp:109
int index
SPDesktop * desktop
void init(int argc, char **argv, Toy *t, int width=600, int height=600)