diff options
| author | Patrick Storz <eduard.braun2@gmx.de> | 2019-07-21 13:24:05 +0000 |
|---|---|---|
| committer | Patrick Storz <eduard.braun2@gmx.de> | 2019-08-31 14:50:38 +0000 |
| commit | 7311f3ac5161a4d53cf2ed7e0a5a97993f646321 (patch) | |
| tree | 050dd572bffca377fd2d510597a28ccb8080fb1c /src/extension/prefdialog | |
| parent | Only register desktop on Dbus once (diff) | |
| download | inkscape-7311f3ac5161a4d53cf2ed7e0a5a97993f646321.tar.gz inkscape-7311f3ac5161a4d53cf2ed7e0a5a97993f646321.zip | |
Move prefdialog to own directory
Diffstat (limited to 'src/extension/prefdialog')
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 : |
