/*
6 * Patrick Storz <eduard.braun2@gmx.de>
8 * Copyright (C) 2019 Authors
10 * Released under GNU GPL v2+, read the file
'COPYING' for more information.
20#include <boost/algorithm/string/case_conv.hpp>
21#include <boost/algorithm/string/join.hpp>
22#include <glibmm/fileutils.h>
23#include <glibmm/i18n.h>
24#include <glibmm/miscutils.h>
25#include <glibmm/regex.h>
26#include <giomm/file.h>
27#include <giomm/liststore.h>
29#include <gtkmm/button.h>
30#include <gtkmm/dialog.h>
31#include <gtkmm/entry.h>
32#include <gtkmm/error.h>
33#include <gtkmm/filedialog.h>
34#include <gtkmm/filefilter.h>
35#include <gtkmm/label.h>
36#include <sigc++/adaptors/bind.h>
37#include <sigc++/functors/mem_fun.h>
51 const char *value =
nullptr;
59 if (
_value.empty() && value) {
66 if (!strcmp(
mode,
"file")) {
68 }
else if (!strcmp(
mode,
"files")) {
71 }
else if (!strcmp(
mode,
"folder")) {
73 }
else if (!strcmp(
mode,
"folders")) {
76 }
else if (!strcmp(
mode,
"file_new")) {
77 _mode = Mode::file_new;
78 }
else if (!strcmp(
mode,
"folder_new")) {
79 _mode = Mode::folder_new;
81 g_warning(
"Invalid value ('%s') for mode of parameter '%s' in extension '%s'",
87 const char *filetypes = xml->
attribute(
"filetypes");
89 _filetypes = Glib::Regex::split_simple(
"," , filetypes);
113 if (!Glib::path_is_absolute(
_value) && !
_value.empty()) {
126class ParamPathEntry :
public Gtk::Entry {
129 sigc::signal<void ()> *_changeSignal;
136 ParamPathEntry(
ParamPath *pref, sigc::signal<
void ()> *changeSignal)
139 , _changeSignal(changeSignal)
141 this->set_text(_pref->
get());
142 this->signal_changed().connect(sigc::mem_fun(*
this, &ParamPathEntry::changed_text));
154void ParamPathEntry::changed_text()
158 if (_changeSignal !=
nullptr) {
159 _changeSignal->emit();
175 auto const label = Gtk::make_managed<Gtk::Label>(
_text, Gtk::Align::START);
176 label->set_visible(
true);
179 auto const textbox = Gtk::make_managed<ParamPathEntry>(
this, changeSignal);
180 textbox->set_visible(
true);
184 auto const button = Gtk::make_managed<Gtk::Button>(
"…");
185 button->set_visible(
true);
189 hbox->set_visible(
true);
199 Glib::ustring dialog_title;
200 if (
_mode == Mode::file) {
202 dialog_title = _(
"Select existing files");
204 dialog_title = _(
"Select existing file");
206 }
else if (
_mode == Mode::folder) {
208 dialog_title = _(
"Select existing folders");
210 dialog_title = _(
"Select existing folder");
212 }
else if (
_mode == Mode::file_new) {
213 dialog_title = _(
"Choose file name");
214 }
else if (
_mode == Mode::folder_new) {
215 dialog_title = _(
"Choose folder name");
217 g_assert_not_reached();
225 Glib::RefPtr<Gtk::FileFilter> file_filter = Gtk::FileFilter::create();
228 file_filter->add_pattern(Glib::ustring::compose(
"*.%1", filetype));
231 std::string filter_name = boost::algorithm::join(
_filetypes,
"+");
232 boost::algorithm::to_upper(filter_name);
233 file_filter->set_name(filter_name);
236 file_filters->append(file_filter);
243 auto first_filename =
_value.substr(0,
_value.find(
"|"));
245 if (!Glib::path_is_absolute(first_filename)) {
249 auto const dirname = Glib::path_get_dirname(first_filename);
250 if (Glib::file_test(dirname, Glib::FileTest::IS_DIR)) {
251 file_dialog->set_initial_folder(Gio::File::create_for_path(dirname));
254 if(
_mode == Mode::file_new ||
_mode == Mode::folder_new) {
255 file_dialog->set_initial_name(Glib::path_get_basename(first_filename));
257 if (Glib::file_test(first_filename, Glib::FileTest::EXISTS)) {
260 file_dialog->set_initial_file(Gio::File::create_for_path(first_filename));
271 file_dialog->open_multiple(std::move(slot));
273 file_dialog->open(std::move(slot));
279 file_dialog->select_multiple_folders(std::move(slot));
281 file_dialog->select_folder(std::move(slot));
286 case Mode::folder_new:
287 file_dialog->save(std::move(slot));
293[[nodiscard]]
static std::vector<Glib::RefPtr<Gio::File>>
295 Glib::RefPtr<Gtk::FileDialog >
const &file_dialog,
296 Mode const mode,
bool const select_multiple)
301 if (select_multiple) {
302 return file_dialog->open_multiple_finish(
result);
304 return {file_dialog->open_finish(
result)};
308 if (select_multiple) {
309 return file_dialog->select_multiple_folders_finish(
result);
311 return {file_dialog->select_folder_finish(
result)};
315 case Mode::folder_new:
316 return {file_dialog->save_finish(
result)};
320}
catch (Gtk::DialogError
const &error) {
321 if (error.code() == Gtk::DialogError::Code::DISMISSED) {
328 Glib::RefPtr<Gtk::FileDialog >
const &file_dialog)
331 if (files.empty())
return;
333 std::vector<Glib::ustring> filenames(files.size());
334 std::transform(files.cbegin(), files.cend(), filenames.begin(),
335 [](
auto const &
file){ return file->get_path(); });
336 auto const filenames_joined = boost::algorithm::join(filenames,
"|");
337 _entry->set_text(filenames_joined);
The object that is the basis for the Extension system.
char const * get_id() const
Get the ID of this extension - not a copy don't delete!
auto const & get_base_directory() const
A class to represent the parameter of an extension.
char * _name
The name of this parameter.
static constexpr int GUI_PARAM_WIDGETS_SPACING
Recommended spacing between the widgets making up a single Parameter (e.g.
Glib::ustring pref_name() const
Build preference name for the current parameter.
char * _text
Parameter text to show as the GUI label.
ParamPath(Inkscape::XML::Node *xml, Inkscape::Extension::Extension *ext)
Gtk::Widget * get_widget(sigc::signal< void()> *changeSignal) override
Creates a text box for the string parameter.
void on_file_dialog_response(Glib::RefPtr< Gio::AsyncResult > const &result, Glib::RefPtr< Gtk::FileDialog > const &file_dialog)
const std::string & set(const std::string &in) override
A function to set the _value.
std::string _value
Internal value.
ParamPathEntry * _entry
pointer to the parameters text entry keep this around, so we can update the value accordingly in on_b...
const std::string & get() const
Returns _value, with a \i const to protect it.
void on_button_clicked()
Create and show the file chooser dialog when the "…" button is clicked Then set the value of the Para...
std::vector< Glib::ustring > _filetypes
filetypes that should be selectable in file chooser
std::string value_to_string() const override
Gets the current value of the parameter in a string form.
Mode _mode
selection mode for the file chooser: files or folders?
void string_to_value(const std::string &in) override
Sets the current value of the parameter from a string.
bool _select_multiple
selection mode for the file chooser: multiple items?
Preference storage class.
Glib::ustring getString(Glib::ustring const &pref_path, Glib::ustring const &def="")
Retrieve an UTF-8 string.
static Preferences * get()
Access the singleton Preferences object.
void setString(Glib::ustring const &pref_path, Glib::ustring const &value)
Set an UTF-8 string value.
Interface for refcounted XML nodes.
virtual Node * firstChild()=0
Get the first child of this node.
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
virtual char const * content() const =0
Get the content of a text or comment node.
Inkscape::Extension::Extension: Frontend to certain, possibly pluggable, actions.
static std::vector< Glib::RefPtr< Gio::File > > get_files(Glib::RefPtr< Gio::AsyncResult > const &result, Glib::RefPtr< Gtk::FileDialog > const &file_dialog, Mode const mode, bool const select_multiple)
void pack_end(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the end of box.
void pack_start(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the start of box.
Glib::RefPtr< Gtk::FileDialog > create_file_dialog(Glib::ustring const &title, Glib::ustring const &accept_label)
Create a Gtk::FileDialog with the given title and label for its default/accept button.
void set_filters(Gtk::FileDialog &file_dialog, Glib::RefPtr< Gio::ListStore< Gtk::FileFilter > > const &filters)
Set available filters to a given list, & default to its 1st filter (if any).
Helpers for using Gtk::Boxes, encapsulating large changes between GTK3 & GTK4.
Path parameter for extensions.
Singleton class to access the preferences file in a convenient way.
char const * get_text(Gtk::Editable const &editable)
Get the text from a GtkEditable without the temporary copy imposed by gtkmm.