summaryrefslogtreecommitdiffstats
path: root/src/live_effects/parameter
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:29:01 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:29:01 +0000
commit35a96bde514a8897f6f0fcc41c5833bf63df2e2a (patch)
tree657d15a03cc46bd099fc2c6546a7a4ad43815d9f /src/live_effects/parameter
parentInitial commit. (diff)
downloadinkscape-35a96bde514a8897f6f0fcc41c5833bf63df2e2a.tar.xz
inkscape-35a96bde514a8897f6f0fcc41c5833bf63df2e2a.zip
Adding upstream version 1.0.2.upstream/1.0.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/live_effects/parameter')
-rw-r--r--src/live_effects/parameter/array.cpp102
-rw-r--r--src/live_effects/parameter/array.h162
-rw-r--r--src/live_effects/parameter/bool.cpp113
-rw-r--r--src/live_effects/parameter/bool.h57
-rw-r--r--src/live_effects/parameter/colorpicker.cpp148
-rw-r--r--src/live_effects/parameter/colorpicker.h62
-rw-r--r--src/live_effects/parameter/enum.h113
-rw-r--r--src/live_effects/parameter/fontbutton.cpp106
-rw-r--r--src/live_effects/parameter/fontbutton.h63
-rw-r--r--src/live_effects/parameter/hidden.cpp94
-rw-r--r--src/live_effects/parameter/hidden.h71
-rw-r--r--src/live_effects/parameter/item-reference.cpp45
-rw-r--r--src/live_effects/parameter/item-reference.h57
-rw-r--r--src/live_effects/parameter/item.cpp296
-rw-r--r--src/live_effects/parameter/item.h81
-rw-r--r--src/live_effects/parameter/message.cpp127
-rw-r--r--src/live_effects/parameter/message.h78
-rw-r--r--src/live_effects/parameter/originalitem.cpp107
-rw-r--r--src/live_effects/parameter/originalitem.h46
-rw-r--r--src/live_effects/parameter/originalitemarray.cpp459
-rw-r--r--src/live_effects/parameter/originalitemarray.h124
-rw-r--r--src/live_effects/parameter/originalpath.cpp148
-rw-r--r--src/live_effects/parameter/originalpath.h55
-rw-r--r--src/live_effects/parameter/originalpatharray.cpp542
-rw-r--r--src/live_effects/parameter/originalpatharray.h134
-rw-r--r--src/live_effects/parameter/parameter.cpp208
-rw-r--r--src/live_effects/parameter/parameter.h158
-rw-r--r--src/live_effects/parameter/path-reference.cpp44
-rw-r--r--src/live_effects/parameter/path-reference.h57
-rw-r--r--src/live_effects/parameter/path.cpp591
-rw-r--r--src/live_effects/parameter/path.h112
-rw-r--r--src/live_effects/parameter/point.cpp268
-rw-r--r--src/live_effects/parameter/point.h77
-rw-r--r--src/live_effects/parameter/powerstrokepointarray.cpp308
-rw-r--r--src/live_effects/parameter/powerstrokepointarray.h93
-rw-r--r--src/live_effects/parameter/random.cpp212
-rw-r--r--src/live_effects/parameter/random.h73
-rw-r--r--src/live_effects/parameter/satellitesarray.cpp575
-rw-r--r--src/live_effects/parameter/satellitesarray.h114
-rw-r--r--src/live_effects/parameter/text.cpp175
-rw-r--r--src/live_effects/parameter/text.h97
-rw-r--r--src/live_effects/parameter/togglebutton.cpp210
-rw-r--r--src/live_effects/parameter/togglebutton.h73
-rw-r--r--src/live_effects/parameter/transformedpoint.cpp212
-rw-r--r--src/live_effects/parameter/transformedpoint.h86
-rw-r--r--src/live_effects/parameter/unit.cpp110
-rw-r--r--src/live_effects/parameter/unit.h56
-rw-r--r--src/live_effects/parameter/vector.cpp251
-rw-r--r--src/live_effects/parameter/vector.h87
49 files changed, 7637 insertions, 0 deletions
diff --git a/src/live_effects/parameter/array.cpp b/src/live_effects/parameter/array.cpp
new file mode 100644
index 0000000..376252f
--- /dev/null
+++ b/src/live_effects/parameter/array.cpp
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Johan Engelen 2008 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/parameter/array.h"
+#include "helper-fns.h"
+#include <2geom/coord.h>
+#include <2geom/point.h>
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+template <>
+double
+ArrayParam<double>::readsvg(const gchar * str)
+{
+ double newx = Geom::infinity();
+ sp_svg_number_read_d(str, &newx);
+ return newx;
+}
+
+template <>
+float
+ArrayParam<float>::readsvg(const gchar * str)
+{
+ float newx = Geom::infinity();
+ sp_svg_number_read_f(str, &newx);
+ return newx;
+}
+
+template <>
+Geom::Point
+ArrayParam<Geom::Point>::readsvg(const gchar * str)
+{
+ gchar ** strarray = g_strsplit(str, ",", 2);
+ double newx, newy;
+ unsigned int success = sp_svg_number_read_d(strarray[0], &newx);
+ success += sp_svg_number_read_d(strarray[1], &newy);
+ g_strfreev (strarray);
+ if (success == 2) {
+ return Geom::Point(newx, newy);
+ }
+ return Geom::Point(Geom::infinity(),Geom::infinity());
+}
+
+
+template <>
+std::vector<Satellite>
+ArrayParam<std::vector<Satellite > >::readsvg(const gchar * str)
+{
+ std::vector<Satellite> subpath_satellites;
+ if (!str) {
+ return subpath_satellites;
+ }
+ gchar ** strarray = g_strsplit(str, "@", 0);
+ gchar ** iter = strarray;
+ while (*iter != nullptr) {
+ gchar ** strsubarray = g_strsplit(*iter, ",", 8);
+ if (*strsubarray[7]) {//steps always > 0
+ Satellite *satellite = new Satellite();
+ satellite->setSatelliteType(g_strstrip(strsubarray[0]));
+ satellite->is_time = strncmp(strsubarray[1],"1",1) == 0;
+ satellite->selected = strncmp(strsubarray[2],"1",1) == 0;
+ satellite->has_mirror = strncmp(strsubarray[3],"1",1) == 0;
+ satellite->hidden = strncmp(strsubarray[4],"1",1) == 0;
+ double amount,angle;
+ float stepsTmp;
+ sp_svg_number_read_d(strsubarray[5], &amount);
+ sp_svg_number_read_d(strsubarray[6], &angle);
+ sp_svg_number_read_f(g_strstrip(strsubarray[7]), &stepsTmp);
+ unsigned int steps = (unsigned int)stepsTmp;
+ satellite->amount = amount;
+ satellite->angle = angle;
+ satellite->steps = steps;
+ subpath_satellites.push_back(*satellite);
+ }
+ g_strfreev (strsubarray);
+ iter++;
+ }
+ g_strfreev (strarray);
+ return subpath_satellites;
+}
+
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/array.h b/src/live_effects/parameter/array.h
new file mode 100644
index 0000000..1c9ffc4
--- /dev/null
+++ b/src/live_effects/parameter/array.h
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_ARRAY_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_ARRAY_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Copyright (C) Johan Engelen 2008 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <vector>
+
+#include <glib.h>
+
+#include "live_effects/parameter/parameter.h"
+
+#include "helper/geom-satellite.h"
+#include "svg/svg.h"
+#include "svg/stringstream.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+template <typename StorageType>
+class ArrayParam : public Parameter {
+public:
+ ArrayParam( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect,
+ size_t n = 0 )
+ : Parameter(label, tip, key, wr, effect), _vector(n), _default_size(n)
+ {
+
+ }
+
+ ~ArrayParam() override = default;;
+
+ std::vector<StorageType> const & data() const {
+ return _vector;
+ }
+
+ Gtk::Widget * param_newWidget() override {
+ return nullptr;
+ }
+
+ bool param_readSVGValue(const gchar * strvalue) override {
+ _vector.clear();
+ gchar ** strarray = g_strsplit(strvalue, "|", 0);
+ gchar ** iter = strarray;
+ while (*iter != nullptr) {
+ _vector.push_back( readsvg(*iter) );
+ iter++;
+ }
+ g_strfreev (strarray);
+ return true;
+ }
+ void param_update_default(const gchar * default_value) override{};
+ Glib::ustring param_getSVGValue() const override {
+ Inkscape::SVGOStringStream os;
+ writesvg(os, _vector);
+ return os.str();
+ }
+
+ Glib::ustring param_getDefaultSVGValue() const override {
+ return "";
+ }
+
+ void param_setValue(std::vector<StorageType> const &new_vector) {
+ _vector = new_vector;
+ }
+
+ void param_set_default() override {
+ param_setValue( std::vector<StorageType>(_default_size) );
+ }
+
+ void param_set_and_write_new_value(std::vector<StorageType> const &new_vector) {
+ Inkscape::SVGOStringStream os;
+ writesvg(os, new_vector);
+ gchar * str = g_strdup(os.str().c_str());
+ param_write_to_repr(str);
+ g_free(str);
+ }
+
+protected:
+ std::vector<StorageType> _vector;
+ size_t _default_size;
+
+ void writesvg(SVGOStringStream &str, std::vector<StorageType> const &vector) const {
+ for (unsigned int i = 0; i < vector.size(); ++i) {
+ if (i != 0) {
+ // separate items with pipe symbol
+ str << " | ";
+ }
+ writesvgData(str,vector[i]);
+ }
+ }
+
+ void writesvgData(SVGOStringStream &str, float const &vector_data) const {
+ str << vector_data;
+ }
+
+ void writesvgData(SVGOStringStream &str, double const &vector_data) const {
+ str << vector_data;
+ }
+
+ void writesvgData(SVGOStringStream &str, Geom::Point const &vector_data) const {
+ str << vector_data;
+ }
+
+ void writesvgData(SVGOStringStream &str, std::vector<Satellite> const &vector_data) const {
+ for (size_t i = 0; i < vector_data.size(); ++i) {
+ if (i != 0) {
+ // separate items with @ symbol ¿Any other?
+ str << " @ ";
+ }
+ str << vector_data[i].getSatelliteTypeGchar();
+ str << ",";
+ str << vector_data[i].is_time;
+ str << ",";
+ str << vector_data[i].selected;
+ str << ",";
+ str << vector_data[i].has_mirror;
+ str << ",";
+ str << vector_data[i].hidden;
+ str << ",";
+ str << vector_data[i].amount;
+ str << ",";
+ str << vector_data[i].angle;
+ str << ",";
+ str << static_cast<int>(vector_data[i].steps);
+ }
+ }
+
+ StorageType readsvg(const gchar * str);
+
+private:
+ ArrayParam(const ArrayParam&);
+ ArrayParam& operator=(const ArrayParam&);
+};
+
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/bool.cpp b/src/live_effects/parameter/bool.cpp
new file mode 100644
index 0000000..d5e77ca
--- /dev/null
+++ b/src/live_effects/parameter/bool.cpp
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "ui/widget/registered-widget.h"
+#include "live_effects/parameter/bool.h"
+#include "live_effects/effect.h"
+#include "svg/svg.h"
+#include "svg/stringstream.h"
+#include "inkscape.h"
+#include "verbs.h"
+#include "helper-fns.h"
+#include <glibmm/i18n.h>
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+BoolParam::BoolParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect, bool default_value)
+ : Parameter(label, tip, key, wr, effect), value(default_value), defvalue(default_value)
+{
+}
+
+BoolParam::~BoolParam()
+= default;
+
+void
+BoolParam::param_set_default()
+{
+ param_setValue(defvalue);
+}
+
+void
+BoolParam::param_update_default(bool const default_value)
+{
+ defvalue = default_value;
+}
+
+void
+BoolParam::param_update_default(const gchar * default_value)
+{
+ param_update_default(helperfns_read_bool(default_value, defvalue));
+}
+
+bool
+BoolParam::param_readSVGValue(const gchar * strvalue)
+{
+ param_setValue(helperfns_read_bool(strvalue, defvalue));
+ return true; // not correct: if value is unacceptable, should return false!
+}
+
+Glib::ustring
+BoolParam::param_getSVGValue() const
+{
+ return value ? "true" : "false";
+}
+
+Glib::ustring
+BoolParam::param_getDefaultSVGValue() const
+{
+ return defvalue ? "true" : "false";
+}
+
+Gtk::Widget *
+BoolParam::param_newWidget()
+{
+ if(widget_is_visible){
+ Inkscape::UI::Widget::RegisteredCheckButton * checkwdg = Gtk::manage(
+ new Inkscape::UI::Widget::RegisteredCheckButton( param_label,
+ param_tooltip,
+ param_key,
+ *param_wr,
+ false,
+ param_effect->getRepr(),
+ param_effect->getSPDoc()) );
+
+ checkwdg->setActive(value);
+ checkwdg->setProgrammatically = false;
+ checkwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change bool parameter"));
+ return dynamic_cast<Gtk::Widget *> (checkwdg);
+ } else {
+ return nullptr;
+ }
+}
+
+void
+BoolParam::param_setValue(bool newvalue)
+{
+ if (value != newvalue) {
+ param_effect->refresh_widgets = true;
+ }
+ value = newvalue;
+}
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/bool.h b/src/live_effects/parameter/bool.h
new file mode 100644
index 0000000..72af921
--- /dev/null
+++ b/src/live_effects/parameter/bool.h
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_BOOL_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_BOOL_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <glib.h>
+
+#include "live_effects/parameter/parameter.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+
+class BoolParam : public Parameter {
+public:
+ BoolParam( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect,
+ bool default_value = false);
+ ~BoolParam() override;
+ BoolParam(const BoolParam&) = delete;
+ BoolParam& operator=(const BoolParam&) = delete;
+
+ Gtk::Widget * param_newWidget() override;
+
+ bool param_readSVGValue(const gchar * strvalue) override;
+ Glib::ustring param_getSVGValue() const override;
+ Glib::ustring param_getDefaultSVGValue() const override;
+
+ void param_setValue(bool newvalue);
+ void param_set_default() override;
+ void param_update_default(bool const default_value);
+ void param_update_default(const gchar * default_value) override;
+ bool get_value() const { return value; };
+ inline operator bool() const { return value; };
+
+private:
+ bool value;
+ bool defvalue;
+};
+
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/colorpicker.cpp b/src/live_effects/parameter/colorpicker.cpp
new file mode 100644
index 0000000..d430069
--- /dev/null
+++ b/src/live_effects/parameter/colorpicker.cpp
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Authors:
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <gtkmm.h>
+#include "ui/widget/registered-widget.h"
+#include "live_effects/parameter/colorpicker.h"
+#include "live_effects/effect.h"
+#include "ui/widget/color-picker.h"
+#include "svg/svg.h"
+#include "svg/svg-color.h"
+#include "color.h"
+#include "inkscape.h"
+#include "svg/stringstream.h"
+#include "verbs.h"
+#include "document.h"
+#include "document-undo.h"
+
+#include <glibmm/i18n.h>
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+ColorPickerParam::ColorPickerParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect, const guint32 default_color )
+ : Parameter(label, tip, key, wr, effect),
+ value(default_color),
+ defvalue(default_color)
+{
+
+}
+
+void
+ColorPickerParam::param_set_default()
+{
+ param_setValue(defvalue);
+}
+
+static guint32 sp_read_color_alpha(gchar const *str, guint32 def)
+{
+ guint32 val = 0;
+ if (str == nullptr) return def;
+ while ((*str <= ' ') && *str) str++;
+ if (!*str) return def;
+
+ if (str[0] == '#') {
+ gint i;
+ for (i = 1; str[i]; i++) {
+ int hexval;
+ if (str[i] >= '0' && str[i] <= '9')
+ hexval = str[i] - '0';
+ else if (str[i] >= 'A' && str[i] <= 'F')
+ hexval = str[i] - 'A' + 10;
+ else if (str[i] >= 'a' && str[i] <= 'f')
+ hexval = str[i] - 'a' + 10;
+ else
+ break;
+ val = (val << 4) + hexval;
+ }
+ if (i != 1 + 8) {
+ return def;
+ }
+ }
+ return val;
+}
+
+void
+ColorPickerParam::param_update_default(const gchar * default_value)
+{
+ defvalue = sp_read_color_alpha(default_value, 0x000000ff);
+}
+
+bool
+ColorPickerParam::param_readSVGValue(const gchar * strvalue)
+{
+ param_setValue(sp_read_color_alpha(strvalue, 0x000000ff));
+ return true;
+}
+
+Glib::ustring
+ColorPickerParam::param_getSVGValue() const
+{
+ gchar c[32];
+ sprintf(c, "#%08x", value);
+ return c;
+}
+
+Glib::ustring
+ColorPickerParam::param_getDefaultSVGValue() const
+{
+ gchar c[32];
+ sprintf(c, "#%08x", defvalue);
+ return c;
+}
+
+Gtk::Widget *
+ColorPickerParam::param_newWidget()
+{
+ Gtk::HBox *hbox = Gtk::manage(new Gtk::HBox());
+
+ hbox->set_border_width(5);
+ hbox->set_homogeneous(false);
+ hbox->set_spacing(2);
+ Inkscape::UI::Widget::RegisteredColorPicker * colorpickerwdg =
+ new Inkscape::UI::Widget::RegisteredColorPicker( param_label,
+ param_label,
+ param_tooltip,
+ param_key,
+ param_key + "_opacity_LPE",
+ *param_wr,
+ param_effect->getRepr(),
+ param_effect->getSPDoc() );
+ SPDocument *document = param_effect->getSPDoc();
+ bool saved = DocumentUndo::getUndoSensitive(document);
+ DocumentUndo::setUndoSensitive(document, false);
+ colorpickerwdg->setRgba32(value);
+ DocumentUndo::setUndoSensitive(document, saved);
+ colorpickerwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change color button parameter"));
+ hbox->pack_start(*dynamic_cast<Gtk::Widget *> (colorpickerwdg), true, true);
+ return dynamic_cast<Gtk::Widget *> (hbox);
+}
+
+void
+ColorPickerParam::param_setValue(const guint32 newvalue)
+{
+ value = newvalue;
+}
+
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/colorpicker.h b/src/live_effects/parameter/colorpicker.h
new file mode 100644
index 0000000..ef917b2
--- /dev/null
+++ b/src/live_effects/parameter/colorpicker.h
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_COLOR_BUTTON_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_COLOR_BUTTON_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Authors:
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#include <glib.h>
+#include "live_effects/parameter/parameter.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class ColorPickerParam : public Parameter {
+public:
+ ColorPickerParam( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect,
+ const guint32 default_color = 0x000000ff);
+ ~ColorPickerParam() override = default;
+
+ Gtk::Widget * param_newWidget() override;
+ bool param_readSVGValue(const gchar * strvalue) override;
+ void param_update_default(const gchar * default_value) override;
+ Glib::ustring param_getSVGValue() const override;
+ Glib::ustring param_getDefaultSVGValue() const override;
+
+ void param_setValue(guint32 newvalue);
+
+ void param_set_default() override;
+
+ const guint32 get_value() const { return value; };
+
+private:
+ ColorPickerParam(const ColorPickerParam&) = delete;
+ ColorPickerParam& operator=(const ColorPickerParam&) = delete;
+ guint32 value;
+ guint32 defvalue;
+};
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/enum.h b/src/live_effects/parameter/enum.h
new file mode 100644
index 0000000..2519593
--- /dev/null
+++ b/src/live_effects/parameter/enum.h
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_ENUM_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_ENUM_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "ui/widget/registered-enums.h"
+#include <glibmm/ustring.h>
+#include "live_effects/effect.h"
+#include "live_effects/parameter/parameter.h"
+#include "verbs.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+template<typename E> class EnumParam : public Parameter {
+public:
+ EnumParam( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ const Util::EnumDataConverter<E>& c,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect,
+ E default_value,
+ bool sort = true)
+ : Parameter(label, tip, key, wr, effect)
+ {
+ enumdataconv = &c;
+ defvalue = default_value;
+ value = defvalue;
+ sorted = sort;
+ };
+
+ ~EnumParam() override = default;;
+ EnumParam(const EnumParam&) = delete;
+ EnumParam& operator=(const EnumParam&) = delete;
+
+ Gtk::Widget * param_newWidget() override {
+ Inkscape::UI::Widget::RegisteredEnum<E> *regenum = Gtk::manage (
+ new Inkscape::UI::Widget::RegisteredEnum<E>( param_label, param_tooltip,
+ param_key, *enumdataconv, *param_wr, param_effect->getRepr(), param_effect->getSPDoc(), sorted ) );
+
+ regenum->set_active_by_id(value);
+ regenum->combobox()->setProgrammatically = false;
+ regenum->combobox()->signal_changed().connect(sigc::mem_fun (*this, &EnumParam::_on_change_combo));
+ regenum->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change enumeration parameter"));
+
+ return dynamic_cast<Gtk::Widget *> (regenum);
+ };
+ void _on_change_combo() { param_effect->refresh_widgets = true; }
+ bool param_readSVGValue(const gchar * strvalue) override {
+ if (!strvalue) {
+ param_set_default();
+ return true;
+ }
+
+ param_set_value( enumdataconv->get_id_from_key(Glib::ustring(strvalue)) );
+
+ return true;
+ };
+ Glib::ustring param_getSVGValue() const override {
+ return enumdataconv->get_key(value);
+ };
+
+ Glib::ustring param_getDefaultSVGValue() const override {
+ return enumdataconv->get_key(defvalue).c_str();
+ };
+
+ E get_value() const {
+ return value;
+ }
+
+ inline operator E() const {
+ return value;
+ };
+
+ void param_set_default() override {
+ param_set_value(defvalue);
+ }
+
+ void param_update_default(E default_value) {
+ defvalue = default_value;
+ }
+
+ void param_update_default(const gchar * default_value) override {
+ param_update_default(enumdataconv->get_id_from_key(Glib::ustring(default_value)));
+ }
+
+ void param_set_value(E val) {
+ value = val;
+ }
+
+private:
+ E value;
+ E defvalue;
+ bool sorted;
+
+ const Util::EnumDataConverter<E> * enumdataconv;
+};
+
+
+}; //namespace LivePathEffect
+
+}; //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/fontbutton.cpp b/src/live_effects/parameter/fontbutton.cpp
new file mode 100644
index 0000000..523c7d8
--- /dev/null
+++ b/src/live_effects/parameter/fontbutton.cpp
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Authors:
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <gtkmm.h>
+#include "ui/widget/registered-widget.h"
+#include "live_effects/parameter/fontbutton.h"
+#include "live_effects/effect.h"
+#include "ui/widget/font-button.h"
+#include "svg/svg.h"
+#include "svg/stringstream.h"
+#include "verbs.h"
+
+#include <glibmm/i18n.h>
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+FontButtonParam::FontButtonParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect, const Glib::ustring default_value )
+ : Parameter(label, tip, key, wr, effect),
+ value(default_value),
+ defvalue(default_value)
+{
+}
+
+void
+FontButtonParam::param_set_default()
+{
+ param_setValue(defvalue);
+}
+
+void
+FontButtonParam::param_update_default(const gchar * default_value)
+{
+ defvalue = Glib::ustring(default_value);
+}
+
+bool
+FontButtonParam::param_readSVGValue(const gchar * strvalue)
+{
+ Inkscape::SVGOStringStream os;
+ os << strvalue;
+ param_setValue((Glib::ustring)os.str());
+ return true;
+}
+
+Glib::ustring
+FontButtonParam::param_getSVGValue() const
+{
+ return value.c_str();
+}
+
+Glib::ustring
+FontButtonParam::param_getDefaultSVGValue() const
+{
+ return defvalue;
+}
+
+
+
+Gtk::Widget *
+FontButtonParam::param_newWidget()
+{
+ Inkscape::UI::Widget::RegisteredFontButton * fontbuttonwdg = Gtk::manage(
+ new Inkscape::UI::Widget::RegisteredFontButton( param_label,
+ param_tooltip,
+ param_key,
+ *param_wr,
+ param_effect->getRepr(),
+ param_effect->getSPDoc() ) );
+ Glib::ustring fontspec = param_getSVGValue();
+ fontbuttonwdg->setValue( fontspec);
+ fontbuttonwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change font button parameter"));
+ return dynamic_cast<Gtk::Widget *> (fontbuttonwdg);
+}
+
+void
+FontButtonParam::param_setValue(const Glib::ustring newvalue)
+{
+ if (value != newvalue) {
+ param_effect->refresh_widgets = true;
+ }
+ value = newvalue;
+}
+
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/fontbutton.h b/src/live_effects/parameter/fontbutton.h
new file mode 100644
index 0000000..4f29509
--- /dev/null
+++ b/src/live_effects/parameter/fontbutton.h
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_FONT_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_FONT_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Authors:
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#include <glib.h>
+#include "live_effects/parameter/parameter.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class FontButtonParam : public Parameter {
+public:
+ FontButtonParam( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect,
+ const Glib::ustring default_value = "Sans 10");
+ ~FontButtonParam() override = default;
+
+ Gtk::Widget * param_newWidget() override;
+ bool param_readSVGValue(const gchar * strvalue) override;
+ void param_update_default(const gchar * default_value) override;
+ Glib::ustring param_getSVGValue() const override;
+ Glib::ustring param_getDefaultSVGValue() const override;
+
+ void param_setValue(Glib::ustring newvalue);
+
+ void param_set_default() override;
+
+ const Glib::ustring get_value() const { return defvalue; };
+
+private:
+ FontButtonParam(const FontButtonParam&) = delete;
+ FontButtonParam& operator=(const FontButtonParam&) = delete;
+ Glib::ustring value;
+ Glib::ustring defvalue;
+
+};
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/hidden.cpp b/src/live_effects/parameter/hidden.cpp
new file mode 100644
index 0000000..dcb468e
--- /dev/null
+++ b/src/live_effects/parameter/hidden.cpp
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) jabiertxof 2017 <jabier.arraiza@marker.es>
+ * Copyright (C) Maximilian Albert 2008 <maximilian.albert@gmail.com>
+ *
+ * Authors:
+ * Jabiertxof
+ * Maximilian Albert
+ * Johan Engelen
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+
+#include "live_effects/parameter/hidden.h"
+#include "live_effects/effect.h"
+#include "svg/svg.h"
+#include "svg/stringstream.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+HiddenParam::HiddenParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect, const Glib::ustring default_value, bool is_visible)
+ : Parameter(label, tip, key, wr, effect),
+ value(default_value),
+ defvalue(default_value)
+{
+ param_widget_is_visible(is_visible);
+}
+
+void
+HiddenParam::param_set_default()
+{
+ param_setValue(defvalue);
+}
+
+void
+HiddenParam::param_update_default(const gchar * default_value)
+{
+ defvalue = (Glib::ustring)default_value;
+}
+
+
+bool
+HiddenParam::param_readSVGValue(const gchar * strvalue)
+{
+ param_setValue(strvalue);
+ return true;
+}
+
+Glib::ustring
+HiddenParam::param_getSVGValue() const
+{
+ return value;
+}
+
+Glib::ustring
+HiddenParam::param_getDefaultSVGValue() const
+{
+ return defvalue;
+}
+
+Gtk::Widget *
+HiddenParam::param_newWidget()
+{
+ return nullptr;
+}
+
+void
+HiddenParam::param_setValue(const Glib::ustring newvalue, bool write)
+{
+ value = newvalue;
+ if (write) {
+ param_write_to_repr(value.c_str());
+ }
+}
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/hidden.h b/src/live_effects/parameter/hidden.h
new file mode 100644
index 0000000..0a0df17
--- /dev/null
+++ b/src/live_effects/parameter/hidden.h
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_HIDDEN_H
+#define INKSCAPE_LIVEPATHEFFECT_HIDDEN_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Authors:
+ * Jabiertxof
+ * Maximilian Albert
+ * Johan Engelen
+ *
+ * Copyright (C) jabiertxof 2017 <jabier.arraiza@marker.es>
+ * Copyright (C) Maximilian Albert 2008 <maximilian.albert@gmail.com>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/parameter/parameter.h"
+
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class HiddenParam : public Parameter {
+public:
+ HiddenParam( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect,
+ const Glib::ustring default_value = "",
+ bool widget_is_visible = false);
+ ~HiddenParam() override = default;
+
+ Gtk::Widget * param_newWidget() override;
+
+ bool param_readSVGValue(const gchar * strvalue) override;
+ Glib::ustring param_getSVGValue() const override;
+ Glib::ustring param_getDefaultSVGValue() const override;
+
+ void param_setValue(Glib::ustring newvalue, bool write = false);
+ void param_set_default() override;
+ void param_update_default(const gchar * default_value) override;
+
+ const Glib::ustring get_value() const { return value; };
+
+private:
+ HiddenParam(const HiddenParam&) = delete;
+ HiddenParam& operator=(const HiddenParam&) = delete;
+ Glib::ustring value;
+ Glib::ustring defvalue;
+};
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/item-reference.cpp b/src/live_effects/parameter/item-reference.cpp
new file mode 100644
index 0000000..cea4901
--- /dev/null
+++ b/src/live_effects/parameter/item-reference.cpp
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * The reference corresponding to href of LPE Item parameter.
+ *
+ * Copyright (C) 2008 Johan Engelen
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/parameter/item-reference.h"
+
+#include "object/sp-shape.h"
+#include "object/sp-text.h"
+#include "object/sp-item-group.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+bool ItemReference::_acceptObject(SPObject * const obj) const
+{
+ if (SP_IS_SHAPE(obj) || SP_IS_TEXT(obj) || SP_IS_GROUP(obj)) {
+ /* Refuse references to lpeobject */
+ if (obj == getOwner()) {
+ return false;
+ }
+ // TODO: check whether the referred item has this LPE applied, if so: deny deny deny!
+ return URIReference::_acceptObject(obj);
+ } else {
+ return false;
+ }
+}
+
+} // namespace LivePathEffect
+} // namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/item-reference.h b/src/live_effects/parameter/item-reference.h
new file mode 100644
index 0000000..64aaac0
--- /dev/null
+++ b/src/live_effects/parameter/item-reference.h
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef SEEN_LPE_ITEM_REFERENCE_H
+#define SEEN_LPE_ITEM_REFERENCE_H
+
+/*
+ * Copyright (C) 2008-2012 Authors
+ * Authors: Johan Engelen
+ * Abhishek Sharma
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "object/uri-references.h"
+
+class SPItem;
+namespace Inkscape {
+namespace XML { class Node; }
+
+namespace LivePathEffect {
+
+/**
+ * The reference corresponding to href of LPE ItemParam.
+ */
+class ItemReference : public Inkscape::URIReference {
+public:
+ ItemReference(SPObject *owner) : URIReference(owner) {}
+
+ SPItem *getObject() const {
+ return (SPItem *)URIReference::getObject();
+ }
+
+protected:
+ bool _acceptObject(SPObject * const obj) const override;
+
+private:
+ ItemReference(const ItemReference&) = delete;
+ ItemReference& operator=(const ItemReference&) = delete;
+};
+
+} // namespace LivePathEffect
+
+} // namespace Inkscape
+
+
+
+#endif /* !SEEN_LPE_PATH_REFERENCE_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/item.cpp b/src/live_effects/parameter/item.cpp
new file mode 100644
index 0000000..e54cb08
--- /dev/null
+++ b/src/live_effects/parameter/item.cpp
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ * Abhishek Sharma
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/parameter/item.h"
+#include "live_effects/lpeobject.h"
+#include "live_effects/lpe-clone-original.h"
+#include <glibmm/i18n.h>
+
+#include <gtkmm/button.h>
+#include <gtkmm/label.h>
+
+#include "bad-uri-exception.h"
+#include "ui/widget/point.h"
+
+#include "live_effects/effect.h"
+#include "live_effects/lpeobject.h"
+#include "live_effects/lpe-clone-original.h"
+#include "svg/svg.h"
+
+#include "desktop.h"
+#include "inkscape.h"
+#include "message-stack.h"
+#include "selection-chemistry.h"
+#include "ui/icon-loader.h"
+#include "xml/repr.h"
+// clipboard support
+#include "ui/clipboard.h"
+// required for linking to other paths
+#include "object/uri.h"
+
+#include "ui/icon-names.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+ItemParam::ItemParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect, const gchar * default_value)
+ : Parameter(label, tip, key, wr, effect),
+ changed(true),
+ href(nullptr),
+ ref( (SPObject*)effect->getLPEObj() )
+{
+ last_transform = Geom::identity();
+ defvalue = g_strdup(default_value);
+ ref_changed_connection = ref.changedSignal().connect(sigc::mem_fun(*this, &ItemParam::ref_changed));
+}
+
+ItemParam::~ItemParam()
+{
+ remove_link();
+ g_free(defvalue);
+}
+
+void
+ItemParam::param_set_default()
+{
+ param_readSVGValue(defvalue);
+}
+
+void
+ItemParam::param_update_default(const gchar * default_value){
+ defvalue = strdup(default_value);
+}
+
+void
+ItemParam::param_set_and_write_default()
+{
+ param_write_to_repr(defvalue);
+}
+
+bool
+ItemParam::param_readSVGValue(const gchar * strvalue)
+{
+ if (strvalue) {
+ remove_link();
+ if (strvalue[0] == '#') {
+ if (href)
+ g_free(href);
+ href = g_strdup(strvalue);
+ try {
+ ref.attach(Inkscape::URI(href));
+ //lp:1299948
+ SPItem* i = ref.getObject();
+ if (i) {
+ linked_modified_callback(i, SP_OBJECT_MODIFIED_FLAG);
+ } // else: document still processing new events. Repr of the linked object not created yet.
+ } catch (Inkscape::BadURIException &e) {
+ g_warning("%s", e.what());
+ ref.detach();
+ }
+ }
+ emit_changed();
+ return true;
+ }
+
+ return false;
+}
+
+Glib::ustring
+ItemParam::param_getSVGValue() const
+{
+ if (href) {
+ return href;
+ }
+ return "";
+}
+
+Glib::ustring
+ItemParam::param_getDefaultSVGValue() const
+{
+ return defvalue;
+}
+
+Gtk::Widget *
+ItemParam::param_newWidget()
+{
+ Gtk::HBox * _widget = Gtk::manage(new Gtk::HBox());
+ Gtk::Image *pIcon = Gtk::manage(sp_get_icon_image("edit-clone", Gtk::ICON_SIZE_BUTTON));
+ Gtk::Button * pButton = Gtk::manage(new Gtk::Button());
+ Gtk::Label* pLabel = Gtk::manage(new Gtk::Label(param_label));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pLabel, true, true);
+ pLabel->set_tooltip_text(param_tooltip);
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &ItemParam::on_link_button_click));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true);
+ pButton->set_tooltip_text(_("Link to item on clipboard"));
+
+ static_cast<Gtk::HBox*>(_widget)->show_all_children();
+
+ return dynamic_cast<Gtk::Widget *> (_widget);
+}
+
+void
+ItemParam::emit_changed()
+{
+ changed = true;
+ signal_item_changed.emit();
+}
+
+
+void
+ItemParam::addCanvasIndicators(SPLPEItem const*/*lpeitem*/, std::vector<Geom::PathVector> &hp_vec)
+{
+}
+
+
+void
+ItemParam::start_listening(SPObject * to)
+{
+ if ( to == nullptr ) {
+ return;
+ }
+ linked_delete_connection = to->connectDelete(sigc::mem_fun(*this, &ItemParam::linked_delete));
+ linked_modified_connection = to->connectModified(sigc::mem_fun(*this, &ItemParam::linked_modified));
+ if (SP_IS_ITEM(to)) {
+ linked_transformed_connection = SP_ITEM(to)->connectTransformed(sigc::mem_fun(*this, &ItemParam::linked_transformed));
+ }
+ linked_modified(to, SP_OBJECT_MODIFIED_FLAG); // simulate linked_modified signal, so that path data is updated
+}
+
+void
+ItemParam::quit_listening()
+{
+ linked_modified_connection.disconnect();
+ linked_delete_connection.disconnect();
+ linked_transformed_connection.disconnect();
+}
+
+void
+ItemParam::ref_changed(SPObject */*old_ref*/, SPObject *new_ref)
+{
+ quit_listening();
+ if ( new_ref ) {
+ start_listening(new_ref);
+ }
+}
+
+void
+ItemParam::remove_link()
+{
+ if (href) {
+ ref.detach();
+ g_free(href);
+ href = nullptr;
+ }
+}
+
+void
+ItemParam::linked_delete(SPObject */*deleted*/)
+{
+ quit_listening();
+ remove_link();
+}
+
+void ItemParam::linked_modified(SPObject *linked_obj, guint flags)
+{
+ linked_modified_callback(linked_obj, flags);
+}
+
+void ItemParam::linked_transformed(Geom::Affine const *rel_transf, SPItem *moved_item)
+{
+ linked_transformed_callback(rel_transf, moved_item);
+}
+
+void
+ItemParam::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
+ItemParam::linked_transformed_callback(Geom::Affine const *rel_transf, SPItem *moved_item)
+{
+ last_transform = *rel_transf;
+ param_effect->getLPEObj()->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ if (dynamic_cast<Inkscape::LivePathEffect::LPECloneOriginal *>(param_effect->getLPEObj()->get_lpe())) {
+ auto hreflist = param_effect->getLPEObj()->hrefList;
+ SPDesktop * desktop = SP_ACTIVE_DESKTOP;
+ if (desktop && hreflist.size()) {
+ Inkscape::Selection *selection = desktop->getSelection();
+ SPLPEItem *sp_lpe_item = dynamic_cast<SPLPEItem *>(*hreflist.begin());
+ SPLPEItem *moved_lpeitem = dynamic_cast<SPLPEItem *>(moved_item);
+ // here use moved item because sp_lpe_item never has optimized transforms because clone LPE
+ if (sp_lpe_item && !selection->includes(sp_lpe_item) && moved_lpeitem && !last_transform.isTranslation()) {
+ if (!moved_lpeitem->optimizeTransforms()) {
+ sp_lpe_item->transform *= last_transform.withoutTranslation();
+ }
+ sp_lpe_item->doWriteTransform(sp_lpe_item->transform);
+ }
+ }
+ }
+}
+
+
+void
+ItemParam::linkitem(Glib::ustring itemid)
+{
+ if (itemid.empty()) {
+ return;
+ }
+
+ // add '#' at start to make it an uri.
+ itemid.insert(itemid.begin(), '#');
+ if ( href && strcmp(itemid.c_str(), href) == 0 ) {
+ // no change, do nothing
+ return;
+ } else {
+ // TODO:
+ // check if id really exists in document, or only in clipboard document: if only in clipboard then invalid
+ // check if linking to object to which LPE is applied (maybe delegated to PathReference
+
+ param_write_to_repr(itemid.c_str());
+ DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Link item parameter to path"));
+ }
+}
+
+void
+ItemParam::on_link_button_click()
+{
+ Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get();
+ const gchar * iid = cm->getFirstObjectID();
+ if (!iid) {
+ return;
+ }
+
+ Glib::ustring itemid(iid);
+ linkitem(itemid);
+}
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/item.h b/src/live_effects/parameter/item.h
new file mode 100644
index 0000000..a6a0e36
--- /dev/null
+++ b/src/live_effects/parameter/item.h
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_ITEM_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_ITEM_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <glib.h>
+
+
+#include "live_effects/parameter/parameter.h"
+#include "live_effects/parameter/item-reference.h"
+#include <cstddef>
+#include <sigc++/sigc++.h>
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class ItemParam : public Parameter {
+public:
+ ItemParam ( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect,
+ const gchar * default_value = "");
+ ~ItemParam() override;
+ Gtk::Widget * param_newWidget() override;
+
+ bool param_readSVGValue(const gchar * strvalue) override;
+ Glib::ustring param_getSVGValue() const override;
+ Glib::ustring param_getDefaultSVGValue() const override;
+ void param_set_default() override;
+ void param_update_default(const gchar * default_value) override;
+ void param_set_and_write_default();
+ void addCanvasIndicators(SPLPEItem const* lpeitem, std::vector<Geom::PathVector> &hp_vec) override;
+ sigc::signal <void> signal_item_pasted;
+ sigc::signal <void> signal_item_changed;
+ void linkitem(Glib::ustring itemid);
+ Geom::Affine last_transform;
+ 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:
+
+ gchar * href; // contains link to other object, e.g. "#path2428", NULL if ItemParam contains pathdata itself
+ ItemReference ref;
+ sigc::connection ref_changed_connection;
+ sigc::connection linked_delete_connection;
+ sigc::connection linked_modified_connection;
+ sigc::connection linked_transformed_connection;
+ void ref_changed(SPObject *old_ref, SPObject *new_ref);
+ void remove_link();
+ void start_listening(SPObject * to);
+ void quit_listening();
+ void linked_delete(SPObject *deleted);
+ void linked_modified(SPObject *linked_obj, guint flags);
+ void linked_transformed(Geom::Affine const *rel_transf, SPItem *moved_item);
+ virtual void linked_modified_callback(SPObject *linked_obj, guint flags);
+ virtual void linked_transformed_callback(Geom::Affine const *rel_transf, SPItem */*moved_item*/);
+ void on_link_button_click();
+ void emit_changed();
+
+ gchar * defvalue;
+
+private:
+ ItemParam(const ItemParam&) = delete;
+ ItemParam& operator=(const ItemParam&) = delete;
+};
+
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/message.cpp b/src/live_effects/parameter/message.cpp
new file mode 100644
index 0000000..341fc58
--- /dev/null
+++ b/src/live_effects/parameter/message.cpp
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Authors:
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <glibmm/i18n.h>
+#include <gtkmm.h>
+
+#include <utility>
+
+#include "include/gtkmm_version.h"
+#include "live_effects/parameter/message.h"
+#include "live_effects/effect.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+MessageParam::MessageParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect, const gchar * default_message, Glib::ustring legend,
+ Gtk::Align halign, Gtk::Align valign, double marginstart, double marginend)
+ : Parameter(label, tip, key, wr, effect),
+ message(default_message),
+ defmessage(default_message),
+ _legend(std::move(legend)),
+ _halign(halign),
+ _valign(valign),
+ _marginstart(marginstart),
+ _marginend(marginend)
+{
+ if (_legend == Glib::ustring("Use Label")) {
+ _legend = label;
+ }
+ _label = nullptr;
+ _min_height = -1;
+}
+
+void
+MessageParam::param_set_default()
+{
+ param_setValue(defmessage);
+}
+
+void
+MessageParam::param_update_default(const gchar * default_message)
+{
+ defmessage = default_message;
+}
+
+bool
+MessageParam::param_readSVGValue(const gchar * strvalue)
+{
+ param_setValue(strvalue);
+ return true;
+}
+
+Glib::ustring
+MessageParam::param_getSVGValue() const
+{
+ return message;
+}
+
+Glib::ustring
+MessageParam::param_getDefaultSVGValue() const
+{
+ return defmessage;
+}
+
+void
+MessageParam::param_set_min_height(int height)
+{
+ _min_height = height;
+ if (_label) {
+ _label->set_size_request(-1, _min_height);
+ }
+}
+
+
+Gtk::Widget *
+MessageParam::param_newWidget()
+{
+ Gtk::Frame * frame = new Gtk::Frame (_legend);
+ Gtk::Widget * widg_frame = frame->get_label_widget();
+
+ widg_frame->set_margin_end(_marginend);
+ widg_frame->set_margin_start(_marginstart);
+ _label = new Gtk::Label (message, Gtk::ALIGN_END);
+ _label->set_use_underline (true);
+ _label->set_use_markup();
+ _label->set_line_wrap(true);
+ _label->set_size_request(-1, _min_height);
+ Gtk::Widget* widg_label = dynamic_cast<Gtk::Widget *> (_label);
+ widg_label->set_halign(_halign);
+ widg_label->set_valign(_valign);
+ widg_label->set_margin_end(_marginend);
+ widg_label->set_margin_start(_marginstart);
+ frame->add(*widg_label);
+ return dynamic_cast<Gtk::Widget *> (frame);
+}
+
+void
+MessageParam::param_setValue(const gchar * strvalue)
+{
+ if (strcmp(strvalue, message) != 0) {
+ param_effect->refresh_widgets = true;
+ }
+ message = strvalue;
+}
+
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/message.h b/src/live_effects/parameter/message.h
new file mode 100644
index 0000000..a08068c
--- /dev/null
+++ b/src/live_effects/parameter/message.h
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_MESSAGE_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_MESSAGE_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Authors:
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#include <glib.h>
+#include "live_effects/parameter/parameter.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class MessageParam : public Parameter {
+public:
+ MessageParam( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect,
+ const gchar * default_message = "Default message",
+ Glib::ustring legend = "Use Label",
+ Gtk::Align halign = Gtk::ALIGN_START,
+ Gtk::Align valign = Gtk::ALIGN_CENTER,
+ double marginstart = 6,
+ double marginend = 6);
+ ~MessageParam() override = default;
+
+ Gtk::Widget * param_newWidget() override;
+ bool param_readSVGValue(const gchar * strvalue) override;
+ void param_update_default(const gchar * default_value) override;
+ Glib::ustring param_getSVGValue() const override;
+ Glib::ustring param_getDefaultSVGValue() const override;
+
+ void param_setValue(const gchar * message);
+
+ void param_set_default() override;
+ void param_set_min_height(int height);
+ const gchar * get_value() const { return message; };
+
+private:
+ Gtk::Label * _label;
+ int _min_height;
+ MessageParam(const MessageParam&) = delete;
+ MessageParam& operator=(const MessageParam&) = delete;
+ const gchar * message;
+ const gchar * defmessage;
+ Glib::ustring _legend;
+ Gtk::Align _halign;
+ Gtk::Align _valign;
+ double _marginstart;
+ double _marginend;
+ double _marginleft;
+ double _marginright;
+
+
+};
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/originalitem.cpp b/src/live_effects/parameter/originalitem.cpp
new file mode 100644
index 0000000..7ad029a
--- /dev/null
+++ b/src/live_effects/parameter/originalitem.cpp
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Johan Engelen 2012 <j.b.c.engelen@alumnus.utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <gtkmm/box.h>
+#include "live_effects/parameter/originalitem.h"
+
+#include <glibmm/i18n.h>
+#include <gtkmm/button.h>
+#include <gtkmm/label.h>
+
+#include "display/curve.h"
+#include "live_effects/effect.h"
+
+#include "inkscape.h"
+#include "desktop.h"
+#include "selection.h"
+
+#include "object/uri.h"
+
+#include "ui/icon-loader.h"
+#include "ui/icon-names.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+OriginalItemParam::OriginalItemParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect)
+ : ItemParam(label, tip, key, wr, effect, "")
+{
+}
+
+OriginalItemParam::~OriginalItemParam()
+= default;
+
+Gtk::Widget *
+OriginalItemParam::param_newWidget()
+{
+ Gtk::HBox *_widget = Gtk::manage(new Gtk::HBox());
+
+ { // Label
+ Gtk::Label *pLabel = Gtk::manage(new Gtk::Label(param_label));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pLabel, true, true);
+ pLabel->set_tooltip_text(param_tooltip);
+ }
+
+ { // Paste item to link button
+ Gtk::Image *pIcon = Gtk::manage(sp_get_icon_image("edit-paste", Gtk::ICON_SIZE_BUTTON));
+ Gtk::Button *pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalItemParam::on_link_button_click));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true);
+ pButton->set_tooltip_text(_("Link to item"));
+ }
+
+ { // Select original button
+ Gtk::Image *pIcon = Gtk::manage(sp_get_icon_image("edit-select-original", Gtk::ICON_SIZE_BUTTON));
+ Gtk::Button *pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalItemParam::on_select_original_button_click));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true);
+ pButton->set_tooltip_text(_("Select original"));
+ }
+
+ static_cast<Gtk::HBox*>(_widget)->show_all_children();
+
+ return dynamic_cast<Gtk::Widget *> (_widget);
+}
+
+void
+OriginalItemParam::on_select_original_button_click()
+{
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ SPItem *original = ref.getObject();
+ if (desktop == nullptr || original == nullptr) {
+ return;
+ }
+ Inkscape::Selection *selection = desktop->getSelection();
+ selection->clear();
+ selection->set(original);
+}
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/originalitem.h b/src/live_effects/parameter/originalitem.h
new file mode 100644
index 0000000..783e5b8
--- /dev/null
+++ b/src/live_effects/parameter/originalitem.h
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINAL_ITEM_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINAL_ITEM_H
+
+/*
+ * Inkscape::LiveItemEffectParameters
+ *
+ * Copyright (C) Johan Engelen 2012 <j.b.c.engelen@alumnus.utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/parameter/item.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class OriginalItemParam: public ItemParam {
+public:
+ OriginalItemParam ( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect);
+ ~OriginalItemParam() override;
+ bool linksToItem() const { return (href != nullptr); }
+ SPItem * getObject() const { return ref.getObject(); }
+
+ Gtk::Widget * param_newWidget() override;
+
+protected:
+
+ void on_select_original_button_click();
+
+private:
+ OriginalItemParam(const OriginalItemParam&) = delete;
+ OriginalItemParam& operator=(const OriginalItemParam&) = delete;
+};
+
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/originalitemarray.cpp b/src/live_effects/parameter/originalitemarray.cpp
new file mode 100644
index 0000000..30b25dc
--- /dev/null
+++ b/src/live_effects/parameter/originalitemarray.cpp
@@ -0,0 +1,459 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/parameter/originalitemarray.h"
+
+#include <gtkmm/widget.h>
+#include <gtkmm/icontheme.h>
+#include <gtkmm/imagemenuitem.h>
+#include <gtkmm/separatormenuitem.h>
+#include <gtkmm/scrolledwindow.h>
+
+#include <glibmm/i18n.h>
+
+#include "inkscape.h"
+#include "originalitem.h"
+#include "svg/stringstream.h"
+#include "svg/svg.h"
+#include "ui/clipboard.h"
+#include "ui/icon-loader.h"
+
+#include "object/uri.h"
+
+#include "live_effects/effect.h"
+
+#include "verbs.h"
+#include "document-undo.h"
+#include "document.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class OriginalItemArrayParam::ModelColumns : public Gtk::TreeModel::ColumnRecord
+{
+public:
+
+ ModelColumns()
+ {
+ add(_colObject);
+ add(_colLabel);
+ add(_colActive);
+ }
+ ~ModelColumns() override = default;
+
+ Gtk::TreeModelColumn<ItemAndActive*> _colObject;
+ Gtk::TreeModelColumn<Glib::ustring> _colLabel;
+ Gtk::TreeModelColumn<bool> _colActive;
+};
+
+OriginalItemArrayParam::OriginalItemArrayParam( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect )
+: Parameter(label, tip, key, wr, effect),
+ _vector(),
+ _tree(),
+ _text_renderer(),
+ _toggle_active(),
+ _scroller()
+{
+ _model = new ModelColumns();
+ _store = Gtk::TreeStore::create(*_model);
+ _tree.set_model(_store);
+
+ _tree.set_reorderable(true);
+ _tree.enable_model_drag_dest (Gdk::ACTION_MOVE);
+
+ Gtk::CellRendererToggle * _toggle_active = manage(new Gtk::CellRendererToggle());
+ int activeColNum = _tree.append_column(_("Active"), *_toggle_active) - 1;
+ Gtk::TreeViewColumn* col_active = _tree.get_column(activeColNum);
+ _toggle_active->set_activatable(true);
+ _toggle_active->signal_toggled().connect(sigc::mem_fun(*this, &OriginalItemArrayParam::on_active_toggled));
+ col_active->add_attribute(_toggle_active->property_active(), _model->_colActive);
+
+ _text_renderer = manage(new Gtk::CellRendererText());
+ int nameColNum = _tree.append_column(_("Name"), *_text_renderer) - 1;
+ _name_column = _tree.get_column(nameColNum);
+ _name_column->add_attribute(_text_renderer->property_text(), _model->_colLabel);
+
+ _tree.set_expander_column( *_tree.get_column(nameColNum) );
+ _tree.set_search_column(_model->_colLabel);
+
+ //quick little hack -- newer versions of gtk gave the item zero space allotment
+ _scroller.set_size_request(-1, 120);
+
+ _scroller.add(_tree);
+ _scroller.set_policy( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC );
+ //_scroller.set_shadow_type(Gtk::SHADOW_IN);
+
+ oncanvas_editable = true;
+}
+
+OriginalItemArrayParam::~OriginalItemArrayParam()
+{
+ while (!_vector.empty()) {
+ ItemAndActive *w = _vector.back();
+ _vector.pop_back();
+ unlink(w);
+ delete w;
+ }
+ delete _model;
+}
+
+void OriginalItemArrayParam::on_active_toggled(const Glib::ustring& item)
+{
+ Gtk::TreeModel::iterator iter = _store->get_iter(item);
+ Gtk::TreeModel::Row row = *iter;
+ ItemAndActive *w = row[_model->_colObject];
+ row[_model->_colActive] = !row[_model->_colActive];
+ w->actived = row[_model->_colActive];
+
+ auto full = param_getSVGValue();
+ param_write_to_repr(full.c_str());
+ DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Link item parameter to item"));
+}
+
+void OriginalItemArrayParam::param_set_default()
+{
+
+}
+
+Gtk::Widget* OriginalItemArrayParam::param_newWidget()
+{
+ Gtk::VBox* vbox = Gtk::manage(new Gtk::VBox());
+ Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
+
+ vbox->pack_start(_scroller, Gtk::PACK_EXPAND_WIDGET);
+
+
+ { // Paste item to link button
+ Gtk::Image *pIcon = Gtk::manage(sp_get_icon_image("edit-clone", Gtk::ICON_SIZE_BUTTON));
+ Gtk::Button *pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalItemArrayParam::on_link_button_click));
+ hbox->pack_start(*pButton, Gtk::PACK_SHRINK);
+ pButton->set_tooltip_text(_("Link to item"));
+ }
+
+ { // Remove linked item
+ Gtk::Image *pIcon = Gtk::manage(sp_get_icon_image("list-remove", Gtk::ICON_SIZE_BUTTON));
+ Gtk::Button *pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalItemArrayParam::on_remove_button_click));
+ hbox->pack_start(*pButton, Gtk::PACK_SHRINK);
+ pButton->set_tooltip_text(_("Remove Item"));
+ }
+
+ { // Move Down
+ Gtk::Image *pIcon = Gtk::manage(sp_get_icon_image("go-down", Gtk::ICON_SIZE_BUTTON));
+ Gtk::Button *pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalItemArrayParam::on_down_button_click));
+ hbox->pack_end(*pButton, Gtk::PACK_SHRINK);
+ pButton->set_tooltip_text(_("Move Down"));
+ }
+
+ { // Move Down
+ Gtk::Image *pIcon = Gtk::manage(sp_get_icon_image("go-up", Gtk::ICON_SIZE_BUTTON));
+ Gtk::Button *pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalItemArrayParam::on_up_button_click));
+ hbox->pack_end(*pButton, Gtk::PACK_SHRINK);
+ pButton->set_tooltip_text(_("Move Up"));
+ }
+
+ vbox->pack_end(*hbox, Gtk::PACK_SHRINK);
+
+ vbox->show_all_children(true);
+
+ return vbox;
+}
+
+bool OriginalItemArrayParam::_selectIndex(const Gtk::TreeIter& iter, int* i)
+{
+ if ((*i)-- <= 0) {
+ _tree.get_selection()->select(iter);
+ return true;
+ }
+ return false;
+}
+
+void OriginalItemArrayParam::on_up_button_click()
+{
+ Gtk::TreeModel::iterator iter = _tree.get_selection()->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+
+ int i = -1;
+ std::vector<ItemAndActive*>::iterator piter = _vector.begin();
+ for (std::vector<ItemAndActive*>::iterator iter = _vector.begin(); iter != _vector.end(); piter = iter, i++, ++iter) {
+ if (*iter == row[_model->_colObject]) {
+ _vector.erase(iter);
+ _vector.insert(piter, row[_model->_colObject]);
+ break;
+ }
+ }
+
+ auto full = param_getSVGValue();
+ param_write_to_repr(full.c_str());
+
+ DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Move item up"));
+
+ _store->foreach_iter(sigc::bind<int*>(sigc::mem_fun(*this, &OriginalItemArrayParam::_selectIndex), &i));
+ }
+}
+
+void OriginalItemArrayParam::on_down_button_click()
+{
+ Gtk::TreeModel::iterator iter = _tree.get_selection()->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+
+ int i = 0;
+ for (std::vector<ItemAndActive*>::iterator iter = _vector.begin(); iter != _vector.end(); i++, ++iter) {
+ if (*iter == row[_model->_colObject]) {
+ std::vector<ItemAndActive*>::iterator niter = _vector.erase(iter);
+ if (niter != _vector.end()) {
+ ++niter;
+ i++;
+ }
+ _vector.insert(niter, row[_model->_colObject]);
+ break;
+ }
+ }
+
+ auto full = param_getSVGValue();
+ param_write_to_repr(full.c_str());
+
+ DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Move item down"));
+
+ _store->foreach_iter(sigc::bind<int*>(sigc::mem_fun(*this, &OriginalItemArrayParam::_selectIndex), &i));
+ }
+}
+
+void OriginalItemArrayParam::on_remove_button_click()
+{
+ Gtk::TreeModel::iterator iter = _tree.get_selection()->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ remove_link(row[_model->_colObject]);
+
+ auto full = param_getSVGValue();
+ param_write_to_repr(full.c_str());
+
+ DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Remove item"));
+ }
+
+}
+
+void
+OriginalItemArrayParam::on_link_button_click()
+{
+ Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get();
+ //without second parameter populate all elements filled inside the called function
+ std::vector<Glib::ustring> itemsid = cm->getElementsOfType(SP_ACTIVE_DESKTOP, "*", 1);
+
+ if (itemsid.empty()) {
+ return;
+ }
+
+ bool foundOne = false;
+ Inkscape::SVGOStringStream os;
+ for (auto iter : _vector) {
+ if (foundOne) {
+ os << "|";
+ } else {
+ foundOne = true;
+ }
+ os << iter->href << "," << (iter->actived ? "1" : "0");
+ }
+ for (auto itemid : itemsid) {
+ // add '#' at start to make it an uri.
+ itemid.insert(itemid.begin(), '#');
+
+ if (foundOne) {
+ os << "|";
+ } else {
+ foundOne = true;
+ }
+ os << itemid.c_str() << ",1";
+ }
+ param_write_to_repr(os.str().c_str());
+ DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Link itemarray parameter to item"));
+}
+
+void OriginalItemArrayParam::unlink(ItemAndActive* to)
+{
+ to->linked_modified_connection.disconnect();
+ to->linked_delete_connection.disconnect();
+ to->ref.detach();
+ if (to->href) {
+ g_free(to->href);
+ to->href = nullptr;
+ }
+}
+
+void OriginalItemArrayParam::remove_link(ItemAndActive* to)
+{
+ unlink(to);
+ for (std::vector<ItemAndActive*>::iterator iter = _vector.begin(); iter != _vector.end(); ++iter) {
+ if (*iter == to) {
+ ItemAndActive *w = *iter;
+ _vector.erase(iter);
+ delete w;
+ return;
+ }
+ }
+}
+
+void OriginalItemArrayParam::linked_delete(SPObject */*deleted*/, ItemAndActive* to)
+{
+ remove_link(to);
+ auto full = param_getSVGValue();
+ param_write_to_repr(full.c_str());
+}
+
+bool OriginalItemArrayParam::_updateLink(const Gtk::TreeIter& iter, ItemAndActive* pd)
+{
+ Gtk::TreeModel::Row row = *iter;
+ if (row[_model->_colObject] == pd) {
+ SPObject *obj = pd->ref.getObject();
+ row[_model->_colLabel] = obj && obj->getId() ? ( obj->label() ? obj->label() : obj->getId() ) : pd->href;
+ return true;
+ }
+ return false;
+}
+
+void OriginalItemArrayParam::linked_changed(SPObject */*old_obj*/, SPObject *new_obj, ItemAndActive* to)
+{
+ to->linked_delete_connection.disconnect();
+ to->linked_modified_connection.disconnect();
+ to->linked_transformed_connection.disconnect();
+
+ if (new_obj && SP_IS_ITEM(new_obj)) {
+ to->linked_delete_connection = new_obj->connectDelete(sigc::bind<ItemAndActive*>(sigc::mem_fun(*this, &OriginalItemArrayParam::linked_delete), to));
+ to->linked_modified_connection = new_obj->connectModified(sigc::bind<ItemAndActive*>(sigc::mem_fun(*this, &OriginalItemArrayParam::linked_modified), to));
+ to->linked_transformed_connection = SP_ITEM(new_obj)->connectTransformed(sigc::bind<ItemAndActive*>(sigc::mem_fun(*this, &OriginalItemArrayParam::linked_transformed), to));
+
+ linked_modified(new_obj, SP_OBJECT_MODIFIED_FLAG, to);
+ } else {
+ SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ _store->foreach_iter(sigc::bind<ItemAndActive*>(sigc::mem_fun(*this, &OriginalItemArrayParam::_updateLink), to));
+ }
+}
+
+void OriginalItemArrayParam::linked_modified(SPObject *linked_obj, guint flags, ItemAndActive* to)
+{
+ if (!to) {
+ return;
+ }
+ SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ _store->foreach_iter(sigc::bind<ItemAndActive*>(sigc::mem_fun(*this, &OriginalItemArrayParam::_updateLink), to));
+}
+
+bool OriginalItemArrayParam::param_readSVGValue(const gchar* strvalue)
+{
+ if (strvalue) {
+ while (!_vector.empty()) {
+ ItemAndActive *w = _vector.back();
+ unlink(w);
+ _vector.pop_back();
+ delete w;
+ }
+ _store->clear();
+
+ gchar ** strarray = g_strsplit(strvalue, "|", 0);
+ for (gchar ** iter = strarray; *iter != nullptr; iter++) {
+ if ((*iter)[0] == '#') {
+ gchar ** substrarray = g_strsplit(*iter, ",", 0);
+ ItemAndActive* w = new ItemAndActive((SPObject *)param_effect->getLPEObj());
+ w->href = g_strdup(*substrarray);
+ w->actived = *(substrarray+1) != nullptr && (*(substrarray+1))[0] == '1';
+ w->linked_changed_connection = w->ref.changedSignal().connect(sigc::bind<ItemAndActive *>(sigc::mem_fun(*this, &OriginalItemArrayParam::linked_changed), w));
+ w->ref.attach(URI(w->href));
+
+ _vector.push_back(w);
+
+ Gtk::TreeModel::iterator iter = _store->append();
+ Gtk::TreeModel::Row row = *iter;
+ SPObject *obj = w->ref.getObject();
+
+ row[_model->_colObject] = w;
+ row[_model->_colLabel] = obj ? ( obj->label() ? obj->label() : obj->getId() ) : w->href;
+ row[_model->_colActive] = w->actived;
+ g_strfreev (substrarray);
+ }
+ }
+ g_strfreev (strarray);
+ return true;
+ }
+ return false;
+}
+
+Glib::ustring
+OriginalItemArrayParam::param_getSVGValue() const
+{
+ Inkscape::SVGOStringStream os;
+ bool foundOne = false;
+ for (auto iter : _vector) {
+ if (foundOne) {
+ os << "|";
+ } else {
+ foundOne = true;
+ }
+ os << iter->href << "," << (iter->actived ? "1" : "0");
+ }
+ return os.str();
+}
+
+Glib::ustring
+OriginalItemArrayParam::param_getDefaultSVGValue() const
+{
+ return "";
+}
+
+void OriginalItemArrayParam::update()
+{
+ for (auto & iter : _vector) {
+ SPObject *linked_obj = iter->ref.getObject();
+ linked_modified(linked_obj, SP_OBJECT_MODIFIED_FLAG, iter);
+ }
+}
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/originalitemarray.h b/src/live_effects/parameter/originalitemarray.h
new file mode 100644
index 0000000..baa7646
--- /dev/null
+++ b/src/live_effects/parameter/originalitemarray.h
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINALITEMARRAY_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINALITEMARRAY_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <vector>
+
+#include <gtkmm/box.h>
+#include <gtkmm/treeview.h>
+#include <gtkmm/treestore.h>
+#include <gtkmm/scrolledwindow.h>
+
+#include "live_effects/parameter/parameter.h"
+#include "live_effects/parameter/item-reference.h"
+
+#include "svg/svg.h"
+#include "svg/stringstream.h"
+#include "item-reference.h"
+
+class SPObject;
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class ItemAndActive {
+public:
+ ItemAndActive(SPObject *owner)
+ : href(nullptr),
+ ref(owner),
+ actived(true)
+ {
+
+ }
+ gchar *href;
+ URIReference ref;
+ bool actived;
+
+ sigc::connection linked_changed_connection;
+ sigc::connection linked_delete_connection;
+ sigc::connection linked_modified_connection;
+ sigc::connection linked_transformed_connection;
+};
+
+class OriginalItemArrayParam : public Parameter {
+public:
+ class ModelColumns;
+
+ OriginalItemArrayParam( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect);
+
+ ~OriginalItemArrayParam() override;
+
+ Gtk::Widget * param_newWidget() override;
+ bool param_readSVGValue(const gchar * strvalue) override;
+ Glib::ustring param_getSVGValue() const override;
+ Glib::ustring param_getDefaultSVGValue() const override;
+ void param_set_default() override;
+ void param_update_default(const gchar * default_value) override{};
+ /** Disable the canvas indicators of parent class by overriding this method */
+ void param_editOncanvas(SPItem * /*item*/, SPDesktop * /*dt*/) override {};
+ /** Disable the canvas indicators of parent class by overriding this method */
+ void addCanvasIndicators(SPLPEItem const* /*lpeitem*/, std::vector<Geom::PathVector> & /*hp_vec*/) override {};
+
+ std::vector<ItemAndActive*> _vector;
+
+protected:
+ bool _updateLink(const Gtk::TreeIter& iter, ItemAndActive* pd);
+ bool _selectIndex(const Gtk::TreeIter& iter, int* i);
+ void unlink(ItemAndActive* to);
+ void remove_link(ItemAndActive* to);
+ void setItem(SPObject *linked_obj, guint flags, ItemAndActive* to);
+
+ void linked_changed(SPObject *old_obj, SPObject *new_obj, ItemAndActive* to);
+ void linked_modified(SPObject *linked_obj, guint flags, ItemAndActive* to);
+ void linked_transformed(Geom::Affine const *, SPItem *, ItemAndActive*) {}
+ void linked_delete(SPObject *deleted, ItemAndActive* to);
+
+ ModelColumns *_model;
+ Glib::RefPtr<Gtk::TreeStore> _store;
+ Gtk::TreeView _tree;
+ Gtk::CellRendererText *_text_renderer;
+ Gtk::CellRendererToggle *_toggle_active;
+ Gtk::TreeView::Column *_name_column;
+ Gtk::ScrolledWindow _scroller;
+
+ void on_link_button_click();
+ void on_remove_button_click();
+ void on_up_button_click();
+ void on_down_button_click();
+ void on_active_toggled(const Glib::ustring& item);
+
+private:
+ void update();
+ OriginalItemArrayParam(const OriginalItemArrayParam&);
+ OriginalItemArrayParam& operator=(const OriginalItemArrayParam&);
+};
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/originalpath.cpp b/src/live_effects/parameter/originalpath.cpp
new file mode 100644
index 0000000..69b62d0
--- /dev/null
+++ b/src/live_effects/parameter/originalpath.cpp
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Johan Engelen 2012 <j.b.c.engelen@alumnus.utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <gtkmm/box.h>
+#include "live_effects/parameter/originalpath.h"
+
+#include <glibmm/i18n.h>
+#include <gtkmm/button.h>
+#include <gtkmm/label.h>
+
+#include "display/curve.h"
+#include "live_effects/effect.h"
+
+#include "object/uri.h"
+#include "object/sp-shape.h"
+#include "object/sp-text.h"
+
+#include "inkscape.h"
+#include "desktop.h"
+#include "selection.h"
+#include "ui/icon-names.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+OriginalPathParam::OriginalPathParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect)
+ : PathParam(label, tip, key, wr, effect, "")
+{
+ oncanvas_editable = false;
+ _from_original_d = false;
+}
+
+OriginalPathParam::~OriginalPathParam()
+= default;
+
+Gtk::Widget *
+OriginalPathParam::param_newWidget()
+{
+ Gtk::HBox *_widget = Gtk::manage(new Gtk::HBox());
+
+ { // Label
+ Gtk::Label *pLabel = Gtk::manage(new Gtk::Label(param_label));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pLabel, true, true);
+ pLabel->set_tooltip_text(param_tooltip);
+ }
+
+ { // Paste path to link button
+ Gtk::Image *pIcon = Gtk::manage(new Gtk::Image());
+ pIcon->set_from_icon_name("edit-clone", Gtk::ICON_SIZE_BUTTON);
+ Gtk::Button *pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalPathParam::on_link_button_click));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true);
+ pButton->set_tooltip_text(_("Link to path in clipboard"));
+ }
+
+ { // Select original button
+ Gtk::Image *pIcon = Gtk::manage(new Gtk::Image());
+ pIcon->set_from_icon_name("edit-select-original", Gtk::ICON_SIZE_BUTTON);
+ Gtk::Button *pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalPathParam::on_select_original_button_click));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true);
+ pButton->set_tooltip_text(_("Select original"));
+ }
+
+ static_cast<Gtk::HBox*>(_widget)->show_all_children();
+
+ return dynamic_cast<Gtk::Widget *> (_widget);
+}
+
+void
+OriginalPathParam::linked_modified_callback(SPObject *linked_obj, guint /*flags*/)
+{
+ SPCurve *curve = nullptr;
+ if (SP_IS_SHAPE(linked_obj)) {
+ if (_from_original_d) {
+ curve = SP_SHAPE(linked_obj)->getCurveForEdit();
+ } else {
+ curve = SP_SHAPE(linked_obj)->getCurve();
+ }
+ }
+ if (SP_IS_TEXT(linked_obj)) {
+ curve = SP_TEXT(linked_obj)->getNormalizedBpath();
+ }
+
+ if (curve == nullptr) {
+ // curve invalid, set empty pathvector
+ _pathvector = Geom::PathVector();
+ } else {
+ _pathvector = curve->get_pathvector();
+ curve->unref();
+ }
+
+ must_recalculate_pwd2 = true;
+ emit_changed();
+ SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG);
+}
+
+void
+OriginalPathParam::linked_transformed_callback(Geom::Affine const * /*rel_transf*/, SPItem * /*moved_item*/)
+{
+/** \todo find good way to compensate for referenced path transform, like done for normal clones.
+ * See sp-use.cpp: sp_use_move_compensate */
+}
+
+
+void
+OriginalPathParam::on_select_original_button_click()
+{
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ SPItem *original = ref.getObject();
+ if (desktop == nullptr || original == nullptr) {
+ return;
+ }
+ Inkscape::Selection *selection = desktop->getSelection();
+ selection->clear();
+ selection->set(original);
+ SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG);
+}
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/originalpath.h b/src/live_effects/parameter/originalpath.h
new file mode 100644
index 0000000..7bdc23c
--- /dev/null
+++ b/src/live_effects/parameter/originalpath.h
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINAL_PATH_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINAL_PATH_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Copyright (C) Johan Engelen 2012 <j.b.c.engelen@alumnus.utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/parameter/path.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class OriginalPathParam: public PathParam {
+public:
+ OriginalPathParam ( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect);
+ ~OriginalPathParam() override;
+
+ bool linksToPath() const { return (href != nullptr); }
+ SPItem * getObject() const { return ref.getObject(); }
+
+ Gtk::Widget * param_newWidget() override;
+ /** Disable the canvas indicators of parent class by overriding this method */
+ void param_editOncanvas(SPItem * /*item*/, SPDesktop * /*dt*/) override {};
+ /** Disable the canvas indicators of parent class by overriding this method */
+ void addCanvasIndicators(SPLPEItem const* /*lpeitem*/, std::vector<Geom::PathVector> & /*hp_vec*/) override {};
+ void setFromOriginalD(bool from_original_d){ _from_original_d = from_original_d; };
+
+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:
+ bool _from_original_d;
+ OriginalPathParam(const OriginalPathParam&) = delete;
+ OriginalPathParam& operator=(const OriginalPathParam&) = delete;
+};
+
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/originalpatharray.cpp b/src/live_effects/parameter/originalpatharray.cpp
new file mode 100644
index 0000000..bee6651
--- /dev/null
+++ b/src/live_effects/parameter/originalpatharray.cpp
@@ -0,0 +1,542 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/parameter/originalpatharray.h"
+#include "live_effects/lpe-spiro.h"
+#include "live_effects/lpe-bspline.h"
+#include "live_effects/lpeobject.h"
+#include "live_effects/lpeobject-reference.h"
+
+#include <gtkmm/widget.h>
+#include <gtkmm/icontheme.h>
+#include <gtkmm/imagemenuitem.h>
+#include <gtkmm/separatormenuitem.h>
+#include <gtkmm/scrolledwindow.h>
+
+#include <glibmm/i18n.h>
+
+#include "inkscape.h"
+#include "ui/clipboard.h"
+#include "svg/svg.h"
+#include "svg/stringstream.h"
+#include "originalpath.h"
+#include "display/curve.h"
+
+#include <2geom/coord.h>
+#include <2geom/point.h>
+
+#include "ui/icon-loader.h"
+
+#include "object/sp-shape.h"
+#include "object/sp-text.h"
+#include "object/uri.h"
+
+#include "live_effects/effect.h"
+
+#include "verbs.h"
+#include "document-undo.h"
+#include "document.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class OriginalPathArrayParam::ModelColumns : public Gtk::TreeModel::ColumnRecord
+{
+public:
+
+ ModelColumns()
+ {
+ add(_colObject);
+ add(_colLabel);
+ add(_colReverse);
+ add(_colVisible);
+ }
+ ~ModelColumns() override = default;
+
+ Gtk::TreeModelColumn<PathAndDirectionAndVisible*> _colObject;
+ Gtk::TreeModelColumn<Glib::ustring> _colLabel;
+ Gtk::TreeModelColumn<bool> _colReverse;
+ Gtk::TreeModelColumn<bool> _colVisible;
+};
+
+OriginalPathArrayParam::OriginalPathArrayParam( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect )
+: Parameter(label, tip, key, wr, effect),
+ _vector(),
+ _tree(),
+ _text_renderer(),
+ _toggle_reverse(),
+ _toggle_visible(),
+ _scroller()
+{
+ _model = new ModelColumns();
+ _store = Gtk::TreeStore::create(*_model);
+ _tree.set_model(_store);
+
+ _tree.set_reorderable(true);
+ _tree.enable_model_drag_dest (Gdk::ACTION_MOVE);
+
+
+ Gtk::CellRendererToggle * _toggle_reverse = manage(new Gtk::CellRendererToggle());
+ int reverseColNum = _tree.append_column(_("Reverse"), *_toggle_reverse) - 1;
+ Gtk::TreeViewColumn* col_reverse = _tree.get_column(reverseColNum);
+ _toggle_reverse->set_activatable(true);
+ _toggle_reverse->signal_toggled().connect(sigc::mem_fun(*this, &OriginalPathArrayParam::on_reverse_toggled));
+ col_reverse->add_attribute(_toggle_reverse->property_active(), _model->_colReverse);
+
+
+ Gtk::CellRendererToggle * _toggle_visible = manage(new Gtk::CellRendererToggle());
+ int visibleColNum = _tree.append_column(_("Visible"), *_toggle_visible) - 1;
+ Gtk::TreeViewColumn* col_visible = _tree.get_column(visibleColNum);
+ _toggle_visible->set_activatable(true);
+ _toggle_visible->signal_toggled().connect(sigc::mem_fun(*this, &OriginalPathArrayParam::on_visible_toggled));
+ col_visible->add_attribute(_toggle_visible->property_active(), _model->_colVisible);
+
+ _text_renderer = manage(new Gtk::CellRendererText());
+ int nameColNum = _tree.append_column(_("Name"), *_text_renderer) - 1;
+ _name_column = _tree.get_column(nameColNum);
+ _name_column->add_attribute(_text_renderer->property_text(), _model->_colLabel);
+
+ _tree.set_expander_column( *_tree.get_column(nameColNum) );
+ _tree.set_search_column(_model->_colLabel);
+
+ //quick little hack -- newer versions of gtk gave the item zero space allotment
+ _scroller.set_size_request(-1, 120);
+
+ _scroller.add(_tree);
+ _scroller.set_policy( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC );
+ //_scroller.set_shadow_type(Gtk::SHADOW_IN);
+
+ oncanvas_editable = true;
+ _from_original_d = false;
+ _allow_only_bspline_spiro = false;
+
+}
+
+OriginalPathArrayParam::~OriginalPathArrayParam()
+{
+ while (!_vector.empty()) {
+ PathAndDirectionAndVisible *w = _vector.back();
+ _vector.pop_back();
+ unlink(w);
+ delete w;
+ }
+ delete _model;
+}
+
+void OriginalPathArrayParam::on_reverse_toggled(const Glib::ustring& path)
+{
+ Gtk::TreeModel::iterator iter = _store->get_iter(path);
+ Gtk::TreeModel::Row row = *iter;
+ PathAndDirectionAndVisible *w = row[_model->_colObject];
+ row[_model->_colReverse] = !row[_model->_colReverse];
+ w->reversed = row[_model->_colReverse];
+
+ param_write_to_repr(param_getSVGValue().c_str());
+ DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Link path parameter to path"));
+}
+
+void OriginalPathArrayParam::on_visible_toggled(const Glib::ustring& path)
+{
+ Gtk::TreeModel::iterator iter = _store->get_iter(path);
+ Gtk::TreeModel::Row row = *iter;
+ PathAndDirectionAndVisible *w = row[_model->_colObject];
+ row[_model->_colVisible] = !row[_model->_colVisible];
+ w->visibled = row[_model->_colVisible];
+
+ param_write_to_repr(param_getSVGValue().c_str());
+ DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Toggle path parameter visibility"));
+}
+
+void OriginalPathArrayParam::param_set_default()
+{
+
+}
+
+Gtk::Widget* OriginalPathArrayParam::param_newWidget()
+{
+ Gtk::VBox* vbox = Gtk::manage(new Gtk::VBox());
+ Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
+
+ vbox->pack_start(_scroller, Gtk::PACK_EXPAND_WIDGET);
+
+
+ { // Paste path to link button
+ Gtk::Image *pIcon = Gtk::manage(sp_get_icon_image("edit-clone", Gtk::ICON_SIZE_BUTTON));
+ Gtk::Button *pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalPathArrayParam::on_link_button_click));
+ hbox->pack_start(*pButton, Gtk::PACK_SHRINK);
+ pButton->set_tooltip_text(_("Link to path in clipboard"));
+ }
+
+ { // Remove linked path
+ Gtk::Image *pIcon = Gtk::manage(sp_get_icon_image("list-remove", Gtk::ICON_SIZE_BUTTON));
+ Gtk::Button *pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalPathArrayParam::on_remove_button_click));
+ hbox->pack_start(*pButton, Gtk::PACK_SHRINK);
+ pButton->set_tooltip_text(_("Remove Path"));
+ }
+
+ { // Move Down
+ Gtk::Image *pIcon = Gtk::manage(sp_get_icon_image("go-down", Gtk::ICON_SIZE_BUTTON));
+ Gtk::Button *pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalPathArrayParam::on_down_button_click));
+ hbox->pack_end(*pButton, Gtk::PACK_SHRINK);
+ pButton->set_tooltip_text(_("Move Down"));
+ }
+
+ { // Move Down
+ Gtk::Image *pIcon = Gtk::manage(sp_get_icon_image("go-up", Gtk::ICON_SIZE_BUTTON));
+ Gtk::Button *pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalPathArrayParam::on_up_button_click));
+ hbox->pack_end(*pButton, Gtk::PACK_SHRINK);
+ pButton->set_tooltip_text(_("Move Up"));
+ }
+
+ vbox->pack_end(*hbox, Gtk::PACK_SHRINK);
+
+ vbox->show_all_children(true);
+
+ return vbox;
+}
+
+bool OriginalPathArrayParam::_selectIndex(const Gtk::TreeIter& iter, int* i)
+{
+ if ((*i)-- <= 0) {
+ _tree.get_selection()->select(iter);
+ return true;
+ }
+ return false;
+}
+
+void OriginalPathArrayParam::on_up_button_click()
+{
+ Gtk::TreeModel::iterator iter = _tree.get_selection()->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+
+ int i = -1;
+ std::vector<PathAndDirectionAndVisible*>::iterator piter = _vector.begin();
+ for (std::vector<PathAndDirectionAndVisible*>::iterator iter = _vector.begin(); iter != _vector.end(); piter = iter, i++, ++iter) {
+ if (*iter == row[_model->_colObject]) {
+ _vector.erase(iter);
+ _vector.insert(piter, row[_model->_colObject]);
+ break;
+ }
+ }
+
+ param_write_to_repr(param_getSVGValue().c_str());
+
+ DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Move path up"));
+
+ _store->foreach_iter(sigc::bind<int*>(sigc::mem_fun(*this, &OriginalPathArrayParam::_selectIndex), &i));
+ }
+}
+
+void OriginalPathArrayParam::on_down_button_click()
+{
+ Gtk::TreeModel::iterator iter = _tree.get_selection()->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+
+ int i = 0;
+ for (std::vector<PathAndDirectionAndVisible*>::iterator iter = _vector.begin(); iter != _vector.end(); i++, ++iter) {
+ if (*iter == row[_model->_colObject]) {
+ std::vector<PathAndDirectionAndVisible*>::iterator niter = _vector.erase(iter);
+ if (niter != _vector.end()) {
+ ++niter;
+ i++;
+ }
+ _vector.insert(niter, row[_model->_colObject]);
+ break;
+ }
+ }
+
+ param_write_to_repr(param_getSVGValue().c_str());
+
+ DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Move path down"));
+
+ _store->foreach_iter(sigc::bind<int*>(sigc::mem_fun(*this, &OriginalPathArrayParam::_selectIndex), &i));
+ }
+}
+
+void OriginalPathArrayParam::on_remove_button_click()
+{
+ Gtk::TreeModel::iterator iter = _tree.get_selection()->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ remove_link(row[_model->_colObject]);
+
+ param_write_to_repr(param_getSVGValue().c_str());
+
+ DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Remove path"));
+ }
+
+}
+
+void
+OriginalPathArrayParam::on_link_button_click()
+{
+ Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get();
+ std::vector<Glib::ustring> pathsid = cm->getElementsOfType(SP_ACTIVE_DESKTOP, "svg:path");
+ std::vector<Glib::ustring> textsid = cm->getElementsOfType(SP_ACTIVE_DESKTOP, "svg:text");
+ pathsid.insert(pathsid.end(), textsid.begin(), textsid.end());
+ if (pathsid.empty()) {
+ return;
+ }
+ bool foundOne = false;
+ Inkscape::SVGOStringStream os;
+ for (auto iter : _vector) {
+ if (foundOne) {
+ os << "|";
+ } else {
+ foundOne = true;
+ }
+ os << iter->href << "," << (iter->reversed ? "1" : "0") << "," << (iter->visibled ? "1" : "0");
+ }
+ for (auto pathid : pathsid) {
+ // add '#' at start to make it an uri.
+ pathid.insert(pathid.begin(), '#');
+
+ if (foundOne) {
+ os << "|";
+ } else {
+ foundOne = true;
+ }
+ os << pathid.c_str() << ",0,1";
+ }
+ param_write_to_repr(os.str().c_str());
+ DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Link patharray parameter to path"));
+}
+
+void OriginalPathArrayParam::unlink(PathAndDirectionAndVisible* to)
+{
+ to->linked_modified_connection.disconnect();
+ to->linked_delete_connection.disconnect();
+ to->ref.detach();
+ to->_pathvector = Geom::PathVector();
+ if (to->href) {
+ g_free(to->href);
+ to->href = nullptr;
+ }
+}
+
+void OriginalPathArrayParam::remove_link(PathAndDirectionAndVisible* to)
+{
+ unlink(to);
+ for (std::vector<PathAndDirectionAndVisible*>::iterator iter = _vector.begin(); iter != _vector.end(); ++iter) {
+ if (*iter == to) {
+ PathAndDirectionAndVisible *w = *iter;
+ _vector.erase(iter);
+ delete w;
+ return;
+ }
+ }
+}
+
+void OriginalPathArrayParam::linked_delete(SPObject */*deleted*/, PathAndDirectionAndVisible* /*to*/)
+{
+ //remove_link(to);
+
+ param_write_to_repr(param_getSVGValue().c_str());
+}
+
+bool OriginalPathArrayParam::_updateLink(const Gtk::TreeIter& iter, PathAndDirectionAndVisible* pd)
+{
+ Gtk::TreeModel::Row row = *iter;
+ if (row[_model->_colObject] == pd) {
+ SPObject *obj = pd->ref.getObject();
+ row[_model->_colLabel] = obj && obj->getId() ? ( obj->label() ? obj->label() : obj->getId() ) : pd->href;
+ return true;
+ }
+ return false;
+}
+
+void OriginalPathArrayParam::linked_changed(SPObject */*old_obj*/, SPObject *new_obj, PathAndDirectionAndVisible* to)
+{
+ to->linked_delete_connection.disconnect();
+ to->linked_modified_connection.disconnect();
+ to->linked_transformed_connection.disconnect();
+
+ if (new_obj && SP_IS_ITEM(new_obj)) {
+ to->linked_delete_connection = new_obj->connectDelete(sigc::bind<PathAndDirectionAndVisible*>(sigc::mem_fun(*this, &OriginalPathArrayParam::linked_delete), to));
+ to->linked_modified_connection = new_obj->connectModified(sigc::bind<PathAndDirectionAndVisible*>(sigc::mem_fun(*this, &OriginalPathArrayParam::linked_modified), to));
+ to->linked_transformed_connection = SP_ITEM(new_obj)->connectTransformed(sigc::bind<PathAndDirectionAndVisible*>(sigc::mem_fun(*this, &OriginalPathArrayParam::linked_transformed), to));
+
+ linked_modified(new_obj, SP_OBJECT_MODIFIED_FLAG, to);
+ } else {
+ to->_pathvector = Geom::PathVector();
+ SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ _store->foreach_iter(sigc::bind<PathAndDirectionAndVisible*>(sigc::mem_fun(*this, &OriginalPathArrayParam::_updateLink), to));
+ }
+}
+
+void OriginalPathArrayParam::setPathVector(SPObject *linked_obj, guint /*flags*/, PathAndDirectionAndVisible* to)
+{
+ if (!to) {
+ return;
+ }
+ SPCurve *curve = nullptr;
+ if (SP_IS_SHAPE(linked_obj)) {
+ SPLPEItem * lpe_item = SP_LPE_ITEM(linked_obj);
+ if (_from_original_d) {
+ curve = SP_SHAPE(linked_obj)->getCurveForEdit();
+ } else if (_allow_only_bspline_spiro && lpe_item && lpe_item->hasPathEffect()){
+ curve = SP_SHAPE(linked_obj)->getCurveForEdit();
+ PathEffectList lpelist = lpe_item->getEffectList();
+ PathEffectList::iterator i;
+ for (i = lpelist.begin(); i != lpelist.end(); ++i) {
+ LivePathEffectObject *lpeobj = (*i)->lpeobject;
+ if (lpeobj) {
+ Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
+ if (dynamic_cast<Inkscape::LivePathEffect::LPEBSpline *>(lpe)) {
+ Geom::PathVector hp;
+ LivePathEffect::sp_bspline_do_effect(curve, 0, hp);
+ } else if (dynamic_cast<Inkscape::LivePathEffect::LPESpiro *>(lpe)) {
+ LivePathEffect::sp_spiro_do_effect(curve);
+ }
+ }
+ }
+ } else {
+ curve = SP_SHAPE(linked_obj)->getCurve();
+ }
+ } else if (SP_IS_TEXT(linked_obj)) {
+ curve = SP_TEXT(linked_obj)->getNormalizedBpath();
+ }
+
+ if (curve == nullptr) {
+ // curve invalid, set empty pathvector
+ to->_pathvector = Geom::PathVector();
+ } else {
+ to->_pathvector = curve->get_pathvector();
+ curve->unref();
+ }
+
+}
+
+void OriginalPathArrayParam::linked_modified(SPObject *linked_obj, guint flags, PathAndDirectionAndVisible* to)
+{
+ if (!to) {
+ return;
+ }
+ setPathVector(linked_obj, flags, to);
+ SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ _store->foreach_iter(sigc::bind<PathAndDirectionAndVisible*>(sigc::mem_fun(*this, &OriginalPathArrayParam::_updateLink), to));
+}
+
+bool OriginalPathArrayParam::param_readSVGValue(const gchar* strvalue)
+{
+ if (strvalue) {
+ while (!_vector.empty()) {
+ PathAndDirectionAndVisible *w = _vector.back();
+ unlink(w);
+ _vector.pop_back();
+ delete w;
+ }
+ _store->clear();
+
+ gchar ** strarray = g_strsplit(strvalue, "|", 0);
+ for (gchar ** iter = strarray; *iter != nullptr; iter++) {
+ if ((*iter)[0] == '#') {
+ gchar ** substrarray = g_strsplit(*iter, ",", 0);
+ PathAndDirectionAndVisible* w = new PathAndDirectionAndVisible((SPObject *)param_effect->getLPEObj());
+ w->href = g_strdup(*substrarray);
+ w->reversed = *(substrarray+1) != nullptr && (*(substrarray+1))[0] == '1';
+ //Like this to make backwards compatible, new value added in 0.93
+ w->visibled = *(substrarray+2) == nullptr || (*(substrarray+2))[0] == '1';
+ w->linked_changed_connection = w->ref.changedSignal().connect(sigc::bind<PathAndDirectionAndVisible *>(sigc::mem_fun(*this, &OriginalPathArrayParam::linked_changed), w));
+ w->ref.attach(URI(w->href));
+
+ _vector.push_back(w);
+
+ Gtk::TreeModel::iterator iter = _store->append();
+ Gtk::TreeModel::Row row = *iter;
+ SPObject *obj = w->ref.getObject();
+
+ row[_model->_colObject] = w;
+ row[_model->_colLabel] = obj ? ( obj->label() ? obj->label() : obj->getId() ) : w->href;
+ row[_model->_colReverse] = w->reversed;
+ row[_model->_colVisible] = w->visibled;
+ g_strfreev (substrarray);
+ }
+ }
+ g_strfreev (strarray);
+ return true;
+ }
+ return false;
+}
+
+Glib::ustring
+OriginalPathArrayParam::param_getSVGValue() const
+{
+ Inkscape::SVGOStringStream os;
+ bool foundOne = false;
+ for (auto iter : _vector) {
+ if (foundOne) {
+ os << "|";
+ } else {
+ foundOne = true;
+ }
+ os << iter->href << "," << (iter->reversed ? "1" : "0") << "," << (iter->visibled ? "1" : "0");
+ }
+ return os.str();
+}
+
+Glib::ustring
+OriginalPathArrayParam::param_getDefaultSVGValue() const
+{
+ return "";
+}
+
+void OriginalPathArrayParam::update()
+{
+ for (auto & iter : _vector) {
+ SPObject *linked_obj = iter->ref.getObject();
+ linked_modified(linked_obj, SP_OBJECT_MODIFIED_FLAG, iter);
+ }
+}
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/originalpatharray.h b/src/live_effects/parameter/originalpatharray.h
new file mode 100644
index 0000000..6f58627
--- /dev/null
+++ b/src/live_effects/parameter/originalpatharray.h
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINALPATHARRAY_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINALPATHARRAY_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <vector>
+
+#include <gtkmm/box.h>
+#include <gtkmm/treeview.h>
+#include <gtkmm/treestore.h>
+#include <gtkmm/scrolledwindow.h>
+
+#include "live_effects/parameter/parameter.h"
+#include "live_effects/parameter/path-reference.h"
+
+#include "svg/svg.h"
+#include "svg/stringstream.h"
+#include "path-reference.h"
+
+class SPObject;
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class PathAndDirectionAndVisible {
+public:
+ PathAndDirectionAndVisible(SPObject *owner)
+ : href(nullptr),
+ ref(owner),
+ _pathvector(Geom::PathVector()),
+ reversed(false),
+ visibled(true)
+ {
+
+ }
+ gchar *href;
+ URIReference ref;
+ Geom::PathVector _pathvector;
+ bool reversed;
+ bool visibled;
+
+ sigc::connection linked_changed_connection;
+ sigc::connection linked_delete_connection;
+ sigc::connection linked_modified_connection;
+ sigc::connection linked_transformed_connection;
+};
+
+class OriginalPathArrayParam : public Parameter {
+public:
+ class ModelColumns;
+
+ OriginalPathArrayParam( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect);
+
+ ~OriginalPathArrayParam() override;
+
+ Gtk::Widget * param_newWidget() override;
+ bool param_readSVGValue(const gchar * strvalue) override;
+ Glib::ustring param_getSVGValue() const override;
+ Glib::ustring param_getDefaultSVGValue() const override;
+ void param_set_default() override;
+ void param_update_default(const gchar * default_value) override{};
+ /** Disable the canvas indicators of parent class by overriding this method */
+ void param_editOncanvas(SPItem * /*item*/, SPDesktop * /*dt*/) override {};
+ /** Disable the canvas indicators of parent class by overriding this method */
+ void addCanvasIndicators(SPLPEItem const* /*lpeitem*/, std::vector<Geom::PathVector> & /*hp_vec*/) override {};
+ void setFromOriginalD(bool from_original_d){ _from_original_d = from_original_d; update();};
+ void allowOnlyBsplineSpiro(bool allow_only_bspline_spiro){ _allow_only_bspline_spiro = allow_only_bspline_spiro; update();};
+
+ std::vector<PathAndDirectionAndVisible*> _vector;
+
+protected:
+ bool _updateLink(const Gtk::TreeIter& iter, PathAndDirectionAndVisible* pd);
+ bool _selectIndex(const Gtk::TreeIter& iter, int* i);
+ void unlink(PathAndDirectionAndVisible* to);
+ void remove_link(PathAndDirectionAndVisible* to);
+ void setPathVector(SPObject *linked_obj, guint flags, PathAndDirectionAndVisible* to);
+
+ void linked_changed(SPObject *old_obj, SPObject *new_obj, PathAndDirectionAndVisible* to);
+ void linked_modified(SPObject *linked_obj, guint flags, PathAndDirectionAndVisible* to);
+ void linked_transformed(Geom::Affine const *, SPItem *, PathAndDirectionAndVisible*) {}
+ void linked_delete(SPObject *deleted, PathAndDirectionAndVisible* to);
+
+ ModelColumns *_model;
+ Glib::RefPtr<Gtk::TreeStore> _store;
+ Gtk::TreeView _tree;
+ Gtk::CellRendererText *_text_renderer;
+ Gtk::CellRendererToggle *_toggle_reverse;
+ Gtk::CellRendererToggle *_toggle_visible;
+ Gtk::TreeView::Column *_name_column;
+ Gtk::ScrolledWindow _scroller;
+
+ void on_link_button_click();
+ void on_remove_button_click();
+ void on_up_button_click();
+ void on_down_button_click();
+ void on_reverse_toggled(const Glib::ustring& path);
+ void on_visible_toggled(const Glib::ustring& path);
+
+private:
+ bool _from_original_d;
+ bool _allow_only_bspline_spiro;
+ void update();
+ OriginalPathArrayParam(const OriginalPathArrayParam&) = delete;
+ OriginalPathArrayParam& operator=(const OriginalPathArrayParam&) = delete;
+};
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/parameter.cpp b/src/live_effects/parameter/parameter.cpp
new file mode 100644
index 0000000..355e7dd
--- /dev/null
+++ b/src/live_effects/parameter/parameter.cpp
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+
+#include "live_effects/parameter/parameter.h"
+#include "live_effects/effect.h"
+#include "svg/svg.h"
+#include "xml/repr.h"
+
+#include "svg/stringstream.h"
+
+#include "verbs.h"
+
+#include <glibmm/i18n.h>
+
+#include <utility>
+
+#define noLPEREALPARAM_DEBUG
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+
+Parameter::Parameter(Glib::ustring label, Glib::ustring tip, Glib::ustring key, Inkscape::UI::Widget::Registry *wr,
+ Effect *effect)
+ : param_key(std::move(key))
+ , param_wr(wr)
+ , param_label(std::move(label))
+ , oncanvas_editable(false)
+ , widget_is_visible(true)
+ , widget_is_enabled(true)
+ , param_tooltip(std::move(tip))
+ , param_effect(effect)
+{
+}
+
+void Parameter::param_write_to_repr(const char *svgd)
+{
+ param_effect->getRepr()->setAttribute(param_key, svgd);
+}
+
+void Parameter::write_to_SVG()
+{
+ param_write_to_repr(param_getSVGValue().c_str());
+}
+
+/*###########################################
+ * REAL PARAM
+ */
+ScalarParam::ScalarParam(const Glib::ustring &label, const Glib::ustring &tip, const Glib::ustring &key,
+ Inkscape::UI::Widget::Registry *wr, Effect *effect, gdouble default_value)
+ : Parameter(label, tip, key, wr, effect)
+ , value(default_value)
+ , min(-SCALARPARAM_G_MAXDOUBLE)
+ , max(SCALARPARAM_G_MAXDOUBLE)
+ , integer(false)
+ , defvalue(default_value)
+ , digits(2)
+ , inc_step(0.1)
+ , inc_page(1)
+ , add_slider(false)
+ , _set_undo(true)
+{
+}
+
+ScalarParam::~ScalarParam() = default;
+
+bool ScalarParam::param_readSVGValue(const gchar *strvalue)
+{
+ double newval;
+ unsigned int success = sp_svg_number_read_d(strvalue, &newval);
+ if (success == 1) {
+ param_set_value(newval);
+ return true;
+ }
+ return false;
+}
+
+Glib::ustring ScalarParam::param_getSVGValue() const
+{
+ Inkscape::SVGOStringStream os;
+ os << value;
+ return os.str();
+}
+
+Glib::ustring ScalarParam::param_getDefaultSVGValue() const
+{
+ Inkscape::SVGOStringStream os;
+ os << defvalue;
+ return os.str();
+}
+
+void ScalarParam::param_set_default() { param_set_value(defvalue); }
+
+void ScalarParam::param_update_default(gdouble default_value) { defvalue = default_value; }
+
+void ScalarParam::param_update_default(const gchar *default_value)
+{
+ double newval;
+ unsigned int success = sp_svg_number_read_d(default_value, &newval);
+ if (success == 1) {
+ param_update_default(newval);
+ }
+}
+
+void ScalarParam::param_transform_multiply(Geom::Affine const &postmul, bool set)
+{
+ // Check if proportional stroke-width scaling is on
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ bool transform_stroke = prefs ? prefs->getBool("/options/transform/stroke", true) : true;
+ if (transform_stroke || set) {
+ param_set_value(value * postmul.descrim());
+ write_to_SVG();
+ }
+}
+
+void ScalarParam::param_set_value(gdouble val)
+{
+ value = val;
+ if (integer)
+ value = round(value);
+ if (value > max)
+ value = max;
+ if (value < min)
+ value = min;
+}
+
+void ScalarParam::param_set_range(gdouble min, gdouble max)
+{
+ // if you look at client code, you'll see that many effects
+ // has a tendency to set an upper range of Geom::infinity().
+ // Once again, in gtk2, this is not a problem. But in gtk3,
+ // widgets get allocated the amount of size they ask for,
+ // leading to excessively long widgets.
+ if (min >= -SCALARPARAM_G_MAXDOUBLE) {
+ this->min = min;
+ } else {
+ this->min = -SCALARPARAM_G_MAXDOUBLE;
+ }
+ if (max <= SCALARPARAM_G_MAXDOUBLE) {
+ this->max = max;
+ } else {
+ this->max = SCALARPARAM_G_MAXDOUBLE;
+ }
+ param_set_value(value); // reset value to see whether it is in ranges
+}
+
+void ScalarParam::param_make_integer(bool yes)
+{
+ integer = yes;
+ digits = 0;
+ inc_step = 1;
+ inc_page = 10;
+}
+
+void ScalarParam::param_set_undo(bool set_undo) { _set_undo = set_undo; }
+
+Gtk::Widget *ScalarParam::param_newWidget()
+{
+ if (widget_is_visible) {
+ Inkscape::UI::Widget::RegisteredScalar *rsu = Gtk::manage(new Inkscape::UI::Widget::RegisteredScalar(
+ param_label, param_tooltip, param_key, *param_wr, param_effect->getRepr(), param_effect->getSPDoc()));
+
+ rsu->setValue(value);
+ rsu->setDigits(digits);
+ rsu->setIncrements(inc_step, inc_page);
+ rsu->setRange(min, max);
+ rsu->setProgrammatically = false;
+ if (add_slider) {
+ rsu->addSlider();
+ }
+ if (_set_undo) {
+ rsu->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change scalar parameter"));
+ }
+ return dynamic_cast<Gtk::Widget *>(rsu);
+ } else {
+ return nullptr;
+ }
+}
+
+void ScalarParam::param_set_digits(unsigned digits) { this->digits = digits; }
+
+void ScalarParam::param_set_increments(double step, double page)
+{
+ inc_step = step;
+ inc_page = page;
+}
+
+
+
+} /* namespace LivePathEffect */
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/parameter.h b/src/live_effects/parameter/parameter.h
new file mode 100644
index 0000000..b73a5fe
--- /dev/null
+++ b/src/live_effects/parameter/parameter.h
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "ui/widget/registered-widget.h"
+#include <2geom/forward.h>
+#include <2geom/pathvector.h>
+#include <glibmm/ustring.h>
+
+// In gtk2, this wasn't an issue; we could toss around
+// G_MAXDOUBLE and not worry about size allocations. But
+// in gtk3, it is an issue: it allocates widget size for the maxmium
+// value you pass to it, leading to some insane lengths.
+// If you need this to be more, please be conservative about it.
+const double SCALARPARAM_G_MAXDOUBLE =
+ 10000000000.0; // TODO fixme: using an arbitrary large number as a magic value seems fragile.
+
+class KnotHolder;
+class SPLPEItem;
+class SPDesktop;
+class SPItem;
+
+namespace Gtk {
+class Widget;
+}
+
+namespace Inkscape {
+
+namespace NodePath {
+class Path;
+}
+
+namespace UI {
+namespace Widget {
+class Registry;
+}
+} // namespace UI
+
+namespace LivePathEffect {
+
+class Effect;
+
+class Parameter {
+ public:
+ Parameter(Glib::ustring label, Glib::ustring tip, Glib::ustring key, Inkscape::UI::Widget::Registry *wr,
+ Effect *effect);
+ virtual ~Parameter() = default;
+ ;
+
+ Parameter(const Parameter &) = delete;
+ Parameter &operator=(const Parameter &) = delete;
+
+ virtual bool param_readSVGValue(const gchar *strvalue) = 0; // returns true if new value is valid / accepted.
+ virtual Glib::ustring param_getSVGValue() const = 0;
+ virtual Glib::ustring param_getDefaultSVGValue() const = 0;
+ virtual void param_widget_is_visible(bool is_visible) { widget_is_visible = is_visible; }
+ virtual void param_widget_is_enabled(bool is_enabled) { widget_is_enabled = is_enabled; }
+ void write_to_SVG();
+
+ virtual void param_set_default() = 0;
+ virtual void param_update_default(const gchar *default_value) = 0;
+ // This creates a new widget (newed with Gtk::manage(new ...);)
+ virtual Gtk::Widget *param_newWidget() = 0;
+
+ virtual Glib::ustring *param_getTooltip() { return &param_tooltip; };
+
+ // overload these for your particular parameter to make it provide knotholder handles or canvas helperpaths
+ virtual bool providesKnotHolderEntities() const { return false; }
+ virtual void addKnotHolderEntities(KnotHolder * /*knotholder*/, SPItem * /*item*/){};
+ virtual void addCanvasIndicators(SPLPEItem const * /*lpeitem*/, std::vector<Geom::PathVector> & /*hp_vec*/){};
+
+ virtual void param_editOncanvas(SPItem * /*item*/, SPDesktop * /*dt*/){};
+ virtual void param_setup_nodepath(Inkscape::NodePath::Path * /*np*/){};
+
+ virtual void param_transform_multiply(Geom::Affine const & /*postmul*/, bool set){};
+
+ Glib::ustring param_key;
+ Glib::ustring param_tooltip;
+ Inkscape::UI::Widget::Registry *param_wr;
+ Glib::ustring param_label;
+
+ bool oncanvas_editable;
+ bool widget_is_visible;
+ bool widget_is_enabled;
+
+ protected:
+ Effect *param_effect;
+
+ void param_write_to_repr(const char *svgd);
+};
+
+
+class ScalarParam : public Parameter {
+ public:
+ ScalarParam(const Glib::ustring &label, const Glib::ustring &tip, const Glib::ustring &key,
+ Inkscape::UI::Widget::Registry *wr, Effect *effect, gdouble default_value = 1.0);
+ ~ScalarParam() override;
+ ScalarParam(const ScalarParam &) = delete;
+ ScalarParam &operator=(const ScalarParam &) = delete;
+
+ bool param_readSVGValue(const gchar *strvalue) override;
+ Glib::ustring param_getSVGValue() const override;
+ Glib::ustring param_getDefaultSVGValue() const override;
+ void param_transform_multiply(Geom::Affine const &postmul, bool set) override;
+
+ void param_set_default() override;
+ void param_update_default(gdouble default_value);
+ void param_update_default(const gchar *default_value) override;
+ void param_set_value(gdouble val);
+ void param_make_integer(bool yes = true);
+ void param_set_range(gdouble min, gdouble max);
+ void param_set_digits(unsigned digits);
+ void param_set_increments(double step, double page);
+ void addSlider(bool add_slider_widget) { add_slider = add_slider_widget; };
+ double param_get_max() { return max; };
+ double param_get_min() { return min; };
+ void param_set_undo(bool set_undo);
+ Gtk::Widget *param_newWidget() override;
+
+ inline operator gdouble() const { return value; };
+
+ protected:
+ gdouble value;
+ gdouble min;
+ gdouble max;
+ bool integer;
+ gdouble defvalue;
+ unsigned digits;
+ double inc_step;
+ double inc_page;
+ bool add_slider;
+ bool _set_undo;
+};
+
+} // namespace LivePathEffect
+
+} // namespace Inkscape
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/live_effects/parameter/path-reference.cpp b/src/live_effects/parameter/path-reference.cpp
new file mode 100644
index 0000000..c3ce3d5
--- /dev/null
+++ b/src/live_effects/parameter/path-reference.cpp
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * The reference corresponding to href of LPE Path parameter.
+ *
+ * Copyright (C) 2008 Johan Engelen
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/parameter/path-reference.h"
+
+#include "object/sp-shape.h"
+#include "object/sp-text.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+bool PathReference::_acceptObject(SPObject * const obj) const
+{
+ if (SP_IS_SHAPE(obj) || SP_IS_TEXT(obj)) {
+ /* Refuse references to lpeobject */
+ if (obj == getOwner()) {
+ return false;
+ }
+ // TODO: check whether the referred path has this LPE applied, if so: deny deny deny!
+ return URIReference::_acceptObject(obj);
+ } else {
+ return false;
+ }
+}
+
+} // namespace LivePathEffect
+} // namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/path-reference.h b/src/live_effects/parameter/path-reference.h
new file mode 100644
index 0000000..0b33194
--- /dev/null
+++ b/src/live_effects/parameter/path-reference.h
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef SEEN_LPE_PATH_REFERENCE_H
+#define SEEN_LPE_PATH_REFERENCE_H
+
+/*
+ * Copyright (C) 2008-2012 Authors
+ * Authors: Johan Engelen
+ * Abhishek Sharma
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "object/uri-references.h"
+
+class SPItem;
+namespace Inkscape {
+namespace XML { class Node; }
+
+namespace LivePathEffect {
+
+/**
+ * The reference corresponding to href of LPE PathParam.
+ */
+class PathReference : public Inkscape::URIReference {
+public:
+ PathReference(SPObject *owner) : URIReference(owner) {}
+
+ SPItem *getObject() const {
+ return (SPItem *)URIReference::getObject();
+ }
+
+protected:
+ bool _acceptObject(SPObject * const obj) const override;
+
+private:
+ PathReference(const PathReference&) = delete;
+ PathReference& operator=(const PathReference&) = delete;
+};
+
+} // namespace LivePathEffect
+
+} // namespace Inkscape
+
+
+
+#endif /* !SEEN_LPE_PATH_REFERENCE_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/path.cpp b/src/live_effects/parameter/path.cpp
new file mode 100644
index 0000000..9b51c2d
--- /dev/null
+++ b/src/live_effects/parameter/path.cpp
@@ -0,0 +1,591 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ * Abhishek Sharma
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/parameter/path.h"
+#include "live_effects/lpeobject.h"
+
+#include <glibmm/i18n.h>
+#include <glibmm/utility.h>
+
+#include <gtkmm/button.h>
+#include <gtkmm/label.h>
+
+#include "bad-uri-exception.h"
+#include "ui/widget/point.h"
+
+#include "live_effects/effect.h"
+#include "svg/svg.h"
+#include <2geom/svg-path-parser.h>
+#include <2geom/sbasis-to-bezier.h>
+#include <2geom/pathvector.h>
+#include <2geom/d2.h>
+
+#include "desktop.h"
+#include "document-undo.h"
+#include "document.h"
+#include "inkscape.h"
+#include "message-stack.h"
+#include "selection-chemistry.h"
+#include "ui/icon-loader.h"
+#include "verbs.h"
+#include "xml/repr.h"
+// needed for on-canvas editing:
+#include "ui/tools-switch.h"
+#include "ui/shape-editor.h"
+
+#include "selection.h"
+// clipboard support
+#include "ui/clipboard.h"
+// required for linking to other paths
+
+#include "object/uri.h"
+#include "object/sp-shape.h"
+#include "object/sp-text.h"
+
+#include "display/curve.h"
+
+#include "ui/tools/node-tool.h"
+#include "ui/tool/multi-path-manipulator.h"
+#include "ui/tool/shape-record.h"
+
+#include "ui/icon-names.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+PathParam::PathParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect, const gchar * default_value)
+ : Parameter(label, tip, key, wr, effect),
+ changed(true),
+ _pathvector(),
+ _pwd2(),
+ must_recalculate_pwd2(false),
+ href(nullptr),
+ ref( (SPObject*)effect->getLPEObj() )
+{
+ defvalue = g_strdup(default_value);
+ param_readSVGValue(defvalue);
+ oncanvas_editable = true;
+ _from_original_d = false;
+ _edit_button = true;
+ _copy_button = true;
+ _paste_button = true;
+ _link_button = true;
+ ref_changed_connection = ref.changedSignal().connect(sigc::mem_fun(*this, &PathParam::ref_changed));
+}
+
+PathParam::~PathParam()
+{
+ remove_link();
+//TODO: Removed to fix a bug https://bugs.launchpad.net/inkscape/+bug/1716926
+// Maybe wee need to resurrect, not know when this code is added, but seems also not working now in a few test I do.
+// in the future and do a deeper fix in multi-path-manipulator
+// using namespace Inkscape::UI;
+// SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+// if (desktop) {
+// if (tools_isactive(desktop, TOOLS_NODES)) {
+// SPItem * item = SP_ACTIVE_DESKTOP->getSelection()->singleItem();
+// if (item) {
+// Inkscape::UI::Tools::NodeTool *nt = static_cast<Inkscape::UI::Tools::NodeTool*>(desktop->event_context);
+// std::set<ShapeRecord> shapes;
+// ShapeRecord r;
+// r.item = item;
+// shapes.insert(r);
+// nt->_multipath->setItems(shapes);
+// }
+// }
+// }
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (desktop) {
+ // TODO remove the tools_switch atrocity.
+ if (tools_isactive(desktop, TOOLS_NODES)) {
+ tools_switch(desktop, TOOLS_SELECT);
+ tools_switch(desktop, TOOLS_NODES);
+ }
+ }
+ g_free(defvalue);
+}
+
+Geom::PathVector const &
+PathParam::get_pathvector() const
+{
+ return _pathvector;
+}
+
+Geom::Piecewise<Geom::D2<Geom::SBasis> > const &
+PathParam::get_pwd2()
+{
+ ensure_pwd2();
+ return _pwd2;
+}
+
+void
+PathParam::param_set_default()
+{
+ param_readSVGValue(defvalue);
+}
+
+void
+PathParam::param_set_and_write_default()
+{
+ param_write_to_repr(defvalue);
+}
+
+bool
+PathParam::param_readSVGValue(const gchar * strvalue)
+{
+ if (strvalue) {
+ _pathvector.clear();
+ remove_link();
+ must_recalculate_pwd2 = true;
+
+ if (strvalue[0] == '#') {
+ if (href)
+ g_free(href);
+ href = g_strdup(strvalue);
+
+ // Now do the attaching, which emits the changed signal.
+ try {
+ ref.attach(Inkscape::URI(href));
+ //lp:1299948
+ SPItem* i = ref.getObject();
+ if (i) {
+ linked_modified_callback(i, SP_OBJECT_MODIFIED_FLAG);
+ } // else: document still processing new events. Repr of the linked object not created yet.
+ } catch (Inkscape::BadURIException &e) {
+ g_warning("%s", e.what());
+ ref.detach();
+ _pathvector = sp_svg_read_pathv(defvalue);
+ }
+ } else {
+ _pathvector = sp_svg_read_pathv(strvalue);
+ }
+
+ emit_changed();
+ return true;
+ }
+
+ return false;
+}
+
+Glib::ustring
+PathParam::param_getSVGValue() const
+{
+ if (href) {
+ return href;
+ } else {
+ gchar * svgd = sp_svg_write_path( _pathvector );
+ return Glib::convert_return_gchar_ptr_to_ustring(svgd);
+ }
+}
+
+Glib::ustring
+PathParam::param_getDefaultSVGValue() const
+{
+ return defvalue;
+}
+
+void
+PathParam::set_buttons(bool edit_button, bool copy_button, bool paste_button, bool link_button)
+{
+ _edit_button = edit_button;
+ _copy_button = copy_button;
+ _paste_button = paste_button;
+ _link_button = link_button;
+}
+
+Gtk::Widget *
+PathParam::param_newWidget()
+{
+ Gtk::HBox * _widget = Gtk::manage(new Gtk::HBox());
+
+ Gtk::Label* pLabel = Gtk::manage(new Gtk::Label(param_label));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pLabel, true, true);
+ pLabel->set_tooltip_text(param_tooltip);
+ Gtk::Image * pIcon = nullptr;
+ Gtk::Button * pButton = nullptr;
+ if (_edit_button) {
+ pIcon = Gtk::manage(sp_get_icon_image("tool-node-editor", Gtk::ICON_SIZE_BUTTON));
+ pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &PathParam::on_edit_button_click));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true);
+ pButton->set_tooltip_text(_("Edit on-canvas"));
+ }
+
+ if (_copy_button) {
+ pIcon = Gtk::manage(sp_get_icon_image("edit-copy", Gtk::ICON_SIZE_BUTTON));
+ pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &PathParam::on_copy_button_click));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true);
+ pButton->set_tooltip_text(_("Copy path"));
+ }
+
+ if (_paste_button) {
+ pIcon = Gtk::manage(sp_get_icon_image("edit-paste", Gtk::ICON_SIZE_BUTTON));
+ pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &PathParam::on_paste_button_click));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true);
+ pButton->set_tooltip_text(_("Paste path"));
+ }
+ if (_link_button) {
+ pIcon = Gtk::manage(sp_get_icon_image("edit-clone", Gtk::ICON_SIZE_BUTTON));
+ pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &PathParam::on_link_button_click));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true);
+ pButton->set_tooltip_text(_("Link to path in clipboard"));
+ }
+
+ static_cast<Gtk::HBox*>(_widget)->show_all_children();
+
+ return dynamic_cast<Gtk::Widget *> (_widget);
+}
+
+void
+PathParam::param_editOncanvas(SPItem *item, SPDesktop * dt)
+{
+ SPDocument *document = dt->getDocument();
+ bool saved = DocumentUndo::getUndoSensitive(document);
+ DocumentUndo::setUndoSensitive(document, false);
+ using namespace Inkscape::UI;
+
+ // TODO remove the tools_switch atrocity.
+ if (!tools_isactive(dt, TOOLS_NODES)) {
+ tools_switch(dt, TOOLS_NODES);
+ }
+
+ Inkscape::UI::Tools::NodeTool *nt = static_cast<Inkscape::UI::Tools::NodeTool*>(dt->event_context);
+ std::set<ShapeRecord> shapes;
+ ShapeRecord r;
+
+ r.role = SHAPE_ROLE_LPE_PARAM;
+ r.edit_transform = item->i2dt_affine(); // TODO is it right?
+ if (!href) {
+ r.object = dynamic_cast<SPObject *>(param_effect->getLPEObj());
+ r.lpe_key = param_key;
+ Geom::PathVector stored_pv = _pathvector;
+ if (_pathvector.empty()) {
+ param_write_to_repr("M0,0 L1,0");
+ } else {
+ gchar *svgd = sp_svg_write_path(stored_pv);
+ param_write_to_repr(svgd);
+ g_free(svgd);
+ }
+ } else {
+ r.object = ref.getObject();
+ }
+ shapes.insert(r);
+ nt->_multipath->setItems(shapes);
+ DocumentUndo::setUndoSensitive(document, saved);
+}
+
+void
+PathParam::param_setup_nodepath(Inkscape::NodePath::Path *)
+{
+ // TODO this method should not exist at all!
+}
+
+void
+PathParam::addCanvasIndicators(SPLPEItem const*/*lpeitem*/, std::vector<Geom::PathVector> &hp_vec)
+{
+ hp_vec.push_back(_pathvector);
+}
+
+/*
+ * Only applies transform when not referring to other path!
+ */
+void
+PathParam::param_transform_multiply(Geom::Affine const& postmul, bool /*set*/)
+{
+ // only apply transform when not referring to other path
+ if (!href) {
+ set_new_value( _pathvector * postmul, true );
+ }
+}
+
+/*
+ * See comments for set_new_value(Geom::PathVector).
+ */
+void
+PathParam::set_new_value (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & newpath, bool write_to_svg)
+{
+ remove_link();
+ _pathvector = Geom::path_from_piecewise(newpath, LPE_CONVERSION_TOLERANCE);
+
+ if (write_to_svg) {
+ gchar * svgd = sp_svg_write_path( _pathvector );
+ param_write_to_repr(svgd);
+ g_free(svgd);
+
+ // After the whole "writing to svg avalanche of function calling": force value upon pwd2 and don't recalculate.
+ _pwd2 = newpath;
+ must_recalculate_pwd2 = false;
+ } else {
+ _pwd2 = newpath;
+ must_recalculate_pwd2 = false;
+ emit_changed();
+ }
+}
+
+/*
+ * This method sets new path data.
+ * If this PathParam refers to another path, this link is removed (and replaced with explicit path data).
+ *
+ * If write_to_svg = true :
+ * The new path data is written to SVG. In this case the signal_path_changed signal
+ * is not directly emitted in this method, because writing to SVG
+ * triggers the LPEObject to which this belongs to call Effect::setParameter which calls
+ * PathParam::readSVGValue, which finally emits the signal_path_changed signal.
+ * If write_to_svg = false :
+ * The new path data is not written to SVG. This method will emit the signal_path_changed signal.
+ */
+void
+PathParam::set_new_value (Geom::PathVector const &newpath, bool write_to_svg)
+{
+ remove_link();
+ if (newpath.empty()) {
+ param_set_and_write_default();
+ return;
+ } else {
+ _pathvector = newpath;
+ }
+ must_recalculate_pwd2 = true;
+
+ if (write_to_svg) {
+ gchar * svgd = sp_svg_write_path( _pathvector );
+ param_write_to_repr(svgd);
+ g_free(svgd);
+ } else {
+ emit_changed();
+ }
+}
+
+void
+PathParam::ensure_pwd2()
+{
+ if (must_recalculate_pwd2) {
+ _pwd2.clear();
+ for (const auto & i : _pathvector) {
+ _pwd2.concat( i.toPwSb() );
+ }
+
+ must_recalculate_pwd2 = false;
+ }
+}
+
+void
+PathParam::emit_changed()
+{
+ changed = true;
+ signal_path_changed.emit();
+}
+
+void
+PathParam::start_listening(SPObject * to)
+{
+ if ( to == nullptr ) {
+ return;
+ }
+ linked_delete_connection = to->connectDelete(sigc::mem_fun(*this, &PathParam::linked_delete));
+ linked_modified_connection = to->connectModified(sigc::mem_fun(*this, &PathParam::linked_modified));
+ if (SP_IS_ITEM(to)) {
+ linked_transformed_connection = SP_ITEM(to)->connectTransformed(sigc::mem_fun(*this, &PathParam::linked_transformed));
+ }
+ linked_modified(to, SP_OBJECT_MODIFIED_FLAG); // simulate linked_modified signal, so that path data is updated
+}
+
+void
+PathParam::quit_listening()
+{
+ linked_modified_connection.disconnect();
+ linked_delete_connection.disconnect();
+ linked_transformed_connection.disconnect();
+}
+
+void
+PathParam::ref_changed(SPObject */*old_ref*/, SPObject *new_ref)
+{
+ quit_listening();
+ if ( new_ref ) {
+ start_listening(new_ref);
+ }
+}
+
+void
+PathParam::remove_link()
+{
+ if (href) {
+ ref.detach();
+ g_free(href);
+ href = nullptr;
+ }
+}
+
+void
+PathParam::linked_delete(SPObject */*deleted*/)
+{
+ quit_listening();
+ remove_link();
+ set_new_value (_pathvector, true);
+}
+
+void PathParam::linked_modified(SPObject *linked_obj, guint flags)
+{
+ linked_modified_callback(linked_obj, flags);
+}
+
+void PathParam::linked_transformed(Geom::Affine const *rel_transf, SPItem *moved_item)
+{
+ linked_transformed_callback(rel_transf, moved_item);
+}
+
+void
+PathParam::linked_modified_callback(SPObject *linked_obj, guint /*flags*/)
+{
+ SPCurve *curve = nullptr;
+ if (SP_IS_SHAPE(linked_obj)) {
+ if (_from_original_d) {
+ curve = SP_SHAPE(linked_obj)->getCurveForEdit();
+ } else {
+ curve = SP_SHAPE(linked_obj)->getCurve();
+ }
+ }
+ if (SP_IS_TEXT(linked_obj)) {
+ curve = SP_TEXT(linked_obj)->getNormalizedBpath();
+ }
+
+ if (curve == nullptr) {
+ // curve invalid, set default value
+ _pathvector = sp_svg_read_pathv(defvalue);
+ } else {
+ _pathvector = curve->get_pathvector();
+ curve->unref();
+ }
+
+ must_recalculate_pwd2 = true;
+ emit_changed();
+ SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG);
+}
+
+void
+PathParam::param_update_default(const gchar * default_value){
+ defvalue = strdup(default_value);
+}
+
+/* CALLBACK FUNCTIONS FOR THE BUTTONS */
+void
+PathParam::on_edit_button_click()
+{
+ SPItem * item = SP_ACTIVE_DESKTOP->getSelection()->singleItem();
+ if (item != nullptr) {
+ param_editOncanvas(item, SP_ACTIVE_DESKTOP);
+ }
+}
+
+void
+PathParam::paste_param_path(const char *svgd)
+{
+ // only recognize a non-null, non-empty string
+ if (svgd && *svgd) {
+ // remove possible link to path
+ remove_link();
+ SPItem * item = SP_ACTIVE_DESKTOP->getSelection()->singleItem();
+ char *svgd_new = nullptr;
+ if (item != nullptr) {
+ Geom::PathVector path_clipboard = sp_svg_read_pathv(svgd);
+ path_clipboard *= item->i2doc_affine().inverse();
+ svgd_new = sp_svg_write_path(path_clipboard);
+ svgd = svgd_new;
+ }
+
+ param_write_to_repr(svgd);
+ g_free(svgd_new);
+ signal_path_pasted.emit();
+ }
+}
+
+void
+PathParam::on_paste_button_click()
+{
+ Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get();
+ Glib::ustring svgd = cm->getPathParameter(SP_ACTIVE_DESKTOP);
+ paste_param_path(svgd.data());
+ DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Paste path parameter"));
+}
+
+void
+PathParam::on_copy_button_click()
+{
+ Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get();
+ cm->copyPathParameter(this);
+}
+
+void
+PathParam::linkitem(Glib::ustring pathid)
+{
+ if (pathid.empty()) {
+ return;
+ }
+
+ // add '#' at start to make it an uri.
+ pathid.insert(pathid.begin(), '#');
+ if ( href && strcmp(pathid.c_str(), href) == 0 ) {
+ // no change, do nothing
+ return;
+ } else {
+ // TODO:
+ // check if id really exists in document, or only in clipboard document: if only in clipboard then invalid
+ // check if linking to object to which LPE is applied (maybe delegated to PathReference
+
+ param_write_to_repr(pathid.c_str());
+ DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Link path parameter to path"));
+ }
+}
+
+void
+PathParam::on_link_button_click()
+{
+ Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get();
+ Glib::ustring pathid = cm->getShapeOrTextObjectId(SP_ACTIVE_DESKTOP);
+
+ linkitem(pathid);
+}
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/path.h b/src/live_effects/parameter/path.h
new file mode 100644
index 0000000..76a6584
--- /dev/null
+++ b/src/live_effects/parameter/path.h
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_PATH_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_PATH_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <glib.h>
+#include <2geom/path.h>
+
+#include "live_effects/parameter/parameter.h"
+#include "live_effects/parameter/path-reference.h"
+#include <cstddef>
+#include <sigc++/sigc++.h>
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class PathParam : public Parameter {
+public:
+ PathParam ( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect,
+ const gchar * default_value = "M0,0 L1,1");
+ ~PathParam() override;
+
+ Geom::PathVector const & get_pathvector() const;
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > const & get_pwd2();
+
+ Gtk::Widget * param_newWidget() override;
+
+ bool param_readSVGValue(const gchar * strvalue) override;
+ Glib::ustring param_getSVGValue() const override;
+ Glib::ustring param_getDefaultSVGValue() const override;
+
+ void param_set_default() override;
+ void param_update_default(const gchar * default_value) override;
+ void param_set_and_write_default();
+ void set_new_value (Geom::PathVector const &newpath, bool write_to_svg);
+ void set_new_value (Geom::Piecewise<Geom::D2<Geom::SBasis> > const &newpath, bool write_to_svg);
+ void set_buttons(bool edit_button, bool copy_button, bool paste_button, bool link_button);
+ void param_editOncanvas(SPItem * item, SPDesktop * dt) override;
+ void param_setup_nodepath(Inkscape::NodePath::Path *np) override;
+ void addCanvasIndicators(SPLPEItem const* lpeitem, std::vector<Geom::PathVector> &hp_vec) override;
+
+ void param_transform_multiply(Geom::Affine const &postmul, bool set) override;
+ void setFromOriginalD(bool from_original_d){ _from_original_d = from_original_d; };
+
+ sigc::signal <void> signal_path_pasted;
+ sigc::signal <void> signal_path_changed;
+ bool changed; /* this gets set whenever the path is changed (this is set to true, and then the signal_path_changed signal is emitted).
+ * the user must set it back to false if she wants to use it sensibly */
+
+ void paste_param_path(const char *svgd);
+ void on_paste_button_click();
+ void linkitem(Glib::ustring pathid);
+
+protected:
+ Geom::PathVector _pathvector; // this is primary data storage, since it is closest to SVG.
+
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > _pwd2; // secondary, hence the bool must_recalculate_pwd2
+ bool must_recalculate_pwd2; // set when _pathvector was updated, but _pwd2 not
+ void ensure_pwd2(); // ensures _pwd2 is up to date
+
+ gchar * href; // contains link to other object, e.g. "#path2428", NULL if PathParam contains pathdata itself
+ PathReference ref;
+ sigc::connection ref_changed_connection;
+ sigc::connection linked_delete_connection;
+ sigc::connection linked_modified_connection;
+ sigc::connection linked_transformed_connection;
+ void ref_changed(SPObject *old_ref, SPObject *new_ref);
+ void remove_link();
+ void start_listening(SPObject * to);
+ void quit_listening();
+ void linked_delete(SPObject *deleted);
+ void linked_modified(SPObject *linked_obj, guint flags);
+ void linked_transformed(Geom::Affine const *rel_transf, SPItem *moved_item);
+ virtual void linked_modified_callback(SPObject *linked_obj, guint flags);
+ virtual void linked_transformed_callback(Geom::Affine const * /*rel_transf*/, SPItem * /*moved_item*/) {};
+
+ void on_edit_button_click();
+ void on_copy_button_click();
+ void on_link_button_click();
+
+ void emit_changed();
+
+ gchar * defvalue;
+
+private:
+ bool _from_original_d;
+ bool _edit_button;
+ bool _copy_button;
+ bool _paste_button;
+ bool _link_button;
+ PathParam(const PathParam&) = delete;
+ PathParam& operator=(const PathParam&) = delete;
+};
+
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/point.cpp b/src/live_effects/parameter/point.cpp
new file mode 100644
index 0000000..8f627a9
--- /dev/null
+++ b/src/live_effects/parameter/point.cpp
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/parameter/point.h"
+#include "live_effects/effect.h"
+#include "svg/svg.h"
+#include "svg/stringstream.h"
+#include "ui/widget/point.h"
+#include "inkscape.h"
+#include "verbs.h"
+#include "knotholder.h"
+#include <glibmm/i18n.h>
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+PointParam::PointParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect, const gchar *htip, Geom::Point default_value,
+ bool live_update )
+ : Parameter(label, tip, key, wr, effect),
+ defvalue(default_value),
+ liveupdate(live_update),
+ _knot_entity(nullptr)
+{
+ knot_shape = SP_KNOT_SHAPE_DIAMOND;
+ knot_mode = SP_KNOT_MODE_XOR;
+ knot_color = 0xffffff00;
+ handle_tip = g_strdup(htip);
+}
+
+PointParam::~PointParam()
+{
+ if (handle_tip)
+ g_free(handle_tip);
+}
+
+void
+PointParam::param_set_default()
+{
+ param_setValue(defvalue,true);
+}
+
+void
+PointParam::param_set_liveupdate( bool live_update)
+{
+ liveupdate = live_update;
+}
+
+Geom::Point
+PointParam::param_get_default() const{
+ return defvalue;
+}
+
+void
+PointParam::param_update_default(Geom::Point default_point)
+{
+ defvalue = default_point;
+}
+
+void
+PointParam::param_update_default(const gchar * default_point)
+{
+ gchar ** strarray = g_strsplit(default_point, ",", 2);
+ double newx, newy;
+ unsigned int success = sp_svg_number_read_d(strarray[0], &newx);
+ success += sp_svg_number_read_d(strarray[1], &newy);
+ g_strfreev (strarray);
+ if (success == 2) {
+ param_update_default( Geom::Point(newx, newy) );
+ }
+}
+
+void
+PointParam::param_hide_knot(bool hide) {
+ if (_knot_entity) {
+ bool update = false;
+ if (hide && _knot_entity->knot->flags & SP_KNOT_VISIBLE) {
+ update = true;
+ _knot_entity->knot->hide();
+ } else if(!hide && !(_knot_entity->knot->flags & SP_KNOT_VISIBLE)) {
+ update = true;
+ _knot_entity->knot->show();
+ }
+ if (update) {
+ _knot_entity->update_knot();
+ }
+ }
+}
+
+void
+PointParam::param_setValue(Geom::Point newpoint, bool write)
+{
+ *dynamic_cast<Geom::Point *>( this ) = newpoint;
+ if(write){
+ Inkscape::SVGOStringStream os;
+ os << newpoint;
+ gchar * str = g_strdup(os.str().c_str());
+ param_write_to_repr(str);
+ g_free(str);
+ }
+ if(_knot_entity && liveupdate){
+ _knot_entity->update_knot();
+ }
+}
+
+bool
+PointParam::param_readSVGValue(const gchar * strvalue)
+{
+ gchar ** strarray = g_strsplit(strvalue, ",", 2);
+ double newx, newy;
+ unsigned int success = sp_svg_number_read_d(strarray[0], &newx);
+ success += sp_svg_number_read_d(strarray[1], &newy);
+ g_strfreev (strarray);
+ if (success == 2) {
+ param_setValue( Geom::Point(newx, newy) );
+ return true;
+ }
+ return false;
+}
+
+Glib::ustring
+PointParam::param_getSVGValue() const
+{
+ Inkscape::SVGOStringStream os;
+ os << *dynamic_cast<Geom::Point const *>( this );
+ return os.str();
+}
+
+Glib::ustring
+PointParam::param_getDefaultSVGValue() const
+{
+ Inkscape::SVGOStringStream os;
+ os << defvalue;
+ return os.str();
+}
+
+void
+PointParam::param_transform_multiply(Geom::Affine const& postmul, bool /*set*/)
+{
+ param_setValue( (*this) * postmul, true);
+}
+
+Gtk::Widget *
+PointParam::param_newWidget()
+{
+ Inkscape::UI::Widget::RegisteredTransformedPoint * pointwdg = Gtk::manage(
+ new Inkscape::UI::Widget::RegisteredTransformedPoint( param_label,
+ param_tooltip,
+ param_key,
+ *param_wr,
+ param_effect->getRepr(),
+ param_effect->getSPDoc() ) );
+ Geom::Affine transf = SP_ACTIVE_DESKTOP->doc2dt();
+ pointwdg->setTransform(transf);
+ pointwdg->setValue( *this );
+ pointwdg->clearProgrammatically();
+ pointwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change point parameter"));
+ pointwdg->signal_button_release_event().connect(sigc::mem_fun (*this, &PointParam::on_button_release));
+
+ Gtk::HBox * hbox = Gtk::manage( new Gtk::HBox() );
+ static_cast<Gtk::HBox*>(hbox)->pack_start(*pointwdg, true, true);
+ static_cast<Gtk::HBox*>(hbox)->show_all_children();
+ return dynamic_cast<Gtk::Widget *> (hbox);
+}
+
+bool PointParam::on_button_release(GdkEventButton* button_event) {
+ param_effect->refresh_widgets = true;
+ return false;
+}
+
+void
+PointParam::set_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color)
+{
+ knot_shape = shape;
+ knot_mode = mode;
+ knot_color = color;
+}
+
+class PointParamKnotHolderEntity : public KnotHolderEntity {
+public:
+ PointParamKnotHolderEntity(PointParam *p) { this->pparam = p; }
+ ~PointParamKnotHolderEntity() override { this->pparam->_knot_entity = nullptr;}
+
+ void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) override;
+ Geom::Point knot_get() const override;
+ void knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state) override;
+ void knot_click(guint state) override;
+
+private:
+ PointParam *pparam;
+};
+
+void
+PointParamKnotHolderEntity::knot_set(Geom::Point const &p, Geom::Point const &origin, guint state)
+{
+ Geom::Point s = snap_knot_position(p, state);
+ if (state & GDK_CONTROL_MASK) {
+ Geom::Point A(origin[Geom::X],p[Geom::Y]);
+ Geom::Point B(p[Geom::X],origin[Geom::Y]);
+ double distanceA = Geom::distance(A,p);
+ double distanceB = Geom::distance(B,p);
+ if(distanceA > distanceB){
+ s = B;
+ } else {
+ s = A;
+ }
+ }
+ if(this->pparam->liveupdate){
+ pparam->param_setValue(s, true);
+ } else {
+ pparam->param_setValue(s);
+ }
+}
+
+void
+PointParamKnotHolderEntity::knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state)
+{
+ pparam->param_effect->refresh_widgets = true;
+ pparam->write_to_SVG();
+}
+
+Geom::Point
+PointParamKnotHolderEntity::knot_get() const
+{
+ return *pparam;
+}
+
+void
+PointParamKnotHolderEntity::knot_click(guint state)
+{
+ if (state & GDK_CONTROL_MASK) {
+ if (state & GDK_MOD1_MASK) {
+ this->pparam->param_set_default();
+ pparam->param_setValue(*pparam,true);
+ }
+ }
+}
+
+void
+PointParam::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item)
+{
+ _knot_entity = new PointParamKnotHolderEntity(this);
+ // TODO: can we ditch handleTip() etc. because we have access to handle_tip etc. itself???
+ _knot_entity->create(nullptr, item, knotholder, Inkscape::CTRL_TYPE_LPE, handleTip(), knot_shape, knot_mode,
+ knot_color);
+ knotholder->add(_knot_entity);
+}
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/point.h b/src/live_effects/parameter/point.h
new file mode 100644
index 0000000..26e0e32
--- /dev/null
+++ b/src/live_effects/parameter/point.h
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_POINT_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_POINT_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <glib.h>
+#include <2geom/point.h>
+#include "ui/widget/registered-widget.h"
+#include "live_effects/parameter/parameter.h"
+
+#include "knot-holder-entity.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class PointParamKnotHolderEntity;
+
+class PointParam : public Geom::Point, public Parameter {
+public:
+ PointParam( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect,
+ const gchar *handle_tip = nullptr,// tip for automatically associated on-canvas handle
+ Geom::Point default_value = Geom::Point(0,0),
+ bool live_update = true );
+ ~PointParam() override;
+
+ Gtk::Widget * param_newWidget() override;
+
+ bool param_readSVGValue(const gchar * strvalue) override;
+ Glib::ustring param_getSVGValue() const override;
+ Glib::ustring param_getDefaultSVGValue() const override;
+ inline const gchar *handleTip() const { return handle_tip ? handle_tip : param_tooltip.c_str(); }
+ void param_setValue(Geom::Point newpoint, bool write = false);
+ void param_set_default() override;
+ void param_hide_knot(bool hide);
+ Geom::Point param_get_default() const;
+ void param_set_liveupdate(bool live_update);
+ void param_update_default(Geom::Point default_point);
+
+ void param_update_default(const gchar * default_point) override;
+ void param_transform_multiply(Geom::Affine const & /*postmul*/, bool set) override;
+
+ void set_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color);
+
+ bool providesKnotHolderEntities() const override { return true; }
+ void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) override;
+ friend class PointParamKnotHolderEntity;
+private:
+ PointParam(const PointParam&) = delete;
+ PointParam& operator=(const PointParam&) = delete;
+ bool on_button_release(GdkEventButton* button_event);
+ Geom::Point defvalue;
+ bool liveupdate;
+ KnotHolderEntity * _knot_entity;
+ SPKnotShapeType knot_shape;
+ SPKnotModeType knot_mode;
+ guint32 knot_color;
+ gchar *handle_tip;
+};
+
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/powerstrokepointarray.cpp b/src/live_effects/parameter/powerstrokepointarray.cpp
new file mode 100644
index 0000000..e33166f
--- /dev/null
+++ b/src/live_effects/parameter/powerstrokepointarray.cpp
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "ui/dialog/lpe-powerstroke-properties.h"
+#include "live_effects/parameter/powerstrokepointarray.h"
+
+#include "knotholder.h"
+#include "live_effects/effect.h"
+#include "live_effects/lpe-powerstroke.h"
+
+
+#include <2geom/piecewise.h>
+#include <2geom/sbasis-geometric.h>
+
+#include "preferences.h" // for proportional stroke/path scaling behavior
+
+#include <glibmm/i18n.h>
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+PowerStrokePointArrayParam::PowerStrokePointArrayParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect)
+ : ArrayParam<Geom::Point>(label, tip, key, wr, effect, 0)
+{
+ knot_shape = SP_KNOT_SHAPE_DIAMOND;
+ knot_mode = SP_KNOT_MODE_XOR;
+ knot_color = 0xff88ff00;
+}
+
+PowerStrokePointArrayParam::~PowerStrokePointArrayParam()
+= default;
+
+Gtk::Widget *
+PowerStrokePointArrayParam::param_newWidget()
+{
+ return nullptr;
+}
+
+void PowerStrokePointArrayParam::param_transform_multiply(Geom::Affine const &postmul, bool /*set*/)
+{
+ // Check if proportional stroke-width scaling is on
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ bool transform_stroke = prefs ? prefs->getBool("/options/transform/stroke", true) : true;
+ if (transform_stroke) {
+ std::vector<Geom::Point> result;
+ result.reserve(_vector.size()); // reserve space for the points that will be added in the for loop
+ for (auto point_it : _vector)
+ {
+ // scale each width knot with the average scaling in X and Y
+ Geom::Coord const A = point_it[Geom::Y] * postmul.descrim();
+ result.emplace_back(point_it[Geom::X], A);
+ }
+ param_set_and_write_new_value(result);
+ }
+}
+
+/** call this method to recalculate the controlpoints such that they stay at the same location relative to the new path. Useful after adding/deleting nodes to the path.*/
+void
+PowerStrokePointArrayParam::recalculate_controlpoints_for_new_pwd2(Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
+{
+ Inkscape::LivePathEffect::LPEPowerStroke *lpe = dynamic_cast<Inkscape::LivePathEffect::LPEPowerStroke *>(param_effect);
+ if (lpe) {
+ if (last_pwd2.size() > pwd2_in.size()) {
+ double factor = (double)pwd2_in.size() / (double)last_pwd2.size();
+ for (auto & i : _vector) {
+ i[Geom::X] *= factor;
+ }
+ } else if (last_pwd2.size() < pwd2_in.size()) {
+ // Path has become longer: probably node added, maintain position of knots
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > normal = rot90(unitVector(derivative(pwd2_in)));
+ for (auto & i : _vector) {
+ Geom::Point pt = i;
+ Geom::Point position = last_pwd2.valueAt(pt[Geom::X]) + pt[Geom::Y] * last_pwd2_normal.valueAt(pt[Geom::X]);
+ double t = nearest_time(position, pwd2_in);
+ i[Geom::X] = t;
+ }
+ }
+ write_to_SVG();
+ }
+}
+
+/** call this method to recalculate the controlpoints when path is reversed.*/
+std::vector<Geom::Point>
+PowerStrokePointArrayParam::reverse_controlpoints(bool write)
+{
+ std::vector<Geom::Point> controlpoints;
+ if (!last_pwd2.empty()) {
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in_reverse = reverse(last_pwd2);
+ for (auto & i : _vector) {
+ Geom::Point control_pos = last_pwd2.valueAt(i[Geom::X]);
+ double new_pos = Geom::nearest_time(control_pos, pwd2_in_reverse);
+ controlpoints.emplace_back(new_pos,i[Geom::Y]);
+ i[Geom::X] = new_pos;
+ }
+ if (write) {
+ write_to_SVG();
+ _vector.clear();
+ _vector = controlpoints;
+ controlpoints.clear();
+ write_to_SVG();
+ return _vector;
+ }
+ }
+ return controlpoints;
+}
+
+float PowerStrokePointArrayParam::median_width()
+{
+ size_t size = _vector.size();
+ if (size > 0)
+ {
+ if (size % 2 == 0)
+ {
+ return (_vector[size / 2 - 1].y() + _vector[size / 2].y()) / 2;
+ }
+ else
+ {
+ return _vector[size / 2].y();
+ }
+ }
+ return 1;
+}
+
+void
+PowerStrokePointArrayParam::set_pwd2(Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in, Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_normal_in)
+{
+ last_pwd2 = pwd2_in;
+ last_pwd2_normal = pwd2_normal_in;
+}
+
+
+void
+PowerStrokePointArrayParam::set_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color)
+{
+ knot_shape = shape;
+ knot_mode = mode;
+ knot_color = color;
+}
+/*
+class PowerStrokePointArrayParamKnotHolderEntity : public KnotHolderEntity {
+public:
+ PowerStrokePointArrayParamKnotHolderEntity(PowerStrokePointArrayParam *p, unsigned int index);
+ virtual ~PowerStrokePointArrayParamKnotHolderEntity() {}
+
+ virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
+ virtual Geom::Point knot_get() const;
+ virtual void knot_click(guint state);
+
+ // Checks whether the index falls within the size of the parameter's vector
+ bool valid_index(unsigned int index) const {
+ return (_pparam->_vector.size() > index);
+ };
+
+private:
+ PowerStrokePointArrayParam *_pparam;
+ unsigned int _index;
+};*/
+
+PowerStrokePointArrayParamKnotHolderEntity::PowerStrokePointArrayParamKnotHolderEntity(PowerStrokePointArrayParam *p, unsigned int index)
+ : _pparam(p),
+ _index(index)
+{
+}
+
+void
+PowerStrokePointArrayParamKnotHolderEntity::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
+{
+ using namespace Geom;
+
+ if (!valid_index(_index)) {
+ return;
+ }
+ /// @todo how about item transforms???
+ Piecewise<D2<SBasis> > const & pwd2 = _pparam->get_pwd2();
+ Piecewise<D2<SBasis> > const & n = _pparam->get_pwd2_normal();
+
+ Geom::Point const s = snap_knot_position(p, state);
+ double t = nearest_time(s, pwd2);
+ double offset = dot(s - pwd2.valueAt(t), n.valueAt(t));
+ _pparam->_vector.at(_index) = Geom::Point(t, offset/_pparam->_scale_width);
+ if (_pparam->_vector.size() == 1 ) {
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setDouble("/live_effects/powerstroke/width", offset);
+ }
+ sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
+}
+
+Geom::Point
+PowerStrokePointArrayParamKnotHolderEntity::knot_get() const
+{
+ using namespace Geom;
+
+ if (!valid_index(_index)) {
+ return Geom::Point(infinity(), infinity());
+ }
+
+ Piecewise<D2<SBasis> > const & pwd2 = _pparam->get_pwd2();
+ Piecewise<D2<SBasis> > const & n = _pparam->get_pwd2_normal();
+
+ Point offset_point = _pparam->_vector.at(_index);
+ if (offset_point[X] > pwd2.size() || offset_point[X] < 0) {
+ g_warning("Broken powerstroke point at %f, I won't try to add that", offset_point[X]);
+ return Geom::Point(infinity(), infinity());
+ }
+ Point canvas_point = pwd2.valueAt(offset_point[X]) + (offset_point[Y] * _pparam->_scale_width) * n.valueAt(offset_point[X]);
+ return canvas_point;
+}
+
+void
+PowerStrokePointArrayParamKnotHolderEntity::knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state)
+{
+ _pparam->param_effect->refresh_widgets = true;
+ _pparam->write_to_SVG();
+}
+
+void PowerStrokePointArrayParamKnotHolderEntity::knot_set_offset(Geom::Point offset)
+{
+ _pparam->_vector.at(_index) = Geom::Point(offset.x(), offset.y() / 2);
+ this->parent_holder->knot_ungrabbed_handler(this->knot, 0);
+}
+
+void
+PowerStrokePointArrayParamKnotHolderEntity::knot_click(guint state)
+{
+ if (state & GDK_CONTROL_MASK) {
+ if (state & GDK_MOD1_MASK) {
+ // delete the clicked knot
+ std::vector<Geom::Point> & vec = _pparam->_vector;
+ if (vec.size() > 1) { //Force don't remove last knot
+ vec.erase(vec.begin() + _index);
+ _pparam->param_set_and_write_new_value(vec);
+ // shift knots down one index
+ for(auto & ent : parent_holder->entity) {
+ PowerStrokePointArrayParamKnotHolderEntity *pspa_ent = dynamic_cast<PowerStrokePointArrayParamKnotHolderEntity *>(ent);
+ if ( pspa_ent && pspa_ent->_pparam == this->_pparam ) { // check if the knotentity belongs to this powerstrokepointarray parameter
+ if (pspa_ent->_index > this->_index) {
+ --pspa_ent->_index;
+ }
+ }
+ };
+ // temporary hide, when knotholder were recreated it finally drop
+ this->knot->hide();
+ }
+ return;
+ } else {
+ // add a knot to XML
+ std::vector<Geom::Point> & vec = _pparam->_vector;
+ vec.insert(vec.begin() + _index, 1, vec.at(_index)); // this clicked knot is duplicated
+ _pparam->param_set_and_write_new_value(vec);
+
+ // shift knots up one index
+ for(auto & ent : parent_holder->entity) {
+ PowerStrokePointArrayParamKnotHolderEntity *pspa_ent = dynamic_cast<PowerStrokePointArrayParamKnotHolderEntity *>(ent);
+ if ( pspa_ent && pspa_ent->_pparam == this->_pparam ) { // check if the knotentity belongs to this powerstrokepointarray parameter
+ if (pspa_ent->_index > this->_index) {
+ ++pspa_ent->_index;
+ }
+ }
+ };
+ // add knot to knotholder
+ PowerStrokePointArrayParamKnotHolderEntity *e = new PowerStrokePointArrayParamKnotHolderEntity(_pparam, _index+1);
+ e->create(this->desktop, this->item, parent_holder, Inkscape::CTRL_TYPE_LPE,
+ _("<b>Stroke width control point</b>: drag to alter the stroke width. <b>Ctrl+click</b> adds a "
+ "control point, <b>Ctrl+Alt+click</b> deletes it, <b>Shift+click</b> launches width dialog."),
+ _pparam->knot_shape, _pparam->knot_mode, _pparam->knot_color);
+ parent_holder->add(e);
+ }
+ }
+ else if ((state & GDK_MOD1_MASK) || (state & GDK_SHIFT_MASK))
+ {
+ Geom::Point offset = Geom::Point(_pparam->_vector.at(_index).x(), _pparam->_vector.at(_index).y() * 2);
+ Inkscape::UI::Dialogs::PowerstrokePropertiesDialog::showDialog(this->desktop, offset, this);
+ }
+}
+
+void PowerStrokePointArrayParam::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item)
+{
+ for (unsigned int i = 0; i < _vector.size(); ++i) {
+ PowerStrokePointArrayParamKnotHolderEntity *e = new PowerStrokePointArrayParamKnotHolderEntity(this, i);
+ e->create(nullptr, item, knotholder, Inkscape::CTRL_TYPE_LPE,
+ _("<b>Stroke width control point</b>: drag to alter the stroke width. <b>Ctrl+click</b> adds a "
+ "control point, <b>Ctrl+Alt+click</b> deletes it, <b>Shift+click</b> launches width dialog."),
+ knot_shape, knot_mode, knot_color);
+ knotholder->add(e);
+ }
+}
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/powerstrokepointarray.h b/src/live_effects/parameter/powerstrokepointarray.h
new file mode 100644
index 0000000..36c4432
--- /dev/null
+++ b/src/live_effects/parameter/powerstrokepointarray.h
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_POWERSTROKE_POINT_ARRAY_H
+#define INKSCAPE_LIVEPATHEFFECT_POWERSTROKE_POINT_ARRAY_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <glib.h>
+#include <2geom/point.h>
+
+#include "live_effects/parameter/array.h"
+
+#include "knot-holder-entity.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class PowerStrokePointArrayParam : public ArrayParam<Geom::Point> {
+public:
+ PowerStrokePointArrayParam( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect);
+ ~PowerStrokePointArrayParam() override;
+
+ PowerStrokePointArrayParam(const PowerStrokePointArrayParam&) = delete;
+ PowerStrokePointArrayParam& operator=(const PowerStrokePointArrayParam&) = delete;
+
+ Gtk::Widget * param_newWidget() override;
+
+ void param_transform_multiply(Geom::Affine const& postmul, bool /*set*/) override;
+
+ void set_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color);
+
+
+ float median_width();
+
+ bool providesKnotHolderEntities() const override { return true; }
+ void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) override;
+ void param_update_default(const gchar * default_value) override{};
+
+ void set_pwd2(Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in, Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_normal_in);
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > const & get_pwd2() const { return last_pwd2; }
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > const & get_pwd2_normal() const { return last_pwd2_normal; }
+
+ void recalculate_controlpoints_for_new_pwd2(Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in);
+ std::vector<Geom::Point> reverse_controlpoints(bool write);
+ void set_scale_width(double scale_width){_scale_width = scale_width;};
+ double _scale_width;
+ friend class PowerStrokePointArrayParamKnotHolderEntity;
+
+private:
+ SPKnotShapeType knot_shape;
+ SPKnotModeType knot_mode;
+ guint32 knot_color;
+
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > last_pwd2;
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > last_pwd2_normal;
+};
+
+class PowerStrokePointArrayParamKnotHolderEntity : public KnotHolderEntity {
+public:
+ PowerStrokePointArrayParamKnotHolderEntity(PowerStrokePointArrayParam *p, unsigned int index);
+ ~PowerStrokePointArrayParamKnotHolderEntity() override = default;
+
+ void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) override;
+ void knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state) override;
+ Geom::Point knot_get() const override;
+ virtual void knot_set_offset(Geom::Point offset);
+ void knot_click(guint state) override;
+
+ /** Checks whether the index falls within the size of the parameter's vector */
+ bool valid_index(unsigned int index) const {
+ return (_pparam->_vector.size() > index);
+ };
+
+private:
+ PowerStrokePointArrayParam *_pparam;
+ unsigned int _index;
+};
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/random.cpp b/src/live_effects/parameter/random.cpp
new file mode 100644
index 0000000..c5346d1
--- /dev/null
+++ b/src/live_effects/parameter/random.cpp
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "ui/widget/registered-widget.h"
+#include "live_effects/parameter/random.h"
+#include "live_effects/effect.h"
+#include <glibmm/i18n.h>
+#include "svg/svg.h"
+#include "ui/widget/random.h"
+
+#include "svg/stringstream.h"
+
+#include "verbs.h"
+
+#define noLPERANDOMPARAM_DEBUG
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+
+RandomParam::RandomParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect, gdouble default_value, long default_seed)
+ : Parameter(label, tip, key, wr, effect)
+{
+ defvalue = default_value;
+ value = defvalue;
+ min = -Geom::infinity();
+ max = Geom::infinity();
+ integer = false;
+
+ defseed = default_seed;
+ startseed = defseed;
+ seed = startseed;
+}
+
+RandomParam::~RandomParam()
+= default;
+
+bool
+RandomParam::param_readSVGValue(const gchar * strvalue)
+{
+ double newval, newstartseed;
+ gchar** stringarray = g_strsplit (strvalue, ";", 2);
+ unsigned int success = sp_svg_number_read_d(stringarray[0], &newval);
+ if (success == 1) {
+ success += sp_svg_number_read_d(stringarray[1], &newstartseed);
+ if (success == 2) {
+ param_set_value(newval, static_cast<long>(newstartseed));
+ } else {
+ param_set_value(newval, defseed);
+ }
+ g_strfreev(stringarray);
+ return true;
+ }
+ g_strfreev(stringarray);
+ return false;
+}
+
+Glib::ustring
+RandomParam::param_getSVGValue() const
+{
+ Inkscape::SVGOStringStream os;
+ os << value << ';' << startseed;
+ return os.str();
+}
+
+Glib::ustring
+RandomParam::param_getDefaultSVGValue() const
+{
+ Inkscape::SVGOStringStream os;
+ os << defvalue << ';' << defseed;
+ return os.str();
+}
+
+void
+RandomParam::param_set_default()
+{
+ param_set_value(defvalue, defseed);
+}
+
+void
+RandomParam::param_update_default(gdouble default_value){
+ defvalue = default_value;
+}
+
+void
+RandomParam::param_update_default(const gchar * default_value){
+ double newval;
+ unsigned int success = sp_svg_number_read_d(default_value, &newval);
+ if (success == 1) {
+ param_update_default(newval);
+ }
+}
+
+void
+RandomParam::param_set_value(gdouble val, long newseed)
+{
+ value = val;
+ if (integer)
+ value = round(value);
+ if (value > max)
+ value = max;
+ if (value < min)
+ value = min;
+
+ startseed = setup_seed(newseed);
+ seed = startseed;
+}
+
+void
+RandomParam::param_set_range(gdouble min, gdouble max)
+{
+ this->min = min;
+ this->max = max;
+}
+
+void
+RandomParam::param_make_integer(bool yes)
+{
+ integer = yes;
+}
+
+void
+RandomParam::resetRandomizer()
+{
+ seed = startseed;
+}
+
+
+Gtk::Widget *
+RandomParam::param_newWidget()
+{
+ Inkscape::UI::Widget::RegisteredRandom* regrandom = Gtk::manage(
+ new Inkscape::UI::Widget::RegisteredRandom( param_label,
+ param_tooltip,
+ param_key,
+ *param_wr,
+ param_effect->getRepr(),
+ param_effect->getSPDoc() ) );
+
+ regrandom->setValue(value, startseed);
+ if (integer) {
+ regrandom->setDigits(0);
+ regrandom->setIncrements(1, 10);
+ }
+ regrandom->setRange(min, max);
+ regrandom->setProgrammatically = false;
+ regrandom->signal_button_release_event().connect(sigc::mem_fun (*this, &RandomParam::on_button_release));
+
+ regrandom->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change random parameter"));
+
+ return dynamic_cast<Gtk::Widget *> (regrandom);
+}
+
+bool RandomParam::on_button_release(GdkEventButton* button_event) {
+ param_effect->refresh_widgets = true;
+ return false;
+}
+
+RandomParam::operator gdouble()
+{
+ return rand() * value;
+};
+
+/* RNG stolen from /display/nr-filter-turbulence.cpp */
+#define RAND_m 2147483647 /* 2**31 - 1 */
+#define RAND_a 16807 /* 7**5; primitive root of m */
+#define RAND_q 127773 /* m / a */
+#define RAND_r 2836 /* m % a */
+#define BSize 0x100
+
+long
+RandomParam::setup_seed(long lSeed)
+{
+ if (lSeed <= 0) lSeed = -(lSeed % (RAND_m - 1)) + 1;
+ if (lSeed > RAND_m - 1) lSeed = RAND_m - 1;
+ return lSeed;
+}
+
+// generates random number between 0 and 1
+gdouble
+RandomParam::rand()
+{
+ long result;
+ result = RAND_a * (seed % RAND_q) - RAND_r * (seed / RAND_q);
+ if (result <= 0) result += RAND_m;
+ seed = result;
+
+ gdouble dresult = (gdouble)(result % BSize) / BSize;
+ return dresult;
+}
+
+
+} /* namespace LivePathEffect */
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/random.h b/src/live_effects/parameter/random.h
new file mode 100644
index 0000000..e0bb07a
--- /dev/null
+++ b/src/live_effects/parameter/random.h
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_RANDOM_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_RANDOM_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/parameter/parameter.h"
+#include <glibmm/ustring.h>
+#include <2geom/point.h>
+#include <2geom/path.h>
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class RandomParam : public Parameter {
+public:
+ RandomParam( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect,
+ gdouble default_value = 1.0,
+ long default_seed = 0);
+ ~RandomParam() override;
+
+ bool param_readSVGValue(const gchar * strvalue) override;
+ Glib::ustring param_getSVGValue() const override;
+ Glib::ustring param_getDefaultSVGValue() const override;
+ void param_set_default() override;
+
+ Gtk::Widget * param_newWidget() override;
+
+ void param_set_value(gdouble val, long newseed);
+ void param_make_integer(bool yes = true);
+ void param_set_range(gdouble min, gdouble max);
+ void param_update_default(gdouble default_value);
+ void param_update_default(const gchar * default_value) override;
+ void resetRandomizer();
+ operator gdouble();
+ inline gdouble get_value() { return value; } ;
+
+protected:
+ long startseed;
+ long seed;
+ long defseed;
+
+ gdouble value;
+ gdouble min;
+ gdouble max;
+ bool integer;
+ gdouble defvalue;
+
+private:
+ bool on_button_release(GdkEventButton* button_event);
+ long setup_seed(long);
+ gdouble rand();
+
+ RandomParam(const RandomParam&) = delete;
+ RandomParam& operator=(const RandomParam&) = delete;
+};
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/satellitesarray.cpp b/src/live_effects/parameter/satellitesarray.cpp
new file mode 100644
index 0000000..7f15298
--- /dev/null
+++ b/src/live_effects/parameter/satellitesarray.cpp
@@ -0,0 +1,575 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Author(s):
+ * Jabiertxo Arraiza Cenoz <jabier.arraiza@marker.es>
+ *
+ * Copyright (C) 2014 Author(s)
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/parameter/satellitesarray.h"
+#include "helper/geom.h"
+#include "knotholder.h"
+#include "live_effects/effect.h"
+#include "live_effects/lpe-fillet-chamfer.h"
+#include "ui/dialog/lpe-fillet-chamfer-properties.h"
+#include "ui/shape-editor.h"
+#include "ui/tools-switch.h"
+#include "ui/tools/node-tool.h"
+
+#include "inkscape.h"
+#include <preferences.h>
+// TODO due to internal breakage in glibmm headers,
+// this has to be included last.
+#include <glibmm/i18n.h>
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+SatellitesArrayParam::SatellitesArrayParam(const Glib::ustring &label,
+ const Glib::ustring &tip,
+ const Glib::ustring &key,
+ Inkscape::UI::Widget::Registry *wr,
+ Effect *effect)
+ : ArrayParam<std::vector<Satellite> >(label, tip, key, wr, effect, 0), _knoth(nullptr)
+{
+ _knot_shape = SP_KNOT_SHAPE_DIAMOND;
+ _knot_mode = SP_KNOT_MODE_XOR;
+ _knot_color = 0xAAFF8800;
+ _use_distance = false;
+ _global_knot_hide = false;
+ _current_zoom = 0;
+ _effectType = FILLET_CHAMFER;
+ _last_pathvector_satellites = nullptr;
+ param_widget_is_visible(false);
+}
+
+
+void SatellitesArrayParam::set_oncanvas_looks(SPKnotShapeType shape,
+ SPKnotModeType mode,
+ guint32 color)
+{
+ _knot_shape = shape;
+ _knot_mode = mode;
+ _knot_color = color;
+}
+
+void SatellitesArrayParam::setPathVectorSatellites(PathVectorSatellites *pathVectorSatellites, bool write)
+{
+ _last_pathvector_satellites = pathVectorSatellites;
+ if (write) {
+ param_set_and_write_new_value(_last_pathvector_satellites->getSatellites());
+ } else {
+ param_setValue(_last_pathvector_satellites->getSatellites());
+ }
+}
+
+void SatellitesArrayParam::reloadKnots()
+{
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (desktop && tools_isactive(desktop, TOOLS_NODES)) {
+ Inkscape::UI::Tools::NodeTool *nt = static_cast<Inkscape::UI::Tools::NodeTool *>(desktop->event_context);
+ if (nt) {
+ for (auto i = nt->_shape_editors.begin(); i != nt->_shape_editors.end(); ++i) {
+ Inkscape::UI::ShapeEditor *shape_editor = i->second;
+ if (shape_editor && shape_editor->lpeknotholder) {
+ SPItem *item = shape_editor->knotholder->item;
+ shape_editor->unset_item(true);
+ shape_editor->set_item(item);
+ }
+ }
+ }
+ }
+}
+void SatellitesArrayParam::setUseDistance(bool use_knot_distance)
+{
+ _use_distance = use_knot_distance;
+}
+
+void SatellitesArrayParam::setCurrentZoom(double current_zoom)
+{
+ _current_zoom = current_zoom;
+}
+
+void SatellitesArrayParam::setGlobalKnotHide(bool global_knot_hide)
+{
+ _global_knot_hide = global_knot_hide;
+}
+
+void SatellitesArrayParam::setEffectType(EffectType et)
+{
+ _effectType = et;
+}
+
+void SatellitesArrayParam::updateCanvasIndicators(bool mirror)
+{
+ if (!_last_pathvector_satellites) {
+ return;
+ }
+
+ if (!_hp.empty()) {
+ _hp.clear();
+ }
+ Geom::PathVector pathv = _last_pathvector_satellites->getPathVector();
+ if (pathv.empty()) {
+ return;
+ }
+ if (mirror == true) {
+ _hp.clear();
+ }
+ if (_effectType == FILLET_CHAMFER) {
+ for (size_t i = 0; i < _vector.size(); ++i) {
+ for (size_t j = 0; j < _vector[i].size(); ++j) {
+ if (_vector[i][j].hidden || //Ignore if hidden
+ (!_vector[i][j].has_mirror && mirror == true) || //Ignore if not have mirror and we are in mirror loop
+ _vector[i][j].amount == 0 || //no helper in 0 value
+ j >= count_path_nodes(pathv[i]) || //ignore last satellite in open paths with fillet chamfer effect
+ (!pathv[i].closed() && j == 0) || //ignore first satellites on open paths
+ count_path_nodes(pathv[i]) == 2)
+ {
+ continue;
+ }
+ Geom::Curve *curve_in = pathv[i][j].duplicate();
+ double pos = 0;
+ bool overflow = false;
+ double size_out = _vector[i][j].arcDistance(*curve_in);
+ double length_out = curve_in->length();
+ gint previous_index = j - 1; //Always are previous index because we skip first satellite on open paths
+ if (j == 0 && pathv[i].closed()) {
+ previous_index = count_path_nodes(pathv[i]) - 1;
+ }
+ if ( previous_index < 0 ) {
+ return;
+ }
+ double length_in = pathv.curveAt(previous_index).length();
+ if (mirror) {
+ curve_in = const_cast<Geom::Curve *>(&pathv.curveAt(previous_index));
+ pos = _vector[i][j].time(size_out, true, *curve_in);
+ if (length_out < size_out) {
+ overflow = true;
+ }
+ } else {
+ pos = _vector[i][j].time(*curve_in);
+ if (length_in < size_out) {
+ overflow = true;
+ }
+ }
+ if (pos <= 0 || pos >= 1) {
+ continue;
+ }
+ }
+ }
+ }
+ if (mirror) {
+ updateCanvasIndicators(false);
+ }
+}
+void SatellitesArrayParam::updateCanvasIndicators()
+{
+ updateCanvasIndicators(true);
+}
+
+void SatellitesArrayParam::addCanvasIndicators(
+ SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec)
+{
+ hp_vec.push_back(_hp);
+}
+
+void SatellitesArrayParam::param_transform_multiply(Geom::Affine const &postmul, bool /*set*/)
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+
+ if (prefs->getBool("/options/transform/rectcorners", true)) {
+ for (auto & i : _vector) {
+ for (auto & j : i) {
+ if (!j.is_time && j.amount > 0) {
+ j.amount = j.amount * ((postmul.expansionX() + postmul.expansionY()) / 2);
+ }
+ }
+ }
+ param_set_and_write_new_value(_vector);
+ }
+}
+
+void SatellitesArrayParam::addKnotHolderEntities(KnotHolder *knotholder,
+ SPItem *item,
+ bool mirror)
+{
+ if (!_last_pathvector_satellites) {
+ return;
+ }
+ size_t index = 0;
+ for (size_t i = 0; i < _vector.size(); ++i) {
+ for (size_t j = 0; j < _vector[i].size(); ++j) {
+ if (!_vector[i][j].has_mirror && mirror) {
+ continue;
+ }
+ SatelliteType type = _vector[i][j].satellite_type;
+ if (mirror && i == 0 && j == 0) {
+ index += _last_pathvector_satellites->getTotalSatellites();
+ }
+ using namespace Geom;
+ //If is for filletChamfer effect...
+ if (_effectType == FILLET_CHAMFER) {
+ const gchar *tip;
+ if (type == CHAMFER) {
+ tip = _("<b>Chamfer</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> reset");
+ } else if (type == INVERSE_CHAMFER) {
+ tip = _("<b>Inverse Chamfer</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> reset");
+ } else if (type == INVERSE_FILLET) {
+ tip = _("<b>Inverse Fillet</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> reset");
+ } else {
+ tip = _("<b>Fillet</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> reset");
+ }
+ FilletChamferKnotHolderEntity *e = new FilletChamferKnotHolderEntity(this, index);
+ e->create(nullptr, item, knotholder, Inkscape::CTRL_TYPE_LPE, _(tip), _knot_shape, _knot_mode,
+ _knot_color);
+ knotholder->add(e);
+ }
+ index++;
+ }
+ }
+ if (mirror) {
+ addKnotHolderEntities(knotholder, item, false);
+ }
+}
+
+void SatellitesArrayParam::updateAmmount(double amount)
+{
+ Geom::PathVector const pathv = _last_pathvector_satellites->getPathVector();
+ Satellites satellites = _last_pathvector_satellites->getSatellites();
+ for (size_t i = 0; i < satellites.size(); ++i) {
+ for (size_t j = 0; j < satellites[i].size(); ++j) {
+ Geom::Curve const &curve_in = pathv[i][j];
+ if (param_effect->isNodePointSelected(curve_in.initialPoint()) ){
+ _vector[i][j].amount = amount;
+ _vector[i][j].setSelected(true);
+ } else {
+ _vector[i][j].setSelected(false);
+ }
+ }
+ }
+}
+
+void SatellitesArrayParam::addKnotHolderEntities(KnotHolder *knotholder,
+ SPItem *item)
+{
+ _knoth = knotholder;
+ addKnotHolderEntities(knotholder, item, true);
+}
+
+FilletChamferKnotHolderEntity::FilletChamferKnotHolderEntity(
+ SatellitesArrayParam *p, size_t index)
+ : _pparam(p), _index(index) {}
+
+
+void FilletChamferKnotHolderEntity::knot_set(Geom::Point const &p,
+ Geom::Point const &/*origin*/,
+ guint state)
+{
+ if (!_pparam->_last_pathvector_satellites) {
+ return;
+ }
+ size_t total_satellites = _pparam->_last_pathvector_satellites->getTotalSatellites();
+ bool is_mirror = false;
+ size_t index = _index;
+ if (_index >= total_satellites) {
+ index = _index - total_satellites;
+ is_mirror = true;
+ }
+ std::pair<size_t, size_t> index_data = _pparam->_last_pathvector_satellites->getIndexData(index);
+ size_t satelite_index = index_data.first;
+ size_t subsatelite_index = index_data.second;
+
+ Geom::Point s = snap_knot_position(p, state);
+ if (!valid_index(satelite_index, subsatelite_index)) {
+ return;
+ }
+ Satellite satellite = _pparam->_vector[satelite_index][subsatelite_index];
+ Geom::PathVector pathv = _pparam->_last_pathvector_satellites->getPathVector();
+ if (satellite.hidden ||
+ (!pathv[satelite_index].closed() &&
+ (subsatelite_index == 0||//ignore first satellites on open paths
+ count_path_nodes(pathv[satelite_index]) - 1 == subsatelite_index))) //ignore last satellite in open paths with fillet chamfer effect
+ {
+ return;
+ }
+ gint previous_index = subsatelite_index - 1;
+ if (subsatelite_index == 0 && pathv[satelite_index].closed()) {
+ previous_index = count_path_nodes(pathv[satelite_index]) - 1;
+ }
+ if ( previous_index < 0 ) {
+ return;
+ }
+ Geom::Curve const &curve_in = pathv[satelite_index][previous_index];
+ double mirror_time = Geom::nearest_time(s, curve_in);
+ Geom::Point mirror = curve_in.pointAt(mirror_time);
+ double normal_time = Geom::nearest_time(s, pathv[satelite_index][subsatelite_index]);
+ Geom::Point normal = pathv[satelite_index][subsatelite_index].pointAt(normal_time);
+ double distance_mirror = Geom::distance(mirror,s);
+ double distance_normal = Geom::distance(normal,s);
+ if (Geom::are_near(s, pathv[satelite_index][subsatelite_index].initialPoint(), 1.5 / _pparam->_current_zoom)) {
+ satellite.amount = 0;
+ } else if (distance_mirror < distance_normal) {
+ double time_start = 0;
+ Satellites satellites = _pparam->_last_pathvector_satellites->getSatellites();
+ time_start = satellites[satelite_index][previous_index].time(curve_in);
+ if (time_start > mirror_time) {
+ mirror_time = time_start;
+ }
+ double size = arcLengthAt(mirror_time, curve_in);
+ double amount = curve_in.length() - size;
+ if (satellite.is_time) {
+ amount = timeAtArcLength(amount, pathv[satelite_index][subsatelite_index]);
+ }
+ satellite.amount = amount;
+ } else {
+ satellite.setPosition(s, pathv[satelite_index][subsatelite_index]);
+ }
+ Inkscape::LivePathEffect::LPEFilletChamfer *filletchamfer = dynamic_cast<Inkscape::LivePathEffect::LPEFilletChamfer *>(_pparam->param_effect);
+ filletchamfer->helperpath = true;
+ _pparam->updateAmmount(satellite.amount);
+ _pparam->_vector[satelite_index][subsatelite_index] = satellite;
+ sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
+}
+
+void
+FilletChamferKnotHolderEntity::knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state)
+{
+ Inkscape::LivePathEffect::LPEFilletChamfer *filletchamfer = dynamic_cast<Inkscape::LivePathEffect::LPEFilletChamfer *>(_pparam->param_effect);
+ if (filletchamfer) {
+ filletchamfer->refresh_widgets = true;
+ filletchamfer->helperpath = false;
+ filletchamfer->writeParamsToSVG();
+ sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
+ }
+}
+
+Geom::Point FilletChamferKnotHolderEntity::knot_get() const
+{
+ if (!_pparam->_last_pathvector_satellites || _pparam->_global_knot_hide) {
+ return Geom::Point(Geom::infinity(), Geom::infinity());
+ }
+ Geom::Point tmp_point;
+ size_t total_satellites = _pparam->_last_pathvector_satellites->getTotalSatellites();
+ bool is_mirror = false;
+ size_t index = _index;
+ if (_index >= total_satellites) {
+ index = _index - total_satellites;
+ is_mirror = true;
+ }
+ std::pair<size_t, size_t> index_data = _pparam->_last_pathvector_satellites->getIndexData(index);
+ size_t satelite_index = index_data.first;
+ size_t subsatelite_index = index_data.second;
+ if (!valid_index(satelite_index, subsatelite_index)) {
+ return Geom::Point(Geom::infinity(), Geom::infinity());
+ }
+ Satellite satellite = _pparam->_vector[satelite_index][subsatelite_index];
+ Geom::PathVector pathv = _pparam->_last_pathvector_satellites->getPathVector();
+ if (satellite.hidden ||
+ (!pathv[satelite_index].closed() && (subsatelite_index == 0 ||
+ subsatelite_index == count_path_nodes(pathv[satelite_index]) - 1))) //ignore first and last satellites on open paths
+ {
+ return Geom::Point(Geom::infinity(), Geom::infinity());
+ }
+ this->knot->show();
+ if (is_mirror) {
+ gint previous_index = subsatelite_index - 1;
+ if (subsatelite_index == 0 && pathv[satelite_index].closed()) {
+ previous_index = count_path_nodes(pathv[satelite_index]) - 1;
+ }
+ if ( previous_index < 0 ) {
+ return Geom::Point(Geom::infinity(), Geom::infinity());
+ }
+ Geom::Curve const &curve_in = pathv[satelite_index][previous_index];
+ double s = satellite.arcDistance(pathv[satelite_index][subsatelite_index]);
+ double t = satellite.time(s, true, curve_in);
+ if (t > 1) {
+ t = 1;
+ }
+ if (t < 0) {
+ t = 0;
+ }
+ double time_start = 0;
+ time_start = _pparam->_last_pathvector_satellites->getSatellites()[satelite_index][previous_index].time(curve_in);
+ if (time_start > t) {
+ t = time_start;
+ }
+ tmp_point = (curve_in).pointAt(t);
+ } else {
+ tmp_point = satellite.getPosition(pathv[satelite_index][subsatelite_index]);
+ }
+ Geom::Point const canvas_point = tmp_point;
+ return canvas_point;
+}
+
+void FilletChamferKnotHolderEntity::knot_click(guint state)
+{
+ if (!_pparam->_last_pathvector_satellites) {
+ return;
+ }
+ size_t total_satellites = _pparam->_last_pathvector_satellites->getTotalSatellites();
+ bool is_mirror = false;
+ size_t index = _index;
+ if (_index >= total_satellites) {
+ index = _index - total_satellites;
+ is_mirror = true;
+ }
+ std::pair<size_t, size_t> index_data = _pparam->_last_pathvector_satellites->getIndexData(index);
+ size_t satelite_index = index_data.first;
+ size_t subsatelite_index = index_data.second;
+ if (!valid_index(satelite_index, subsatelite_index)) {
+ return;
+ }
+ Geom::PathVector pathv = _pparam->_last_pathvector_satellites->getPathVector();
+ if (!pathv[satelite_index].closed() &&
+ (subsatelite_index == 0 ||
+ count_path_nodes(pathv[satelite_index]) - 1 == subsatelite_index)) //ignore last satellite in open paths with fillet chamfer effect
+ {
+ return;
+ }
+ if (state & GDK_CONTROL_MASK) {
+ if (state & GDK_MOD1_MASK) {
+ _pparam->_vector[satelite_index][subsatelite_index].amount = 0.0;
+ sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
+ } else {
+ using namespace Geom;
+ SatelliteType type = _pparam->_vector[satelite_index][subsatelite_index].satellite_type;
+ switch (type) {
+ case FILLET:
+ type = INVERSE_FILLET;
+ break;
+ case INVERSE_FILLET:
+ type = CHAMFER;
+ break;
+ case CHAMFER:
+ type = INVERSE_CHAMFER;
+ break;
+ default:
+ type = FILLET;
+ break;
+ }
+ _pparam->_vector[satelite_index][subsatelite_index].satellite_type = type;
+ sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
+ const gchar *tip;
+ if (type == CHAMFER) {
+ tip = _("<b>Chamfer</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> resets");
+ } else if (type == INVERSE_CHAMFER) {
+ tip = _("<b>Inverse Chamfer</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> resets");
+ } else if (type == INVERSE_FILLET) {
+ tip = _("<b>Inverse Fillet</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> resets");
+ } else {
+ tip = _("<b>Fillet</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> resets");
+ }
+ this->knot->tip = g_strdup(tip);
+ this->knot->show();
+ }
+ } else if (state & GDK_SHIFT_MASK) {
+ double amount = _pparam->_vector[satelite_index][subsatelite_index].amount;
+ gint previous_index = subsatelite_index - 1;
+ if (subsatelite_index == 0 && pathv[satelite_index].closed()) {
+ previous_index = count_path_nodes(pathv[satelite_index]) - 1;
+ }
+ if ( previous_index < 0 ) {
+ return;
+ }
+ if (!_pparam->_use_distance && !_pparam->_vector[satelite_index][subsatelite_index].is_time) {
+ amount = _pparam->_vector[satelite_index][subsatelite_index].lenToRad(
+ amount, pathv[satelite_index][previous_index], pathv[satelite_index][subsatelite_index],
+ _pparam->_vector[satelite_index][previous_index]);
+ }
+ bool aprox = false;
+ Geom::D2<Geom::SBasis> d2_out = pathv[satelite_index][subsatelite_index].toSBasis();
+ Geom::D2<Geom::SBasis> d2_in = pathv[satelite_index][previous_index].toSBasis();
+ aprox = ((d2_in)[0].degreesOfFreedom() != 2 ||
+ d2_out[0].degreesOfFreedom() != 2) &&
+ !_pparam->_use_distance
+ ? true
+ : false;
+ Inkscape::UI::Dialogs::FilletChamferPropertiesDialog::showDialog(
+ this->desktop, amount, this, _pparam->_use_distance, aprox,
+ _pparam->_vector[satelite_index][subsatelite_index]);
+ }
+}
+
+void FilletChamferKnotHolderEntity::knot_set_offset(Satellite satellite)
+{
+ if (!_pparam->_last_pathvector_satellites) {
+ return;
+ }
+ size_t total_satellites = _pparam->_last_pathvector_satellites->getTotalSatellites();
+ bool is_mirror = false;
+ size_t index = _index;
+ if (_index >= total_satellites) {
+ index = _index - total_satellites;
+ is_mirror = true;
+ }
+ std::pair<size_t, size_t> index_data = _pparam->_last_pathvector_satellites->getIndexData(index);
+ size_t satelite_index = index_data.first;
+ size_t subsatelite_index = index_data.second;
+ if (!valid_index(satelite_index, subsatelite_index)) {
+ return;
+ }
+ Geom::PathVector pathv = _pparam->_last_pathvector_satellites->getPathVector();
+ if (satellite.hidden ||
+ (!pathv[satelite_index].closed() &&
+ (subsatelite_index == 0 || count_path_nodes(pathv[satelite_index]) - 1 == subsatelite_index))) //ignore last satellite in open paths with fillet chamfer effect
+ {
+ return;
+ }
+ double amount = satellite.amount;
+ double max_amount = amount;
+ if (!_pparam->_use_distance && !satellite.is_time) {
+ gint previous_index = subsatelite_index - 1;
+ if (subsatelite_index == 0 && pathv[satelite_index].closed()) {
+ previous_index = count_path_nodes(pathv[satelite_index]) - 1;
+ }
+ if ( previous_index < 0 ) {
+ return;
+ }
+ amount = _pparam->_vector[satelite_index][subsatelite_index].radToLen(
+ amount, pathv[satelite_index][previous_index], pathv[satelite_index][subsatelite_index]);
+ if (max_amount > 0 && amount == 0) {
+ amount = _pparam->_vector[satelite_index][subsatelite_index].amount;
+ }
+ }
+ satellite.amount = amount;
+ _pparam->_vector[satelite_index][subsatelite_index] = satellite;
+ this->parent_holder->knot_ungrabbed_handler(this->knot, 0);
+ SPLPEItem *splpeitem = dynamic_cast<SPLPEItem *>(item);
+ if (splpeitem) {
+ sp_lpe_item_update_patheffect(splpeitem, false, false);
+ }
+}
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/satellitesarray.h b/src/live_effects/parameter/satellitesarray.h
new file mode 100644
index 0000000..c15b710
--- /dev/null
+++ b/src/live_effects/parameter/satellitesarray.h
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_SATELLITES_ARRAY_H
+#define INKSCAPE_LIVEPATHEFFECT_SATELLITES_ARRAY_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ * Copyright (C) Jabiertxo Arraiza Cenoz <jabier.arraiza@marker.es>
+ * Special thanks to Johan Engelen for the base of the effect -powerstroke-
+ * Also to ScislaC for pointing me to the idea
+ * Also su_v for his constructive feedback and time
+ * To Nathan Hurst for his review and help on refactor
+ * and finally to Liam P. White for his big help on coding,
+ * that saved me a lot of hours
+ *
+ *
+ * This parameter acts as a bridge from pathVectorSatellites class to serialize it as a LPE
+ * parameter
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/parameter/array.h"
+#include "live_effects/effect-enum.h"
+#include "helper/geom-pathvectorsatellites.h"
+#include "knot-holder-entity.h"
+#include <glib.h>
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class FilletChamferKnotHolderEntity;
+
+class SatellitesArrayParam : public ArrayParam<std::vector<Satellite> > {
+public:
+ SatellitesArrayParam(const Glib::ustring &label, const Glib::ustring &tip,
+ const Glib::ustring &key,
+ Inkscape::UI::Widget::Registry *wr, Effect *effect);
+
+ Gtk::Widget *param_newWidget() override
+ {
+ return nullptr;
+ }
+ void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) override;
+ virtual void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item, bool mirror);
+ void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec) override;
+ virtual void updateCanvasIndicators();
+ virtual void updateCanvasIndicators(bool mirror);
+ bool providesKnotHolderEntities() const override
+ {
+ return true;
+ }
+ void param_transform_multiply(Geom::Affine const &postmul, bool /*set*/) override;
+ void setUseDistance(bool use_knot_distance);
+ void setCurrentZoom(double current_zoom);
+ void setGlobalKnotHide(bool global_knot_hide);
+ void setEffectType(EffectType et);
+ void reloadKnots();
+ void updateAmmount(double amount);
+ void setPathVectorSatellites(PathVectorSatellites *pathVectorSatellites, bool write = true);
+ void set_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color);
+
+ friend class FilletChamferKnotHolderEntity;
+ friend class LPEFilletChamfer;
+
+protected:
+ KnotHolder *_knoth;
+
+private:
+ SatellitesArrayParam(const SatellitesArrayParam &) = delete;
+ SatellitesArrayParam &operator=(const SatellitesArrayParam &) = delete;
+
+ SPKnotShapeType _knot_shape;
+ SPKnotModeType _knot_mode;
+ guint32 _knot_color;
+ Geom::PathVector _hp;
+ bool _use_distance;
+ bool _global_knot_hide;
+ double _current_zoom;
+ EffectType _effectType;
+ PathVectorSatellites *_last_pathvector_satellites;
+
+};
+
+class FilletChamferKnotHolderEntity : public KnotHolderEntity {
+public:
+ FilletChamferKnotHolderEntity(SatellitesArrayParam *p, size_t index);
+ ~FilletChamferKnotHolderEntity() override
+ {
+ _pparam->_knoth = nullptr;
+ }
+ void knot_set(Geom::Point const &p, Geom::Point const &origin,
+ guint state) override;
+ Geom::Point knot_get() const override;
+ void knot_click(guint state) override;
+ void knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state) override;
+ void knot_set_offset(Satellite);
+ /** Checks whether the index falls within the size of the parameter's vector
+ */
+ bool valid_index(size_t index, size_t subindex) const
+ {
+ return (_pparam->_vector.size() > index && _pparam->_vector[index].size() > subindex);
+ };
+
+private:
+ SatellitesArrayParam *_pparam;
+ size_t _index;
+};
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/text.cpp b/src/live_effects/parameter/text.cpp
new file mode 100644
index 0000000..0a32bf7
--- /dev/null
+++ b/src/live_effects/parameter/text.cpp
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Maximilian Albert 2008 <maximilian.albert@gmail.com>
+ *
+ * Authors:
+ * Maximilian Albert
+ * Johan Engelen
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "ui/widget/registered-widget.h"
+#include <gtkmm/alignment.h>
+
+#include "live_effects/parameter/text.h"
+#include "live_effects/effect.h"
+#include "svg/svg.h"
+#include "svg/stringstream.h"
+#include "inkscape.h"
+#include "verbs.h"
+#include "display/canvas-text.h"
+#include <2geom/sbasis-geometric.h>
+
+#include <glibmm/i18n.h>
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+TextParam::TextParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect, const Glib::ustring default_value )
+ : Parameter(label, tip, key, wr, effect),
+ value(default_value),
+ defvalue(default_value),
+ _hide_canvas_text(false)
+{
+ if (SPDesktop *desktop = SP_ACTIVE_DESKTOP) { // FIXME: we shouldn't use this!
+ canvas_text = (SPCanvasText *) sp_canvastext_new(desktop->getTempGroup(), desktop, Geom::Point(0,0), "");
+ sp_canvastext_set_text (canvas_text, default_value.c_str());
+ sp_canvastext_set_coords (canvas_text, 0, 0);
+ } else {
+ _hide_canvas_text = true;
+ }
+}
+
+void
+TextParam::param_set_default()
+{
+ param_setValue(defvalue);
+}
+
+void
+TextParam::param_update_default(const gchar * default_value)
+{
+ defvalue = (Glib::ustring)default_value;
+}
+
+void
+TextParam::param_hide_canvas_text()
+{
+ if (!_hide_canvas_text) {
+ sp_canvastext_set_text(canvas_text, " ");
+ _hide_canvas_text = true;
+ }
+}
+
+void
+TextParam::setPos(Geom::Point pos)
+{
+ if (!_hide_canvas_text) {
+ sp_canvastext_set_coords (canvas_text, pos);
+ }
+}
+
+void
+TextParam::setPosAndAnchor(const Geom::Piecewise<Geom::D2<Geom::SBasis> > &pwd2,
+ const double t, const double length, bool /*use_curvature*/)
+{
+ using namespace Geom;
+
+ Piecewise<D2<SBasis> > pwd2_reparam = arc_length_parametrization(pwd2, 2 , 0.1);
+ double t_reparam = pwd2_reparam.cuts.back() * t;
+ Point pos = pwd2_reparam.valueAt(t_reparam);
+ Point dir = unit_vector(derivative(pwd2_reparam).valueAt(t_reparam));
+ Point n = -rot90(dir);
+ double angle = Geom::angle_between(dir, Point(1,0));
+ if (!_hide_canvas_text) {
+ sp_canvastext_set_coords(canvas_text, pos + n * length);
+ sp_canvastext_set_anchor_manually(canvas_text, std::sin(angle), -std::cos(angle));
+ }
+}
+
+void
+TextParam::setAnchor(double x_value, double y_value)
+{
+ anchor_x = x_value;
+ anchor_y = y_value;
+ if (!_hide_canvas_text) {
+ sp_canvastext_set_anchor_manually (canvas_text, anchor_x, anchor_y);
+ }
+}
+
+bool
+TextParam::param_readSVGValue(const gchar * strvalue)
+{
+ param_setValue(strvalue);
+ return true;
+}
+
+Glib::ustring
+TextParam::param_getSVGValue() const
+{
+ return value;
+}
+
+Glib::ustring
+TextParam::param_getDefaultSVGValue() const
+{
+ return defvalue;
+}
+
+void
+TextParam::setTextParam(Inkscape::UI::Widget::RegisteredText *rsu)
+{
+ Glib::ustring str(rsu->getText());
+ param_setValue(str);
+ write_to_SVG();
+}
+
+Gtk::Widget *
+TextParam::param_newWidget()
+{
+ Inkscape::UI::Widget::RegisteredText *rsu = Gtk::manage(new Inkscape::UI::Widget::RegisteredText(
+ param_label, param_tooltip, param_key, *param_wr, param_effect->getRepr(), param_effect->getSPDoc()));
+ rsu->setText(value);
+ rsu->setProgrammatically = false;
+ rsu->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change text parameter"));
+ Gtk::Box *text_container = Gtk::manage(new Gtk::Box());
+ Gtk::Button *set = Gtk::manage(new Gtk::Button(Glib::ustring("✔")));
+ set->signal_clicked()
+ .connect(sigc::bind<Inkscape::UI::Widget::RegisteredText *>(sigc::mem_fun(*this, &TextParam::setTextParam),rsu));
+ text_container->pack_start(*rsu, false, false, 2);
+ text_container->pack_start(*set, false, false, 2);
+ Gtk::Widget *return_widg = dynamic_cast<Gtk::Widget *> (text_container);
+ return_widg->set_halign(Gtk::ALIGN_END);
+ return return_widg;
+}
+
+void
+TextParam::param_setValue(const Glib::ustring newvalue)
+{
+ if (value != newvalue) {
+ param_effect->refresh_widgets = true;
+ }
+ value = newvalue;
+ if (!_hide_canvas_text) {
+ sp_canvastext_set_text (canvas_text, newvalue.c_str());
+ }
+}
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/text.h b/src/live_effects/parameter/text.h
new file mode 100644
index 0000000..2d25c61
--- /dev/null
+++ b/src/live_effects/parameter/text.h
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_TEXT_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_TEXT_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Authors:
+ * Maximilian Albert
+ * Johan Engelen
+ *
+ * Copyright (C) Maximilian Albert 2008 <maximilian.albert@gmail.com>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <glib.h>
+
+#include "display/canvas-bpath.h"
+#include "live_effects/parameter/parameter.h"
+
+struct SPCanvasText;
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class TextParam : public Parameter {
+public:
+ TextParam( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect,
+ const Glib::ustring default_value = "");
+ ~TextParam() override = default;
+
+ Gtk::Widget * param_newWidget() override;
+
+ bool param_readSVGValue(const gchar * strvalue) override;
+ Glib::ustring param_getSVGValue() const override;
+ Glib::ustring param_getDefaultSVGValue() const override;
+
+ void param_setValue(Glib::ustring newvalue);
+ void param_hide_canvas_text();
+ void setTextParam(Inkscape::UI::Widget::RegisteredText *rsu);
+ void param_set_default() override;
+ void param_update_default(const gchar * default_value) override;
+ void setPos(Geom::Point pos);
+ void setPosAndAnchor(const Geom::Piecewise<Geom::D2<Geom::SBasis> > &pwd2,
+ const double t, const double length, bool use_curvature = false);
+ void setAnchor(double x_value, double y_value);
+
+ const Glib::ustring get_value() const { return value; };
+
+private:
+ TextParam(const TextParam&) = delete;
+ TextParam& operator=(const TextParam&) = delete;
+ double anchor_x;
+ double anchor_y;
+ bool _hide_canvas_text;
+ Glib::ustring value;
+ Glib::ustring defvalue;
+
+ SPCanvasText *canvas_text;
+};
+
+/*
+ * This parameter does not display a widget in the LPE dialog; LPEs can use it to display on-canvas
+ * text that should not be settable by the user. Note that since no widget is provided, the
+ * parameter must be initialized differently than usual (only with a pointer to the parent effect;
+ * no label, no tooltip, etc.).
+ */
+class TextParamInternal : public TextParam {
+public:
+ TextParamInternal(Effect* effect) :
+ TextParam("", "", "", nullptr, effect) {}
+
+ Gtk::Widget * param_newWidget() override { return nullptr; }
+};
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/togglebutton.cpp b/src/live_effects/parameter/togglebutton.cpp
new file mode 100644
index 0000000..afabefc
--- /dev/null
+++ b/src/live_effects/parameter/togglebutton.cpp
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ * Copyright (C) Jabiertxo Arraiza Cenoz 2014 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "ui/widget/registered-widget.h"
+#include <glibmm/i18n.h>
+
+#include <utility>
+
+#include "helper-fns.h"
+#include "inkscape.h"
+#include "live_effects/effect.h"
+#include "live_effects/parameter/togglebutton.h"
+#include "selection.h"
+#include "svg/stringstream.h"
+#include "svg/svg.h"
+#include "ui/icon-loader.h"
+#include "verbs.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+ToggleButtonParam::ToggleButtonParam(const Glib::ustring &label, const Glib::ustring &tip, const Glib::ustring &key,
+ Inkscape::UI::Widget::Registry *wr, Effect *effect, bool default_value,
+ Glib::ustring inactive_label, char const *_icon_active, char const *_icon_inactive,
+ Gtk::BuiltinIconSize _icon_size)
+ : Parameter(label, tip, key, wr, effect)
+ , value(default_value)
+ , defvalue(default_value)
+ , inactive_label(std::move(inactive_label))
+ , _icon_active(_icon_active)
+ , _icon_inactive(_icon_inactive)
+ , _icon_size(_icon_size)
+{
+ checkwdg = nullptr;
+}
+
+ToggleButtonParam::~ToggleButtonParam()
+{
+ if (_toggled_connection.connected()) {
+ _toggled_connection.disconnect();
+ }
+}
+
+void
+ToggleButtonParam::param_set_default()
+{
+ param_setValue(defvalue);
+}
+
+bool
+ToggleButtonParam::param_readSVGValue(const gchar * strvalue)
+{
+ param_setValue(helperfns_read_bool(strvalue, defvalue));
+ return true; // not correct: if value is unacceptable, should return false!
+}
+
+Glib::ustring
+ToggleButtonParam::param_getSVGValue() const
+{
+ return value ? "true" : "false";
+}
+
+Glib::ustring
+ToggleButtonParam::param_getDefaultSVGValue() const
+{
+ return defvalue ? "true" : "false";
+}
+
+void
+ToggleButtonParam::param_update_default(bool default_value)
+{
+ defvalue = default_value;
+}
+
+void
+ToggleButtonParam::param_update_default(const gchar * default_value)
+{
+ param_update_default(helperfns_read_bool(default_value, defvalue));
+}
+
+Gtk::Widget *
+ToggleButtonParam::param_newWidget()
+{
+ if (_toggled_connection.connected()) {
+ _toggled_connection.disconnect();
+ }
+
+ checkwdg = Gtk::manage(
+ new Inkscape::UI::Widget::RegisteredToggleButton(param_label,
+ param_tooltip,
+ param_key,
+ *param_wr,
+ false,
+ param_effect->getRepr(),
+ param_effect->getSPDoc()) );
+ auto box_button = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL);
+ box_button->set_homogeneous(false);
+ Gtk::Label *label = new Gtk::Label("");
+ if (!param_label.empty()) {
+ if (value || inactive_label.empty()) {
+ label->set_text(param_label.c_str());
+ } else {
+ label->set_text(inactive_label.c_str());
+ }
+ }
+ label->show();
+ if (_icon_active) {
+ if (!_icon_inactive) {
+ _icon_inactive = _icon_active;
+ }
+ box_button->show();
+ Gtk::Widget *icon_button = nullptr;
+ if (!value) {
+ icon_button = sp_get_icon_image(_icon_inactive, _icon_size);
+ } else {
+ icon_button = sp_get_icon_image(_icon_active, _icon_size);
+ }
+ icon_button->show();
+ box_button->pack_start(*icon_button, false, false, 1);
+ if (!param_label.empty()) {
+ box_button->pack_start(*label, false, false, 1);
+ }
+ } else {
+ box_button->pack_start(*label, false, false, 1);
+ }
+
+ checkwdg->add(*Gtk::manage(box_button));
+ checkwdg->setActive(value);
+ checkwdg->setProgrammatically = false;
+ checkwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change togglebutton parameter"));
+
+ _toggled_connection = checkwdg->signal_toggled().connect(sigc::mem_fun(*this, &ToggleButtonParam::toggled));
+ return checkwdg;
+}
+
+void
+ToggleButtonParam::refresh_button()
+{
+ if (!_toggled_connection.connected()) {
+ return;
+ }
+
+ if(!checkwdg){
+ return;
+ }
+ Gtk::Container *box_button = dynamic_cast<Gtk::Container *>(checkwdg->get_child());
+ if(!box_button){
+ return;
+ }
+ std::vector<Gtk::Widget *> children = box_button->get_children();
+ if (!param_label.empty()) {
+ Gtk::Label *lab = dynamic_cast<Gtk::Label*>(children[children.size()-1]);
+ if (!lab) return;
+ if(value || inactive_label.empty()){
+ lab->set_text(param_label.c_str());
+ }else{
+ lab->set_text(inactive_label.c_str());
+ }
+ }
+ if ( _icon_active ) {
+ Gdk::Pixbuf *icon_pixbuf = nullptr;
+ Gtk::Widget *im = dynamic_cast<Gtk::Image *>(children[0]);
+ if (!im) return;
+ if (!value) {
+ im = sp_get_icon_image(_icon_inactive, _icon_size);
+ } else {
+ im = sp_get_icon_image(_icon_active, _icon_size);
+ }
+ }
+}
+
+void
+ToggleButtonParam::param_setValue(bool newvalue)
+{
+ if (value != newvalue) {
+ param_effect->refresh_widgets = true;
+ }
+ value = newvalue;
+ refresh_button();
+}
+
+void
+ToggleButtonParam::toggled() {
+ if (SP_ACTIVE_DESKTOP) {
+ Inkscape::Selection *selection = SP_ACTIVE_DESKTOP->getSelection();
+ selection->emitModified();
+ }
+ _signal_toggled.emit();
+}
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/togglebutton.h b/src/live_effects/parameter/togglebutton.h
new file mode 100644
index 0000000..1c44ffb
--- /dev/null
+++ b/src/live_effects/parameter/togglebutton.h
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_TOGGLEBUTTON_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_TOGGLEBUTTON_H
+
+/*
+ * Copyright (C) Jabiertxo Arraiza Cenoz 2014
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <glib.h>
+#include <sigc++/connection.h>
+#include <sigc++/signal.h>
+
+#include "live_effects/parameter/parameter.h"
+#include "ui/widget/registered-widget.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+/**
+ * class ToggleButtonParam:
+ * represents a Gtk::ToggleButton as a Live Path Effect parameter
+ */
+class ToggleButtonParam : public Parameter {
+public:
+ ToggleButtonParam(const Glib::ustring &label, const Glib::ustring &tip, const Glib::ustring &key,
+ Inkscape::UI::Widget::Registry *wr, Effect *effect, bool default_value = false,
+ Glib::ustring inactive_label = "", char const *icon_active = nullptr,
+ char const *icon_inactive = nullptr, Gtk::BuiltinIconSize icon_size = Gtk::ICON_SIZE_SMALL_TOOLBAR);
+ ~ToggleButtonParam() override;
+ ToggleButtonParam(const ToggleButtonParam &) = delete;
+ ToggleButtonParam &operator=(const ToggleButtonParam &) = delete;
+
+ Gtk::Widget *param_newWidget() override;
+
+ bool param_readSVGValue(const gchar *strvalue) override;
+ Glib::ustring param_getSVGValue() const override;
+ Glib::ustring param_getDefaultSVGValue() const override;
+
+ void param_setValue(bool newvalue);
+ void param_set_default() override;
+
+ bool get_value() const { return value; };
+
+ inline operator bool() const { return value; };
+
+ sigc::signal<void> &signal_toggled() { return _signal_toggled; }
+ virtual void toggled();
+ void param_update_default(bool default_value);
+ void param_update_default(const gchar *default_value) override;
+
+private:
+ void refresh_button();
+ bool value;
+ bool defvalue;
+ const Glib::ustring inactive_label;
+ const char * _icon_active;
+ const char * _icon_inactive;
+ Gtk::BuiltinIconSize _icon_size;
+ Inkscape::UI::Widget::RegisteredToggleButton * checkwdg;
+
+ sigc::signal<void> _signal_toggled;
+ sigc::connection _toggled_connection;
+};
+
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/transformedpoint.cpp b/src/live_effects/parameter/transformedpoint.cpp
new file mode 100644
index 0000000..694f946
--- /dev/null
+++ b/src/live_effects/parameter/transformedpoint.cpp
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "ui/widget/registered-widget.h"
+#include "live_effects/parameter/transformedpoint.h"
+
+#include "knotholder.h"
+#include "svg/svg.h"
+#include "svg/stringstream.h"
+
+#include "live_effects/effect.h"
+#include "desktop.h"
+#include "verbs.h"
+
+#include <glibmm/i18n.h>
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+TransformedPointParam::TransformedPointParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect, Geom::Point default_vector,
+ bool dontTransform)
+ : Parameter(label, tip, key, wr, effect),
+ defvalue(default_vector),
+ origin(0.,0.),
+ vector(default_vector),
+ noTransform(dontTransform)
+{
+ vec_knot_shape = SP_KNOT_SHAPE_DIAMOND;
+ vec_knot_mode = SP_KNOT_MODE_XOR;
+ vec_knot_color = 0xffffb500;
+}
+
+TransformedPointParam::~TransformedPointParam()
+= default;
+
+void
+TransformedPointParam::param_set_default()
+{
+ setOrigin(Geom::Point(0.,0.));
+ setVector(defvalue);
+}
+
+bool
+TransformedPointParam::param_readSVGValue(const gchar * strvalue)
+{
+ gchar ** strarray = g_strsplit(strvalue, ",", 4);
+ if (!strarray) {
+ return false;
+ }
+ double val[4];
+ unsigned int i = 0;
+ while (i < 4 && strarray[i]) {
+ if (sp_svg_number_read_d(strarray[i], &val[i]) != 0) {
+ i++;
+ } else {
+ break;
+ }
+ }
+ g_strfreev (strarray);
+ if (i == 4) {
+ setOrigin( Geom::Point(val[0], val[1]) );
+ setVector( Geom::Point(val[2], val[3]) );
+ return true;
+ }
+ return false;
+}
+
+Glib::ustring
+TransformedPointParam::param_getSVGValue() const
+{
+ Inkscape::SVGOStringStream os;
+ os << origin << " , " << vector;
+ return os.str();
+}
+
+Glib::ustring
+TransformedPointParam::param_getDefaultSVGValue() const
+{
+ Inkscape::SVGOStringStream os;
+ os << defvalue;
+ return os.str();
+}
+
+void
+TransformedPointParam::param_update_default(Geom::Point default_point)
+{
+ defvalue = default_point;
+}
+
+void
+TransformedPointParam::param_update_default(const gchar * default_point)
+{
+ gchar ** strarray = g_strsplit(default_point, ",", 2);
+ double newx, newy;
+ unsigned int success = sp_svg_number_read_d(strarray[0], &newx);
+ success += sp_svg_number_read_d(strarray[1], &newy);
+ g_strfreev (strarray);
+ if (success == 2) {
+ param_update_default( Geom::Point(newx, newy) );
+ }
+}
+
+Gtk::Widget *
+TransformedPointParam::param_newWidget()
+{
+ Inkscape::UI::Widget::RegisteredVector * pointwdg = Gtk::manage(
+ new Inkscape::UI::Widget::RegisteredVector( param_label,
+ param_tooltip,
+ param_key,
+ *param_wr,
+ param_effect->getRepr(),
+ param_effect->getSPDoc() ) );
+ pointwdg->setPolarCoords();
+ pointwdg->setValue( vector, origin );
+ pointwdg->clearProgrammatically();
+ pointwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change vector parameter"));
+
+ Gtk::HBox * hbox = Gtk::manage( new Gtk::HBox() );
+ static_cast<Gtk::HBox*>(hbox)->pack_start(*pointwdg, true, true);
+ static_cast<Gtk::HBox*>(hbox)->show_all_children();
+
+ return dynamic_cast<Gtk::Widget *> (hbox);
+}
+
+void
+TransformedPointParam::set_and_write_new_values(Geom::Point const &new_origin, Geom::Point const &new_vector)
+{
+ setValues(new_origin, new_vector);
+ param_write_to_repr(param_getSVGValue().c_str());
+}
+
+void
+TransformedPointParam::param_transform_multiply(Geom::Affine const& postmul, bool /*set*/)
+{
+ if (!noTransform) {
+ set_and_write_new_values( origin * postmul, vector * postmul.withoutTranslation() );
+ }
+}
+
+
+void
+TransformedPointParam::set_vector_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color)
+{
+ vec_knot_shape = shape;
+ vec_knot_mode = mode;
+ vec_knot_color = color;
+}
+
+void
+TransformedPointParam::set_oncanvas_color(guint32 color)
+{
+ vec_knot_color = color;
+}
+
+class TransformedPointParamKnotHolderEntity_Vector : public KnotHolderEntity {
+public:
+ TransformedPointParamKnotHolderEntity_Vector(TransformedPointParam *p) : param(p) { }
+ ~TransformedPointParamKnotHolderEntity_Vector() override = default;
+
+ void knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint /*state*/) override {
+ Geom::Point const s = p - param->origin;
+ /// @todo implement angle snapping when holding CTRL
+ param->setVector(s);
+ param->set_and_write_new_values(param->origin, param->vector);
+ sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
+ };
+ void knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state) override
+ {
+ param->param_effect->refresh_widgets = true;
+ param->write_to_SVG();
+ };
+ Geom::Point knot_get() const override{
+ return param->origin + param->vector;
+ };
+ void knot_click(guint /*state*/) override{
+ g_print ("This is the vector handle associated to parameter '%s'\n", param->param_key.c_str());
+ };
+
+private:
+ TransformedPointParam *param;
+};
+
+void
+TransformedPointParam::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item)
+{
+ TransformedPointParamKnotHolderEntity_Vector *vector_e = new TransformedPointParamKnotHolderEntity_Vector(this);
+ vector_e->create(desktop, item, knotholder, Inkscape::CTRL_TYPE_LPE, handleTip(), vec_knot_shape, vec_knot_mode,
+ vec_knot_color);
+ knotholder->add(vector_e);
+}
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/transformedpoint.h b/src/live_effects/parameter/transformedpoint.h
new file mode 100644
index 0000000..3fd6989
--- /dev/null
+++ b/src/live_effects/parameter/transformedpoint.h
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_TRANSFORMED_POINT_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_TRANSFORMED_POINT_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <glib.h>
+#include <2geom/point.h>
+
+#include "live_effects/parameter/parameter.h"
+
+#include "knot-holder-entity.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+
+class TransformedPointParam : public Parameter {
+public:
+ TransformedPointParam( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect,
+ Geom::Point default_vector = Geom::Point(1,0),
+ bool dontTransform = false);
+ ~TransformedPointParam() override;
+
+ Gtk::Widget * param_newWidget() override;
+ inline const gchar *handleTip() const { return param_tooltip.c_str(); }
+
+ bool param_readSVGValue(const gchar * strvalue) override;
+ Glib::ustring param_getSVGValue() const override;
+ Glib::ustring param_getDefaultSVGValue() const override;
+
+ Geom::Point getVector() const { return vector; };
+ Geom::Point getOrigin() const { return origin; };
+ void setValues(Geom::Point const &new_origin, Geom::Point const &new_vector) { setVector(new_vector); setOrigin(new_origin); };
+ void setVector(Geom::Point const &new_vector) { vector = new_vector; };
+ void setOrigin(Geom::Point const &new_origin) { origin = new_origin; };
+ void param_set_default() override;
+
+ void set_and_write_new_values(Geom::Point const &new_origin, Geom::Point const &new_vector);
+
+ void param_transform_multiply(Geom::Affine const &postmul, bool set) override;
+
+ void set_vector_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color);
+ void set_oncanvas_color(guint32 color);
+ Geom::Point param_get_default() { return defvalue; }
+ void param_update_default(Geom::Point default_point);
+ void param_update_default(const gchar * default_point) override;
+ bool providesKnotHolderEntities() const override { return true; }
+ virtual void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item);
+
+private:
+ TransformedPointParam(const TransformedPointParam&) = delete;
+ TransformedPointParam& operator=(const TransformedPointParam&) = delete;
+
+ Geom::Point defvalue;
+
+ Geom::Point origin;
+ Geom::Point vector;
+
+ bool noTransform;
+
+ /// The looks of the vector and origin knots oncanvas
+ SPKnotShapeType vec_knot_shape;
+ SPKnotModeType vec_knot_mode;
+ guint32 vec_knot_color;
+
+ friend class TransformedPointParamKnotHolderEntity_Vector;
+};
+
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/unit.cpp b/src/live_effects/parameter/unit.cpp
new file mode 100644
index 0000000..65a939b
--- /dev/null
+++ b/src/live_effects/parameter/unit.cpp
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Maximilian Albert 2008 <maximilian.albert@gmail.com>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "ui/widget/registered-widget.h"
+#include <glibmm/i18n.h>
+
+#include "live_effects/parameter/unit.h"
+#include "live_effects/effect.h"
+#include "verbs.h"
+#include "util/units.h"
+
+using Inkscape::Util::unit_table;
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+
+UnitParam::UnitParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect, Glib::ustring default_unit)
+ : Parameter(label, tip, key, wr, effect)
+{
+ defunit = unit_table.getUnit(default_unit);
+ unit = defunit;
+}
+
+UnitParam::~UnitParam()
+= default;
+
+bool
+UnitParam::param_readSVGValue(const gchar * strvalue)
+{
+ if (strvalue) {
+ param_set_value(*unit_table.getUnit(strvalue));
+ return true;
+ }
+ return false;
+}
+
+Glib::ustring
+UnitParam::param_getSVGValue() const
+{
+ return unit->abbr;
+}
+
+Glib::ustring
+UnitParam::param_getDefaultSVGValue() const
+{
+ return defunit->abbr;
+}
+
+void
+UnitParam::param_set_default()
+{
+ param_set_value(*defunit);
+}
+
+void
+UnitParam::param_update_default(const gchar * default_unit)
+{
+ defunit = unit_table.getUnit((Glib::ustring)default_unit);
+}
+
+void
+UnitParam::param_set_value(Inkscape::Util::Unit const &val)
+{
+ param_effect->refresh_widgets = true;
+ unit = new Inkscape::Util::Unit(val);
+}
+
+const gchar *
+UnitParam::get_abbreviation() const
+{
+ return unit->abbr.c_str();
+}
+
+Gtk::Widget *
+UnitParam::param_newWidget()
+{
+ Inkscape::UI::Widget::RegisteredUnitMenu* unit_menu = Gtk::manage(
+ new Inkscape::UI::Widget::RegisteredUnitMenu(param_label,
+ param_key,
+ *param_wr,
+ param_effect->getRepr(),
+ param_effect->getSPDoc()));
+
+ unit_menu->setUnit(unit->abbr);
+ unit_menu->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change unit parameter"));
+
+ return dynamic_cast<Gtk::Widget *> (unit_menu);
+}
+
+} /* namespace LivePathEffect */
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/unit.h b/src/live_effects/parameter/unit.h
new file mode 100644
index 0000000..b87d29a
--- /dev/null
+++ b/src/live_effects/parameter/unit.h
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_UNIT_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_UNIT_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Copyright (C) Maximilian Albert 2008 <maximilian.albert@gmail.com>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/parameter/parameter.h"
+
+namespace Inkscape {
+
+namespace Util {
+ class Unit;
+}
+
+namespace LivePathEffect {
+
+class UnitParam : public Parameter {
+public:
+ UnitParam(const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect,
+ Glib::ustring default_unit = "px");
+ ~UnitParam() override;
+
+ bool param_readSVGValue(const gchar * strvalue) override;
+ Glib::ustring param_getSVGValue() const override;
+ Glib::ustring param_getDefaultSVGValue() const override;
+ void param_set_default() override;
+ void param_set_value(Inkscape::Util::Unit const &val);
+ void param_update_default(const gchar * default_unit) override;
+ const gchar *get_abbreviation() const;
+ Gtk::Widget * param_newWidget() override;
+
+ operator Inkscape::Util::Unit const *() const { return unit; }
+
+private:
+ Inkscape::Util::Unit const *unit;
+ Inkscape::Util::Unit const *defunit;
+
+ UnitParam(const UnitParam&) = delete;
+ UnitParam& operator=(const UnitParam&) = delete;
+};
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/vector.cpp b/src/live_effects/parameter/vector.cpp
new file mode 100644
index 0000000..ab0a9ae
--- /dev/null
+++ b/src/live_effects/parameter/vector.cpp
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Johan Engelen 2008 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "ui/widget/registered-widget.h"
+#include <glibmm/i18n.h>
+
+#include "live_effects/parameter/vector.h"
+
+#include "knotholder.h"
+#include "svg/svg.h"
+#include "svg/stringstream.h"
+
+#include "live_effects/effect.h"
+#include "verbs.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+VectorParam::VectorParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect, Geom::Point default_vector)
+ : Parameter(label, tip, key, wr, effect),
+ defvalue(default_vector),
+ origin(0.,0.),
+ vector(default_vector)
+{
+ vec_knot_shape = SP_KNOT_SHAPE_DIAMOND;
+ vec_knot_mode = SP_KNOT_MODE_XOR;
+ vec_knot_color = 0xffffb500;
+ ori_knot_shape = SP_KNOT_SHAPE_CIRCLE;
+ ori_knot_mode = SP_KNOT_MODE_XOR;
+ ori_knot_color = 0xffffb500;
+}
+
+VectorParam::~VectorParam()
+= default;
+
+void
+VectorParam::param_set_default()
+{
+ setOrigin(Geom::Point(0.,0.));
+ setVector(defvalue);
+}
+
+void
+VectorParam::param_update_default(Geom::Point default_point)
+{
+ defvalue = default_point;
+}
+
+void
+VectorParam::param_update_default(const gchar * default_point)
+{
+ gchar ** strarray = g_strsplit(default_point, ",", 2);
+ double newx, newy;
+ unsigned int success = sp_svg_number_read_d(strarray[0], &newx);
+ success += sp_svg_number_read_d(strarray[1], &newy);
+ g_strfreev (strarray);
+ if (success == 2) {
+ param_update_default( Geom::Point(newx, newy) );
+ }
+}
+
+bool
+VectorParam::param_readSVGValue(const gchar * strvalue)
+{
+ gchar ** strarray = g_strsplit(strvalue, ",", 4);
+ if (!strarray) {
+ return false;
+ }
+ double val[4];
+ unsigned int i = 0;
+ while (i < 4 && strarray[i]) {
+ if (sp_svg_number_read_d(strarray[i], &val[i]) != 0) {
+ i++;
+ } else {
+ break;
+ }
+ }
+ g_strfreev (strarray);
+ if (i == 4) {
+ setOrigin( Geom::Point(val[0], val[1]) );
+ setVector( Geom::Point(val[2], val[3]) );
+ return true;
+ }
+ return false;
+}
+
+Glib::ustring
+VectorParam::param_getSVGValue() const
+{
+ Inkscape::SVGOStringStream os;
+ os << origin << " , " << vector;
+ return os.str();
+}
+
+Glib::ustring
+VectorParam::param_getDefaultSVGValue() const
+{
+ Inkscape::SVGOStringStream os;
+ os << defvalue;
+ return os.str();
+}
+
+Gtk::Widget *
+VectorParam::param_newWidget()
+{
+ Inkscape::UI::Widget::RegisteredVector * pointwdg = Gtk::manage(
+ new Inkscape::UI::Widget::RegisteredVector( param_label,
+ param_tooltip,
+ param_key,
+ *param_wr,
+ param_effect->getRepr(),
+ param_effect->getSPDoc() ) );
+ pointwdg->setPolarCoords();
+ pointwdg->setValue( vector, origin );
+ pointwdg->clearProgrammatically();
+ pointwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change vector parameter"));
+
+ Gtk::HBox * hbox = Gtk::manage( new Gtk::HBox() );
+ static_cast<Gtk::HBox*>(hbox)->pack_start(*pointwdg, true, true);
+ static_cast<Gtk::HBox*>(hbox)->show_all_children();
+
+ return dynamic_cast<Gtk::Widget *> (hbox);
+}
+
+void
+VectorParam::set_and_write_new_values(Geom::Point const &new_origin, Geom::Point const &new_vector)
+{
+ setValues(new_origin, new_vector);
+ param_write_to_repr(param_getSVGValue().c_str());
+}
+
+void
+VectorParam::param_transform_multiply(Geom::Affine const& postmul, bool /*set*/)
+{
+ set_and_write_new_values( origin * postmul, vector * postmul.withoutTranslation() );
+}
+
+
+void
+VectorParam::set_vector_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color)
+{
+ vec_knot_shape = shape;
+ vec_knot_mode = mode;
+ vec_knot_color = color;
+}
+
+void
+VectorParam::set_origin_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color)
+{
+ ori_knot_shape = shape;
+ ori_knot_mode = mode;
+ ori_knot_color = color;
+}
+
+void
+VectorParam::set_oncanvas_color(guint32 color)
+{
+ vec_knot_color = color;
+ ori_knot_color = color;
+}
+
+class VectorParamKnotHolderEntity_Origin : public KnotHolderEntity {
+public:
+ VectorParamKnotHolderEntity_Origin(VectorParam *p) : param(p) { }
+ ~VectorParamKnotHolderEntity_Origin() override = default;
+
+ void knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state) override {
+ Geom::Point const s = snap_knot_position(p, state);
+ param->setOrigin(s);
+ param->set_and_write_new_values(param->origin, param->vector);
+ sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
+ };
+ Geom::Point knot_get() const override {
+ return param->origin;
+ };
+ void knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state) override
+ {
+ param->param_effect->refresh_widgets = true;
+ param->write_to_SVG();
+ };
+ void knot_click(guint /*state*/) override{
+ g_print ("This is the origin handle associated to parameter '%s'\n", param->param_key.c_str());
+ };
+
+private:
+ VectorParam *param;
+};
+
+class VectorParamKnotHolderEntity_Vector : public KnotHolderEntity {
+public:
+ VectorParamKnotHolderEntity_Vector(VectorParam *p) : param(p) { }
+ ~VectorParamKnotHolderEntity_Vector() override = default;
+
+ void knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint /*state*/) override {
+ Geom::Point const s = p - param->origin;
+ /// @todo implement angle snapping when holding CTRL
+ param->setVector(s);
+ param->set_and_write_new_values(param->origin, param->vector);
+ sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
+ };
+ Geom::Point knot_get() const override {
+ return param->origin + param->vector;
+ };
+ void knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state) override
+ {
+ param->param_effect->refresh_widgets = true;
+ param->write_to_SVG();
+ };
+ void knot_click(guint /*state*/) override{
+ g_print ("This is the vector handle associated to parameter '%s'\n", param->param_key.c_str());
+ };
+
+private:
+ VectorParam *param;
+};
+
+void
+VectorParam::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item)
+{
+ VectorParamKnotHolderEntity_Origin *origin_e = new VectorParamKnotHolderEntity_Origin(this);
+ origin_e->create(nullptr, item, knotholder, Inkscape::CTRL_TYPE_LPE, handleTip(), ori_knot_shape, ori_knot_mode,
+ ori_knot_color);
+ knotholder->add(origin_e);
+
+ VectorParamKnotHolderEntity_Vector *vector_e = new VectorParamKnotHolderEntity_Vector(this);
+ vector_e->create(nullptr, item, knotholder, Inkscape::CTRL_TYPE_LPE, handleTip(), vec_knot_shape, vec_knot_mode,
+ vec_knot_color);
+ knotholder->add(vector_e);
+}
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/vector.h b/src/live_effects/parameter/vector.h
new file mode 100644
index 0000000..d9894e9
--- /dev/null
+++ b/src/live_effects/parameter/vector.h
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_VECTOR_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_VECTOR_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Copyright (C) Johan Engelen 2008 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <glib.h>
+#include <2geom/point.h>
+
+#include "live_effects/parameter/parameter.h"
+
+#include "knot-holder-entity.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+
+class VectorParam : public Parameter {
+public:
+ VectorParam( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect,
+ Geom::Point default_vector = Geom::Point(1,0) );
+ ~VectorParam() override;
+
+ Gtk::Widget * param_newWidget() override;
+ inline const gchar *handleTip() const { return param_tooltip.c_str(); }
+
+ bool param_readSVGValue(const gchar * strvalue) override;
+ Glib::ustring param_getSVGValue() const override;
+ Glib::ustring param_getDefaultSVGValue() const override;
+
+ Geom::Point getVector() const { return vector; };
+ Geom::Point getOrigin() const { return origin; };
+ void setValues(Geom::Point const &new_origin, Geom::Point const &new_vector) { setVector(new_vector); setOrigin(new_origin); };
+ void setVector(Geom::Point const &new_vector) { vector = new_vector; };
+ void setOrigin(Geom::Point const &new_origin) { origin = new_origin; };
+ void param_set_default() override;
+
+ void set_and_write_new_values(Geom::Point const &new_origin, Geom::Point const &new_vector);
+
+ void param_transform_multiply(Geom::Affine const &postmul, bool set) override;
+
+ void set_vector_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color);
+ void set_origin_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color);
+ void set_oncanvas_color(guint32 color);
+ void param_update_default(Geom::Point default_point);
+ void param_update_default(const gchar * default_point) override;
+ bool providesKnotHolderEntities() const override { return true; }
+ void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) override;
+
+private:
+ VectorParam(const VectorParam&) = delete;
+ VectorParam& operator=(const VectorParam&) = delete;
+
+ Geom::Point defvalue;
+
+ Geom::Point origin;
+ Geom::Point vector;
+
+ /// The looks of the vector and origin knots oncanvas
+ SPKnotShapeType vec_knot_shape;
+ SPKnotModeType vec_knot_mode;
+ guint32 vec_knot_color;
+ SPKnotShapeType ori_knot_shape;
+ SPKnotModeType ori_knot_mode;
+ guint32 ori_knot_color;
+
+ friend class VectorParamKnotHolderEntity_Origin;
+ friend class VectorParamKnotHolderEntity_Vector;
+};
+
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif