Inkscape
Vector Graphics Editor
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"
22#include "inkscape.h"
23#include "streq.h"
24#include "timer.h"
25
27#include "extension/internal/filter/filter.h" // for Filter
29#include "io/sys.h"
31#include "xml/node.h"
32
33/* Inkscape::Extension::Effect */
34
35namespace Inkscape {
36namespace Extension {
37
39
40Effect::Effect(Inkscape::XML::Node *in_repr, ImplementationHolder implementation, std::string *base_directory, std::string* file_name)
41 : Extension(in_repr, std::move(implementation), base_directory)
42 , _menu_node(nullptr)
43 , _prefDialog(nullptr)
44{
45 // can't use document level because it is not defined
46 static auto app = InkscapeApplication::instance();
47
48 if (!app) {
49 // This happens during tests.
50 return;
51 }
52
54 return;
55 }
56
57 // This is a weird hack
58 if (!strcmp(this->get_id(), "org.inkscape.filter.dropshadow"))
59 return;
60
61 if (file_name) {
62 _file_name = *file_name;
63 }
64
65 no_doc = false;
66 no_live_preview = false;
67
68 if (repr != nullptr) {
69 for (Inkscape::XML::Node *child = repr->firstChild(); child != nullptr; child = child->next()) {
70 // look for "effect"
71 if (strcmp(child->name(), INKSCAPE_EXTENSION_NS "effect")) continue;
72
73 if (child->attribute("needs-document") && !strcmp(child->attribute("needs-document"), "false")) {
74 no_doc = true;
75 }
76 if (child->attribute("needs-live-preview") && !strcmp(child->attribute("needs-live-preview"), "false")) {
77 no_live_preview = true;
78 }
79 if (child->attribute("implements-custom-gui") && !strcmp(child->attribute("implements-custom-gui"), "true")) {
80 _workingDialog = false;
81 ignore_stderr = true;
82 }
83 for (auto effect_child = child->firstChild(); effect_child != nullptr; effect_child = effect_child->next()) {
84 if (!strcmp(effect_child->name(), INKSCAPE_EXTENSION_NS "effects-menu")) {
85 _local_effects_menu = effect_child->firstChild();
86 if (effect_child->attribute("hidden") && !strcmp(effect_child->attribute("hidden"), "true")) {
87 _hidden_from_menu = true;
88 }
89 }
90 if (!strcmp(effect_child->name(), INKSCAPE_EXTENSION_NS "menu-tip") ||
91 !strcmp(effect_child->name(), INKSCAPE_EXTENSION_NS "_menu-tip")) {
92 _menu_tip = effect_child->firstChild()->content();
93 }
94 if (streq(effect_child->name(), INKSCAPE_EXTENSION_NS "icon")) {
95 _icon_path = effect_child->firstChild()->content();
96 }
97 } // children of "effect"
98 break; // there can only be one effect
99 } // children of "inkscape-extension"
100 } // if we have an XML file
101
102 _filter_effect = dynamic_cast<Internal::Filter::Filter *>(imp.get()) != nullptr;
103}
104
108std::string Effect::get_sanitized_id() const
109{
110 std::string id = get_id();
111 auto allowed = [] (char ch) {
112 // Note: std::isalnum() is locale-dependent
113 if ('A' <= ch && ch <= 'Z') return true;
114 if ('a' <= ch && ch <= 'z') return true;
115 if ('0' <= ch && ch <= '9') return true;
116 if (ch == '.' || ch == '-') return true;
117 return false;
118 };
119
120 // Silently replace any underscores with dashes.
121 std::replace(id.begin(), id.end(), '_', '-');
122
123 // Detect remaining invalid characters and print a warning if found
124 bool errored = false;
125 for (auto &ch : id) {
126 if (!allowed(ch)) {
127 if (!errored) {
128 auto message = std::string{"Invalid extension action ID found: \""} + id + "\".";
129 g_warn_message("Inkscape", __FILE__, __LINE__, "Effect::_sanitizeId()", message.c_str());
130 errored = true;
131 }
132 ch = 'X';
133 }
134 }
135 return id;
136}
137
138
139void Effect::get_menu(Inkscape::XML::Node * pattern, std::list<Glib::ustring>& sub_menu_list) const
140{
141 if (!pattern) {
142 return;
143 }
144
145 Glib::ustring merge_name;
146
147 gchar const *menu_name = pattern->attribute("name");
148 if (!menu_name) {
149 menu_name = pattern->attribute("_name");
150 }
151 if (!menu_name) {
152 return;
153 }
154
156 merge_name = get_translation(menu_name);
157 } else {
158 merge_name = _(menu_name);
159 }
160
161 // Making sub menu string
162 sub_menu_list.push_back(merge_name);
163
164 get_menu(pattern->firstChild(), sub_menu_list);
165}
166
167void
169{
170 /* FIXME: https://gitlab.com/inkscape/inkscape/-/issues/4381
171 * Effects don't have actions anymore, so this is not possible:
172 if (action)
173 action->set_enabled(false);
174 if (action_noprefs)
175 action_noprefs->set_enabled(false);
176 */
178}
179
181{
182 if (get_last_effect() == this)
183 set_last_effect(nullptr);
184 if (_menu_node) {
185 if (_menu_node->parent()) {
187 }
189 }
190 return;
191}
192
193bool
195{
196 if (_prefDialog != nullptr) {
197 _prefDialog->present();
198 return true;
199 }
200
201 if (!widget_visible_count()) {
202 effect(desktop);
203 return true;
204 }
205
206 if (!loaded())
208 if (!loaded()) return false;
209
210 Glib::ustring name = this->get_name();
211 _prefDialog = new PrefDialog(name, nullptr, this);
212 _prefDialog->set_visible(true);
213
214 return true;
215}
216
227void Effect::effect(SPDesktop *desktop, SPDocument *document)
228{
229 //printf("Execute effect\n");
230 if (!loaded())
232 if (!loaded()) return;
233 ExecutionEnv executionEnv(this, desktop, nullptr, _workingDialog, true);
234 execution_env = &executionEnv;
235 if (document)
236 execution_env->set_document(document);
237 timer->lock();
238 executionEnv.run();
239 if (executionEnv.wait()) {
240 executionEnv.commit();
241 } else {
242 executionEnv.cancel();
243 }
244 timer->unlock();
245
246 return;
247}
248
254void
256{
257 _last_effect = in_effect;
258 if (in_effect) {
260 }
261}
262
264Effect::find_menu (Inkscape::XML::Node * menustruct, const gchar *name)
265{
266 if (menustruct == nullptr) return nullptr;
267 for (Inkscape::XML::Node * child = menustruct;
268 child != nullptr;
269 child = child->next()) {
270 if (!strcmp(child->name(), name)) {
271 return child;
272 }
273 Inkscape::XML::Node * firstchild = child->firstChild();
274 if (firstchild != nullptr) {
275 Inkscape::XML::Node *found = find_menu (firstchild, name);
276 if (found) {
277 return found;
278 }
279 }
280 }
281 return nullptr;
282}
283
284
285Gtk::Box *
287{
289}
290
293{
294 return _prefDialog;
295}
296
297void
299{
300 _prefDialog = prefdialog;
301 return;
302}
303
304// Try locating effect's thumbnail file using:
305// <icon> path
306// or extension's file name
307// or extension's ID
308std::string Effect::find_icon_file(const std::string& default_dir) const {
309 auto& dir = _base_directory.empty() ? default_dir : _base_directory;
310
311 if (!dir.empty()) {
312 // icon path provided?
313 if (!_icon_path.empty()) {
314 auto path = Glib::build_filename(dir, _icon_path);
315 if (Glib::file_test(path, Glib::FileTest::IS_REGULAR)) {
316 return path;
317 }
318 }
319 else {
320 // fallback 1: try the same name as extension file, but with ".svg" instead of ".inx"
321 if (!_file_name.empty()) {
323 auto filename = _file_name.substr(0, _file_name.size() - ext.size());
324 auto path = Glib::build_filename(dir, filename + ".svg");
325 if (Glib::file_test(path, Glib::FileTest::IS_REGULAR)) {
326 return path;
327 }
328 }
329 // fallback 2: look for icon in extension's folder, inside "icons", this time using extension ID as a name
330 std::string id = get_id();
331 auto path = Glib::build_filename(dir, "icons", id + ".svg");
332 if (Glib::file_test(path, Glib::FileTest::IS_REGULAR)) {
333 return path;
334 }
335 }
336 }
337
338 return std::string();
339}
340
342 return _hidden_from_menu;
343}
344
346 return widget_visible_count() > 0;
347}
348
350 return _filter_effect;
351}
352
353const Glib::ustring& Effect::get_menu_tip() const {
354 return _menu_tip;
355}
356
357std::list<Glib::ustring> Effect::get_menu_list() const {
358 std::list<Glib::ustring> menu;
361
362 // remove "Filters" from sub menu hierarchy to keep it the same as extension effects
363 if (_filter_effect) menu.pop_front();
364 }
365 return menu;
366}
367
369 return get_imp()->apply_filter(this, item);
370}
371
372} } /* namespace Inkscape, Extension */
373
374/*
375 Local Variables:
376 mode:c++
377 c-file-style:"stroustrup"
378 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
379 indent-tabs-mode:nil
380 fill-column:99
381 End:
382*/
383// 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, if it exists (will not create it)
static bool exists()
Checks whether the current Inkscape::Application global object exists.
Definition: inkscape.cpp:163
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:40
void set_pref_dialog(PrefDialog *prefdialog)
Definition: effect.cpp:298
Glib::ustring _menu_tip
Definition: effect.h:113
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:255
void get_menu(Inkscape::XML::Node *pattern, std::list< Glib::ustring > &sub_menu_list) const
Definition: effect.cpp:139
bool hidden_from_menu() const
Definition: effect.cpp:341
void deactivate() override
This function diactivates the extension (which makes it unusable, but not deleted)
Definition: effect.cpp:168
Inkscape::XML::Node * _menu_node
Menu node created for this effect.
Definition: effect.h:48
bool takes_input() const
Definition: effect.cpp:345
Inkscape::XML::Node * find_menu(Inkscape::XML::Node *menustruct, const gchar *name)
Definition: effect.cpp:264
Inkscape::XML::Node * _local_effects_menu
Definition: effect.h:114
PrefDialog * get_pref_dialog()
Definition: effect.cpp:292
std::string find_icon_file(const std::string &default_dir) const
Definition: effect.cpp:308
const Glib::ustring & get_menu_tip() const
Definition: effect.cpp:353
std::string _file_name
Definition: effect.h:110
static Effect * get_last_effect()
Static function to get the last effect used.
Definition: effect.h:67
bool apply_filter(SPItem *item)
Definition: effect.cpp:368
bool is_filter_effect() const
Definition: effect.cpp:349
static Effect * _last_effect
This is the last effect that was used.
Definition: effect.h:42
std::string _icon_path
Definition: effect.h:115
std::string get_sanitized_id() const
Sanitizes the id and returns.
Definition: effect.cpp:108
std::list< Glib::ustring > get_menu_list() const
Definition: effect.cpp:357
void effect(SPDesktop *desktop, SPDocument *document=nullptr)
The function that 'does' the effect itself.
Definition: effect.cpp:227
Gtk::Box * get_info_widget()
Definition: effect.cpp:286
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)
Definition: execution-env.h:80
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:130
virtual void deactivate()
This function diactivates the extension (which makes it unusable, but not deleted)
Definition: extension.cpp:347
ExecutionEnv * execution_env
Execution environment of the extension (currently only used by Effects)
Definition: extension.h:160
char const * get_translation(char const *msgid, char const *msgctxt=nullptr) const
Gets a translation within the context of the current extension.
Definition: extension.cpp:498
std::unique_ptr< ExpirationTimer > timer
Timeout to unload after a given time.
Definition: extension.h:164
@ STATE_LOADED
The extension has been loaded successfully.
Definition: extension.h:134
unsigned int widget_visible_count() const
A function to get the number of visible parameters of the extension.
Definition: extension.cpp:1044
char const * get_name() const
Get the name of this extension - not a copy don't delete!
Definition: extension.cpp:326
Implementation::Implementation * get_imp()
Definition: extension.h:190
Inkscape::XML::Node * repr
The XML description of the Extension.
Definition: extension.h:153
ImplementationHolder imp
An Implementation object provides the actual implementation of the Extension.
Definition: extension.h:159
char const * get_id() const
Get the ID of this extension - not a copy don't delete!
Definition: extension.cpp:316
void set_state(state_t in_state)
A function to set whether the extension should be loaded or unloaded.
Definition: extension.cpp:190
bool _translation_enabled
Attempt translation of strings provided by the extension?
Definition: extension.h:165
bool loaded()
A quick function to test the state of the extension.
Definition: extension.cpp:235
std::string _base_directory
Directory containing the .inx file, relative paths in the extension should usually be relative to it.
Definition: extension.h:162
virtual bool prefs()
Create a dialog for preference for this extension.
Definition: extension.cpp:1061
virtual bool apply_filter(Inkscape::Extension::Effect *module, SPItem *item)
A class to represent the preferences for an extension.
Definition: prefdialog.h:31
NonOwningPtr get() const
Interface for refcounted XML nodes.
Definition: node.h:80
virtual Node * parent()=0
Get the parent 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 void removeChild(Node *child)=0
Remove a child of this node.
To do: update description of desktop.
Definition: desktop.h:150
Typed SVG document implementation.
Definition: document.h:106
Base class for visual SVG elements.
Definition: sp-item.h:107
SPItem * item
Definition: imagemagick.cpp:43
Geom::Point end
static R & release(R &r)
Decrements the reference count of a anchored object.
Definition: gc-anchored.h:133
Glib::ustring get_file_extension(Glib::ustring const &path)
Definition: sys.cpp:207
bool file_test(char const *utf8name, GFileTest test)
Definition: sys.cpp:109
CMYK to sRGB conversion routines.
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
Interface for XML nodes.