summaryrefslogtreecommitdiffstats
path: root/src/extension
diff options
context:
space:
mode:
authorPatrick Storz <eduard.braun2@gmx.de>2019-08-12 20:49:30 +0000
committerPatrick Storz <eduard.braun2@gmx.de>2019-08-31 14:50:39 +0000
commit10845e6925978cd9cf5bc9d0961802e06c9b3799 (patch)
treed9c6493e7198f1f8c6e94b010e7f03cb5401a893 /src/extension
parentAdjust <print> extensions for previous commit. (diff)
downloadinkscape-10845e6925978cd9cf5bc9d0961802e06c9b3799.tar.gz
inkscape-10845e6925978cd9cf5bc9d0961802e06c9b3799.zip
Add new parameter of type "path"
* Similar to parameter's of type "string": Has a text entry and stores a string preference * Additionally offers a button to show a file chooser dialog * The node's content is the initial (default) file path. * Relative paths will be considered relative to the extension's .inx file. This allows to reference files/folders shipped with the extension in a portable way. The stored value as well as the parameter value passed to the script interpreter will always be absolute, though. * The attribute "mode" controls what type(s) of paths the file chooser allows to select. Valid values: - 'file', 'files', 'folder', 'folder' (pick existing items) - 'file_new', 'folder_new' (create a new file/folder) * Note that manually entered values will be passed as-is without checking for existence.
Diffstat (limited to 'src/extension')
-rw-r--r--src/extension/CMakeLists.txt2
-rw-r--r--src/extension/prefdialog/parameter-path.cpp282
-rw-r--r--src/extension/prefdialog/parameter-path.h75
-rw-r--r--src/extension/prefdialog/parameter.cpp3
4 files changed, 362 insertions, 0 deletions
diff --git a/src/extension/CMakeLists.txt b/src/extension/CMakeLists.txt
index be8ee38f1..5708c2ee0 100644
--- a/src/extension/CMakeLists.txt
+++ b/src/extension/CMakeLists.txt
@@ -60,6 +60,7 @@ set(extension_SRC
prefdialog/parameter-int.cpp
prefdialog/parameter-notebook.cpp
prefdialog/parameter-optiongroup.cpp
+ prefdialog/parameter-path.cpp
prefdialog/parameter-string.cpp
prefdialog/widget.cpp
prefdialog/widget-box.cpp
@@ -139,6 +140,7 @@ set(extension_SRC
prefdialog/parameter-int.h
prefdialog/parameter-notebook.h
prefdialog/parameter-optiongroup.h
+ prefdialog/parameter-path.h
prefdialog/parameter-string.h
prefdialog/widget.h
prefdialog/widget-box.h
diff --git a/src/extension/prefdialog/parameter-path.cpp b/src/extension/prefdialog/parameter-path.cpp
new file mode 100644
index 000000000..76774eb46
--- /dev/null
+++ b/src/extension/prefdialog/parameter-path.cpp
@@ -0,0 +1,282 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * Path parameter for extensions
+ *//*
+ * Authors:
+ * Patrick Storz <eduard.braun2@gmx.de>
+ *
+ * Copyright (C) 2019 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "parameter-path.h"
+
+#include <boost/algorithm/string/case_conv.hpp>
+#include <boost/algorithm/string/join.hpp>
+
+#include <glibmm/i18n.h>
+#include <glibmm/fileutils.h>
+#include <glibmm/miscutils.h>
+#include <glibmm/regex.h>
+
+#include <gtkmm/box.h>
+#include <gtkmm/button.h>
+#include <gtkmm/dialog.h>
+#include <gtkmm/entry.h>
+
+#include "include/gtkmm_version.h"
+#if GTKMM_CHECK_VERSION(3,24,0) // unfortunately GtkFileChooserNative (since gtk 3.20) was not wrapped until gtkmm 3.24
+# include <gtkmm/filechoosernative.h>
+#else
+# include <gtkmm/filechooserdialog.h>
+#endif
+
+#include "xml/node.h"
+#include "extension/extension.h"
+#include "preferences.h"
+
+namespace Inkscape {
+namespace Extension {
+
+ParamPath::ParamPath(Inkscape::XML::Node *xml, Inkscape::Extension::Extension *ext)
+ : InxParameter(xml, ext)
+{
+ // get value
+ const char *value = nullptr;
+ if (xml->firstChild()) {
+ value = xml->firstChild()->content();
+ }
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ _value = prefs->getString(pref_name());
+
+ if (_value.empty() && value) {
+ _value = value;
+ }
+
+ // parse selection mode
+ const char *mode = xml->attribute("mode");
+ if (mode) {
+ if (!strcmp(mode, "file")) {
+ // this is the default
+ } else if (!strcmp(mode, "files")) {
+ _select_multiple = true;
+ } else if (!strcmp(mode, "folder")) {
+ _mode = FOLDER;
+ } else if (!strcmp(mode, "folders")) {
+ _mode = FOLDER;
+ _select_multiple = true;
+ } else if (!strcmp(mode, "file_new")) {
+ _mode = FILE_NEW;
+ } else if (!strcmp(mode, "folder_new")) {
+ _mode = FOLDER_NEW;
+ } else {
+ g_warning("Invalid value ('%s') for mode of parameter '%s' in extension '%s'",
+ mode, _name, _extension->get_id());
+ }
+ }
+
+ // parse filetypes
+ const char *filetypes = xml->attribute("filetypes");
+ if (filetypes) {
+ _filetypes = Glib::Regex::split_simple("," , filetypes);
+ }
+}
+
+/**
+ * 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_name() is used.
+ *
+ * @param in The value to set to.
+ */
+const std::string& ParamPath::set(const std::string in)
+{
+ _value = in;
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setString(pref_name(), _value);
+
+ return _value;
+}
+
+std::string ParamPath::value_to_string() const
+{
+ if (!Glib::path_is_absolute(_value)) {
+ return Glib::build_filename(_extension->get_base_directory(), _value);
+ } else {
+ return _value;
+ }
+}
+
+
+/** A special type of Gtk::Entry to handle path parameters. */
+class ParamPathEntry : public Gtk::Entry {
+private:
+ ParamPath *_pref;
+ 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.
+ */
+ ParamPathEntry(ParamPath *pref, sigc::signal<void> *changeSignal)
+ : Gtk::Entry()
+ , _pref(pref)
+ , _changeSignal(changeSignal)
+ {
+ this->set_text(_pref->get());
+ this->signal_changed().connect(sigc::mem_fun(this, &ParamPathEntry::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 ParamPathEntry::changed_text()
+{
+ std::string data = this->get_text();
+ _pref->set(data.c_str());
+ 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 *ParamPath::get_widget(sigc::signal<void> *changeSignal)
+{
+ if (_hidden) {
+ return nullptr;
+ }
+
+ Gtk::HBox *hbox = Gtk::manage(new Gtk::HBox(false, GUI_PARAM_WIDGETS_SPACING));
+ Gtk::Label *label = Gtk::manage(new Gtk::Label(_text, Gtk::ALIGN_START));
+ label->show();
+ hbox->pack_start(*label, false, false);
+
+ ParamPathEntry *textbox = new ParamPathEntry(this, changeSignal);
+ textbox->show();
+ hbox->pack_start(*textbox, true, true);
+ _entry = textbox;
+
+ Gtk::Button *button = Gtk::manage(new Gtk::Button("…"));
+ button->show();
+ hbox->pack_end(*button, false, false);
+ button->signal_clicked().connect(sigc::mem_fun(*this, &ParamPath::on_button_clicked));
+
+ hbox->show();
+
+ return dynamic_cast<Gtk::Widget *>(hbox);
+}
+
+/**
+ * Create and show the file chooser dialog when the "…" button is clicked
+ * Then set the value of the ParamPathEntry holding the current value accordingly
+ */
+void ParamPath::on_button_clicked()
+{
+ // set-up action and dialog title according to 'mode'
+ Gtk::FileChooserAction action;
+ std::string dialog_title;
+ if (_mode == FILE) {
+ // pick the "save" variants here - otherwise the dialog will only accept existing files
+ action = Gtk::FILE_CHOOSER_ACTION_OPEN;
+ if (_select_multiple) {
+ dialog_title = _("Select existing files");
+ } else {
+ dialog_title = _("Select existing file");
+ }
+ } else if (_mode == FOLDER) {
+ // pick the "create" variant here - otherwise the dialog will only accept existing folders
+ action = Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER;
+ if (_select_multiple) {
+ dialog_title = _("Select existing folders");
+ } else {
+ dialog_title = _("Select existing folder");
+ }
+ } else if (_mode == FILE_NEW) {
+ action = Gtk::FILE_CHOOSER_ACTION_SAVE;
+ dialog_title = _("Choose file name");
+ } else if (_mode == FOLDER_NEW) {
+ action = Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER;
+ dialog_title = _("Choose folder name");
+ } else {
+ g_assert_not_reached();
+ }
+
+ // create file chooser dialog
+#if GTKMM_CHECK_VERSION(3,24,0) // unfortunately GtkFileChooserNative (since gtk 3.20) was not wrapped until gtkmm 3.24
+ Glib::RefPtr<Gtk::FileChooserNative> file_chooser =
+ Gtk::FileChooserNative::create(dialog_title + "…", action, _("Select"));
+#else
+ Gtk::FileChooserDialog file_chooser_instance(dialog_title + "…", action);
+ Gtk::FileChooserDialog *file_chooser = &file_chooser_instance;
+ file_chooser->add_button(_("Select"), Gtk::RESPONSE_ACCEPT);
+ file_chooser->add_button(_("Cancel"), Gtk::RESPONSE_CANCEL);
+#endif
+ file_chooser->set_select_multiple(_select_multiple);
+ file_chooser->set_do_overwrite_confirmation(true);
+ file_chooser->set_create_folders(true);
+
+ // set FileFilter according to 'filetype' attribute
+ if (!_filetypes.empty() && _mode != FOLDER && _mode != FOLDER_NEW) {
+ Glib::RefPtr<Gtk::FileFilter> file_filter = Gtk::FileFilter::create();
+
+ for (auto filetype : _filetypes) {
+ file_filter->add_pattern(Glib::ustring::compose("*.%1", filetype));
+ }
+
+ std::string filter_name = boost::algorithm::join(_filetypes, "+");
+ boost::algorithm::to_upper(filter_name);
+ file_filter->set_name(filter_name);
+
+ file_chooser->add_filter(file_filter);
+ }
+
+ // set current file/folder suitable for current value
+ // (use basepath of first filename; relative paths are considered relative to .inx file's location)
+ if (!_value.empty()) {
+ std::string first_filename = _value.substr(0, _value.find("|"));
+
+ if (!Glib::path_is_absolute(first_filename)) {
+ first_filename = Glib::build_filename(_extension->get_base_directory(), first_filename);
+ }
+
+ std::string dirname = Glib::path_get_dirname(first_filename);
+ if (Glib::file_test(dirname, Glib::FILE_TEST_IS_DIR)) {
+ file_chooser->set_current_folder(dirname);
+ }
+
+ if(_mode == FILE_NEW || _mode == FOLDER_NEW) {
+ file_chooser->set_current_name(Glib::path_get_basename(first_filename));
+ } else {
+ if (Glib::file_test(first_filename, Glib::FILE_TEST_EXISTS)) {
+ // TODO: This does not seem to work (at least on Windows)
+ // file_chooser->set_filename(first_filename);
+ }
+ }
+ }
+
+ // show dialog and parse result
+ int res = file_chooser->run();
+ if (res == Gtk::ResponseType::RESPONSE_ACCEPT) {
+ std::vector<std::string> filenames = file_chooser->get_filenames();
+ std::string filenames_joined = boost::algorithm::join(filenames, "|");
+ _entry->set_text(filenames_joined); // let the ParamPathEntry handle the rest (including setting the preference)
+ }
+}
+
+} /* namespace Extension */
+} /* namespace Inkscape */
diff --git a/src/extension/prefdialog/parameter-path.h b/src/extension/prefdialog/parameter-path.h
new file mode 100644
index 000000000..2de231190
--- /dev/null
+++ b/src/extension/prefdialog/parameter-path.h
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * Path parameter for extensions
+ *//*
+ * Authors:
+ * Patrick Storz <eduard.braun2@gmx.de>
+ *
+ * Copyright (C) 2019 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifndef INK_EXTENSION_PARAM_PATH_H_SEEN
+#define INK_EXTENSION_PARAM_PATH_H_SEEN
+
+#include "parameter.h"
+
+
+namespace Inkscape {
+namespace Extension {
+
+class ParamPathEntry;
+
+class ParamPath : public InxParameter {
+public:
+ ParamPath(Inkscape::XML::Node *xml, Inkscape::Extension::Extension *ext);
+
+ /** \brief Returns \c _value, with a \i const to protect it. */
+ const std::string& get() const { return _value; }
+ const std::string& set(const std::string in);
+
+ Gtk::Widget *get_widget(sigc::signal<void> *changeSignal) override;
+
+ std::string value_to_string() const override;
+
+private:
+ enum Mode {
+ FILE, FOLDER, FILE_NEW, FOLDER_NEW
+ };
+
+ /** \brief Internal value. */
+ std::string _value;
+
+ /** selection mode for the file chooser: files or folders? */
+ Mode _mode = FILE;
+
+ /** selection mode for the file chooser: multiple items? */
+ bool _select_multiple = false;
+
+ /** filetypes that should be selectable in file chooser */
+ std::vector<std::string> _filetypes;
+
+ /** pointer to the parameters text entry
+ * keep this around, so we can update the value accordingly in \a on_button_clicked() */
+ ParamPathEntry *_entry;
+
+ void on_button_clicked();
+};
+
+
+} // namespace Extension
+} // namespace Inkscape
+
+#endif /* INK_EXTENSION_PARAM_PATH_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/parameter.cpp b/src/extension/prefdialog/parameter.cpp
index 4519f8b5d..65072f6a8 100644
--- a/src/extension/prefdialog/parameter.cpp
+++ b/src/extension/prefdialog/parameter.cpp
@@ -25,6 +25,7 @@
#include "parameter-int.h"
#include "parameter-notebook.h"
#include "parameter-optiongroup.h"
+#include "parameter-path.h"
#include "parameter-string.h"
#include "widget.h"
#include "widget-label.h"
@@ -79,6 +80,8 @@ InxParameter *InxParameter::make(Inkscape::XML::Node *in_repr, Inkscape::Extensi
param = new ParamFloat(in_repr, in_ext);
} else if (!strcmp(type, "string")) {
param = new ParamString(in_repr, in_ext);
+ } else if (!strcmp(type, "path")) {
+ param = new ParamPath(in_repr, in_ext);
} else if (!strcmp(type, "description")) {
// support deprecated "description" for backwards-compatibility
in_repr->setAttribute("gui-text", "description"); // TODO: hack to allow descriptions to be parameters