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), _verb(get_id(), get_name(), NULL, NULL, this), _menu_node(NULL)
28 {
29 Inkscape::XML::Node * local_effects_menu = NULL;
31 // This is a weird hack
32 if (!strcmp(this->get_id(), "org.inkscape.filter.dropshadow"))
33 return;
35 bool hidden = false;
37 no_doc = false;
39 if (repr != NULL) {
41 for (Inkscape::XML::Node *child = sp_repr_children(repr); child != NULL; child = child->next()) {
42 if (!strcmp(child->name(), "effect")) {
43 if (child->attribute("needs-document") && !strcmp(child->attribute("needs-document"), "no")) {
44 no_doc = true;
45 }
46 for (Inkscape::XML::Node *effect_child = sp_repr_children(child); effect_child != NULL; effect_child = effect_child->next()) {
47 if (!strcmp(effect_child->name(), "effects-menu")) {
48 // printf("Found local effects menu in %s\n", this->get_name());
49 local_effects_menu = sp_repr_children(effect_child);
50 if (effect_child->attribute("hidden") && !strcmp(effect_child->attribute("hidden"), "yes")) {
51 hidden = true;
52 }
53 }
54 if (!strcmp(effect_child->name(), "menu-name") ||
55 !strcmp(effect_child->name(), "_menu-name")) {
56 // printf("Found local effects menu in %s\n", this->get_name());
57 _verb.set_name(sp_repr_children(effect_child)->content());
58 }
59 if (!strcmp(effect_child->name(), "menu-tip") ||
60 !strcmp(effect_child->name(), "_menu-tip")) {
61 // printf("Found local effects menu in %s\n", this->get_name());
62 _verb.set_tip(sp_repr_children(effect_child)->content());
63 }
64 } // children of "effect"
65 break; // there can only be one effect
66 } // find "effect"
67 } // children of "inkscape-extension"
68 } // if we have an XML file
70 if (_effects_list == NULL)
71 find_effects_list(inkscape_get_menus(INKSCAPE));
73 if (_effects_list != NULL) {
74 _menu_node = sp_repr_new("verb");
75 _menu_node->setAttribute("verb-id", this->get_id(), false);
77 if (!hidden)
78 merge_menu(_effects_list->parent(), _effects_list, local_effects_menu, _menu_node);
79 }
81 return;
82 }
84 void
85 Effect::merge_menu (Inkscape::XML::Node * base,
86 Inkscape::XML::Node * start,
87 Inkscape::XML::Node * patern,
88 Inkscape::XML::Node * mergee) {
89 Glib::ustring mergename;
90 Inkscape::XML::Node * tomerge = NULL;
91 Inkscape::XML::Node * submenu = NULL;
93 /* printf("Merge menu with '%s' '%s' '%s'\n",
94 base != NULL ? base->name() : "NULL",
95 patern != NULL ? patern->name() : "NULL",
96 mergee != NULL ? mergee->name() : "NULL"); */
98 if (patern == NULL) {
99 // Merge the verb name
100 tomerge = mergee;
101 mergename = _(this->get_name());
102 } else {
103 gchar const * menuname = patern->attribute("name");
104 if (menuname == NULL) menuname = patern->attribute("_name");
105 if (menuname == NULL) return;
107 tomerge = sp_repr_new("submenu");
108 tomerge->setAttribute("name", menuname, false);
110 mergename = _(menuname);
111 }
113 int position = -1;
115 if (start != NULL) {
116 Inkscape::XML::Node * menupass;
117 for (menupass = start->next(); menupass != NULL; menupass = menupass->next()) {
118 gchar const * compare_char = NULL;
119 if (!strcmp(menupass->name(), "verb")) {
120 gchar const * verbid = menupass->attribute("verb-id");
121 Inkscape::Verb * verb = Inkscape::Verb::getbyid(verbid);
122 if (verb == NULL) {
123 continue;
124 }
125 compare_char = verb->get_name();
126 } else if (!strcmp(menupass->name(), "submenu")) {
127 compare_char = menupass->attribute("name");
128 if (compare_char == NULL)
129 compare_char = menupass->attribute("_name");
130 }
132 /* This will cause us to skip tags we don't understand */
133 if (compare_char == NULL) {
134 continue;
135 }
137 Glib::ustring compare(_(compare_char));
139 if (mergename == compare) {
140 Inkscape::GC::release(tomerge);
141 tomerge = NULL;
142 submenu = menupass;
143 break;
144 }
146 if (mergename < compare) {
147 position = menupass->position();
148 break;
149 }
150 } // for menu items
151 } // start != NULL
153 if (tomerge != NULL) {
154 base->appendChild(tomerge);
155 Inkscape::GC::release(tomerge);
156 if (position != -1)
157 tomerge->setPosition(position);
158 }
160 if (patern != NULL) {
161 if (submenu == NULL)
162 submenu = tomerge;
163 merge_menu(submenu, submenu->firstChild(), patern->firstChild(), mergee);
164 }
166 return;
167 }
169 Effect::~Effect (void)
170 {
171 if (get_last_effect() == this)
172 set_last_effect(NULL);
173 return;
174 }
176 bool
177 Effect::check (void)
178 {
179 if (!Extension::check()) {
180 /** \todo Check to see if parent has this as its only child,
181 if so, delete it too */
182 if (_menu_node != NULL)
183 sp_repr_unparent(_menu_node);
184 _menu_node = NULL;
185 return false;
186 }
187 return true;
188 }
190 bool
191 Effect::prefs (Inkscape::UI::View::View * doc)
192 {
193 if (!loaded())
194 set_state(Extension::STATE_LOADED);
195 if (!loaded()) return false;
197 Gtk::Widget * controls;
198 controls = imp->prefs_effect(this, doc);
199 if (controls == NULL) {
200 // std::cout << "No preferences for Effect" << std::endl;
201 return true;
202 }
204 PrefDialog * dialog = new PrefDialog(this->get_name(), this->get_help(), controls);
205 int response = dialog->run();
206 dialog->hide();
208 delete dialog;
210 if (response == Gtk::RESPONSE_OK) return true;
212 return false;
213 }
215 /**
216 \brief The function that 'does' the effect itself
217 \param doc The Inkscape::UI::View::View to do the effect on
219 This function first insures that the extension is loaded, and if not,
220 loads it. It then calls the implemention to do the actual work. It
221 also resets the last effect pointer to be this effect. Finally, it
222 executes a \c sp_document_done to commit the changes to the undo
223 stack.
224 */
225 void
226 Effect::effect (Inkscape::UI::View::View * doc)
227 {
228 if (!loaded())
229 set_state(Extension::STATE_LOADED);
230 if (!loaded()) return;
232 set_last_effect(this);
233 imp->effect(this, doc);
235 sp_document_done(doc->doc(), SP_VERB_NONE, _(this->get_name()));
237 return;
238 }
240 void
241 Effect::set_last_effect (Effect * in_effect)
242 {
243 if (in_effect == NULL) {
244 Inkscape::Verb::get(SP_VERB_EFFECT_LAST)->sensitive(NULL, false);
245 Inkscape::Verb::get(SP_VERB_EFFECT_LAST_PREF)->sensitive(NULL, false);
246 } else if (_last_effect == NULL) {
247 Inkscape::Verb::get(SP_VERB_EFFECT_LAST)->sensitive(NULL, true);
248 Inkscape::Verb::get(SP_VERB_EFFECT_LAST_PREF)->sensitive(NULL, true);
249 }
251 _last_effect = in_effect;
252 return;
253 }
255 #define EFFECTS_LIST "effects-list"
257 bool
258 Effect::find_effects_list (Inkscape::XML::Node * menustruct)
259 {
260 if (menustruct == NULL) return false;
261 for (Inkscape::XML::Node * child = menustruct;
262 child != NULL;
263 child = child->next()) {
264 if (!strcmp(child->name(), EFFECTS_LIST)) {
265 _effects_list = child;
266 return true;
267 }
268 Inkscape::XML::Node * firstchild = child->firstChild();
269 if (firstchild != NULL)
270 if (find_effects_list(firstchild))
271 return true;
272 }
273 return false;
274 }
276 Gtk::VBox *
277 Effect::get_info_widget(void)
278 {
279 return Extension::get_info_widget();
280 }
282 /** \brief Create an action for a \c EffectVerb
283 \param view Which view the action should be created for
284 \return The built action.
286 Calls \c make_action_helper with the \c vector.
287 */
288 SPAction *
289 Effect::EffectVerb::make_action (Inkscape::UI::View::View * view)
290 {
291 return make_action_helper(view, &vector, static_cast<void *>(_effect));
292 }
294 /** \brief Decode the verb code and take appropriate action */
295 void
296 Effect::EffectVerb::perform (SPAction *action, void * data, void *pdata)
297 {
298 Inkscape::UI::View::View * current_view = sp_action_get_view(action);
299 // SPDocument * current_document = current_view->doc;
300 Effect * effect = reinterpret_cast<Effect *>(data);
302 if (effect == NULL) return;
303 if (current_view == NULL) return;
305 // std::cout << "Executing: " << effect->get_name() << std::endl;
306 if (effect->prefs(current_view))
307 effect->effect(current_view);
309 return;
310 }
312 /**
313 * Action vector to define functions called if a staticly defined file verb
314 * is called.
315 */
316 SPActionEventVector Effect::EffectVerb::vector =
317 {{NULL}, Effect::EffectVerb::perform, NULL, NULL, NULL, NULL};
320 } } /* namespace Inkscape, Extension */
322 /*
323 Local Variables:
324 mode:c++
325 c-file-style:"stroustrup"
326 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
327 indent-tabs-mode:nil
328 fill-column:99
329 End:
330 */
331 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :