Code

Merge and cleanup of GSoC C++-ification project.
[inkscape.git] / src / extension / effect.cpp
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;
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;
141         
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;
209 Effect::~Effect (void)
211     if (get_last_effect() == this)
212         set_last_effect(NULL);
213     if (_menu_node)
214         Inkscape::GC::release(_menu_node);
215     return;
218 bool
219 Effect::check (void)
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;
232 bool
233 Effect::prefs (Inkscape::UI::View::View * doc)
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;
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)
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;
287 /** \brief  Sets which effect was called last
288     \param in_effect  The effect that has been called
289     
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)
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;
324 Inkscape::XML::Node *
325 Effect::find_menu (Inkscape::XML::Node * menustruct, const gchar *name)
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;
345 Gtk::VBox *
346 Effect::get_info_widget(void)
348     return Extension::get_info_widget();
351 void
352 Effect::set_pref_dialog (PrefDialog * prefdialog)
354     _prefDialog = prefdialog;
355     return;
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)
367     return make_action_helper(view, &vector, static_cast<void *>(this));
370 /** \brief  Decode the verb code and take appropriate action */
371 void
372 Effect::EffectVerb::perform( SPAction *action, void * data, void */*pdata*/ )
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;
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 :