Code

A simple layout document as to what, why and how is cppification.
[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 SPDocumentUndo::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     if (in_effect == NULL) {
299         Inkscape::Verb::get(SP_VERB_EFFECT_LAST)->sensitive(NULL, false);
300         Inkscape::Verb::get(SP_VERB_EFFECT_LAST_PREF)->sensitive(NULL, false);
301     } else if (_last_effect == NULL) {
302         Inkscape::Verb::get(SP_VERB_EFFECT_LAST)->sensitive(NULL, true);
303         Inkscape::Verb::get(SP_VERB_EFFECT_LAST_PREF)->sensitive(NULL, true);
304     }
306     _last_effect = in_effect;
307     return;
310 Inkscape::XML::Node *
311 Effect::find_menu (Inkscape::XML::Node * menustruct, const gchar *name)
313     if (menustruct == NULL) return false;
314     for (Inkscape::XML::Node * child = menustruct;
315             child != NULL;
316             child = child->next()) {
317         if (!strcmp(child->name(), name)) {
318             return child;
319         }
320         Inkscape::XML::Node * firstchild = child->firstChild();
321         if (firstchild != NULL) {
322             Inkscape::XML::Node *found = find_menu (firstchild, name);
323             if (found)
324                 return found;
325         }
326     }
327     return NULL;
331 Gtk::VBox *
332 Effect::get_info_widget(void)
334     return Extension::get_info_widget();
337 void
338 Effect::set_pref_dialog (PrefDialog * prefdialog)
340     _prefDialog = prefdialog;
341     return;
344 /** \brief  Create an action for a \c EffectVerb
345     \param  view  Which view the action should be created for
346     \return The built action.
348     Calls \c make_action_helper with the \c vector.
349 */
350 SPAction *
351 Effect::EffectVerb::make_action (Inkscape::UI::View::View * view)
353     return make_action_helper(view, &vector, static_cast<void *>(this));
356 /** \brief  Decode the verb code and take appropriate action */
357 void
358 Effect::EffectVerb::perform( SPAction *action, void * data, void */*pdata*/ )
360     Inkscape::UI::View::View * current_view = sp_action_get_view(action);
361 //  SPDocument * current_document = current_view->doc;
362     Effect::EffectVerb * ev = reinterpret_cast<Effect::EffectVerb *>(data);
363     Effect * effect = ev->_effect;
365     if (effect == NULL) return;
366     if (current_view == NULL) return;
368     if (ev->_showPrefs) {
369         effect->prefs(current_view);
370     } else {
371         effect->effect(current_view);
372     }
374     return;
377 /**
378  * Action vector to define functions called if a staticly defined file verb
379  * is called.
380  */
381 SPActionEventVector Effect::EffectVerb::vector =
382             {{NULL}, Effect::EffectVerb::perform, NULL, NULL, NULL, NULL};
385 } }  /* namespace Inkscape, Extension */
387 /*
388   Local Variables:
389   mode:c++
390   c-file-style:"stroustrup"
391   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
392   indent-tabs-mode:nil
393   fill-column:99
394   End:
395 */
396 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :