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;
50 no_live_preview = false;
52 if (repr != NULL) {
54 for (Inkscape::XML::Node *child = sp_repr_children(repr); child != NULL; child = child->next()) {
55 if (!strcmp(child->name(), INKSCAPE_EXTENSION_NS "effect")) {
56 if (child->attribute("needs-document") && !strcmp(child->attribute("needs-document"), "false")) {
57 no_doc = true;
58 }
59 if (child->attribute("needs-live-preview") && !strcmp(child->attribute("needs-live-preview"), "false")) {
60 no_live_preview = true;
61 }
62 for (Inkscape::XML::Node *effect_child = sp_repr_children(child); effect_child != NULL; effect_child = effect_child->next()) {
63 if (!strcmp(effect_child->name(), INKSCAPE_EXTENSION_NS "effects-menu")) {
64 // printf("Found local effects menu in %s\n", this->get_name());
65 local_effects_menu = sp_repr_children(effect_child);
66 if (effect_child->attribute("hidden") && !strcmp(effect_child->attribute("hidden"), "true")) {
67 hidden = true;
68 }
69 }
70 if (!strcmp(effect_child->name(), INKSCAPE_EXTENSION_NS "menu-name") ||
71 !strcmp(effect_child->name(), INKSCAPE_EXTENSION_NS "_menu-name")) {
72 // printf("Found local effects menu in %s\n", this->get_name());
73 _verb.set_name(sp_repr_children(effect_child)->content());
74 }
75 if (!strcmp(effect_child->name(), INKSCAPE_EXTENSION_NS "menu-tip") ||
76 !strcmp(effect_child->name(), INKSCAPE_EXTENSION_NS "_menu-tip")) {
77 // printf("Found local effects menu in %s\n", this->get_name());
78 _verb.set_tip(sp_repr_children(effect_child)->content());
79 }
80 } // children of "effect"
81 break; // there can only be one effect
82 } // find "effect"
83 } // children of "inkscape-extension"
84 } // if we have an XML file
86 if (_effects_list == NULL && INKSCAPE != NULL) {
87 find_effects_list(inkscape_get_menus(INKSCAPE));
88 }
90 if (_effects_list != NULL) {
91 Inkscape::XML::Document *xml_doc;
92 xml_doc = _effects_list->document();
93 _menu_node = xml_doc->createElement("verb");
94 _menu_node->setAttribute("verb-id", this->get_id(), false);
96 if (!hidden)
97 merge_menu(_effects_list->parent(), _effects_list, local_effects_menu, _menu_node);
98 }
100 return;
101 }
103 void
104 Effect::merge_menu (Inkscape::XML::Node * base,
105 Inkscape::XML::Node * start,
106 Inkscape::XML::Node * patern,
107 Inkscape::XML::Node * mergee) {
108 Glib::ustring mergename;
109 Inkscape::XML::Node * tomerge = NULL;
110 Inkscape::XML::Node * submenu = NULL;
112 /* printf("Merge menu with '%s' '%s' '%s'\n",
113 base != NULL ? base->name() : "NULL",
114 patern != NULL ? patern->name() : "NULL",
115 mergee != NULL ? mergee->name() : "NULL"); */
117 if (patern == NULL) {
118 // Merge the verb name
119 tomerge = mergee;
120 mergename = _(this->get_name());
121 } else {
122 gchar const * menuname = patern->attribute("name");
123 if (menuname == NULL) menuname = patern->attribute("_name");
124 if (menuname == NULL) return;
126 Inkscape::XML::Document *xml_doc;
127 xml_doc = base->document();
128 tomerge = xml_doc->createElement("submenu");
129 tomerge->setAttribute("name", menuname, false);
131 mergename = _(menuname);
132 }
134 int position = -1;
136 if (start != NULL) {
137 Inkscape::XML::Node * menupass;
138 for (menupass = start->next(); menupass != NULL; menupass = menupass->next()) {
139 gchar const * compare_char = NULL;
140 if (!strcmp(menupass->name(), "verb")) {
141 gchar const * verbid = menupass->attribute("verb-id");
142 Inkscape::Verb * verb = Inkscape::Verb::getbyid(verbid);
143 if (verb == NULL) {
144 continue;
145 }
146 compare_char = verb->get_name();
147 } else if (!strcmp(menupass->name(), "submenu")) {
148 compare_char = menupass->attribute("name");
149 if (compare_char == NULL)
150 compare_char = menupass->attribute("_name");
151 }
153 /* This will cause us to skip tags we don't understand */
154 if (compare_char == NULL) {
155 continue;
156 }
158 Glib::ustring compare(_(compare_char));
160 if (mergename == compare) {
161 Inkscape::GC::release(tomerge);
162 tomerge = NULL;
163 submenu = menupass;
164 break;
165 }
167 if (mergename < compare) {
168 position = menupass->position();
169 break;
170 }
171 } // for menu items
172 } // start != NULL
174 if (tomerge != NULL) {
175 base->appendChild(tomerge);
176 Inkscape::GC::release(tomerge);
177 if (position != -1)
178 tomerge->setPosition(position);
179 }
181 if (patern != NULL) {
182 if (submenu == NULL)
183 submenu = tomerge;
184 merge_menu(submenu, submenu->firstChild(), patern->firstChild(), mergee);
185 }
187 return;
188 }
190 Effect::~Effect (void)
191 {
192 if (get_last_effect() == this)
193 set_last_effect(NULL);
194 if (_menu_node)
195 Inkscape::GC::release(_menu_node);
196 return;
197 }
199 bool
200 Effect::check (void)
201 {
202 if (!Extension::check()) {
203 /** \todo Check to see if parent has this as its only child,
204 if so, delete it too */
205 if (_menu_node != NULL)
206 sp_repr_unparent(_menu_node);
207 _menu_node = NULL;
208 return false;
209 }
210 return true;
211 }
213 bool
214 Effect::prefs (Inkscape::UI::View::View * doc)
215 {
216 if (_prefDialog != NULL) {
217 _prefDialog->raise();
218 return true;
219 }
221 if (param_visible_count() == 0) {
222 effect(doc);
223 return true;
224 }
226 if (!loaded())
227 set_state(Extension::STATE_LOADED);
228 if (!loaded()) return false;
230 _prefDialog = new PrefDialog(this->get_name(), this->get_help(), NULL, this);
231 _prefDialog->show();
233 return true;
234 }
236 /**
237 \brief The function that 'does' the effect itself
238 \param doc The Inkscape::UI::View::View to do the effect on
240 This function first insures that the extension is loaded, and if not,
241 loads it. It then calls the implemention to do the actual work. It
242 also resets the last effect pointer to be this effect. Finally, it
243 executes a \c sp_document_done to commit the changes to the undo
244 stack.
245 */
246 void
247 Effect::effect (Inkscape::UI::View::View * doc)
248 {
249 //printf("Execute effect\n");
250 if (!loaded())
251 set_state(Extension::STATE_LOADED);
252 if (!loaded()) return;
255 ExecutionEnv executionEnv(this, doc);
256 timer->lock();
257 executionEnv.run();
258 if (executionEnv.wait()) {
259 executionEnv.commit();
260 } else {
261 executionEnv.cancel();
262 }
263 timer->unlock();
265 return;
266 }
268 /** \brief Sets which effect was called last
269 \param in_effect The effect that has been called
271 This function sets the static variable \c _last_effect and it
272 ensures that the last effect verb is sensitive.
274 If the \c in_effect variable is \c NULL then the last effect
275 verb is made insesitive.
276 */
277 void
278 Effect::set_last_effect (Effect * in_effect)
279 {
280 if (in_effect == NULL) {
281 Inkscape::Verb::get(SP_VERB_EFFECT_LAST)->sensitive(NULL, false);
282 Inkscape::Verb::get(SP_VERB_EFFECT_LAST_PREF)->sensitive(NULL, false);
283 } else if (_last_effect == NULL) {
284 Inkscape::Verb::get(SP_VERB_EFFECT_LAST)->sensitive(NULL, true);
285 Inkscape::Verb::get(SP_VERB_EFFECT_LAST_PREF)->sensitive(NULL, true);
286 }
288 _last_effect = in_effect;
289 return;
290 }
292 #define EFFECTS_LIST "effects-list"
294 bool
295 Effect::find_effects_list (Inkscape::XML::Node * menustruct)
296 {
297 if (menustruct == NULL) return false;
298 for (Inkscape::XML::Node * child = menustruct;
299 child != NULL;
300 child = child->next()) {
301 if (!strcmp(child->name(), EFFECTS_LIST)) {
302 _effects_list = child;
303 return true;
304 }
305 Inkscape::XML::Node * firstchild = child->firstChild();
306 if (firstchild != NULL)
307 if (find_effects_list(firstchild))
308 return true;
309 }
310 return false;
311 }
313 Gtk::VBox *
314 Effect::get_info_widget(void)
315 {
316 return Extension::get_info_widget();
317 }
319 void
320 Effect::set_pref_dialog (PrefDialog * prefdialog)
321 {
322 _prefDialog = prefdialog;
323 return;
324 }
326 /** \brief Create an action for a \c EffectVerb
327 \param view Which view the action should be created for
328 \return The built action.
330 Calls \c make_action_helper with the \c vector.
331 */
332 SPAction *
333 Effect::EffectVerb::make_action (Inkscape::UI::View::View * view)
334 {
335 return make_action_helper(view, &vector, static_cast<void *>(this));
336 }
338 /** \brief Decode the verb code and take appropriate action */
339 void
340 Effect::EffectVerb::perform( SPAction *action, void * data, void */*pdata*/ )
341 {
342 Inkscape::UI::View::View * current_view = sp_action_get_view(action);
343 // SPDocument * current_document = current_view->doc;
344 Effect::EffectVerb * ev = reinterpret_cast<Effect::EffectVerb *>(data);
345 Effect * effect = ev->_effect;
347 if (effect == NULL) return;
348 if (current_view == NULL) return;
350 if (ev->_showPrefs) {
351 effect->prefs(current_view);
352 } else {
353 effect->effect(current_view);
354 }
356 return;
357 }
359 /**
360 * Action vector to define functions called if a staticly defined file verb
361 * is called.
362 */
363 SPActionEventVector Effect::EffectVerb::vector =
364 {{NULL}, Effect::EffectVerb::perform, NULL, NULL, NULL, NULL};
367 } } /* namespace Inkscape, Extension */
369 /*
370 Local Variables:
371 mode:c++
372 c-file-style:"stroustrup"
373 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
374 indent-tabs-mode:nil
375 fill-column:99
376 End:
377 */
378 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :