1 /*
2 * Authors:
3 * Ted Gould <ted@gould.cx>
4 * Abhishek Sharma
5 *
6 * Copyright (C) 2002-2007 Authors
7 *
8 * Released under GNU GPL, read the file 'COPYING' for more information
9 */
11 #include "inkscape-private.h"
12 #include "helper/action.h"
13 #include "ui/view/view.h"
14 #include "desktop-handles.h"
15 #include "selection.h"
16 #include "sp-namedview.h"
17 #include "desktop.h"
18 #include "implementation/implementation.h"
19 #include "effect.h"
20 #include "execution-env.h"
21 #include "timer.h"
25 /* Inkscape::Extension::Effect */
27 namespace Inkscape {
28 namespace Extension {
30 Effect * Effect::_last_effect = NULL;
31 Inkscape::XML::Node * Effect::_effects_list = NULL;
32 Inkscape::XML::Node * Effect::_filters_list = NULL;
34 #define EFFECTS_LIST "effects-list"
35 #define FILTERS_LIST "filters-list"
37 Effect::Effect (Inkscape::XML::Node * in_repr, Implementation::Implementation * in_imp)
38 : Extension(in_repr, in_imp),
39 _id_noprefs(Glib::ustring(get_id()) + ".noprefs"),
40 _name_noprefs(Glib::ustring(get_name()) + _(" (No preferences)")),
41 _verb(get_id(), get_name(), NULL, NULL, this, true),
42 _verb_nopref(_id_noprefs.c_str(), _name_noprefs.c_str(), NULL, NULL, this, false),
43 _menu_node(NULL), _workingDialog(true),
44 _prefDialog(NULL)
45 {
46 Inkscape::XML::Node * local_effects_menu = NULL;
48 // This is a weird hack
49 if (!strcmp(this->get_id(), "org.inkscape.filter.dropshadow"))
50 return;
52 bool hidden = false;
54 no_doc = false;
55 no_live_preview = false;
57 if (repr != NULL) {
59 for (Inkscape::XML::Node *child = sp_repr_children(repr); child != NULL; child = child->next()) {
60 if (!strcmp(child->name(), INKSCAPE_EXTENSION_NS "effect")) {
61 if (child->attribute("needs-document") && !strcmp(child->attribute("needs-document"), "false")) {
62 no_doc = true;
63 }
64 if (child->attribute("needs-live-preview") && !strcmp(child->attribute("needs-live-preview"), "false")) {
65 no_live_preview = true;
66 }
67 for (Inkscape::XML::Node *effect_child = sp_repr_children(child); effect_child != NULL; effect_child = effect_child->next()) {
68 if (!strcmp(effect_child->name(), INKSCAPE_EXTENSION_NS "effects-menu")) {
69 // printf("Found local effects menu in %s\n", this->get_name());
70 local_effects_menu = sp_repr_children(effect_child);
71 if (effect_child->attribute("hidden") && !strcmp(effect_child->attribute("hidden"), "true")) {
72 hidden = true;
73 }
74 }
75 if (!strcmp(effect_child->name(), INKSCAPE_EXTENSION_NS "menu-name") ||
76 !strcmp(effect_child->name(), INKSCAPE_EXTENSION_NS "_menu-name")) {
77 // printf("Found local effects menu in %s\n", this->get_name());
78 _verb.set_name(sp_repr_children(effect_child)->content());
79 }
80 if (!strcmp(effect_child->name(), INKSCAPE_EXTENSION_NS "menu-tip") ||
81 !strcmp(effect_child->name(), INKSCAPE_EXTENSION_NS "_menu-tip")) {
82 // printf("Found local effects menu in %s\n", this->get_name());
83 _verb.set_tip(sp_repr_children(effect_child)->content());
84 }
85 } // children of "effect"
86 break; // there can only be one effect
87 } // find "effect"
88 } // children of "inkscape-extension"
89 } // if we have an XML file
91 if (INKSCAPE != NULL) {
92 if (_effects_list == NULL)
93 _effects_list = find_menu(inkscape_get_menus(INKSCAPE), EFFECTS_LIST);
94 if (_filters_list == NULL)
95 _filters_list = find_menu(inkscape_get_menus(INKSCAPE), FILTERS_LIST);
96 }
98 if ((_effects_list != NULL || _filters_list != NULL)) {
99 Inkscape::XML::Document *xml_doc;
100 xml_doc = _effects_list->document();
101 _menu_node = xml_doc->createElement("verb");
102 _menu_node->setAttribute("verb-id", this->get_id(), false);
104 if (!hidden) {
105 if (_filters_list &&
106 local_effects_menu &&
107 local_effects_menu->attribute("name") &&
108 !strcmp(local_effects_menu->attribute("name"), ("Filters"))) {
109 merge_menu(_filters_list->parent(), _filters_list, sp_repr_children(local_effects_menu), _menu_node);
110 } else if (_effects_list) {
111 merge_menu(_effects_list->parent(), _effects_list, local_effects_menu, _menu_node);
112 }
113 }
114 }
116 return;
117 }
119 void
120 Effect::merge_menu (Inkscape::XML::Node * base,
121 Inkscape::XML::Node * start,
122 Inkscape::XML::Node * patern,
123 Inkscape::XML::Node * mergee) {
124 Glib::ustring mergename;
125 Inkscape::XML::Node * tomerge = NULL;
126 Inkscape::XML::Node * submenu = NULL;
128 /* printf("Merge menu with '%s' '%s' '%s'\n",
129 base != NULL ? base->name() : "NULL",
130 patern != NULL ? patern->name() : "NULL",
131 mergee != NULL ? mergee->name() : "NULL"); */
133 if (patern == NULL) {
134 // Merge the verb name
135 tomerge = mergee;
136 mergename = _(this->get_name());
137 } else {
138 gchar const * menuname = patern->attribute("name");
139 if (menuname == NULL) menuname = patern->attribute("_name");
140 if (menuname == NULL) return;
142 Inkscape::XML::Document *xml_doc;
143 xml_doc = base->document();
144 tomerge = xml_doc->createElement("submenu");
145 tomerge->setAttribute("name", menuname, false);
147 mergename = _(menuname);
148 }
150 int position = -1;
152 if (start != NULL) {
153 Inkscape::XML::Node * menupass;
154 for (menupass = start; menupass != NULL && strcmp(menupass->name(), "separator"); menupass = menupass->next()) {
155 gchar const * compare_char = NULL;
156 if (!strcmp(menupass->name(), "verb")) {
157 gchar const * verbid = menupass->attribute("verb-id");
158 Inkscape::Verb * verb = Inkscape::Verb::getbyid(verbid);
159 if (verb == NULL) {
160 g_warning("Unable to find verb '%s' which is referred to in the menus.", verbid);
161 continue;
162 }
163 compare_char = verb->get_name();
164 } else if (!strcmp(menupass->name(), "submenu")) {
165 compare_char = menupass->attribute("name");
166 if (compare_char == NULL)
167 compare_char = menupass->attribute("_name");
168 }
170 position = menupass->position() + 1;
172 /* This will cause us to skip tags we don't understand */
173 if (compare_char == NULL) {
174 continue;
175 }
177 Glib::ustring compare(_(compare_char));
179 if (mergename == compare) {
180 Inkscape::GC::release(tomerge);
181 tomerge = NULL;
182 submenu = menupass;
183 break;
184 }
186 if (mergename < compare) {
187 position = menupass->position();
188 break;
189 }
190 } // for menu items
191 } // start != NULL
193 if (tomerge != NULL) {
194 base->appendChild(tomerge);
195 Inkscape::GC::release(tomerge);
196 if (position != -1)
197 tomerge->setPosition(position);
198 }
200 if (patern != NULL) {
201 if (submenu == NULL)
202 submenu = tomerge;
203 merge_menu(submenu, submenu->firstChild(), patern->firstChild(), mergee);
204 }
206 return;
207 }
209 Effect::~Effect (void)
210 {
211 if (get_last_effect() == this)
212 set_last_effect(NULL);
213 if (_menu_node)
214 Inkscape::GC::release(_menu_node);
215 return;
216 }
218 bool
219 Effect::check (void)
220 {
221 if (!Extension::check()) {
222 /** \todo Check to see if parent has this as its only child,
223 if so, delete it too */
224 if (_menu_node != NULL)
225 sp_repr_unparent(_menu_node);
226 _menu_node = NULL;
227 return false;
228 }
229 return true;
230 }
232 bool
233 Effect::prefs (Inkscape::UI::View::View * doc)
234 {
235 if (_prefDialog != NULL) {
236 _prefDialog->raise();
237 return true;
238 }
240 if (param_visible_count() == 0) {
241 effect(doc);
242 return true;
243 }
245 if (!loaded())
246 set_state(Extension::STATE_LOADED);
247 if (!loaded()) return false;
249 _prefDialog = new PrefDialog(this->get_name(), this->get_help(), NULL, this);
250 _prefDialog->show();
252 return true;
253 }
255 /**
256 \brief The function that 'does' the effect itself
257 \param doc The Inkscape::UI::View::View to do the effect on
259 This function first insures that the extension is loaded, and if not,
260 loads it. It then calls the implemention to do the actual work. It
261 also resets the last effect pointer to be this effect. Finally, it
262 executes a \c SPDocumentUndo::done to commit the changes to the undo
263 stack.
264 */
265 void
266 Effect::effect (Inkscape::UI::View::View * doc)
267 {
268 //printf("Execute effect\n");
269 if (!loaded())
270 set_state(Extension::STATE_LOADED);
271 if (!loaded()) return;
274 ExecutionEnv executionEnv(this, doc);
275 timer->lock();
276 executionEnv.run();
277 if (executionEnv.wait()) {
278 executionEnv.commit();
279 } else {
280 executionEnv.cancel();
281 }
282 timer->unlock();
284 return;
285 }
287 /** \brief Sets which effect was called last
288 \param in_effect The effect that has been called
290 This function sets the static variable \c _last_effect and it
291 ensures that the last effect verb is sensitive.
293 If the \c in_effect variable is \c NULL then the last effect
294 verb is made insesitive.
295 */
296 void
297 Effect::set_last_effect (Effect * in_effect)
298 {
299 gchar const * verb_id = in_effect->get_verb()->get_id();
300 gchar const * help_id_prefix = "org.inkscape.help.";
302 // We don't want these "effects" to register as the last effect,
303 // this wouldn't be helpful to the user who selects a real effect,
304 // then goes to the help file (implemented as an effect), then goes
305 // back to the effect, only to see it written over by the help file
306 // selection.
308 // This snippet should fix this bug:
309 // https://bugs.launchpad.net/inkscape/+bug/600671
310 if (strncmp(verb_id, help_id_prefix, strlen(help_id_prefix)) == 0) return;
312 if (in_effect == NULL) {
313 Inkscape::Verb::get(SP_VERB_EFFECT_LAST)->sensitive(NULL, false);
314 Inkscape::Verb::get(SP_VERB_EFFECT_LAST_PREF)->sensitive(NULL, false);
315 } else if (_last_effect == NULL) {
316 Inkscape::Verb::get(SP_VERB_EFFECT_LAST)->sensitive(NULL, true);
317 Inkscape::Verb::get(SP_VERB_EFFECT_LAST_PREF)->sensitive(NULL, true);
318 }
320 _last_effect = in_effect;
321 return;
322 }
324 Inkscape::XML::Node *
325 Effect::find_menu (Inkscape::XML::Node * menustruct, const gchar *name)
326 {
327 if (menustruct == NULL) return false;
328 for (Inkscape::XML::Node * child = menustruct;
329 child != NULL;
330 child = child->next()) {
331 if (!strcmp(child->name(), name)) {
332 return child;
333 }
334 Inkscape::XML::Node * firstchild = child->firstChild();
335 if (firstchild != NULL) {
336 Inkscape::XML::Node *found = find_menu (firstchild, name);
337 if (found)
338 return found;
339 }
340 }
341 return NULL;
342 }
345 Gtk::VBox *
346 Effect::get_info_widget(void)
347 {
348 return Extension::get_info_widget();
349 }
351 void
352 Effect::set_pref_dialog (PrefDialog * prefdialog)
353 {
354 _prefDialog = prefdialog;
355 return;
356 }
358 /** \brief Create an action for a \c EffectVerb
359 \param view Which view the action should be created for
360 \return The built action.
362 Calls \c make_action_helper with the \c vector.
363 */
364 SPAction *
365 Effect::EffectVerb::make_action (Inkscape::UI::View::View * view)
366 {
367 return make_action_helper(view, &vector, static_cast<void *>(this));
368 }
370 /** \brief Decode the verb code and take appropriate action */
371 void
372 Effect::EffectVerb::perform( SPAction *action, void * data, void */*pdata*/ )
373 {
374 Inkscape::UI::View::View * current_view = sp_action_get_view(action);
375 // SPDocument * current_document = current_view->doc;
376 Effect::EffectVerb * ev = reinterpret_cast<Effect::EffectVerb *>(data);
377 Effect * effect = ev->_effect;
379 if (effect == NULL) return;
380 if (current_view == NULL) return;
382 if (ev->_showPrefs) {
383 effect->prefs(current_view);
384 } else {
385 effect->effect(current_view);
386 }
388 return;
389 }
391 /**
392 * Action vector to define functions called if a staticly defined file verb
393 * is called.
394 */
395 SPActionEventVector Effect::EffectVerb::vector =
396 {{NULL}, Effect::EffectVerb::perform, NULL, NULL, NULL, NULL};
399 } } /* namespace Inkscape, Extension */
401 /*
402 Local Variables:
403 mode:c++
404 c-file-style:"stroustrup"
405 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
406 indent-tabs-mode:nil
407 fill-column:99
408 End:
409 */
410 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :