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_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem);
60 static void lpeobject_ref_modified(SPObject *href, guint flags, SPLPEItem *lpeitem);
62 static void sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem);
63 static void sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem);
64 typedef std::list<std::string> HRefList;
65 static std::string patheffectlist_write_svg(PathEffectList const & list);
66 static std::string hreflist_write_svg(HRefList const & list);
68 static SPItemClass *parent_class;
70 GType
71 sp_lpe_item_get_type()
72 {
73 static GType lpe_item_type = 0;
75 if (!lpe_item_type) {
76 GTypeInfo lpe_item_info = {
77 sizeof(SPLPEItemClass),
78 NULL, NULL,
79 (GClassInitFunc) sp_lpe_item_class_init,
80 NULL, NULL,
81 sizeof(SPLPEItem),
82 16,
83 (GInstanceInitFunc) sp_lpe_item_init,
84 NULL, /* value_table */
85 };
86 lpe_item_type = g_type_register_static(SP_TYPE_ITEM, "SPLPEItem", &lpe_item_info, (GTypeFlags)0);
87 }
88 return lpe_item_type;
89 }
91 static void
92 sp_lpe_item_class_init(SPLPEItemClass *klass)
93 {
94 GObjectClass *gobject_class;
95 SPObjectClass *sp_object_class;
97 gobject_class = (GObjectClass *) klass;
98 sp_object_class = (SPObjectClass *) klass;
99 parent_class = (SPItemClass *)g_type_class_peek_parent (klass);
101 gobject_class->finalize = sp_lpe_item_finalize;
103 sp_object_class->build = sp_lpe_item_build;
104 sp_object_class->release = sp_lpe_item_release;
105 sp_object_class->set = sp_lpe_item_set;
106 sp_object_class->update = sp_lpe_item_update;
107 sp_object_class->modified = sp_lpe_item_modified;
108 sp_object_class->write = sp_lpe_item_write;
109 sp_object_class->child_added = sp_lpe_item_child_added;
110 sp_object_class->remove_child = sp_lpe_item_remove_child;
112 klass->update_patheffect = NULL;
113 }
115 static void
116 sp_lpe_item_init(SPLPEItem *lpeitem)
117 {
118 lpeitem->path_effects_enabled = 1;
120 lpeitem->path_effect_list = new PathEffectList();
121 lpeitem->current_path_effect = NULL;
123 new (&lpeitem->lpe_modified_connection) sigc::connection();
124 }
126 static void
127 sp_lpe_item_finalize(GObject *object)
128 {
129 if (((GObjectClass *) (parent_class))->finalize) {
130 (* ((GObjectClass *) (parent_class))->finalize)(object);
131 }
132 }
134 /**
135 * Reads the Inkscape::XML::Node, and initializes SPLPEItem variables. For this to get called,
136 * our name must be associated with a repr via "sp_object_type_register". Best done through
137 * sp-object-repr.cpp's repr_name_entries array.
138 */
139 static void
140 sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
141 {
142 sp_object_read_attr(object, "inkscape:path-effect");
144 if (((SPObjectClass *) parent_class)->build) {
145 ((SPObjectClass *) parent_class)->build(object, document, repr);
146 }
147 }
149 /**
150 * Drops any allocated memory.
151 */
152 static void
153 sp_lpe_item_release(SPObject *object)
154 {
155 SPLPEItem *lpeitem = (SPLPEItem *) object;
157 lpeitem->lpe_modified_connection.disconnect();
158 lpeitem->lpe_modified_connection.~connection();
160 PathEffectList::iterator it = lpeitem->path_effect_list->begin();
161 while ( it != lpeitem->path_effect_list->end() ) {
162 // unlink and delete all references in the list
163 (*it)->unlink();
164 delete *it;
165 it = lpeitem->path_effect_list->erase(it);
166 }
167 // delete the list itself
168 delete SP_LPE_ITEM(object)->path_effect_list;
170 if (((SPObjectClass *) parent_class)->release)
171 ((SPObjectClass *) parent_class)->release(object);
172 }
174 /**
175 * Sets a specific value in the SPLPEItem.
176 */
177 static void
178 sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value)
179 {
180 SPLPEItem *lpeitem = (SPLPEItem *) object;
182 switch (key) {
183 case SP_ATTR_INKSCAPE_PATH_EFFECT:
184 {
185 lpeitem->current_path_effect = NULL;
187 // Disable the path effects while populating the LPE list
188 sp_lpe_item_enable_path_effects(lpeitem, false);
190 // Clear the path effect list
191 PathEffectList::iterator it = lpeitem->path_effect_list->begin();
192 while ( it != lpeitem->path_effect_list->end() )
193 {
194 (*it)->unlink();
195 delete *it;
196 it = lpeitem->path_effect_list->erase(it);
197 }
199 // Parse the contents of "value" to rebuild the path effect reference list
200 if ( value ) {
201 std::istringstream iss(value);
202 std::string href;
203 while (std::getline(iss, href, ';'))
204 {
205 Inkscape::LivePathEffect::LPEObjectReference *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 when trying to find LPE: %s", e.what());
213 path_effect_ref->unlink();
214 delete path_effect_ref;
215 path_effect_ref = NULL;
216 }
218 lpeitem->path_effect_list->push_back(path_effect_ref);
219 if ( !(path_effect_ref->lpeobject && path_effect_ref->lpeobject->get_lpe()) ) {
220 // something has gone wrong in finding the right patheffect.
221 g_warning("Unknown LPE type specified, LPE stack effectively disabled");
222 // keep the effect in the lpestack, so the whole stack is effectively disabled but maintained
223 }
224 }
225 }
227 sp_lpe_item_enable_path_effects(lpeitem, true);
228 }
229 break;
230 default:
231 if (((SPObjectClass *) parent_class)->set) {
232 ((SPObjectClass *) parent_class)->set(object, key, value);
233 }
234 break;
235 }
236 }
238 /**
239 * Receives update notifications.
240 */
241 static void
242 sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags)
243 {
244 if (((SPObjectClass *) parent_class)->update) {
245 ((SPObjectClass *) parent_class)->update(object, ctx, flags);
246 }
248 // update the helperpaths of all LPEs applied to the item
249 // TODO: is there a more canonical place for this, since we don't have instant access to the item's nodepath?
250 // FIXME: this is called multiple (at least 3) times; how can we avoid this?
252 // FIXME: ditch inkscape_active_event_context()
253 SPEventContext *ec = inkscape_active_event_context();
254 if (!SP_IS_NODE_CONTEXT(ec)) return;
255 SPNodeContext *nc = SP_NODE_CONTEXT(ec);
256 ShapeEditor *sh = nc->shape_editor;
257 g_assert(sh);
258 if (!sh->has_nodepath()) return;
260 Inkscape::NodePath::Path *np = sh->get_nodepath();
261 sp_nodepath_update_helperpaths(np);
262 }
264 /**
265 * Sets modified flag for all sub-item views.
266 */
267 static void
268 sp_lpe_item_modified (SPObject *object, unsigned int flags)
269 {
270 if (SP_IS_GROUP(object) && (flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_USER_MODIFIED_FLAG_B)) {
271 sp_lpe_item_update_patheffect(SP_LPE_ITEM(object), true, true);
272 }
274 if (((SPObjectClass *) (parent_class))->modified) {
275 (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
276 }
277 }
279 /**
280 * Writes its settings to an incoming repr object, if any.
281 */
282 static Inkscape::XML::Node *
283 sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
284 {
285 SPLPEItem *lpeitem = (SPLPEItem *) object;
287 if ( sp_lpe_item_has_path_effect(lpeitem) ) {
288 std::string href = patheffectlist_write_svg(*lpeitem->path_effect_list);
289 repr->setAttribute("inkscape:path-effect", href.c_str());
290 } else {
291 repr->setAttribute("inkscape:path-effect", NULL);
292 }
294 if (((SPObjectClass *)(parent_class))->write) {
295 ((SPObjectClass *)(parent_class))->write(object, xml_doc, repr, flags);
296 }
298 return repr;
299 }
301 /**
302 * returns true when LPE was successful.
303 */
304 bool sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve) {
305 if (!lpeitem) return false;
306 if (!curve) return false;
308 if (sp_lpe_item_has_path_effect(lpeitem) && sp_lpe_item_path_effects_enabled(lpeitem)) {
309 for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
310 {
311 LivePathEffectObject *lpeobj = (*it)->lpeobject;
312 if (!lpeobj) {
313 /** \todo Investigate the cause of this.
314 * 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.
315 */
316 g_warning("sp_lpe_item_perform_path_effect - NULL lpeobj in list!");
317 return false;
318 }
319 Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
320 if (!lpe) {
321 /** \todo Investigate the cause of this.
322 * Not sure, but I think this can happen when an unknown effect type is specified...
323 */
324 g_warning("sp_lpe_item_perform_path_effect - lpeobj with invalid lpe in the stack!");
325 return false;
326 }
328 if (lpe->isVisible()) {
329 if (lpe->acceptsNumClicks() > 0 && !lpe->isReady()) {
330 // if the effect expects mouse input before being applied and the input is not finished
331 // yet, we don't alter the path
332 return false;
333 }
335 // Groups have their doBeforeEffect called elsewhere
336 if (!SP_IS_GROUP(lpeitem)) {
337 lpe->doBeforeEffect(lpeitem);
338 }
340 try {
341 lpe->doEffect(curve);
342 }
343 catch (std::exception & e) {
344 g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what());
345 if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->messageStack()) {
346 SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
347 _("An exception occurred during execution of the Path Effect.") );
348 }
349 return false;
350 }
351 }
352 }
353 }
355 return true;
356 }
358 /**
359 * Calls any registered handlers for the update_patheffect action
360 */
361 void
362 sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write)
363 {
364 #ifdef SHAPE_VERBOSE
365 g_message("sp_lpe_item_update_patheffect: %p\n", lpeitem);
366 #endif
367 g_return_if_fail (lpeitem != NULL);
368 g_return_if_fail (SP_IS_LPE_ITEM (lpeitem));
370 if (!sp_lpe_item_path_effects_enabled(lpeitem))
371 return;
373 // TODO: hack! this will be removed when path length measuring is reimplemented in a better way
374 PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
375 std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
376 for (i = lpelist.begin(); i != lpelist.end(); ++i) {
377 if ((*i)->lpeobject) {
378 Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe();
379 if (dynamic_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)) {
380 if (!lpe->isVisible()) {
381 // we manually disable text for LPEPathLength
382 dynamic_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)->hideCanvasText();
383 }
384 }
385 }
386 }
388 SPLPEItem *top;
390 if (wholetree) {
391 SPObject *prev_parent = lpeitem;
392 SPObject *parent = prev_parent->parent;
393 while (parent && SP_IS_LPE_ITEM(parent) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent))) {
394 prev_parent = parent;
395 parent = prev_parent->parent;
396 }
397 top = SP_LPE_ITEM(prev_parent);
398 }
399 else {
400 top = lpeitem;
401 }
403 if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect) {
404 SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect (top, write);
405 }
406 }
408 /**
409 * Gets called when (re)attached to another lpeobject.
410 */
411 static void
412 lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem)
413 {
414 if (old_ref) {
415 sp_signal_disconnect_by_data(old_ref, lpeitem);
416 }
417 if ( IS_LIVEPATHEFFECT(ref) && ref != lpeitem )
418 {
419 lpeitem->lpe_modified_connection.disconnect();
420 lpeitem->lpe_modified_connection = ref->connectModified(sigc::bind(sigc::ptr_fun(&lpeobject_ref_modified), lpeitem));
421 lpeobject_ref_modified(ref, 0, lpeitem);
422 }
423 }
425 /**
426 * Gets called when lpeobject repr contents change: i.e. parameter change.
427 */
428 static void
429 lpeobject_ref_modified(SPObject */*href*/, guint /*flags*/, SPLPEItem *lpeitem)
430 {
431 sp_lpe_item_update_patheffect (lpeitem, true, true);
432 }
434 static void
435 sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem)
436 {
437 if (SP_IS_GROUP(lpeitem)) {
438 GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
439 for ( GSList const *iter = item_list; iter; iter = iter->next ) {
440 SPObject *subitem = static_cast<SPObject *>(iter->data);
441 if (SP_IS_LPE_ITEM(subitem)) {
442 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(subitem));
443 }
444 }
445 }
446 else if (SP_IS_PATH(lpeitem)) {
447 Inkscape::XML::Node *pathrepr = SP_OBJECT_REPR(lpeitem);
448 if ( !pathrepr->attribute("inkscape:original-d") ) {
449 pathrepr->setAttribute("inkscape:original-d", pathrepr->attribute("d"));
450 }
451 }
452 }
454 static void
455 sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem)
456 {
457 if (SP_IS_GROUP(lpeitem)) {
458 GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
459 for ( GSList const *iter = item_list; iter; iter = iter->next ) {
460 SPObject *subitem = static_cast<SPObject *>(iter->data);
461 if (SP_IS_LPE_ITEM(subitem)) {
462 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(subitem));
463 }
464 }
465 }
466 else if (SP_IS_PATH(lpeitem)) {
467 Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem);
468 if (!sp_lpe_item_has_path_effect_recursive(lpeitem)
469 && repr->attribute("inkscape:original-d")) {
470 repr->setAttribute("d", repr->attribute("inkscape:original-d"));
471 repr->setAttribute("inkscape:original-d", NULL);
472 }
473 else {
474 sp_lpe_item_update_patheffect(lpeitem, true, true);
475 }
476 }
477 }
479 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset)
480 {
481 if (value) {
482 // Apply the path effects here because in the casse of a group, lpe->resetDefaults
483 // needs that all the subitems have their effects applied
484 sp_lpe_item_update_patheffect(lpeitem, false, true);
486 // Disable the path effects while preparing the new lpe
487 sp_lpe_item_enable_path_effects(lpeitem, false);
489 // Add the new reference to the list of LPE references
490 HRefList hreflist;
491 for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
492 {
493 hreflist.push_back( std::string((*it)->lpeobject_href) );
494 }
495 hreflist.push_back( std::string(value) );
496 std::string hrefs = hreflist_write_svg(hreflist);
498 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", hrefs.c_str());
500 // make sure there is an original-d for paths!!!
501 sp_lpe_item_create_original_path_recursive(lpeitem);
503 LivePathEffectObject *lpeobj = lpeitem->path_effect_list->back()->lpeobject;
504 if (lpeobj && lpeobj->get_lpe()) {
505 Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
506 // Ask the path effect to reset itself if it doesn't have parameters yet
507 if (reset) {
508 // has to be called when all the subitems have their lpes applied
509 lpe->resetDefaults(lpeitem);
510 }
512 // perform this once when the effect is applied
513 lpe->doOnApply(SP_LPE_ITEM(lpeitem));
515 // indicate that all necessary preparations are done and the effect can be performed
516 lpe->setReady();
517 }
519 //Enable the path effects now that everything is ready to apply the new path effect
520 sp_lpe_item_enable_path_effects(lpeitem, true);
522 // Apply the path effect
523 sp_lpe_item_update_patheffect(lpeitem, true, true);
524 }
525 }
527 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj)
528 {
529 const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
530 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
531 sp_lpe_item_add_path_effect(lpeitem, hrefstr, false);
532 g_free(hrefstr);
533 }
535 void sp_lpe_item_remove_current_path_effect(SPLPEItem *lpeitem, bool keep_paths)
536 {
537 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
538 if (!lperef)
539 return;
541 PathEffectList new_list = *lpeitem->path_effect_list;
542 new_list.remove(lperef); //current lpe ref is always our 'own' pointer from the path_effect_list
543 std::string r = patheffectlist_write_svg(new_list);
545 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
547 if (!keep_paths) {
548 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
549 }
550 }
552 void sp_lpe_item_remove_all_path_effects(SPLPEItem *lpeitem, bool keep_paths)
553 {
554 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
556 if (!keep_paths) {
557 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
558 }
559 }
561 void sp_lpe_item_down_current_path_effect(SPLPEItem *lpeitem)
562 {
563 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
564 if (!lperef)
565 return;
567 PathEffectList new_list = *lpeitem->path_effect_list;
568 PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
569 if (cur_it != new_list.end()) {
570 PathEffectList::iterator down_it = cur_it;
571 down_it++;
572 if (down_it != new_list.end()) { // perhaps current effect is already last effect
573 std::iter_swap(cur_it, down_it);
574 }
575 }
576 std::string r = patheffectlist_write_svg(new_list);
577 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
579 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
580 }
582 void sp_lpe_item_up_current_path_effect(SPLPEItem *lpeitem)
583 {
584 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
585 if (!lperef)
586 return;
588 PathEffectList new_list = *lpeitem->path_effect_list;
589 PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
590 if (cur_it != new_list.end() && cur_it != new_list.begin()) {
591 PathEffectList::iterator up_it = cur_it;
592 up_it--;
593 std::iter_swap(cur_it, up_it);
594 }
595 std::string r = patheffectlist_write_svg(new_list);
597 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
599 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
600 }
603 bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem)
604 {
605 return !lpeitem->path_effect_list->empty();
606 }
608 bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem)
609 {
610 SPObject *parent = lpeitem->parent;
611 if (parent && SP_IS_LPE_ITEM(parent)) {
612 return sp_lpe_item_has_path_effect(lpeitem) || sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent));
613 }
614 else {
615 return sp_lpe_item_has_path_effect(lpeitem);
616 }
617 }
619 Inkscape::LivePathEffect::Effect*
620 sp_lpe_item_has_path_effect_of_type(SPLPEItem *lpeitem, int type)
621 {
622 std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
623 for (i = lpeitem->path_effect_list->begin(); i != lpeitem->path_effect_list->end(); ++i) {
624 Inkscape::LivePathEffect::Effect* lpe = (*i)->lpeobject->get_lpe();
625 if (lpe && (lpe->effectType() == type)) {
626 return lpe;
627 }
628 }
629 return NULL;
630 }
632 /* Return false if the item is not a path or already has a shape applied */
633 bool sp_lpe_item_can_accept_freehand_shape(SPLPEItem *lpeitem)
634 {
635 if (!SP_IS_PATH(lpeitem))
636 return false;
638 if (sp_lpe_item_has_path_effect_of_type(lpeitem, Inkscape::LivePathEffect::FREEHAND_SHAPE))
639 return false;
641 return true;
642 }
644 void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt)
645 {
646 Inkscape::LivePathEffect::LPEObjectReference *lperef = sp_lpe_item_get_current_lpereference(lpeitem);
647 if (lperef && lperef->lpeobject && lperef->lpeobject->get_lpe()) {
648 lperef->lpeobject->get_lpe()->editNextParamOncanvas(SP_ITEM(lpeitem), dt);
649 }
650 }
652 static void
653 sp_lpe_item_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
654 {
655 if (((SPObjectClass *) (parent_class))->child_added)
656 (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref);
658 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
659 SPObject *ochild = sp_object_get_child_by_repr(object, child);
660 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
661 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(ochild));
662 }
663 }
664 }
666 static void
667 sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child)
668 {
669 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
670 SPObject *ochild = sp_object_get_child_by_repr(object, child);
671 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
672 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(ochild));
673 }
674 }
676 if (((SPObjectClass *) (parent_class))->remove_child)
677 (* ((SPObjectClass *) (parent_class))->remove_child) (object, child);
678 }
680 static std::string patheffectlist_write_svg(PathEffectList const & list)
681 {
682 HRefList hreflist;
683 for (PathEffectList::const_iterator it = list.begin(); it != list.end(); ++it)
684 {
685 hreflist.push_back( std::string((*it)->lpeobject_href) );
686 }
687 return hreflist_write_svg(hreflist);
688 }
690 /**
691 * THE function that should be used to generate any patheffectlist string.
692 * one of the methods to change the effect list:
693 * - create temporary href list
694 * - populate the templist with the effects from the old list that you want to have and their order
695 * - call this function with temp list as param
696 */
697 static std::string hreflist_write_svg(HRefList const & list)
698 {
699 std::string r;
700 bool semicolon_first = false;
701 for (HRefList::const_iterator it = list.begin(); it != list.end(); ++it)
702 {
703 if (semicolon_first) {
704 r += ';';
705 }
706 semicolon_first = true;
708 r += (*it);
709 }
710 return r;
711 }
713 // Return a copy of the effect list
714 PathEffectList sp_lpe_item_get_effect_list(SPLPEItem *lpeitem)
715 {
716 return *lpeitem->path_effect_list;
717 }
719 Inkscape::LivePathEffect::LPEObjectReference* sp_lpe_item_get_current_lpereference(SPLPEItem *lpeitem)
720 {
721 if (!lpeitem->current_path_effect && !lpeitem->path_effect_list->empty())
722 sp_lpe_item_set_current_path_effect(lpeitem, lpeitem->path_effect_list->back());
724 return lpeitem->current_path_effect;
725 }
727 Inkscape::LivePathEffect::Effect* sp_lpe_item_get_current_lpe(SPLPEItem *lpeitem)
728 {
729 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
731 if (lperef && lperef->lpeobject)
732 return lperef->lpeobject->get_lpe();
733 else
734 return NULL;
735 }
737 bool sp_lpe_item_set_current_path_effect(SPLPEItem *lpeitem, Inkscape::LivePathEffect::LPEObjectReference* lperef)
738 {
739 for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); it++) {
740 if ((*it)->lpeobject_repr == lperef->lpeobject_repr) {
741 lpeobject_ref_changed(NULL, (*it)->lpeobject, SP_LPE_ITEM(lpeitem)); // FIXME: explain why this is here?
742 lpeitem->current_path_effect = (*it); // current_path_effect should always be a pointer from the path_effect_list !
743 return true;
744 }
745 }
747 return false;
748 }
750 void sp_lpe_item_replace_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * old_lpeobj,
751 LivePathEffectObject * new_lpeobj)
752 {
753 HRefList hreflist;
754 for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
755 {
756 if ((*it)->lpeobject == old_lpeobj) {
757 const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
758 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
759 hreflist.push_back( std::string(hrefstr) );
760 g_free(hrefstr);
761 }
762 else {
763 hreflist.push_back( std::string((*it)->lpeobject_href) );
764 }
765 }
766 std::string r = hreflist_write_svg(hreflist);
767 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
768 }
770 // Enable or disable the path effects of the item.
771 // The counter allows nested calls
772 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable)
773 {
774 if (enable) {
775 lpeitem->path_effects_enabled++;
776 }
777 else {
778 lpeitem->path_effects_enabled--;
779 }
780 }
782 // Are the path effects enabled on this item ?
783 bool sp_lpe_item_path_effects_enabled(SPLPEItem *lpeitem)
784 {
785 return lpeitem->path_effects_enabled > 0;
786 }
788 /*
789 Local Variables:
790 mode:c++
791 c-file-style:"stroustrup"
792 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
793 indent-tabs-mode:nil
794 fill-column:99
795 End:
796 */
797 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :