Code

switch from invokeBbox to getBounds (need to fix problems with empty
[inkscape.git] / src / extension / effect.cpp
1 /*
2  * Authors:
3  *   Ted Gould <ted@gould.cx>
4  *
5  * Copyright (C) 2002-2006 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 "document.h"
13 #include "prefdialog.h"
14 #include "implementation/implementation.h"
15 #include "effect.h"
16 #include "ui/view/view.h"
18 /* Inkscape::Extension::Effect */
20 namespace Inkscape {
21 namespace Extension {
23 Effect * Effect::_last_effect = NULL;
24 Inkscape::XML::Node * Effect::_effects_list = NULL;
26 Effect::Effect (Inkscape::XML::Node * in_repr, Implementation::Implementation * in_imp)
27     : Extension(in_repr, in_imp),
28       _id_noprefs(Glib::ustring(get_id()) + ".noprefs"),
29       _name_noprefs(Glib::ustring(get_name()) + _(" (No preferences)")),
30       _verb(get_id(), get_name(), NULL, NULL, this, true),
31       _verb_nopref(_id_noprefs.c_str(), _name_noprefs.c_str(), NULL, NULL, this, false),
32       _menu_node(NULL)
33 {
34     Inkscape::XML::Node * local_effects_menu = NULL;
36     // This is a weird hack
37     if (!strcmp(this->get_id(), "org.inkscape.filter.dropshadow"))
38         return;
40     bool hidden = false;
42     no_doc = false;
44     if (repr != NULL) {
46         for (Inkscape::XML::Node *child = sp_repr_children(repr); child != NULL; child = child->next()) {
47             if (!strcmp(child->name(), "effect")) {
48                 if (child->attribute("needs-document") && !strcmp(child->attribute("needs-document"), "no")) {
49                   no_doc = true;
50                 }
51                 for (Inkscape::XML::Node *effect_child = sp_repr_children(child); effect_child != NULL; effect_child = effect_child->next()) {
52                     if (!strcmp(effect_child->name(), "effects-menu")) {
53                         // printf("Found local effects menu in %s\n", this->get_name());
54                         local_effects_menu = sp_repr_children(effect_child);
55                         if (effect_child->attribute("hidden") && !strcmp(effect_child->attribute("hidden"), "yes")) {
56                             hidden = true;
57                         }
58                     }
59                     if (!strcmp(effect_child->name(), "menu-name") ||
60                             !strcmp(effect_child->name(), "_menu-name")) {
61                         // printf("Found local effects menu in %s\n", this->get_name());
62                         _verb.set_name(sp_repr_children(effect_child)->content());
63                     }
64                     if (!strcmp(effect_child->name(), "menu-tip") ||
65                             !strcmp(effect_child->name(), "_menu-tip")) {
66                         // printf("Found local effects menu in %s\n", this->get_name());
67                         _verb.set_tip(sp_repr_children(effect_child)->content());
68                     }
69                 } // children of "effect"
70                 break; // there can only be one effect
71             } // find "effect"
72         } // children of "inkscape-extension"
73     } // if we have an XML file
75     if (_effects_list == NULL && INKSCAPE != NULL) {
76         find_effects_list(inkscape_get_menus(INKSCAPE));
77     }
79     if (_effects_list != NULL) {
80         Inkscape::XML::Document *xml_doc;
81         xml_doc = _effects_list->document();
82         _menu_node = xml_doc->createElement("verb");
83         _menu_node->setAttribute("verb-id", this->get_id(), false);
85         if (!hidden)
86             merge_menu(_effects_list->parent(), _effects_list, local_effects_menu, _menu_node);
87     }
89     return;
90 }
92 void
93 Effect::merge_menu (Inkscape::XML::Node * base,
94                     Inkscape::XML::Node * start,
95                     Inkscape::XML::Node * patern,
96                     Inkscape::XML::Node * mergee) {
97     Glib::ustring mergename;
98     Inkscape::XML::Node * tomerge = NULL;
99     Inkscape::XML::Node * submenu = NULL;
101     /* printf("Merge menu with '%s' '%s' '%s'\n",
102             base != NULL ? base->name() : "NULL",
103             patern != NULL ? patern->name() : "NULL",
104             mergee != NULL ? mergee->name() : "NULL"); */
106     if (patern == NULL) {
107         // Merge the verb name
108         tomerge = mergee;
109         mergename = _(this->get_name());
110     } else {
111         gchar const * menuname = patern->attribute("name");
112         if (menuname == NULL) menuname = patern->attribute("_name");
113         if (menuname == NULL) return;
114         
115         Inkscape::XML::Document *xml_doc;
116         xml_doc = base->document();
117         tomerge = xml_doc->createElement("submenu");
118         tomerge->setAttribute("name", menuname, false);
120         mergename = _(menuname);
121     }
123     int position = -1;
125     if (start != NULL) {
126         Inkscape::XML::Node * menupass;
127         for (menupass = start->next(); menupass != NULL; menupass = menupass->next()) {
128             gchar const * compare_char = NULL;
129             if (!strcmp(menupass->name(), "verb")) {
130                 gchar const * verbid = menupass->attribute("verb-id");
131                 Inkscape::Verb * verb = Inkscape::Verb::getbyid(verbid);
132                 if (verb == NULL) {
133                     continue;
134                 }
135                 compare_char = verb->get_name();
136             } else if (!strcmp(menupass->name(), "submenu")) {
137                 compare_char = menupass->attribute("name");
138                 if (compare_char == NULL)
139                     compare_char = menupass->attribute("_name");
140             }
142             /* This will cause us to skip tags we don't understand */
143             if (compare_char == NULL) {
144                 continue;
145             }
147             Glib::ustring compare(_(compare_char));
149             if (mergename == compare) {
150                 Inkscape::GC::release(tomerge);
151                 tomerge = NULL;
152                 submenu = menupass;
153                 break;
154             }
156             if (mergename < compare) {
157                 position = menupass->position();
158                 break;
159             }
160         } // for menu items
161     } // start != NULL
163     if (tomerge != NULL) {
164         base->appendChild(tomerge);
165         Inkscape::GC::release(tomerge);
166         if (position != -1)
167             tomerge->setPosition(position);
168     }
170     if (patern != NULL) {
171         if (submenu == NULL)
172             submenu = tomerge;
173         merge_menu(submenu, submenu->firstChild(), patern->firstChild(), mergee);
174     }
176     return;
179 Effect::~Effect (void)
181     if (get_last_effect() == this)
182         set_last_effect(NULL);
183     return;
186 bool
187 Effect::check (void)
189     if (!Extension::check()) {
190         /** \todo  Check to see if parent has this as its only child,
191                    if so, delete it too */
192         if (_menu_node != NULL)
193             sp_repr_unparent(_menu_node);
194         _menu_node = NULL;
195         return false;
196     }
197     return true;
200 bool
201 Effect::prefs (Inkscape::UI::View::View * doc)
203     if (!loaded())
204         set_state(Extension::STATE_LOADED);
205     if (!loaded()) return false;
207     Gtk::Widget * controls;
208     controls = imp->prefs_effect(this, doc);
209     if (controls == NULL) {
210         // std::cout << "No preferences for Effect" << std::endl;
211         return true;
212     }
214     PrefDialog * dialog = new PrefDialog(this->get_name(), this->get_help(), controls);
215     int response = dialog->run();
216     dialog->hide();
218     delete dialog;
220     if (response == Gtk::RESPONSE_OK) return true;
222     return false;
225 /**
226     \brief  The function that 'does' the effect itself
227     \param  doc  The Inkscape::UI::View::View to do the effect on
229     This function first insures that the extension is loaded, and if not,
230     loads it.  It then calls the implemention to do the actual work.  It
231     also resets the last effect pointer to be this effect.  Finally, it
232     executes a \c sp_document_done to commit the changes to the undo
233     stack.
234 */
235 void
236 Effect::effect (Inkscape::UI::View::View * doc)
238     if (!loaded())
239         set_state(Extension::STATE_LOADED);
240     if (!loaded()) return;
242     set_last_effect(this);
243     imp->effect(this, doc);
245     sp_document_done(doc->doc(), SP_VERB_NONE, _(this->get_name()));
247     return;
250 void
251 Effect::set_last_effect (Effect * in_effect)
253     if (in_effect == NULL) {
254         Inkscape::Verb::get(SP_VERB_EFFECT_LAST)->sensitive(NULL, false);
255         Inkscape::Verb::get(SP_VERB_EFFECT_LAST_PREF)->sensitive(NULL, false);
256     } else if (_last_effect == NULL) {
257         Inkscape::Verb::get(SP_VERB_EFFECT_LAST)->sensitive(NULL, true);
258         Inkscape::Verb::get(SP_VERB_EFFECT_LAST_PREF)->sensitive(NULL, true);
259     }
261     _last_effect = in_effect;
262     return;
265 #define  EFFECTS_LIST  "effects-list"
267 bool
268 Effect::find_effects_list (Inkscape::XML::Node * menustruct)
270     if (menustruct == NULL) return false;
271     for (Inkscape::XML::Node * child = menustruct;
272             child != NULL;
273             child = child->next()) {
274         if (!strcmp(child->name(), EFFECTS_LIST)) {
275             _effects_list = child;
276             return true;
277         }
278         Inkscape::XML::Node * firstchild = child->firstChild();
279         if (firstchild != NULL)
280             if (find_effects_list(firstchild))
281                 return true;
282     }
283     return false;
286 Gtk::VBox *
287 Effect::get_info_widget(void)
289     return Extension::get_info_widget();
292 /** \brief  Create an action for a \c EffectVerb
293     \param  view  Which view the action should be created for
294     \return The built action.
296     Calls \c make_action_helper with the \c vector.
297 */
298 SPAction *
299 Effect::EffectVerb::make_action (Inkscape::UI::View::View * view)
301     return make_action_helper(view, &vector, static_cast<void *>(this));
304 /** \brief  Decode the verb code and take appropriate action */
305 void
306 Effect::EffectVerb::perform (SPAction *action, void * data, void *pdata)
308     Inkscape::UI::View::View * current_view = sp_action_get_view(action);
309 //  SPDocument * current_document = current_view->doc;
310     Effect::EffectVerb * ev = reinterpret_cast<Effect::EffectVerb *>(data);
311     Effect * effect = ev->_effect;
313     if (effect == NULL) return;
314     if (current_view == NULL) return;
316     // std::cout << "Executing: " << effect->get_name() << std::endl;
317     bool execute = true;
319     if (ev->_showPrefs)
320         execute = effect->prefs(current_view);
321     if (execute)
322         effect->effect(current_view);
324     return;
327 /**
328  * Action vector to define functions called if a staticly defined file verb
329  * is called.
330  */
331 SPActionEventVector Effect::EffectVerb::vector =
332             {{NULL}, Effect::EffectVerb::perform, NULL, NULL, NULL, NULL};
335 } }  /* namespace Inkscape, Extension */
337 /*
338   Local Variables:
339   mode:c++
340   c-file-style:"stroustrup"
341   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
342   indent-tabs-mode:nil
343   fill-column:99
344   End:
345 */
346 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :