Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
effect.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Authors:
4 * Ted Gould <ted@gould.cx>
5 * Abhishek Sharma
6 * Sushant A.A. <sushant.co19@gmail.com>
7 *
8 * Copyright (C) 2002-2007 Authors
9 *
10 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
11 */
12
13#include "effect.h"
14
15#include <string>
16
17#include <glibmm/fileutils.h>
18#include <glibmm/miscutils.h>
19
20#include "execution-env.h"
21#include "extension/output.h"
23#include "inkscape.h"
24#include "streq.h"
25#include "timer.h"
26
28#include "extension/internal/filter/filter.h" // for Filter
30#include "io/sys.h"
32#include "xml/node.h"
33
34/* Inkscape::Extension::Effect */
35
36namespace Inkscape {
37namespace Extension {
38
40
41Effect::Effect(Inkscape::XML::Node *in_repr, ImplementationHolder implementation, std::string *base_directory, std::string* file_name)
42 : Extension(in_repr, std::move(implementation), base_directory)
43 , _menu_node(nullptr)
44 , _prefDialog(nullptr)
45{
46 // can't use document level because it is not defined
47 static auto app = InkscapeApplication::instance();
48
49 if (!app) {
50 // This happens during tests.
51 return;
52 }
53
55 return;
56 }
57
58 // This is a weird hack
59 if (!strcmp(this->get_id(), "org.inkscape.filter.dropshadow"))
60 return;
61
62 if (file_name) {
63 _file_name = *file_name;
64 }
65
66 no_doc = false;
67 no_live_preview = false;
68
69 if (repr != nullptr) {
70 for (Inkscape::XML::Node *child = repr->firstChild(); child != nullptr; child = child->next()) {
71 // look for "effect"
72 if (strcmp(child->name(), INKSCAPE_EXTENSION_NS "effect")) continue;
73
74 if (child->attribute("needs-document") && !strcmp(child->attribute("needs-document"), "false")) {
75 no_doc = true;
76 }
77 if (child->attribute("needs-live-preview") && !strcmp(child->attribute("needs-live-preview"), "false")) {
78 no_live_preview = true;
79 }
80 if (child->attribute("implements-custom-gui") && !strcmp(child->attribute("implements-custom-gui"), "true")) {
81 _workingDialog = false;
82 if (!(child->attribute("show-stderr") && !strcmp(child->attribute("show-stderr"), "true"))) {
83 ignore_stderr = true;
84 }
85 if (child->attribute("pipe-diffs") && !strcmp(child->attribute("pipe-diffs"), "true")) {
86 pipe_diffs = true;
87 }
88 }
89 for (auto effect_child = child->firstChild(); effect_child != nullptr; effect_child = effect_child->next()) {
90 if (!strcmp(effect_child->name(), INKSCAPE_EXTENSION_NS "effects-menu")) {
91 _local_effects_menu = effect_child->firstChild();
92 if (effect_child->attribute("hidden") && !strcmp(effect_child->attribute("hidden"), "true")) {
93 _hidden_from_menu = true;
94 }
95 }
96 if (!strcmp(effect_child->name(), INKSCAPE_EXTENSION_NS "menu-tip") ||
97 !strcmp(effect_child->name(), INKSCAPE_EXTENSION_NS "_menu-tip")) {
98 _menu_tip = effect_child->firstChild()->content();
99 }
100 if (streq(effect_child->name(), INKSCAPE_EXTENSION_NS "icon")) {
101 _icon_path = effect_child->firstChild()->content();
102 }
103 } // children of "effect"
104 break; // there can only be one effect
105 } // children of "inkscape-extension"
106 } // if we have an XML file
107
108 _filter_effect = dynamic_cast<Internal::Filter::Filter *>(imp.get()) != nullptr;
109}
110
114std::string Effect::get_sanitized_id() const
115{
116 std::string id = get_id();
117 auto allowed = [] (char ch) {
118 // Note: std::isalnum() is locale-dependent
119 if ('A' <= ch && ch <= 'Z') return true;
120 if ('a' <= ch && ch <= 'z') return true;
121 if ('0' <= ch && ch <= '9') return true;
122 if (ch == '.' || ch == '-') return true;
123 return false;
124 };
125
126 // Silently replace any underscores with dashes.
127 std::replace(id.begin(), id.end(), '_', '-');
128
129 // Detect remaining invalid characters and print a warning if found
130 bool errored = false;
131 for (auto &ch : id) {
132 if (!allowed(ch)) {
133 if (!errored) {
134 auto message = std::string{"Invalid extension action ID found: \""} + id + "\".";
135 g_warn_message("Inkscape", __FILE__, __LINE__, "Effect::_sanitizeId()", message.c_str());
136 errored = true;
137 }
138 ch = 'X';
139 }
140 }
141 return id;
142}
143
144
145void Effect::get_menu(Inkscape::XML::Node * pattern, std::list<Glib::ustring>& sub_menu_list) const
146{
147 if (!pattern) {
148 return;
149 }
150
151 Glib::ustring merge_name;
152
153 gchar const *menu_name = pattern->attribute("name");
154 if (!menu_name) {
155 menu_name = pattern->attribute("_name");
156 }
157 if (!menu_name) {
158 return;
159 }
160
162 merge_name = get_translation(menu_name);
163 } else {
164 merge_name = _(menu_name);
165 }
166
167 // Making sub menu string
168 sub_menu_list.push_back(merge_name);
169
170 get_menu(pattern->firstChild(), sub_menu_list);
171}
172
173void
175{
176 /* FIXME: https://gitlab.com/inkscape/inkscape/-/issues/4381
177 * Effects don't have actions anymore, so this is not possible:
178 if (action)
179 action->set_enabled(false);
180 if (action_noprefs)
181 action_noprefs->set_enabled(false);
182 */
184}
185
187{
188 if (get_last_effect() == this)
189 set_last_effect(nullptr);
190 if (_menu_node) {
191 if (_menu_node->parent()) {
193 }
195 }
196 return;
197}
198
199bool
201{
202 if (_prefDialog != nullptr) {
203 _prefDialog->present();
204 return true;
205 }
206
207 if (!widget_visible_count()) {
209 return true;
210 }
211
212 if (!loaded())
214 if (!loaded()) return false;
215
216 Glib::ustring name = this->get_name();
217 _prefDialog = new PrefDialog(name, nullptr, this);
218 _prefDialog->set_visible(true);
219
220 return true;
221}
222
234{
235 //printf("Execute effect\n");
236 if (!loaded())
238 if (!loaded()) return;
239 ExecutionEnv executionEnv(this, desktop, nullptr, _workingDialog, true);
240 if (document)
241 executionEnv.set_document(document);
242 timer->lock();
243 try {
244 executionEnv.run();
245 if (executionEnv.wait()) {
246 executionEnv.commit();
247 } else {
248 executionEnv.cancel();
249 }
251 }
252 timer->unlock();
253}
254
260void
262{
263 _last_effect = in_effect;
264 if (in_effect) {
266 }
267}
268
270Effect::find_menu (Inkscape::XML::Node * menustruct, const gchar *name)
271{
272 if (menustruct == nullptr) return nullptr;
273 for (Inkscape::XML::Node * child = menustruct;
274 child != nullptr;
275 child = child->next()) {
276 if (!strcmp(child->name(), name)) {
277 return child;
278 }
279 Inkscape::XML::Node * firstchild = child->firstChild();
280 if (firstchild != nullptr) {
281 Inkscape::XML::Node *found = find_menu (firstchild, name);
282 if (found) {
283 return found;
284 }
285 }
286 }
287 return nullptr;
288}
289
290
291Gtk::Box *
296
299{
300 return _prefDialog;
301}
302
303void
305{
306 _prefDialog = prefdialog;
307 return;
308}
309
310// Try locating effect's thumbnail file using:
311// <icon> path
312// or extension's file name
313// or extension's ID
314std::string Effect::find_icon_file(const std::string& default_dir) const {
315 auto& dir = _base_directory.empty() ? default_dir : _base_directory;
316
317 if (!dir.empty()) {
318 // icon path provided?
319 if (!_icon_path.empty()) {
320 auto path = Glib::build_filename(dir, _icon_path);
321 if (Glib::file_test(path, Glib::FileTest::IS_REGULAR)) {
322 return path;
323 }
324 }
325 else {
326 // fallback 1: try the same name as extension file, but with ".svg" instead of ".inx"
327 if (!_file_name.empty()) {
329 auto filename = _file_name.substr(0, _file_name.size() - ext.size());
330 auto path = Glib::build_filename(dir, filename + ".svg");
331 if (Glib::file_test(path, Glib::FileTest::IS_REGULAR)) {
332 return path;
333 }
334 }
335 // fallback 2: look for icon in extension's folder, inside "icons", this time using extension ID as a name
336 std::string id = get_id();
337 auto path = Glib::build_filename(dir, "icons", id + ".svg");
338 if (Glib::file_test(path, Glib::FileTest::IS_REGULAR)) {
339 return path;
340 }
341 }
342 }
343
344 return std::string();
345}
346
348 return _hidden_from_menu;
349}
350
352 return widget_visible_count() > 0;
353}
354
356 return _filter_effect;
357}
358
359const Glib::ustring& Effect::get_menu_tip() const {
360 return _menu_tip;
361}
362
363std::list<Glib::ustring> Effect::get_menu_list() const {
364 std::list<Glib::ustring> menu;
367
368 // remove "Filters" from sub menu hierarchy to keep it the same as extension effects
369 if (_filter_effect) menu.pop_front();
370 }
371 return menu;
372}
373
375 return get_imp()->apply_filter(this, item);
376}
377
378} } /* namespace Inkscape, Extension */
379
380/*
381 Local Variables:
382 mode:c++
383 c-file-style:"stroustrup"
384 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
385 indent-tabs-mode:nil
386 fill-column:99
387 End:
388*/
389// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
void enable_effect_actions(InkscapeApplication *app, bool enabled)
Authors: Sushant A A sushant.co19@gmail.com
static InkscapeApplication * instance()
Singleton instance.
static bool exists()
Checks whether the current Inkscape::Application global object exists.
Definition inkscape.cpp:165
Effects are extensions that take a document and do something to it in place.
Definition effect.h:39
bool ignore_stderr
If stderr log should be shown, when process return code is 0.
Definition effect.h:64
Effect(Inkscape::XML::Node *in_repr, ImplementationHolder implementation, std::string *base_directory, std::string *file_name)
Definition effect.cpp:41
void set_pref_dialog(PrefDialog *prefdialog)
Definition effect.cpp:304
Glib::ustring _menu_tip
Definition effect.h:116
PrefDialog * _prefDialog
The preference dialog if it is shown.
Definition effect.h:51
static void set_last_effect(Effect *in_effect)
Sets which effect was called last.
Definition effect.cpp:261
void get_menu(Inkscape::XML::Node *pattern, std::list< Glib::ustring > &sub_menu_list) const
Definition effect.cpp:145
bool hidden_from_menu() const
Definition effect.cpp:347
void deactivate() override
This function diactivates the extension (which makes it unusable, but not deleted)
Definition effect.cpp:174
Inkscape::XML::Node * _menu_node
Menu node created for this effect.
Definition effect.h:48
Inkscape::XML::Node * find_menu(Inkscape::XML::Node *menustruct, const gchar *name)
Definition effect.cpp:270
Inkscape::XML::Node * _local_effects_menu
Definition effect.h:117
PrefDialog * get_pref_dialog()
Definition effect.cpp:298
std::string find_icon_file(const std::string &default_dir) const
Definition effect.cpp:314
const Glib::ustring & get_menu_tip() const
Definition effect.cpp:359
bool pipe_diffs
If changesets should be piped in via stdin.
Definition effect.h:67
static Effect * get_last_effect()
Static function to get the last effect used.
Definition effect.h:70
bool apply_filter(SPItem *item)
Definition effect.cpp:374
bool is_filter_effect() const
Definition effect.cpp:355
static Effect * _last_effect
This is the last effect that was used.
Definition effect.h:42
std::string get_sanitized_id() const
Sanitizes the id and returns.
Definition effect.cpp:114
std::list< Glib::ustring > get_menu_list() const
Definition effect.cpp:363
void effect(SPDesktop *desktop, SPDocument *document=nullptr)
The function that 'does' the effect itself.
Definition effect.cpp:233
Gtk::Box * get_info_widget()
Definition effect.cpp:292
bool _workingDialog
Whether a working dialog should be shown.
Definition effect.h:61
bool wait()
Wait for the effect to complete if it hasn't.
void set_document(SPDocument *document)
void commit()
Commit the changes to the document.
void cancel()
Cancel the execution of the effect.
void run()
Starts the execution of the effect.
The object that is the basis for the Extension system.
Definition extension.h:133
virtual void deactivate()
This function diactivates the extension (which makes it unusable, but not deleted)
char const * get_translation(char const *msgid, char const *msgctxt=nullptr) const
Gets a translation within the context of the current extension.
std::unique_ptr< ExpirationTimer > timer
Timeout to unload after a given time.
Definition extension.h:165
@ STATE_LOADED
The extension has been loaded successfully.
Definition extension.h:137
unsigned int widget_visible_count() const
A function to get the number of visible parameters of the extension.
char const * get_name() const
Get the name of this extension - not a copy don't delete!
Implementation::Implementation * get_imp()
Definition extension.h:191
Inkscape::XML::Node * repr
The XML description of the Extension.
Definition extension.h:156
ImplementationHolder imp
An Implementation object provides the actual implementation of the Extension.
Definition extension.h:162
char const * get_id() const
Get the ID of this extension - not a copy don't delete!
void set_state(state_t in_state)
A function to set whether the extension should be loaded or unloaded.
bool _translation_enabled
Attempt translation of strings provided by the extension?
Definition extension.h:166
bool loaded()
A quick function to test the state of the extension.
std::string _base_directory
Directory containing the .inx file, relative paths in the extension should usually be relative to it.
Definition extension.h:163
virtual bool prefs()
Create a dialog for preference for this extension.
virtual bool apply_filter(Inkscape::Extension::Effect *module, SPItem *item)
A class to represent the preferences for an extension.
Definition prefdialog.h:31
Interface for refcounted XML nodes.
Definition node.h:80
virtual Node * parent()=0
Get the parent of this node.
virtual Node * next()=0
Get the next sibling of this node.
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.
virtual void removeChild(Node *child)=0
Remove a child of this node.
To do: update description of desktop.
Definition desktop.h:149
Typed SVG document implementation.
Definition document.h:103
Base class for visual SVG elements.
Definition sp-item.h:109
SPItem * item
Geom::Point end
static R & release(R &r)
Decrements the reference count of a anchored object.
Glib::ustring get_file_extension(Glib::ustring const &path)
Definition sys.cpp:214
Helper class to stream background task notifications as a series of messages.
STL namespace.
Ocnode * child[8]
Definition quantize.cpp:33
TODO: insert short description here.
bool streq(char const *a, char const *b)
Convenience/readability wrapper for strcmp(a,b)==0.
Definition streq.h:17
Document was closed during execution of async extension.
Definition output.h:43
SPDesktop * desktop
Glib::ustring name
Definition toolbars.cpp:55
Interface for XML nodes.