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