1 #define __SP_LPE_ITEM_CPP__
3 /** \file
4 * Base class for live path effect items
5 */
6 /*
7 * Authors:
8 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
9 * Bastien Bouclet <bgkweb@gmail.com>
10 *
11 * Copyright (C) 2008 authors
12 *
13 * Released under GNU GPL, read the file 'COPYING' for more information
14 */
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
20 #include "live_effects/effect.h"
21 #include "live_effects/lpeobject.h"
22 #include "live_effects/lpeobject-reference.h"
24 #include "sp-path.h"
25 #include "sp-item-group.h"
26 #include "streq.h"
27 #include "macros.h"
28 #include "attributes.h"
29 #include "sp-lpe-item.h"
30 #include "xml/repr.h"
31 #include "uri.h"
32 #include "message-stack.h"
33 #include "inkscape.h"
34 #include "desktop.h"
36 #include <algorithm>
38 /* LPEItem base class */
40 static void sp_lpe_item_class_init(SPLPEItemClass *klass);
41 static void sp_lpe_item_init(SPLPEItem *lpe_item);
42 static void sp_lpe_item_finalize(GObject *object);
44 static void sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
45 static void sp_lpe_item_release(SPObject *object);
46 static void sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value);
47 static void sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags);
48 static void sp_lpe_item_modified (SPObject *object, unsigned int flags);
49 static Inkscape::XML::Node *sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags);
51 static void sp_lpe_item_child_added (SPObject * object, Inkscape::XML::Node * child, Inkscape::XML::Node * ref);
52 static void sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child);
54 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable);
56 static void lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem);
57 static void lpeobject_ref_modified(SPObject *href, guint flags, SPLPEItem *lpeitem);
59 static void sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem);
60 static void sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem);
61 typedef std::list<std::string> HRefList;
62 static std::string patheffectlist_write_svg(PathEffectList const & list);
63 static std::string hreflist_write_svg(HRefList const & list);
65 static SPItemClass *parent_class;
67 GType
68 sp_lpe_item_get_type()
69 {
70 static GType lpe_item_type = 0;
72 if (!lpe_item_type) {
73 GTypeInfo lpe_item_info = {
74 sizeof(SPLPEItemClass),
75 NULL, NULL,
76 (GClassInitFunc) sp_lpe_item_class_init,
77 NULL, NULL,
78 sizeof(SPLPEItem),
79 16,
80 (GInstanceInitFunc) sp_lpe_item_init,
81 NULL, /* value_table */
82 };
83 lpe_item_type = g_type_register_static(SP_TYPE_ITEM, "SPLPEItem", &lpe_item_info, (GTypeFlags)0);
84 }
85 return lpe_item_type;
86 }
88 static void
89 sp_lpe_item_class_init(SPLPEItemClass *klass)
90 {
91 GObjectClass *gobject_class;
92 SPObjectClass *sp_object_class;
94 gobject_class = (GObjectClass *) klass;
95 sp_object_class = (SPObjectClass *) klass;
96 parent_class = (SPItemClass *)g_type_class_peek_parent (klass);
98 gobject_class->finalize = sp_lpe_item_finalize;
100 sp_object_class->build = sp_lpe_item_build;
101 sp_object_class->release = sp_lpe_item_release;
102 sp_object_class->set = sp_lpe_item_set;
103 sp_object_class->update = sp_lpe_item_update;
104 sp_object_class->modified = sp_lpe_item_modified;
105 sp_object_class->write = sp_lpe_item_write;
106 sp_object_class->child_added = sp_lpe_item_child_added;
107 sp_object_class->remove_child = sp_lpe_item_remove_child;
109 klass->update_patheffect = NULL;
110 }
112 static void
113 sp_lpe_item_init(SPLPEItem *lpeitem)
114 {
115 lpeitem->path_effects_enabled = 1;
117 lpeitem->path_effect_list = new PathEffectList();
118 lpeitem->current_path_effect = NULL;
120 new (&lpeitem->lpe_modified_connection) sigc::connection();
122 lpeitem->adding_helperpaths = false;
123 lpeitem->removing_helperpaths = false;
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;
206 path_effect_ref = new Inkscape::LivePathEffect::LPEObjectReference(SP_OBJECT(lpeitem));
207 path_effect_ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(lpeobject_ref_changed), SP_LPE_ITEM(object)));
208 // Now do the attaching, which emits the changed signal.
209 // Fixme, it should not do this changed signal and updating before all effects are added to the path_effect_list
210 try {
211 path_effect_ref->link(href.c_str());
212 } catch (Inkscape::BadURIException &e) {
213 g_warning("BadURIException: %s", e.what());
214 path_effect_ref->unlink();
215 delete path_effect_ref;
216 path_effect_ref = NULL;
217 }
219 if (path_effect_ref) {
220 lpeitem->path_effect_list->push_back(path_effect_ref);
221 }
222 }
223 }
225 sp_lpe_item_enable_path_effects(lpeitem, true);
226 }
227 break;
228 default:
229 if (((SPObjectClass *) parent_class)->set) {
230 ((SPObjectClass *) parent_class)->set(object, key, value);
231 }
232 break;
233 }
234 }
236 /**
237 * Receives update notifications.
238 */
239 static void
240 sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags)
241 {
242 if (((SPObjectClass *) parent_class)->update) {
243 ((SPObjectClass *) parent_class)->update(object, ctx, flags);
244 }
245 }
247 /**
248 * Sets modified flag for all sub-item views.
249 */
250 static void
251 sp_lpe_item_modified (SPObject *object, unsigned int flags)
252 {
254 if (SP_IS_GROUP(object) && (flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_USER_MODIFIED_FLAG_B)) {
255 sp_lpe_item_update_patheffect(SP_LPE_ITEM(object), true, true);
256 }
258 if (((SPObjectClass *) (parent_class))->modified) {
259 (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
260 }
261 }
263 /**
264 * Writes its settings to an incoming repr object, if any.
265 */
266 static Inkscape::XML::Node *
267 sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
268 {
269 SPLPEItem *lpeitem = (SPLPEItem *) object;
271 if ( sp_lpe_item_has_path_effect(lpeitem) ) {
272 std::string href = patheffectlist_write_svg(*lpeitem->path_effect_list);
273 repr->setAttribute("inkscape:path-effect", href.c_str());
274 } else {
275 repr->setAttribute("inkscape:path-effect", NULL);
276 }
278 if (((SPObjectClass *)(parent_class))->write) {
279 ((SPObjectClass *)(parent_class))->write(object, xml_doc, repr, flags);
280 }
282 return repr;
283 }
285 void sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve) {
286 if (!lpeitem) return;
287 if (!curve) return;
289 if (sp_lpe_item_has_path_effect(lpeitem) && sp_lpe_item_path_effects_enabled(lpeitem)) {
290 for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
291 {
292 LivePathEffectObject *lpeobj = (*it)->lpeobject;
293 if (!lpeobj) {
294 g_warning("sp_lpe_item_perform_path_effect - NULL lpeobj in list!");
295 return;
296 }
297 if (!lpeobj->lpe) {
298 g_warning("sp_lpe_item_perform_path_effect - lpeobj without lpe!");
299 return;
300 }
302 Inkscape::LivePathEffect::Effect *lpe = lpeobj->lpe;
303 if (lpe->isVisible()) {
304 if (lpe->acceptsNumParams() > 0 && !lpe->pathParamAccepted()) {
305 // if the effect expects mouse input before being applied and the input is not finished
306 // yet, we don't alter the path
307 return;
308 }
310 // Groups have their doBeforeEffect called elsewhere
311 if (!SP_IS_GROUP(lpeitem)) {
312 lpe->doBeforeEffect(lpeitem);
313 }
315 try {
316 lpe->doEffect(curve);
317 }
318 catch (std::exception & e) {
319 g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what());
320 SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
321 _("An exception occurred during execution of the Path Effect.") );
322 }
323 }
324 }
325 }
326 }
328 /**
329 * Calls any registered handlers for the update_patheffect action
330 */
331 void
332 sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write)
333 {
334 #ifdef SHAPE_VERBOSE
335 g_message("sp_lpe_item_update_patheffect: %p\n", lpeitem);
336 #endif
337 g_return_if_fail (lpeitem != NULL);
338 g_return_if_fail (SP_IS_LPE_ITEM (lpeitem));
340 if (!sp_lpe_item_path_effects_enabled(lpeitem))
341 return;
343 SPLPEItem *top;
345 if (wholetree) {
346 SPObject *prev_parent = lpeitem;
347 SPObject *parent = prev_parent->parent;
348 while (parent && SP_IS_LPE_ITEM(parent) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent))) {
349 prev_parent = parent;
350 parent = prev_parent->parent;
351 }
352 top = SP_LPE_ITEM(prev_parent);
353 }
354 else {
355 top = lpeitem;
356 }
358 // TODO: ditch inkscape_active_desktop()
359 SPDesktop *desktop = inkscape_active_desktop();
360 if (desktop) {
361 //sp_lpe_item_remove_temporary_canvasitems(lpeitem, desktop);
362 //sp_lpe_item_add_temporary_canvasitems(lpeitem, desktop);
363 }
365 if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect) {
366 SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect (top, write);
367 }
368 }
370 /**
371 * Gets called when (re)attached to another lpeobject.
372 */
373 static void
374 lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem)
375 {
376 if (old_ref) {
377 sp_signal_disconnect_by_data(old_ref, lpeitem);
378 }
379 if ( IS_LIVEPATHEFFECT(ref) && ref != lpeitem )
380 {
381 lpeitem->lpe_modified_connection.disconnect();
382 lpeitem->lpe_modified_connection = ref->connectModified(sigc::bind(sigc::ptr_fun(&lpeobject_ref_modified), lpeitem));
383 lpeobject_ref_modified(ref, 0, lpeitem);
384 }
385 }
387 /**
388 * Gets called when lpeobject repr contents change: i.e. parameter change.
389 */
390 static void
391 lpeobject_ref_modified(SPObject */*href*/, guint /*flags*/, SPLPEItem *lpeitem)
392 {
393 sp_lpe_item_update_patheffect (lpeitem, true, true);
394 }
396 static void
397 sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem)
398 {
399 if (SP_IS_GROUP(lpeitem)) {
400 GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
401 for ( GSList const *iter = item_list; iter; iter = iter->next ) {
402 SPObject *subitem = static_cast<SPObject *>(iter->data);
403 if (SP_IS_LPE_ITEM(subitem)) {
404 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(subitem));
405 }
406 }
407 }
408 else if (SP_IS_PATH(lpeitem)) {
409 Inkscape::XML::Node *pathrepr = SP_OBJECT_REPR(lpeitem);
410 if ( !pathrepr->attribute("inkscape:original-d") ) {
411 pathrepr->setAttribute("inkscape:original-d", pathrepr->attribute("d"));
412 }
413 }
414 }
416 static void
417 sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem)
418 {
419 if (SP_IS_GROUP(lpeitem)) {
420 GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
421 for ( GSList const *iter = item_list; iter; iter = iter->next ) {
422 SPObject *subitem = static_cast<SPObject *>(iter->data);
423 if (SP_IS_LPE_ITEM(subitem)) {
424 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(subitem));
425 }
426 }
427 }
428 else if (SP_IS_PATH(lpeitem)) {
429 Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem);
430 if (!sp_lpe_item_has_path_effect_recursive(lpeitem)
431 && repr->attribute("inkscape:original-d")) {
432 repr->setAttribute("d", repr->attribute("inkscape:original-d"));
433 repr->setAttribute("inkscape:original-d", NULL);
434 }
435 else {
436 sp_lpe_item_update_patheffect(lpeitem, true, true);
437 }
438 }
439 }
441 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset)
442 {
443 if (value) {
444 // Apply the path effects here because in the casse of a group, lpe->resetDefaults
445 // needs that all the subitems have their effects applied
446 sp_lpe_item_update_patheffect(lpeitem, false, true);
448 // Disable the path effects while preparing the new lpe
449 sp_lpe_item_enable_path_effects(lpeitem, false);
451 // Add the new reference to the list of LPE references
452 HRefList hreflist;
453 for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
454 {
455 hreflist.push_back( std::string((*it)->lpeobject_href) );
456 }
457 hreflist.push_back( std::string(value) );
458 std::string hrefs = hreflist_write_svg(hreflist);
460 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", hrefs.c_str());
462 // make sure there is an original-d for paths!!!
463 sp_lpe_item_create_original_path_recursive(lpeitem);
465 LivePathEffectObject *lpeobj = lpeitem->path_effect_list->back()->lpeobject;
466 if (lpeobj && lpeobj->lpe) {
467 Inkscape::LivePathEffect::Effect *lpe = lpeobj->lpe;
468 // Ask the path effect to reset itself if it doesn't have parameters yet
469 if (reset) {
470 // has to be called when all the subitems have their lpes applied
471 lpe->resetDefaults(lpeitem);
472 }
474 // perform this once when the effect is applied
475 lpe->doOnApply(SP_LPE_ITEM(lpeitem));
477 // if the effect expects a number of mouse clicks to set a parameter path, perform the
478 // necessary preparations
479 if (lpe->acceptsNumParams() > 0) {
480 lpe->doAcceptPathPreparations(lpeitem);
481 }
482 }
484 //Enable the path effects now that everything is ready to apply the new path effect
485 sp_lpe_item_enable_path_effects(lpeitem, true);
487 // Apply the path effect
488 sp_lpe_item_update_patheffect(lpeitem, true, true);
489 }
490 }
492 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj)
493 {
494 const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
495 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
496 sp_lpe_item_add_path_effect(lpeitem, hrefstr, false);
497 g_free(hrefstr);
498 }
500 void sp_lpe_item_remove_current_path_effect(SPLPEItem *lpeitem, bool keep_paths)
501 {
502 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
503 if (!lperef)
504 return;
506 PathEffectList new_list = *lpeitem->path_effect_list;
507 new_list.remove(lperef); //current lpe ref is always our 'own' pointer from the path_effect_list
508 std::string r = patheffectlist_write_svg(new_list);
510 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
512 if (!keep_paths) {
513 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
514 }
515 }
517 void sp_lpe_item_remove_all_path_effects(SPLPEItem *lpeitem, bool keep_paths)
518 {
519 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
521 if (!keep_paths) {
522 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
523 }
524 }
526 void sp_lpe_item_down_current_path_effect(SPLPEItem *lpeitem)
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 PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
534 if (cur_it != new_list.end()) {
535 PathEffectList::iterator down_it = cur_it;
536 down_it++;
537 if (down_it != new_list.end()) { // perhaps current effect is already last effect
538 std::iter_swap(cur_it, down_it);
539 }
540 }
541 std::string r = patheffectlist_write_svg(new_list);
542 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
544 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
545 }
547 void sp_lpe_item_up_current_path_effect(SPLPEItem *lpeitem)
548 {
549 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
550 if (!lperef)
551 return;
553 PathEffectList new_list = *lpeitem->path_effect_list;
554 PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
555 if (cur_it != new_list.end() && cur_it != new_list.begin()) {
556 PathEffectList::iterator up_it = cur_it;
557 up_it--;
558 std::iter_swap(cur_it, up_it);
559 }
560 std::string r = patheffectlist_write_svg(new_list);
562 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
564 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
565 }
568 bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem)
569 {
570 return !lpeitem->path_effect_list->empty();
571 }
573 bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem)
574 {
575 SPObject *parent = lpeitem->parent;
576 if (parent && SP_IS_LPE_ITEM(parent)) {
577 return sp_lpe_item_has_path_effect(lpeitem) || sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent));
578 }
579 else {
580 return sp_lpe_item_has_path_effect(lpeitem);
581 }
582 }
584 Inkscape::LivePathEffect::Effect*
585 sp_lpe_item_has_path_effect_of_type(SPLPEItem *lpeitem, int type)
586 {
587 std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
588 for (i = lpeitem->path_effect_list->begin(); i != lpeitem->path_effect_list->end(); ++i) {
589 if ((*i)->lpeobject->lpe->effectType() == type) {
590 return (*i)->lpeobject->lpe;
591 }
592 }
593 return NULL;
594 }
596 /* Return false if the item is not a path or already has a shape applied */
597 bool sp_lpe_item_can_accept_freehand_shape(SPLPEItem *lpeitem)
598 {
599 if (!SP_IS_PATH(lpeitem))
600 return false;
602 if (sp_lpe_item_has_path_effect_of_type(lpeitem, Inkscape::LivePathEffect::FREEHAND_SHAPE))
603 return false;
605 return true;
606 }
608 void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt)
609 {
610 Inkscape::LivePathEffect::LPEObjectReference *lperef = sp_lpe_item_get_current_lpereference(lpeitem);
611 if (lperef && lperef->lpeobject && lperef->lpeobject->lpe) {
612 lperef->lpeobject->lpe->editNextParamOncanvas(SP_ITEM(lpeitem), dt);
613 }
614 }
616 static void
617 sp_lpe_item_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
618 {
619 if (((SPObjectClass *) (parent_class))->child_added)
620 (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref);
622 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
623 SPObject *ochild = sp_object_get_child_by_repr(object, child);
624 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
625 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(ochild));
626 }
627 }
628 }
630 static void
631 sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child)
632 {
633 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
634 SPObject *ochild = sp_object_get_child_by_repr(object, child);
635 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
636 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(ochild));
637 }
638 }
640 if (((SPObjectClass *) (parent_class))->remove_child)
641 (* ((SPObjectClass *) (parent_class))->remove_child) (object, child);
642 }
644 static std::string patheffectlist_write_svg(PathEffectList const & list)
645 {
646 HRefList hreflist;
647 for (PathEffectList::const_iterator it = list.begin(); it != list.end(); ++it)
648 {
649 hreflist.push_back( std::string((*it)->lpeobject_href) );
650 }
651 return hreflist_write_svg(hreflist);
652 }
654 /**
655 * THE function that should be used to generate any patheffectlist string.
656 * one of the methods to change the effect list:
657 * - create temporary href list
658 * - populate the templist with the effects from the old list that you want to have and their order
659 * - call this function with temp list as param
660 */
661 static std::string hreflist_write_svg(HRefList const & list)
662 {
663 std::string r;
664 bool semicolon_first = false;
665 for (HRefList::const_iterator it = list.begin(); it != list.end(); ++it)
666 {
667 if (semicolon_first) {
668 r += ';';
669 }
670 semicolon_first = true;
672 r += (*it);
673 }
674 return r;
675 }
677 // Return a copy of the effect list
678 PathEffectList sp_lpe_item_get_effect_list(SPLPEItem *lpeitem)
679 {
680 return *lpeitem->path_effect_list;
681 }
683 Inkscape::LivePathEffect::LPEObjectReference* sp_lpe_item_get_current_lpereference(SPLPEItem *lpeitem)
684 {
685 if (!lpeitem->current_path_effect && !lpeitem->path_effect_list->empty())
686 sp_lpe_item_set_current_path_effect(lpeitem, lpeitem->path_effect_list->back());
688 return lpeitem->current_path_effect;
689 }
691 Inkscape::LivePathEffect::Effect* sp_lpe_item_get_current_lpe(SPLPEItem *lpeitem)
692 {
693 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
695 if (lperef && lperef->lpeobject)
696 return lperef->lpeobject->lpe;
697 else
698 return NULL;
699 }
701 bool sp_lpe_item_set_current_path_effect(SPLPEItem *lpeitem, Inkscape::LivePathEffect::LPEObjectReference* lperef)
702 {
703 for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); it++) {
704 if ((*it)->lpeobject_repr == lperef->lpeobject_repr) {
705 lpeobject_ref_changed(NULL, (*it)->lpeobject, SP_LPE_ITEM(lpeitem)); // FIXME: explain why this is here?
706 lpeitem->current_path_effect = (*it); // current_path_effect should always be a pointer from the path_effect_list !
707 return true;
708 }
709 }
711 return false;
712 }
714 void sp_lpe_item_replace_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * old_lpeobj,
715 LivePathEffectObject * new_lpeobj)
716 {
717 HRefList hreflist;
718 for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
719 {
720 if ((*it)->lpeobject == old_lpeobj) {
721 const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
722 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
723 hreflist.push_back( std::string(hrefstr) );
724 g_free(hrefstr);
725 }
726 else {
727 hreflist.push_back( std::string((*it)->lpeobject_href) );
728 }
729 }
730 std::string r = hreflist_write_svg(hreflist);
731 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
732 }
734 // Enable or disable the path effects of the item.
735 // The counter allows nested calls
736 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable)
737 {
738 if (enable) {
739 lpeitem->path_effects_enabled++;
740 }
741 else {
742 lpeitem->path_effects_enabled--;
743 }
744 }
746 // Are the path effects enabled on this item ?
747 bool sp_lpe_item_path_effects_enabled(SPLPEItem *lpeitem)
748 {
749 return lpeitem->path_effects_enabled > 0;
750 }
752 void
753 sp_lpe_item_add_temporary_canvasitems(SPLPEItem *lpeitem, SPDesktop *desktop)
754 {
755 if (lpeitem->adding_helperpaths) {
756 return;
757 }
758 lpeitem->adding_helperpaths = true;
759 // FIXME: for some reason it seems that we must create the variable lpe AFTER checking
760 // for adding_helperpaths == true; otherwise we get a crash on startup. why??
761 Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(lpeitem);
762 if (lpe) {
763 // TODO: can we just update the tempitem's SPCurve instead of recreating it each time?
764 lpe->addHelperPaths(lpeitem, desktop);
765 }
766 lpeitem->adding_helperpaths = false;
767 }
769 void
770 sp_lpe_item_remove_temporary_canvasitems(SPLPEItem *lpeitem, SPDesktop *desktop)
771 {
772 g_return_if_fail(lpeitem);
773 g_return_if_fail(desktop);
775 if (lpeitem->removing_helperpaths) {
776 return;
777 }
778 lpeitem->removing_helperpaths = true;
780 // destroy all temporary canvasitems created by LPEs
781 std::vector<Inkscape::Display::TemporaryItem*>::iterator i;
782 for (i = lpeitem->lpe_helperpaths.begin(); i != lpeitem->lpe_helperpaths.end(); ++i) {
783 desktop->remove_temporary_canvasitem(*i);
784 }
786 lpeitem->removing_helperpaths = false;
787 }
789 /*
790 Local Variables:
791 mode:c++
792 c-file-style:"stroustrup"
793 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
794 indent-tabs-mode:nil
795 fill-column:99
796 End:
797 */
798 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :