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/lpe-path_length.h"
22 #include "live_effects/lpeobject.h"
23 #include "live_effects/lpeobject-reference.h"
25 #include "sp-path.h"
26 #include "sp-item-group.h"
27 #include "streq.h"
28 #include "macros.h"
29 #include "attributes.h"
30 #include "sp-lpe-item.h"
31 #include "xml/repr.h"
32 #include "uri.h"
33 #include "message-stack.h"
34 #include "inkscape.h"
35 #include "desktop.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_modified(SPObject *href, guint flags, SPLPEItem *lpeitem);
60 static void sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem);
61 static void sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem);
62 typedef std::list<std::string> HRefList;
63 static std::string patheffectlist_write_svg(PathEffectList const & list);
64 static std::string hreflist_write_svg(HRefList const & list);
66 static SPItemClass *parent_class;
68 GType
69 sp_lpe_item_get_type()
70 {
71 static GType lpe_item_type = 0;
73 if (!lpe_item_type) {
74 GTypeInfo lpe_item_info = {
75 sizeof(SPLPEItemClass),
76 NULL, NULL,
77 (GClassInitFunc) sp_lpe_item_class_init,
78 NULL, NULL,
79 sizeof(SPLPEItem),
80 16,
81 (GInstanceInitFunc) sp_lpe_item_init,
82 NULL, /* value_table */
83 };
84 lpe_item_type = g_type_register_static(SP_TYPE_ITEM, "SPLPEItem", &lpe_item_info, (GTypeFlags)0);
85 }
86 return lpe_item_type;
87 }
89 static void
90 sp_lpe_item_class_init(SPLPEItemClass *klass)
91 {
92 GObjectClass *gobject_class;
93 SPObjectClass *sp_object_class;
95 gobject_class = (GObjectClass *) klass;
96 sp_object_class = (SPObjectClass *) klass;
97 parent_class = (SPItemClass *)g_type_class_peek_parent (klass);
99 gobject_class->finalize = sp_lpe_item_finalize;
101 sp_object_class->build = sp_lpe_item_build;
102 sp_object_class->release = sp_lpe_item_release;
103 sp_object_class->set = sp_lpe_item_set;
104 sp_object_class->update = sp_lpe_item_update;
105 sp_object_class->modified = sp_lpe_item_modified;
106 sp_object_class->write = sp_lpe_item_write;
107 sp_object_class->child_added = sp_lpe_item_child_added;
108 sp_object_class->remove_child = sp_lpe_item_remove_child;
110 klass->update_patheffect = NULL;
111 }
113 static void
114 sp_lpe_item_init(SPLPEItem *lpeitem)
115 {
116 lpeitem->path_effects_enabled = 1;
118 lpeitem->path_effect_list = new PathEffectList();
119 lpeitem->current_path_effect = NULL;
121 lpeitem->lpe_modified_connection_list = new std::list<sigc::connection>();
122 }
124 static void
125 sp_lpe_item_finalize(GObject *object)
126 {
127 if (((GObjectClass *) (parent_class))->finalize) {
128 (* ((GObjectClass *) (parent_class))->finalize)(object);
129 }
130 }
132 /**
133 * Reads the Inkscape::XML::Node, and initializes SPLPEItem variables. For this to get called,
134 * our name must be associated with a repr via "sp_object_type_register". Best done through
135 * sp-object-repr.cpp's repr_name_entries array.
136 */
137 static void
138 sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
139 {
140 sp_object_read_attr(object, "inkscape:path-effect");
142 if (((SPObjectClass *) parent_class)->build) {
143 ((SPObjectClass *) parent_class)->build(object, document, repr);
144 }
145 }
147 /**
148 * Drops any allocated memory.
149 */
150 static void
151 sp_lpe_item_release(SPObject *object)
152 {
153 SPLPEItem *lpeitem = (SPLPEItem *) object;
155 // disconnect all modified listeners:
156 for (std::list<sigc::connection>::iterator mod_it = lpeitem->lpe_modified_connection_list->begin();
157 mod_it != lpeitem->lpe_modified_connection_list->end(); ++mod_it)
158 {
159 mod_it->disconnect();
160 }
161 delete lpeitem->lpe_modified_connection_list;
162 lpeitem->lpe_modified_connection_list = NULL;
164 PathEffectList::iterator it = lpeitem->path_effect_list->begin();
165 while ( it != lpeitem->path_effect_list->end() ) {
166 // unlink and delete all references in the list
167 (*it)->unlink();
168 delete *it;
169 it = lpeitem->path_effect_list->erase(it);
170 }
171 // delete the list itself
172 delete lpeitem->path_effect_list;
173 lpeitem->path_effect_list = NULL;
175 if (((SPObjectClass *) parent_class)->release)
176 ((SPObjectClass *) parent_class)->release(object);
177 }
179 /**
180 * Sets a specific value in the SPLPEItem.
181 */
182 static void
183 sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value)
184 {
185 SPLPEItem *lpeitem = (SPLPEItem *) object;
187 switch (key) {
188 case SP_ATTR_INKSCAPE_PATH_EFFECT:
189 {
190 lpeitem->current_path_effect = NULL;
192 // Disable the path effects while populating the LPE list
193 sp_lpe_item_enable_path_effects(lpeitem, false);
195 // disconnect all modified listeners:
196 for ( std::list<sigc::connection>::iterator mod_it = lpeitem->lpe_modified_connection_list->begin();
197 mod_it != lpeitem->lpe_modified_connection_list->end();
198 ++mod_it)
199 {
200 mod_it->disconnect();
201 }
202 lpeitem->lpe_modified_connection_list->clear();
203 // Clear the path effect list
204 PathEffectList::iterator it = lpeitem->path_effect_list->begin();
205 while ( it != lpeitem->path_effect_list->end() )
206 {
207 (*it)->unlink();
208 delete *it;
209 it = lpeitem->path_effect_list->erase(it);
210 }
212 // Parse the contents of "value" to rebuild the path effect reference list
213 if ( value ) {
214 std::istringstream iss(value);
215 std::string href;
216 while (std::getline(iss, href, ';'))
217 {
218 Inkscape::LivePathEffect::LPEObjectReference *path_effect_ref = new Inkscape::LivePathEffect::LPEObjectReference(object);
219 try {
220 path_effect_ref->link(href.c_str());
221 } catch (Inkscape::BadURIException e) {
222 g_warning("BadURIException when trying to find LPE: %s", e.what());
223 path_effect_ref->unlink();
224 delete path_effect_ref;
225 path_effect_ref = NULL;
226 }
228 lpeitem->path_effect_list->push_back(path_effect_ref);
229 if ( path_effect_ref->lpeobject && path_effect_ref->lpeobject->get_lpe() ) {
230 // connect modified-listener
231 lpeitem->lpe_modified_connection_list->push_back(
232 path_effect_ref->lpeobject->connectModified(sigc::bind(sigc::ptr_fun(&lpeobject_ref_modified), lpeitem)) );
233 } else {
234 // something has gone wrong in finding the right patheffect.
235 g_warning("Unknown LPE type specified, LPE stack effectively disabled");
236 // keep the effect in the lpestack, so the whole stack is effectively disabled but maintained
237 }
238 }
239 }
241 sp_lpe_item_enable_path_effects(lpeitem, true);
242 }
243 break;
244 default:
245 if (((SPObjectClass *) parent_class)->set) {
246 ((SPObjectClass *) parent_class)->set(object, key, value);
247 }
248 break;
249 }
250 }
252 /**
253 * Receives update notifications.
254 */
255 static void
256 sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags)
257 {
258 if (((SPObjectClass *) parent_class)->update) {
259 ((SPObjectClass *) parent_class)->update(object, ctx, flags);
260 }
262 // update the helperpaths of all LPEs applied to the item
263 // TODO: re-add for the new node tool
264 }
266 /**
267 * Sets modified flag for all sub-item views.
268 */
269 static void
270 sp_lpe_item_modified (SPObject *object, unsigned int flags)
271 {
272 if (SP_IS_GROUP(object) && (flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_USER_MODIFIED_FLAG_B)) {
273 sp_lpe_item_update_patheffect(SP_LPE_ITEM(object), true, true);
274 }
276 if (((SPObjectClass *) (parent_class))->modified) {
277 (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
278 }
279 }
281 /**
282 * Writes its settings to an incoming repr object, if any.
283 */
284 static Inkscape::XML::Node *
285 sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
286 {
287 SPLPEItem *lpeitem = (SPLPEItem *) object;
289 if (flags & SP_OBJECT_WRITE_EXT) {
290 if ( sp_lpe_item_has_path_effect(lpeitem) ) {
291 std::string href = patheffectlist_write_svg(*lpeitem->path_effect_list);
292 repr->setAttribute("inkscape:path-effect", href.c_str());
293 } else {
294 repr->setAttribute("inkscape:path-effect", NULL);
295 }
296 }
298 if (((SPObjectClass *)(parent_class))->write) {
299 ((SPObjectClass *)(parent_class))->write(object, xml_doc, repr, flags);
300 }
302 return repr;
303 }
305 /**
306 * returns true when LPE was successful.
307 */
308 bool sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve) {
309 if (!lpeitem) return false;
310 if (!curve) return false;
312 if (sp_lpe_item_has_path_effect(lpeitem) && sp_lpe_item_path_effects_enabled(lpeitem)) {
313 for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
314 {
315 LivePathEffectObject *lpeobj = (*it)->lpeobject;
316 if (!lpeobj) {
317 /** \todo Investigate the cause of this.
318 * For example, this happens when copy pasting an object with LPE applied. Probably because the object is pasted while the effect is not yet pasted to defs, and cannot be found.
319 */
320 g_warning("sp_lpe_item_perform_path_effect - NULL lpeobj in list!");
321 return false;
322 }
323 Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
324 if (!lpe) {
325 /** \todo Investigate the cause of this.
326 * Not sure, but I think this can happen when an unknown effect type is specified...
327 */
328 g_warning("sp_lpe_item_perform_path_effect - lpeobj with invalid lpe in the stack!");
329 return false;
330 }
332 if (lpe->isVisible()) {
333 if (lpe->acceptsNumClicks() > 0 && !lpe->isReady()) {
334 // if the effect expects mouse input before being applied and the input is not finished
335 // yet, we don't alter the path
336 return false;
337 }
339 // Groups have their doBeforeEffect called elsewhere
340 if (!SP_IS_GROUP(lpeitem)) {
341 lpe->doBeforeEffect(lpeitem);
342 }
344 try {
345 lpe->doEffect(curve);
346 }
347 catch (std::exception & e) {
348 g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what());
349 if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->messageStack()) {
350 SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
351 _("An exception occurred during execution of the Path Effect.") );
352 }
353 return false;
354 }
355 }
356 }
357 }
359 return true;
360 }
362 /**
363 * Calls any registered handlers for the update_patheffect action
364 */
365 void
366 sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write)
367 {
368 #ifdef SHAPE_VERBOSE
369 g_message("sp_lpe_item_update_patheffect: %p\n", lpeitem);
370 #endif
371 g_return_if_fail (lpeitem != NULL);
372 g_return_if_fail (SP_IS_LPE_ITEM (lpeitem));
374 if (!sp_lpe_item_path_effects_enabled(lpeitem))
375 return;
377 // TODO: hack! this will be removed when path length measuring is reimplemented in a better way
378 PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
379 std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
380 for (i = lpelist.begin(); i != lpelist.end(); ++i) {
381 if ((*i)->lpeobject) {
382 Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe();
383 if (dynamic_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)) {
384 if (!lpe->isVisible()) {
385 // we manually disable text for LPEPathLength
386 // use static_cast, because we already checked for the right type above
387 static_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)->hideCanvasText();
388 }
389 }
390 }
391 }
393 SPLPEItem *top;
395 if (wholetree) {
396 SPObject *prev_parent = lpeitem;
397 SPObject *parent = prev_parent->parent;
398 while (parent && SP_IS_LPE_ITEM(parent) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent))) {
399 prev_parent = parent;
400 parent = prev_parent->parent;
401 }
402 top = SP_LPE_ITEM(prev_parent);
403 }
404 else {
405 top = lpeitem;
406 }
408 if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect) {
409 SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect (top, write);
410 }
411 }
413 /**
414 * Gets called when any of the lpestack's lpeobject repr contents change: i.e. parameter change in any of the stacked LPEs
415 */
416 static void
417 lpeobject_ref_modified(SPObject */*href*/, guint /*flags*/, SPLPEItem *lpeitem)
418 {
419 #ifdef SHAPE_VERBOSE
420 g_message("lpeobject_ref_modified");
421 #endif
422 sp_lpe_item_update_patheffect (lpeitem, true, true);
423 }
425 static void
426 sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem)
427 {
428 if (SP_IS_GROUP(lpeitem)) {
429 GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
430 for ( GSList const *iter = item_list; iter; iter = iter->next ) {
431 SPObject *subitem = static_cast<SPObject *>(iter->data);
432 if (SP_IS_LPE_ITEM(subitem)) {
433 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(subitem));
434 }
435 }
436 }
437 else if (SP_IS_PATH(lpeitem)) {
438 Inkscape::XML::Node *pathrepr = SP_OBJECT_REPR(lpeitem);
439 if ( !pathrepr->attribute("inkscape:original-d") ) {
440 pathrepr->setAttribute("inkscape:original-d", pathrepr->attribute("d"));
441 }
442 }
443 }
445 static void
446 sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem)
447 {
448 if (SP_IS_GROUP(lpeitem)) {
449 GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
450 for ( GSList const *iter = item_list; iter; iter = iter->next ) {
451 SPObject *subitem = static_cast<SPObject *>(iter->data);
452 if (SP_IS_LPE_ITEM(subitem)) {
453 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(subitem));
454 }
455 }
456 }
457 else if (SP_IS_PATH(lpeitem)) {
458 Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem);
459 if (!sp_lpe_item_has_path_effect_recursive(lpeitem)
460 && repr->attribute("inkscape:original-d")) {
461 repr->setAttribute("d", repr->attribute("inkscape:original-d"));
462 repr->setAttribute("inkscape:original-d", NULL);
463 }
464 else {
465 sp_lpe_item_update_patheffect(lpeitem, true, true);
466 }
467 }
468 }
470 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset)
471 {
472 if (value) {
473 // Apply the path effects here because in the casse of a group, lpe->resetDefaults
474 // needs that all the subitems have their effects applied
475 sp_lpe_item_update_patheffect(lpeitem, false, true);
477 // Disable the path effects while preparing the new lpe
478 sp_lpe_item_enable_path_effects(lpeitem, false);
480 // Add the new reference to the list of LPE references
481 HRefList hreflist;
482 for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
483 {
484 hreflist.push_back( std::string((*it)->lpeobject_href) );
485 }
486 hreflist.push_back( std::string(value) );
487 std::string hrefs = hreflist_write_svg(hreflist);
489 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", hrefs.c_str());
491 // make sure there is an original-d for paths!!!
492 sp_lpe_item_create_original_path_recursive(lpeitem);
494 LivePathEffectObject *lpeobj = lpeitem->path_effect_list->back()->lpeobject;
495 if (lpeobj && lpeobj->get_lpe()) {
496 Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
497 // Ask the path effect to reset itself if it doesn't have parameters yet
498 if (reset) {
499 // has to be called when all the subitems have their lpes applied
500 lpe->resetDefaults(lpeitem);
501 }
503 // perform this once when the effect is applied
504 lpe->doOnApply(SP_LPE_ITEM(lpeitem));
506 // indicate that all necessary preparations are done and the effect can be performed
507 lpe->setReady();
508 }
510 //Enable the path effects now that everything is ready to apply the new path effect
511 sp_lpe_item_enable_path_effects(lpeitem, true);
513 // Apply the path effect
514 sp_lpe_item_update_patheffect(lpeitem, true, true);
515 }
516 }
518 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj)
519 {
520 const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
521 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
522 sp_lpe_item_add_path_effect(lpeitem, hrefstr, false);
523 g_free(hrefstr);
524 }
526 void sp_lpe_item_remove_current_path_effect(SPLPEItem *lpeitem, bool keep_paths)
527 {
528 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
529 if (!lperef)
530 return;
532 PathEffectList new_list = *lpeitem->path_effect_list;
533 new_list.remove(lperef); //current lpe ref is always our 'own' pointer from the path_effect_list
534 std::string r = patheffectlist_write_svg(new_list);
536 if (!r.empty()) {
537 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
538 } else {
539 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
540 }
542 if (!keep_paths) {
543 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
544 }
545 }
547 void sp_lpe_item_remove_all_path_effects(SPLPEItem *lpeitem, bool keep_paths)
548 {
549 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
551 if (!keep_paths) {
552 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
553 }
554 }
556 void sp_lpe_item_down_current_path_effect(SPLPEItem *lpeitem)
557 {
558 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
559 if (!lperef)
560 return;
562 PathEffectList new_list = *lpeitem->path_effect_list;
563 PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
564 if (cur_it != new_list.end()) {
565 PathEffectList::iterator down_it = cur_it;
566 down_it++;
567 if (down_it != new_list.end()) { // perhaps current effect is already last effect
568 std::iter_swap(cur_it, down_it);
569 }
570 }
571 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 }
577 void sp_lpe_item_up_current_path_effect(SPLPEItem *lpeitem)
578 {
579 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
580 if (!lperef)
581 return;
583 PathEffectList new_list = *lpeitem->path_effect_list;
584 PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
585 if (cur_it != new_list.end() && cur_it != new_list.begin()) {
586 PathEffectList::iterator up_it = cur_it;
587 up_it--;
588 std::iter_swap(cur_it, up_it);
589 }
590 std::string r = patheffectlist_write_svg(new_list);
592 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
594 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
595 }
597 /** used for shapes so they can see if they should also disable shape calculation and read from d= */
598 bool sp_lpe_item_has_broken_path_effect(SPLPEItem *lpeitem)
599 {
600 if (lpeitem->path_effect_list->empty())
601 return false;
603 // go through the list; if some are unknown or invalid, return true
604 PathEffectList effect_list = sp_lpe_item_get_effect_list(lpeitem);
605 for (PathEffectList::iterator it = effect_list.begin(); it != effect_list.end(); it++)
606 {
607 LivePathEffectObject *lpeobj = (*it)->lpeobject;
608 if (!lpeobj || !lpeobj->get_lpe())
609 return true;
610 }
612 return false;
613 }
616 bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem)
617 {
618 if (lpeitem->path_effect_list->empty())
619 return false;
621 // go through the list; if some are unknown or invalid, we are not an LPE item!
622 PathEffectList effect_list = sp_lpe_item_get_effect_list(lpeitem);
623 for (PathEffectList::iterator it = effect_list.begin(); it != effect_list.end(); it++)
624 {
625 LivePathEffectObject *lpeobj = (*it)->lpeobject;
626 if (!lpeobj || !lpeobj->get_lpe())
627 return false;
628 }
630 return true;
631 }
633 bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem)
634 {
635 SPObject *parent = lpeitem->parent;
636 if (parent && SP_IS_LPE_ITEM(parent)) {
637 return sp_lpe_item_has_path_effect(lpeitem) || sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent));
638 }
639 else {
640 return sp_lpe_item_has_path_effect(lpeitem);
641 }
642 }
644 Inkscape::LivePathEffect::Effect*
645 sp_lpe_item_has_path_effect_of_type(SPLPEItem *lpeitem, int type)
646 {
647 std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
648 for (i = lpeitem->path_effect_list->begin(); i != lpeitem->path_effect_list->end(); ++i) {
649 Inkscape::LivePathEffect::Effect* lpe = (*i)->lpeobject->get_lpe();
650 if (lpe && (lpe->effectType() == type)) {
651 return lpe;
652 }
653 }
654 return NULL;
655 }
657 /* Return false if the item is not a path or already has a shape applied */
658 bool sp_lpe_item_can_accept_freehand_shape(SPLPEItem *lpeitem)
659 {
660 if (!SP_IS_PATH(lpeitem))
661 return false;
663 if (sp_lpe_item_has_path_effect_of_type(lpeitem, Inkscape::LivePathEffect::FREEHAND_SHAPE))
664 return false;
666 return true;
667 }
669 void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt)
670 {
671 Inkscape::LivePathEffect::LPEObjectReference *lperef = sp_lpe_item_get_current_lpereference(lpeitem);
672 if (lperef && lperef->lpeobject && lperef->lpeobject->get_lpe()) {
673 lperef->lpeobject->get_lpe()->editNextParamOncanvas(SP_ITEM(lpeitem), dt);
674 }
675 }
677 static void
678 sp_lpe_item_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
679 {
680 if (((SPObjectClass *) (parent_class))->child_added)
681 (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref);
683 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
684 SPObject *ochild = sp_object_get_child_by_repr(object, child);
685 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
686 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(ochild));
687 }
688 }
689 }
691 static void
692 sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child)
693 {
694 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
695 SPObject *ochild = sp_object_get_child_by_repr(object, child);
696 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
697 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(ochild));
698 }
699 }
701 if (((SPObjectClass *) (parent_class))->remove_child)
702 (* ((SPObjectClass *) (parent_class))->remove_child) (object, child);
703 }
705 static std::string patheffectlist_write_svg(PathEffectList const & list)
706 {
707 HRefList hreflist;
708 for (PathEffectList::const_iterator it = list.begin(); it != list.end(); ++it)
709 {
710 hreflist.push_back( std::string((*it)->lpeobject_href) );
711 }
712 return hreflist_write_svg(hreflist);
713 }
715 /**
716 * THE function that should be used to generate any patheffectlist string.
717 * one of the methods to change the effect list:
718 * - create temporary href list
719 * - populate the templist with the effects from the old list that you want to have and their order
720 * - call this function with temp list as param
721 */
722 static std::string hreflist_write_svg(HRefList const & list)
723 {
724 std::string r;
725 bool semicolon_first = false;
726 for (HRefList::const_iterator it = list.begin(); it != list.end(); ++it)
727 {
728 if (semicolon_first) {
729 r += ';';
730 }
731 semicolon_first = true;
733 r += (*it);
734 }
735 return r;
736 }
738 // Return a copy of the effect list
739 PathEffectList sp_lpe_item_get_effect_list(SPLPEItem *lpeitem)
740 {
741 return *lpeitem->path_effect_list;
742 }
744 Inkscape::LivePathEffect::LPEObjectReference* sp_lpe_item_get_current_lpereference(SPLPEItem *lpeitem)
745 {
746 if (!lpeitem->current_path_effect && !lpeitem->path_effect_list->empty()) {
747 sp_lpe_item_set_current_path_effect(lpeitem, lpeitem->path_effect_list->back());
748 }
750 return lpeitem->current_path_effect;
751 }
753 Inkscape::LivePathEffect::Effect* sp_lpe_item_get_current_lpe(SPLPEItem *lpeitem)
754 {
755 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
757 if (lperef && lperef->lpeobject)
758 return lperef->lpeobject->get_lpe();
759 else
760 return NULL;
761 }
763 bool sp_lpe_item_set_current_path_effect(SPLPEItem *lpeitem, Inkscape::LivePathEffect::LPEObjectReference* lperef)
764 {
765 for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); it++) {
766 if ((*it)->lpeobject_repr == lperef->lpeobject_repr) {
767 lpeitem->current_path_effect = (*it); // current_path_effect should always be a pointer from the path_effect_list !
768 return true;
769 }
770 }
772 return false;
773 }
775 /**
776 * Writes a new "inkscape:path-effect" string to xml, where the old_lpeobjects are substituted by the new ones.
777 * Note that this method messes up the item's \c PathEffectList.
778 */
779 void SPLPEItem::replacePathEffects( std::vector<LivePathEffectObject const *> const old_lpeobjs,
780 std::vector<LivePathEffectObject const *> const new_lpeobjs )
781 {
782 HRefList hreflist;
783 for (PathEffectList::const_iterator it = this->path_effect_list->begin(); it != this->path_effect_list->end(); ++it)
784 {
785 LivePathEffectObject const * current_lpeobj = (*it)->lpeobject;
786 std::vector<LivePathEffectObject const *>::const_iterator found_it(std::find(old_lpeobjs.begin(), old_lpeobjs.end(), current_lpeobj));
787 if ( found_it != old_lpeobjs.end() ) {
788 std::vector<LivePathEffectObject const *>::difference_type found_index = std::distance (old_lpeobjs.begin(), found_it);
789 const gchar * repr_id = SP_OBJECT_REPR(new_lpeobjs[found_index])->attribute("id");
790 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
791 hreflist.push_back( std::string(hrefstr) );
792 g_free(hrefstr);
793 }
794 else {
795 hreflist.push_back( std::string((*it)->lpeobject_href) );
796 }
797 }
798 std::string r = hreflist_write_svg(hreflist);
799 SP_OBJECT_REPR(this)->setAttribute("inkscape:path-effect", r.c_str());
800 }
802 /**
803 * Check all effects in the stack if they are used by other items, and fork them if so.
804 * It is not recommended to fork the effects by yourself calling LivePathEffectObject::fork_private_if_necessary,
805 * use this method instead.
806 * Returns true if one or more effects were forked; returns false if nothing was done.
807 */
808 bool sp_lpe_item_fork_path_effects_if_necessary(SPLPEItem *lpeitem, unsigned int nr_of_allowed_users)
809 {
810 bool forked = false;
812 if ( sp_lpe_item_has_path_effect(lpeitem) ) {
813 // If one of the path effects is used by 2 or more items, fork it
814 // so that each object has its own independent copy of the effect.
815 // Note: replacing path effects messes up the path effect list
817 // Clones of the LPEItem will increase the refcount of the lpeobjects.
818 // Therefore, nr_of_allowed_users should be increased with the number of clones (i.e. refs to the lpeitem)
819 nr_of_allowed_users += SP_OBJECT(lpeitem)->hrefcount;
821 std::vector<LivePathEffectObject const *> old_lpeobjs, new_lpeobjs;
822 PathEffectList effect_list = sp_lpe_item_get_effect_list(lpeitem);
823 for (PathEffectList::iterator it = effect_list.begin(); it != effect_list.end(); it++)
824 {
825 LivePathEffectObject *lpeobj = (*it)->lpeobject;
826 if (lpeobj) {
827 LivePathEffectObject *forked_lpeobj = lpeobj->fork_private_if_necessary(nr_of_allowed_users);
828 if (forked_lpeobj != lpeobj) {
829 forked = true;
830 old_lpeobjs.push_back(lpeobj);
831 new_lpeobjs.push_back(forked_lpeobj);
832 }
833 }
834 }
836 if (forked) {
837 lpeitem->replacePathEffects(old_lpeobjs, new_lpeobjs);
838 }
839 }
841 return forked;
842 }
844 // Enable or disable the path effects of the item.
845 // The counter allows nested calls
846 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable)
847 {
848 if (enable) {
849 lpeitem->path_effects_enabled++;
850 }
851 else {
852 lpeitem->path_effects_enabled--;
853 }
854 }
856 // Are the path effects enabled on this item ?
857 bool sp_lpe_item_path_effects_enabled(SPLPEItem *lpeitem)
858 {
859 return lpeitem->path_effects_enabled > 0;
860 }
862 /*
863 Local Variables:
864 mode:c++
865 c-file-style:"stroustrup"
866 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
867 indent-tabs-mode:nil
868 fill-column:99
869 End:
870 */
871 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :