Code

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