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"), "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;
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;
184 }
186 Effect::~Effect (void)
187 {
188 if (get_last_effect() == this)
189 set_last_effect(NULL);
190 return;
191 }
193 bool
194 Effect::check (void)
195 {
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;
205 }
207 bool
208 Effect::prefs (Inkscape::UI::View::View * doc)
209 {
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 Implementation::ImplementationDocumentCache * docCache = imp->newDocCache(this, doc);
223 controls = imp->prefs_effect(this, doc, changeSignal, docCache);
225 ExecutionEnv executionEnv(this, doc, controls, changeSignal, NULL, docCache);
227 timer->lock();
228 executionEnv.run();
229 timer->unlock();
231 return true;
232 }
234 /**
235 \brief The function that 'does' the effect itself
236 \param doc The Inkscape::UI::View::View to do the effect on
238 This function first insures that the extension is loaded, and if not,
239 loads it. It then calls the implemention to do the actual work. It
240 also resets the last effect pointer to be this effect. Finally, it
241 executes a \c sp_document_done to commit the changes to the undo
242 stack.
243 */
244 void
245 Effect::effect (Inkscape::UI::View::View * doc)
246 {
247 //printf("Execute effect\n");
248 if (!loaded())
249 set_state(Extension::STATE_LOADED);
250 if (!loaded()) return;
253 ExecutionEnv executionEnv(this, doc, NULL);
254 timer->lock();
255 executionEnv.run();
256 timer->unlock();
258 return;
259 }
261 /** \brief Sets which effect was called last
262 \param in_effect The effect that has been called
264 This function sets the static variable \c _last_effect and it
265 ensures that the last effect verb is sensitive.
267 If the \c in_effect variable is \c NULL then the last effect
268 verb is made insesitive.
269 */
270 void
271 Effect::set_last_effect (Effect * in_effect)
272 {
273 if (in_effect == NULL) {
274 Inkscape::Verb::get(SP_VERB_EFFECT_LAST)->sensitive(NULL, false);
275 Inkscape::Verb::get(SP_VERB_EFFECT_LAST_PREF)->sensitive(NULL, false);
276 } else if (_last_effect == NULL) {
277 Inkscape::Verb::get(SP_VERB_EFFECT_LAST)->sensitive(NULL, true);
278 Inkscape::Verb::get(SP_VERB_EFFECT_LAST_PREF)->sensitive(NULL, true);
279 }
281 _last_effect = in_effect;
282 return;
283 }
285 #define EFFECTS_LIST "effects-list"
287 bool
288 Effect::find_effects_list (Inkscape::XML::Node * menustruct)
289 {
290 if (menustruct == NULL) return false;
291 for (Inkscape::XML::Node * child = menustruct;
292 child != NULL;
293 child = child->next()) {
294 if (!strcmp(child->name(), EFFECTS_LIST)) {
295 _effects_list = child;
296 return true;
297 }
298 Inkscape::XML::Node * firstchild = child->firstChild();
299 if (firstchild != NULL)
300 if (find_effects_list(firstchild))
301 return true;
302 }
303 return false;
304 }
306 Gtk::VBox *
307 Effect::get_info_widget(void)
308 {
309 return Extension::get_info_widget();
310 }
312 void
313 Effect::set_pref_dialog (PrefDialog * prefdialog)
314 {
315 _prefDialog = prefdialog;
316 if (_prefDialog == NULL) {
317 timer->unlock();
318 } else {
319 timer->lock();
320 }
321 return;
322 }
324 /** \brief Create an action for a \c EffectVerb
325 \param view Which view the action should be created for
326 \return The built action.
328 Calls \c make_action_helper with the \c vector.
329 */
330 SPAction *
331 Effect::EffectVerb::make_action (Inkscape::UI::View::View * view)
332 {
333 return make_action_helper(view, &vector, static_cast<void *>(this));
334 }
336 /** \brief Decode the verb code and take appropriate action */
337 void
338 Effect::EffectVerb::perform( SPAction *action, void * data, void */*pdata*/ )
339 {
340 Inkscape::UI::View::View * current_view = sp_action_get_view(action);
341 // SPDocument * current_document = current_view->doc;
342 Effect::EffectVerb * ev = reinterpret_cast<Effect::EffectVerb *>(data);
343 Effect * effect = ev->_effect;
345 if (effect == NULL) return;
346 if (current_view == NULL) return;
348 if (ev->_showPrefs) {
349 effect->prefs(current_view);
350 } else {
351 effect->effect(current_view);
352 }
354 return;
355 }
357 /**
358 * Action vector to define functions called if a staticly defined file verb
359 * is called.
360 */
361 SPActionEventVector Effect::EffectVerb::vector =
362 {{NULL}, Effect::EffectVerb::perform, NULL, NULL, NULL, NULL};
365 } } /* namespace Inkscape, Extension */
367 /*
368 Local Variables:
369 mode:c++
370 c-file-style:"stroustrup"
371 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
372 indent-tabs-mode:nil
373 fill-column:99
374 End:
375 */
376 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :