Code

7ac4825a3afdcf274d73beac0185858bd8e1df82
[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 "document.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"), "no")) {
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"), "yes")) {
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 (!loaded())
216         set_state(Extension::STATE_LOADED);
217     if (!loaded()) return false;
219     sigc::signal<void> * changeSignal = new sigc::signal<void>;
221     Gtk::Widget * controls;
222     controls = imp->prefs_effect(this, doc, changeSignal);
224     ExecutionEnv executionEnv(this, doc, controls, changeSignal);
226     timer->lock();
227     executionEnv.run();
228     timer->unlock();
230     return true;
233 /**
234     \brief  The function that 'does' the effect itself
235     \param  doc  The Inkscape::UI::View::View to do the effect on
237     This function first insures that the extension is loaded, and if not,
238     loads it.  It then calls the implemention to do the actual work.  It
239     also resets the last effect pointer to be this effect.  Finally, it
240     executes a \c sp_document_done to commit the changes to the undo
241     stack.
242 */
243 void
244 Effect::effect (Inkscape::UI::View::View * doc)
246     //printf("Execute effect\n");
247     if (!loaded())
248         set_state(Extension::STATE_LOADED);
249     if (!loaded()) return;
252     ExecutionEnv executionEnv(this, doc, NULL);
253     executionEnv.run();
255     return;
258 /** \brief  Sets which effect was called last
259     \param in_effect  The effect that has been called
260     
261     This function sets the static variable \c _last_effect and it
262     ensures that the last effect verb is sensitive.
264     If the \c in_effect variable is \c NULL then the last effect
265     verb is made insesitive.
266 */
267 void
268 Effect::set_last_effect (Effect * in_effect)
270     if (in_effect == NULL) {
271         Inkscape::Verb::get(SP_VERB_EFFECT_LAST)->sensitive(NULL, false);
272         Inkscape::Verb::get(SP_VERB_EFFECT_LAST_PREF)->sensitive(NULL, false);
273     } else if (_last_effect == NULL) {
274         Inkscape::Verb::get(SP_VERB_EFFECT_LAST)->sensitive(NULL, true);
275         Inkscape::Verb::get(SP_VERB_EFFECT_LAST_PREF)->sensitive(NULL, true);
276     }
278     _last_effect = in_effect;
279     return;
282 #define  EFFECTS_LIST  "effects-list"
284 bool
285 Effect::find_effects_list (Inkscape::XML::Node * menustruct)
287     if (menustruct == NULL) return false;
288     for (Inkscape::XML::Node * child = menustruct;
289             child != NULL;
290             child = child->next()) {
291         if (!strcmp(child->name(), EFFECTS_LIST)) {
292             _effects_list = child;
293             return true;
294         }
295         Inkscape::XML::Node * firstchild = child->firstChild();
296         if (firstchild != NULL)
297             if (find_effects_list(firstchild))
298                 return true;
299     }
300     return false;
303 Gtk::VBox *
304 Effect::get_info_widget(void)
306     return Extension::get_info_widget();
309 void
310 Effect::set_pref_dialog (PrefDialog * prefdialog)
312     _prefDialog = prefdialog;
313     if (_prefDialog == NULL) {
314         timer->unlock();
315     } else {
316         timer->lock();
317     }
318     return;
321 /** \brief  Create an action for a \c EffectVerb
322     \param  view  Which view the action should be created for
323     \return The built action.
325     Calls \c make_action_helper with the \c vector.
326 */
327 SPAction *
328 Effect::EffectVerb::make_action (Inkscape::UI::View::View * view)
330     return make_action_helper(view, &vector, static_cast<void *>(this));
333 /** \brief  Decode the verb code and take appropriate action */
334 void
335 Effect::EffectVerb::perform (SPAction *action, void * data, void *pdata)
337     Inkscape::UI::View::View * current_view = sp_action_get_view(action);
338 //  SPDocument * current_document = current_view->doc;
339     Effect::EffectVerb * ev = reinterpret_cast<Effect::EffectVerb *>(data);
340     Effect * effect = ev->_effect;
342     if (effect == NULL) return;
343     if (current_view == NULL) return;
345     if (ev->_showPrefs) {
346         effect->prefs(current_view);
347     } else {
348         effect->effect(current_view);
349     }
351     return;
354 /**
355  * Action vector to define functions called if a staticly defined file verb
356  * is called.
357  */
358 SPActionEventVector Effect::EffectVerb::vector =
359             {{NULL}, Effect::EffectVerb::perform, NULL, NULL, NULL, NULL};
362 } }  /* namespace Inkscape, Extension */
364 /*
365   Local Variables:
366   mode:c++
367   c-file-style:"stroustrup"
368   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
369   indent-tabs-mode:nil
370   fill-column:99
371   End:
372 */
373 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :