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 executionEnv.run();
256 return;
257 }
259 /** \brief Sets which effect was called last
260 \param in_effect The effect that has been called
262 This function sets the static variable \c _last_effect and it
263 ensures that the last effect verb is sensitive.
265 If the \c in_effect variable is \c NULL then the last effect
266 verb is made insesitive.
267 */
268 void
269 Effect::set_last_effect (Effect * in_effect)
270 {
271 if (in_effect == NULL) {
272 Inkscape::Verb::get(SP_VERB_EFFECT_LAST)->sensitive(NULL, false);
273 Inkscape::Verb::get(SP_VERB_EFFECT_LAST_PREF)->sensitive(NULL, false);
274 } else if (_last_effect == NULL) {
275 Inkscape::Verb::get(SP_VERB_EFFECT_LAST)->sensitive(NULL, true);
276 Inkscape::Verb::get(SP_VERB_EFFECT_LAST_PREF)->sensitive(NULL, true);
277 }
279 _last_effect = in_effect;
280 return;
281 }
283 #define EFFECTS_LIST "effects-list"
285 bool
286 Effect::find_effects_list (Inkscape::XML::Node * menustruct)
287 {
288 if (menustruct == NULL) return false;
289 for (Inkscape::XML::Node * child = menustruct;
290 child != NULL;
291 child = child->next()) {
292 if (!strcmp(child->name(), EFFECTS_LIST)) {
293 _effects_list = child;
294 return true;
295 }
296 Inkscape::XML::Node * firstchild = child->firstChild();
297 if (firstchild != NULL)
298 if (find_effects_list(firstchild))
299 return true;
300 }
301 return false;
302 }
304 Gtk::VBox *
305 Effect::get_info_widget(void)
306 {
307 return Extension::get_info_widget();
308 }
310 void
311 Effect::set_pref_dialog (PrefDialog * prefdialog)
312 {
313 _prefDialog = prefdialog;
314 if (_prefDialog == NULL) {
315 timer->unlock();
316 } else {
317 timer->lock();
318 }
319 return;
320 }
322 /** \brief Create an action for a \c EffectVerb
323 \param view Which view the action should be created for
324 \return The built action.
326 Calls \c make_action_helper with the \c vector.
327 */
328 SPAction *
329 Effect::EffectVerb::make_action (Inkscape::UI::View::View * view)
330 {
331 return make_action_helper(view, &vector, static_cast<void *>(this));
332 }
334 /** \brief Decode the verb code and take appropriate action */
335 void
336 Effect::EffectVerb::perform( SPAction *action, void * data, void */*pdata*/ )
337 {
338 Inkscape::UI::View::View * current_view = sp_action_get_view(action);
339 // SPDocument * current_document = current_view->doc;
340 Effect::EffectVerb * ev = reinterpret_cast<Effect::EffectVerb *>(data);
341 Effect * effect = ev->_effect;
343 if (effect == NULL) return;
344 if (current_view == NULL) return;
346 if (ev->_showPrefs) {
347 effect->prefs(current_view);
348 } else {
349 effect->effect(current_view);
350 }
352 return;
353 }
355 /**
356 * Action vector to define functions called if a staticly defined file verb
357 * is called.
358 */
359 SPActionEventVector Effect::EffectVerb::vector =
360 {{NULL}, Effect::EffectVerb::perform, NULL, NULL, NULL, NULL};
363 } } /* namespace Inkscape, Extension */
365 /*
366 Local Variables:
367 mode:c++
368 c-file-style:"stroustrup"
369 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
370 indent-tabs-mode:nil
371 fill-column:99
372 End:
373 */
374 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :