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 // update the helperpaths of all LPEs applied to the item
246 // TODO: is there a more canonical place for this, since we don't have instant access to the item's nodepath?
247 // FIXME: this is called multiple (at least 3) times; how can we avoid this?
249 // FIXME: ditch inkscape_active_event_context()
250 SPEventContext *ec = inkscape_active_event_context();
251 if (!SP_IS_NODE_CONTEXT(ec)) return;
252 SPNodeContext *nc = SP_NODE_CONTEXT(ec);
253 ShapeEditor *sh = nc->shape_editor;
254 g_assert(sh);
255 if (!sh->has_nodepath()) return;
257 Inkscape::NodePath::Path *np = sh->get_nodepath();
258 sp_nodepath_update_helperpaths(np);
259 }
261 /**
262 * Sets modified flag for all sub-item views.
263 */
264 static void
265 sp_lpe_item_modified (SPObject *object, unsigned int flags)
266 {
267 if (SP_IS_GROUP(object) && (flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_USER_MODIFIED_FLAG_B)) {
268 sp_lpe_item_update_patheffect(SP_LPE_ITEM(object), true, true);
269 }
271 if (((SPObjectClass *) (parent_class))->modified) {
272 (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
273 }
274 }
276 /**
277 * Writes its settings to an incoming repr object, if any.
278 */
279 static Inkscape::XML::Node *
280 sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
281 {
282 SPLPEItem *lpeitem = (SPLPEItem *) object;
284 if ( sp_lpe_item_has_path_effect(lpeitem) ) {
285 std::string href = patheffectlist_write_svg(*lpeitem->path_effect_list);
286 repr->setAttribute("inkscape:path-effect", href.c_str());
287 } else {
288 repr->setAttribute("inkscape:path-effect", NULL);
289 }
291 if (((SPObjectClass *)(parent_class))->write) {
292 ((SPObjectClass *)(parent_class))->write(object, xml_doc, repr, flags);
293 }
295 return repr;
296 }
298 void sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve) {
299 if (!lpeitem) return;
300 if (!curve) return;
302 if (sp_lpe_item_has_path_effect(lpeitem) && sp_lpe_item_path_effects_enabled(lpeitem)) {
303 for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
304 {
305 LivePathEffectObject *lpeobj = (*it)->lpeobject;
306 if (!lpeobj) {
307 g_warning("sp_lpe_item_perform_path_effect - NULL lpeobj in list!");
308 return;
309 }
310 if (!lpeobj->lpe) {
311 g_warning("sp_lpe_item_perform_path_effect - lpeobj without lpe!");
312 return;
313 }
315 Inkscape::LivePathEffect::Effect *lpe = lpeobj->lpe;
316 if (lpe->isVisible()) {
317 if (lpe->acceptsNumParams() > 0 && !lpe->pathParamAccepted()) {
318 // if the effect expects mouse input before being applied and the input is not finished
319 // yet, we don't alter the path
320 return;
321 }
323 // Groups have their doBeforeEffect called elsewhere
324 if (!SP_IS_GROUP(lpeitem)) {
325 lpe->doBeforeEffect(lpeitem);
326 }
328 try {
329 lpe->doEffect(curve);
330 }
331 catch (std::exception & e) {
332 g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what());
333 SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
334 _("An exception occurred during execution of the Path Effect.") );
335 }
336 }
337 }
338 }
339 }
341 /**
342 * Calls any registered handlers for the update_patheffect action
343 */
344 void
345 sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write)
346 {
347 #ifdef SHAPE_VERBOSE
348 g_message("sp_lpe_item_update_patheffect: %p\n", lpeitem);
349 #endif
350 g_return_if_fail (lpeitem != NULL);
351 g_return_if_fail (SP_IS_LPE_ITEM (lpeitem));
353 if (!sp_lpe_item_path_effects_enabled(lpeitem))
354 return;
356 SPLPEItem *top;
358 if (wholetree) {
359 SPObject *prev_parent = lpeitem;
360 SPObject *parent = prev_parent->parent;
361 while (parent && SP_IS_LPE_ITEM(parent) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent))) {
362 prev_parent = parent;
363 parent = prev_parent->parent;
364 }
365 top = SP_LPE_ITEM(prev_parent);
366 }
367 else {
368 top = lpeitem;
369 }
371 if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect) {
372 SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect (top, write);
373 }
374 }
376 /**
377 * Gets called when (re)attached to another lpeobject.
378 */
379 static void
380 lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem)
381 {
382 if (old_ref) {
383 sp_signal_disconnect_by_data(old_ref, lpeitem);
384 }
385 if ( IS_LIVEPATHEFFECT(ref) && ref != lpeitem )
386 {
387 lpeitem->lpe_modified_connection.disconnect();
388 lpeitem->lpe_modified_connection = ref->connectModified(sigc::bind(sigc::ptr_fun(&lpeobject_ref_modified), lpeitem));
389 lpeobject_ref_modified(ref, 0, lpeitem);
390 }
391 }
393 /**
394 * Gets called when lpeobject repr contents change: i.e. parameter change.
395 */
396 static void
397 lpeobject_ref_modified(SPObject */*href*/, guint /*flags*/, SPLPEItem *lpeitem)
398 {
399 sp_lpe_item_update_patheffect (lpeitem, true, true);
400 }
402 static void
403 sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem)
404 {
405 if (SP_IS_GROUP(lpeitem)) {
406 GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
407 for ( GSList const *iter = item_list; iter; iter = iter->next ) {
408 SPObject *subitem = static_cast<SPObject *>(iter->data);
409 if (SP_IS_LPE_ITEM(subitem)) {
410 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(subitem));
411 }
412 }
413 }
414 else if (SP_IS_PATH(lpeitem)) {
415 Inkscape::XML::Node *pathrepr = SP_OBJECT_REPR(lpeitem);
416 if ( !pathrepr->attribute("inkscape:original-d") ) {
417 pathrepr->setAttribute("inkscape:original-d", pathrepr->attribute("d"));
418 }
419 }
420 }
422 static void
423 sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem)
424 {
425 if (SP_IS_GROUP(lpeitem)) {
426 GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
427 for ( GSList const *iter = item_list; iter; iter = iter->next ) {
428 SPObject *subitem = static_cast<SPObject *>(iter->data);
429 if (SP_IS_LPE_ITEM(subitem)) {
430 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(subitem));
431 }
432 }
433 }
434 else if (SP_IS_PATH(lpeitem)) {
435 Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem);
436 if (!sp_lpe_item_has_path_effect_recursive(lpeitem)
437 && repr->attribute("inkscape:original-d")) {
438 repr->setAttribute("d", repr->attribute("inkscape:original-d"));
439 repr->setAttribute("inkscape:original-d", NULL);
440 }
441 else {
442 sp_lpe_item_update_patheffect(lpeitem, true, true);
443 }
444 }
445 }
447 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset)
448 {
449 if (value) {
450 // Apply the path effects here because in the casse of a group, lpe->resetDefaults
451 // needs that all the subitems have their effects applied
452 sp_lpe_item_update_patheffect(lpeitem, false, true);
454 // Disable the path effects while preparing the new lpe
455 sp_lpe_item_enable_path_effects(lpeitem, false);
457 // Add the new reference to the list of LPE references
458 HRefList hreflist;
459 for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
460 {
461 hreflist.push_back( std::string((*it)->lpeobject_href) );
462 }
463 hreflist.push_back( std::string(value) );
464 std::string hrefs = hreflist_write_svg(hreflist);
466 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", hrefs.c_str());
468 // make sure there is an original-d for paths!!!
469 sp_lpe_item_create_original_path_recursive(lpeitem);
471 LivePathEffectObject *lpeobj = lpeitem->path_effect_list->back()->lpeobject;
472 if (lpeobj && lpeobj->lpe) {
473 Inkscape::LivePathEffect::Effect *lpe = lpeobj->lpe;
474 // Ask the path effect to reset itself if it doesn't have parameters yet
475 if (reset) {
476 // has to be called when all the subitems have their lpes applied
477 lpe->resetDefaults(lpeitem);
478 }
480 // perform this once when the effect is applied
481 lpe->doOnApply(SP_LPE_ITEM(lpeitem));
483 // if the effect expects a number of mouse clicks to set a parameter path, perform the
484 // necessary preparations
485 if (lpe->acceptsNumParams() > 0) {
486 lpe->doAcceptPathPreparations(lpeitem);
487 }
488 }
490 //Enable the path effects now that everything is ready to apply the new path effect
491 sp_lpe_item_enable_path_effects(lpeitem, true);
493 // Apply the path effect
494 sp_lpe_item_update_patheffect(lpeitem, true, true);
495 }
496 }
498 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj)
499 {
500 const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
501 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
502 sp_lpe_item_add_path_effect(lpeitem, hrefstr, false);
503 g_free(hrefstr);
504 }
506 void sp_lpe_item_remove_current_path_effect(SPLPEItem *lpeitem, bool keep_paths)
507 {
508 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
509 if (!lperef)
510 return;
512 PathEffectList new_list = *lpeitem->path_effect_list;
513 new_list.remove(lperef); //current lpe ref is always our 'own' pointer from the path_effect_list
514 std::string r = patheffectlist_write_svg(new_list);
516 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
518 if (!keep_paths) {
519 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
520 }
521 }
523 void sp_lpe_item_remove_all_path_effects(SPLPEItem *lpeitem, bool keep_paths)
524 {
525 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
527 if (!keep_paths) {
528 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
529 }
530 }
532 void sp_lpe_item_down_current_path_effect(SPLPEItem *lpeitem)
533 {
534 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
535 if (!lperef)
536 return;
538 PathEffectList new_list = *lpeitem->path_effect_list;
539 PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
540 if (cur_it != new_list.end()) {
541 PathEffectList::iterator down_it = cur_it;
542 down_it++;
543 if (down_it != new_list.end()) { // perhaps current effect is already last effect
544 std::iter_swap(cur_it, down_it);
545 }
546 }
547 std::string r = patheffectlist_write_svg(new_list);
548 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
550 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
551 }
553 void sp_lpe_item_up_current_path_effect(SPLPEItem *lpeitem)
554 {
555 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
556 if (!lperef)
557 return;
559 PathEffectList new_list = *lpeitem->path_effect_list;
560 PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
561 if (cur_it != new_list.end() && cur_it != new_list.begin()) {
562 PathEffectList::iterator up_it = cur_it;
563 up_it--;
564 std::iter_swap(cur_it, up_it);
565 }
566 std::string r = patheffectlist_write_svg(new_list);
568 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
570 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
571 }
574 bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem)
575 {
576 return !lpeitem->path_effect_list->empty();
577 }
579 bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem)
580 {
581 SPObject *parent = lpeitem->parent;
582 if (parent && SP_IS_LPE_ITEM(parent)) {
583 return sp_lpe_item_has_path_effect(lpeitem) || sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent));
584 }
585 else {
586 return sp_lpe_item_has_path_effect(lpeitem);
587 }
588 }
590 Inkscape::LivePathEffect::Effect*
591 sp_lpe_item_has_path_effect_of_type(SPLPEItem *lpeitem, int type)
592 {
593 std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
594 for (i = lpeitem->path_effect_list->begin(); i != lpeitem->path_effect_list->end(); ++i) {
595 if ((*i)->lpeobject->lpe->effectType() == type) {
596 return (*i)->lpeobject->lpe;
597 }
598 }
599 return NULL;
600 }
602 /* Return false if the item is not a path or already has a shape applied */
603 bool sp_lpe_item_can_accept_freehand_shape(SPLPEItem *lpeitem)
604 {
605 if (!SP_IS_PATH(lpeitem))
606 return false;
608 if (sp_lpe_item_has_path_effect_of_type(lpeitem, Inkscape::LivePathEffect::FREEHAND_SHAPE))
609 return false;
611 return true;
612 }
614 void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt)
615 {
616 Inkscape::LivePathEffect::LPEObjectReference *lperef = sp_lpe_item_get_current_lpereference(lpeitem);
617 if (lperef && lperef->lpeobject && lperef->lpeobject->lpe) {
618 lperef->lpeobject->lpe->editNextParamOncanvas(SP_ITEM(lpeitem), dt);
619 }
620 }
622 static void
623 sp_lpe_item_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
624 {
625 if (((SPObjectClass *) (parent_class))->child_added)
626 (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref);
628 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
629 SPObject *ochild = sp_object_get_child_by_repr(object, child);
630 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
631 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(ochild));
632 }
633 }
634 }
636 static void
637 sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child)
638 {
639 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
640 SPObject *ochild = sp_object_get_child_by_repr(object, child);
641 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
642 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(ochild));
643 }
644 }
646 if (((SPObjectClass *) (parent_class))->remove_child)
647 (* ((SPObjectClass *) (parent_class))->remove_child) (object, child);
648 }
650 static std::string patheffectlist_write_svg(PathEffectList const & list)
651 {
652 HRefList hreflist;
653 for (PathEffectList::const_iterator it = list.begin(); it != list.end(); ++it)
654 {
655 hreflist.push_back( std::string((*it)->lpeobject_href) );
656 }
657 return hreflist_write_svg(hreflist);
658 }
660 /**
661 * THE function that should be used to generate any patheffectlist string.
662 * one of the methods to change the effect list:
663 * - create temporary href list
664 * - populate the templist with the effects from the old list that you want to have and their order
665 * - call this function with temp list as param
666 */
667 static std::string hreflist_write_svg(HRefList const & list)
668 {
669 std::string r;
670 bool semicolon_first = false;
671 for (HRefList::const_iterator it = list.begin(); it != list.end(); ++it)
672 {
673 if (semicolon_first) {
674 r += ';';
675 }
676 semicolon_first = true;
678 r += (*it);
679 }
680 return r;
681 }
683 // Return a copy of the effect list
684 PathEffectList sp_lpe_item_get_effect_list(SPLPEItem *lpeitem)
685 {
686 return *lpeitem->path_effect_list;
687 }
689 Inkscape::LivePathEffect::LPEObjectReference* sp_lpe_item_get_current_lpereference(SPLPEItem *lpeitem)
690 {
691 if (!lpeitem->current_path_effect && !lpeitem->path_effect_list->empty())
692 sp_lpe_item_set_current_path_effect(lpeitem, lpeitem->path_effect_list->back());
694 return lpeitem->current_path_effect;
695 }
697 Inkscape::LivePathEffect::Effect* sp_lpe_item_get_current_lpe(SPLPEItem *lpeitem)
698 {
699 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
701 if (lperef && lperef->lpeobject)
702 return lperef->lpeobject->lpe;
703 else
704 return NULL;
705 }
707 bool sp_lpe_item_set_current_path_effect(SPLPEItem *lpeitem, Inkscape::LivePathEffect::LPEObjectReference* lperef)
708 {
709 for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); it++) {
710 if ((*it)->lpeobject_repr == lperef->lpeobject_repr) {
711 lpeobject_ref_changed(NULL, (*it)->lpeobject, SP_LPE_ITEM(lpeitem)); // FIXME: explain why this is here?
712 lpeitem->current_path_effect = (*it); // current_path_effect should always be a pointer from the path_effect_list !
713 return true;
714 }
715 }
717 return false;
718 }
720 void sp_lpe_item_replace_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * old_lpeobj,
721 LivePathEffectObject * new_lpeobj)
722 {
723 HRefList hreflist;
724 for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
725 {
726 if ((*it)->lpeobject == old_lpeobj) {
727 const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
728 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
729 hreflist.push_back( std::string(hrefstr) );
730 g_free(hrefstr);
731 }
732 else {
733 hreflist.push_back( std::string((*it)->lpeobject_href) );
734 }
735 }
736 std::string r = hreflist_write_svg(hreflist);
737 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
738 }
740 // Enable or disable the path effects of the item.
741 // The counter allows nested calls
742 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable)
743 {
744 if (enable) {
745 lpeitem->path_effects_enabled++;
746 }
747 else {
748 lpeitem->path_effects_enabled--;
749 }
750 }
752 // Are the path effects enabled on this item ?
753 bool sp_lpe_item_path_effects_enabled(SPLPEItem *lpeitem)
754 {
755 return lpeitem->path_effects_enabled > 0;
756 }
758 /*
759 Local Variables:
760 mode:c++
761 c-file-style:"stroustrup"
762 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
763 indent-tabs-mode:nil
764 fill-column:99
765 End:
766 */
767 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :