summaryrefslogtreecommitdiffstats
path: root/src/extension/prefdialog
diff options
context:
space:
mode:
authorPatrick Storz <eduard.braun2@gmx.de>2019-07-21 13:24:05 +0000
committerPatrick Storz <eduard.braun2@gmx.de>2019-08-31 14:50:38 +0000
commit7311f3ac5161a4d53cf2ed7e0a5a97993f646321 (patch)
tree050dd572bffca377fd2d510597a28ccb8080fb1c /src/extension/prefdialog
parentOnly register desktop on Dbus once (diff)
downloadinkscape-7311f3ac5161a4d53cf2ed7e0a5a97993f646321.tar.gz
inkscape-7311f3ac5161a4d53cf2ed7e0a5a97993f646321.zip
Move prefdialog to own directory
Diffstat (limited to 'src/extension/prefdialog')
-rw-r--r--src/extension/prefdialog/param/bool.cpp154
-rw-r--r--src/extension/prefdialog/param/bool.h95
-rw-r--r--src/extension/prefdialog/param/color.cpp119
-rw-r--r--src/extension/prefdialog/param/color.h74
-rw-r--r--src/extension/prefdialog/param/description.cpp134
-rw-r--r--src/extension/prefdialog/param/description.h63
-rw-r--r--src/extension/prefdialog/param/enum.cpp268
-rw-r--r--src/extension/prefdialog/param/enum.h106
-rw-r--r--src/extension/prefdialog/param/float.cpp205
-rw-r--r--src/extension/prefdialog/param/float.h83
-rw-r--r--src/extension/prefdialog/param/int.cpp201
-rw-r--r--src/extension/prefdialog/param/int.h80
-rw-r--r--src/extension/prefdialog/param/notebook.cpp413
-rw-r--r--src/extension/prefdialog/param/notebook.h117
-rw-r--r--src/extension/prefdialog/param/parameter.cpp461
-rw-r--r--src/extension/prefdialog/param/parameter.h219
-rw-r--r--src/extension/prefdialog/param/radiobutton.cpp349
-rw-r--r--src/extension/prefdialog/param/radiobutton.h107
-rw-r--r--src/extension/prefdialog/param/string.cpp181
-rw-r--r--src/extension/prefdialog/param/string.h67
-rw-r--r--src/extension/prefdialog/prefdialog.cpp277
-rw-r--r--src/extension/prefdialog/prefdialog.h94
22 files changed, 3867 insertions, 0 deletions
diff --git a/src/extension/prefdialog/param/bool.cpp b/src/extension/prefdialog/param/bool.cpp
new file mode 100644
index 000000000..f304f9aef
--- /dev/null
+++ b/src/extension/prefdialog/param/bool.cpp
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2005-2007 Authors:
+ * Ted Gould <ted@gould.cx>
+ * Johan Engelen <johan@shouraizou.nl> *
+ * Jon A. Cruz <jon@joncruz.org>
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <gtkmm/adjustment.h>
+#include <gtkmm/box.h>
+#include <gtkmm/spinbutton.h>
+#include <gtkmm/checkbutton.h>
+
+#include "xml/node.h"
+#include "extension/extension.h"
+#include "bool.h"
+#include "preferences.h"
+
+namespace Inkscape {
+namespace Extension {
+
+ParamBool::ParamBool(const gchar * name,
+ const gchar * text,
+ const gchar * description,
+ bool hidden,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml)
+ : Parameter(name, text, description, hidden, indent, ext)
+ , _value(false)
+{
+ const char * defaultval = nullptr;
+ if (xml->firstChild() != nullptr) {
+ defaultval = xml->firstChild()->content();
+ }
+
+ if (defaultval != nullptr && (!strcmp(defaultval, "true") || !strcmp(defaultval, "1"))) {
+ _value = true;
+ } else {
+ _value = false;
+ }
+
+ gchar * pref_name = this->pref_name();
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ _value = prefs->getBool(extension_pref_root + pref_name, _value);
+ g_free(pref_name);
+
+ return;
+}
+
+bool ParamBool::set( bool in, SPDocument * /*doc*/, Inkscape::XML::Node * /*node*/ )
+{
+ _value = in;
+
+ gchar * prefname = this->pref_name();
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setBool(extension_pref_root + prefname, _value);
+ g_free(prefname);
+
+ return _value;
+}
+
+bool ParamBool::get(const SPDocument * /*doc*/, const Inkscape::XML::Node * /*node*/) const
+{
+ return _value;
+}
+
+/**
+ * A check button which is Param aware. It works with the
+ * parameter to change it's value as the check button changes
+ * value.
+ */
+class ParamBoolCheckButton : public Gtk::CheckButton {
+public:
+ /**
+ * Initialize the check button.
+ * This function sets the value of the checkbox to be that of the
+ * parameter, and then sets up a callback to \c on_toggle.
+ *
+ * @param param Which parameter to adjust on changing the check button
+ */
+ ParamBoolCheckButton (ParamBool * param, gchar * label, SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal) :
+ Gtk::CheckButton(label), _pref(param), _doc(doc), _node(node), _changeSignal(changeSignal) {
+ this->set_active(_pref->get(nullptr, nullptr) /**\todo fix */);
+ this->signal_toggled().connect(sigc::mem_fun(this, &ParamBoolCheckButton::on_toggle));
+ return;
+ }
+
+ /**
+ * A function to respond to the check box changing.
+ * Adjusts the value of the preference to match that in the check box.
+ */
+ void on_toggle ();
+
+private:
+ /** Param to change. */
+ ParamBool * _pref;
+ SPDocument * _doc;
+ Inkscape::XML::Node * _node;
+ sigc::signal<void> * _changeSignal;
+};
+
+void ParamBoolCheckButton::on_toggle()
+{
+ _pref->set(this->get_active(), nullptr /**\todo fix this */, nullptr);
+ if (_changeSignal != nullptr) {
+ _changeSignal->emit();
+ }
+ return;
+}
+
+void ParamBool::string(std::string &string) const
+{
+ if (_value) {
+ string += "true";
+ } else {
+ string += "false";
+ }
+
+ return;
+}
+
+Gtk::Widget *ParamBool::get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal)
+{
+ if (_hidden) {
+ return nullptr;
+ }
+
+ auto hbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, Parameter::GUI_PARAM_WIDGETS_SPACING));
+ hbox->set_homogeneous(false);
+
+ ParamBoolCheckButton * checkbox = Gtk::manage(new ParamBoolCheckButton(this, _text, doc, node, changeSignal));
+ checkbox->show();
+ hbox->pack_start(*checkbox, false, false);
+
+ hbox->show();
+
+ return dynamic_cast<Gtk::Widget *>(hbox);
+}
+
+} /* namespace Extension */
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/extension/prefdialog/param/bool.h b/src/extension/prefdialog/param/bool.h
new file mode 100644
index 000000000..9864cac38
--- /dev/null
+++ b/src/extension/prefdialog/param/bool.h
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef SEEN_INK_EXTENSION_PARAMBOOL_H
+#define SEEN_INK_EXTENSION_PARAMBOOL_H
+/*
+ * Copyright (C) 2005-2007 Authors:
+ * Ted Gould <ted@gould.cx>
+ * Johan Engelen <johan@shouraizou.nl> *
+ * Jon A. Cruz <jon@joncruz.org>
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "parameter.h"
+
+class SPDocument;
+
+namespace Gtk {
+class Widget;
+}
+
+namespace Inkscape {
+namespace XML {
+class Node;
+}
+
+namespace Extension {
+
+/**
+ * A boolean parameter.
+ */
+class ParamBool : public Parameter {
+public:
+
+ /**
+ * Use the superclass' allocator and set the \c _value.
+ */
+ ParamBool(const gchar * name,
+ const gchar * text,
+ const gchar * description,
+ bool hidden,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml);
+
+ /**
+ * Returns the current state/value.
+ */
+ bool get(const SPDocument *doc, const Inkscape::XML::Node *node) const;
+
+ /**
+ * A function to set the state/value.
+ * This function sets the internal value, but it also sets the value
+ * in the preferences structure. To put it in the right place, \c PREF_DIR
+ * and \c pref_name() are used.
+ *
+ * @param in The value to set to
+ * @param doc A document that should be used to set the value.
+ * @param node The node where the value may be placed
+ */
+ bool set(bool in, SPDocument * doc, Inkscape::XML::Node * node);
+
+ /**
+ * Creates a bool check button for a bool parameter.
+ * Builds a hbox with a label and a check button in it.
+ */
+ Gtk::Widget *get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal) override;
+
+ // Explicitly call superclass version to avoid method being hidden.
+ void string(std::list <std::string> &list) const override { return Parameter::string(list); }
+
+ /**
+ * Appends 'true' or 'false'.
+ * @todo investigate. Returning a value that can then be appended would probably work better/safer.
+ */
+ void string(std::string &string) const override;
+
+private:
+ /** Internal value. */
+ bool _value;
+};
+
+} // namespace Extension
+} // namespace Inkscape
+
+#endif // SEEN_INK_EXTENSION_PARAMBOOL_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/extension/prefdialog/param/color.cpp b/src/extension/prefdialog/param/color.cpp
new file mode 100644
index 000000000..ed9940154
--- /dev/null
+++ b/src/extension/prefdialog/param/color.cpp
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2005-2007 Authors:
+ * Ted Gould <ted@gould.cx>
+ * Johan Engelen <johan@shouraizou.nl>
+ * Christopher Brown <audiere@gmail.com>
+ * Jon A. Cruz <jon@joncruz.org>
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <iostream>
+#include <sstream>
+
+#include <gtkmm/adjustment.h>
+#include <gtkmm/box.h>
+#include <gtkmm/spinbutton.h>
+
+#include <xml/node.h>
+
+#include "extension/extension.h"
+#include "color.h"
+
+#include <color.h>
+#include "ui/widget/color-notebook.h"
+#include "preferences.h"
+
+namespace Inkscape {
+namespace Extension {
+
+ParamColor::~ParamColor()
+{
+ _color_changed.disconnect();
+}
+
+guint32 ParamColor::set( guint32 in, SPDocument * /*doc*/, Inkscape::XML::Node * /*node*/ )
+{
+ _color_changed.block(true);
+ _color.setValue(in);
+ _color_changed.block(false);
+
+ gchar * prefname = this->pref_name();
+ std::string value;
+ string(value);
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setString(extension_pref_root + prefname, value);
+ g_free(prefname);
+
+ return in;
+}
+
+ParamColor::ParamColor(const gchar * name,
+ const gchar * text,
+ const gchar * description,
+ bool hidden,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml)
+ : Parameter(name, text, description, hidden, indent, ext)
+ , _changeSignal(nullptr)
+{
+ const char * defaulthex = nullptr;
+ if (xml->firstChild() != nullptr)
+ defaulthex = xml->firstChild()->content();
+
+ gchar * pref_name = this->pref_name();
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ Glib::ustring paramval = prefs->getString(extension_pref_root + pref_name);
+ g_free(pref_name);
+
+ if (!paramval.empty())
+ defaulthex = paramval.data();
+
+ if (defaulthex) {
+ _color.setValue(atoi(defaulthex));
+ }
+ _color_changed = _color.signal_changed.connect(sigc::mem_fun(this, &ParamColor::_onColorChanged));
+
+}
+
+void ParamColor::string(std::string &string) const
+{
+ char str[16];
+ snprintf(str, 16, "%i", _color.value());
+ string += str;
+}
+
+Gtk::Widget *ParamColor::get_widget( SPDocument * /*doc*/, Inkscape::XML::Node * /*node*/, sigc::signal<void> * changeSignal )
+{
+ using Inkscape::UI::Widget::ColorNotebook;
+
+ if (_hidden) return nullptr;
+
+ if (changeSignal) {
+ _changeSignal = new sigc::signal<void>(*changeSignal);
+ }
+
+ if (_color.value() < 1) {
+ _color_changed.block(true);
+ _color.setValue(0xFF000000);
+ _color_changed.block(false);
+ }
+
+ Gtk::HBox *hbox = Gtk::manage(new Gtk::HBox(false, Parameter::GUI_PARAM_WIDGETS_SPACING));
+ Gtk::Widget *selector = Gtk::manage(new ColorNotebook(_color));
+ hbox->pack_start(*selector, true, true, 0);
+ selector->show();
+ hbox->show();
+ return hbox;
+}
+
+void ParamColor::_onColorChanged()
+{
+ if (_changeSignal)
+ _changeSignal->emit();
+}
+
+}; /* namespace Extension */
+}; /* namespace Inkscape */
diff --git a/src/extension/prefdialog/param/color.h b/src/extension/prefdialog/param/color.h
new file mode 100644
index 000000000..c2d3c2ccb
--- /dev/null
+++ b/src/extension/prefdialog/param/color.h
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef SEEN_INK_EXTENSION_PARAMCOLOR_H__
+#define SEEN_INK_EXTENSION_PARAMCOLOR_H__
+/*
+ * Copyright (C) 2005-2007 Authors:
+ * Ted Gould <ted@gould.cx>
+ * Johan Engelen <johan@shouraizou.nl> *
+ * Jon A. Cruz <jon@joncruz.org>
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "parameter.h"
+#include "ui/selected-color.h"
+
+class SPDocument;
+
+namespace Gtk {
+class Widget;
+}
+
+namespace Inkscape {
+namespace XML {
+class Node;
+}
+
+namespace Extension {
+
+class ParamColor : public Parameter {
+private:
+ void _onColorChanged();
+
+ Inkscape::UI::SelectedColor _color;
+ sigc::connection _color_changed;
+public:
+ ParamColor(const gchar * name,
+ const gchar * text,
+ const gchar * description,
+ bool hidden,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml);
+ ~ParamColor() override;
+
+ /** Returns \c _value, with a \i const to protect it. */
+ guint32 get( SPDocument const * /*doc*/, Inkscape::XML::Node const * /*node*/ ) const { return _color.value(); }
+
+ guint32 set (guint32 in, SPDocument * doc, Inkscape::XML::Node * node);
+
+ Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal) override;
+
+ // Explicitly call superclass version to avoid method being hidden.
+ void string(std::list <std::string> &list) const override { return Parameter::string(list); }
+
+ void string (std::string &string) const override;
+
+ sigc::signal<void> * _changeSignal;
+
+}; // class ParamColor
+
+} // namespace Extension
+} // namespace Inkscape
+
+#endif // SEEN_INK_EXTENSION_PARAMCOLOR_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/extension/prefdialog/param/description.cpp b/src/extension/prefdialog/param/description.cpp
new file mode 100644
index 000000000..60e2d02c8
--- /dev/null
+++ b/src/extension/prefdialog/param/description.cpp
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2005-2007 Authors:
+ * Ted Gould <ted@gould.cx>
+ * Johan Engelen <johan@shouraizou.nl> *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifdef linux // does the dollar sign need escaping when passed as string parameter?
+# define ESCAPE_DOLLAR_COMMANDLINE
+#endif
+
+#include <gtkmm/box.h>
+#include <gtkmm/label.h>
+#include <glibmm/i18n.h>
+#include <glibmm/markup.h>
+#include <glibmm/regex.h>
+
+#include "xml/node.h"
+#include "extension/extension.h"
+#include "description.h"
+
+namespace Inkscape {
+namespace Extension {
+
+
+/** \brief Initialize the object, to do that, copy the data. */
+ParamDescription::ParamDescription(const gchar * name,
+ const gchar * text,
+ const gchar * description,
+ bool hidden,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml,
+ AppearanceMode mode)
+ : Parameter(name, text, description, hidden, indent, ext)
+ , _value(nullptr)
+ , _mode(mode)
+{
+ // construct the text content by concatenating all (non-empty) text nodes,
+ // removing all other nodes (e.g. comment nodes) and replacing <extension:br> elements with "<br/>"
+ Glib::ustring value;
+ Inkscape::XML::Node * cur_child = xml->firstChild();
+ while (cur_child != nullptr) {
+ if (cur_child->type() == XML::TEXT_NODE && cur_child->content() != nullptr) {
+ value += cur_child->content();
+ } else if (cur_child->type() == XML::ELEMENT_NODE && !g_strcmp0(cur_child->name(), "extension:br")) {
+ value += "<br/>";
+ }
+ cur_child = cur_child->next();
+ }
+
+ // if there is no text content we can return immediately (the description will be invisible)
+ if (value == Glib::ustring("")) {
+ return;
+ }
+
+ // do replacements in the source string to account for the attribute xml:space="preserve"
+ // (those should match replacements potentially performed by xgettext to allow for proper translation)
+ if (g_strcmp0(xml->attribute("xml:space"), "preserve") == 0) {
+ // xgettext copies the source string verbatim in this case, so no changes needed
+ } else {
+ // remove all whitespace from start/end of string and replace intermediate whitespace with a single space
+ value = Glib::Regex::create("^\\s+|\\s+$")->replace_literal(value, 0, "", (Glib::RegexMatchFlags)0);
+ value = Glib::Regex::create("\\s+")->replace_literal(value, 0, " ", (Glib::RegexMatchFlags)0);
+ }
+
+ // translate if underscored version (_param) was used
+ if (g_str_has_prefix(xml->name(), "extension:_")) {
+ const gchar * context = xml->attribute("msgctxt");
+ if (context != nullptr) {
+ value = g_dpgettext2(nullptr, context, value.c_str());
+ } else {
+ value = _(value.c_str());
+ }
+ }
+
+ // finally replace all remaining <br/> with a real newline character
+ value = Glib::Regex::create("<br/>")->replace_literal(value, 0, "\n", (Glib::RegexMatchFlags)0);
+
+ _value = g_strdup(value.c_str());
+
+ return;
+}
+
+/** \brief Create a label for the description */
+Gtk::Widget *
+ParamDescription::get_widget (SPDocument * /*doc*/, Inkscape::XML::Node * /*node*/, sigc::signal<void> * /*changeSignal*/)
+{
+ if (_hidden) {
+ return nullptr;
+ }
+ if (_value == nullptr) {
+ return nullptr;
+ }
+
+ Glib::ustring newtext = _value;
+
+ Gtk::Label * label = Gtk::manage(new Gtk::Label());
+ if (_mode == HEADER) {
+ label->set_markup(Glib::ustring("<b>") + Glib::Markup::escape_text(newtext) + Glib::ustring("</b>"));
+ label->set_margin_top(5);
+ label->set_margin_bottom(5);
+ } else if (_mode == URL) {
+ Glib::ustring escaped_url = Glib::Markup::escape_text(newtext);
+ label->set_markup(Glib::ustring::compose("<a href='%1'>%1</a>", escaped_url));
+ } else {
+ label->set_text(newtext);
+ }
+ label->set_line_wrap();
+ label->set_xalign(0);
+
+ // TODO: Ugly "fix" for gtk3 width/height calculation of labels.
+ // - If not applying any limits long labels will make the window grow horizontally until it uses up
+ // most of the available space (i.e. most of the screen area) which is ridicously wide
+ // - By using "set_default_size(0,0)" in prefidalog.cpp we tell the window to shrink as much as possible,
+ // however this can result in a much to narrow dialog instead and much unnecessary wrapping
+ // - Here we set a lower limit of GUI_MAX_LINE_LENGTH characters per line that long texts will always use
+ // This means texts can not shrink anymore (they can still grow, though) and it's also necessary
+ // to prevent https://bugzilla.gnome.org/show_bug.cgi?id=773572
+ int len = newtext.length();
+ label->set_width_chars(len > Parameter::GUI_MAX_LINE_LENGTH ? Parameter::GUI_MAX_LINE_LENGTH : len);
+
+ label->show();
+
+ Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox());
+ hbox->pack_start(*label, true, true);
+ hbox->show();
+
+ return hbox;
+}
+
+} /* namespace Extension */
+} /* namespace Inkscape */
diff --git a/src/extension/prefdialog/param/description.h b/src/extension/prefdialog/param/description.h
new file mode 100644
index 000000000..822370b82
--- /dev/null
+++ b/src/extension/prefdialog/param/description.h
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef __INK_EXTENSION_PARAMDESCRIPTION_H__
+#define __INK_EXTENSION_PARAMDESCRIPTION_H__
+
+/*
+ * Copyright (C) 2005-2007 Authors:
+ * Ted Gould <ted@gould.cx>
+ * Johan Engelen <johan@shouraizou.nl> *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "parameter.h"
+
+class SPDocument;
+
+namespace Gtk {
+ class Widget;
+}
+
+namespace Inkscape {
+namespace Xml {
+ class Node;
+}
+
+namespace Extension {
+
+/** \brief A description parameter */
+class ParamDescription : public Parameter {
+public:
+ enum AppearanceMode {
+ DESCRIPTION, HEADER, URL
+ };
+ ParamDescription(const gchar * name,
+ const gchar * text,
+ const gchar * description,
+ bool hidden,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml,
+ AppearanceMode mode);
+
+ Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal) override;
+private:
+ /** \brief Internal value. */
+ gchar * _value;
+ AppearanceMode _mode;
+};
+
+} /* namespace Extension */
+} /* namespace Inkscape */
+
+#endif /* __INK_EXTENSION_PARAMDESCRIPTION_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/extension/prefdialog/param/enum.cpp b/src/extension/prefdialog/param/enum.cpp
new file mode 100644
index 000000000..5ba3e5772
--- /dev/null
+++ b/src/extension/prefdialog/param/enum.cpp
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** \file
+ * extension parameter for enumerations.
+ *
+ * It uses a Gtk:ComboBoxText widget in the extension UI.
+ */
+
+/*
+ * Author:
+ * Johan Engelen <johan@shouraizou.nl>
+ * Jon A. Cruz <jon@joncruz.org>
+ *
+ * Copyright (C) 2006-2007 Johan Engelen
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <gtkmm/box.h>
+#include <gtkmm/comboboxtext.h>
+#include <glibmm/i18n.h>
+
+#include "xml/node.h"
+#include "extension/extension.h"
+#include "enum.h"
+#include "preferences.h"
+
+namespace Inkscape {
+namespace Extension {
+
+ParamComboBox::ParamComboBox(const gchar * name,
+ const gchar * text,
+ const gchar * description,
+ bool hidden,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml)
+ : Parameter(name, text, description, hidden, indent, ext)
+ , _value(nullptr)
+{
+ const char *xmlval = nullptr; // the value stored in XML
+
+ if (xml != nullptr) {
+ // Read XML tree to add enumeration items:
+ for (Inkscape::XML::Node *node = xml->firstChild(); node; node = node->next()) {
+ char const * chname = node->name();
+ if (!strcmp(chname, INKSCAPE_EXTENSION_NS "item") || !strcmp(chname, INKSCAPE_EXTENSION_NS "_item")) {
+ Glib::ustring newtext, newvalue;
+ const char * contents = nullptr;
+ if (node->firstChild()) {
+ contents = node->firstChild()->content();
+ }
+ if (contents != nullptr) {
+ // don't translate when 'item' but do translate when '_item'
+ // NOTE: internal extensions use build_from_mem and don't need _item but
+ // still need to include if are to be localized
+ if (!strcmp(chname, INKSCAPE_EXTENSION_NS "_item")) {
+ if (node->attribute("msgctxt") != nullptr) {
+ newtext = g_dpgettext2(nullptr, node->attribute("msgctxt"), contents);
+ } else {
+ newtext = _(contents);
+ }
+ } else {
+ newtext = contents;
+ }
+ } else
+ continue;
+
+ const char * val = node->attribute("value");
+ if (val != nullptr) {
+ newvalue = val;
+ } else {
+ newvalue = contents;
+ }
+
+ if ( (!newtext.empty()) && (!newvalue.empty()) ) { // logical error if this is not true here
+ choices.push_back(new enumentry(newvalue, newtext) );
+ }
+ }
+ }
+
+ // Initialize _value with the default value from xml
+ // for simplicity : default to the contents of the first xml-child
+ if (xml->firstChild() && xml->firstChild()->firstChild()) {
+ xmlval = xml->firstChild()->attribute("value");
+ }
+ }
+
+ gchar * pref_name = this->pref_name();
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ Glib::ustring paramval = prefs ? prefs->getString(extension_pref_root + pref_name) : "";
+ g_free(pref_name);
+
+ if (!paramval.empty()) {
+ _value = g_strdup(paramval.data());
+ } else if (xmlval) {
+ _value = g_strdup(xmlval);
+ }
+}
+
+ParamComboBox::~ParamComboBox ()
+{
+ //destroy choice strings
+ for (auto i:choices) {
+ delete i;
+ }
+ g_free(_value);
+}
+
+
+/**
+ * A function to set the \c _value.
+ *
+ * This function sets ONLY the internal value, but it also sets the value
+ * in the preferences structure. To put it in the right place, \c PREF_DIR
+ * and \c pref_name() are used.
+ *
+ * To copy the data into _value the old memory must be free'd first.
+ * It is important to note that \c g_free handles \c NULL just fine. Then
+ * the passed in value is duplicated using \c g_strdup().
+ *
+ * @param in The value to set.
+ * @param doc A document that should be used to set the value.
+ * @param node The node where the value may be placed.
+ */
+const gchar *ParamComboBox::set(const gchar * in, SPDocument * /*doc*/, Inkscape::XML::Node * /*node*/)
+{
+ if (in == nullptr) {
+ return nullptr; /* Can't have NULL string */
+ }
+
+ Glib::ustring settext;
+ for (auto entr:choices) {
+ if ( !entr->text.compare(in) ) {
+ settext = entr->value;
+ break; // break out of for loop
+ }
+ }
+ if (!settext.empty()) {
+ if (_value != nullptr) {
+ g_free(_value);
+ }
+ _value = g_strdup(settext.data());
+ gchar * prefname = this->pref_name();
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setString(extension_pref_root + prefname, _value);
+ g_free(prefname);
+ }
+
+ return _value;
+}
+
+/**
+ * function to test if \c text is selectable
+ */
+bool ParamComboBox::contains(const gchar * text, SPDocument const * /*doc*/, Inkscape::XML::Node const * /*node*/) const
+{
+ if (text == nullptr) {
+ return false; /* Can't have NULL string */
+ }
+
+ for (auto entr:choices) {
+ if ( !entr->text.compare(text) )
+ return true;
+ }
+ // if we did not find the text in this ParamComboBox:
+ return false;
+}
+
+void
+ParamComboBox::changed () {
+
+}
+
+void ParamComboBox::string(std::string &string) const
+{
+ string += _value;
+}
+
+
+
+
+/** A special category of Gtk::Entry to handle string parameteres. */
+class ParamComboBoxEntry : public Gtk::ComboBoxText {
+private:
+ ParamComboBox * _pref;
+ SPDocument * _doc;
+ Inkscape::XML::Node * _node;
+ sigc::signal<void> * _changeSignal;
+public:
+ /**
+ * Build a string preference for the given parameter.
+ * @param pref Where to get the string from, and where to put it
+ * when it changes.
+ */
+ ParamComboBoxEntry (ParamComboBox * pref, SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal) :
+ Gtk::ComboBoxText(), _pref(pref), _doc(doc), _node(node), _changeSignal(changeSignal) {
+ this->signal_changed().connect(sigc::mem_fun(this, &ParamComboBoxEntry::changed));
+ };
+ void changed ();
+};
+
+/**
+ * Respond to the text box changing.
+ *
+ * This function responds to the box changing by grabbing the value
+ * from the text box and putting it in the parameter.
+ */
+void
+ParamComboBoxEntry::changed ()
+{
+ Glib::ustring data = this->get_active_text();
+ _pref->set(data.c_str(), _doc, _node);
+ if (_changeSignal != nullptr) {
+ _changeSignal->emit();
+ }
+}
+
+/**
+ * Creates a combobox widget for an enumeration parameter.
+ */
+Gtk::Widget *ParamComboBox::get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal)
+{
+ if (_hidden) {
+ return nullptr;
+ }
+
+ Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, Parameter::GUI_PARAM_WIDGETS_SPACING));
+ Gtk::Label * label = Gtk::manage(new Gtk::Label(_text, Gtk::ALIGN_START));
+ label->show();
+ hbox->pack_start(*label, false, false);
+
+ ParamComboBoxEntry * combo = Gtk::manage(new ParamComboBoxEntry(this, doc, node, changeSignal));
+ // add choice strings:
+ Glib::ustring settext;
+ for (auto entr:choices) {
+ Glib::ustring text = entr->text;
+ combo->append(text);
+
+ if ( _value && !entr->value.compare(_value) ) {
+ settext = entr->text;
+ }
+ }
+ if (!settext.empty()) {
+ combo->set_active_text(settext);
+ }
+
+ combo->show();
+ hbox->pack_start(*combo, true, true);
+
+ hbox->show();
+
+ return dynamic_cast<Gtk::Widget *>(hbox);
+}
+
+
+} // namespace Extension
+} // 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/extension/prefdialog/param/enum.h b/src/extension/prefdialog/param/enum.h
new file mode 100644
index 000000000..329bb26f9
--- /dev/null
+++ b/src/extension/prefdialog/param/enum.h
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INK_EXTENSION_PARAMENUM_H_SEEN
+#define INK_EXTENSION_PARAMENUM_H_SEEN
+
+/** \file
+ * Enumeration parameter for extensions.
+ */
+
+/*
+ * Authors:
+ * Johan Engelen <johan@shouraizou.nl>
+ * Jon A. Cruz <jon@joncruz.org>
+ *
+ * Copyright (C) 2006-2007 Johan Engelen
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <vector>
+
+#include "parameter.h"
+#include "document.h"
+
+namespace Gtk {
+class Widget;
+}
+
+namespace Inkscape {
+namespace Extension {
+
+class Extension;
+
+
+// \brief A class to represent a notebookparameter of an extension
+class ParamComboBox : public Parameter {
+private:
+ /** \brief Internal value. This should point to a string that has
+ been allocated in memory. And should be free'd.
+ It is the value of the current selected string */
+ gchar * _value;
+
+ /* For internal use only.
+ * Note that value and text MUST be non-NULL.
+ * This is ensured by newing only at one location in the code where non-NULL checks are made.
+ */
+ class enumentry {
+ public:
+ enumentry (Glib::ustring &val, Glib::ustring &text) :
+ value(val),
+ text(text)
+ {}
+
+ Glib::ustring value;
+ Glib::ustring text;
+ };
+
+ std::vector<enumentry *> choices; /**< A table to store the choice strings */
+
+public:
+ ParamComboBox(const gchar * name,
+ const gchar * text,
+ const gchar * description,
+ bool hidden,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml);
+ ~ParamComboBox() override;
+
+ Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal) override;
+
+ // Explicitly call superclass version to avoid method being hidden.
+ void string(std::list <std::string> &list) const override { return Parameter::string(list); }
+
+ void string(std::string &string) const override;
+
+ gchar const *get(SPDocument const * /*doc*/, Inkscape::XML::Node const * /*node*/) const { return _value; }
+
+ const gchar * set (const gchar * in, SPDocument * doc, Inkscape::XML::Node * node);
+
+ /**
+ * @returns true if text is part of this enum
+ */
+ bool contains(const gchar * text, SPDocument const * /*doc*/, Inkscape::XML::Node const * /*node*/) const;
+
+ void changed ();
+}; /* class ParamComboBox */
+
+
+
+
+
+} /* namespace Extension */
+} /* namespace Inkscape */
+
+#endif /* INK_EXTENSION_PARAMENUM_H_SEEN */
+
+/*
+ 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/extension/prefdialog/param/float.cpp b/src/extension/prefdialog/param/float.cpp
new file mode 100644
index 000000000..661c533ca
--- /dev/null
+++ b/src/extension/prefdialog/param/float.cpp
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2005-2007 Authors:
+ * Ted Gould <ted@gould.cx>
+ * Johan Engelen <johan@shouraizou.nl> *
+ * Jon A. Cruz <jon@joncruz.org>
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <gtkmm/adjustment.h>
+#include <gtkmm/box.h>
+#include <gtkmm/scale.h>
+#include "ui/widget/spinbutton.h"
+#include "ui/widget/spin-scale.h"
+
+#include "xml/node.h"
+#include "extension/extension.h"
+#include "preferences.h"
+#include "./float.h"
+
+namespace Inkscape {
+namespace Extension {
+
+
+/** Use the superclass' allocator and set the \c _value. */
+ParamFloat::ParamFloat(const gchar * name,
+ const gchar * text,
+ const gchar * description,
+ bool hidden,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml,
+ AppearanceMode mode)
+ : Parameter(name, text, description, hidden, indent, ext)
+ , _value(0.0)
+ , _mode(mode)
+ , _min(0.0)
+ , _max(10.0)
+{
+ const gchar * defaultval = nullptr;
+ if (xml->firstChild() != nullptr) {
+ defaultval = xml->firstChild()->content();
+ }
+ if (defaultval != nullptr) {
+ _value = g_ascii_strtod (defaultval,nullptr);
+ }
+
+ const char * maxval = xml->attribute("max");
+ if (maxval != nullptr) {
+ _max = g_ascii_strtod (maxval,nullptr);
+ }
+
+ const char * minval = xml->attribute("min");
+ if (minval != nullptr) {
+ _min = g_ascii_strtod (minval,nullptr);
+ }
+
+ _precision = 1;
+ const char * precision = xml->attribute("precision");
+ if (precision != nullptr) {
+ _precision = atoi(precision);
+ }
+
+ /* We're handling this by just killing both values */
+ if (_max < _min) {
+ _max = 10.0;
+ _min = 0.0;
+ }
+
+ gchar * pref_name = this->pref_name();
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ _value = prefs->getDouble(extension_pref_root + pref_name, _value);
+ g_free(pref_name);
+
+ // std::cout << "New Float:: value: " << _value << " max: " << _max << " min: " << _min << std::endl;
+
+ if (_value > _max) {
+ _value = _max;
+ }
+ if (_value < _min) {
+ _value = _min;
+ }
+
+ return;
+}
+
+/**
+ * A function to set the \c _value.
+ *
+ * This function sets the internal value, but it also sets the value
+ * in the preferences structure. To put it in the right place, \c PREF_DIR
+ * and \c pref_name() are used.
+ *
+ * @param in The value to set to.
+ * @param doc A document that should be used to set the value.
+ * @param node The node where the value may be placed.
+ */
+float ParamFloat::set(float in, SPDocument * /*doc*/, Inkscape::XML::Node * /*node*/)
+{
+ _value = in;
+ if (_value > _max) {
+ _value = _max;
+ }
+ if (_value < _min) {
+ _value = _min;
+ }
+
+ gchar * prefname = this->pref_name();
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setDouble(extension_pref_root + prefname, _value);
+ g_free(prefname);
+
+ return _value;
+}
+
+void ParamFloat::string(std::string &string) const
+{
+ char startstring[G_ASCII_DTOSTR_BUF_SIZE];
+ g_ascii_dtostr(startstring, G_ASCII_DTOSTR_BUF_SIZE, _value);
+ string += startstring;
+ return;
+}
+
+/** A class to make an adjustment that uses Extension params. */
+class ParamFloatAdjustment : public Gtk::Adjustment {
+ /** The parameter to adjust. */
+ ParamFloat * _pref;
+ SPDocument * _doc;
+ Inkscape::XML::Node * _node;
+ sigc::signal<void> * _changeSignal;
+public:
+ /** Make the adjustment using an extension and the string
+ describing the parameter. */
+ ParamFloatAdjustment (ParamFloat * param, SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal) :
+ Gtk::Adjustment(0.0, param->min(), param->max(), 0.1, 1.0, 0), _pref(param), _doc(doc), _node(node), _changeSignal(changeSignal) {
+ this->set_value(_pref->get(nullptr, nullptr) /* \todo fix */);
+ this->signal_value_changed().connect(sigc::mem_fun(this, &ParamFloatAdjustment::val_changed));
+ return;
+ };
+
+ void val_changed ();
+}; /* class ParamFloatAdjustment */
+
+/**
+ * A function to respond to the value_changed signal from the adjustment.
+ *
+ * This function just grabs the value from the adjustment and writes
+ * it to the parameter. Very simple, but yet beautiful.
+ */
+void ParamFloatAdjustment::val_changed()
+{
+ //std::cout << "Value Changed to: " << this->get_value() << std::endl;
+ _pref->set(this->get_value(), _doc, _node);
+ if (_changeSignal != nullptr) {
+ _changeSignal->emit();
+ }
+ return;
+}
+
+/**
+ * Creates a Float Adjustment for a float parameter.
+ *
+ * Builds a hbox with a label and a float adjustment in it.
+ */
+Gtk::Widget * ParamFloat::get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal)
+{
+ if (_hidden) {
+ return nullptr;
+ }
+
+ Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, Parameter::GUI_PARAM_WIDGETS_SPACING));
+
+ auto pfa = new ParamFloatAdjustment(this, doc, node, changeSignal);
+ Glib::RefPtr<Gtk::Adjustment> fadjust(pfa);
+
+ if (_mode == FULL) {
+
+ Glib::ustring text;
+ if (_text != nullptr)
+ text = _text;
+ UI::Widget::SpinScale *scale = new UI::Widget::SpinScale(text, fadjust, _precision);
+ scale->set_size_request(400, -1);
+ scale->show();
+ hbox->pack_start(*scale, true, true);
+
+ }
+ else if (_mode == MINIMAL) {
+
+ Gtk::Label * label = Gtk::manage(new Gtk::Label(_text, Gtk::ALIGN_START));
+ label->show();
+ hbox->pack_start(*label, true, true);
+
+ auto spin = Gtk::manage(new Inkscape::UI::Widget::SpinButton(fadjust, 0.1, _precision));
+ spin->show();
+ hbox->pack_start(*spin, false, false);
+ }
+
+ hbox->show();
+
+ return dynamic_cast<Gtk::Widget *>(hbox);
+}
+
+
+} /* namespace Extension */
+} /* namespace Inkscape */
diff --git a/src/extension/prefdialog/param/float.h b/src/extension/prefdialog/param/float.h
new file mode 100644
index 000000000..c1d0f0f79
--- /dev/null
+++ b/src/extension/prefdialog/param/float.h
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INK_EXTENSION_PARAMFLOAT_H_SEEN
+#define INK_EXTENSION_PARAMFLOAT_H_SEEN
+
+/*
+ * Copyright (C) 2005-2007 Authors:
+ * Ted Gould <ted@gould.cx>
+ * Johan Engelen <johan@shouraizou.nl> *
+ * Jon A. Cruz <jon@joncruz.org>
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "parameter.h"
+
+class SPDocument;
+
+namespace Gtk {
+class Widget;
+}
+
+namespace Inkscape {
+namespace XML {
+class Node;
+}
+
+namespace Extension {
+
+class ParamFloat : public Parameter {
+public:
+ enum AppearanceMode {
+ FULL, MINIMAL
+ };
+ ParamFloat(const gchar * name,
+ const gchar * text,
+ const gchar * description,
+ bool hidden,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml,
+ AppearanceMode mode);
+
+ /** Returns \c _value. */
+ float get(const SPDocument * /*doc*/, const Inkscape::XML::Node * /*node*/) const { return _value; }
+
+ float set (float in, SPDocument * doc, Inkscape::XML::Node * node);
+
+ float max () { return _max; }
+
+ float min () { return _min; }
+
+ float precision () { return _precision; }
+
+ Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal) override;
+
+ // Explicitly call superclass version to avoid method being hidden.
+ void string(std::list <std::string> &list) const override { return Parameter::string(list); }
+
+ void string(std::string &string) const override;
+
+private:
+ /** Internal value. */
+ float _value;
+ AppearanceMode _mode;
+ float _min;
+ float _max;
+ int _precision;
+};
+
+} /* namespace Extension */
+} /* namespace Inkscape */
+
+#endif /* INK_EXTENSION_PARAMFLOAT_H_SEEN */
+
+/*
+ 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/extension/prefdialog/param/int.cpp b/src/extension/prefdialog/param/int.cpp
new file mode 100644
index 000000000..1c41c0834
--- /dev/null
+++ b/src/extension/prefdialog/param/int.cpp
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2005-2007 Authors:
+ * Ted Gould <ted@gould.cx>
+ * Johan Engelen <johan@shouraizou.nl> *
+ * Jon A. Cruz <jon@joncruz.org>
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <gtkmm/adjustment.h>
+#include <gtkmm/box.h>
+#include <gtkmm/scale.h>
+#include "ui/widget/spinbutton.h"
+#include "ui/widget/spin-scale.h"
+
+#include "xml/node.h"
+#include "extension/extension.h"
+#include "preferences.h"
+#include "int.h"
+
+namespace Inkscape {
+namespace Extension {
+
+
+/** Use the superclass' allocator and set the \c _value. */
+ParamInt::ParamInt(const gchar * name,
+ const gchar * text,
+ const gchar * description,
+ bool hidden,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml,
+ AppearanceMode mode)
+ : Parameter(name, text, description, hidden, indent, ext)
+ , _value(0)
+ , _mode(mode)
+ , _min(0)
+ , _max(10)
+{
+ const char * defaultval = nullptr;
+ if (xml->firstChild() != nullptr) {
+ defaultval = xml->firstChild()->content();
+ }
+ if (defaultval != nullptr) {
+ _value = atoi(defaultval);
+ }
+
+ const char * maxval = xml->attribute("max");
+ if (maxval != nullptr) {
+ _max = atoi(maxval);
+ }
+
+ const char * minval = xml->attribute("min");
+ if (minval != nullptr) {
+ _min = atoi(minval);
+ }
+ /* We're handling this by just killing both values */
+ if (_max < _min) {
+ _max = 10;
+ _min = 0;
+ }
+
+ gchar *pref_name = this->pref_name();
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ _value = prefs->getInt(extension_pref_root + pref_name, _value);
+ g_free(pref_name);
+
+ // std::cout << "New Int:: value: " << _value << " max: " << _max << " min: " << _min << std::endl;
+
+ if (_value > _max) {
+ _value = _max;
+ }
+ if (_value < _min) {
+ _value = _min;
+ }
+}
+
+/**
+ * A function to set the \c _value.
+ * This function sets the internal value, but it also sets the value
+ * in the preferences structure. To put it in the right place, \c PREF_DIR
+ * and \c pref_name() are used.
+ *
+ * @param in The value to set to.
+ * @param doc A document that should be used to set the value.
+ * @param node The node where the value may be placed.
+ */
+int ParamInt::set(int in, SPDocument * /*doc*/, Inkscape::XML::Node * /*node*/)
+{
+ _value = in;
+ if (_value > _max) {
+ _value = _max;
+ }
+ if (_value < _min) {
+ _value = _min;
+ }
+
+ gchar * prefname = this->pref_name();
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setInt(extension_pref_root + prefname, _value);
+ g_free(prefname);
+
+ return _value;
+}
+
+/** A class to make an adjustment that uses Extension params. */
+class ParamIntAdjustment : public Gtk::Adjustment {
+ /** The parameter to adjust. */
+ ParamInt * _pref;
+ SPDocument * _doc;
+ Inkscape::XML::Node * _node;
+ sigc::signal<void> * _changeSignal;
+public:
+ /** Make the adjustment using an extension and the string
+ describing the parameter. */
+ ParamIntAdjustment (ParamInt * param, SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal) :
+ Gtk::Adjustment(0.0, param->min(), param->max(), 1.0, 10.0, 0), _pref(param), _doc(doc), _node(node), _changeSignal(changeSignal) {
+ this->set_value(_pref->get(nullptr, nullptr) /* \todo fix */);
+ this->signal_value_changed().connect(sigc::mem_fun(this, &ParamIntAdjustment::val_changed));
+ };
+
+ void val_changed ();
+}; /* class ParamIntAdjustment */
+
+/**
+ * A function to respond to the value_changed signal from the adjustment.
+ *
+ * This function just grabs the value from the adjustment and writes
+ * it to the parameter. Very simple, but yet beautiful.
+ */
+void ParamIntAdjustment::val_changed()
+{
+ //std::cout << "Value Changed to: " << this->get_value() << std::endl;
+ _pref->set((int)this->get_value(), _doc, _node);
+ if (_changeSignal != nullptr) {
+ _changeSignal->emit();
+ }
+}
+
+/**
+ * Creates a Int Adjustment for a int parameter.
+ *
+ * Builds a hbox with a label and a int adjustment in it.
+ */
+Gtk::Widget *
+ParamInt::get_widget (SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal)
+{
+ if (_hidden) {
+ return nullptr;
+ }
+
+ Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, Parameter::GUI_PARAM_WIDGETS_SPACING));
+
+ auto pia = new ParamIntAdjustment(this, doc, node, changeSignal);
+ Glib::RefPtr<Gtk::Adjustment> fadjust(pia);
+
+ if (_mode == FULL) {
+
+ Glib::ustring text;
+ if (_text != nullptr)
+ text = _text;
+ UI::Widget::SpinScale *scale = new UI::Widget::SpinScale(text, fadjust, 0);
+ scale->set_size_request(400, -1);
+ scale->show();
+ hbox->pack_start(*scale, true, true);
+ }
+ else if (_mode == MINIMAL) {
+ Gtk::Label * label = Gtk::manage(new Gtk::Label(_text, Gtk::ALIGN_START));
+ label->show();
+ hbox->pack_start(*label, true, true);
+
+ auto spin = Gtk::manage(new Inkscape::UI::Widget::SpinButton(fadjust, 1.0, 0));
+ spin->show();
+ hbox->pack_start(*spin, false, false);
+ }
+
+ hbox->show();
+
+ return dynamic_cast<Gtk::Widget *>(hbox);
+}
+
+void ParamInt::string(std::string &string) const
+{
+ char startstring[32];
+ sprintf(startstring, "%d", _value);
+ string += startstring;
+}
+
+} // namespace Extension
+} // namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/extension/prefdialog/param/int.h b/src/extension/prefdialog/param/int.h
new file mode 100644
index 000000000..fac481267
--- /dev/null
+++ b/src/extension/prefdialog/param/int.h
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INK_EXTENSION_PARAMINT_H_SEEN
+#define INK_EXTENSION_PARAMINT_H_SEEN
+
+/*
+ * Copyright (C) 2005-2007 Authors:
+ * Ted Gould <ted@gould.cx>
+ * Johan Engelen <johan@shouraizou.nl> *
+ * Jon A. Cruz <jon@joncruz.org>
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "parameter.h"
+
+class SPDocument;
+
+namespace Gtk {
+class Widget;
+}
+
+namespace Inkscape {
+namespace XML {
+class Node;
+}
+
+namespace Extension {
+
+class ParamInt : public Parameter {
+public:
+ enum AppearanceMode {
+ FULL, MINIMAL
+ };
+ ParamInt(const gchar * name,
+ const gchar * text,
+ const gchar * description,
+ bool hidden,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml,
+ AppearanceMode mode);
+
+ /** Returns \c _value. */
+ int get(const SPDocument * /*doc*/, const Inkscape::XML::Node * /*node*/) const { return _value; }
+
+ int set (int in, SPDocument * doc, Inkscape::XML::Node * node);
+
+ int max () { return _max; }
+
+ int min () { return _min; }
+
+ Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal) override;
+
+ // Explicitly call superclass version to avoid method being hidden.
+ void string(std::list <std::string> &list) const override { return Parameter::string(list); }
+
+ void string(std::string &string) const override;
+
+private:
+ /** Internal value. */
+ int _value;
+ AppearanceMode _mode;
+ int _min;
+ int _max;
+};
+
+} /* namespace Extension */
+} /* namespace Inkscape */
+
+#endif /* INK_EXTENSION_PARAMINT_H_SEEN */
+
+/*
+ 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/extension/prefdialog/param/notebook.cpp b/src/extension/prefdialog/param/notebook.cpp
new file mode 100644
index 000000000..838c09d5b
--- /dev/null
+++ b/src/extension/prefdialog/param/notebook.cpp
@@ -0,0 +1,413 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** \file
+ * Notebook and NotebookPage parameters for extensions.
+ */
+
+/*
+ * Authors:
+ * Johan Engelen <johan@shouraizou.nl>
+ * Jon A. Cruz <jon@joncruz.org>
+ *
+ * Copyright (C) 2006 Author
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <gtkmm/adjustment.h>
+#include <gtkmm/box.h>
+#include <gtkmm/spinbutton.h>
+#include <gtkmm/notebook.h>
+
+#include <glibmm/i18n.h>
+
+#include <xml/node.h>
+
+#include <extension/extension.h>
+#include "preferences.h"
+
+#include "notebook.h"
+
+/**
+ * The root directory in the preferences database for extension
+ * related parameters.
+ */
+#define PREF_DIR "extensions"
+
+namespace Inkscape {
+namespace Extension {
+
+
+ParamNotebook::ParamNotebookPage::ParamNotebookPage(const gchar * name,
+ const gchar * text,
+ const gchar * description,
+ bool hidden,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml)
+ : Parameter(name, text, description, hidden, /*indent*/ 0, ext)
+{
+
+ // Read XML to build page
+ if (xml != nullptr) {
+ Inkscape::XML::Node *child_repr = xml->firstChild();
+ while (child_repr != nullptr) {
+ char const * chname = child_repr->name();
+ if (!strncmp(chname, INKSCAPE_EXTENSION_NS_NC, strlen(INKSCAPE_EXTENSION_NS_NC))) {
+ chname += strlen(INKSCAPE_EXTENSION_NS);
+ }
+ if (chname[0] == '_') // Allow _ for translation of tags
+ chname++;
+ if (!strcmp(chname, "param") || !strcmp(chname, "_param")) {
+ Parameter * param;
+ param = Parameter::make(child_repr, ext);
+ if (param != nullptr) parameters.push_back(param);
+ }
+ child_repr = child_repr->next();
+ }
+ }
+}
+
+ParamNotebook::ParamNotebookPage::~ParamNotebookPage ()
+{
+ //destroy parameters
+ for (auto param:parameters) {
+ delete param;
+ }
+}
+
+/** Return the value as a string. */
+void ParamNotebook::ParamNotebookPage::paramString(std::list <std::string> &list)
+{
+ for (auto param:parameters) {
+ param->string(list);
+ }
+}
+
+
+/**
+ \return None
+ \brief This function creates a page that can be used later. This
+ is typically done in the creation of the notebook and defined
+ in the XML file describing the extension (it's private so people
+ have to use the system) :)
+ \param in_repr The XML describing the page
+ \todo the 'gui-hidden' attribute is read but not used!
+
+ This function first grabs all of the data out of the Repr and puts
+ it into local variables. Actually, these are just pointers, and the
+ data is not duplicated so we need to be careful with it. If there
+ isn't a name in the XML, then no page is created as
+ the function just returns.
+
+ From this point on, we're pretty committed as we've allocated an
+ object and we're starting to fill it. The name is set first, and
+ is created with a strdup to actually allocate memory for it. Then
+ there is a case statement (roughly because strcmp requires 'ifs')
+ based on what type of parameter this is. Depending which type it
+ is, the value is interpreted differently, but they are relatively
+ straight forward. In all cases the value is set to the default
+ value from the XML and the type is set to the interpreted type.
+*/
+ParamNotebook::ParamNotebookPage *
+ParamNotebook::ParamNotebookPage::makepage (Inkscape::XML::Node * in_repr, Inkscape::Extension::Extension * in_ext)
+{
+ const char * name;
+ const char * text;
+ const char * description;
+ bool hidden = false;
+ const char * hide;
+
+ name = in_repr->attribute("name");
+ text = in_repr->attribute("gui-text");
+ if (text == nullptr)
+ text = in_repr->attribute("_gui-text");
+ description = in_repr->attribute("gui-description");
+ if (description == nullptr)
+ description = in_repr->attribute("_gui-description");
+ hide = in_repr->attribute("gui-hidden");
+ if (hide != nullptr) {
+ if (strcmp(hide, "1") == 0 ||
+ strcmp(hide, "true") == 0) {
+ hidden = true;
+ }
+ /* else stays false */
+ }
+
+ /* In this case we just don't have enough information */
+ if (name == nullptr) {
+ return nullptr;
+ }
+
+ ParamNotebookPage * page = new ParamNotebookPage(name, text, description, hidden, in_ext, in_repr);
+
+ /* Note: page could equal NULL */
+ return page;
+}
+
+
+
+/**
+ * Creates a notebookpage widget for a notebook.
+ *
+ * Builds a notebook page (a vbox) and puts parameters on it.
+ */
+Gtk::Widget * ParamNotebook::ParamNotebookPage::get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal)
+{
+ if (_hidden) {
+ return nullptr;
+ }
+
+ Gtk::VBox * vbox = Gtk::manage(new Gtk::VBox);
+ vbox->set_border_width(Parameter::GUI_BOX_MARGIN);
+ vbox->set_spacing(Parameter::GUI_BOX_SPACING);
+
+ // add parameters onto page (if any)
+ for (auto param:parameters) {
+ Gtk::Widget * widg = param->get_widget(doc, node, changeSignal);
+ if (widg) {
+ int indent = param->get_indent();
+ widg->set_margin_start(indent * Parameter::GUI_INDENTATION);
+ vbox->pack_start(*widg, false, false, 0);
+
+ gchar const * tip = param->get_tooltip();
+ if (tip) {
+ widg->set_tooltip_text(tip);
+ } else {
+ widg->set_tooltip_text("");
+ widg->set_has_tooltip(false);
+ }
+ }
+ }
+
+ vbox->show();
+
+ return dynamic_cast<Gtk::Widget *>(vbox);
+}
+
+/** Search the parameter's name in the page content. */
+Parameter *ParamNotebook::ParamNotebookPage::get_param(const gchar * name)
+{
+ if (name == nullptr) {
+ throw Extension::param_not_exist();
+ }
+ if (this->parameters.empty()) {
+ // the list of parameters is empty
+ throw Extension::param_not_exist();
+ }
+
+ for (auto param:parameters) {
+ if (!strcmp(param->name(), name)) {
+ return param;
+ }
+ }
+
+ return nullptr;
+}
+
+/** End ParamNotebookPage **/
+/** ParamNotebook **/
+
+ParamNotebook::ParamNotebook(const gchar * name,
+ const gchar * text,
+ const gchar * description,
+ bool hidden,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml)
+ : Parameter(name, text, description, hidden, indent, ext)
+{
+ // Read XML tree to add pages:
+ if (xml != nullptr) {
+ Inkscape::XML::Node *child_repr = xml->firstChild();
+ while (child_repr != nullptr) {
+ char const * chname = child_repr->name();
+ if (!strncmp(chname, INKSCAPE_EXTENSION_NS_NC, strlen(INKSCAPE_EXTENSION_NS_NC))) {
+ chname += strlen(INKSCAPE_EXTENSION_NS);
+ }
+ if (chname[0] == '_') // Allow _ for translation of tags
+ chname++;
+ if (!strcmp(chname, "page")) {
+ ParamNotebookPage * page;
+ page = ParamNotebookPage::makepage(child_repr, ext);
+ if (page != nullptr) pages.push_back(page);
+ }
+ child_repr = child_repr->next();
+ }
+ }
+
+ // Initialize _value with the current page
+ const char * defaultval = nullptr;
+ // set first page as default
+ if (!pages.empty()) {
+ defaultval = pages[0]->name();
+ }
+
+ gchar * pref_name = this->pref_name();
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ Glib::ustring paramval = prefs->getString(extension_pref_root + pref_name);
+ g_free(pref_name);
+
+ if (!paramval.empty())
+ defaultval = paramval.data();
+ if (defaultval != nullptr)
+ _value = g_strdup(defaultval); // allocate space for _value
+}
+
+ParamNotebook::~ParamNotebook ()
+{
+ //destroy pages
+ for (auto page:pages) {
+ delete page;
+ }
+ g_free(_value);
+}
+
+
+/**
+ * A function to set the \c _value.
+ *
+ * This function sets the internal value, but it also sets the value
+ * in the preferences structure. To put it in the right place, \c PREF_DIR
+ * and \c pref_name() are used.
+ *
+ * To copy the data into _value the old memory must be free'd first.
+ * It is important to note that \c g_free handles \c NULL just fine. Then
+ * the passed in value is duplicated using \c g_strdup().
+ *
+ * @param in The number of the page which value must be set.
+ * @param doc A document that should be used to set the value.
+ * @param node The node where the value may be placed.
+ */
+const gchar *ParamNotebook::set(const int in, SPDocument * /*doc*/, Inkscape::XML::Node * /*node*/)
+{
+ int i = in < pages.size() ? in : pages.size()-1;
+ ParamNotebookPage * page = pages[i];
+
+ if (page == nullptr) return _value;
+
+ if (_value != nullptr) g_free(_value);
+ _value = g_strdup(page->name());
+
+ gchar * prefname = this->pref_name();
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setString(extension_pref_root + prefname, _value);
+ g_free(prefname);
+
+ return _value;
+}
+
+void ParamNotebook::string(std::list <std::string> &list) const
+{
+ std::string param_string;
+ param_string += "--";
+ param_string += name();
+ param_string += "=";
+
+ param_string += "\"";
+ param_string += _value; // the name of the current page
+ param_string += "\"";
+ list.insert(list.end(), param_string);
+
+ for (auto page:pages) {
+ page->paramString(list);
+ }
+}
+
+/** A special category of Gtk::Notebook to handle notebook parameters. */
+class ParamNotebookWdg : public Gtk::Notebook {
+private:
+ ParamNotebook * _pref;
+ SPDocument * _doc;
+ Inkscape::XML::Node * _node;
+public:
+ /**
+ * Build a notebookpage preference for the given parameter.
+ * @param pref Where to get the string (pagename) from, and where to put it
+ * when it changes.
+ */
+ ParamNotebookWdg (ParamNotebook * pref, SPDocument * doc, Inkscape::XML::Node * node) :
+ Gtk::Notebook(), _pref(pref), _doc(doc), _node(node), activated(false) {
+ // don't have to set the correct page: this is done in ParamNotebook::get_widget.
+ // hook function
+ this->signal_switch_page().connect(sigc::mem_fun(this, &ParamNotebookWdg::changed_page));
+ };
+ void changed_page(Gtk::Widget *page, guint pagenum);
+ bool activated;
+};
+
+/**
+ * Respond to the selected page of notebook changing.
+ * This function responds to the changing by reporting it to
+ * ParamNotebook. The change is only reported when the notebook
+ * is actually visible. This to exclude 'fake' changes when the
+ * notebookpages are added or removed.
+ */
+void ParamNotebookWdg::changed_page(Gtk::Widget * /*page*/, guint pagenum)
+{
+ if (get_visible()) {
+ _pref->set((int)pagenum, _doc, _node);
+ }
+}
+
+/** Search the parameter's name in the notebook content. */
+Parameter *ParamNotebook::get_param(const gchar * name)
+{
+ if (name == nullptr) {
+ throw Extension::param_not_exist();
+ }
+ for (auto page:pages) {
+ Parameter * subparam = page->get_param(name);
+ if (subparam) {
+ return subparam;
+ }
+ }
+
+ return nullptr;
+}
+
+
+/**
+ * Creates a Notebook widget for a notebook parameter.
+ *
+ * Builds a notebook and puts pages in it.
+ */
+Gtk::Widget * ParamNotebook::get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal)
+{
+ if (_hidden) {
+ return nullptr;
+ }
+
+ ParamNotebookWdg * nb = Gtk::manage(new ParamNotebookWdg(this, doc, node));
+
+ // add pages (if any)
+ int i = -1;
+ int pagenr = i;
+ for (auto page:pages) {
+ i++;
+ Gtk::Widget * widg = page->get_widget(doc, node, changeSignal);
+ nb->append_page(*widg, _(page->get_text()));
+ if (!strcmp(_value, page->name())) {
+ pagenr = i; // this is the page to be displayed?
+ }
+ }
+
+ nb->show();
+
+ if (pagenr >= 0) nb->set_current_page(pagenr);
+
+ return dynamic_cast<Gtk::Widget *>(nb);
+}
+
+
+} // namespace Extension
+} // namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/extension/prefdialog/param/notebook.h b/src/extension/prefdialog/param/notebook.h
new file mode 100644
index 000000000..dd7154f06
--- /dev/null
+++ b/src/extension/prefdialog/param/notebook.h
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INK_EXTENSION_PARAMNOTEBOOK_H_SEEN
+#define INK_EXTENSION_PARAMNOTEBOOK_H_SEEN
+
+/** \file
+ * Notebook parameter for extensions.
+ */
+
+/*
+ * Author:
+ * Johan Engelen <johan@shouraizou.nl>
+ * Jon A. Cruz <jon@joncruz.org>
+ *
+ * Copyright (C) 2006 Author
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "parameter.h"
+
+namespace Gtk {
+class Widget;
+}
+
+namespace Inkscape {
+namespace Extension {
+
+class Extension;
+
+
+/** A class to represent a notebookparameter of an extension. */
+class ParamNotebook : public Parameter {
+private:
+ /**
+ * Internal value. This should point to a string that has
+ * been allocated in memory. And should be free'd.
+ * It is the name of the current page.
+ */
+ gchar * _value;
+
+ /**
+ * A class to represent the pages of a notebookparameter of an extension.
+ */
+ class ParamNotebookPage : public Parameter {
+ private:
+ std::vector<Parameter *> parameters; /**< A table to store the parameters for this page.
+ This only gets created if there are parameters on this
+ page */
+
+ public:
+ static ParamNotebookPage * makepage (Inkscape::XML::Node * in_repr, Inkscape::Extension::Extension * in_ext);
+
+ ParamNotebookPage(const gchar * name,
+ const gchar * text,
+ const gchar * description,
+ bool hidden,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml);
+ ~ParamNotebookPage() override;
+
+ Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal) override;
+ void paramString (std::list <std::string> &list);
+ gchar * get_text () {return _text;};
+ Parameter * get_param (const gchar * name) override;
+ }; /* class ParamNotebookPage */
+
+
+ std::vector<ParamNotebookPage*> pages; /**< A table to store the pages with parameters for this notebook.
+ This only gets created if there are pages in this
+ notebook */
+public:
+ ParamNotebook(const gchar * name,
+ const gchar * text,
+ const gchar * description,
+ bool hidden,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml);
+ ~ParamNotebook() override;
+
+ Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal) override;
+
+ /**
+ * A function to get the currentpage and the parameters in a string form.
+ * @return A string with the 'value' and all the parameters on all pages as command line arguments.
+ */
+ void string (std::list <std::string> &list) const override;
+
+ // Explicitly call superclass version to avoid method being hidden.
+ void string(std::string &string) const override {return Parameter::string(string);}
+
+
+ Parameter * get_param (const gchar * name) override;
+
+ const gchar * get (const SPDocument * /*doc*/, const Inkscape::XML::Node * /*node*/) { return _value; }
+ const gchar * set (const int in, SPDocument * doc, Inkscape::XML::Node * node);
+}; /* class ParamNotebook */
+
+
+
+
+
+} // namespace Extension
+} // namespace Inkscape
+
+#endif /* INK_EXTENSION_PARAMNOTEBOOK_H_SEEN */
+
+/*
+ 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/extension/prefdialog/param/parameter.cpp b/src/extension/prefdialog/param/parameter.cpp
new file mode 100644
index 000000000..1a60ef32e
--- /dev/null
+++ b/src/extension/prefdialog/param/parameter.cpp
@@ -0,0 +1,461 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * Parameters for extensions.
+ */
+/* Author:
+ * Ted Gould <ted@gould.cx>
+ * Johan Engelen <johan@shouraizou.nl>
+ * Jon A. Cruz <jon@joncruz.org>
+ *
+ * Copyright (C) 2005-2007 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifdef linux // does the dollar sign need escaping when passed as string parameter?
+# define ESCAPE_DOLLAR_COMMANDLINE
+#endif
+
+#include <cstring>
+#include <list>
+
+#include <glibmm/i18n.h>
+#include <sigc++/sigc++.h>
+
+#include "bool.h"
+#include "color.h"
+#include "description.h"
+#include "enum.h"
+#include "./float.h"
+#include "int.h"
+#include "notebook.h"
+#include "parameter.h"
+#include "radiobutton.h"
+#include "./string.h"
+
+#include "extension/extension.h"
+
+#include "object/sp-defs.h"
+
+#include "ui/widget/color-notebook.h"
+
+#include "xml/node.h"
+
+
+namespace Inkscape {
+namespace Extension {
+
+Parameter *Parameter::make(Inkscape::XML::Node *in_repr, Inkscape::Extension::Extension *in_ext)
+{
+ const char *name = in_repr->attribute("name");
+ const char *type = in_repr->attribute("type");
+
+ // we can't create a parameter without type
+ if (!type) {
+ return nullptr;
+ }
+ // also require name unless it's a pure UI element that does not store its value
+ if (!name) {
+ static std::vector<std::string> ui_elements = {"description"};
+ if (std::find(ui_elements.begin(), ui_elements.end(), type) == ui_elements.end()) {
+ return nullptr;
+ }
+ }
+
+ const char *text = in_repr->attribute("gui-text");
+ if (text == nullptr) {
+ text = in_repr->attribute("_gui-text");
+ if (text == nullptr) {
+ // text = ""; // probably better to require devs to explicitly set an empty gui-text if this is what they want
+ } else {
+ const char *context = in_repr->attribute("msgctxt");
+ if (context != nullptr) {
+ text = g_dpgettext2(nullptr, context, text);
+ } else {
+ text = _(text);
+ }
+ }
+ }
+ const char *description = in_repr->attribute("gui-description");
+ if (description == nullptr) {
+ description = in_repr->attribute("_gui-description");
+ if (description != nullptr) {
+ const char *context = in_repr->attribute("msgctxt");
+ if (context != nullptr) {
+ description = g_dpgettext2(nullptr, context, description);
+ } else {
+ description = _(description);
+ }
+ }
+ }
+ bool hidden = false;
+ {
+ const char *gui_hide = in_repr->attribute("gui-hidden");
+ if (gui_hide != nullptr) {
+ if (strcmp(gui_hide, "1") == 0 ||
+ strcmp(gui_hide, "true") == 0) {
+ hidden = true;
+ }
+ /* else stays false */
+ }
+ }
+ int indent = 0;
+ {
+ const char *indent_attr = in_repr->attribute("indent");
+ if (indent_attr != nullptr) {
+ if (strcmp(indent_attr, "true") == 0) {
+ indent = 1;
+ } else {
+ indent = atoi(indent_attr);
+ }
+ }
+ }
+ const gchar* appearance = in_repr->attribute("appearance");
+
+ Parameter * param = nullptr;
+ if (!strcmp(type, "boolean")) {
+ param = new ParamBool(name, text, description, hidden, indent, in_ext, in_repr);
+ } else if (!strcmp(type, "int")) {
+ if (appearance && !strcmp(appearance, "full")) {
+ param = new ParamInt(name, text, description, hidden, indent, in_ext, in_repr, ParamInt::FULL);
+ } else {
+ param = new ParamInt(name, text, description, hidden, indent, in_ext, in_repr, ParamInt::MINIMAL);
+ }
+ } else if (!strcmp(type, "float")) {
+ if (appearance && !strcmp(appearance, "full")) {
+ param = new ParamFloat(name, text, description, hidden, indent, in_ext, in_repr, ParamFloat::FULL);
+ } else {
+ param = new ParamFloat(name, text, description, hidden, indent, in_ext, in_repr, ParamFloat::MINIMAL);
+ }
+ } else if (!strcmp(type, "string")) {
+ param = new ParamString(name, text, description, hidden, indent, in_ext, in_repr);
+ gchar const * max_length = in_repr->attribute("max_length");
+ if (max_length != nullptr) {
+ ParamString * ps = dynamic_cast<ParamString *>(param);
+ ps->setMaxLength(atoi(max_length));
+ }
+ } else if (!strcmp(type, "description")) {
+ ParamDescription::AppearanceMode appearance_mode = ParamDescription::DESCRIPTION;
+ if (appearance) {
+ if (!strcmp(appearance, "header")) {
+ appearance_mode = ParamDescription::HEADER;
+ } else if (!strcmp(appearance, "url")) {
+ appearance_mode = ParamDescription::URL;
+ }
+ }
+ param = new ParamDescription(name, text, description, hidden, indent, in_ext, in_repr, appearance_mode);
+ } else if (!strcmp(type, "enum")) {
+ param = new ParamComboBox(name, text, description, hidden, indent, in_ext, in_repr);
+ } else if (!strcmp(type, "notebook")) {
+ param = new ParamNotebook(name, text, description, hidden, indent, in_ext, in_repr);
+ } else if (!strcmp(type, "optiongroup")) {
+ if (appearance && !strcmp(appearance, "minimal")) {
+ param = new ParamRadioButton(name, text, description, hidden, indent, in_ext, in_repr, ParamRadioButton::MINIMAL);
+ } else {
+ param = new ParamRadioButton(name, text, description, hidden, indent, in_ext, in_repr, ParamRadioButton::FULL);
+ }
+ } else if (!strcmp(type, "color")) {
+ param = new ParamColor(name, text, description, hidden, indent, in_ext, in_repr);
+ }
+
+ // Note: param could equal NULL
+ return param;
+}
+
+bool Parameter::get_bool(SPDocument const *doc, Inkscape::XML::Node const *node) const
+{
+ ParamBool const *boolpntr = dynamic_cast<ParamBool const *>(this);
+ if (!boolpntr) {
+ throw Extension::param_not_bool_param();
+ }
+ return boolpntr->get(doc, node);
+}
+
+int Parameter::get_int(SPDocument const *doc, Inkscape::XML::Node const *node) const
+{
+ ParamInt const *intpntr = dynamic_cast<ParamInt const *>(this);
+ if (!intpntr) {
+ throw Extension::param_not_int_param();
+ }
+ return intpntr->get(doc, node);
+}
+
+float Parameter::get_float(SPDocument const *doc, Inkscape::XML::Node const *node) const
+{
+ ParamFloat const *floatpntr = dynamic_cast<ParamFloat const *>(this);
+ if (!floatpntr) {
+ throw Extension::param_not_float_param();
+ }
+ return floatpntr->get(doc, node);
+}
+
+gchar const *Parameter::get_string(SPDocument const *doc, Inkscape::XML::Node const *node) const
+{
+ ParamString const *stringpntr = dynamic_cast<ParamString const *>(this);
+ if (!stringpntr) {
+ throw Extension::param_not_string_param();
+ }
+ return stringpntr->get(doc, node);
+}
+
+gchar const *Parameter::get_enum(SPDocument const *doc, Inkscape::XML::Node const *node) const
+{
+ ParamComboBox const *param = dynamic_cast<ParamComboBox const *>(this);
+ if (!param) {
+ throw Extension::param_not_enum_param();
+ }
+ return param->get(doc, node);
+}
+
+bool Parameter::get_enum_contains(gchar const * value, SPDocument const *doc, Inkscape::XML::Node const *node) const
+{
+ ParamComboBox const *param = dynamic_cast<ParamComboBox const *>(this);
+ if (!param) {
+ throw Extension::param_not_enum_param();
+ }
+ return param->contains(value, doc, node);
+}
+
+gchar const *Parameter::get_optiongroup(SPDocument const *doc, Inkscape::XML::Node const * node) const
+{
+ ParamRadioButton const *param = dynamic_cast<ParamRadioButton const *>(this);
+ if (!param) {
+ throw Extension::param_not_optiongroup_param();
+ }
+ return param->get(doc, node);
+}
+
+guint32 Parameter::get_color(const SPDocument* doc, Inkscape::XML::Node const *node) const
+{
+ ParamColor const *param = dynamic_cast<ParamColor const *>(this);
+ if (!param) {
+ throw Extension::param_not_color_param();
+ }
+ return param->get(doc, node);
+}
+
+bool Parameter::set_bool(bool in, SPDocument * doc, Inkscape::XML::Node * node)
+{
+ ParamBool * boolpntr = dynamic_cast<ParamBool *>(this);
+ if (boolpntr == nullptr)
+ throw Extension::param_not_bool_param();
+ return boolpntr->set(in, doc, node);
+}
+
+int Parameter::set_int(int in, SPDocument * doc, Inkscape::XML::Node * node)
+{
+ ParamInt * intpntr = dynamic_cast<ParamInt *>(this);
+ if (intpntr == nullptr)
+ throw Extension::param_not_int_param();
+ return intpntr->set(in, doc, node);
+}
+
+/** Wrapper to cast to the object and use it's function. */
+float
+Parameter::set_float (float in, SPDocument * doc, Inkscape::XML::Node * node)
+{
+ ParamFloat * floatpntr;
+ floatpntr = dynamic_cast<ParamFloat *>(this);
+ if (floatpntr == nullptr)
+ throw Extension::param_not_float_param();
+ return floatpntr->set(in, doc, node);
+}
+
+/** Wrapper to cast to the object and use it's function. */
+gchar const *
+Parameter::set_string (gchar const * in, SPDocument * doc, Inkscape::XML::Node * node)
+{
+ ParamString * stringpntr = dynamic_cast<ParamString *>(this);
+ if (stringpntr == nullptr)
+ throw Extension::param_not_string_param();
+ return stringpntr->set(in, doc, node);
+}
+
+gchar const * Parameter::set_optiongroup( gchar const * in, SPDocument * doc, Inkscape::XML::Node * node )
+{
+ ParamRadioButton *param = dynamic_cast<ParamRadioButton *>(this);
+ if (!param) {
+ throw Extension::param_not_optiongroup_param();
+ }
+ return param->set(in, doc, node);
+}
+
+gchar const *Parameter::set_enum( gchar const * in, SPDocument * doc, Inkscape::XML::Node * node )
+{
+ ParamComboBox *param = dynamic_cast<ParamComboBox *>(this);
+ if (!param) {
+ throw Extension::param_not_enum_param();
+ }
+ return param->set(in, doc, node);
+}
+
+
+/** Wrapper to cast to the object and use it's function. */
+guint32
+Parameter::set_color (guint32 in, SPDocument * doc, Inkscape::XML::Node * node)
+{
+ ParamColor* param = dynamic_cast<ParamColor *>(this);
+ if (param == nullptr)
+ throw Extension::param_not_color_param();
+ return param->set(in, doc, node);
+}
+
+
+/** Oop, now that we need a parameter, we need it's name. */
+Parameter::Parameter(gchar const * name, gchar const * text, gchar const * description, bool hidden, int indent, Inkscape::Extension::Extension * ext) :
+ _description(nullptr),
+ _text(nullptr),
+ _hidden(hidden),
+ _indent(indent),
+ _extension(ext),
+ _name(nullptr)
+{
+ if (name != nullptr) {
+ _name = g_strdup(name);
+ }
+
+ if (description != nullptr) {
+ _description = g_strdup(description);
+ }
+
+ if (text != nullptr) {
+ _text = g_strdup(text);
+ } else {
+ _text = g_strdup(name);
+ }
+
+ return;
+}
+
+/** Oop, now that we need a parameter, we need it's name. */
+Parameter::Parameter (gchar const * name, gchar const * text, Inkscape::Extension::Extension * ext) :
+ _description(nullptr),
+ _text(nullptr),
+ _hidden(false),
+ _indent(0),
+ _extension(ext),
+ _name(nullptr)
+{
+ if (name != nullptr) {
+ _name = g_strdup(name);
+ }
+ if (text != nullptr) {
+ _text = g_strdup(text);
+ } else {
+ _text = g_strdup(name);
+ }
+
+ return;
+}
+
+Parameter::~Parameter()
+{
+ g_free(_name);
+ _name = nullptr;
+
+ g_free(_text);
+ _text = nullptr;
+
+ g_free(_description);
+ _description = nullptr;
+}
+
+gchar *Parameter::pref_name() const
+{
+ return g_strdup_printf("%s.%s", _extension->get_id(), _name);
+}
+
+Inkscape::XML::Node *
+Parameter::find_child (Inkscape::XML::Node * adult)
+{
+ return sp_repr_lookup_child(adult, "name", _name);
+}
+
+Inkscape::XML::Node *
+Parameter::new_child (Inkscape::XML::Node * parent)
+{
+ Inkscape::XML::Node * retval;
+ retval = parent->document()->createElement("inkscape:extension-param");
+ retval->setAttribute("name", _name);
+
+ parent->appendChild(retval);
+ Inkscape::GC::release(retval);
+ return retval;
+}
+
+Inkscape::XML::Node *Parameter::document_param_node(SPDocument * doc)
+{
+ Inkscape::XML::Document *xml_doc = doc->getReprDoc();
+ Inkscape::XML::Node * defs = doc->getDefs()->getRepr();
+ Inkscape::XML::Node * params = nullptr;
+
+ GQuark const name_quark = g_quark_from_string("inkscape:extension-params");
+
+ for (Inkscape::XML::Node * child = defs->firstChild();
+ child != nullptr;
+ child = child->next()) {
+ if ((GQuark)child->code() == name_quark &&
+ !strcmp(child->attribute("extension"), _extension->get_id())) {
+ params = child;
+ break;
+ }
+ }
+
+ if (params == nullptr) {
+ params = xml_doc->createElement("inkscape:extension-param");
+ params->setAttribute("extension", _extension->get_id());
+ defs->appendChild(params);
+ Inkscape::GC::release(params);
+ }
+
+ return params;
+}
+
+/** Basically, if there is no widget pass a NULL. */
+Gtk::Widget *
+Parameter::get_widget (SPDocument * /*doc*/, Inkscape::XML::Node * /*node*/, sigc::signal<void> * /*changeSignal*/)
+{
+ return nullptr;
+}
+
+/** If I'm not sure which it is, just don't return a value. */
+void Parameter::string(std::string &/*string*/) const
+{
+ // TODO investigate clearing the target string.
+}
+
+void Parameter::string(std::list <std::string> &list) const
+{
+ std::string value;
+ string(value);
+ if (!value.empty()) {
+ std::string final;
+ final += "--";
+ final += name();
+ final += "=";
+ final += value;
+
+ list.insert(list.end(), final);
+ }
+}
+
+Parameter *Parameter::get_param(gchar const * /*name*/)
+{
+ return nullptr;
+}
+
+Glib::ustring const extension_pref_root = "/extensions/";
+
+} // namespace Extension
+} // namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/extension/prefdialog/param/parameter.h b/src/extension/prefdialog/param/parameter.h
new file mode 100644
index 000000000..c37d1539a
--- /dev/null
+++ b/src/extension/prefdialog/param/parameter.h
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * Parameters for extensions.
+ */
+/* Authors:
+ * Ted Gould <ted@gould.cx>
+ * Jon A. Cruz <jon@joncruz.org>
+ *
+ * Copyright (C) 2005-2006 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifndef SEEN_INK_EXTENSION_PARAM_H__
+#define SEEN_INK_EXTENSION_PARAM_H__
+
+#include <string>
+
+#include <glibmm/ustring.h>
+
+class SPDocument;
+
+namespace Gtk {
+class Widget;
+}
+
+namespace Inkscape {
+namespace XML {
+class Node;
+}
+
+namespace Extension {
+
+class Extension;
+
+
+/**
+ * The root directory in the preferences database for extension-related parameters.
+ *
+ * The directory path has both a leading and a trailing slash, so that extension_pref_root + pref_name works
+ * without having to append a separator.
+ */
+extern Glib::ustring const extension_pref_root;
+
+/**
+ * A class to represent the parameter of an extension.
+ *
+ * This is really a super class that allows them to abstract all
+ * the different types of parameters into some that can be passed
+ * around. There is also a few functions that are used by all the
+ * different parameters.
+ */
+class Parameter {
+
+public:
+ Parameter(gchar const *name,
+ gchar const *text,
+ gchar const *description,
+ bool hidden,
+ int indent,
+ Inkscape::Extension::Extension * ext);
+
+ Parameter(gchar const *name,
+ gchar const *text,
+ Inkscape::Extension::Extension * ext);
+
+ virtual ~Parameter();
+
+ /** Wrapper to cast to the object and use its function. */
+ bool get_bool(SPDocument const *doc, Inkscape::XML::Node const *node) const;
+
+ /** Wrapper to cast to the object and use it's function. */
+ int get_int(SPDocument const *doc, Inkscape::XML::Node const *node) const;
+
+ /** Wrapper to cast to the object and use it's function. */
+ float get_float(SPDocument const *doc, Inkscape::XML::Node const *node) const;
+
+ /** Wrapper to cast to the object and use it's function. */
+ gchar const *get_string(SPDocument const *doc, Inkscape::XML::Node const *node) const;
+
+ guint32 get_color(SPDocument const *doc, Inkscape::XML::Node const *node) const;
+
+ /** Wrapper to cast to the object and use it's function. */
+ gchar const *get_enum(SPDocument const *doc, Inkscape::XML::Node const *node) const;
+
+ /** Wrapper to cast to the object and use it's function. */
+ bool get_enum_contains(gchar const * value, SPDocument const *doc, Inkscape::XML::Node const *node) const;
+
+ /** Wrapper to cast to the object and use it's function. */
+ gchar const *get_optiongroup(SPDocument const * doc, Inkscape::XML::Node const *node) const;
+
+ /** Wrapper to cast to the object and use it's function. */
+ bool set_bool(bool in, SPDocument * doc, Inkscape::XML::Node * node);
+
+ /** Wrapper to cast to the object and use it's function. */
+ int set_int(int in, SPDocument * doc, Inkscape::XML::Node * node);
+
+ float set_float(float in, SPDocument * doc, Inkscape::XML::Node * node);
+
+ gchar const *set_optiongroup(gchar const *in, SPDocument * doc, Inkscape::XML::Node *node);
+
+ gchar const *set_enum(gchar const * in, SPDocument * doc, Inkscape::XML::Node *node);
+
+ gchar const *set_string(gchar const * in, SPDocument * doc, Inkscape::XML::Node * node);
+
+ guint32 set_color(guint32 in, SPDocument * doc, Inkscape::XML::Node * node);
+
+ gchar const * name() const {return _name;}
+
+ /**
+ * This function creates a parameter that can be used later. This
+ * is typically done in the creation of the extension and defined
+ * in the XML file describing the extension (it's private so people
+ * have to use the system) :)
+ *
+ * This function first grabs all of the data out of the Repr and puts
+ * it into local variables. Actually, these are just pointers, and the
+ * data is not duplicated so we need to be careful with it. If there
+ * isn't a name or a type in the XML, then no parameter is created as
+ * the function just returns.
+ *
+ * From this point on, we're pretty committed as we've allocated an
+ * object and we're starting to fill it. The name is set first, and
+ * is created with a strdup to actually allocate memory for it. Then
+ * there is a case statement (roughly because strcmp requires 'ifs')
+ * based on what type of parameter this is. Depending which type it
+ * is, the value is interpreted differently, but they are relatively
+ * straight forward. In all cases the value is set to the default
+ * value from the XML and the type is set to the interpreted type.
+ *
+ * @param in_repr The XML describing the parameter.
+ * @return a pointer to a new Parameter if applicable, null otherwise..
+ */
+ static Parameter *make(Inkscape::XML::Node * in_repr, Inkscape::Extension::Extension * in_ext);
+
+ virtual Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal);
+
+ gchar const * get_tooltip() const { return _description; }
+
+ /** Indicates if the GUI for this parameter is hidden or not */
+ bool get_hidden() const { return _hidden; }
+
+ /** Indentation level of the parameter */
+ int get_indent() const { return _indent; }
+
+ virtual void string(std::list <std::string> &list) const;
+
+ /**
+ * Gets the current value of the parameter in a string form.
+ * @return A string with the 'value'.
+ */
+ virtual void string(std::string &string) const;
+
+ /** All the code in Notebook::get_param to get the notebook content. */
+ virtual Parameter *get_param(gchar const *name);
+
+
+ /** Recommended margin of boxes containing multiple Parameters (in px) */
+ const static int GUI_BOX_MARGIN = 10;
+ /** Recommended spacing between multiple Parameters packed into a box (in px) */
+ const static int GUI_BOX_SPACING = 4;
+ /** Recommended spacing between the widgets making up a single Parameter (e.g. label and input) (in px) */
+ const static int GUI_PARAM_WIDGETS_SPACING = 4;
+ /** Recommended indentation width of parameters (in px) */
+ const static int GUI_INDENTATION = 12;
+ /** Recommended maximum line length for wrapping textual parameters (in chars) */
+ const static int GUI_MAX_LINE_LENGTH = 60;
+
+
+protected:
+ /** Parameter text to show as the GUI label. */
+ gchar * _text;
+
+ /** Extended description of the parameter (crrently shown as tooltip on hover). */
+ gchar * _description;
+
+ /** Whether the parameter is visible. */
+ bool _hidden;
+
+ /** Indentation level of the parameter. */
+ int _indent;
+
+
+ /* **** funcs **** */
+
+ /**
+ * Build the name to write the parameter from the extension's ID and the name of this parameter.
+ */
+ gchar *pref_name() const;
+
+ Inkscape::XML::Node * find_child (Inkscape::XML::Node * adult);
+
+ Inkscape::XML::Node * document_param_node (SPDocument * doc);
+
+ Inkscape::XML::Node * new_child (Inkscape::XML::Node * parent);
+
+private:
+ /** Which extension is this parameter attached to. */
+ Inkscape::Extension::Extension *_extension;
+
+ /** The name of this parameter. */
+ gchar *_name;
+};
+
+} // namespace Extension
+} // namespace Inkscape
+
+#endif // SEEN_INK_EXTENSION_PARAM_H__
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/extension/prefdialog/param/radiobutton.cpp b/src/extension/prefdialog/param/radiobutton.cpp
new file mode 100644
index 000000000..8c1d6a5f7
--- /dev/null
+++ b/src/extension/prefdialog/param/radiobutton.cpp
@@ -0,0 +1,349 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** \file
+ * extension parameter for radiobuttons.
+ *
+ * It uses a Gtk:ComboBoxText widget in the extension UI.
+ */
+
+/*
+ * Author:
+ * Johan Engelen <johan@shouraizou.nl>
+ *
+ * Copyright (C) 2006-2007 Johan Engelen
+ * Copyright (C) 2008 Jon A. Cruz
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <gtkmm/box.h>
+#include <gtkmm/comboboxtext.h>
+#include <gtkmm/radiobutton.h>
+#include <glibmm/i18n.h>
+
+#include "xml/node.h"
+#include "extension/extension.h"
+#include "preferences.h"
+
+#include "radiobutton.h"
+
+/**
+ * The root directory in the preferences database for extension
+ * related parameters.
+ */
+#define PREF_DIR "extensions"
+
+namespace Inkscape {
+namespace Extension {
+
+/* For internal use only.
+ Note that value and text MUST be non-NULL. This is ensured by newing only at one location in the code where non-NULL checks are made. */
+
+ParamRadioButton::ParamRadioButton(const gchar * name,
+ const gchar * text,
+ const gchar * description,
+ bool hidden,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml,
+ AppearanceMode mode)
+ : Parameter(name, text, description, hidden, indent, ext)
+ , _value(nullptr)
+ , _mode(mode)
+{
+ // Read XML tree to add enumeration items:
+ // printf("Extension Constructor: ");
+ if (xml != nullptr) {
+ Inkscape::XML::Node *child_repr = xml->firstChild();
+ while (child_repr != nullptr) {
+ char const * chname = child_repr->name();
+ if (!strcmp(chname, INKSCAPE_EXTENSION_NS "option") || !strcmp(chname, INKSCAPE_EXTENSION_NS "_option")) {
+ Glib::ustring * newtext = nullptr;
+ Glib::ustring * newvalue = nullptr;
+ const char * contents = child_repr->firstChild()->content();
+
+ if (contents != nullptr) {
+ // don't translate when 'item' but do translate when '_option'
+ if (!strcmp(chname, INKSCAPE_EXTENSION_NS "_option")) {
+ if (child_repr->attribute("msgctxt") != nullptr) {
+ newtext = new Glib::ustring(g_dpgettext2(nullptr, child_repr->attribute("msgctxt"), contents));
+ } else {
+ newtext = new Glib::ustring(_(contents));
+ }
+ } else {
+ newtext = new Glib::ustring(contents);
+ }
+ } else {
+ continue;
+ }
+
+
+ const char * val = child_repr->attribute("value");
+ if (val != nullptr) {
+ newvalue = new Glib::ustring(val);
+ } else {
+ newvalue = new Glib::ustring(contents);
+ }
+
+ if ( (newtext) && (newvalue) ) { // logical error if this is not true here
+ choices.push_back(new optionentry(newvalue, newtext));
+ }
+ }
+ child_repr = child_repr->next();
+ }
+ }
+
+ // Initialize _value with the default value from xml
+ // for simplicity : default to the contents of the first xml-child
+ const char * defaultval = nullptr;
+ if (!choices.empty()) {
+ defaultval = (static_cast<optionentry*> (choices[0]))->value->c_str();
+ }
+
+ gchar * pref_name = this->pref_name();
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ Glib::ustring paramval = prefs->getString(extension_pref_root + pref_name);
+ g_free(pref_name);
+
+ if (!paramval.empty()) {
+ defaultval = paramval.data();
+ }
+ if (defaultval != nullptr) {
+ _value = g_strdup(defaultval); // allocate space for _value
+ }
+}
+
+ParamRadioButton::~ParamRadioButton ()
+{
+ //destroy choice strings
+ for(auto i:choices) {
+ delete i;
+ }
+ g_free(_value);
+}
+
+
+/**
+ * A function to set the \c _value.
+ *
+ * This function sets ONLY the internal value, but it also sets the value
+ * in the preferences structure. To put it in the right place, \c PREF_DIR
+ * and \c pref_name() are used.
+ *
+ * To copy the data into _value the old memory must be free'd first.
+ * It is important to note that \c g_free handles \c NULL just fine. Then
+ * the passed in value is duplicated using \c g_strdup().
+ *
+ * @param in The value to set.
+ * @param doc A document that should be used to set the value.
+ * @param node The node where the value may be placed.
+ */
+const gchar *ParamRadioButton::set(const gchar * in, SPDocument * /*doc*/, Inkscape::XML::Node * /*node*/)
+{
+ if (in == nullptr) {
+ return nullptr; /* Can't have NULL string */
+ }
+
+ Glib::ustring * settext = nullptr;
+ for (auto entr:choices) {
+ if ( !entr->value->compare(in) ) {
+ settext = entr->value;
+ break; // break out of for loop
+ }
+ }
+ if (settext) {
+ if (_value != nullptr) {
+ g_free(_value);
+ }
+ _value = g_strdup(settext->c_str());
+ gchar * prefname = this->pref_name();
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setString(extension_pref_root + prefname, _value);
+ g_free(prefname);
+ } else {
+ g_warning("Couldn't set ParamRadioButton %s", in);
+ }
+
+ return _value;
+}
+
+void ParamRadioButton::string(std::string &string) const
+{
+ string += _value;
+}
+
+/** A special radiobutton class to use in ParamRadioButton. */
+class ParamRadioButtonWdg : public Gtk::RadioButton {
+private:
+ ParamRadioButton * _pref;
+ SPDocument * _doc;
+ Inkscape::XML::Node * _node;
+ sigc::signal<void> * _changeSignal;
+public:
+ /**
+ * Build a string preference for the given parameter.
+ * @param pref Where to put the radiobutton's string when it is selected.
+ */
+ ParamRadioButtonWdg ( Gtk::RadioButtonGroup& group, const Glib::ustring& label,
+ ParamRadioButton * pref, SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal ) :
+ Gtk::RadioButton(group, label), _pref(pref), _doc(doc), _node(node), _changeSignal(changeSignal) {
+ add_changesignal();
+ };
+ ParamRadioButtonWdg ( const Glib::ustring& label,
+ ParamRadioButton * pref, SPDocument * doc, Inkscape::XML::Node * node , sigc::signal<void> * changeSignal) :
+ Gtk::RadioButton(label), _pref(pref), _doc(doc), _node(node), _changeSignal(changeSignal) {
+ add_changesignal();
+ };
+ void add_changesignal() {
+ this->signal_toggled().connect(sigc::mem_fun(this, &ParamRadioButtonWdg::changed));
+ };
+ void changed ();
+};
+
+/**
+ * Respond to the selected radiobutton changing.
+ *
+ * This function responds to the radiobutton selection changing by grabbing the value
+ * from the text box and putting it in the parameter.
+ */
+void ParamRadioButtonWdg::changed()
+{
+ if (this->get_active()) {
+ Glib::ustring value = _pref->value_from_label(this->get_label());
+ _pref->set(value.c_str(), _doc, _node);
+ }
+ if (_changeSignal != nullptr) {
+ _changeSignal->emit();
+ }
+}
+
+
+class ComboWdg : public Gtk::ComboBoxText {
+private:
+ ParamRadioButton* _base;
+ SPDocument* _doc;
+ Inkscape::XML::Node* _node;
+ sigc::signal<void> * _changeSignal;
+
+public:
+ ComboWdg(ParamRadioButton* base, SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal) :
+ _base(base),
+ _doc(doc),
+ _node(node),
+ _changeSignal(changeSignal)
+ {
+ this->signal_changed().connect(sigc::mem_fun(this, &ComboWdg::changed));
+ }
+ ~ComboWdg() override = default;
+ void changed ();
+};
+
+void ComboWdg::changed()
+{
+ if ( _base ) {
+ Glib::ustring value = _base->value_from_label(get_active_text());
+ _base->set(value.c_str(), _doc, _node);
+ }
+ if (_changeSignal != nullptr) {
+ _changeSignal->emit();
+ }
+}
+
+/**
+ * Returns the value for the options label parameter
+ */
+Glib::ustring ParamRadioButton::value_from_label(const Glib::ustring label)
+{
+ Glib::ustring value = "";
+
+ for ( auto entr:choices) {
+ if ( !entr->text->compare(label) ) {
+ value = *(entr->value);
+ break;
+ }
+ }
+
+ return value;
+
+}
+
+/**
+ * Creates a combobox widget for an enumeration parameter.
+ */
+Gtk::Widget * ParamRadioButton::get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal)
+{
+ if (_hidden) {
+ return nullptr;
+ }
+
+ auto hbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, Parameter::GUI_PARAM_WIDGETS_SPACING));
+ hbox->set_homogeneous(false);
+ auto vbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0));
+ vbox->set_homogeneous(false);
+
+ Gtk::Label * label = Gtk::manage(new Gtk::Label(_text, Gtk::ALIGN_START, Gtk::ALIGN_START));
+ label->show();
+ hbox->pack_start(*label, false, false);
+
+ Gtk::ComboBoxText* cbt = nullptr;
+ bool comboSet = false;
+ if (_mode == MINIMAL) {
+ cbt = Gtk::manage(new ComboWdg(this, doc, node, changeSignal));
+ cbt->show();
+ vbox->pack_start(*cbt, false, false);
+ }
+
+ // add choice strings as radiobuttons
+ // and select last selected option (_value)
+ Gtk::RadioButtonGroup group;
+ for (auto entr:choices) {
+ Glib::ustring * text = entr->text;
+ switch ( _mode ) {
+ case MINIMAL:
+ {
+ cbt->append(*text);
+ if (!entr->value->compare(_value)) {
+ cbt->set_active_text(*text);
+ comboSet = true;
+ }
+ }
+ break;
+ case COMPACT:
+ case FULL:
+ {
+ ParamRadioButtonWdg * radio = Gtk::manage(new ParamRadioButtonWdg(group, *text, this, doc, node, changeSignal));
+ radio->show();
+ vbox->pack_start(*radio, true, true);
+ if (!entr->value->compare(_value)) {
+ radio->set_active();
+ }
+ }
+ break;
+ }
+ }
+
+ if ( (_mode == MINIMAL) && !comboSet) {
+ cbt->set_active(0);
+ }
+
+ vbox->show();
+ hbox->pack_end(*vbox, false, false);
+ hbox->show();
+
+
+ return dynamic_cast<Gtk::Widget *>(hbox);
+}
+
+
+} /* namespace Extension */
+} /* 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/extension/prefdialog/param/radiobutton.h b/src/extension/prefdialog/param/radiobutton.h
new file mode 100644
index 000000000..48d470a01
--- /dev/null
+++ b/src/extension/prefdialog/param/radiobutton.h
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INK_EXTENSION_PARAMRADIOBUTTON_H_SEEN
+#define INK_EXTENSION_PARAMRADIOBUTTON_H_SEEN
+
+/** \file
+ * Radiobutton parameter for extensions.
+ */
+
+/*
+ * Authors:
+ * Johan Engelen <johan@shouraizou.nl>
+ * Jon A. Cruz <jon@joncruz.org>
+ *
+ * Copyright (C) 2006-2007 Johan Engelen
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "parameter.h"
+
+namespace Gtk {
+class Widget;
+}
+
+namespace Inkscape {
+namespace Extension {
+
+class Extension;
+
+
+
+// \brief A class to represent a radiobutton parameter of an extension
+class ParamRadioButton : public Parameter {
+public:
+ enum AppearanceMode {
+ FULL, COMPACT, MINIMAL
+ };
+
+ ParamRadioButton(const gchar * name,
+ const gchar * text,
+ const gchar * description,
+ bool hidden,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml,
+ AppearanceMode mode);
+ ~ParamRadioButton() override;
+
+ Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal) override;
+
+ // Explicitly call superclass version to avoid method being hidden.
+ void string(std::list <std::string> &list) const override { return Parameter::string(list); }
+
+ void string(std::string &string) const override;
+
+ Glib::ustring value_from_label(const Glib::ustring label);
+
+ const gchar *get(const SPDocument * /*doc*/, const Inkscape::XML::Node * /*node*/) const { return _value; }
+
+ const gchar *set(const gchar *in, SPDocument *doc, Inkscape::XML::Node *node);
+
+private:
+ /** \brief Internal value. This should point to a string that has
+ been allocated in memory. And should be free'd.
+ It is the value of the current selected string */
+ gchar * _value;
+ AppearanceMode _mode;
+
+ /* For internal use only.
+ Note that value and text MUST be non-NULL. This is ensured by newing only at one location in the code where non-NULL checks are made. */
+ class optionentry {
+ public:
+ optionentry (Glib::ustring * val, Glib::ustring * txt) {
+ value = val;
+ text = txt;
+ }
+ ~optionentry() {
+ delete value;
+ delete text;
+ }
+ Glib::ustring * value;
+ Glib::ustring * text;
+ };
+
+ std::vector<optionentry*> choices; /**< A table to store the choice strings */
+
+}; /* class ParamRadioButton */
+
+
+
+
+
+} /* namespace Extension */
+} /* namespace Inkscape */
+
+#endif /* INK_EXTENSION_PARAMRADIOBUTTON_H_SEEN */
+
+/*
+ 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/extension/prefdialog/param/string.cpp b/src/extension/prefdialog/param/string.cpp
new file mode 100644
index 000000000..c1986cf92
--- /dev/null
+++ b/src/extension/prefdialog/param/string.cpp
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2005-2007 Authors:
+ * Ted Gould <ted@gould.cx>
+ * Johan Engelen <johan@shouraizou.nl> *
+ * Jon A. Cruz <jon@joncruz.org>
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <gtkmm/adjustment.h>
+#include <gtkmm/box.h>
+#include <gtkmm/spinbutton.h>
+
+#include <glibmm/value.h>
+
+#include "xml/node.h"
+#include "extension/extension.h"
+#include "./string.h"
+#include "preferences.h"
+#include <glibmm/i18n.h>
+
+namespace Inkscape {
+namespace Extension {
+
+
+/** Free the allocated data. */
+ParamString::~ParamString()
+{
+ g_free(_value);
+ _value = nullptr;
+}
+
+/**
+ * A function to set the \c _value.
+ *
+ * This function sets the internal value, but it also sets the value
+ * in the preferences structure. To put it in the right place, \c PREF_DIR
+ * and \c pref_name() are used.
+ *
+ * To copy the data into _value the old memory must be free'd first.
+ * It is important to note that \c g_free handles \c NULL just fine. Then
+ * the passed in value is duplicated using \c g_strdup().
+ *
+ * @param in The value to set to.
+ * @param doc A document that should be used to set the value.
+ * @param node The node where the value may be placed.
+ */
+const gchar * ParamString::set(const gchar * in, SPDocument * /*doc*/, Inkscape::XML::Node * /*node*/)
+{
+ if (in == nullptr) {
+ return nullptr; /* Can't have NULL string */
+ }
+
+ if (_value != nullptr) {
+ g_free(_value);
+ }
+
+ _value = g_strdup(in);
+
+ gchar * prefname = this->pref_name();
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setString(extension_pref_root + prefname, _value);
+ g_free(prefname);
+
+ return _value;
+}
+
+void ParamString::string(std::string &string) const
+{
+ if (_value) {
+ string += _value;
+ }
+}
+
+/** Initialize the object, to do that, copy the data. */
+ParamString::ParamString(const gchar * name,
+ const gchar * text,
+ const gchar * description,
+ bool hidden,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml)
+ : Parameter(name, text, description, hidden, indent, ext)
+ , _value(nullptr)
+{
+ const char * defaultval = nullptr;
+ if (xml->firstChild() != nullptr) {
+ defaultval = xml->firstChild()->content();
+ }
+
+ gchar * pref_name = this->pref_name();
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ Glib::ustring paramval = prefs->getString(extension_pref_root + pref_name);
+ g_free(pref_name);
+
+ if (!paramval.empty()) {
+ defaultval = paramval.data();
+ }
+ if (defaultval != nullptr) {
+ char const * chname = xml->name();
+ if (!strcmp(chname, INKSCAPE_EXTENSION_NS "_param")) {
+ if (xml->attribute("msgctxt") != nullptr) {
+ _value = g_strdup(g_dpgettext2(nullptr, xml->attribute("msgctxt"), defaultval));
+ } else {
+ _value = g_strdup(_(defaultval));
+ }
+ } else {
+ _value = g_strdup(defaultval);
+ }
+ }
+
+ _max_length = 0;
+}
+
+/** A special type of Gtk::Entry to handle string parameteres. */
+class ParamStringEntry : public Gtk::Entry {
+private:
+ ParamString * _pref;
+ SPDocument * _doc;
+ Inkscape::XML::Node * _node;
+ sigc::signal<void> * _changeSignal;
+public:
+ /**
+ * Build a string preference for the given parameter.
+ * @param pref Where to get the string from, and where to put it
+ * when it changes.
+ */
+ ParamStringEntry (ParamString * pref, SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal) :
+ Gtk::Entry(), _pref(pref), _doc(doc), _node(node), _changeSignal(changeSignal) {
+ if (_pref->get(nullptr, nullptr) != nullptr) {
+ this->set_text(Glib::ustring(_pref->get(nullptr, nullptr)));
+ }
+ this->set_max_length(_pref->getMaxLength()); //Set the max length - default zero means no maximum
+ this->signal_changed().connect(sigc::mem_fun(this, &ParamStringEntry::changed_text));
+ };
+ void changed_text ();
+};
+
+
+/**
+ * Respond to the text box changing.
+ *
+ * This function responds to the box changing by grabbing the value
+ * from the text box and putting it in the parameter.
+ */
+void ParamStringEntry::changed_text()
+{
+ Glib::ustring data = this->get_text();
+ _pref->set(data.c_str(), _doc, _node);
+ if (_changeSignal != nullptr) {
+ _changeSignal->emit();
+ }
+}
+
+/**
+ * Creates a text box for the string parameter.
+ *
+ * Builds a hbox with a label and a text box in it.
+ */
+Gtk::Widget * ParamString::get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal)
+{
+ if (_hidden) {
+ return nullptr;
+ }
+
+ Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, Parameter::GUI_PARAM_WIDGETS_SPACING));
+ Gtk::Label * label = Gtk::manage(new Gtk::Label(_text, Gtk::ALIGN_START));
+ label->show();
+ hbox->pack_start(*label, false, false);
+
+ ParamStringEntry * textbox = new ParamStringEntry(this, doc, node, changeSignal);
+ textbox->show();
+ hbox->pack_start(*textbox, true, true);
+
+ hbox->show();
+
+ return dynamic_cast<Gtk::Widget *>(hbox);
+}
+
+} /* namespace Extension */
+} /* namespace Inkscape */
diff --git a/src/extension/prefdialog/param/string.h b/src/extension/prefdialog/param/string.h
new file mode 100644
index 000000000..7c094d394
--- /dev/null
+++ b/src/extension/prefdialog/param/string.h
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef INK_EXTENSION_PARAMSTRING_H_SEEN
+#define INK_EXTENSION_PARAMSTRING_H_SEEN
+
+/*
+ * Copyright (C) 2005-2007 Authors:
+ * Ted Gould <ted@gould.cx>
+ * Johan Engelen <johan@shouraizou.nl> *
+ * Jon A. Cruz <jon@joncruz.org>
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "parameter.h"
+
+namespace Inkscape {
+namespace Extension {
+
+class ParamString : public Parameter {
+private:
+ /** \brief Internal value. This should point to a string that has
+ been allocated in memory. And should be free'd. */
+ gchar * _value;
+ /** \brief Internal value. This indicates the maximum length of the string. Zero meaning unlimited.
+ */
+ gint _max_length;
+public:
+ ParamString(const gchar * name,
+ const gchar * text,
+ const gchar * description,
+ bool hidden,
+ int indent,
+ Inkscape::Extension::Extension * ext,
+ Inkscape::XML::Node * xml);
+ ~ParamString() override;
+
+ /** \brief Returns \c _value, with a \i const to protect it. */
+ const gchar *get(SPDocument const * /*doc*/, Inkscape::XML::Node const * /*node*/) const { return _value; }
+
+ const gchar * set (const gchar * in, SPDocument * doc, Inkscape::XML::Node * node);
+
+ Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal) override;
+
+ // Explicitly call superclass version to avoid method being hidden.
+ void string(std::list <std::string> &list) const override { return Parameter::string(list); }
+
+ void string(std::string &string) const override;
+
+ void setMaxLength(int maxLength) { _max_length = maxLength; }
+ int getMaxLength() { return _max_length; }
+};
+
+
+} // namespace Extension
+} // namespace Inkscape
+
+#endif /* INK_EXTENSION_PARAMSTRING_H_SEEN */
+
+/*
+ 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/extension/prefdialog/prefdialog.cpp b/src/extension/prefdialog/prefdialog.cpp
new file mode 100644
index 000000000..6fbffb262
--- /dev/null
+++ b/src/extension/prefdialog/prefdialog.cpp
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Authors:
+ * Ted Gould <ted@gould.cx>
+ *
+ * Copyright (C) 2005-2008 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "prefdialog.h"
+
+#include <gtkmm/checkbutton.h>
+#include <gtkmm/separator.h>
+#include <glibmm/i18n.h>
+
+#include "ui/dialog-events.h"
+#include "xml/repr.h"
+
+// Used to get SP_ACTIVE_DESKTOP
+#include "inkscape.h"
+#include "desktop.h"
+#include "document.h"
+#include "document-undo.h"
+
+#include "extension/effect.h"
+#include "extension/execution-env.h"
+#include "extension/implementation/implementation.h"
+
+#include "param/parameter.h"
+
+
+namespace Inkscape {
+namespace Extension {
+
+
+/** \brief Creates a new preference dialog for extension preferences
+ \param name Name of the Extension whose dialog this is
+ \param help The help string for the extension (NULL if none)
+ \param controls The extension specific widgets in the dialog
+
+ This function initializes the dialog with the name of the extension
+ in the title. It adds a few buttons and sets up handlers for
+ them. It also places the passed-in widgets into the dialog.
+*/
+PrefDialog::PrefDialog (Glib::ustring name, gchar const * help, Gtk::Widget * controls, Effect * effect) :
+ Gtk::Dialog(_(name.c_str()), true),
+ _help(help),
+ _name(name),
+ _button_ok(nullptr),
+ _button_cancel(nullptr),
+ _button_preview(nullptr),
+ _param_preview(nullptr),
+ _effect(effect),
+ _exEnv(nullptr)
+{
+ this->set_default_size(0,0); // we want the window to be as small as possible instead of clobbering up space
+
+ Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox());
+ if (controls == nullptr) {
+ if (_effect == nullptr) {
+ std::cout << "AH!!! No controls and no effect!!!" << std::endl;
+ return;
+ }
+ controls = _effect->get_imp()->prefs_effect(_effect, SP_ACTIVE_DESKTOP, &_signal_param_change, nullptr);
+ _signal_param_change.connect(sigc::mem_fun(this, &PrefDialog::param_change));
+ }
+
+ hbox->pack_start(*controls, true, true, 0);
+ hbox->show();
+
+ this->get_content_area()->pack_start(*hbox, true, true, 0);
+
+ /*
+ Gtk::Button * help_button = add_button(Gtk::Stock::HELP, Gtk::RESPONSE_HELP);
+ if (_help == NULL)
+ help_button->set_sensitive(false);
+ */
+ _button_cancel = add_button(_effect == nullptr ? _("_Cancel") : _("_Close"), Gtk::RESPONSE_CANCEL);
+ _button_ok = add_button(_effect == nullptr ? _("_OK") : _("_Apply"), Gtk::RESPONSE_OK);
+ set_default_response(Gtk::RESPONSE_OK);
+ _button_ok->grab_focus();
+
+ if (_effect != nullptr && !_effect->no_live_preview) {
+ if (_param_preview == nullptr) {
+ XML::Document * doc = sp_repr_read_mem(live_param_xml, strlen(live_param_xml), nullptr);
+ if (doc == nullptr) {
+ std::cout << "Error encountered loading live parameter XML !!!" << std::endl;
+ return;
+ }
+ _param_preview = Parameter::make(doc->root(), _effect);
+ }
+
+ auto sep = Gtk::manage(new Gtk::Separator());
+ sep->show();
+
+ this->get_content_area()->pack_start(*sep, false, false, Parameter::GUI_BOX_SPACING);
+
+ hbox = Gtk::manage(new Gtk::HBox());
+ hbox->set_border_width(Parameter::GUI_BOX_MARGIN);
+ _button_preview = _param_preview->get_widget(nullptr, nullptr, &_signal_preview);
+ _button_preview->show();
+ hbox->pack_start(*_button_preview, true, true, 0);
+ hbox->show();
+
+ this->get_content_area()->pack_start(*hbox, false, false, 0);
+
+ Gtk::Box * hbox = dynamic_cast<Gtk::Box *>(_button_preview);
+ if (hbox != nullptr) {
+ _checkbox_preview = dynamic_cast<Gtk::CheckButton *>(hbox->get_children().front());
+ }
+
+ preview_toggle();
+ _signal_preview.connect(sigc::mem_fun(this, &PrefDialog::preview_toggle));
+ }
+
+ // Set window modality for effects that don't use live preview
+ if (_effect != nullptr && _effect->no_live_preview) {
+ set_modal(false);
+ }
+
+ GtkWidget *dlg = GTK_WIDGET(gobj());
+ sp_transientize(dlg);
+
+ return;
+}
+
+PrefDialog::~PrefDialog ( )
+{
+ if (_param_preview != nullptr) {
+ delete _param_preview;
+ _param_preview = nullptr;
+ }
+
+ if (_exEnv != nullptr) {
+ _exEnv->cancel();
+ delete _exEnv;
+ _exEnv = nullptr;
+ _effect->set_execution_env(_exEnv);
+ }
+
+ if (_effect != nullptr) {
+ _effect->set_pref_dialog(nullptr);
+ }
+
+ return;
+}
+
+#if 0
+/** \brief Runs the dialog
+ \return The response to the dialog
+
+ This function overrides the run function in the GTKmm dialog
+ class, but basically it only calls it. This function only
+ handles the \c Gtk::RESPONSE_HELP return, and in that case it
+ brings up the help window. All other return values are returned
+ to the calling function.
+*/
+int
+PrefDialog::run (void) {
+ int resp = Gtk::RESPONSE_HELP;
+ while (resp == Gtk::RESPONSE_HELP) {
+ resp = Gtk::Dialog::run();
+ if (resp == Gtk::RESPONSE_HELP) {
+ /*
+ if (_helpDialog == NULL) {
+ _helpDialog = new HelpDialog(_help);
+ }
+ */
+ }
+ }
+ return resp;
+}
+#endif
+
+void
+PrefDialog::preview_toggle () {
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ SPDocument *document = SP_ACTIVE_DOCUMENT;
+ bool modified = document->isModifiedSinceSave();
+ if(_param_preview->get_bool(nullptr, nullptr)) {
+ if (_exEnv == nullptr) {
+ set_modal(true);
+ _exEnv = new ExecutionEnv(_effect, SP_ACTIVE_DESKTOP, nullptr, false, false);
+ _effect->set_execution_env(_exEnv);
+ _exEnv->run();
+ }
+ } else {
+ set_modal(false);
+ if (_exEnv != nullptr) {
+ _exEnv->cancel();
+ _exEnv->undo();
+ _exEnv->reselect();
+ delete _exEnv;
+ _exEnv = nullptr;
+ _effect->set_execution_env(_exEnv);
+ }
+ }
+ document->setModifiedSinceSave(modified);
+}
+
+void
+PrefDialog::param_change () {
+ if (_exEnv != nullptr) {
+ if (!_effect->loaded()) {
+ _effect->set_state(Extension::STATE_LOADED);
+ }
+ _timersig.disconnect();
+ _timersig = Glib::signal_timeout().connect(sigc::mem_fun(this, &PrefDialog::param_timer_expire),
+ 250, /* ms */
+ Glib::PRIORITY_DEFAULT_IDLE);
+ }
+
+ return;
+}
+
+bool
+PrefDialog::param_timer_expire () {
+ if (_exEnv != nullptr) {
+ _exEnv->cancel();
+ _exEnv->undo();
+ _exEnv->reselect();
+ _exEnv->run();
+ }
+
+ return false;
+}
+
+void
+PrefDialog::on_response (int signal) {
+ if (signal == Gtk::RESPONSE_OK) {
+ if (_exEnv == nullptr) {
+ if (_effect != nullptr) {
+ _effect->effect(SP_ACTIVE_DESKTOP);
+ } else {
+ // Shutdown run()
+ return;
+ }
+ } else {
+ if (_exEnv->wait()) {
+ _exEnv->commit();
+ } else {
+ _exEnv->undo();
+ _exEnv->reselect();
+ }
+ delete _exEnv;
+ _exEnv = nullptr;
+ _effect->set_execution_env(_exEnv);
+ }
+ }
+
+ if (_param_preview != nullptr) {
+ _checkbox_preview->set_active(false);
+ }
+
+ if ((signal == Gtk::RESPONSE_CANCEL || signal == Gtk::RESPONSE_DELETE_EVENT) && _effect != nullptr) {
+ delete this;
+ }
+ return;
+}
+
+#include "extension/internal/clear-n_.h"
+
+const char * PrefDialog::live_param_xml = "<param name=\"__live_effect__\" type=\"boolean\" _gui-text=\"" N_("Live preview") "\" gui-description=\"" N_("Is the effect previewed live on canvas?") "\">false</param>";
+
+}; }; /* namespace Inkscape, Extension */
+
+/*
+ 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/extension/prefdialog/prefdialog.h b/src/extension/prefdialog/prefdialog.h
new file mode 100644
index 000000000..087900fbe
--- /dev/null
+++ b/src/extension/prefdialog/prefdialog.h
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Authors:
+ * Ted Gould <ted@gould.cx>
+ *
+ * Copyright (C) 2005,2007-2008 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifndef INKSCAPE_EXTENSION_DIALOG_H__
+#define INKSCAPE_EXTENSION_DIALOG_H__
+
+#include <gtkmm/dialog.h>
+#include <glibmm/value.h>
+#include <glibmm/ustring.h>
+
+namespace Gtk {
+class CheckButton;
+}
+
+namespace Inkscape {
+namespace Extension {
+class Effect;
+class ExecutionEnv;
+class Parameter;
+
+/** \brief A class to represent the preferences for an extension */
+class PrefDialog : public Gtk::Dialog {
+ /** \brief Help string if it exists */
+ gchar const * _help;
+ /** \brief Name of the extension */
+ Glib::ustring _name;
+
+ /** \brief A pointer to the OK button */
+ Gtk::Button * _button_ok;
+ /** \brief A pointer to the CANCEL button */
+ Gtk::Button * _button_cancel;
+
+ /** \brief Button to control live preview */
+ Gtk::Widget * _button_preview;
+ /** \brief Checkbox for the preview */
+ Gtk::CheckButton * _checkbox_preview;
+
+ /** \brief Parameter to control live preview */
+ Parameter * _param_preview;
+
+ /** \brief XML to define the live effects parameter on the dialog */
+ static const char * live_param_xml;
+
+ /** \brief Signal that the user is changing the live effect state */
+ sigc::signal<void> _signal_preview;
+ /** \brief Signal that one of the parameters change */
+ sigc::signal<void> _signal_param_change;
+
+ /** \brief If this is the preferences for an effect, the effect
+ that we're working with. */
+ Effect * _effect;
+ /** \brief If we're executing in preview mode here is the execution
+ environment for the effect. */
+ ExecutionEnv * _exEnv;
+
+ /** \brief The timer used to make it so that parameters don't respond
+ directly and allows for changes. */
+ sigc::connection _timersig;
+
+ void preview_toggle();
+ void param_change();
+ bool param_timer_expire();
+ void on_response (int signal) override;
+
+public:
+ PrefDialog (Glib::ustring name,
+ gchar const * help,
+ Gtk::Widget * controls = nullptr,
+ Effect * effect = nullptr);
+ ~PrefDialog () override;
+};
+
+
+};}; /* namespace Inkscape, Extension */
+
+#endif /* INKSCAPE_EXTENSION_DIALOG_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 :