summaryrefslogtreecommitdiffstats
path: root/src/live_effects/parameter
diff options
context:
space:
mode:
Diffstat (limited to 'src/live_effects/parameter')
-rw-r--r--src/live_effects/parameter/array.cpp128
-rw-r--r--src/live_effects/parameter/array.h175
-rw-r--r--src/live_effects/parameter/bool.cpp117
-rw-r--r--src/live_effects/parameter/bool.h58
-rw-r--r--src/live_effects/parameter/colorpicker.cpp153
-rw-r--r--src/live_effects/parameter/colorpicker.h63
-rw-r--r--src/live_effects/parameter/enum.h115
-rw-r--r--src/live_effects/parameter/fontbutton.cpp108
-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/message.cpp127
-rw-r--r--src/live_effects/parameter/message.h74
-rw-r--r--src/live_effects/parameter/nodesatellitesarray.cpp569
-rw-r--r--src/live_effects/parameter/nodesatellitesarray.h118
-rw-r--r--src/live_effects/parameter/originalpath.cpp113
-rw-r--r--src/live_effects/parameter/originalpath.h49
-rw-r--r--src/live_effects/parameter/originalsatellite.cpp103
-rw-r--r--src/live_effects/parameter/originalsatellite.h39
-rw-r--r--src/live_effects/parameter/parameter.cpp389
-rw-r--r--src/live_effects/parameter/parameter.h166
-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.cpp655
-rw-r--r--src/live_effects/parameter/path.h125
-rw-r--r--src/live_effects/parameter/patharray.cpp593
-rw-r--r--src/live_effects/parameter/patharray.h130
-rw-r--r--src/live_effects/parameter/point.cpp270
-rw-r--r--src/live_effects/parameter/point.h84
-rw-r--r--src/live_effects/parameter/powerstrokepointarray.cpp343
-rw-r--r--src/live_effects/parameter/powerstrokepointarray.h95
-rw-r--r--src/live_effects/parameter/random.cpp227
-rw-r--r--src/live_effects/parameter/random.h76
-rw-r--r--src/live_effects/parameter/satellite-reference.cpp56
-rw-r--r--src/live_effects/parameter/satellite-reference.h57
-rw-r--r--src/live_effects/parameter/satellite.cpp295
-rw-r--r--src/live_effects/parameter/satellite.h76
-rw-r--r--src/live_effects/parameter/satellitearray.cpp506
-rw-r--r--src/live_effects/parameter/satellitearray.h92
-rw-r--r--src/live_effects/parameter/text.cpp184
-rw-r--r--src/live_effects/parameter/text.h95
-rw-r--r--src/live_effects/parameter/togglebutton.cpp214
-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.h88
-rw-r--r--src/live_effects/parameter/unit.cpp111
-rw-r--r--src/live_effects/parameter/unit.h56
-rw-r--r--src/live_effects/parameter/vector.cpp250
-rw-r--r--src/live_effects/parameter/vector.h90
49 files changed, 8046 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..98b3db2
--- /dev/null
+++ b/src/live_effects/parameter/array.cpp
@@ -0,0 +1,128 @@
+// 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 <2geom/coord.h>
+#include <2geom/point.h>
+
+#include "helper-fns.h"
+#include "live_effects/effect.h"
+#include "live_effects/lpeobject.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::shared_ptr<SatelliteReference> ArrayParam<std::shared_ptr<SatelliteReference>>::readsvg(const gchar *str)
+{
+ std::shared_ptr<SatelliteReference> satellitereference = nullptr;
+ if (!str) {
+ return satellitereference;
+ }
+
+ gchar **strarray = g_strsplit(str, ",", 2);
+ if (strarray[0] != nullptr && g_strstrip(strarray[0])[0] == '#') {
+ try {
+ bool active = strarray[1] != nullptr;
+ satellitereference = std::make_shared<SatelliteReference>(param_effect->getLPEObj(), active);
+ satellitereference->attach(Inkscape::URI(g_strstrip(strarray[0])));
+ if (active) {
+ satellitereference->setActive(strncmp(strarray[1], "1", 1) == 0);
+ }
+ } catch (Inkscape::BadURIException &e) {
+ g_warning("%s (%s)", e.what(), strarray[0]);
+ satellitereference->detach();
+ }
+ }
+ g_strfreev(strarray);
+ return satellitereference;
+}
+
+template <>
+std::vector<NodeSatellite> ArrayParam<std::vector<NodeSatellite>>::readsvg(const gchar *str)
+{
+ std::vector<NodeSatellite> subpath_nodesatellites;
+ if (!str) {
+ return subpath_nodesatellites;
+ }
+ gchar ** strarray = g_strsplit(str, "@", 0);
+ gchar ** iter = strarray;
+ while (*iter != nullptr) {
+ gchar ** strsubarray = g_strsplit(*iter, ",", 8);
+ if (*strsubarray[7]) {//steps always > 0
+ NodeSatellite *nodesatellite = new NodeSatellite();
+ nodesatellite->setNodeSatellitesType(g_strstrip(strsubarray[0]));
+ nodesatellite->is_time = strncmp(strsubarray[1], "1", 1) == 0;
+ nodesatellite->selected = strncmp(strsubarray[2], "1", 1) == 0;
+ nodesatellite->has_mirror = strncmp(strsubarray[3], "1", 1) == 0;
+ nodesatellite->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;
+ nodesatellite->amount = amount;
+ nodesatellite->angle = angle;
+ nodesatellite->steps = steps;
+ subpath_nodesatellites.push_back(*nodesatellite);
+ }
+ g_strfreev (strsubarray);
+ iter++;
+ }
+ g_strfreev (strarray);
+ return subpath_nodesatellites;
+}
+
+} /* 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..64e8d6d
--- /dev/null
+++ b/src/live_effects/parameter/array.h
@@ -0,0 +1,175 @@
+// 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 <glib.h>
+#include <vector>
+
+#include "bad-uri-exception.h"
+#include "helper/geom-nodesatellite.h"
+#include "live_effects/parameter/parameter.h"
+#include "live_effects/parameter/satellite-reference.h"
+#include "object/uri.h"
+#include "svg/stringstream.h"
+#include "svg/svg.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);
+ }
+ ParamType paramType() const override { return ParamType::ARRAY; };
+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::shared_ptr<SatelliteReference> const &vector_data) const
+ {
+ if (vector_data && vector_data->isAttached()) {
+ str << vector_data->getURI()->str();
+ if (vector_data->getHasActive()) {
+ str << ",";
+ str << vector_data->getActive();
+ }
+ }
+ }
+
+ void writesvgData(SVGOStringStream &str, std::vector<NodeSatellite> const &vector_data) const
+ {
+ for (size_t i = 0; i < vector_data.size(); ++i) {
+ if (i != 0) {
+ // separate nodes with @ symbol ( we use | for paths)
+ str << " @ ";
+ }
+ str << vector_data[i].getNodeSatellitesTypeGchar();
+ 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..79841b4
--- /dev/null
+++ b/src/live_effects/parameter/bool.cpp
@@ -0,0 +1,117 @@
+// 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 "bool.h"
+
+#include <glibmm/i18n.h>
+
+#include "helper-fns.h"
+#include "inkscape.h"
+
+#include "live_effects/effect.h"
+#include "svg/stringstream.h"
+#include "svg/svg.h"
+#include "ui/icon-names.h"
+#include "ui/widget/registered-widget.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(_("Change bool parameter"), INKSCAPE_ICON("dialog-path-effects"));
+ 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..b67f555
--- /dev/null
+++ b/src/live_effects/parameter/bool.h
@@ -0,0 +1,58 @@
+// 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; };
+ ParamType paramType() const override { return ParamType::BOOL; };
+
+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..cf9243e
--- /dev/null
+++ b/src/live_effects/parameter/colorpicker.cpp
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Authors:
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "colorpicker.h"
+
+#include <gtkmm.h>
+
+#include "color.h"
+#include "document-undo.h"
+#include "document.h"
+#include "inkscape.h"
+
+#include "live_effects/effect.h"
+#include "live_effects/parameter/colorpicker.h"
+
+#include "svg/stringstream.h"
+#include "svg/svg-color.h"
+#include "svg/svg.h"
+
+#include "ui/icon-names.h"
+#include "ui/widget/registered-widget.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::Box *hbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
+
+ 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(_("Change color button parameter"), INKSCAPE_ICON("dialog-path-effects"));
+ 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..ff7abe0
--- /dev/null
+++ b/src/live_effects/parameter/colorpicker.h
@@ -0,0 +1,63 @@
+// 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;
+
+ guint32 get_value() const { return value; };
+ ParamType paramType() const override { return ParamType::COLOR_PICKER; };
+
+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..91af18c
--- /dev/null
+++ b/src/live_effects/parameter/enum.h
@@ -0,0 +1,115 @@
+// 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 <glibmm/ustring.h>
+
+#include "live_effects/effect.h"
+#include "live_effects/parameter/parameter.h"
+
+#include "ui/icon-names.h"
+#include "ui/widget/registered-enums.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(_("Change enumeration parameter"), INKSCAPE_ICON("dialog-path-effects"));
+
+ 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;
+ }
+ ParamType paramType() const override { return ParamType::ENUM; };
+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..441f14e
--- /dev/null
+++ b/src/live_effects/parameter/fontbutton.cpp
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Authors:
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "fontbutton.h"
+
+#include <glibmm/i18n.h>
+#include <gtkmm.h>
+
+#include "live_effects/effect.h"
+#include "svg/stringstream.h"
+#include "svg/svg.h"
+#include "ui/icon-names.h"
+#include "ui/widget/font-button.h"
+#include "ui/widget/registered-widget.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(_("Change font button parameter"), INKSCAPE_ICON("dialog-path-effects"));
+ 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..266d324
--- /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; };
+ ParamType paramType() const override { return ParamType::FONT_BUTTON; };
+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..714455a
--- /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; };
+ ParamType paramType() const override { return ParamType::HIDDEN; };
+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/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..aa6a8da
--- /dev/null
+++ b/src/live_effects/parameter/message.h
@@ -0,0 +1,74 @@
+// 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; };
+ ParamType paramType() const override { return ParamType::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;
+};
+
+} //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/nodesatellitesarray.cpp b/src/live_effects/parameter/nodesatellitesarray.cpp
new file mode 100644
index 0000000..5f12f4c
--- /dev/null
+++ b/src/live_effects/parameter/nodesatellitesarray.cpp
@@ -0,0 +1,569 @@
+// 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 "nodesatellitesarray.h"
+
+#include "helper/geom.h"
+#include "inkscape.h"
+#include "live_effects/effect.h"
+#include "live_effects/lpe-fillet-chamfer.h"
+#include "preferences.h"
+#include "ui/dialog/lpe-fillet-chamfer-properties.h"
+#include "ui/knot/knot-holder.h"
+#include "ui/shape-editor.h"
+#include "ui/tools/node-tool.h"
+
+// TODO due to internal breakage in glibmm headers,
+// this has to be included last.
+#include <glibmm/i18n.h>
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+NodeSatelliteArrayParam::NodeSatelliteArrayParam(const Glib::ustring &label, const Glib::ustring &tip,
+ const Glib::ustring &key, Inkscape::UI::Widget::Registry *wr,
+ Effect *effect)
+ : ArrayParam<std::vector<NodeSatellite>>(label, tip, key, wr, effect, 0)
+ , _knoth(nullptr)
+{
+ param_widget_is_visible(false);
+}
+
+void NodeSatelliteArrayParam::set_oncanvas_looks(Inkscape::CanvasItemCtrlShape shape, Inkscape::CanvasItemCtrlMode mode,
+ guint32 color)
+{
+ _knot_shape = shape;
+ _knot_mode = mode;
+ _knot_color = color;
+}
+
+void NodeSatelliteArrayParam::setPathVectorNodeSatellites(PathVectorNodeSatellites *pathVectorNodeSatellites,
+ bool write)
+{
+ _last_pathvector_nodesatellites = pathVectorNodeSatellites;
+ if (write) {
+ param_set_and_write_new_value(_last_pathvector_nodesatellites->getNodeSatellites());
+ } else {
+ param_setValue(_last_pathvector_nodesatellites->getNodeSatellites());
+ }
+}
+
+void NodeSatelliteArrayParam::reloadKnots()
+{
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (desktop) {
+ Inkscape::UI::Tools::NodeTool *nt = dynamic_cast<Inkscape::UI::Tools::NodeTool *>(desktop->event_context);
+ if (nt) {
+ for (auto &_shape_editor : nt->_shape_editors) {
+ Inkscape::UI::ShapeEditor *shape_editor = _shape_editor.second.get();
+ if (shape_editor && shape_editor->lpeknotholder) {
+ SPItem *item = shape_editor->knotholder->item;
+ shape_editor->unset_item(true);
+ shape_editor->set_item(item);
+ }
+ }
+ }
+ }
+}
+void NodeSatelliteArrayParam::setUseDistance(bool use_knot_distance)
+{
+ _use_distance = use_knot_distance;
+}
+
+void NodeSatelliteArrayParam::setCurrentZoom(double current_zoom)
+{
+ _current_zoom = current_zoom;
+}
+
+void NodeSatelliteArrayParam::setGlobalKnotHide(bool global_knot_hide)
+{
+ _global_knot_hide = global_knot_hide;
+}
+
+void NodeSatelliteArrayParam::setEffectType(EffectType et)
+{
+ _effectType = et;
+}
+
+void NodeSatelliteArrayParam::updateCanvasIndicators(bool mirror)
+{
+ if (!_last_pathvector_nodesatellites) {
+ return;
+ }
+
+ if (!_hp.empty()) {
+ _hp.clear();
+ }
+ Geom::PathVector pathv = _last_pathvector_nodesatellites->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 nodesatellite in open paths with fillet chamfer
+ // effect
+ (!pathv[i].closed() && j == 0) || // ignore first nodesatellites 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 nodesatellite 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 NodeSatelliteArrayParam::updateCanvasIndicators()
+{
+ updateCanvasIndicators(true);
+}
+
+void NodeSatelliteArrayParam::addCanvasIndicators(SPLPEItem const * /*lpeitem*/, std::vector<Geom::PathVector> &hp_vec)
+{
+ hp_vec.push_back(_hp);
+}
+
+void NodeSatelliteArrayParam::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 NodeSatelliteArrayParam::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item, bool mirror)
+{
+ if (!_last_pathvector_nodesatellites) {
+ 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;
+ }
+ NodeSatelliteType type = _vector[i][j].nodesatellite_type;
+ if (mirror && i == 0 && j == 0) {
+ index += _last_pathvector_nodesatellites->getTotalNodeSatellites();
+ }
+ 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::CANVAS_ITEM_CTRL_TYPE_LPE, "LPE:Chamfer",
+ _(tip), _knot_color);
+ knotholder->add(e);
+ }
+ index++;
+ }
+ }
+ if (mirror) {
+ addKnotHolderEntities(knotholder, item, false);
+ }
+}
+
+void NodeSatelliteArrayParam::updateAmmount(double amount)
+{
+ Geom::PathVector const pathv = _last_pathvector_nodesatellites->getPathVector();
+ NodeSatellites nodesatellites = _last_pathvector_nodesatellites->getNodeSatellites();
+ for (size_t i = 0; i < nodesatellites.size(); ++i) {
+ for (size_t j = 0; j < nodesatellites[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 NodeSatelliteArrayParam::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item)
+{
+ _knoth = knotholder;
+ addKnotHolderEntities(knotholder, item, true);
+}
+
+FilletChamferKnotHolderEntity::FilletChamferKnotHolderEntity(NodeSatelliteArrayParam *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_nodesatellites) {
+ return;
+ }
+ size_t total_nodesatellites = _pparam->_last_pathvector_nodesatellites->getTotalNodeSatellites();
+ bool is_mirror = false;
+ size_t index = _index;
+ if (_index >= total_nodesatellites) {
+ index = _index - total_nodesatellites;
+ is_mirror = true;
+ }
+ std::pair<size_t, size_t> index_data = _pparam->_last_pathvector_nodesatellites->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;
+ }
+ NodeSatellite nodesatellite = _pparam->_vector[satelite_index][subsatelite_index];
+ Geom::PathVector pathv = _pparam->_last_pathvector_nodesatellites->getPathVector();
+ if (nodesatellite.hidden ||
+ (!pathv[satelite_index].closed() &&
+ (subsatelite_index == 0 || // ignore first nodesatellites on open paths
+ count_path_nodes(pathv[satelite_index]) - 1 == subsatelite_index))) // ignore last nodesatellite 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)) {
+ nodesatellite.amount = 0;
+ } else if (distance_mirror < distance_normal) {
+ double time_start = 0;
+ NodeSatellites nodesatellites = _pparam->_last_pathvector_nodesatellites->getNodeSatellites();
+ time_start = nodesatellites[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 (nodesatellite.is_time) {
+ amount = timeAtArcLength(amount, pathv[satelite_index][subsatelite_index]);
+ }
+ nodesatellite.amount = amount;
+ } else {
+ nodesatellite.setPosition(s, pathv[satelite_index][subsatelite_index]);
+ }
+ Inkscape::LivePathEffect::LPEFilletChamfer *filletchamfer = dynamic_cast<Inkscape::LivePathEffect::LPEFilletChamfer *>(_pparam->param_effect);
+ filletchamfer->helperpath = true;
+ _pparam->updateAmmount(nodesatellite.amount);
+ _pparam->_vector[satelite_index][subsatelite_index] = nodesatellite;
+ 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_nodesatellites || _pparam->_global_knot_hide) {
+ return Geom::Point(Geom::infinity(), Geom::infinity());
+ }
+ Geom::Point tmp_point;
+ size_t total_nodesatellites = _pparam->_last_pathvector_nodesatellites->getTotalNodeSatellites();
+ bool is_mirror = false;
+ size_t index = _index;
+ if (_index >= total_nodesatellites) {
+ index = _index - total_nodesatellites;
+ is_mirror = true;
+ }
+ std::pair<size_t, size_t> index_data = _pparam->_last_pathvector_nodesatellites->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());
+ }
+ NodeSatellite nodesatellite = _pparam->_vector[satelite_index][subsatelite_index];
+ Geom::PathVector pathv = _pparam->_last_pathvector_nodesatellites->getPathVector();
+ if (nodesatellite.hidden ||
+ (!pathv[satelite_index].closed() &&
+ (subsatelite_index == 0 || subsatelite_index == count_path_nodes(pathv[satelite_index]) -
+ 1))) // ignore first and last nodesatellites 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 = nodesatellite.arcDistance(pathv[satelite_index][subsatelite_index]);
+ double t = nodesatellite.time(s, true, curve_in);
+ if (t > 1) {
+ t = 1;
+ }
+ if (t < 0) {
+ t = 0;
+ }
+ double time_start = 0;
+ time_start = _pparam->_last_pathvector_nodesatellites->getNodeSatellites()[satelite_index][previous_index].time(
+ curve_in);
+ if (time_start > t) {
+ t = time_start;
+ }
+ tmp_point = (curve_in).pointAt(t);
+ } else {
+ tmp_point = nodesatellite.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_nodesatellites) {
+ return;
+ }
+ size_t total_nodesatellites = _pparam->_last_pathvector_nodesatellites->getTotalNodeSatellites();
+ bool is_mirror = false;
+ size_t index = _index;
+ if (_index >= total_nodesatellites) {
+ index = _index - total_nodesatellites;
+ is_mirror = true;
+ }
+ std::pair<size_t, size_t> index_data = _pparam->_last_pathvector_nodesatellites->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_nodesatellites->getPathVector();
+ if (!pathv[satelite_index].closed() &&
+ (subsatelite_index == 0 ||
+ count_path_nodes(pathv[satelite_index]) - 1 == subsatelite_index)) // ignore last nodesatellite 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;
+ NodeSatelliteType type = _pparam->_vector[satelite_index][subsatelite_index].nodesatellite_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].nodesatellite_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(NodeSatellite nodesatellite)
+{
+ if (!_pparam->_last_pathvector_nodesatellites) {
+ return;
+ }
+ size_t total_nodesatellites = _pparam->_last_pathvector_nodesatellites->getTotalNodeSatellites();
+ bool is_mirror = false;
+ size_t index = _index;
+ if (_index >= total_nodesatellites) {
+ index = _index - total_nodesatellites;
+ is_mirror = true;
+ }
+ std::pair<size_t, size_t> index_data = _pparam->_last_pathvector_nodesatellites->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_nodesatellites->getPathVector();
+ if (nodesatellite.hidden ||
+ (!pathv[satelite_index].closed() &&
+ (subsatelite_index == 0 ||
+ count_path_nodes(pathv[satelite_index]) - 1 == subsatelite_index))) // ignore last nodesatellite in open paths
+ // with fillet chamfer effect
+ {
+ return;
+ }
+ double amount = nodesatellite.amount;
+ double max_amount = amount;
+ if (!_pparam->_use_distance && !nodesatellite.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;
+ }
+ }
+ nodesatellite.amount = amount;
+ _pparam->_vector[satelite_index][subsatelite_index] = nodesatellite;
+ 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/nodesatellitesarray.h b/src/live_effects/parameter/nodesatellitesarray.h
new file mode 100644
index 0000000..2075e43
--- /dev/null
+++ b/src/live_effects/parameter/nodesatellitesarray.h
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_NODESATELLITES_ARRAY_H
+#define INKSCAPE_LIVEPATHEFFECT_NODESATELLITES_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 pathVectorNodeSatellites class to serialize it as a LPE
+ * parameter
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <glib.h>
+
+#include "helper/geom-pathvector_nodesatellites.h"
+#include "live_effects/effect-enum.h"
+#include "live_effects/parameter/array.h"
+#include "ui/knot/knot-holder-entity.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class FilletChamferKnotHolderEntity;
+
+class NodeSatelliteArrayParam : public ArrayParam<std::vector<NodeSatellite>>
+{
+public:
+ NodeSatelliteArrayParam(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 setPathVectorNodeSatellites(PathVectorNodeSatellites *pathVectorNodeSatellites, bool write = true);
+
+ void set_oncanvas_looks(Inkscape::CanvasItemCtrlShape shape,
+ Inkscape::CanvasItemCtrlMode mode,
+ guint32 color);
+
+
+ friend class FilletChamferKnotHolderEntity;
+ friend class LPEFilletChamfer;
+ ParamType paramType() const override { return ParamType::NODE_SATELLITE_ARRAY; };
+protected:
+ KnotHolder *_knoth;
+
+private:
+ NodeSatelliteArrayParam(const NodeSatelliteArrayParam &) = delete;
+ NodeSatelliteArrayParam &operator=(const NodeSatelliteArrayParam &) = delete;
+
+ Inkscape::CanvasItemCtrlShape _knot_shape = Inkscape::CANVAS_ITEM_CTRL_SHAPE_DIAMOND;
+ Inkscape::CanvasItemCtrlMode _knot_mode = Inkscape::CANVAS_ITEM_CTRL_MODE_XOR;
+ guint32 _knot_color = 0xaaff8800;
+ Geom::PathVector _hp;
+ bool _use_distance = false;
+ bool _global_knot_hide = false;
+ double _current_zoom = 0;
+ EffectType _effectType = FILLET_CHAMFER;
+ PathVectorNodeSatellites *_last_pathvector_nodesatellites = nullptr;
+};
+
+class FilletChamferKnotHolderEntity : public KnotHolderEntity {
+public:
+ FilletChamferKnotHolderEntity(NodeSatelliteArrayParam *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(NodeSatellite);
+ /** 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:
+ NodeSatelliteArrayParam *_pparam;
+ size_t _index;
+};
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/originalpath.cpp b/src/live_effects/parameter/originalpath.cpp
new file mode 100644
index 0000000..c4d582d
--- /dev/null
+++ b/src/live_effects/parameter/originalpath.cpp
@@ -0,0 +1,113 @@
+// 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 "live_effects/lpeobject.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::Box *_widget = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
+
+ { // Label
+ Gtk::Label *pLabel = Gtk::manage(new Gtk::Label(param_label));
+ _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));
+ _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));
+ _widget->pack_start(*pButton, true, true);
+ pButton->set_tooltip_text(_("Select original"));
+ }
+
+ _widget->show_all_children();
+
+ return dynamic_cast<Gtk::Widget *> (_widget);
+}
+
+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);
+ 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..200d1cf
--- /dev/null
+++ b/src/live_effects/parameter/originalpath.h
@@ -0,0 +1,49 @@
+// 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 {};
+ ParamType paramType() const override { return ParamType::ORIGINAL_PATH; };
+protected:
+ void on_select_original_button_click();
+
+private:
+ OriginalPathParam(const OriginalPathParam&) = delete;
+ OriginalPathParam& operator=(const OriginalPathParam&) = delete;
+};
+
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/originalsatellite.cpp b/src/live_effects/parameter/originalsatellite.cpp
new file mode 100644
index 0000000..5c98c52
--- /dev/null
+++ b/src/live_effects/parameter/originalsatellite.cpp
@@ -0,0 +1,103 @@
+// 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 "live_effects/parameter/originalsatellite.h"
+
+#include <glibmm/i18n.h>
+#include <gtkmm/box.h>
+#include <gtkmm/button.h>
+#include <gtkmm/label.h>
+
+#include "desktop.h"
+#include "display/curve.h"
+#include "inkscape.h"
+#include "live_effects/effect.h"
+#include "live_effects/parameter/satellite-reference.h"
+#include "object/uri.h"
+#include "selection.h"
+#include "ui/icon-loader.h"
+#include "ui/icon-names.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+OriginalSatelliteParam::OriginalSatelliteParam(const Glib::ustring &label, const Glib::ustring &tip,
+ const Glib::ustring &key, Inkscape::UI::Widget::Registry *wr,
+ Effect *effect)
+ : SatelliteParam(label, tip, key, wr, effect)
+{
+}
+
+OriginalSatelliteParam::~OriginalSatelliteParam() = default;
+
+Gtk::Widget *OriginalSatelliteParam::param_newWidget()
+{
+ Gtk::Box *_widget = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
+
+ { // Label
+ Gtk::Label *pLabel = Gtk::manage(new Gtk::Label(param_label));
+ _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, &OriginalSatelliteParam::on_link_button_click));
+ _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, &OriginalSatelliteParam::on_select_original_button_click));
+ _widget->pack_start(*pButton, true, true);
+ pButton->set_tooltip_text(_("Select original"));
+ }
+
+ _widget->show_all_children();
+
+ return dynamic_cast<Gtk::Widget *> (_widget);
+}
+
+void OriginalSatelliteParam::on_select_original_button_click()
+{
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ SPItem *original = dynamic_cast<SPItem *>(lperef->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/originalsatellite.h b/src/live_effects/parameter/originalsatellite.h
new file mode 100644
index 0000000..683f66d
--- /dev/null
+++ b/src/live_effects/parameter/originalsatellite.h
@@ -0,0 +1,39 @@
+// 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/satellite.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class OriginalSatelliteParam : public SatelliteParam
+{
+public:
+ OriginalSatelliteParam(const Glib::ustring &label, const Glib::ustring &tip, const Glib::ustring &key,
+ Inkscape::UI::Widget::Registry *wr, Effect *effect);
+ ~OriginalSatelliteParam() override;
+ Gtk::Widget * param_newWidget() override;
+ ParamType paramType() const override { return ParamType::ORIGINAL_SATELLITE; };
+protected:
+ void on_select_original_button_click();
+
+private:
+ OriginalSatelliteParam(const OriginalSatelliteParam &) = delete;
+ OriginalSatelliteParam &operator=(const OriginalSatelliteParam &) = delete;
+};
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/parameter.cpp b/src/live_effects/parameter/parameter.cpp
new file mode 100644
index 0000000..ec40e5e
--- /dev/null
+++ b/src/live_effects/parameter/parameter.cpp
@@ -0,0 +1,389 @@
+// 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 "parameter.h"
+
+#include <glibmm/i18n.h>
+#include <utility>
+
+#include "display/control/canvas-item-bpath.h"
+#include "display/curve.h"
+#include "live_effects/effect.h"
+#include "live_effects/effect-enum.h"
+#include "svg/stringstream.h"
+#include "svg/svg.h"
+#include "ui/icon-names.h"
+#include "xml/repr.h"
+
+#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)
+{
+}
+
+Parameter::~Parameter()
+{
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (desktop && ownerlocator) {
+ desktop->remove_temporary_canvasitem(ownerlocator);
+ }
+ if (selection_changed_connection) {
+ selection_changed_connection->disconnect();
+ delete selection_changed_connection;
+ selection_changed_connection = nullptr;
+ }
+}
+
+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());
+}
+
+EffectType Parameter::effectType() const
+{
+ if (param_effect) {
+ return param_effect->effectType();
+ }
+ return INVALID_LPE;
+};
+
+ParamType Parameter::paramType() const
+{
+ return INVALID_PARAM;
+};
+
+void
+sp_add_class(SPObject *item, Glib::ustring classglib) {
+ gchar const *classlpe = item->getAttribute("class");
+ if (classlpe) {
+ classglib = classlpe;
+ if (classglib.find("UnoptimicedTransforms") == Glib::ustring::npos) {
+ classglib += " UnoptimicedTransforms";
+ item->setAttribute("class",classglib.c_str());
+ }
+ } else {
+ item->setAttribute("class","UnoptimicedTransforms");
+ }
+}
+
+/*
+ * sometimes for example on ungrouping or loading documents we need to relay in stored value instead the volatile
+ * version in the parameter
+ */
+void Parameter::read_from_SVG()
+{
+ const gchar *val = param_effect->getRepr()->attribute(param_key.c_str());
+ if (val) {
+ param_readSVGValue(val);
+ }
+}
+
+void Parameter::param_higlight(bool highlight, bool select)
+{
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (desktop) {
+ std::vector<SPLPEItem *> lpeitems = param_effect->getCurrrentLPEItems();
+ if (lpeitems.size()) {
+ sp_add_class(lpeitems[0], "UnoptimicedTransforms");
+ }
+ if (!highlight && ownerlocator) {
+ desktop->remove_temporary_canvasitem(ownerlocator);
+ ownerlocator = nullptr;
+ }
+ if (highlight) {
+ if (lpeitems.size() == 1 && param_effect->is_visible) {
+ if (select && !lpeitems[0]->isHidden()) {
+ desktop->selection->clear();
+ desktop->selection->add(lpeitems[0]);
+ return;
+ }
+ auto c = std::make_unique<SPCurve>();
+ std::vector<Geom::PathVector> cs; // = param_effect->getCanvasIndicators(lpeitems[0]);
+ Geom::OptRect bbox = lpeitems[0]->documentVisualBounds();
+
+ if (param_effect->helperLineSatellites) {
+ std::vector<SPObject *> satellites = param_get_satellites();
+ for (auto iter : satellites) {
+ SPItem *satelliteitem = dynamic_cast<SPItem *>(iter);
+ if (satelliteitem) {
+ bbox.unionWith(satelliteitem->documentVisualBounds());
+ }
+ }
+ }
+ Geom::PathVector out;
+ if (bbox) {
+ Geom::Path p = Geom::Path(*bbox);
+ out.push_back(p);
+ }
+ cs.push_back(out);
+ for (auto &p2 : cs) {
+ p2 *= desktop->dt2doc();
+ c->append(p2);
+ }
+ if (!c->is_empty()) {
+ desktop->remove_temporary_canvasitem(ownerlocator);
+ auto tmpitem = new Inkscape::CanvasItemBpath(desktop->getCanvasTemp(), c.get(), true);
+ tmpitem->set_stroke(0x0000ff9a);
+ tmpitem->set_fill(0x0, SP_WIND_RULE_NONZERO); // No fill
+ ownerlocator = desktop->add_temporary_canvasitem(tmpitem, 0);
+ }
+ }
+ }
+ }
+}
+
+void Parameter::change_selection(Inkscape::Selection *selection)
+{
+ update_satellites(false);
+}
+
+void Parameter::connect_selection_changed()
+{
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (desktop) {
+ Inkscape::Selection *selection = desktop->selection;
+ if (selection) {
+ std::vector<SPObject *> satellites = param_get_satellites();
+ if (!selection_changed_connection) {
+ selection_changed_connection = new sigc::connection(
+ selection->connectChanged(sigc::mem_fun(*this, &Parameter::change_selection)));
+ }
+ }
+ }
+}
+
+void Parameter::update_satellites(bool updatelpe)
+{
+ if (paramType() == ParamType::SATELLITE || paramType() == ParamType::SATELLITE_ARRAY || paramType() == ParamType::PATH ||
+ paramType() == ParamType::PATH_ARRAY || paramType() == ParamType::ORIGINAL_PATH || paramType() == ParamType::ORIGINAL_SATELLITE) {
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ std::vector<SPLPEItem *> lpeitems = param_effect->getCurrrentLPEItems();
+ if (lpeitems.size() == 1){
+ if (desktop) {
+ DocumentUndo::ScopedInsensitive _no_undo(desktop->getDocument());
+ param_higlight(false, false);
+ Inkscape::Selection *selection = desktop->selection;
+ if (selection) {
+ std::vector<SPObject *> satellites = param_get_satellites();
+ connect_selection_changed();
+ if (selection->singleItem()) {
+ if (param_effect->isOnClipboard()) {
+ return;
+ }
+ // we always start hiding helper path
+ for (auto iter : satellites) {
+ sp_add_class(iter, "UnoptimicedTransforms");
+ // if selection is current ref we highlight original sp_lpe_item to
+ // give visual feedback to the user to know what's the LPE item that generated the selection
+ if (iter && selection->includes(iter, true)) {
+ const gchar *classtoparentchar = iter->getAttribute("class");
+ if (classtoparentchar) {
+ Glib::ustring classtoparent = classtoparentchar;
+ if (classtoparent.find("lpeselectparent ") != Glib::ustring::npos) {
+ param_higlight(true, true);
+ } else {
+ param_higlight(true, false);
+ }
+ } else {
+ param_higlight(true, false);
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (updatelpe && param_effect->is_visible) {
+ sp_lpe_item_update_patheffect(lpeitems[0], false, false);
+ }
+ }
+ }
+}
+
+/*
+ * we get satellites of parameter, virtual function overided by some parameter with linked satellites
+ */
+std::vector<SPObject *> Parameter::param_get_satellites()
+{
+ std::vector<SPObject *> objs;
+ return objs;
+};
+
+/*###########################################
+ * 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(_("Change scalar parameter"), INKSCAPE_ICON("dialog-path-effects"));
+ }
+ 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..d9a901b
--- /dev/null
+++ b/src/live_effects/parameter/parameter.h
@@ -0,0 +1,166 @@
+// 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 <2geom/forward.h>
+#include <2geom/pathvector.h>
+#include <glibmm/ustring.h>
+
+#include "live_effects/lpeobject.h"
+#include "ui/widget/registered-widget.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 Display {
+class TemporaryItem;
+}
+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();
+
+ 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();
+ void read_from_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){};
+ virtual std::vector<SPObject *> param_get_satellites();
+ void param_higlight(bool highlight, bool select);
+ sigc::connection *selection_changed_connection = nullptr;
+ void change_selection(Inkscape::Selection *selection);
+ void update_satellites(bool updatelpe = false);
+ Glib::ustring param_key;
+ Glib::ustring param_tooltip;
+ Inkscape::UI::Widget::Registry *param_wr;
+ Glib::ustring param_label;
+ EffectType effectType() const;
+ virtual ParamType paramType() const;
+ bool oncanvas_editable;
+ bool widget_is_visible;
+ bool widget_is_enabled;
+ void connect_selection_changed();
+
+ protected:
+ Inkscape::Display::TemporaryItem *ownerlocator = nullptr;
+ 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..7dc5895
--- /dev/null
+++ b/src/live_effects/parameter/path.cpp
@@ -0,0 +1,655 @@
+// 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 "path.h"
+
+#include <glibmm/i18n.h>
+#include <glibmm/utility.h>
+
+#include <gtkmm/button.h>
+#include <gtkmm/label.h>
+
+
+#include <2geom/svg-path-parser.h>
+#include <2geom/sbasis-to-bezier.h>
+#include <2geom/pathvector.h>
+#include <2geom/d2.h>
+
+#include "bad-uri-exception.h"
+
+#include "desktop.h"
+#include "document-undo.h"
+#include "document.h"
+#include "inkscape.h"
+#include "message-stack.h"
+#include "selection.h"
+#include "selection-chemistry.h"
+
+#include "actions/actions-tools.h"
+#include "display/curve.h"
+#include "live_effects/effect.h"
+#include "live_effects/lpeobject.h"
+#include "object/uri.h"
+#include "object/sp-shape.h"
+#include "object/sp-item.h"
+#include "object/sp-text.h"
+#include "svg/svg.h"
+
+#include "ui/clipboard.h" // clipboard support
+#include "ui/icon-loader.h"
+#include "ui/icon-names.h"
+#include "ui/shape-editor.h" // needed for on-canvas editing:
+#include "ui/tools/node-tool.h"
+#include "ui/tool/multi-path-manipulator.h"
+#include "ui/tool/shape-record.h"
+#include "ui/widget/point.h"
+
+#include "xml/repr.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()
+{
+ unlink();
+//TODO: Removed to fix a bug https://bugs.launchpad.net/inkscape/+bug/1716926
+// Maybe we 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) {
+// Inkscape::UI::Tools::NodeTool *nt = dynamic_cast<Inkscape::UI::Tools::NodeTool*>(desktop->event_context);
+// if (nt) {
+// SPItem * item = SP_ACTIVE_DESKTOP->getSelection()->singleItem();
+// if (item) {
+// std::set<ShapeRecord> shapes;
+// ShapeRecord r;
+// r.item = item;
+// shapes.insert(r);
+// nt->_multipath->setItems(shapes);
+// }
+// }
+// }
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (desktop) {
+ if (dynamic_cast<Inkscape::UI::Tools::NodeTool* >(desktop->event_context)) {
+ // Why is this switching tools twice? Probably to reinitialize Node Tool.
+ set_active_tool(desktop, "Select");
+ set_active_tool(desktop, "Node");
+ }
+ }
+ g_free(defvalue);
+}
+
+void PathParam::reload() {
+ setUpdating(false);
+ start_listening(getObject());
+ connect_selection_changed();
+ SPItem *item = nullptr;
+ if (( item = dynamic_cast<SPItem *>(getObject()) )) {
+ item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ }
+}
+
+Geom::Affine
+PathParam::get_relative_affine() {
+ Geom::Affine affine = Geom::identity();
+ SPItem *item = nullptr;
+ if (( item = dynamic_cast<SPItem *>(getObject()) )) {
+ std::vector<SPLPEItem *> lpeitems = param_effect->getCurrrentLPEItems();
+ if (lpeitems.size() == 1) {
+ param_effect->sp_lpe_item = lpeitems[0];
+ }
+ affine = item->getRelativeTransform(param_effect->sp_lpe_item);
+ }
+ return affine;
+}
+
+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);
+}
+
+std::vector<SPObject *> PathParam::param_get_satellites()
+{
+
+ std::vector<SPObject *> objs;
+ if (ref.isAttached()) {
+ // we reload connexions in case are lost for example item recreation on ungroup
+ if (!linked_transformed_connection) {
+ write_to_SVG();
+ }
+
+ SPObject * linked_obj = ref.getObject();
+ if (linked_obj) {
+ objs.push_back(linked_obj);
+ }
+ }
+ return objs;
+}
+
+bool
+PathParam::param_readSVGValue(const gchar * strvalue)
+{
+ if (strvalue) {
+ _pathvector.clear();
+ unlink();
+ must_recalculate_pwd2 = true;
+
+
+ if (strvalue[0] == '#') {
+ bool write = false;
+ SPObject * old_ref = param_effect->getSPDoc()->getObjectByHref(strvalue);
+ Glib::ustring id_tmp;
+ if (old_ref) {
+ SPObject * successor = old_ref->_successor;
+ if (successor) {
+ id_tmp = successor->getId();
+ id_tmp.insert(id_tmp.begin(), '#');
+ write = true;
+ }
+ }
+ if (href)
+ g_free(href);
+ href = g_strdup(id_tmp.empty() ? strvalue : id_tmp.c_str());
+
+ // 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);
+ }
+ if (write) {
+ auto full = param_getSVGValue();
+ param_write_to_repr(full.c_str());
+ }
+ } else {
+ _pathvector = sp_svg_read_pathv(strvalue);
+ }
+
+ emit_changed();
+ return true;
+ }
+
+ return false;
+}
+
+Glib::ustring
+PathParam::param_getSVGValue() const
+{
+ if (href) {
+ return href;
+ } else {
+ return sp_svg_write_path(_pathvector);
+ }
+}
+
+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::Box * _widget = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
+
+ Gtk::Label* pLabel = Gtk::manage(new Gtk::Label(param_label));
+ _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));
+ _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));
+ _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));
+ _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));
+ _widget->pack_start(*pButton, true, true);
+ pButton->set_tooltip_text(_("Link to path in clipboard"));
+ }
+
+ _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;
+
+ Inkscape::UI::Tools::NodeTool *nt = dynamic_cast<Inkscape::UI::Tools::NodeTool*>(dt->event_context);
+ if (!nt) {
+ set_active_tool(dt, "Node");
+ nt = dynamic_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 {
+ param_write_to_repr(sp_svg_write_path(stored_pv).c_str());
+ }
+ } 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)
+{
+ unlink();
+ _pathvector = Geom::path_from_piecewise(newpath, LPE_CONVERSION_TOLERANCE);
+
+ if (write_to_svg) {
+ param_write_to_repr(sp_svg_write_path(_pathvector).c_str());
+
+ // 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)
+{
+ unlink();
+ if (newpath.empty()) {
+ param_set_and_write_default();
+ return;
+ } else {
+ _pathvector = newpath;
+ }
+ must_recalculate_pwd2 = true;
+
+ if (write_to_svg) {
+ param_write_to_repr(sp_svg_write_path(_pathvector).c_str());
+ } 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::unlink()
+{
+ if (href) {
+ ref.detach();
+ g_free(href);
+ href = nullptr;
+ }
+}
+
+void
+PathParam::linked_delete(SPObject */*deleted*/)
+{
+ quit_listening();
+ unlink();
+ set_new_value (_pathvector, true);
+}
+
+void PathParam::linked_modified(SPObject *linked_obj, guint flags)
+{
+ if (!param_effect->is_load || ownerlocator || !SP_ACTIVE_DESKTOP) {
+ 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)
+{
+ if (!_updating && flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG |
+ SP_OBJECT_CHILD_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG))
+ {
+ std::unique_ptr<SPCurve> curve;
+ if (auto shape = dynamic_cast<SPShape const *>(linked_obj)) {
+ if (_from_original_d) {
+ curve = SPCurve::copy(shape->curveForEdit());
+ } else {
+ curve = SPCurve::copy(shape->curve());
+ }
+ }
+
+ SPText *text = dynamic_cast<SPText *>(linked_obj);
+ if (text) {
+ bool hidden = text->isHidden();
+ if (hidden) {
+ if (_pathvector.empty()) {
+ text->setHidden(false);
+ curve = text->getNormalizedBpath();
+ text->setHidden(true);
+ } else {
+ if (curve == nullptr) {
+ curve.reset(new SPCurve());
+ }
+ curve->set_pathvector(_pathvector);
+ }
+ } else {
+ curve = text->getNormalizedBpath();
+ }
+ }
+
+ if (curve == nullptr) {
+ // curve invalid, set default value
+ _pathvector = sp_svg_read_pathv(defvalue);
+ } else {
+ _pathvector = curve->get_pathvector();
+ }
+
+ must_recalculate_pwd2 = true;
+ emit_changed();
+ 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
+ unlink();
+ SPItem * item = SP_ACTIVE_DESKTOP->getSelection()->singleItem();
+ std::string svgd_new;
+ 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.c_str();
+ }
+
+ param_write_to_repr(svgd);
+ 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(), _("Paste path parameter"), INKSCAPE_ICON("dialog-path-effects"));
+}
+
+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(), _("Link path parameter to path"), INKSCAPE_ICON("dialog-path-effects"));
+ }
+}
+
+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..a4b5eea
--- /dev/null
+++ b/src/live_effects/parameter/path.h
@@ -0,0 +1,125 @@
+// 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;
+ void reload();
+ Geom::Affine get_relative_affine();
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > const & get_pwd2();
+
+ Gtk::Widget * param_newWidget() override;
+ std::vector<SPObject *> param_get_satellites() 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 setUpdating(bool updating) {_updating = updating;};
+ 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 */
+ SPObject * getObject() const { if (ref.isAttached()) {return ref.getObject();} return nullptr;}
+ void paste_param_path(const char *svgd);
+ void on_paste_button_click();
+ void linkitem(Glib::ustring pathid);
+ ParamType paramType() const override { return ParamType::PATH; };
+
+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;
+ friend class LPEFillBetweenStrokes;
+ friend class LPEPatternAlongPath;
+ friend class LPEBendPath;
+ friend class LPECurveStitch;
+ friend class LPEAttachPath;
+ friend class LPEEnvelope;
+ friend class LPEBoundingBox;
+ friend class LPEInterpolate;
+ friend class LPEVonKoch;
+ 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 unlink();
+ 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;
+ bool _from_original_d;
+private:
+ bool _edit_button;
+ bool _copy_button;
+ bool _paste_button;
+ bool _link_button;
+ bool _updating = false;
+ PathParam(const PathParam&) = delete;
+ PathParam& operator=(const PathParam&) = delete;
+};
+
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/patharray.cpp b/src/live_effects/parameter/patharray.cpp
new file mode 100644
index 0000000..e2e81dc
--- /dev/null
+++ b/src/live_effects/parameter/patharray.cpp
@@ -0,0 +1,593 @@
+// 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/patharray.h"
+
+#include <2geom/coord.h>
+#include <2geom/point.h>
+#include <glibmm/i18n.h>
+#include <gtkmm/icontheme.h>
+#include <gtkmm/imagemenuitem.h>
+#include <gtkmm/scrolledwindow.h>
+#include <gtkmm/separatormenuitem.h>
+#include <gtkmm/widget.h>
+
+#include "display/curve.h"
+#include "document-undo.h"
+#include "document.h"
+#include "inkscape.h"
+#include "live_effects/effect.h"
+#include "live_effects/lpe-bspline.h"
+#include "live_effects/lpe-spiro.h"
+#include "live_effects/lpeobject-reference.h"
+#include "live_effects/lpeobject.h"
+#include "live_effects/parameter/patharray.h"
+#include "object/sp-shape.h"
+#include "object/sp-text.h"
+#include "object/uri.h"
+#include "originalpath.h"
+#include "svg/stringstream.h"
+#include "svg/svg.h"
+#include "ui/clipboard.h"
+#include "ui/icon-loader.h"
+#include "ui/icon-names.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class PathArrayParam::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;
+};
+
+PathArrayParam::PathArrayParam(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 = nullptr;
+ _scroller = nullptr;
+ _model = nullptr;
+ // refresh widgets on load to allow to remove the
+ // memory leak calling initui here
+ param_effect->refresh_widgets = true;
+ oncanvas_editable = true;
+ _from_original_d = false;
+ _allow_only_bspline_spiro = false;
+}
+
+PathArrayParam::~PathArrayParam()
+{
+ while (!_vector.empty()) {
+ PathAndDirectionAndVisible *w = _vector.back();
+ unlink(w);
+ }
+ delete _model;
+}
+
+void PathArrayParam::initui()
+{
+ SPDesktop * desktop = SP_ACTIVE_DESKTOP;
+ if (!desktop) {
+ return;
+ }
+ if (!_tree) {
+ _tree = manage(new Gtk::TreeView());
+ _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, &PathArrayParam::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, &PathArrayParam::on_visible_toggled));
+ col_visible->add_attribute(toggle_visible->property_active(), _model->_colVisible);
+
+ Gtk::CellRendererText *text_renderer = manage(new Gtk::CellRendererText());
+ int nameColNum = _tree->append_column(_("Name"), *text_renderer) - 1;
+ Gtk::TreeView::Column *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);
+ _scroller = Gtk::manage(new Gtk::ScrolledWindow());
+ //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);
+ }
+ param_readSVGValue(param_getSVGValue().c_str());
+}
+
+void PathArrayParam::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(), _("Link path parameter to path"), INKSCAPE_ICON("dialog-path-effects"));
+}
+
+void PathArrayParam::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(), _("Toggle path parameter visibility"), INKSCAPE_ICON("dialog-path-effects"));
+}
+
+void PathArrayParam::param_set_default() {}
+
+Gtk::Widget *PathArrayParam::param_newWidget()
+{
+
+ Gtk::Box* vbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
+ Gtk::Box* hbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
+ _tree = nullptr;
+ _model = nullptr;
+ _scroller = nullptr;
+ initui();
+ 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, &PathArrayParam::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, &PathArrayParam::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, &PathArrayParam::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, &PathArrayParam::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 PathArrayParam::_selectIndex(const Gtk::TreeIter &iter, int *i)
+{
+ if ((*i)-- <= 0) {
+ _tree->get_selection()->select(iter);
+ return true;
+ }
+ return false;
+}
+
+std::vector<SPObject *> PathArrayParam::param_get_satellites()
+{
+ std::vector<SPObject *> objs;
+ for (auto &iter : _vector) {
+ if (iter && iter->ref.isAttached()) {
+ SPObject *obj = iter->ref.getObject();
+ if (obj) {
+ objs.push_back(obj);
+ }
+ }
+ }
+ return objs;
+}
+
+void PathArrayParam::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(), _("Move path up"), INKSCAPE_ICON("dialog-path-effects"));
+
+ _store->foreach_iter(sigc::bind<int *>(sigc::mem_fun(*this, &PathArrayParam::_selectIndex), &i));
+ }
+}
+
+void PathArrayParam::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(), _("Move path down"), INKSCAPE_ICON("dialog-path-effects"));
+
+ _store->foreach_iter(sigc::bind<int *>(sigc::mem_fun(*this, &PathArrayParam::_selectIndex), &i));
+ }
+}
+
+void PathArrayParam::on_remove_button_click()
+{
+ Gtk::TreeModel::iterator iter = _tree->get_selection()->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ unlink(row[_model->_colObject]);
+
+ param_write_to_repr(param_getSVGValue().c_str());
+
+ DocumentUndo::done(param_effect->getSPDoc(), _("Remove path"), INKSCAPE_ICON("dialog-path-effects"));
+ }
+}
+
+void PathArrayParam::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(), _("Link patharray parameter to path"), INKSCAPE_ICON("dialog-path-effects"));
+}
+
+void PathArrayParam::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;
+ }
+ 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 PathArrayParam::start_listening()
+{
+ for (auto w : _vector) {
+ linked_changed(nullptr,w->ref.getObject(), w);
+ }
+}
+
+void PathArrayParam::linked_delete(SPObject * /*deleted*/, PathAndDirectionAndVisible * /*to*/)
+{
+ // unlink(to);
+
+ param_write_to_repr(param_getSVGValue().c_str());
+}
+
+bool PathArrayParam::_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 PathArrayParam::linked_changed(SPObject * /*old_obj*/, SPObject *new_obj, PathAndDirectionAndVisible *to)
+{
+ to->linked_delete_connection.disconnect();
+ to->linked_modified_connection.disconnect();
+
+ if (new_obj && SP_IS_ITEM(new_obj)) {
+ to->linked_delete_connection = new_obj->connectDelete(
+ sigc::bind<PathAndDirectionAndVisible *>(sigc::mem_fun(*this, &PathArrayParam::linked_delete), to));
+ to->linked_modified_connection = new_obj->connectModified(
+ sigc::bind<PathAndDirectionAndVisible *>(sigc::mem_fun(*this, &PathArrayParam::linked_modified), to));
+
+ linked_modified(new_obj, SP_OBJECT_MODIFIED_FLAG, to);
+ } else {
+ to->_pathvector = Geom::PathVector();
+ param_effect->getLPEObj()->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ if (_store.get()) {
+ _store->foreach_iter(
+ sigc::bind<PathAndDirectionAndVisible *>(sigc::mem_fun(*this, &PathArrayParam::_updateLink), to));
+ }
+ }
+}
+
+void PathArrayParam::setPathVector(SPObject *linked_obj, guint /*flags*/, PathAndDirectionAndVisible *to)
+{
+ if (!to) {
+ return;
+ }
+ std::unique_ptr<SPCurve> curve;
+ SPText *text = dynamic_cast<SPText *>(linked_obj);
+ if (auto shape = dynamic_cast<SPShape const *>(linked_obj)) {
+ SPLPEItem * lpe_item = SP_LPE_ITEM(linked_obj);
+ if (_from_original_d) {
+ curve = SPCurve::copy(shape->curveForEdit());
+ } else if (_allow_only_bspline_spiro && lpe_item && lpe_item->hasPathEffect()){
+ curve = SPCurve::copy(shape->curveForEdit());
+ 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.get(), 0, hp);
+ } else if (dynamic_cast<Inkscape::LivePathEffect::LPESpiro *>(lpe)) {
+ LivePathEffect::sp_spiro_do_effect(curve.get());
+ }
+ }
+ }
+ } else {
+ curve = SPCurve::copy(shape->curve());
+ }
+ } else if (text) {
+ bool hidden = text->isHidden();
+ if (hidden) {
+ if (to->_pathvector.empty()) {
+ text->setHidden(false);
+ curve = text->getNormalizedBpath();
+ text->setHidden(true);
+ } else {
+ if (curve == nullptr) {
+ curve = std::make_unique<SPCurve>();
+ }
+ curve->set_pathvector(to->_pathvector);
+ }
+ } else {
+ curve = text->getNormalizedBpath();
+ }
+ }
+
+ if (curve == nullptr) {
+ // curve invalid, set empty pathvector
+ to->_pathvector = Geom::PathVector();
+ } else {
+ to->_pathvector = curve->get_pathvector();
+ }
+
+}
+
+void PathArrayParam::linked_modified(SPObject *linked_obj, guint flags, PathAndDirectionAndVisible *to)
+{
+ if (!_updating && param_effect->getSPDoc()->isSensitive() && flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG |
+ SP_OBJECT_CHILD_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG))
+ {
+ if (!to) {
+ return;
+ }
+ setPathVector(linked_obj, flags, to);
+ param_effect->getLPEObj()->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ if (_store.get()) {
+ _store->foreach_iter(
+ sigc::bind<PathAndDirectionAndVisible *>(sigc::mem_fun(*this, &PathArrayParam::_updateLink), to));
+ }
+ }
+}
+
+bool PathArrayParam::param_readSVGValue(const gchar *strvalue)
+{
+ if (strvalue) {
+ while (!_vector.empty()) {
+ PathAndDirectionAndVisible *w = _vector.back();
+ unlink(w);
+ }
+
+ if (_store.get()) {
+ _store->clear();
+ }
+
+ gchar ** strarray = g_strsplit(strvalue, "|", 0);
+ bool write = false;
+ for (gchar ** iter = strarray; *iter != nullptr; iter++) {
+ if ((*iter)[0] == '#') {
+ gchar ** substrarray = g_strsplit(*iter, ",", 0);
+ SPObject * old_ref = param_effect->getSPDoc()->getObjectByHref(*substrarray);
+ if (old_ref) {
+ SPObject * successor = old_ref->_successor;
+ Glib::ustring id = *substrarray;
+ if (successor) {
+ id = successor->getId();
+ id.insert(id.begin(), '#');
+ write = true;
+ }
+ *(substrarray) = g_strdup(id.c_str());
+ }
+ 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, &PathArrayParam::linked_changed), w));
+ w->ref.attach(URI(w->href));
+
+ _vector.push_back(w);
+ if (_store.get()) {
+ 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);
+ if (write) {
+ auto full = param_getSVGValue();
+ param_write_to_repr(full.c_str());
+ }
+ return true;
+
+ }
+ return false;
+}
+
+Glib::ustring PathArrayParam::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 PathArrayParam::param_getDefaultSVGValue() const
+{
+ return "";
+}
+
+void PathArrayParam::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/patharray.h b/src/live_effects/parameter/patharray.h
new file mode 100644
index 0000000..a85d0f9
--- /dev/null
+++ b/src/live_effects/parameter/patharray.h
@@ -0,0 +1,130 @@
+// 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;
+};
+
+class PathArrayParam : public Parameter
+{
+public:
+ class ModelColumns;
+
+ PathArrayParam(const Glib::ustring &label, const Glib::ustring &tip, const Glib::ustring &key,
+ Inkscape::UI::Widget::Registry *wr, Effect *effect);
+
+ ~PathArrayParam() override;
+
+ Gtk::Widget * param_newWidget() override;
+ std::vector<SPObject *> param_get_satellites() 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();};
+ void setUpdating(bool updating) {_updating = updating;};
+ std::vector<PathAndDirectionAndVisible*> _vector;
+ ParamType paramType() const override { return ParamType::PATH_ARRAY; };
+protected:
+ friend class LPEFillBetweenMany;
+ bool _updateLink(const Gtk::TreeIter& iter, PathAndDirectionAndVisible* pd);
+ bool _selectIndex(const Gtk::TreeIter& iter, int* i);
+ void unlink(PathAndDirectionAndVisible* to);
+ void start_listening();
+ 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_delete(SPObject *deleted, PathAndDirectionAndVisible* to);
+
+ ModelColumns *_model;
+ Glib::RefPtr<Gtk::TreeStore> _store;
+ Gtk::TreeView *_tree;
+ 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;
+ bool _updating = false;
+ void update();
+ void initui();
+ PathArrayParam(const PathArrayParam &) = delete;
+ PathArrayParam &operator=(const PathArrayParam &) = 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/point.cpp b/src/live_effects/parameter/point.cpp
new file mode 100644
index 0000000..f99a098
--- /dev/null
+++ b/src/live_effects/parameter/point.cpp
@@ -0,0 +1,270 @@
+// 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 "point.h"
+
+#include "inkscape.h"
+
+#include "live_effects/effect.h"
+#include "svg/stringstream.h"
+#include "svg/svg.h"
+#include "ui/icon-names.h"
+#include "ui/knot/knot-holder.h"
+#include "ui/knot/knot-holder-entity.h"
+#include "ui/widget/point.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)
+{
+ 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(_("Change point parameter"), INKSCAPE_ICON("dialog-path-effects"));
+ pointwdg->signal_button_release_event().connect(sigc::mem_fun (*this, &PointParam::on_button_release));
+
+ Gtk::Box * hbox = Gtk::manage( new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL) );
+ hbox->pack_start(*pointwdg, true, true);
+ 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(Inkscape::CanvasItemCtrlShape shape,
+ Inkscape::CanvasItemCtrlMode 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_setValue(*pparam, true);
+ pparam->param_effect->refresh_widgets = true;
+}
+
+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::CANVAS_ITEM_CTRL_TYPE_LPE, "LPE:Point",
+ handleTip(), 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..0f45c62
--- /dev/null
+++ b/src/live_effects/parameter/point.h
@@ -0,0 +1,84 @@
+// 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 "display/control/canvas-item-enums.h"
+#include "live_effects/parameter/parameter.h"
+#include "ui/widget/registered-widget.h"
+
+class KnotHolder;
+class KnotHolderEntity;
+
+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(Inkscape::CanvasItemCtrlShape shape,
+ Inkscape::CanvasItemCtrlMode mode,
+ guint32 color);
+
+ bool providesKnotHolderEntities() const override { return true; }
+ void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) override;
+ ParamType paramType() const override { return ParamType::POINT; };
+ 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 = nullptr;
+ Inkscape::CanvasItemCtrlShape knot_shape = Inkscape::CANVAS_ITEM_CTRL_SHAPE_DIAMOND;
+ Inkscape::CanvasItemCtrlMode knot_mode = Inkscape::CANVAS_ITEM_CTRL_MODE_XOR;
+ guint32 knot_color = 0xffffff00;
+ 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..b9e1d6d
--- /dev/null
+++ b/src/live_effects/parameter/powerstrokepointarray.cpp
@@ -0,0 +1,343 @@
+// 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 "powerstrokepointarray.h"
+
+#include <2geom/sbasis-2d.h>
+#include <2geom/bezier-to-sbasis.h>
+#include <2geom/piecewise.h>
+#include <2geom/sbasis-geometric.h>
+
+#include "ui/dialog/lpe-powerstroke-properties.h"
+
+#include "ui/knot/knot-holder.h"
+
+#include "live_effects/effect.h"
+#include "live_effects/lpe-powerstroke.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(Inkscape::CANVAS_ITEM_CTRL_SHAPE_DIAMOND)
+ , knot_mode(Inkscape::CANVAS_ITEM_CTRL_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(Inkscape::CanvasItemCtrlShape shape,
+ Inkscape::CanvasItemCtrlMode 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;
+ }
+ static gint prev_index = 0;
+ Piecewise<D2<SBasis> > const & pwd2 = _pparam->get_pwd2();
+ Piecewise<D2<SBasis> > pwd2port = _pparam->get_pwd2();
+ Geom::Point s = snap_knot_position(p, state);
+ double t2 = 0;
+ LPEPowerStroke *ps = dynamic_cast<LPEPowerStroke *>(_pparam->param_effect);
+ if (ps && ps->not_jump) {
+ s = p;
+ t2 = _pparam->_vector.at(_index)[Geom::X];
+ Geom::PathVector pathv = path_from_piecewise(pwd2port, 0.001);
+ pathv[0] = pathv[0].portion(std::max(std::floor(t2) - 1, 0.0), std::min(std::ceil(t2) + 1, (double)pathv[0].size()));
+ pwd2port = paths_to_pw(pathv);
+ }
+ /// @todo how about item transforms???
+
+ Piecewise<D2<SBasis> > const & n = _pparam->get_pwd2_normal();
+ gint index = std::floor(nearest_time(s, pwd2));
+ bool bigjump = false;
+ if (std::abs(prev_index - index) > 1) {
+ bigjump = true;
+ } else {
+ prev_index = index;
+ }
+ double t = nearest_time(s, pwd2port);
+ double offset = 0.0;
+ if (ps && ps->not_jump) {
+ double tpos = t + std::max(std::floor(t2) - 1, 0.0);
+ double prevpos = _pparam->_vector.at(_index)[Geom::X];
+ if (bigjump) {
+ tpos = prevpos;
+ }
+ offset = dot(s - pwd2.valueAt(tpos), n.valueAt(tpos));
+ _pparam->_vector.at(_index) = Geom::Point(tpos, offset/_pparam->_scale_width);
+ } else {
+ 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(Geom::infinity(), Geom::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(Geom::infinity(), Geom::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::CANVAS_ITEM_CTRL_TYPE_LPE, "LPE:PowerStroke",
+ _("<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_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::CANVAS_ITEM_CTRL_TYPE_LPE, "LPE:PowerStroke",
+ _("<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_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..73d1686
--- /dev/null
+++ b/src/live_effects/parameter/powerstrokepointarray.h
@@ -0,0 +1,95 @@
+// 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 "ui/knot/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(Inkscape::CanvasItemCtrlShape shape,
+ Inkscape::CanvasItemCtrlMode 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;
+ ParamType paramType() const override { return ParamType::POWERSTROKE_POINT_ARRAY; };
+ friend class PowerStrokePointArrayParamKnotHolderEntity;
+
+private:
+ Inkscape::CanvasItemCtrlShape knot_shape = Inkscape::CANVAS_ITEM_CTRL_SHAPE_DIAMOND;
+ Inkscape::CanvasItemCtrlMode knot_mode = Inkscape::CANVAS_ITEM_CTRL_MODE_XOR;
+ 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..42f5065
--- /dev/null
+++ b/src/live_effects/parameter/random.cpp
@@ -0,0 +1,227 @@
+// 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 "random.h"
+
+#include <glibmm/i18n.h>
+
+#include "live_effects/effect.h"
+#include "svg/stringstream.h"
+#include "svg/svg.h"
+#include "ui/icon-names.h"
+#include "ui/widget/random.h"
+#include "ui/widget/registered-widget.h"
+
+#define noLPERANDOMPARAM_DEBUG
+
+/* 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
+
+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, bool randomsign)
+ : 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;
+ _randomsign = randomsign;
+}
+
+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);
+ // we reach maximum value so randomize over to fix duple in next cycle
+ Glib::ustring version = param_effect->lpeversion.param_getSVGValue();
+ if (startseed == RAND_m - 1 && ((
+ effectType() != ROUGH_HATCHES &&
+ effectType() != ROUGHEN) ||
+ version >= "1.2"))
+ {
+ startseed = rand() * startseed;
+ }
+ 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(_("Change random parameter"), INKSCAPE_ICON("dialog-path-effects"));
+
+ return dynamic_cast<Gtk::Widget *> (regrandom);
+}
+
+bool RandomParam::on_button_release(GdkEventButton* button_event) {
+ param_effect->refresh_widgets = true;
+ return false;
+}
+
+RandomParam::operator gdouble()
+{
+ if (_randomsign) {
+ return (rand() * value) - (rand() * value);
+ } else {
+ return rand() * value;
+ }
+};
+
+
+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..b727885
--- /dev/null
+++ b/src/live_effects/parameter/random.h
@@ -0,0 +1,76 @@
+// 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,
+ bool randomsign = false);
+ ~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;
+ void param_set_randomsign(bool randomsign) {_randomsign = randomsign;};
+ Gtk::Widget * param_newWidget() override;
+ double param_get_random_number() { return rand(); };
+ 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; } ;
+ ParamType paramType() const override { return ParamType::RANDOM; };
+
+protected:
+ long startseed;
+ long seed;
+ long defseed;
+
+ gdouble value;
+ gdouble min;
+ gdouble max;
+ bool integer;
+ bool _randomsign;
+ 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/satellite-reference.cpp b/src/live_effects/parameter/satellite-reference.cpp
new file mode 100644
index 0000000..d86be33
--- /dev/null
+++ b/src/live_effects/parameter/satellite-reference.cpp
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "satellite-reference.h"
+
+#include "document.h"
+#include "live_effects/lpeobject.h"
+#include "object/sp-item-group.h"
+#include "object/sp-lpe-item.h"
+#include "object/sp-shape.h"
+#include "object/sp-text.h"
+#include "object/uri-references.h"
+
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+bool SatelliteReference::_acceptObject(SPObject *const obj) const
+{
+ if (SP_IS_SHAPE(obj) || SP_IS_TEXT(obj) || SP_IS_GROUP(obj)) {
+ /* Refuse references to lpeobject */
+ SPObject *owner = getOwner();
+ if (obj == owner) {
+ return false;
+ }
+ if (!dynamic_cast<LivePathEffectObject *>(owner)) {
+ return false;
+ }
+ 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:fileencoding=utf-8:textwidth=99 :
diff --git a/src/live_effects/parameter/satellite-reference.h b/src/live_effects/parameter/satellite-reference.h
new file mode 100644
index 0000000..a9f31a9
--- /dev/null
+++ b/src/live_effects/parameter/satellite-reference.h
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#ifndef SEEN_SP_SATELLITE_REFERENCE_H
+#define SEEN_SP_SATELLITE_REFERENCE_H
+
+#include "object/sp-item.h"
+#include "object/uri-references.h"
+/**
+ * The reference corresponding to a satelite in a LivePathEffectObject.
+ */
+namespace Inkscape {
+namespace LivePathEffect {
+
+class SatelliteReference : public Inkscape::URIReference
+{
+public:
+ SatelliteReference(SPObject *owner, bool hasactive = false)
+ : URIReference(owner)
+ , _hasactive(hasactive)
+ , _active(true)
+ {
+ }
+
+ bool getHasActive() const { return _hasactive; }
+ bool getActive() const { return _active; }
+ void setActive(bool active) { _active = active; }
+
+protected:
+ bool _acceptObject(SPObject *obj) const override;
+
+private:
+ bool _active;
+ bool _hasactive;
+};
+
+} // namespace LivePathEffect
+} // namespace Inkscape
+
+#endif /* !SEEN_SP_SATELLITE_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:fileencoding=utf-8:textwidth=99 : \ No newline at end of file
diff --git a/src/live_effects/parameter/satellite.cpp b/src/live_effects/parameter/satellite.cpp
new file mode 100644
index 0000000..989a73a
--- /dev/null
+++ b/src/live_effects/parameter/satellite.cpp
@@ -0,0 +1,295 @@
+// 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/satellite.h"
+
+#include "bad-uri-exception.h"
+#include "desktop.h"
+#include "enums.h"
+#include "inkscape.h"
+#include "live_effects/effect.h"
+#include "live_effects/lpeobject.h"
+#include "message-stack.h"
+#include "selection-chemistry.h"
+#include "svg/svg.h"
+#include "ui/icon-loader.h"
+#include "ui/widget/point.h"
+#include "xml/repr.h"
+// clipboard support
+#include "ui/clipboard.h"
+// required for linking to other paths
+#include <glibmm/i18n.h>
+
+#include "bad-uri-exception.h"
+#include "object/sp-item.h"
+#include "object/uri.h"
+#include "ui/icon-names.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+SatelliteParam::SatelliteParam(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)
+ , lperef(std::make_shared<SatelliteReference>(param_effect->getLPEObj(), false))
+ , last_transform(Geom::identity())
+{}
+
+SatelliteParam::~SatelliteParam()
+{
+ quit_listening();
+}
+
+std::vector<SPObject *> SatelliteParam::param_get_satellites()
+{
+ std::vector<SPObject *> objs;
+ // we reload connexions in case are lost for example item recreation on ungroup
+ if (!linked_transformed_connection) {
+ write_to_SVG();
+ }
+
+ SPObject * linked_obj = lperef->getObject();
+ if (linked_obj) {
+ objs.push_back(linked_obj);
+ }
+ return objs;
+}
+
+bool SatelliteParam::param_readSVGValue(const gchar *strvalue)
+{
+ if (strvalue) {
+ bool write = false;
+ auto lpeitems = param_effect->getCurrrentLPEItems();
+ Glib::ustring id_tmp;
+ if (!lpeitems.size() && !param_effect->is_applied && !param_effect->getSPDoc()->isSeeking()) {
+ SPObject * old_ref = param_effect->getSPDoc()->getObjectByHref(strvalue);
+ if (old_ref) {
+ SPObject * successor = old_ref->_successor;
+ // cast to effect is not possible now
+ if (!g_strcmp0("clone_original", param_effect->getLPEObj()->getAttribute("effect"))) {
+ id_tmp = strvalue;
+ }
+ if (successor) {
+ id_tmp = successor->getId();
+ id_tmp.insert(id_tmp.begin(), '#');
+ write = true;
+ }
+ strvalue = id_tmp.c_str();
+ }
+ }
+ SPObject *old_ref = lperef->getObject();
+ if (old_ref) {
+ unlink();
+ }
+ if (strvalue[0] == '#') {
+ try {
+ lperef->attach(Inkscape::URI(g_strdup(strvalue)));
+ // lp:1299948
+ SPObject *new_ref = lperef->getObject();
+ if (new_ref) {
+ linked_changed(old_ref, new_ref);
+ // linked_modified(new_ref, SP_OBJECT_STYLESHEET_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());
+ lperef->detach();
+ }
+ } else if (!lpeitems.size() && !param_effect->is_applied && !param_effect->getSPDoc()->isSeeking()) {
+ param_write_to_repr("");
+ }
+ if (write) {
+ auto full = param_getSVGValue();
+ param_write_to_repr(full.c_str());
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool SatelliteParam::linksToItem() const
+{
+ return lperef->isAttached();
+}
+
+SPObject *SatelliteParam::getObject() const
+{
+ return lperef->isAttached() ? lperef->getObject() : nullptr;
+}
+
+Glib::ustring SatelliteParam::param_getSVGValue() const
+{
+ if (lperef->getURI()) {
+ return lperef->getURI()->str();
+ }
+ return "";
+}
+
+Glib::ustring SatelliteParam::param_getDefaultSVGValue() const
+{
+ return "";
+}
+
+void SatelliteParam::param_set_default()
+{
+ param_readSVGValue("");
+}
+
+void SatelliteParam::unlink()
+{
+ quit_listening();
+ if (linksToItem()) {
+ lperef->detach();
+ }
+}
+
+void SatelliteParam::link(Glib::ustring itemid)
+{
+ if (itemid.empty()) {
+ return;
+ }
+ auto *document = param_effect->getSPDoc();
+ SPObject *object = document->getObjectById(itemid);
+
+ if (object && object != getObject()) {
+ itemid.insert(itemid.begin(), '#');
+ param_write_to_repr(itemid.c_str());
+ } else {
+ param_write_to_repr("");
+ }
+ DocumentUndo::done(document, _("Link item parameter to path"), "");
+}
+
+// SIGNALS
+
+void SatelliteParam::start_listening(SPObject *to)
+{
+ if (!to) {
+ return;
+ }
+ quit_listening();
+ linked_changed_connection = lperef->changedSignal().connect(sigc::mem_fun(*this, &SatelliteParam::linked_changed));
+ SPItem *item = dynamic_cast<SPItem *>(to);
+ if (item) {
+ linked_released_connection = item->connectRelease(sigc::mem_fun(*this, &SatelliteParam::linked_released));
+ linked_modified_connection = item->connectModified(sigc::mem_fun(*this, &SatelliteParam::linked_modified));
+ linked_transformed_connection =
+ item->connectTransformed(sigc::mem_fun(*this, &SatelliteParam::linked_transformed));
+ if (!param_effect->is_load) {
+ linked_modified(item, SP_OBJECT_MODIFIED_FLAG);
+ }
+ }
+}
+
+void SatelliteParam::quit_listening()
+{
+ if (linked_changed_connection) {
+ linked_changed_connection.disconnect();
+ }
+ if (linked_released_connection) {
+ linked_released_connection.disconnect();
+ }
+ if (linked_modified_connection) {
+ linked_modified_connection.disconnect();
+ }
+ if (linked_transformed_connection) {
+ linked_transformed_connection.disconnect();
+ }
+}
+
+void SatelliteParam::linked_changed(SPObject *old_obj, SPObject *new_obj)
+{
+ quit_listening();
+ if (new_obj) {
+ start_listening(new_obj);
+ }
+}
+
+void SatelliteParam::linked_released(SPObject *released)
+{
+ unlink();
+ param_effect->processObjects(LPE_UPDATE);
+}
+
+
+void SatelliteParam::linked_modified(SPObject *linked_obj, guint flags)
+{
+ if (!_updating && (!param_effect->is_load || ownerlocator || !SP_ACTIVE_DESKTOP) &&
+ flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG |
+ SP_OBJECT_CHILD_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG))
+ {
+ param_effect->getLPEObj()->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ last_transform = Geom::identity();
+ if (effectType() != CLONE_ORIGINAL) {
+ update_satellites();
+ }
+ }
+}
+
+void SatelliteParam::linked_transformed(Geom::Affine const *rel_transf, SPItem *moved_item)
+{
+ if (!_updating) {
+ update_satellites();
+ }
+}
+
+// UI
+
+void SatelliteParam::addCanvasIndicators(SPLPEItem const * /*lpeitem*/, std::vector<Geom::PathVector> &hp_vec) {}
+
+void SatelliteParam::on_link_button_click()
+{
+ Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get();
+ // here prevent item is reseted transform on link
+ if (effectType() == CLONE_ORIGINAL) {
+ param_effect->is_load = false;
+ }
+ auto itemid = cm->getFirstObjectID();
+ if (itemid.empty()) {
+ return;
+ }
+
+ link(itemid);
+}
+
+Gtk::Widget *SatelliteParam::param_newWidget()
+{
+ Gtk::Box *_widget = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
+ 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));
+ _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, &SatelliteParam::on_link_button_click));
+ _widget->pack_start(*pButton, true, true);
+ pButton->set_tooltip_text(_("Link to item on clipboard"));
+
+ _widget->show_all_children();
+
+ return dynamic_cast<Gtk::Widget *>(_widget);
+}
+
+} /* 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/satellite.h b/src/live_effects/parameter/satellite.h
new file mode 100644
index 0000000..d3faf29
--- /dev/null
+++ b/src/live_effects/parameter/satellite.h
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_SATELLITE_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_SATELLITE_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 <sigc++/sigc++.h>
+
+#include "live_effects/parameter/parameter.h"
+#include "live_effects/parameter/satellite-reference.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+class LPECloneOriginal;
+
+class SatelliteParam : public Parameter
+{
+public:
+ SatelliteParam(const Glib::ustring &label, const Glib::ustring &tip, const Glib::ustring &key,
+ Inkscape::UI::Widget::Registry *wr, Effect *effect);
+ ~SatelliteParam() 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 setUpdating(bool updating) { _updating = updating; }
+ bool getUpdating() const { return _updating; }
+ bool linksToItem() const;
+ SPObject *getObject() const;
+ // UI
+ Gtk::Widget *param_newWidget() override;
+ void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec) override;
+ void on_link_button_click();
+ friend class LPEBool;
+ friend class LPECloneOriginal;
+ friend class Effect;
+ Geom::Affine last_transform;
+ bool isConnected() {return !(!linked_changed_connection);}
+ void start_listening(SPObject *to);
+ void unlink();
+ ParamType paramType() const override { return ParamType::SATELLITE; };
+protected:
+ void link(Glib::ustring itemid);
+ void quit_listening();
+ void linked_released(SPObject *released_item);
+ void linked_deleted(SPObject *deleted_item);
+ void linked_transformed(Geom::Affine const *rel_transf, SPItem *moved_item);
+ void linked_modified(SPObject *linked_obj, guint flags);
+ void linked_changed(SPObject *old_obj, SPObject *new_obj);
+
+ std::shared_ptr<SatelliteReference> lperef;
+
+private:
+ std::vector<SPObject *> param_get_satellites() override;
+ bool _updating = false;
+ sigc::connection linked_released_connection;
+ sigc::connection linked_modified_connection;
+ sigc::connection linked_transformed_connection;
+ sigc::connection linked_changed_connection;
+ SatelliteParam(const SatelliteParam &) = delete;
+ SatelliteParam &operator=(const SatelliteParam &) = delete;
+};
+
+} // namespace LivePathEffect
+
+} // namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/satellitearray.cpp b/src/live_effects/parameter/satellitearray.cpp
new file mode 100644
index 0000000..6516e1f
--- /dev/null
+++ b/src/live_effects/parameter/satellitearray.cpp
@@ -0,0 +1,506 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) Jabiertxof <jabier.arraiza@marker.es>
+ * this class handle satellites of a lpe as a parameter
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/parameter/satellitearray.h"
+#include "live_effects/effect.h"
+#include "live_effects/lpeobject.h"
+#include "inkscape.h"
+#include "ui/clipboard.h"
+#include "ui/icon-loader.h"
+#include <glibmm/i18n.h>
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class SatelliteArrayParam::ModelColumns : public Gtk::TreeModel::ColumnRecord
+{
+public:
+ ModelColumns()
+ {
+ add(_colObject);
+ add(_colLabel);
+ add(_colActive);
+ }
+ ~ModelColumns() override = default;
+
+ Gtk::TreeModelColumn<Glib::ustring> _colObject;
+ Gtk::TreeModelColumn<Glib::ustring> _colLabel;
+ Gtk::TreeModelColumn<bool> _colActive;
+};
+SatelliteArrayParam::SatelliteArrayParam(const Glib::ustring &label, const Glib::ustring &tip, const Glib::ustring &key,
+ Inkscape::UI::Widget::Registry *wr, Effect *effect, bool visible)
+ : ArrayParam<std::shared_ptr<SatelliteReference>>(label, tip, key, wr, effect)
+ , _visible(visible)
+{
+ param_widget_is_visible(_visible);
+ if (_visible) {
+ _tree = nullptr;
+ _scroller = nullptr;
+ _model = nullptr;
+ initui();
+ oncanvas_editable = true;
+ }
+}
+
+SatelliteArrayParam::~SatelliteArrayParam()
+{
+ _vector.clear();
+ if (_store.get() && _model) {
+ delete _model;
+ }
+ quit_listening();
+}
+
+void SatelliteArrayParam::initui()
+{
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (!desktop) {
+ return;
+ }
+ if (!_tree) {
+ _tree = manage(new Gtk::TreeView());
+ _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, &SatelliteArrayParam::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 = manage(new Gtk::ScrolledWindow());
+ _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);
+ }
+ param_readSVGValue(param_getSVGValue().c_str());
+}
+
+void SatelliteArrayParam::start_listening()
+{
+ quit_listening();
+ for (auto ref : _vector) {
+ if (ref && ref->isAttached()) {
+ SPItem *item = dynamic_cast<SPItem *>(ref->getObject());
+ if (item) {
+ linked_connections.emplace_back(item->connectRelease(
+ sigc::hide(sigc::mem_fun(*this, &SatelliteArrayParam::updatesignal))));
+ linked_connections.emplace_back(item->connectModified(
+ sigc::mem_fun(*this, &SatelliteArrayParam::linked_modified)));
+ linked_connections.emplace_back(item->connectTransformed(
+ sigc::hide(sigc::hide(sigc::mem_fun(*this, &SatelliteArrayParam::updatesignal)))));
+ linked_connections.emplace_back(ref->changedSignal().connect(
+ sigc::hide(sigc::hide(sigc::mem_fun(*this, &SatelliteArrayParam::updatesignal)))));
+ }
+ }
+ }
+}
+
+void SatelliteArrayParam::linked_modified(SPObject *linked_obj, guint flags) {
+ if (!param_effect->is_load && param_effect->_lpe_action == LPE_NONE &&
+ flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG |
+ SP_OBJECT_VIEWPORT_MODIFIED_FLAG))
+ {
+ param_effect->processObjects(LPE_UPDATE);
+ }
+}
+
+void SatelliteArrayParam::updatesignal()
+{
+ if (!param_effect->is_load && param_effect->_lpe_action == LPE_NONE) {
+ param_effect->processObjects(LPE_UPDATE);
+ }
+}
+
+void SatelliteArrayParam::quit_listening()
+{
+ for (auto connexion : linked_connections) {
+ if (connexion) {
+ connexion.disconnect();
+ }
+ }
+ linked_connections.clear();
+};
+
+void SatelliteArrayParam::on_active_toggled(const Glib::ustring &item)
+{
+ int i = 0;
+ for (auto w : _vector) {
+ if (w && w->isAttached() && w->getObject()) {
+ Gtk::TreeModel::Row row = *_store->get_iter(Glib::ustring::format(i));
+ Glib::ustring id = w->getObject()->getId() ? w->getObject()->getId() : "";
+ if (id == row[_model->_colObject]) {
+ row[_model->_colActive] = !row[_model->_colActive];
+ w->setActive(row[_model->_colActive]);
+ i++;
+ break;
+ }
+ }
+ }
+ auto full = param_getSVGValue();
+ param_write_to_repr(full.c_str());
+ DocumentUndo::done(param_effect->getSPDoc(), _("Active switched"), "");
+}
+
+bool SatelliteArrayParam::param_readSVGValue(const gchar *strvalue)
+{
+ if (strvalue) {
+ bool changed = !linked_connections.size() || !param_effect->is_load;
+ if (!ArrayParam::param_readSVGValue(strvalue)) {
+ return false;
+ }
+ auto lpeitems = param_effect->getCurrrentLPEItems();
+ if (!lpeitems.size() && !param_effect->is_applied && !param_effect->getSPDoc()->isSeeking()) {
+ size_t pos = 0;
+ for (auto w : _vector) {
+ if (w) {
+ SPObject * tmp = w->getObject();
+ if (tmp) {
+ SPObject * successor = tmp->_successor;
+ unlink(tmp);
+ if (successor) {
+ link(successor,pos);
+ }
+ }
+ }
+ pos ++;
+ }
+ auto full = param_getSVGValue();
+ param_write_to_repr(full.c_str());
+ update_satellites(false);
+ }
+ if (_store.get()) {
+ _store->clear();
+ for (auto w : _vector) {
+ if (w) {
+ Gtk::TreeModel::iterator iter = _store->append();
+ Gtk::TreeModel::Row row = *iter;
+ if (auto obj = w->getObject()) {
+ row[_model->_colObject] = Glib::ustring(obj->getId());
+ row[_model->_colLabel] = obj->label() ? obj->label() : obj->getId();
+ row[_model->_colActive] = w->getActive();
+ }
+ }
+ }
+ }
+ if (changed) {
+ start_listening();
+ }
+ return true;
+ }
+ return false;
+}
+
+bool SatelliteArrayParam::_selectIndex(const Gtk::TreeIter &iter, int *i)
+{
+ if ((*i)-- <= 0) {
+ _tree->get_selection()->select(iter);
+ return true;
+ }
+ return false;
+}
+
+void SatelliteArrayParam::on_up_button_click()
+{
+ Gtk::TreeModel::iterator iter = _tree->get_selection()->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row rowselected = *iter;
+ int i = 0;
+ for (auto w : _vector) {
+ if (w && w->isAttached() && w->getObject()) {
+ Gtk::TreeModel::Row row = *_store->get_iter(Glib::ustring::format(i));
+ if (rowselected == row && i > 0) {
+ std::swap(_vector[i],_vector[i-1]);
+ i--;
+ break;
+ }
+ i++;
+ }
+ }
+ auto full = param_getSVGValue();
+ param_write_to_repr(full.c_str());
+
+ DocumentUndo::done(param_effect->getSPDoc(), _("Move item up"), "");
+
+ _store->foreach_iter(sigc::bind<int *>(sigc::mem_fun(*this, &SatelliteArrayParam::_selectIndex), &i));
+ }
+}
+
+void SatelliteArrayParam::on_down_button_click()
+{
+ Gtk::TreeModel::iterator iter = _tree->get_selection()->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row rowselected = *iter;
+ int i = 0;
+ for (auto w : _vector) {
+ if (w && w->isAttached() && w->getObject()) {
+ Gtk::TreeModel::Row row = *_store->get_iter(Glib::ustring::format(i));
+ if (rowselected == row && i < _vector.size() - 1) {
+ std::swap(_vector[i],_vector[i+1]);
+ i++;
+ break;
+ }
+ i++;
+ }
+ }
+ auto full = param_getSVGValue();
+ param_write_to_repr(full.c_str());
+
+ DocumentUndo::done(param_effect->getSPDoc(), _("Move item down"), "");
+
+ _store->foreach_iter(sigc::bind<int *>(sigc::mem_fun(*this, &SatelliteArrayParam::_selectIndex), &i));
+ }
+}
+
+void SatelliteArrayParam::on_remove_button_click()
+{
+ Gtk::TreeModel::iterator iter = _tree->get_selection()->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ unlink(param_effect->getSPDoc()->getObjectById(row[_model->_colObject]));
+
+ auto full = param_getSVGValue();
+ param_write_to_repr(full.c_str());
+
+ DocumentUndo::done(param_effect->getSPDoc(), _("Remove item"), "");
+ }
+}
+
+void SatelliteArrayParam::on_link_button_click()
+{
+ Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get();
+ std::vector<Glib::ustring> itemsid;
+ // Here we ignore auto clipboard group wrapper
+ std::vector<Glib::ustring> itemsids = cm->getElementsOfType(SP_ACTIVE_DESKTOP, "*", 2);
+ std::vector<Glib::ustring> containers = cm->getElementsOfType(SP_ACTIVE_DESKTOP, "*", 1);
+ for (auto item : itemsids) {
+ bool cont = false;
+ for (auto citems : containers) {
+ if (citems == item) {
+ cont = true;
+ }
+ }
+ if (cont == false) {
+ itemsid.push_back(item);
+ }
+ }
+ if (itemsid.empty()) {
+ return;
+ }
+ auto hreflist = param_effect->getLPEObj()->hrefList;
+ if (hreflist.size()) {
+ SPLPEItem *sp_lpe_item = dynamic_cast<SPLPEItem *>(*hreflist.begin());
+ if (sp_lpe_item) {
+ for (auto itemid : itemsid) {
+ SPObject *added = param_effect->getSPDoc()->getObjectById(itemid);
+ if (added && sp_lpe_item != added) {
+ itemid.insert(itemid.begin(), '#');
+ std::shared_ptr<SatelliteReference> satellitereference =
+ std::make_shared<SatelliteReference>(param_effect->getLPEObj(), _visible);
+ try {
+ satellitereference->attach(Inkscape::URI(itemid.c_str()));
+ satellitereference->setActive(true);
+ _vector.push_back(satellitereference);
+ } catch (Inkscape::BadURIException &e) {
+ g_warning("%s", e.what());
+ satellitereference->detach();
+ }
+ }
+ }
+ }
+ }
+ write_to_SVG();
+ DocumentUndo::done(param_effect->getSPDoc(), _("Link itemarray parameter to item"), "");
+}
+
+Gtk::Widget *SatelliteArrayParam::param_newWidget()
+{
+ if (!_visible) {
+ return nullptr;
+ }
+ Gtk::Box *vbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
+ Gtk::Box *hbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
+ _tree = nullptr;
+ _scroller = nullptr;
+ _model = nullptr;
+ initui();
+ 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, &SatelliteArrayParam::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, &SatelliteArrayParam::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, &SatelliteArrayParam::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, &SatelliteArrayParam::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;
+}
+
+std::vector<SPObject *> SatelliteArrayParam::param_get_satellites()
+{
+ std::vector<SPObject *> objs;
+ for (auto &iter : _vector) {
+ if (iter && iter->isAttached()) {
+ SPObject *obj = iter->getObject();
+ if (obj) {
+ objs.push_back(obj);
+ }
+ }
+ }
+ return objs;
+}
+
+/*
+ * This function link a satellite writing into XML directly
+ * @param obj: object to link
+ * @param obj: position in vector
+ */
+void SatelliteArrayParam::link(SPObject *obj, size_t pos)
+{
+ if (obj && obj->getId()) {
+ Glib::ustring itemid = "#";
+ itemid += obj->getId();
+ std::shared_ptr<SatelliteReference> satellitereference =
+ std::make_shared<SatelliteReference>(param_effect->getLPEObj(), _visible);
+ try {
+ satellitereference->attach(Inkscape::URI(itemid.c_str()));
+ if (_visible) {
+ satellitereference->setActive(true);
+ }
+ if (_vector.size() == pos || pos == Glib::ustring::npos) {
+ _vector.push_back(satellitereference);
+ } else {
+ _vector[pos] = satellitereference;
+ }
+ } catch (Inkscape::BadURIException &e) {
+ g_warning("%s", e.what());
+ satellitereference->detach();
+ }
+ }
+}
+
+void SatelliteArrayParam::unlink(SPObject *obj)
+{
+ if (!obj) {
+ return;
+ }
+ gint pos = -1;
+ for (auto w : _vector) {
+ pos++;
+ if (w) {
+ if (w->getObject() == obj) {
+ break;
+ }
+ }
+ }
+ if (pos != -1) {
+ _vector.erase(_vector.begin() + pos);
+ _vector.insert(_vector.begin() + pos, nullptr);
+ }
+}
+
+void SatelliteArrayParam::unlink(std::shared_ptr<SatelliteReference> to)
+{
+ if (!to) {
+ return;
+ }
+ gint pos = -1;
+ for (auto w : _vector) {
+ pos++;
+ if (w) {
+ if (w->getObject() == to->getObject()) {
+ break;
+ }
+
+ }
+ }
+ if (pos != -1) {
+ _vector.erase(_vector.begin() + pos);
+ _vector.insert(_vector.begin() + pos, nullptr);
+ }
+}
+
+void SatelliteArrayParam::clear()
+{
+ _vector.clear();
+}
+
+} /* 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/satellitearray.h b/src/live_effects/parameter/satellitearray.h
new file mode 100644
index 0000000..0a8534d
--- /dev/null
+++ b/src/live_effects/parameter/satellitearray.h
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_SATELLITEARRAY_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_SATELLITEARRAY_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <gtkmm/scrolledwindow.h>
+#include <gtkmm/treestore.h>
+#include <sigc++/sigc++.h>
+
+#include "live_effects/lpeobject.h"
+#include "live_effects/parameter/array.h"
+#include "live_effects/parameter/parameter.h"
+#include "live_effects/parameter/satellite-reference.h"
+
+class SPObject;
+
+namespace Inkscape {
+namespace LivePathEffect {
+class SatelliteReference;
+class SatelliteArrayParam : public ArrayParam<std::shared_ptr<SatelliteReference>>
+{
+public:
+ class ModelColumns;
+
+ SatelliteArrayParam(const Glib::ustring &label, const Glib::ustring &tip, const Glib::ustring &key,
+ Inkscape::UI::Widget::Registry *wr, Effect *effect, bool visible);
+
+ ~SatelliteArrayParam() override;
+ Gtk::Widget *param_newWidget() override;
+ bool param_readSVGValue(const gchar *strvalue) override;
+ void link(SPObject *to, size_t pos = Glib::ustring::npos);
+ void unlink(std::shared_ptr<SatelliteReference> to);
+ void unlink(SPObject *to);
+ bool is_connected(){ return linked_connections.size() != 0; };
+ void clear();
+ void setUpdating(bool updating) { _updating = updating; }
+ bool getUpdating() const { return _updating; }
+ void start_listening();
+ ParamType paramType() const override { return ParamType::SATELLITE_ARRAY; };
+protected:
+ void quit_listening();
+ void linked_modified(SPObject *linked_obj, guint flags);
+ bool _updateLink(const Gtk::TreeIter &iter, std::shared_ptr<SatelliteReference> lpref);
+ bool _selectIndex(const Gtk::TreeIter &iter, int *i);
+ void updatesignal();
+ ModelColumns *_model;
+ Glib::RefPtr<Gtk::TreeStore> _store;
+ Gtk::TreeView *_tree;
+ Gtk::ScrolledWindow *_scroller;
+ Gtk::CellRendererText *_text_renderer;
+ Gtk::CellRendererToggle *_toggle_active;
+ Gtk::TreeView::Column *_name_column;
+ 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:
+ bool _updating = false;
+ void update();
+ void initui();
+ bool _visible;
+ std::vector<sigc::connection> linked_connections;
+ std::vector<SPObject *> param_get_satellites() override;
+ SatelliteArrayParam(const SatelliteArrayParam &) = delete;
+ SatelliteArrayParam &operator=(const SatelliteArrayParam &) = 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/text.cpp b/src/live_effects/parameter/text.cpp
new file mode 100644
index 0000000..ea0171d
--- /dev/null
+++ b/src/live_effects/parameter/text.cpp
@@ -0,0 +1,184 @@
+// 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 "text.h"
+
+#include <glibmm/i18n.h>
+#include <gtkmm/alignment.h>
+
+#include <2geom/sbasis-geometric.h>
+
+#include "inkscape.h"
+
+#include "display/control/canvas-item-text.h"
+
+#include "live_effects/effect.h"
+
+#include "svg/stringstream.h"
+#include "svg/svg.h"
+
+#include "ui/icon-names.h"
+#include "ui/widget/registered-widget.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)
+{
+ if (SPDesktop *desktop = SP_ACTIVE_DESKTOP) { // FIXME: we shouldn't use this!
+ canvas_text = new Inkscape::CanvasItemText(desktop->getCanvasTemp(), Geom::Point(0, 0), default_value);
+ }
+}
+
+TextParam::~TextParam()
+{
+ if (canvas_text) {
+ delete canvas_text;
+ }
+}
+
+void
+TextParam::param_set_default()
+{
+ param_setValue(defvalue);
+}
+
+void
+TextParam::param_update_default(const gchar * default_value)
+{
+ defvalue = (Glib::ustring)default_value;
+}
+
+// This is a bit silly, we should have an option in the constructor to not create the canvas_text object.
+void
+TextParam::param_hide_canvas_text()
+{
+ if (canvas_text) {
+ delete canvas_text;
+ canvas_text = nullptr;
+ }
+}
+
+void
+TextParam::setPos(Geom::Point pos)
+{
+ if (canvas_text) {
+ canvas_text->set_coord(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 (canvas_text) {
+ canvas_text->set_coord(pos + n * length);
+ canvas_text->set_anchor(Geom::Point(std::sin(angle), -std::cos(angle)));
+ }
+}
+
+void
+TextParam::setAnchor(double x_value, double y_value)
+{
+ anchor_x = x_value;
+ anchor_y = y_value;
+ if (canvas_text) {
+ canvas_text->set_anchor(Geom::Point(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(_("Change text parameter"), INKSCAPE_ICON("dialog-path-effects"));
+ 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 (canvas_text) {
+ canvas_text->set_text(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/text.h b/src/live_effects/parameter/text.h
new file mode 100644
index 0000000..42700ad
--- /dev/null
+++ b/src/live_effects/parameter/text.h
@@ -0,0 +1,95 @@
+// 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 "live_effects/parameter/parameter.h"
+
+namespace Inkscape {
+
+class CanvasItemText;
+
+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;
+
+ 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; };
+ ParamType paramType() const override { return ParamType::TEXT; };
+
+private:
+ TextParam(const TextParam&) = delete;
+ TextParam& operator=(const TextParam&) = delete;
+ double anchor_x;
+ double anchor_y;
+ Glib::ustring value;
+ Glib::ustring defvalue;
+ Inkscape::CanvasItemText *canvas_text = nullptr;
+};
+
+/*
+ * 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..c8a80c8
--- /dev/null
+++ b/src/live_effects/parameter/togglebutton.cpp
@@ -0,0 +1,214 @@
+// 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 "togglebutton.h"
+
+#include <utility>
+
+#include <glibmm/i18n.h>
+
+#include "helper-fns.h"
+#include "inkscape.h"
+#include "selection.h"
+
+#include "live_effects/effect.h"
+
+#include "svg/stringstream.h"
+#include "svg/svg.h"
+
+#include "ui/icon-names.h"
+#include "ui/icon-loader.h"
+#include "ui/widget/registered-widget.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(_("Change togglebutton parameter"), INKSCAPE_ICON("dialog-path-effects"));
+
+ _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 ) {
+ 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..205598a
--- /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;
+ ParamType paramType() const override { return ParamType::TOGGLE_BUTTON; };
+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..9b2b41f
--- /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 "transformedpoint.h"
+
+#include <glibmm/i18n.h>
+
+#include "desktop.h"
+
+#include "live_effects/effect.h"
+#include "svg/svg.h"
+#include "svg/stringstream.h"
+#include "ui/icon-names.h"
+#include "ui/knot/knot-holder.h"
+#include "ui/knot/knot-holder-entity.h"
+#include "ui/widget/registered-widget.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),
+ vector(default_vector),
+ noTransform(dontTransform)
+{
+}
+
+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(_("Change vector parameter"), INKSCAPE_ICON("dialog-path-effects"));
+
+ Gtk::Box * hbox = Gtk::manage( new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL) );
+ hbox->pack_start(*pointwdg, true, true);
+ 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(Inkscape::CanvasItemCtrlShape shape,
+ Inkscape::CanvasItemCtrlMode 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::CANVAS_ITEM_CTRL_TYPE_LPE, "LPE:Point", handleTip(),
+ 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..34c07bc
--- /dev/null
+++ b/src/live_effects/parameter/transformedpoint.h
@@ -0,0 +1,88 @@
+// 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 "display/control/canvas-item-enums.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(Inkscape::CanvasItemCtrlShape shape,
+ Inkscape::CanvasItemCtrlMode 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);
+ ParamType paramType() const override { return ParamType::TRANSFORMED_POINT; };
+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
+ Inkscape::CanvasItemCtrlShape vec_knot_shape = Inkscape::CANVAS_ITEM_CTRL_SHAPE_DIAMOND;
+ Inkscape::CanvasItemCtrlMode vec_knot_mode = Inkscape::CANVAS_ITEM_CTRL_MODE_XOR;
+ guint32 vec_knot_color = 0xffffb500;
+
+ 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..7be0fc2
--- /dev/null
+++ b/src/live_effects/parameter/unit.cpp
@@ -0,0 +1,111 @@
+// 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 "unit.h"
+
+#include <glibmm/i18n.h>
+
+#include "live_effects/effect.h"
+#include "ui/icon-names.h"
+#include "ui/widget/registered-widget.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(_("Change unit parameter"), INKSCAPE_ICON("dialog-path-effects"));
+
+ 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..0c407d5
--- /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; }
+ ParamType paramType() const override { return ParamType::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..16b83fb
--- /dev/null
+++ b/src/live_effects/parameter/vector.cpp
@@ -0,0 +1,250 @@
+// 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 "vector.h"
+
+#include <glibmm/i18n.h>
+
+#include "live_effects/effect.h"
+#include "svg/svg.h"
+#include "svg/stringstream.h"
+#include "ui/icon-names.h"
+#include "ui/knot/knot-holder.h"
+#include "ui/knot/knot-holder-entity.h"
+#include "ui/widget/registered-widget.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)
+{
+}
+
+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(_("Change vector parameter"), INKSCAPE_ICON("dialog-path-effects"));
+
+ Gtk::Box * hbox = Gtk::manage( new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL) );
+ hbox->pack_start(*pointwdg, true, true);
+ 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(Inkscape::CanvasItemCtrlShape shape,
+ Inkscape::CanvasItemCtrlMode mode,
+ guint32 color)
+{
+ vec_knot_shape = shape;
+ vec_knot_mode = mode;
+ vec_knot_color = color;
+}
+
+void
+VectorParam::set_origin_oncanvas_looks(Inkscape::CanvasItemCtrlShape shape,
+ Inkscape::CanvasItemCtrlMode 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::CANVAS_ITEM_CTRL_TYPE_LPE, "LPE:Origin",
+ handleTip(), ori_knot_color);
+ knotholder->add(origin_e);
+
+ VectorParamKnotHolderEntity_Vector *vector_e = new VectorParamKnotHolderEntity_Vector(this);
+ vector_e->create(nullptr, item, knotholder, Inkscape::CANVAS_ITEM_CTRL_TYPE_LPE, "LPE:Vector",
+ handleTip(), 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..65e7355
--- /dev/null
+++ b/src/live_effects/parameter/vector.h
@@ -0,0 +1,90 @@
+// 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 "display/control/canvas-item-enums.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(Inkscape::CanvasItemCtrlShape shape,
+ Inkscape::CanvasItemCtrlMode mode,
+ guint32 color);
+ void set_origin_oncanvas_looks(Inkscape::CanvasItemCtrlShape shape,
+ Inkscape::CanvasItemCtrlMode 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;
+ ParamType paramType() const override { return ParamType::VECTOR; };
+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
+ Inkscape::CanvasItemCtrlShape vec_knot_shape = Inkscape::CANVAS_ITEM_CTRL_SHAPE_DIAMOND;
+ Inkscape::CanvasItemCtrlMode vec_knot_mode = Inkscape::CANVAS_ITEM_CTRL_MODE_XOR;;
+ guint32 vec_knot_color = 0xffffb500;
+ Inkscape::CanvasItemCtrlShape ori_knot_shape = Inkscape::CANVAS_ITEM_CTRL_SHAPE_CIRCLE;
+ Inkscape::CanvasItemCtrlMode ori_knot_mode = Inkscape::CANVAS_ITEM_CTRL_MODE_XOR;;
+ guint32 ori_knot_color = 0xffffb500;
+
+ friend class VectorParamKnotHolderEntity_Origin;
+ friend class VectorParamKnotHolderEntity_Vector;
+};
+
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif