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 "node-context.h"
37 #include "shape-editor.h"
39 #include <algorithm>
41 /* LPEItem base class */
43 static void sp_lpe_item_class_init(SPLPEItemClass *klass);
44 static void sp_lpe_item_init(SPLPEItem *lpe_item);
45 static void sp_lpe_item_finalize(GObject *object);
47 static void sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
48 static void sp_lpe_item_release(SPObject *object);
49 static void sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value);
50 static void sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags);
51 static void sp_lpe_item_modified (SPObject *object, unsigned int flags);
52 static Inkscape::XML::Node *sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags);
54 static void sp_lpe_item_child_added (SPObject * object, Inkscape::XML::Node * child, Inkscape::XML::Node * ref);
55 static void sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child);
57 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable);
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 lpeitem->lpe_modified_connection_list = new std::list<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 // disconnect all modified listeners:
157 for (std::list<sigc::connection>::iterator mod_it = lpeitem->lpe_modified_connection_list->begin();
158 mod_it != lpeitem->lpe_modified_connection_list->end(); ++mod_it)
159 {
160 mod_it->disconnect();
161 }
162 delete lpeitem->lpe_modified_connection_list;
163 lpeitem->lpe_modified_connection_list = NULL;
165 PathEffectList::iterator it = lpeitem->path_effect_list->begin();
166 while ( it != lpeitem->path_effect_list->end() ) {
167 // unlink and delete all references in the list
168 (*it)->unlink();
169 delete *it;
170 it = lpeitem->path_effect_list->erase(it);
171 }
172 // delete the list itself
173 delete lpeitem->path_effect_list;
174 lpeitem->path_effect_list = NULL;
176 if (((SPObjectClass *) parent_class)->release)
177 ((SPObjectClass *) parent_class)->release(object);
178 }
180 /**
181 * Sets a specific value in the SPLPEItem.
182 */
183 static void
184 sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value)
185 {
186 SPLPEItem *lpeitem = (SPLPEItem *) object;
188 switch (key) {
189 case SP_ATTR_INKSCAPE_PATH_EFFECT:
190 {
191 lpeitem->current_path_effect = NULL;
193 // Disable the path effects while populating the LPE list
194 sp_lpe_item_enable_path_effects(lpeitem, false);
196 // disconnect all modified listeners:
197 for ( std::list<sigc::connection>::iterator mod_it = lpeitem->lpe_modified_connection_list->begin();
198 mod_it != lpeitem->lpe_modified_connection_list->end();
199 ++mod_it)
200 {
201 mod_it->disconnect();
202 }
203 lpeitem->lpe_modified_connection_list->clear();
204 // Clear the path effect list
205 PathEffectList::iterator it = lpeitem->path_effect_list->begin();
206 while ( it != lpeitem->path_effect_list->end() )
207 {
208 (*it)->unlink();
209 delete *it;
210 it = lpeitem->path_effect_list->erase(it);
211 }
213 // Parse the contents of "value" to rebuild the path effect reference list
214 if ( value ) {
215 std::istringstream iss(value);
216 std::string href;
217 while (std::getline(iss, href, ';'))
218 {
219 Inkscape::LivePathEffect::LPEObjectReference *path_effect_ref = new Inkscape::LivePathEffect::LPEObjectReference(object);
220 try {
221 path_effect_ref->link(href.c_str());
222 } catch (Inkscape::BadURIException e) {
223 g_warning("BadURIException when trying to find LPE: %s", e.what());
224 path_effect_ref->unlink();
225 delete path_effect_ref;
226 path_effect_ref = NULL;
227 }
229 lpeitem->path_effect_list->push_back(path_effect_ref);
230 if ( path_effect_ref->lpeobject && path_effect_ref->lpeobject->get_lpe() ) {
231 // connect modified-listener
232 lpeitem->lpe_modified_connection_list->push_back(
233 path_effect_ref->lpeobject->connectModified(sigc::bind(sigc::ptr_fun(&lpeobject_ref_modified), lpeitem)) );
234 } else {
235 // something has gone wrong in finding the right patheffect.
236 g_warning("Unknown LPE type specified, LPE stack effectively disabled");
237 // keep the effect in the lpestack, so the whole stack is effectively disabled but maintained
238 }
239 }
240 }
242 sp_lpe_item_enable_path_effects(lpeitem, true);
243 }
244 break;
245 default:
246 if (((SPObjectClass *) parent_class)->set) {
247 ((SPObjectClass *) parent_class)->set(object, key, value);
248 }
249 break;
250 }
251 }
253 /**
254 * Receives update notifications.
255 */
256 static void
257 sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags)
258 {
259 if (((SPObjectClass *) parent_class)->update) {
260 ((SPObjectClass *) parent_class)->update(object, ctx, flags);
261 }
263 // update the helperpaths of all LPEs applied to the item
264 // TODO: is there a more canonical place for this, since we don't have instant access to the item's nodepath?
265 // FIXME: this is called multiple (at least 3) times; how can we avoid this?
267 // FIXME: ditch inkscape_active_event_context()
268 SPEventContext *ec = inkscape_active_event_context();
269 if (!SP_IS_NODE_CONTEXT(ec)) return;
270 ShapeEditor *sh = ec->shape_editor;
271 g_assert(sh);
272 if (!sh->has_nodepath()) return;
274 Inkscape::NodePath::Path *np = sh->get_nodepath();
275 sp_nodepath_update_helperpaths(np);
276 }
278 /**
279 * Sets modified flag for all sub-item views.
280 */
281 static void
282 sp_lpe_item_modified (SPObject *object, unsigned int flags)
283 {
284 if (SP_IS_GROUP(object) && (flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_USER_MODIFIED_FLAG_B)) {
285 sp_lpe_item_update_patheffect(SP_LPE_ITEM(object), true, true);
286 }
288 if (((SPObjectClass *) (parent_class))->modified) {
289 (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
290 }
291 }
293 /**
294 * Writes its settings to an incoming repr object, if any.
295 */
296 static Inkscape::XML::Node *
297 sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
298 {
299 SPLPEItem *lpeitem = (SPLPEItem *) object;
301 if (flags & SP_OBJECT_WRITE_EXT) {
302 if ( sp_lpe_item_has_path_effect(lpeitem) ) {
303 std::string href = patheffectlist_write_svg(*lpeitem->path_effect_list);
304 repr->setAttribute("inkscape:path-effect", href.c_str());
305 } else {
306 repr->setAttribute("inkscape:path-effect", NULL);
307 }
308 }
310 if (((SPObjectClass *)(parent_class))->write) {
311 ((SPObjectClass *)(parent_class))->write(object, xml_doc, repr, flags);
312 }
314 return repr;
315 }
317 /**
318 * returns true when LPE was successful.
319 */
320 bool sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve) {
321 if (!lpeitem) return false;
322 if (!curve) return false;
324 if (sp_lpe_item_has_path_effect(lpeitem) && sp_lpe_item_path_effects_enabled(lpeitem)) {
325 for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
326 {
327 LivePathEffectObject *lpeobj = (*it)->lpeobject;
328 if (!lpeobj) {
329 /** \todo Investigate the cause of this.
330 * 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.
331 */
332 g_warning("sp_lpe_item_perform_path_effect - NULL lpeobj in list!");
333 return false;
334 }
335 Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
336 if (!lpe) {
337 /** \todo Investigate the cause of this.
338 * Not sure, but I think this can happen when an unknown effect type is specified...
339 */
340 g_warning("sp_lpe_item_perform_path_effect - lpeobj with invalid lpe in the stack!");
341 return false;
342 }
344 if (lpe->isVisible()) {
345 if (lpe->acceptsNumClicks() > 0 && !lpe->isReady()) {
346 // if the effect expects mouse input before being applied and the input is not finished
347 // yet, we don't alter the path
348 return false;
349 }
351 // Groups have their doBeforeEffect called elsewhere
352 if (!SP_IS_GROUP(lpeitem)) {
353 lpe->doBeforeEffect(lpeitem);
354 }
356 try {
357 lpe->doEffect(curve);
358 }
359 catch (std::exception & e) {
360 g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what());
361 if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->messageStack()) {
362 SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
363 _("An exception occurred during execution of the Path Effect.") );
364 }
365 return false;
366 }
367 }
368 }
369 }
371 return true;
372 }
374 /**
375 * Calls any registered handlers for the update_patheffect action
376 */
377 void
378 sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write)
379 {
380 #ifdef SHAPE_VERBOSE
381 g_message("sp_lpe_item_update_patheffect: %p\n", lpeitem);
382 #endif
383 g_return_if_fail (lpeitem != NULL);
384 g_return_if_fail (SP_IS_LPE_ITEM (lpeitem));
386 if (!sp_lpe_item_path_effects_enabled(lpeitem))
387 return;
389 // TODO: hack! this will be removed when path length measuring is reimplemented in a better way
390 PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
391 std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
392 for (i = lpelist.begin(); i != lpelist.end(); ++i) {
393 if ((*i)->lpeobject) {
394 Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe();
395 if (dynamic_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)) {
396 if (!lpe->isVisible()) {
397 // we manually disable text for LPEPathLength
398 dynamic_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)->hideCanvasText();
399 }
400 }
401 }
402 }
404 SPLPEItem *top;
406 if (wholetree) {
407 SPObject *prev_parent = lpeitem;
408 SPObject *parent = prev_parent->parent;
409 while (parent && SP_IS_LPE_ITEM(parent) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent))) {
410 prev_parent = parent;
411 parent = prev_parent->parent;
412 }
413 top = SP_LPE_ITEM(prev_parent);
414 }
415 else {
416 top = lpeitem;
417 }
419 if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect) {
420 SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect (top, write);
421 }
422 }
424 /**
425 * Gets called when any of the lpestack's lpeobject repr contents change: i.e. parameter change in any of the stacked LPEs
426 */
427 static void
428 lpeobject_ref_modified(SPObject */*href*/, guint /*flags*/, SPLPEItem *lpeitem)
429 {
430 #ifdef SHAPE_VERBOSE
431 g_message("lpeobject_ref_modified");
432 #endif
433 sp_lpe_item_update_patheffect (lpeitem, true, true);
434 }
436 static void
437 sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem)
438 {
439 if (SP_IS_GROUP(lpeitem)) {
440 GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
441 for ( GSList const *iter = item_list; iter; iter = iter->next ) {
442 SPObject *subitem = static_cast<SPObject *>(iter->data);
443 if (SP_IS_LPE_ITEM(subitem)) {
444 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(subitem));
445 }
446 }
447 }
448 else if (SP_IS_PATH(lpeitem)) {
449 Inkscape::XML::Node *pathrepr = SP_OBJECT_REPR(lpeitem);
450 if ( !pathrepr->attribute("inkscape:original-d") ) {
451 pathrepr->setAttribute("inkscape:original-d", pathrepr->attribute("d"));
452 }
453 }
454 }
456 static void
457 sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem)
458 {
459 if (SP_IS_GROUP(lpeitem)) {
460 GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
461 for ( GSList const *iter = item_list; iter; iter = iter->next ) {
462 SPObject *subitem = static_cast<SPObject *>(iter->data);
463 if (SP_IS_LPE_ITEM(subitem)) {
464 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(subitem));
465 }
466 }
467 }
468 else if (SP_IS_PATH(lpeitem)) {
469 Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem);
470 if (!sp_lpe_item_has_path_effect_recursive(lpeitem)
471 && repr->attribute("inkscape:original-d")) {
472 repr->setAttribute("d", repr->attribute("inkscape:original-d"));
473 repr->setAttribute("inkscape:original-d", NULL);
474 }
475 else {
476 sp_lpe_item_update_patheffect(lpeitem, true, true);
477 }
478 }
479 }
481 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset)
482 {
483 if (value) {
484 // Apply the path effects here because in the casse of a group, lpe->resetDefaults
485 // needs that all the subitems have their effects applied
486 sp_lpe_item_update_patheffect(lpeitem, false, true);
488 // Disable the path effects while preparing the new lpe
489 sp_lpe_item_enable_path_effects(lpeitem, false);
491 // Add the new reference to the list of LPE references
492 HRefList hreflist;
493 for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
494 {
495 hreflist.push_back( std::string((*it)->lpeobject_href) );
496 }
497 hreflist.push_back( std::string(value) );
498 std::string hrefs = hreflist_write_svg(hreflist);
500 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", hrefs.c_str());
502 // make sure there is an original-d for paths!!!
503 sp_lpe_item_create_original_path_recursive(lpeitem);
505 LivePathEffectObject *lpeobj = lpeitem->path_effect_list->back()->lpeobject;
506 if (lpeobj && lpeobj->get_lpe()) {
507 Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
508 // Ask the path effect to reset itself if it doesn't have parameters yet
509 if (reset) {
510 // has to be called when all the subitems have their lpes applied
511 lpe->resetDefaults(lpeitem);
512 }
514 // perform this once when the effect is applied
515 lpe->doOnApply(SP_LPE_ITEM(lpeitem));
517 // indicate that all necessary preparations are done and the effect can be performed
518 lpe->setReady();
519 }
521 //Enable the path effects now that everything is ready to apply the new path effect
522 sp_lpe_item_enable_path_effects(lpeitem, true);
524 // Apply the path effect
525 sp_lpe_item_update_patheffect(lpeitem, true, true);
526 }
527 }
529 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj)
530 {
531 const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
532 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
533 sp_lpe_item_add_path_effect(lpeitem, hrefstr, false);
534 g_free(hrefstr);
535 }
537 void sp_lpe_item_remove_current_path_effect(SPLPEItem *lpeitem, bool keep_paths)
538 {
539 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
540 if (!lperef)
541 return;
543 PathEffectList new_list = *lpeitem->path_effect_list;
544 new_list.remove(lperef); //current lpe ref is always our 'own' pointer from the path_effect_list
545 std::string r = patheffectlist_write_svg(new_list);
547 if (!r.empty()) {
548 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
549 } else {
550 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
551 }
553 if (!keep_paths) {
554 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
555 }
556 }
558 void sp_lpe_item_remove_all_path_effects(SPLPEItem *lpeitem, bool keep_paths)
559 {
560 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
562 if (!keep_paths) {
563 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
564 }
565 }
567 void sp_lpe_item_down_current_path_effect(SPLPEItem *lpeitem)
568 {
569 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
570 if (!lperef)
571 return;
573 PathEffectList new_list = *lpeitem->path_effect_list;
574 PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
575 if (cur_it != new_list.end()) {
576 PathEffectList::iterator down_it = cur_it;
577 down_it++;
578 if (down_it != new_list.end()) { // perhaps current effect is already last effect
579 std::iter_swap(cur_it, down_it);
580 }
581 }
582 std::string r = patheffectlist_write_svg(new_list);
583 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
585 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
586 }
588 void sp_lpe_item_up_current_path_effect(SPLPEItem *lpeitem)
589 {
590 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
591 if (!lperef)
592 return;
594 PathEffectList new_list = *lpeitem->path_effect_list;
595 PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
596 if (cur_it != new_list.end() && cur_it != new_list.begin()) {
597 PathEffectList::iterator up_it = cur_it;
598 up_it--;
599 std::iter_swap(cur_it, up_it);
600 }
601 std::string r = patheffectlist_write_svg(new_list);
603 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
605 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
606 }
608 /** used for shapes so they can see if they should also disable shape calculation and read from d= */
609 bool sp_lpe_item_has_broken_path_effect(SPLPEItem *lpeitem)
610 {
611 if (lpeitem->path_effect_list->empty())
612 return false;
614 // go through the list; if some are unknown or invalid, return true
615 PathEffectList effect_list = sp_lpe_item_get_effect_list(lpeitem);
616 for (PathEffectList::iterator it = effect_list.begin(); it != effect_list.end(); it++)
617 {
618 LivePathEffectObject *lpeobj = (*it)->lpeobject;
619 if (!lpeobj || !lpeobj->get_lpe())
620 return true;
621 }
623 return false;
624 }
627 bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem)
628 {
629 if (lpeitem->path_effect_list->empty())
630 return false;
632 // go through the list; if some are unknown or invalid, we are not an LPE item!
633 PathEffectList effect_list = sp_lpe_item_get_effect_list(lpeitem);
634 for (PathEffectList::iterator it = effect_list.begin(); it != effect_list.end(); it++)
635 {
636 LivePathEffectObject *lpeobj = (*it)->lpeobject;
637 if (!lpeobj || !lpeobj->get_lpe())
638 return false;
639 }
641 return true;
642 }
644 bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem)
645 {
646 SPObject *parent = lpeitem->parent;
647 if (parent && SP_IS_LPE_ITEM(parent)) {
648 return sp_lpe_item_has_path_effect(lpeitem) || sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent));
649 }
650 else {
651 return sp_lpe_item_has_path_effect(lpeitem);
652 }
653 }
655 Inkscape::LivePathEffect::Effect*
656 sp_lpe_item_has_path_effect_of_type(SPLPEItem *lpeitem, int type)
657 {
658 std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
659 for (i = lpeitem->path_effect_list->begin(); i != lpeitem->path_effect_list->end(); ++i) {
660 Inkscape::LivePathEffect::Effect* lpe = (*i)->lpeobject->get_lpe();
661 if (lpe && (lpe->effectType() == type)) {
662 return lpe;
663 }
664 }
665 return NULL;
666 }
668 /* Return false if the item is not a path or already has a shape applied */
669 bool sp_lpe_item_can_accept_freehand_shape(SPLPEItem *lpeitem)
670 {
671 if (!SP_IS_PATH(lpeitem))
672 return false;
674 if (sp_lpe_item_has_path_effect_of_type(lpeitem, Inkscape::LivePathEffect::FREEHAND_SHAPE))
675 return false;
677 return true;
678 }
680 void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt)
681 {
682 Inkscape::LivePathEffect::LPEObjectReference *lperef = sp_lpe_item_get_current_lpereference(lpeitem);
683 if (lperef && lperef->lpeobject && lperef->lpeobject->get_lpe()) {
684 lperef->lpeobject->get_lpe()->editNextParamOncanvas(SP_ITEM(lpeitem), dt);
685 }
686 }
688 static void
689 sp_lpe_item_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
690 {
691 if (((SPObjectClass *) (parent_class))->child_added)
692 (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref);
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_create_original_path_recursive(SP_LPE_ITEM(ochild));
698 }
699 }
700 }
702 static void
703 sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child)
704 {
705 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
706 SPObject *ochild = sp_object_get_child_by_repr(object, child);
707 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
708 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(ochild));
709 }
710 }
712 if (((SPObjectClass *) (parent_class))->remove_child)
713 (* ((SPObjectClass *) (parent_class))->remove_child) (object, child);
714 }
716 static std::string patheffectlist_write_svg(PathEffectList const & list)
717 {
718 HRefList hreflist;
719 for (PathEffectList::const_iterator it = list.begin(); it != list.end(); ++it)
720 {
721 hreflist.push_back( std::string((*it)->lpeobject_href) );
722 }
723 return hreflist_write_svg(hreflist);
724 }
726 /**
727 * THE function that should be used to generate any patheffectlist string.
728 * one of the methods to change the effect list:
729 * - create temporary href list
730 * - populate the templist with the effects from the old list that you want to have and their order
731 * - call this function with temp list as param
732 */
733 static std::string hreflist_write_svg(HRefList const & list)
734 {
735 std::string r;
736 bool semicolon_first = false;
737 for (HRefList::const_iterator it = list.begin(); it != list.end(); ++it)
738 {
739 if (semicolon_first) {
740 r += ';';
741 }
742 semicolon_first = true;
744 r += (*it);
745 }
746 return r;
747 }
749 // Return a copy of the effect list
750 PathEffectList sp_lpe_item_get_effect_list(SPLPEItem *lpeitem)
751 {
752 return *lpeitem->path_effect_list;
753 }
755 Inkscape::LivePathEffect::LPEObjectReference* sp_lpe_item_get_current_lpereference(SPLPEItem *lpeitem)
756 {
757 if (!lpeitem->current_path_effect && !lpeitem->path_effect_list->empty()) {
758 sp_lpe_item_set_current_path_effect(lpeitem, lpeitem->path_effect_list->back());
759 }
761 return lpeitem->current_path_effect;
762 }
764 Inkscape::LivePathEffect::Effect* sp_lpe_item_get_current_lpe(SPLPEItem *lpeitem)
765 {
766 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
768 if (lperef && lperef->lpeobject)
769 return lperef->lpeobject->get_lpe();
770 else
771 return NULL;
772 }
774 bool sp_lpe_item_set_current_path_effect(SPLPEItem *lpeitem, Inkscape::LivePathEffect::LPEObjectReference* lperef)
775 {
776 for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); it++) {
777 if ((*it)->lpeobject_repr == lperef->lpeobject_repr) {
778 lpeitem->current_path_effect = (*it); // current_path_effect should always be a pointer from the path_effect_list !
779 return true;
780 }
781 }
783 return false;
784 }
786 void sp_lpe_item_replace_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * old_lpeobj,
787 LivePathEffectObject * new_lpeobj)
788 {
789 HRefList hreflist;
790 for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
791 {
792 if ((*it)->lpeobject == old_lpeobj) {
793 const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
794 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
795 hreflist.push_back( std::string(hrefstr) );
796 g_free(hrefstr);
797 }
798 else {
799 hreflist.push_back( std::string((*it)->lpeobject_href) );
800 }
801 }
802 std::string r = hreflist_write_svg(hreflist);
803 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
804 }
806 // Enable or disable the path effects of the item.
807 // The counter allows nested calls
808 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable)
809 {
810 if (enable) {
811 lpeitem->path_effects_enabled++;
812 }
813 else {
814 lpeitem->path_effects_enabled--;
815 }
816 }
818 // Are the path effects enabled on this item ?
819 bool sp_lpe_item_path_effects_enabled(SPLPEItem *lpeitem)
820 {
821 return lpeitem->path_effects_enabled > 0;
822 }
824 /*
825 Local Variables:
826 mode:c++
827 c-file-style:"stroustrup"
828 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
829 indent-tabs-mode:nil
830 fill-column:99
831 End:
832 */
833 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :