1 #define __SP_LPE_ITEM_CPP__
3 /** \file
4 * Base class for live path effect items
5 */
6 /*
7 * Authors:
8 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
9 * Bastien Bouclet <bgkweb@gmail.com>
10 *
11 * Copyright (C) 2008 authors
12 *
13 * Released under GNU GPL, read the file 'COPYING' for more information
14 */
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
20 #include "live_effects/effect.h"
21 #include "live_effects/lpeobject.h"
22 #include "live_effects/lpeobject-reference.h"
24 #include "sp-path.h"
25 #include "sp-item-group.h"
26 #include "streq.h"
27 #include "macros.h"
28 #include "attributes.h"
29 #include "sp-lpe-item.h"
30 #include "xml/repr.h"
31 #include "uri.h"
32 #include "message-stack.h"
33 #include "inkscape.h"
34 #include "desktop.h"
35 #include "node-context.h"
36 #include "shape-editor.h"
38 #include <algorithm>
40 /* LPEItem base class */
42 static void sp_lpe_item_class_init(SPLPEItemClass *klass);
43 static void sp_lpe_item_init(SPLPEItem *lpe_item);
44 static void sp_lpe_item_finalize(GObject *object);
46 static void sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
47 static void sp_lpe_item_release(SPObject *object);
48 static void sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value);
49 static void sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags);
50 static void sp_lpe_item_modified (SPObject *object, unsigned int flags);
51 static Inkscape::XML::Node *sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags);
53 static void sp_lpe_item_child_added (SPObject * object, Inkscape::XML::Node * child, Inkscape::XML::Node * ref);
54 static void sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child);
56 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable);
58 static void lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem);
59 static void lpeobject_ref_modified(SPObject *href, guint flags, SPLPEItem *lpeitem);
61 static void sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem);
62 static void sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem);
63 typedef std::list<std::string> HRefList;
64 static std::string patheffectlist_write_svg(PathEffectList const & list);
65 static std::string hreflist_write_svg(HRefList const & list);
67 static SPItemClass *parent_class;
69 GType
70 sp_lpe_item_get_type()
71 {
72 static GType lpe_item_type = 0;
74 if (!lpe_item_type) {
75 GTypeInfo lpe_item_info = {
76 sizeof(SPLPEItemClass),
77 NULL, NULL,
78 (GClassInitFunc) sp_lpe_item_class_init,
79 NULL, NULL,
80 sizeof(SPLPEItem),
81 16,
82 (GInstanceInitFunc) sp_lpe_item_init,
83 NULL, /* value_table */
84 };
85 lpe_item_type = g_type_register_static(SP_TYPE_ITEM, "SPLPEItem", &lpe_item_info, (GTypeFlags)0);
86 }
87 return lpe_item_type;
88 }
90 static void
91 sp_lpe_item_class_init(SPLPEItemClass *klass)
92 {
93 GObjectClass *gobject_class;
94 SPObjectClass *sp_object_class;
96 gobject_class = (GObjectClass *) klass;
97 sp_object_class = (SPObjectClass *) klass;
98 parent_class = (SPItemClass *)g_type_class_peek_parent (klass);
100 gobject_class->finalize = sp_lpe_item_finalize;
102 sp_object_class->build = sp_lpe_item_build;
103 sp_object_class->release = sp_lpe_item_release;
104 sp_object_class->set = sp_lpe_item_set;
105 sp_object_class->update = sp_lpe_item_update;
106 sp_object_class->modified = sp_lpe_item_modified;
107 sp_object_class->write = sp_lpe_item_write;
108 sp_object_class->child_added = sp_lpe_item_child_added;
109 sp_object_class->remove_child = sp_lpe_item_remove_child;
111 klass->update_patheffect = NULL;
112 }
114 static void
115 sp_lpe_item_init(SPLPEItem *lpeitem)
116 {
117 lpeitem->path_effects_enabled = 1;
119 lpeitem->path_effect_list = new PathEffectList();
120 lpeitem->current_path_effect = NULL;
122 new (&lpeitem->lpe_modified_connection) sigc::connection();
123 }
125 static void
126 sp_lpe_item_finalize(GObject *object)
127 {
128 if (((GObjectClass *) (parent_class))->finalize) {
129 (* ((GObjectClass *) (parent_class))->finalize)(object);
130 }
131 }
133 /**
134 * Reads the Inkscape::XML::Node, and initializes SPLPEItem variables. For this to get called,
135 * our name must be associated with a repr via "sp_object_type_register". Best done through
136 * sp-object-repr.cpp's repr_name_entries array.
137 */
138 static void
139 sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
140 {
141 sp_object_read_attr(object, "inkscape:path-effect");
143 if (((SPObjectClass *) parent_class)->build) {
144 ((SPObjectClass *) parent_class)->build(object, document, repr);
145 }
146 }
148 /**
149 * Drops any allocated memory.
150 */
151 static void
152 sp_lpe_item_release(SPObject *object)
153 {
154 SPLPEItem *lpeitem = (SPLPEItem *) object;
156 lpeitem->lpe_modified_connection.disconnect();
157 lpeitem->lpe_modified_connection.~connection();
159 PathEffectList::iterator it = lpeitem->path_effect_list->begin();
160 while ( it != lpeitem->path_effect_list->end() ) {
161 // unlink and delete all references in the list
162 (*it)->unlink();
163 delete *it;
164 it = lpeitem->path_effect_list->erase(it);
165 }
166 // delete the list itself
167 delete SP_LPE_ITEM(object)->path_effect_list;
169 if (((SPObjectClass *) parent_class)->release)
170 ((SPObjectClass *) parent_class)->release(object);
171 }
173 /**
174 * Sets a specific value in the SPLPEItem.
175 */
176 static void
177 sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value)
178 {
179 SPLPEItem *lpeitem = (SPLPEItem *) object;
181 switch (key) {
182 case SP_ATTR_INKSCAPE_PATH_EFFECT:
183 {
184 lpeitem->current_path_effect = NULL;
186 // Disable the path effects while populating the LPE list
187 sp_lpe_item_enable_path_effects(lpeitem, false);
189 // Clear the path effect list
190 PathEffectList::iterator it = lpeitem->path_effect_list->begin();
191 while ( it != lpeitem->path_effect_list->end() )
192 {
193 (*it)->unlink();
194 delete *it;
195 it = lpeitem->path_effect_list->erase(it);
196 }
198 // Parse the contents of "value" to rebuild the path effect reference list
199 if ( value ) {
200 std::istringstream iss(value);
201 std::string href;
202 while (std::getline(iss, href, ';'))
203 {
204 Inkscape::LivePathEffect::LPEObjectReference *path_effect_ref;
205 path_effect_ref = new Inkscape::LivePathEffect::LPEObjectReference(SP_OBJECT(lpeitem));
206 path_effect_ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(lpeobject_ref_changed), SP_LPE_ITEM(object)));
207 // Now do the attaching, which emits the changed signal.
208 // Fixme, it should not do this changed signal and updating before all effects are added to the path_effect_list
209 try {
210 path_effect_ref->link(href.c_str());
211 } catch (Inkscape::BadURIException &e) {
212 g_warning("BadURIException: %s", e.what());
213 path_effect_ref->unlink();
214 delete path_effect_ref;
215 path_effect_ref = NULL;
216 }
218 if (path_effect_ref) {
219 lpeitem->path_effect_list->push_back(path_effect_ref);
220 }
221 }
222 }
224 sp_lpe_item_enable_path_effects(lpeitem, true);
225 }
226 break;
227 default:
228 if (((SPObjectClass *) parent_class)->set) {
229 ((SPObjectClass *) parent_class)->set(object, key, value);
230 }
231 break;
232 }
233 }
235 /**
236 * Receives update notifications.
237 */
238 static void
239 sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags)
240 {
241 if (((SPObjectClass *) parent_class)->update) {
242 ((SPObjectClass *) parent_class)->update(object, ctx, flags);
243 }
245 g_print ("sp_lpe_item_update()\n");
247 // update the helperpaths of all LPEs applied to the item
248 // TODO: is there a more canonical place for this, since we don't have instant access to the item's nodepath?
249 // FIXME: this is called multiple (at least 3) times; how can we avoid this?
251 // FIXME: ditch inkscape_active_event_context()
252 SPEventContext *ec = inkscape_active_event_context();
253 if (!SP_IS_NODE_CONTEXT(ec)) return;
254 SPNodeContext *nc = SP_NODE_CONTEXT(ec);
255 ShapeEditor *sh = nc->shape_editor;
256 g_assert(sh);
257 if (!sh->has_nodepath()) return;
259 Inkscape::NodePath::Path *np = sh->get_nodepath();
260 sp_nodepath_update_helperpaths(np);
261 }
263 /**
264 * Sets modified flag for all sub-item views.
265 */
266 static void
267 sp_lpe_item_modified (SPObject *object, unsigned int flags)
268 {
269 g_print ("sp_lpe_item_modified()\n");
271 if (SP_IS_GROUP(object) && (flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_USER_MODIFIED_FLAG_B)) {
272 sp_lpe_item_update_patheffect(SP_LPE_ITEM(object), true, true);
273 }
275 if (((SPObjectClass *) (parent_class))->modified) {
276 (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
277 }
278 }
280 /**
281 * Writes its settings to an incoming repr object, if any.
282 */
283 static Inkscape::XML::Node *
284 sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
285 {
286 SPLPEItem *lpeitem = (SPLPEItem *) object;
288 if ( sp_lpe_item_has_path_effect(lpeitem) ) {
289 std::string href = patheffectlist_write_svg(*lpeitem->path_effect_list);
290 repr->setAttribute("inkscape:path-effect", href.c_str());
291 } else {
292 repr->setAttribute("inkscape:path-effect", NULL);
293 }
295 if (((SPObjectClass *)(parent_class))->write) {
296 ((SPObjectClass *)(parent_class))->write(object, xml_doc, repr, flags);
297 }
299 return repr;
300 }
302 void sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve) {
303 if (!lpeitem) return;
304 if (!curve) return;
306 if (sp_lpe_item_has_path_effect(lpeitem) && sp_lpe_item_path_effects_enabled(lpeitem)) {
307 for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
308 {
309 LivePathEffectObject *lpeobj = (*it)->lpeobject;
310 if (!lpeobj) {
311 g_warning("sp_lpe_item_perform_path_effect - NULL lpeobj in list!");
312 return;
313 }
314 if (!lpeobj->lpe) {
315 g_warning("sp_lpe_item_perform_path_effect - lpeobj without lpe!");
316 return;
317 }
319 Inkscape::LivePathEffect::Effect *lpe = lpeobj->lpe;
320 if (lpe->isVisible()) {
321 if (lpe->acceptsNumParams() > 0 && !lpe->pathParamAccepted()) {
322 // if the effect expects mouse input before being applied and the input is not finished
323 // yet, we don't alter the path
324 return;
325 }
327 // Groups have their doBeforeEffect called elsewhere
328 if (!SP_IS_GROUP(lpeitem)) {
329 lpe->doBeforeEffect(lpeitem);
330 }
332 try {
333 lpe->doEffect(curve);
334 }
335 catch (std::exception & e) {
336 g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what());
337 SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
338 _("An exception occurred during execution of the Path Effect.") );
339 }
340 }
341 }
342 }
343 }
345 /**
346 * Calls any registered handlers for the update_patheffect action
347 */
348 void
349 sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write)
350 {
351 #ifdef SHAPE_VERBOSE
352 g_message("sp_lpe_item_update_patheffect: %p\n", lpeitem);
353 #endif
354 g_return_if_fail (lpeitem != NULL);
355 g_return_if_fail (SP_IS_LPE_ITEM (lpeitem));
357 if (!sp_lpe_item_path_effects_enabled(lpeitem))
358 return;
360 SPLPEItem *top;
362 if (wholetree) {
363 SPObject *prev_parent = lpeitem;
364 SPObject *parent = prev_parent->parent;
365 while (parent && SP_IS_LPE_ITEM(parent) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent))) {
366 prev_parent = parent;
367 parent = prev_parent->parent;
368 }
369 top = SP_LPE_ITEM(prev_parent);
370 }
371 else {
372 top = lpeitem;
373 }
375 if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect) {
376 SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect (top, write);
377 }
378 }
380 /**
381 * Gets called when (re)attached to another lpeobject.
382 */
383 static void
384 lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem)
385 {
386 if (old_ref) {
387 sp_signal_disconnect_by_data(old_ref, lpeitem);
388 }
389 if ( IS_LIVEPATHEFFECT(ref) && ref != lpeitem )
390 {
391 lpeitem->lpe_modified_connection.disconnect();
392 lpeitem->lpe_modified_connection = ref->connectModified(sigc::bind(sigc::ptr_fun(&lpeobject_ref_modified), lpeitem));
393 lpeobject_ref_modified(ref, 0, lpeitem);
394 }
395 }
397 /**
398 * Gets called when lpeobject repr contents change: i.e. parameter change.
399 */
400 static void
401 lpeobject_ref_modified(SPObject */*href*/, guint /*flags*/, SPLPEItem *lpeitem)
402 {
403 sp_lpe_item_update_patheffect (lpeitem, true, true);
404 }
406 static void
407 sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem)
408 {
409 if (SP_IS_GROUP(lpeitem)) {
410 GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
411 for ( GSList const *iter = item_list; iter; iter = iter->next ) {
412 SPObject *subitem = static_cast<SPObject *>(iter->data);
413 if (SP_IS_LPE_ITEM(subitem)) {
414 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(subitem));
415 }
416 }
417 }
418 else if (SP_IS_PATH(lpeitem)) {
419 Inkscape::XML::Node *pathrepr = SP_OBJECT_REPR(lpeitem);
420 if ( !pathrepr->attribute("inkscape:original-d") ) {
421 pathrepr->setAttribute("inkscape:original-d", pathrepr->attribute("d"));
422 }
423 }
424 }
426 static void
427 sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem)
428 {
429 if (SP_IS_GROUP(lpeitem)) {
430 GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
431 for ( GSList const *iter = item_list; iter; iter = iter->next ) {
432 SPObject *subitem = static_cast<SPObject *>(iter->data);
433 if (SP_IS_LPE_ITEM(subitem)) {
434 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(subitem));
435 }
436 }
437 }
438 else if (SP_IS_PATH(lpeitem)) {
439 Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem);
440 if (!sp_lpe_item_has_path_effect_recursive(lpeitem)
441 && repr->attribute("inkscape:original-d")) {
442 repr->setAttribute("d", repr->attribute("inkscape:original-d"));
443 repr->setAttribute("inkscape:original-d", NULL);
444 }
445 else {
446 sp_lpe_item_update_patheffect(lpeitem, true, true);
447 }
448 }
449 }
451 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset)
452 {
453 if (value) {
454 // Apply the path effects here because in the casse of a group, lpe->resetDefaults
455 // needs that all the subitems have their effects applied
456 sp_lpe_item_update_patheffect(lpeitem, false, true);
458 // Disable the path effects while preparing the new lpe
459 sp_lpe_item_enable_path_effects(lpeitem, false);
461 // Add the new reference to the list of LPE references
462 HRefList hreflist;
463 for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
464 {
465 hreflist.push_back( std::string((*it)->lpeobject_href) );
466 }
467 hreflist.push_back( std::string(value) );
468 std::string hrefs = hreflist_write_svg(hreflist);
470 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", hrefs.c_str());
472 // make sure there is an original-d for paths!!!
473 sp_lpe_item_create_original_path_recursive(lpeitem);
475 LivePathEffectObject *lpeobj = lpeitem->path_effect_list->back()->lpeobject;
476 if (lpeobj && lpeobj->lpe) {
477 Inkscape::LivePathEffect::Effect *lpe = lpeobj->lpe;
478 // Ask the path effect to reset itself if it doesn't have parameters yet
479 if (reset) {
480 // has to be called when all the subitems have their lpes applied
481 lpe->resetDefaults(lpeitem);
482 }
484 // perform this once when the effect is applied
485 lpe->doOnApply(SP_LPE_ITEM(lpeitem));
487 // if the effect expects a number of mouse clicks to set a parameter path, perform the
488 // necessary preparations
489 if (lpe->acceptsNumParams() > 0) {
490 lpe->doAcceptPathPreparations(lpeitem);
491 }
492 }
494 //Enable the path effects now that everything is ready to apply the new path effect
495 sp_lpe_item_enable_path_effects(lpeitem, true);
497 // Apply the path effect
498 sp_lpe_item_update_patheffect(lpeitem, true, true);
499 }
500 }
502 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj)
503 {
504 const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
505 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
506 sp_lpe_item_add_path_effect(lpeitem, hrefstr, false);
507 g_free(hrefstr);
508 }
510 void sp_lpe_item_remove_current_path_effect(SPLPEItem *lpeitem, bool keep_paths)
511 {
512 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
513 if (!lperef)
514 return;
516 PathEffectList new_list = *lpeitem->path_effect_list;
517 new_list.remove(lperef); //current lpe ref is always our 'own' pointer from the path_effect_list
518 std::string r = patheffectlist_write_svg(new_list);
520 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
522 if (!keep_paths) {
523 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
524 }
525 }
527 void sp_lpe_item_remove_all_path_effects(SPLPEItem *lpeitem, bool keep_paths)
528 {
529 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
531 if (!keep_paths) {
532 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
533 }
534 }
536 void sp_lpe_item_down_current_path_effect(SPLPEItem *lpeitem)
537 {
538 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
539 if (!lperef)
540 return;
542 PathEffectList new_list = *lpeitem->path_effect_list;
543 PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
544 if (cur_it != new_list.end()) {
545 PathEffectList::iterator down_it = cur_it;
546 down_it++;
547 if (down_it != new_list.end()) { // perhaps current effect is already last effect
548 std::iter_swap(cur_it, down_it);
549 }
550 }
551 std::string r = patheffectlist_write_svg(new_list);
552 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
554 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
555 }
557 void sp_lpe_item_up_current_path_effect(SPLPEItem *lpeitem)
558 {
559 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
560 if (!lperef)
561 return;
563 PathEffectList new_list = *lpeitem->path_effect_list;
564 PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
565 if (cur_it != new_list.end() && cur_it != new_list.begin()) {
566 PathEffectList::iterator up_it = cur_it;
567 up_it--;
568 std::iter_swap(cur_it, up_it);
569 }
570 std::string r = patheffectlist_write_svg(new_list);
572 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
574 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
575 }
578 bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem)
579 {
580 return !lpeitem->path_effect_list->empty();
581 }
583 bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem)
584 {
585 SPObject *parent = lpeitem->parent;
586 if (parent && SP_IS_LPE_ITEM(parent)) {
587 return sp_lpe_item_has_path_effect(lpeitem) || sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent));
588 }
589 else {
590 return sp_lpe_item_has_path_effect(lpeitem);
591 }
592 }
594 Inkscape::LivePathEffect::Effect*
595 sp_lpe_item_has_path_effect_of_type(SPLPEItem *lpeitem, int type)
596 {
597 std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
598 for (i = lpeitem->path_effect_list->begin(); i != lpeitem->path_effect_list->end(); ++i) {
599 if ((*i)->lpeobject->lpe->effectType() == type) {
600 return (*i)->lpeobject->lpe;
601 }
602 }
603 return NULL;
604 }
606 /* Return false if the item is not a path or already has a shape applied */
607 bool sp_lpe_item_can_accept_freehand_shape(SPLPEItem *lpeitem)
608 {
609 if (!SP_IS_PATH(lpeitem))
610 return false;
612 if (sp_lpe_item_has_path_effect_of_type(lpeitem, Inkscape::LivePathEffect::FREEHAND_SHAPE))
613 return false;
615 return true;
616 }
618 void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt)
619 {
620 Inkscape::LivePathEffect::LPEObjectReference *lperef = sp_lpe_item_get_current_lpereference(lpeitem);
621 if (lperef && lperef->lpeobject && lperef->lpeobject->lpe) {
622 lperef->lpeobject->lpe->editNextParamOncanvas(SP_ITEM(lpeitem), dt);
623 }
624 }
626 static void
627 sp_lpe_item_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
628 {
629 if (((SPObjectClass *) (parent_class))->child_added)
630 (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref);
632 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
633 SPObject *ochild = sp_object_get_child_by_repr(object, child);
634 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
635 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(ochild));
636 }
637 }
638 }
640 static void
641 sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child)
642 {
643 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
644 SPObject *ochild = sp_object_get_child_by_repr(object, child);
645 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
646 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(ochild));
647 }
648 }
650 if (((SPObjectClass *) (parent_class))->remove_child)
651 (* ((SPObjectClass *) (parent_class))->remove_child) (object, child);
652 }
654 static std::string patheffectlist_write_svg(PathEffectList const & list)
655 {
656 HRefList hreflist;
657 for (PathEffectList::const_iterator it = list.begin(); it != list.end(); ++it)
658 {
659 hreflist.push_back( std::string((*it)->lpeobject_href) );
660 }
661 return hreflist_write_svg(hreflist);
662 }
664 /**
665 * THE function that should be used to generate any patheffectlist string.
666 * one of the methods to change the effect list:
667 * - create temporary href list
668 * - populate the templist with the effects from the old list that you want to have and their order
669 * - call this function with temp list as param
670 */
671 static std::string hreflist_write_svg(HRefList const & list)
672 {
673 std::string r;
674 bool semicolon_first = false;
675 for (HRefList::const_iterator it = list.begin(); it != list.end(); ++it)
676 {
677 if (semicolon_first) {
678 r += ';';
679 }
680 semicolon_first = true;
682 r += (*it);
683 }
684 return r;
685 }
687 // Return a copy of the effect list
688 PathEffectList sp_lpe_item_get_effect_list(SPLPEItem *lpeitem)
689 {
690 return *lpeitem->path_effect_list;
691 }
693 Inkscape::LivePathEffect::LPEObjectReference* sp_lpe_item_get_current_lpereference(SPLPEItem *lpeitem)
694 {
695 if (!lpeitem->current_path_effect && !lpeitem->path_effect_list->empty())
696 sp_lpe_item_set_current_path_effect(lpeitem, lpeitem->path_effect_list->back());
698 return lpeitem->current_path_effect;
699 }
701 Inkscape::LivePathEffect::Effect* sp_lpe_item_get_current_lpe(SPLPEItem *lpeitem)
702 {
703 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
705 if (lperef && lperef->lpeobject)
706 return lperef->lpeobject->lpe;
707 else
708 return NULL;
709 }
711 bool sp_lpe_item_set_current_path_effect(SPLPEItem *lpeitem, Inkscape::LivePathEffect::LPEObjectReference* lperef)
712 {
713 for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); it++) {
714 if ((*it)->lpeobject_repr == lperef->lpeobject_repr) {
715 lpeobject_ref_changed(NULL, (*it)->lpeobject, SP_LPE_ITEM(lpeitem)); // FIXME: explain why this is here?
716 lpeitem->current_path_effect = (*it); // current_path_effect should always be a pointer from the path_effect_list !
717 return true;
718 }
719 }
721 return false;
722 }
724 void sp_lpe_item_replace_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * old_lpeobj,
725 LivePathEffectObject * new_lpeobj)
726 {
727 HRefList hreflist;
728 for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
729 {
730 if ((*it)->lpeobject == old_lpeobj) {
731 const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
732 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
733 hreflist.push_back( std::string(hrefstr) );
734 g_free(hrefstr);
735 }
736 else {
737 hreflist.push_back( std::string((*it)->lpeobject_href) );
738 }
739 }
740 std::string r = hreflist_write_svg(hreflist);
741 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
742 }
744 // Enable or disable the path effects of the item.
745 // The counter allows nested calls
746 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable)
747 {
748 if (enable) {
749 lpeitem->path_effects_enabled++;
750 }
751 else {
752 lpeitem->path_effects_enabled--;
753 }
754 }
756 // Are the path effects enabled on this item ?
757 bool sp_lpe_item_path_effects_enabled(SPLPEItem *lpeitem)
758 {
759 return lpeitem->path_effects_enabled > 0;
760 }
762 /*
763 Local Variables:
764 mode:c++
765 c-file-style:"stroustrup"
766 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
767 indent-tabs-mode:nil
768 fill-column:99
769 End:
770 */
771 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :