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 ShapeEditor *sh = ec->shape_editor;
256 g_assert(sh);
257 if (!sh->has_nodepath()) return;
259 Inkscape::NodePath::Path *np = sh->get_nodepath();
260 sp_nodepath_update_helperpaths(np);
261 }
263 /**
264 * Sets modified flag for all sub-item views.
265 */
266 static void
267 sp_lpe_item_modified (SPObject *object, unsigned int flags)
268 {
269 if (SP_IS_GROUP(object) && (flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_USER_MODIFIED_FLAG_B)) {
270 sp_lpe_item_update_patheffect(SP_LPE_ITEM(object), true, true);
271 }
273 if (((SPObjectClass *) (parent_class))->modified) {
274 (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
275 }
276 }
278 /**
279 * Writes its settings to an incoming repr object, if any.
280 */
281 static Inkscape::XML::Node *
282 sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
283 {
284 SPLPEItem *lpeitem = (SPLPEItem *) object;
286 if ( sp_lpe_item_has_path_effect(lpeitem) ) {
287 std::string href = patheffectlist_write_svg(*lpeitem->path_effect_list);
288 repr->setAttribute("inkscape:path-effect", href.c_str());
289 } else {
290 repr->setAttribute("inkscape:path-effect", NULL);
291 }
293 if (((SPObjectClass *)(parent_class))->write) {
294 ((SPObjectClass *)(parent_class))->write(object, xml_doc, repr, flags);
295 }
297 return repr;
298 }
300 /**
301 * returns true when LPE was successful.
302 */
303 bool sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve) {
304 if (!lpeitem) return false;
305 if (!curve) return false;
307 if (sp_lpe_item_has_path_effect(lpeitem) && sp_lpe_item_path_effects_enabled(lpeitem)) {
308 for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
309 {
310 LivePathEffectObject *lpeobj = (*it)->lpeobject;
311 if (!lpeobj) {
312 /** \todo Investigate the cause of this.
313 * 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.
314 */
315 g_warning("sp_lpe_item_perform_path_effect - NULL lpeobj in list!");
316 return false;
317 }
318 Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
319 if (!lpe) {
320 /** \todo Investigate the cause of this.
321 * Not sure, but I think this can happen when an unknown effect type is specified...
322 */
323 g_warning("sp_lpe_item_perform_path_effect - lpeobj with invalid lpe in the stack!");
324 return false;
325 }
327 if (lpe->isVisible()) {
328 if (lpe->acceptsNumClicks() > 0 && !lpe->isReady()) {
329 // if the effect expects mouse input before being applied and the input is not finished
330 // yet, we don't alter the path
331 return false;
332 }
334 // Groups have their doBeforeEffect called elsewhere
335 if (!SP_IS_GROUP(lpeitem)) {
336 lpe->doBeforeEffect(lpeitem);
337 }
339 try {
340 lpe->doEffect(curve);
341 }
342 catch (std::exception & e) {
343 g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what());
344 if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->messageStack()) {
345 SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
346 _("An exception occurred during execution of the Path Effect.") );
347 }
348 return false;
349 }
350 }
351 }
352 }
354 return true;
355 }
357 /**
358 * Calls any registered handlers for the update_patheffect action
359 */
360 void
361 sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write)
362 {
363 #ifdef SHAPE_VERBOSE
364 g_message("sp_lpe_item_update_patheffect: %p\n", lpeitem);
365 #endif
366 g_return_if_fail (lpeitem != NULL);
367 g_return_if_fail (SP_IS_LPE_ITEM (lpeitem));
369 if (!sp_lpe_item_path_effects_enabled(lpeitem))
370 return;
372 // TODO: hack! this will be removed when path length measuring is reimplemented in a better way
373 PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
374 std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
375 for (i = lpelist.begin(); i != lpelist.end(); ++i) {
376 if ((*i)->lpeobject) {
377 Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe();
378 if (dynamic_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)) {
379 if (!lpe->isVisible()) {
380 // we manually disable text for LPEPathLength
381 dynamic_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)->hideCanvasText();
382 }
383 }
384 }
385 }
387 SPLPEItem *top;
389 if (wholetree) {
390 SPObject *prev_parent = lpeitem;
391 SPObject *parent = prev_parent->parent;
392 while (parent && SP_IS_LPE_ITEM(parent) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent))) {
393 prev_parent = parent;
394 parent = prev_parent->parent;
395 }
396 top = SP_LPE_ITEM(prev_parent);
397 }
398 else {
399 top = lpeitem;
400 }
402 if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect) {
403 SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect (top, write);
404 }
405 }
407 /**
408 * Gets called when (re)attached to another lpeobject.
409 */
410 static void
411 lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem)
412 {
413 if (old_ref) {
414 sp_signal_disconnect_by_data(old_ref, lpeitem);
415 }
416 if ( IS_LIVEPATHEFFECT(ref) && ref != lpeitem )
417 {
418 lpeitem->lpe_modified_connection.disconnect();
419 lpeitem->lpe_modified_connection = ref->connectModified(sigc::bind(sigc::ptr_fun(&lpeobject_ref_modified), lpeitem));
420 lpeobject_ref_modified(ref, 0, lpeitem);
421 }
422 }
424 /**
425 * Gets called when lpeobject repr contents change: i.e. parameter change.
426 */
427 static void
428 lpeobject_ref_modified(SPObject */*href*/, guint /*flags*/, SPLPEItem *lpeitem)
429 {
430 sp_lpe_item_update_patheffect (lpeitem, true, true);
431 }
433 static void
434 sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem)
435 {
436 if (SP_IS_GROUP(lpeitem)) {
437 GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
438 for ( GSList const *iter = item_list; iter; iter = iter->next ) {
439 SPObject *subitem = static_cast<SPObject *>(iter->data);
440 if (SP_IS_LPE_ITEM(subitem)) {
441 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(subitem));
442 }
443 }
444 }
445 else if (SP_IS_PATH(lpeitem)) {
446 Inkscape::XML::Node *pathrepr = SP_OBJECT_REPR(lpeitem);
447 if ( !pathrepr->attribute("inkscape:original-d") ) {
448 pathrepr->setAttribute("inkscape:original-d", pathrepr->attribute("d"));
449 }
450 }
451 }
453 static void
454 sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem)
455 {
456 if (SP_IS_GROUP(lpeitem)) {
457 GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
458 for ( GSList const *iter = item_list; iter; iter = iter->next ) {
459 SPObject *subitem = static_cast<SPObject *>(iter->data);
460 if (SP_IS_LPE_ITEM(subitem)) {
461 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(subitem));
462 }
463 }
464 }
465 else if (SP_IS_PATH(lpeitem)) {
466 Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem);
467 if (!sp_lpe_item_has_path_effect_recursive(lpeitem)
468 && repr->attribute("inkscape:original-d")) {
469 repr->setAttribute("d", repr->attribute("inkscape:original-d"));
470 repr->setAttribute("inkscape:original-d", NULL);
471 }
472 else {
473 sp_lpe_item_update_patheffect(lpeitem, true, true);
474 }
475 }
476 }
478 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset)
479 {
480 if (value) {
481 // Apply the path effects here because in the casse of a group, lpe->resetDefaults
482 // needs that all the subitems have their effects applied
483 sp_lpe_item_update_patheffect(lpeitem, false, true);
485 // Disable the path effects while preparing the new lpe
486 sp_lpe_item_enable_path_effects(lpeitem, false);
488 // Add the new reference to the list of LPE references
489 HRefList hreflist;
490 for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
491 {
492 hreflist.push_back( std::string((*it)->lpeobject_href) );
493 }
494 hreflist.push_back( std::string(value) );
495 std::string hrefs = hreflist_write_svg(hreflist);
497 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", hrefs.c_str());
499 // make sure there is an original-d for paths!!!
500 sp_lpe_item_create_original_path_recursive(lpeitem);
502 LivePathEffectObject *lpeobj = lpeitem->path_effect_list->back()->lpeobject;
503 if (lpeobj && lpeobj->get_lpe()) {
504 Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
505 // Ask the path effect to reset itself if it doesn't have parameters yet
506 if (reset) {
507 // has to be called when all the subitems have their lpes applied
508 lpe->resetDefaults(lpeitem);
509 }
511 // perform this once when the effect is applied
512 lpe->doOnApply(SP_LPE_ITEM(lpeitem));
514 // indicate that all necessary preparations are done and the effect can be performed
515 lpe->setReady();
516 }
518 //Enable the path effects now that everything is ready to apply the new path effect
519 sp_lpe_item_enable_path_effects(lpeitem, true);
521 // Apply the path effect
522 sp_lpe_item_update_patheffect(lpeitem, true, true);
523 }
524 }
526 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj)
527 {
528 const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
529 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
530 sp_lpe_item_add_path_effect(lpeitem, hrefstr, false);
531 g_free(hrefstr);
532 }
534 void sp_lpe_item_remove_current_path_effect(SPLPEItem *lpeitem, bool keep_paths)
535 {
536 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
537 if (!lperef)
538 return;
540 PathEffectList new_list = *lpeitem->path_effect_list;
541 new_list.remove(lperef); //current lpe ref is always our 'own' pointer from the path_effect_list
542 std::string r = patheffectlist_write_svg(new_list);
544 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
546 if (!keep_paths) {
547 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
548 }
549 }
551 void sp_lpe_item_remove_all_path_effects(SPLPEItem *lpeitem, bool keep_paths)
552 {
553 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
555 if (!keep_paths) {
556 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
557 }
558 }
560 void sp_lpe_item_down_current_path_effect(SPLPEItem *lpeitem)
561 {
562 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
563 if (!lperef)
564 return;
566 PathEffectList new_list = *lpeitem->path_effect_list;
567 PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
568 if (cur_it != new_list.end()) {
569 PathEffectList::iterator down_it = cur_it;
570 down_it++;
571 if (down_it != new_list.end()) { // perhaps current effect is already last effect
572 std::iter_swap(cur_it, down_it);
573 }
574 }
575 std::string r = patheffectlist_write_svg(new_list);
576 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
578 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
579 }
581 void sp_lpe_item_up_current_path_effect(SPLPEItem *lpeitem)
582 {
583 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
584 if (!lperef)
585 return;
587 PathEffectList new_list = *lpeitem->path_effect_list;
588 PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
589 if (cur_it != new_list.end() && cur_it != new_list.begin()) {
590 PathEffectList::iterator up_it = cur_it;
591 up_it--;
592 std::iter_swap(cur_it, up_it);
593 }
594 std::string r = patheffectlist_write_svg(new_list);
596 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
598 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
599 }
602 bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem)
603 {
604 return !lpeitem->path_effect_list->empty();
605 }
607 bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem)
608 {
609 SPObject *parent = lpeitem->parent;
610 if (parent && SP_IS_LPE_ITEM(parent)) {
611 return sp_lpe_item_has_path_effect(lpeitem) || sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent));
612 }
613 else {
614 return sp_lpe_item_has_path_effect(lpeitem);
615 }
616 }
618 Inkscape::LivePathEffect::Effect*
619 sp_lpe_item_has_path_effect_of_type(SPLPEItem *lpeitem, int type)
620 {
621 std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
622 for (i = lpeitem->path_effect_list->begin(); i != lpeitem->path_effect_list->end(); ++i) {
623 Inkscape::LivePathEffect::Effect* lpe = (*i)->lpeobject->get_lpe();
624 if (lpe && (lpe->effectType() == type)) {
625 return lpe;
626 }
627 }
628 return NULL;
629 }
631 /* Return false if the item is not a path or already has a shape applied */
632 bool sp_lpe_item_can_accept_freehand_shape(SPLPEItem *lpeitem)
633 {
634 if (!SP_IS_PATH(lpeitem))
635 return false;
637 if (sp_lpe_item_has_path_effect_of_type(lpeitem, Inkscape::LivePathEffect::FREEHAND_SHAPE))
638 return false;
640 return true;
641 }
643 void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt)
644 {
645 Inkscape::LivePathEffect::LPEObjectReference *lperef = sp_lpe_item_get_current_lpereference(lpeitem);
646 if (lperef && lperef->lpeobject && lperef->lpeobject->get_lpe()) {
647 lperef->lpeobject->get_lpe()->editNextParamOncanvas(SP_ITEM(lpeitem), dt);
648 }
649 }
651 static void
652 sp_lpe_item_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
653 {
654 if (((SPObjectClass *) (parent_class))->child_added)
655 (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref);
657 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
658 SPObject *ochild = sp_object_get_child_by_repr(object, child);
659 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
660 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(ochild));
661 }
662 }
663 }
665 static void
666 sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child)
667 {
668 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
669 SPObject *ochild = sp_object_get_child_by_repr(object, child);
670 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
671 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(ochild));
672 }
673 }
675 if (((SPObjectClass *) (parent_class))->remove_child)
676 (* ((SPObjectClass *) (parent_class))->remove_child) (object, child);
677 }
679 static std::string patheffectlist_write_svg(PathEffectList const & list)
680 {
681 HRefList hreflist;
682 for (PathEffectList::const_iterator it = list.begin(); it != list.end(); ++it)
683 {
684 hreflist.push_back( std::string((*it)->lpeobject_href) );
685 }
686 return hreflist_write_svg(hreflist);
687 }
689 /**
690 * THE function that should be used to generate any patheffectlist string.
691 * one of the methods to change the effect list:
692 * - create temporary href list
693 * - populate the templist with the effects from the old list that you want to have and their order
694 * - call this function with temp list as param
695 */
696 static std::string hreflist_write_svg(HRefList const & list)
697 {
698 std::string r;
699 bool semicolon_first = false;
700 for (HRefList::const_iterator it = list.begin(); it != list.end(); ++it)
701 {
702 if (semicolon_first) {
703 r += ';';
704 }
705 semicolon_first = true;
707 r += (*it);
708 }
709 return r;
710 }
712 // Return a copy of the effect list
713 PathEffectList sp_lpe_item_get_effect_list(SPLPEItem *lpeitem)
714 {
715 return *lpeitem->path_effect_list;
716 }
718 Inkscape::LivePathEffect::LPEObjectReference* sp_lpe_item_get_current_lpereference(SPLPEItem *lpeitem)
719 {
720 if (!lpeitem->current_path_effect && !lpeitem->path_effect_list->empty())
721 sp_lpe_item_set_current_path_effect(lpeitem, lpeitem->path_effect_list->back());
723 return lpeitem->current_path_effect;
724 }
726 Inkscape::LivePathEffect::Effect* sp_lpe_item_get_current_lpe(SPLPEItem *lpeitem)
727 {
728 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
730 if (lperef && lperef->lpeobject)
731 return lperef->lpeobject->get_lpe();
732 else
733 return NULL;
734 }
736 bool sp_lpe_item_set_current_path_effect(SPLPEItem *lpeitem, Inkscape::LivePathEffect::LPEObjectReference* lperef)
737 {
738 for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); it++) {
739 if ((*it)->lpeobject_repr == lperef->lpeobject_repr) {
740 lpeobject_ref_changed(NULL, (*it)->lpeobject, SP_LPE_ITEM(lpeitem)); // FIXME: explain why this is here?
741 lpeitem->current_path_effect = (*it); // current_path_effect should always be a pointer from the path_effect_list !
742 return true;
743 }
744 }
746 return false;
747 }
749 void sp_lpe_item_replace_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * old_lpeobj,
750 LivePathEffectObject * new_lpeobj)
751 {
752 HRefList hreflist;
753 for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
754 {
755 if ((*it)->lpeobject == old_lpeobj) {
756 const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
757 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
758 hreflist.push_back( std::string(hrefstr) );
759 g_free(hrefstr);
760 }
761 else {
762 hreflist.push_back( std::string((*it)->lpeobject_href) );
763 }
764 }
765 std::string r = hreflist_write_svg(hreflist);
766 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
767 }
769 // Enable or disable the path effects of the item.
770 // The counter allows nested calls
771 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable)
772 {
773 if (enable) {
774 lpeitem->path_effects_enabled++;
775 }
776 else {
777 lpeitem->path_effects_enabled--;
778 }
779 }
781 // Are the path effects enabled on this item ?
782 bool sp_lpe_item_path_effects_enabled(SPLPEItem *lpeitem)
783 {
784 return lpeitem->path_effects_enabled > 0;
785 }
787 /*
788 Local Variables:
789 mode:c++
790 c-file-style:"stroustrup"
791 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
792 indent-tabs-mode:nil
793 fill-column:99
794 End:
795 */
796 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :