From cca66b9ec4e494c1d919bff0f71a820d8afab1fa Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:24:48 +0200 Subject: Adding upstream version 1.2.2. Signed-off-by: Daniel Baumann --- src/live_effects/parameter/array.cpp | 128 ++++ src/live_effects/parameter/array.h | 175 ++++++ src/live_effects/parameter/bool.cpp | 117 ++++ src/live_effects/parameter/bool.h | 58 ++ src/live_effects/parameter/colorpicker.cpp | 153 +++++ src/live_effects/parameter/colorpicker.h | 63 ++ src/live_effects/parameter/enum.h | 115 ++++ src/live_effects/parameter/fontbutton.cpp | 108 ++++ src/live_effects/parameter/fontbutton.h | 63 ++ src/live_effects/parameter/hidden.cpp | 94 +++ src/live_effects/parameter/hidden.h | 71 +++ src/live_effects/parameter/message.cpp | 127 ++++ src/live_effects/parameter/message.h | 74 +++ src/live_effects/parameter/nodesatellitesarray.cpp | 569 ++++++++++++++++++ src/live_effects/parameter/nodesatellitesarray.h | 118 ++++ src/live_effects/parameter/originalpath.cpp | 113 ++++ src/live_effects/parameter/originalpath.h | 49 ++ src/live_effects/parameter/originalsatellite.cpp | 103 ++++ src/live_effects/parameter/originalsatellite.h | 39 ++ src/live_effects/parameter/parameter.cpp | 389 ++++++++++++ src/live_effects/parameter/parameter.h | 166 ++++++ src/live_effects/parameter/path-reference.cpp | 44 ++ src/live_effects/parameter/path-reference.h | 57 ++ src/live_effects/parameter/path.cpp | 655 +++++++++++++++++++++ src/live_effects/parameter/path.h | 125 ++++ src/live_effects/parameter/patharray.cpp | 593 +++++++++++++++++++ src/live_effects/parameter/patharray.h | 130 ++++ src/live_effects/parameter/point.cpp | 270 +++++++++ src/live_effects/parameter/point.h | 84 +++ .../parameter/powerstrokepointarray.cpp | 343 +++++++++++ src/live_effects/parameter/powerstrokepointarray.h | 95 +++ src/live_effects/parameter/random.cpp | 227 +++++++ src/live_effects/parameter/random.h | 76 +++ src/live_effects/parameter/satellite-reference.cpp | 56 ++ src/live_effects/parameter/satellite-reference.h | 57 ++ src/live_effects/parameter/satellite.cpp | 295 ++++++++++ src/live_effects/parameter/satellite.h | 76 +++ src/live_effects/parameter/satellitearray.cpp | 506 ++++++++++++++++ src/live_effects/parameter/satellitearray.h | 92 +++ src/live_effects/parameter/text.cpp | 184 ++++++ src/live_effects/parameter/text.h | 95 +++ src/live_effects/parameter/togglebutton.cpp | 214 +++++++ src/live_effects/parameter/togglebutton.h | 73 +++ src/live_effects/parameter/transformedpoint.cpp | 212 +++++++ src/live_effects/parameter/transformedpoint.h | 88 +++ src/live_effects/parameter/unit.cpp | 111 ++++ src/live_effects/parameter/unit.h | 56 ++ src/live_effects/parameter/vector.cpp | 250 ++++++++ src/live_effects/parameter/vector.h | 90 +++ 49 files changed, 8046 insertions(+) create mode 100644 src/live_effects/parameter/array.cpp create mode 100644 src/live_effects/parameter/array.h create mode 100644 src/live_effects/parameter/bool.cpp create mode 100644 src/live_effects/parameter/bool.h create mode 100644 src/live_effects/parameter/colorpicker.cpp create mode 100644 src/live_effects/parameter/colorpicker.h create mode 100644 src/live_effects/parameter/enum.h create mode 100644 src/live_effects/parameter/fontbutton.cpp create mode 100644 src/live_effects/parameter/fontbutton.h create mode 100644 src/live_effects/parameter/hidden.cpp create mode 100644 src/live_effects/parameter/hidden.h create mode 100644 src/live_effects/parameter/message.cpp create mode 100644 src/live_effects/parameter/message.h create mode 100644 src/live_effects/parameter/nodesatellitesarray.cpp create mode 100644 src/live_effects/parameter/nodesatellitesarray.h create mode 100644 src/live_effects/parameter/originalpath.cpp create mode 100644 src/live_effects/parameter/originalpath.h create mode 100644 src/live_effects/parameter/originalsatellite.cpp create mode 100644 src/live_effects/parameter/originalsatellite.h create mode 100644 src/live_effects/parameter/parameter.cpp create mode 100644 src/live_effects/parameter/parameter.h create mode 100644 src/live_effects/parameter/path-reference.cpp create mode 100644 src/live_effects/parameter/path-reference.h create mode 100644 src/live_effects/parameter/path.cpp create mode 100644 src/live_effects/parameter/path.h create mode 100644 src/live_effects/parameter/patharray.cpp create mode 100644 src/live_effects/parameter/patharray.h create mode 100644 src/live_effects/parameter/point.cpp create mode 100644 src/live_effects/parameter/point.h create mode 100644 src/live_effects/parameter/powerstrokepointarray.cpp create mode 100644 src/live_effects/parameter/powerstrokepointarray.h create mode 100644 src/live_effects/parameter/random.cpp create mode 100644 src/live_effects/parameter/random.h create mode 100644 src/live_effects/parameter/satellite-reference.cpp create mode 100644 src/live_effects/parameter/satellite-reference.h create mode 100644 src/live_effects/parameter/satellite.cpp create mode 100644 src/live_effects/parameter/satellite.h create mode 100644 src/live_effects/parameter/satellitearray.cpp create mode 100644 src/live_effects/parameter/satellitearray.h create mode 100644 src/live_effects/parameter/text.cpp create mode 100644 src/live_effects/parameter/text.h create mode 100644 src/live_effects/parameter/togglebutton.cpp create mode 100644 src/live_effects/parameter/togglebutton.h create mode 100644 src/live_effects/parameter/transformedpoint.cpp create mode 100644 src/live_effects/parameter/transformedpoint.h create mode 100644 src/live_effects/parameter/unit.cpp create mode 100644 src/live_effects/parameter/unit.h create mode 100644 src/live_effects/parameter/vector.cpp create mode 100644 src/live_effects/parameter/vector.h (limited to 'src/live_effects/parameter') 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 + * + * 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::readsvg(const gchar * str) +{ + double newx = Geom::infinity(); + sp_svg_number_read_d(str, &newx); + return newx; +} + +template <> +float +ArrayParam::readsvg(const gchar * str) +{ + float newx = Geom::infinity(); + sp_svg_number_read_f(str, &newx); + return newx; +} + +template <> +Geom::Point +ArrayParam::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 ArrayParam>::readsvg(const gchar *str) +{ + std::shared_ptr 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(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 ArrayParam>::readsvg(const gchar *str) +{ + std::vector 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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include +#include + +#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 +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 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 const &new_vector) { + _vector = new_vector; + } + + void param_set_default() override { + param_setValue( std::vector(_default_size) ); + } + + void param_set_and_write_new_value(std::vector 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 _vector; + size_t _default_size; + + void writesvg(SVGOStringStream &str, std::vector 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 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 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(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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "bool.h" + +#include + +#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 (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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include + +#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 + +#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 + +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 (colorpickerwdg), true, true); + return dynamic_cast (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 +#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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include + +#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 class EnumParam : public Parameter { +public: + EnumParam( const Glib::ustring& label, + const Glib::ustring& tip, + const Glib::ustring& key, + const Util::EnumDataConverter& 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 *regenum = Gtk::manage ( + new Inkscape::UI::Widget::RegisteredEnum( 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 (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 * 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 +#include + +#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 (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 +#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 + * Copyright (C) Maximilian Albert 2008 + * + * 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 + * Copyright (C) Maximilian Albert 2008 + * + * 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 +#include + +#include + +#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 (_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 (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 +#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 + * + * 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 + +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>(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(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(&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 &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 = _("Chamfer: Ctrl+Click toggles type, " + "Shift+Click open dialog, " + "Ctrl+Alt+Click reset"); + } else if (type == INVERSE_CHAMFER) { + tip = _("Inverse Chamfer: Ctrl+Click toggles type, " + "Shift+Click open dialog, " + "Ctrl+Alt+Click reset"); + } else if (type == INVERSE_FILLET) { + tip = _("Inverse Fillet: Ctrl+Click toggles type, " + "Shift+Click open dialog, " + "Ctrl+Alt+Click reset"); + } else { + tip = _("Fillet: Ctrl+Click toggles type, " + "Shift+Click open dialog, " + "Ctrl+Alt+Click 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 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(_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(_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 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 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 = _("Chamfer: Ctrl+Click toggles type, " + "Shift+Click open dialog, " + "Ctrl+Alt+Click resets"); + } else if (type == INVERSE_CHAMFER) { + tip = _("Inverse Chamfer: Ctrl+Click toggles type, " + "Shift+Click open dialog, " + "Ctrl+Alt+Click resets"); + } else if (type == INVERSE_FILLET) { + tip = _("Inverse Fillet: Ctrl+Click toggles type, " + "Shift+Click open dialog, " + "Ctrl+Alt+Click resets"); + } else { + tip = _("Fillet: Ctrl+Click toggles type, " + "Shift+Click open dialog, " + "Ctrl+Alt+Click 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 d2_out = pathv[satelite_index][subsatelite_index].toSBasis(); + Geom::D2 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 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(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 + * 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 + +#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> +{ +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 &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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include +#include "live_effects/parameter/originalpath.h" + +#include +#include +#include + +#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 (_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 + * + * 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 & /*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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "live_effects/parameter/originalsatellite.h" + +#include +#include +#include +#include + +#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 (_widget); +} + +void OriginalSatelliteParam::on_select_original_button_click() +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + SPItem *original = dynamic_cast(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 + * + * 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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "parameter.h" + +#include +#include + +#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 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(); + std::vector cs; // = param_effect->getCanvasIndicators(lpeitems[0]); + Geom::OptRect bbox = lpeitems[0]->documentVisualBounds(); + + if (param_effect->helperLineSatellites) { + std::vector satellites = param_get_satellites(); + for (auto iter : satellites) { + SPItem *satelliteitem = dynamic_cast(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 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 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 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 Parameter::param_get_satellites() +{ + std::vector 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(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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include <2geom/forward.h> +#include <2geom/pathvector.h> +#include + +#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 ¶m_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 & /*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 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 + * Abhishek Sharma + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "path.h" + +#include +#include + +#include +#include + + +#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(desktop->event_context); +// if (nt) { +// SPItem * item = SP_ACTIVE_DESKTOP->getSelection()->singleItem(); +// if (item) { +// std::set shapes; +// ShapeRecord r; +// r.item = item; +// shapes.insert(r); +// nt->_multipath->setItems(shapes); +// } +// } +// } + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (desktop) { + if (dynamic_cast(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(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(getObject()) )) { + std::vector 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 > 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 PathParam::param_get_satellites() +{ + + std::vector 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 (_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(dt->event_context); + if (!nt) { + set_active_tool(dt, "Node"); + nt = dynamic_cast(dt->event_context); + } + + std::set 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(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 &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 > 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 curve; + if (auto shape = dynamic_cast(linked_obj)) { + if (_from_original_d) { + curve = SPCurve::copy(shape->curveForEdit()); + } else { + curve = SPCurve::copy(shape->curve()); + } + } + + SPText *text = dynamic_cast(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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include +#include <2geom/path.h> + +#include "live_effects/parameter/parameter.h" +#include "live_effects/parameter/path-reference.h" +#include +#include + +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 > const & get_pwd2(); + + Gtk::Widget * param_newWidget() override; + std::vector 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 > 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 &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 signal_path_pasted; + sigc::signal 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 > _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 + * + * 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 +#include +#include +#include +#include +#include + +#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 _colObject; + Gtk::TreeModelColumn _colLabel; + Gtk::TreeModelColumn _colReverse; + Gtk::TreeModelColumn _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 PathArrayParam::param_get_satellites() +{ + std::vector 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::iterator piter = _vector.begin(); + for (std::vector::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(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::iterator iter = _vector.begin(); iter != _vector.end(); i++, ++iter) { + if (*iter == row[_model->_colObject]) { + std::vector::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(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 pathsid = cm->getElementsOfType(SP_ACTIVE_DESKTOP, "svg:path"); + std::vector 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::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(sigc::mem_fun(*this, &PathArrayParam::linked_delete), to)); + to->linked_modified_connection = new_obj->connectModified( + sigc::bind(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(sigc::mem_fun(*this, &PathArrayParam::_updateLink), to)); + } + } +} + +void PathArrayParam::setPathVector(SPObject *linked_obj, guint /*flags*/, PathAndDirectionAndVisible *to) +{ + if (!to) { + return; + } + std::unique_ptr curve; + SPText *text = dynamic_cast(linked_obj); + if (auto shape = dynamic_cast(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(lpe)) { + Geom::PathVector hp; + LivePathEffect::sp_bspline_do_effect(curve.get(), 0, hp); + } else if (dynamic_cast(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(); + } + 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(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(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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include + +#include +#include +#include +#include + +#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 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 & /*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 _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 _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 + * + * 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 + +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( 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( 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 (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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include + +#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 + * + * 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 + +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(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 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 > const & pwd2_in) +{ + Inkscape::LivePathEffect::LPEPowerStroke *lpe = dynamic_cast(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 > 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 +PowerStrokePointArrayParam::reverse_controlpoints(bool write) +{ + std::vector controlpoints; + if (!last_pwd2.empty()) { + Geom::Piecewise > 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 > const & pwd2_in, Geom::Piecewise > 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 > const & pwd2 = _pparam->get_pwd2(); + Piecewise > pwd2port = _pparam->get_pwd2(); + Geom::Point s = snap_knot_position(p, state); + double t2 = 0; + LPEPowerStroke *ps = dynamic_cast(_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 > 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 > const & pwd2 = _pparam->get_pwd2(); + Piecewise > 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 & 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(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 & 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(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", + _("Stroke width control point: drag to alter the stroke width. Ctrl+click adds a " + "control point, Ctrl+Alt+click deletes it, Shift+click 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", + _("Stroke width control point: drag to alter the stroke width. Ctrl+click adds a " + "control point, Ctrl+Alt+click deletes it, Shift+click 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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include +#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 { +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 > const & pwd2_in, Geom::Piecewise > const & pwd2_normal_in); + Geom::Piecewise > const & get_pwd2() const { return last_pwd2; } + Geom::Piecewise > const & get_pwd2_normal() const { return last_pwd2_normal; } + + void recalculate_controlpoints_for_new_pwd2(Geom::Piecewise > const & pwd2_in); + std::vector 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 > last_pwd2; + Geom::Piecewise > 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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "random.h" + +#include + +#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(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 (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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "live_effects/parameter/parameter.h" +#include +#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(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 + * 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 + +#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(param_effect->getLPEObj(), false)) + , last_transform(Geom::identity()) +{} + +SatelliteParam::~SatelliteParam() +{ + quit_listening(); +} + +std::vector SatelliteParam::param_get_satellites() +{ + std::vector 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(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 &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(_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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include + +#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 &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 lperef; + +private: + std::vector 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 + * 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 + +namespace Inkscape { + +namespace LivePathEffect { + +class SatelliteArrayParam::ModelColumns : public Gtk::TreeModel::ColumnRecord +{ +public: + ModelColumns() + { + add(_colObject); + add(_colLabel); + add(_colActive); + } + ~ModelColumns() override = default; + + Gtk::TreeModelColumn _colObject; + Gtk::TreeModelColumn _colLabel; + Gtk::TreeModelColumn _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>(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(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(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(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 itemsid; + // Here we ignore auto clipboard group wrapper + std::vector itemsids = cm->getElementsOfType(SP_ACTIVE_DESKTOP, "*", 2); + std::vector 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(*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 = + std::make_shared(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 SatelliteArrayParam::param_get_satellites() +{ + std::vector 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 = + std::make_shared(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 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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include +#include +#include + +#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> +{ +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 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 lpref); + bool _selectIndex(const Gtk::TreeIter &iter, int *i); + void updatesignal(); + ModelColumns *_model; + Glib::RefPtr _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 linked_connections; + std::vector 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 + * + * Authors: + * Maximilian Albert + * Johan Engelen + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "text.h" + +#include +#include + +#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 > &pwd2, + const double t, const double length, bool /*use_curvature*/) +{ + using namespace Geom; + + Piecewise > 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(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 (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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include + +#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 > &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 + * Copyright (C) Jabiertxo Arraiza Cenoz 2014 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "togglebutton.h" + +#include + +#include + +#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(checkwdg->get_child()); + if(!box_button){ + return; + } + std::vector children = box_button->get_children(); + if (!param_label.empty()) { + Gtk::Label *lab = dynamic_cast(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(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 +#include +#include + +#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 &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 _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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "transformedpoint.h" + +#include + +#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 (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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include +#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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "unit.h" + +#include + +#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 (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 + * + * 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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "vector.h" + +#include + +#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 (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 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include +#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 -- cgit v1.2.3