Code

b4eeb9c9a5aa88f5c029f1ede2ebd8a817f9dbb1
[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;
32 Effect::Effect (Inkscape::XML::Node * in_repr, Implementation::Implementation * in_imp)
33     : Extension(in_repr, in_imp),
34       _id_noprefs(Glib::ustring(get_id()) + ".noprefs"),
35       _name_noprefs(Glib::ustring(get_name()) + _(" (No preferences)")),
36       _verb(get_id(), get_name(), NULL, NULL, this, true),
37       _verb_nopref(_id_noprefs.c_str(), _name_noprefs.c_str(), NULL, NULL, this, false),
38       _menu_node(NULL), _workingDialog(true),
39       _prefDialog(NULL)
40 {
41     Inkscape::XML::Node * local_effects_menu = NULL;
43     // This is a weird hack
44     if (!strcmp(this->get_id(), "org.inkscape.filter.dropshadow"))
45         return;
47     bool hidden = false;
49     no_doc = false;
51     if (repr != NULL) {
53         for (Inkscape::XML::Node *child = sp_repr_children(repr); child != NULL; child = child->next()) {
54             if (!strcmp(child->name(), "effect")) {
55                 if (child->attribute("needs-document") && !strcmp(child->attribute("needs-document"), "false")) {
56                   no_doc = true;
57                 }
58                 for (Inkscape::XML::Node *effect_child = sp_repr_children(child); effect_child != NULL; effect_child = effect_child->next()) {
59                     if (!strcmp(effect_child->name(), "effects-menu")) {
60                         // printf("Found local effects menu in %s\n", this->get_name());
61                         local_effects_menu = sp_repr_children(effect_child);
62                         if (effect_child->attribute("hidden") && !strcmp(effect_child->attribute("hidden"), "true")) {
63                             hidden = true;
64                         }
65                     }
66                     if (!strcmp(effect_child->name(), "menu-name") ||
67                             !strcmp(effect_child->name(), "_menu-name")) {
68                         // printf("Found local effects menu in %s\n", this->get_name());
69                         _verb.set_name(sp_repr_children(effect_child)->content());
70                     }
71                     if (!strcmp(effect_child->name(), "menu-tip") ||
72                             !strcmp(effect_child->name(), "_menu-tip")) {
73                         // printf("Found local effects menu in %s\n", this->get_name());
74                         _verb.set_tip(sp_repr_children(effect_child)->content());
75                     }
76                 } // children of "effect"
77                 break; // there can only be one effect
78             } // find "effect"
79         } // children of "inkscape-extension"
80     } // if we have an XML file
82     if (_effects_list == NULL && INKSCAPE != NULL) {
83         find_effects_list(inkscape_get_menus(INKSCAPE));
84     }
86     if (_effects_list != NULL) {
87         Inkscape::XML::Document *xml_doc;
88         xml_doc = _effects_list->document();
89         _menu_node = xml_doc->createElement("verb");
90         _menu_node->setAttribute("verb-id", this->get_id(), false);
92         if (!hidden)
93             merge_menu(_effects_list->parent(), _effects_list, local_effects_menu, _menu_node);
94     }
96     return;
97 }
99 void
100 Effect::merge_menu (Inkscape::XML::Node * base,
101                     Inkscape::XML::Node * start,
102                     Inkscape::XML::Node * patern,
103                     Inkscape::XML::Node * mergee) {
104     Glib::ustring mergename;
105     Inkscape::XML::Node * tomerge = NULL;
106     Inkscape::XML::Node * submenu = NULL;
108     /* printf("Merge menu with '%s' '%s' '%s'\n",
109             base != NULL ? base->name() : "NULL",
110             patern != NULL ? patern->name() : "NULL",
111             mergee != NULL ? mergee->name() : "NULL"); */
113     if (patern == NULL) {
114         // Merge the verb name
115         tomerge = mergee;
116         mergename = _(this->get_name());
117     } else {
118         gchar const * menuname = patern->attribute("name");
119         if (menuname == NULL) menuname = patern->attribute("_name");
120         if (menuname == NULL) return;
121         
122         Inkscape::XML::Document *xml_doc;
123         xml_doc = base->document();
124         tomerge = xml_doc->createElement("submenu");
125         tomerge->setAttribute("name", menuname, false);
127         mergename = _(menuname);
128     }
130     int position = -1;
132     if (start != NULL) {
133         Inkscape::XML::Node * menupass;
134         for (menupass = start->next(); menupass != NULL; menupass = menupass->next()) {
135             gchar const * compare_char = NULL;
136             if (!strcmp(menupass->name(), "verb")) {
137                 gchar const * verbid = menupass->attribute("verb-id");
138                 Inkscape::Verb * verb = Inkscape::Verb::getbyid(verbid);
139                 if (verb == NULL) {
140                     continue;
141                 }
142                 compare_char = verb->get_name();
143             } else if (!strcmp(menupass->name(), "submenu")) {
144                 compare_char = menupass->attribute("name");
145                 if (compare_char == NULL)
146                     compare_char = menupass->attribute("_name");
147             }
149             /* This will cause us to skip tags we don't understand */
150             if (compare_char == NULL) {
151                 continue;
152             }
154             Glib::ustring compare(_(compare_char));
156             if (mergename == compare) {
157                 Inkscape::GC::release(tomerge);
158                 tomerge = NULL;
159                 submenu = menupass;
160                 break;
161             }
163             if (mergename < compare) {
164                 position = menupass->position();
165                 break;
166             }
167         } // for menu items
168     } // start != NULL
170     if (tomerge != NULL) {
171         base->appendChild(tomerge);
172         Inkscape::GC::release(tomerge);
173         if (position != -1)
174             tomerge->setPosition(position);
175     }
177     if (patern != NULL) {
178         if (submenu == NULL)
179             submenu = tomerge;
180         merge_menu(submenu, submenu->firstChild(), patern->firstChild(), mergee);
181     }
183     return;
186 Effect::~Effect (void)
188     if (get_last_effect() == this)
189         set_last_effect(NULL);
190     return;
193 bool
194 Effect::check (void)
196     if (!Extension::check()) {
197         /** \todo  Check to see if parent has this as its only child,
198                    if so, delete it too */
199         if (_menu_node != NULL)
200             sp_repr_unparent(_menu_node);
201         _menu_node = NULL;
202         return false;
203     }
204     return true;
207 bool
208 Effect::prefs (Inkscape::UI::View::View * doc)
210     if (_prefDialog != NULL) {
211         _prefDialog->raise();
212         return true;
213     }
215     if (param_visible_count() == 0) {
216         effect(doc);
217         return true;
218     }
220     if (!loaded())
221         set_state(Extension::STATE_LOADED);
222     if (!loaded()) return false;
224     _prefDialog = new PrefDialog(this->get_name(), this->get_help(), NULL, this);
225     _prefDialog->show();
227     return true;
230 /**
231     \brief  The function that 'does' the effect itself
232     \param  doc  The Inkscape::UI::View::View to do the effect on
234     This function first insures that the extension is loaded, and if not,
235     loads it.  It then calls the implemention to do the actual work.  It
236     also resets the last effect pointer to be this effect.  Finally, it
237     executes a \c sp_document_done to commit the changes to the undo
238     stack.
239 */
240 void
241 Effect::effect (Inkscape::UI::View::View * doc)
243     //printf("Execute effect\n");
244     if (!loaded())
245         set_state(Extension::STATE_LOADED);
246     if (!loaded()) return;
249     ExecutionEnv executionEnv(this, doc);
250     timer->lock();
251     executionEnv.run();
252     if (executionEnv.wait()) {
253         executionEnv.commit();
254     } else {
255         executionEnv.cancel();
256     }
257     timer->unlock();
259     return;
262 /** \brief  Sets which effect was called last
263     \param in_effect  The effect that has been called
264     
265     This function sets the static variable \c _last_effect and it
266     ensures that the last effect verb is sensitive.
268     If the \c in_effect variable is \c NULL then the last effect
269     verb is made insesitive.
270 */
271 void
272 Effect::set_last_effect (Effect * in_effect)
274     if (in_effect == NULL) {
275         Inkscape::Verb::get(SP_VERB_EFFECT_LAST)->sensitive(NULL, false);
276         Inkscape::Verb::get(SP_VERB_EFFECT_LAST_PREF)->sensitive(NULL, false);
277     } else if (_last_effect == NULL) {
278         Inkscape::Verb::get(SP_VERB_EFFECT_LAST)->sensitive(NULL, true);
279         Inkscape::Verb::get(SP_VERB_EFFECT_LAST_PREF)->sensitive(NULL, true);
280     }
282     _last_effect = in_effect;
283     return;
286 #define  EFFECTS_LIST  "effects-list"
288 bool
289 Effect::find_effects_list (Inkscape::XML::Node * menustruct)
291     if (menustruct == NULL) return false;
292     for (Inkscape::XML::Node * child = menustruct;
293             child != NULL;
294             child = child->next()) {
295         if (!strcmp(child->name(), EFFECTS_LIST)) {
296             _effects_list = child;
297             return true;
298         }
299         Inkscape::XML::Node * firstchild = child->firstChild();
300         if (firstchild != NULL)
301             if (find_effects_list(firstchild))
302                 return true;
303     }
304     return false;
307 Gtk::VBox *
308 Effect::get_info_widget(void)
310     return Extension::get_info_widget();
313 void
314 Effect::set_pref_dialog (PrefDialog * prefdialog)
316     _prefDialog = prefdialog;
317     return;
320 /** \brief  Create an action for a \c EffectVerb
321     \param  view  Which view the action should be created for
322     \return The built action.
324     Calls \c make_action_helper with the \c vector.
325 */
326 SPAction *
327 Effect::EffectVerb::make_action (Inkscape::UI::View::View * view)
329     return make_action_helper(view, &vector, static_cast<void *>(this));
332 /** \brief  Decode the verb code and take appropriate action */
333 void
334 Effect::EffectVerb::perform( SPAction *action, void * data, void */*pdata*/ )
336     Inkscape::UI::View::View * current_view = sp_action_get_view(action);
337 //  SPDocument * current_document = current_view->doc;
338     Effect::EffectVerb * ev = reinterpret_cast<Effect::EffectVerb *>(data);
339     Effect * effect = ev->_effect;
341     if (effect == NULL) return;
342     if (current_view == NULL) return;
344     if (ev->_showPrefs) {
345         effect->prefs(current_view);
346     } else {
347         effect->effect(current_view);
348     }
350     return;
353 /**
354  * Action vector to define functions called if a staticly defined file verb
355  * is called.
356  */
357 SPActionEventVector Effect::EffectVerb::vector =
358             {{NULL}, Effect::EffectVerb::perform, NULL, NULL, NULL, NULL};
361 } }  /* namespace Inkscape, Extension */
363 /*
364   Local Variables:
365   mode:c++
366   c-file-style:"stroustrup"
367   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
368   indent-tabs-mode:nil
369   fill-column:99
370   End:
371 */
372 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :