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;
116 }
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;
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;
206 }
208 Effect::~Effect (void)
209 {
210 if (get_last_effect() == this)
211 set_last_effect(NULL);
212 if (_menu_node)
213 Inkscape::GC::release(_menu_node);
214 return;
215 }
217 bool
218 Effect::check (void)
219 {
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;
229 }
231 bool
232 Effect::prefs (Inkscape::UI::View::View * doc)
233 {
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;
252 }
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 sp_document_done to commit the changes to the undo
262 stack.
263 */
264 void
265 Effect::effect (Inkscape::UI::View::View * doc)
266 {
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;
284 }
286 /** \brief Sets which effect was called last
287 \param in_effect The effect that has been called
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)
297 {
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;
308 }
310 Inkscape::XML::Node *
311 Effect::find_menu (Inkscape::XML::Node * menustruct, const gchar *name)
312 {
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;
328 }
331 Gtk::VBox *
332 Effect::get_info_widget(void)
333 {
334 return Extension::get_info_widget();
335 }
337 void
338 Effect::set_pref_dialog (PrefDialog * prefdialog)
339 {
340 _prefDialog = prefdialog;
341 return;
342 }
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)
352 {
353 return make_action_helper(view, &vector, static_cast<void *>(this));
354 }
356 /** \brief Decode the verb code and take appropriate action */
357 void
358 Effect::EffectVerb::perform( SPAction *action, void * data, void */*pdata*/ )
359 {
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;
375 }
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 :