Code

Merge and cleanup of GSoC C++-ification project.
[inkscape.git] / src / sp-lpe-item.cpp
index 2eecd8e45904b3bf86babb3458c852f487de6b54..afd36d2ddef30cf1a68293cd86112984c86cf416 100644 (file)
@@ -1,5 +1,3 @@
-#define __SP_LPE_ITEM_CPP__
-
 /** \file
  * Base class for live path effect items
  */
@@ -7,6 +5,7 @@
  * Authors:
  *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
  *   Bastien Bouclet <bgkweb@gmail.com>
+ *   Abhishek Sharma
  *
  * Copyright (C) 2008 authors
  *
@@ -18,6 +17,7 @@
 #endif
 
 #include "live_effects/effect.h"
+#include "live_effects/lpe-path_length.h"
 #include "live_effects/lpeobject.h"
 #include "live_effects/lpeobject-reference.h"
 
@@ -32,6 +32,7 @@
 #include "message-stack.h"
 #include "inkscape.h"
 #include "desktop.h"
+#include "shape-editor.h"
 
 #include <algorithm>
 
@@ -53,7 +54,6 @@ static void sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * c
 
 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable);
 
-static void lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem);
 static void lpeobject_ref_modified(SPObject *href, guint flags, SPLPEItem *lpeitem);
 
 static void sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem);
@@ -117,10 +117,7 @@ sp_lpe_item_init(SPLPEItem *lpeitem)
     lpeitem->path_effect_list = new PathEffectList();
     lpeitem->current_path_effect = NULL;
 
-    new (&lpeitem->lpe_modified_connection) sigc::connection();
-
-    lpeitem->adding_helperpaths = false;
-    lpeitem->removing_helperpaths = false;
+    lpeitem->lpe_modified_connection_list = new std::list<sigc::connection>();
 }
 
 static void
@@ -139,7 +136,7 @@ sp_lpe_item_finalize(GObject *object)
 static void
 sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
 {
-    sp_object_read_attr(object, "inkscape:path-effect");
+    object->readAttr( "inkscape:path-effect" );
 
     if (((SPObjectClass *) parent_class)->build) {
         ((SPObjectClass *) parent_class)->build(object, document, repr);
@@ -154,8 +151,14 @@ sp_lpe_item_release(SPObject *object)
 {
     SPLPEItem *lpeitem = (SPLPEItem *) object;
 
-    lpeitem->lpe_modified_connection.disconnect();
-    lpeitem->lpe_modified_connection.~connection();
+    // disconnect all modified listeners:
+    for (std::list<sigc::connection>::iterator mod_it = lpeitem->lpe_modified_connection_list->begin();
+         mod_it != lpeitem->lpe_modified_connection_list->end(); ++mod_it)
+    {
+        mod_it->disconnect();
+    }
+    delete lpeitem->lpe_modified_connection_list;
+    lpeitem->lpe_modified_connection_list = NULL;
 
     PathEffectList::iterator it = lpeitem->path_effect_list->begin();
     while ( it != lpeitem->path_effect_list->end() ) {
@@ -165,7 +168,8 @@ sp_lpe_item_release(SPObject *object)
         it = lpeitem->path_effect_list->erase(it);
     }
     // delete the list itself
-    delete SP_LPE_ITEM(object)->path_effect_list;
+    delete lpeitem->path_effect_list;
+    lpeitem->path_effect_list = NULL;
 
     if (((SPObjectClass *) parent_class)->release)
         ((SPObjectClass *) parent_class)->release(object);
@@ -187,6 +191,14 @@ sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value)
                 // Disable the path effects while populating the LPE list
                  sp_lpe_item_enable_path_effects(lpeitem, false);
 
+                // disconnect all modified listeners:
+                for ( std::list<sigc::connection>::iterator mod_it = lpeitem->lpe_modified_connection_list->begin();
+                      mod_it != lpeitem->lpe_modified_connection_list->end();
+                      ++mod_it)
+                {
+                    mod_it->disconnect();
+                }
+                lpeitem->lpe_modified_connection_list->clear();
                 // Clear the path effect list
                 PathEffectList::iterator it = lpeitem->path_effect_list->begin();
                 while ( it != lpeitem->path_effect_list->end() )
@@ -202,22 +214,25 @@ sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value)
                     std::string href;
                     while (std::getline(iss, href, ';'))
                     {
-                        Inkscape::LivePathEffect::LPEObjectReference *path_effect_ref;
-                        path_effect_ref = new Inkscape::LivePathEffect::LPEObjectReference(SP_OBJECT(lpeitem));
-                        path_effect_ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(lpeobject_ref_changed), SP_LPE_ITEM(object)));
-                        // Now do the attaching, which emits the changed signal.
-                        // Fixme, it should not do this changed signal and updating before all effects are added to the path_effect_list
+                        Inkscape::LivePathEffect::LPEObjectReference *path_effect_ref = new Inkscape::LivePathEffect::LPEObjectReference(object);
                         try {
                             path_effect_ref->link(href.c_str());
-                        } catch (Inkscape::BadURIException &e) {
-                            g_warning("BadURIException: %s", e.what());
+                        } catch (Inkscape::BadURIException e) {
+                            g_warning("BadURIException when trying to find LPE: %s", e.what());
                             path_effect_ref->unlink();
                             delete path_effect_ref;
                             path_effect_ref = NULL;
                         }
 
-                        if (path_effect_ref) {
-                            lpeitem->path_effect_list->push_back(path_effect_ref);
+                        lpeitem->path_effect_list->push_back(path_effect_ref);
+                        if ( path_effect_ref->lpeobject && path_effect_ref->lpeobject->get_lpe() ) {
+                            // connect modified-listener
+                            lpeitem->lpe_modified_connection_list->push_back(
+                                                path_effect_ref->lpeobject->connectModified(sigc::bind(sigc::ptr_fun(&lpeobject_ref_modified), lpeitem)) );
+                        } else {
+                            // something has gone wrong in finding the right patheffect.
+                            g_warning("Unknown LPE type specified, LPE stack effectively disabled");
+                            // keep the effect in the lpestack, so the whole stack is effectively disabled but maintained
                         }
                     }
                 }
@@ -242,6 +257,9 @@ sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags)
     if (((SPObjectClass *) parent_class)->update) {
         ((SPObjectClass *) parent_class)->update(object, ctx, flags);
     }
+
+    // update the helperpaths of all LPEs applied to the item
+    // TODO: re-add for the new node tool
 }
 
 /**
@@ -250,7 +268,6 @@ sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags)
 static void
 sp_lpe_item_modified (SPObject *object, unsigned int flags)
 {
-
     if (SP_IS_GROUP(object) && (flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_USER_MODIFIED_FLAG_B)) {
         sp_lpe_item_update_patheffect(SP_LPE_ITEM(object), true, true);
     }
@@ -268,11 +285,13 @@ sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::
 {
     SPLPEItem *lpeitem = (SPLPEItem *) object;
 
-    if ( sp_lpe_item_has_path_effect(lpeitem) ) {
-        std::string href = patheffectlist_write_svg(*lpeitem->path_effect_list);
-        repr->setAttribute("inkscape:path-effect", href.c_str());
-    } else {
-        repr->setAttribute("inkscape:path-effect", NULL);
+    if (flags & SP_OBJECT_WRITE_EXT) {
+        if ( sp_lpe_item_has_path_effect(lpeitem) ) {
+            std::string href = patheffectlist_write_svg(*lpeitem->path_effect_list);
+            repr->setAttribute("inkscape:path-effect", href.c_str());
+        } else {
+            repr->setAttribute("inkscape:path-effect", NULL);
+        }
     }
 
     if (((SPObjectClass *)(parent_class))->write) {
@@ -282,29 +301,38 @@ sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::
     return repr;
 }
 
-void sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve) {
-    if (!lpeitem) return;
-    if (!curve) return;
+/**
+ * returns true when LPE was successful.
+ */
+bool sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve) {
+    if (!lpeitem) return false;
+    if (!curve) return false;
 
     if (sp_lpe_item_has_path_effect(lpeitem) && sp_lpe_item_path_effects_enabled(lpeitem)) {
         for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
         {
             LivePathEffectObject *lpeobj = (*it)->lpeobject;
             if (!lpeobj) {
+                /** \todo Investigate the cause of this.
+                 * 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.
+                 */
                 g_warning("sp_lpe_item_perform_path_effect - NULL lpeobj in list!");
-                return;
+                return false;
             }
-            if (!lpeobj->lpe) {
-                g_warning("sp_lpe_item_perform_path_effect - lpeobj without lpe!");
-                return;
+            Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
+            if (!lpe) {
+                /** \todo Investigate the cause of this.
+                 * Not sure, but I think this can happen when an unknown effect type is specified...
+                 */
+                g_warning("sp_lpe_item_perform_path_effect - lpeobj with invalid lpe in the stack!");
+                return false;
             }
 
-            Inkscape::LivePathEffect::Effect *lpe = lpeobj->lpe;
             if (lpe->isVisible()) {
-                if (lpe->acceptsNumParams() > 0 && !lpe->pathParamAccepted()) {
+                if (lpe->acceptsNumClicks() > 0 && !lpe->isReady()) {
                     // if the effect expects mouse input before being applied and the input is not finished
                     // yet, we don't alter the path
-                    return;
+                    return false;
                 }
 
                 // Groups have their doBeforeEffect called elsewhere
@@ -317,12 +345,17 @@ void sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve) {
                 }
                 catch (std::exception & e) {
                     g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what());
-                    SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
-                        _("An exception occurred during execution of the Path Effect.") );
+                    if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->messageStack()) {
+                        SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
+                                        _("An exception occurred during execution of the Path Effect.") );
+                    }
+                    return false;
                 }
             }
         }
     }
+
+    return true;
 }
 
 /**
@@ -340,6 +373,22 @@ sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write)
     if (!sp_lpe_item_path_effects_enabled(lpeitem))
         return;
 
+    // TODO: hack! this will be removed when path length measuring is reimplemented in a better way
+    PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
+    std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
+    for (i = lpelist.begin(); i != lpelist.end(); ++i) {
+        if ((*i)->lpeobject) {
+            Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe();
+            if (dynamic_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)) {
+                if (!lpe->isVisible()) {
+                    // we manually disable text for LPEPathLength
+                    // use static_cast, because we already checked for the right type above
+                    static_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)->hideCanvasText();
+                }
+            }
+        }
+    }
+
     SPLPEItem *top;
 
     if (wholetree) {
@@ -355,41 +404,20 @@ sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write)
         top = lpeitem;
     }
 
-    // TODO: ditch inkscape_active_desktop()
-    SPDesktop *desktop = inkscape_active_desktop();
-    if (desktop) {
-        //sp_lpe_item_remove_temporary_canvasitems(lpeitem, desktop);
-        //sp_lpe_item_add_temporary_canvasitems(lpeitem, desktop);
-    }
-
     if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect) {
         SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect (top, write);
     }
 }
 
 /**
- * Gets called when (re)attached to another lpeobject.
- */
-static void
-lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem)
-{
-    if (old_ref) {
-        sp_signal_disconnect_by_data(old_ref, lpeitem);
-    }
-    if ( IS_LIVEPATHEFFECT(ref) && ref != lpeitem )
-    {
-        lpeitem->lpe_modified_connection.disconnect();
-        lpeitem->lpe_modified_connection = ref->connectModified(sigc::bind(sigc::ptr_fun(&lpeobject_ref_modified), lpeitem));
-        lpeobject_ref_modified(ref, 0, lpeitem);
-    }
-}
-
-/**
- * Gets called when lpeobject repr contents change: i.e. parameter change.
+ * Gets called when any of the lpestack's lpeobject repr contents change: i.e. parameter change in any of the stacked LPEs
  */
 static void
 lpeobject_ref_modified(SPObject */*href*/, guint /*flags*/, SPLPEItem *lpeitem)
 {
+#ifdef SHAPE_VERBOSE
+    g_message("lpeobject_ref_modified");
+#endif
     sp_lpe_item_update_patheffect (lpeitem, true, true);
 }
 
@@ -463,8 +491,8 @@ void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset)
         sp_lpe_item_create_original_path_recursive(lpeitem);
 
         LivePathEffectObject *lpeobj = lpeitem->path_effect_list->back()->lpeobject;
-        if (lpeobj && lpeobj->lpe) {
-            Inkscape::LivePathEffect::Effect *lpe = lpeobj->lpe;
+        if (lpeobj && lpeobj->get_lpe()) {
+            Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
             // Ask the path effect to reset itself if it doesn't have parameters yet
             if (reset) {
                 // has to be called when all the subitems have their lpes applied
@@ -474,11 +502,8 @@ void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset)
             // perform this once when the effect is applied
             lpe->doOnApply(SP_LPE_ITEM(lpeitem));
 
-            // if the effect expects a number of mouse clicks to set a parameter path, perform the
-            // necessary preparations
-            if (lpe->acceptsNumParams() > 0) {
-                lpe->doAcceptPathPreparations(lpeitem);
-            }
+            // indicate that all necessary preparations are done and the effect can be performed
+            lpe->setReady();
         }
 
         //Enable the path effects now that everything is ready to apply the new path effect
@@ -507,7 +532,11 @@ void sp_lpe_item_remove_current_path_effect(SPLPEItem *lpeitem, bool keep_paths)
     new_list.remove(lperef); //current lpe ref is always our 'own' pointer from the path_effect_list
     std::string r = patheffectlist_write_svg(new_list);
 
-    SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
+    if (!r.empty()) {
+        SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
+    } else {
+        SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
+    }
 
     if (!keep_paths) {
         sp_lpe_item_cleanup_original_path_recursive(lpeitem);
@@ -564,10 +593,40 @@ void sp_lpe_item_up_current_path_effect(SPLPEItem *lpeitem)
     sp_lpe_item_cleanup_original_path_recursive(lpeitem);
 }
 
+/** used for shapes so they can see if they should also disable shape calculation and read from d= */
+bool sp_lpe_item_has_broken_path_effect(SPLPEItem *lpeitem)
+{
+    if (lpeitem->path_effect_list->empty())
+        return false;
+
+    // go through the list; if some are unknown or invalid, return true
+    PathEffectList effect_list =  sp_lpe_item_get_effect_list(lpeitem);
+    for (PathEffectList::iterator it = effect_list.begin(); it != effect_list.end(); it++)
+    {
+        LivePathEffectObject *lpeobj = (*it)->lpeobject;
+        if (!lpeobj || !lpeobj->get_lpe())
+            return true;
+    }
+
+    return false;
+}
+
 
 bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem)
 {
-    return !lpeitem->path_effect_list->empty();
+    if (lpeitem->path_effect_list->empty())
+        return false;
+
+    // go through the list; if some are unknown or invalid, we are not an LPE item!
+    PathEffectList effect_list =  sp_lpe_item_get_effect_list(lpeitem);
+    for (PathEffectList::iterator it = effect_list.begin(); it != effect_list.end(); it++)
+    {
+        LivePathEffectObject *lpeobj = (*it)->lpeobject;
+        if (!lpeobj || !lpeobj->get_lpe())
+            return false;
+    }
+
+    return true;
 }
 
 bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem)
@@ -586,8 +645,9 @@ sp_lpe_item_has_path_effect_of_type(SPLPEItem *lpeitem, int type)
 {
     std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
     for (i = lpeitem->path_effect_list->begin(); i != lpeitem->path_effect_list->end(); ++i) {
-        if ((*i)->lpeobject->lpe->effectType() == type) {
-            return (*i)->lpeobject->lpe;
+        Inkscape::LivePathEffect::Effect* lpe = (*i)->lpeobject->get_lpe();
+        if (lpe && (lpe->effectType() == type)) {
+            return lpe;
         }
     }
     return NULL;
@@ -608,8 +668,8 @@ bool sp_lpe_item_can_accept_freehand_shape(SPLPEItem *lpeitem)
 void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt)
 {
     Inkscape::LivePathEffect::LPEObjectReference *lperef = sp_lpe_item_get_current_lpereference(lpeitem);
-    if (lperef && lperef->lpeobject && lperef->lpeobject->lpe) {
-        lperef->lpeobject->lpe->editNextParamOncanvas(SP_ITEM(lpeitem), dt);
+    if (lperef && lperef->lpeobject && lperef->lpeobject->get_lpe()) {
+        lperef->lpeobject->get_lpe()->editNextParamOncanvas(SP_ITEM(lpeitem), dt);
     }
 }
 
@@ -620,7 +680,7 @@ sp_lpe_item_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape:
         (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref);
 
     if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
-        SPObject *ochild = sp_object_get_child_by_repr(object, child);
+        SPObject *ochild = object->get_child_by_repr(child);
         if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
             sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(ochild));
         }
@@ -631,7 +691,7 @@ static void
 sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child)
 {
     if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
-        SPObject *ochild = sp_object_get_child_by_repr(object, child);
+        SPObject *ochild = object->get_child_by_repr(child);
         if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
             sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(ochild));
         }
@@ -682,8 +742,9 @@ PathEffectList sp_lpe_item_get_effect_list(SPLPEItem *lpeitem)
 
 Inkscape::LivePathEffect::LPEObjectReference* sp_lpe_item_get_current_lpereference(SPLPEItem *lpeitem)
 {
-    if (!lpeitem->current_path_effect && !lpeitem->path_effect_list->empty())
+    if (!lpeitem->current_path_effect && !lpeitem->path_effect_list->empty()) {
         sp_lpe_item_set_current_path_effect(lpeitem, lpeitem->path_effect_list->back());
+    }
 
     return lpeitem->current_path_effect;
 }
@@ -693,7 +754,7 @@ Inkscape::LivePathEffect::Effect* sp_lpe_item_get_current_lpe(SPLPEItem *lpeitem
     Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
 
     if (lperef && lperef->lpeobject)
-        return lperef->lpeobject->lpe;
+        return lperef->lpeobject->get_lpe();
     else
         return NULL;
 }
@@ -702,7 +763,6 @@ bool sp_lpe_item_set_current_path_effect(SPLPEItem *lpeitem, Inkscape::LivePathE
 {
     for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); it++) {
         if ((*it)->lpeobject_repr == lperef->lpeobject_repr) {
-            lpeobject_ref_changed(NULL, (*it)->lpeobject, SP_LPE_ITEM(lpeitem)); // FIXME: explain why this is here?
             lpeitem->current_path_effect = (*it);  // current_path_effect should always be a pointer from the path_effect_list !
             return true;
         }
@@ -711,14 +771,21 @@ bool sp_lpe_item_set_current_path_effect(SPLPEItem *lpeitem, Inkscape::LivePathE
     return false;
 }
 
-void sp_lpe_item_replace_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * old_lpeobj,
-                                        LivePathEffectObject * new_lpeobj)
+/**
+ * Writes a new "inkscape:path-effect" string to xml, where the old_lpeobjects are substituted by the new ones.
+ *  Note that this method messes up the item's \c PathEffectList.
+ */
+void SPLPEItem::replacePathEffects( std::vector<LivePathEffectObject const *> const old_lpeobjs,
+                                    std::vector<LivePathEffectObject const *> const new_lpeobjs )
 {
     HRefList hreflist;
-    for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
+    for (PathEffectList::const_iterator it = this->path_effect_list->begin(); it != this->path_effect_list->end(); ++it)
     {
-        if ((*it)->lpeobject == old_lpeobj) {
-            const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
+        LivePathEffectObject const * current_lpeobj = (*it)->lpeobject;
+        std::vector<LivePathEffectObject const *>::const_iterator found_it(std::find(old_lpeobjs.begin(), old_lpeobjs.end(), current_lpeobj));
+        if ( found_it != old_lpeobjs.end() ) {
+            std::vector<LivePathEffectObject const *>::difference_type found_index = std::distance (old_lpeobjs.begin(), found_it);
+            const gchar * repr_id = SP_OBJECT_REPR(new_lpeobjs[found_index])->attribute("id");
             gchar *hrefstr = g_strdup_printf("#%s", repr_id);
             hreflist.push_back( std::string(hrefstr) );
             g_free(hrefstr);
@@ -728,7 +795,49 @@ void sp_lpe_item_replace_path_effect(SPLPEItem *lpeitem, LivePathEffectObject *
         }
     }
     std::string r = hreflist_write_svg(hreflist);
-    SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
+    SP_OBJECT_REPR(this)->setAttribute("inkscape:path-effect", r.c_str());
+}
+
+/**
+ *  Check all effects in the stack if they are used by other items, and fork them if so.
+ *  It is not recommended to fork the effects by yourself calling LivePathEffectObject::fork_private_if_necessary,
+ *  use this method instead.
+ *  Returns true if one or more effects were forked; returns false if nothing was done.
+ */
+bool sp_lpe_item_fork_path_effects_if_necessary(SPLPEItem *lpeitem, unsigned int nr_of_allowed_users)
+{
+    bool forked = false;
+
+    if ( sp_lpe_item_has_path_effect(lpeitem) ) {
+        // If one of the path effects is used by 2 or more items, fork it
+        // so that each object has its own independent copy of the effect.
+        // Note: replacing path effects messes up the path effect list
+
+        // Clones of the LPEItem will increase the refcount of the lpeobjects.
+        // Therefore, nr_of_allowed_users should be increased with the number of clones (i.e. refs to the lpeitem)
+        nr_of_allowed_users += SP_OBJECT(lpeitem)->hrefcount;
+
+        std::vector<LivePathEffectObject const *> old_lpeobjs, new_lpeobjs;
+        PathEffectList effect_list =  sp_lpe_item_get_effect_list(lpeitem);
+        for (PathEffectList::iterator it = effect_list.begin(); it != effect_list.end(); it++)
+        {
+            LivePathEffectObject *lpeobj = (*it)->lpeobject;
+            if (lpeobj) {
+                LivePathEffectObject *forked_lpeobj = lpeobj->fork_private_if_necessary(nr_of_allowed_users);
+                if (forked_lpeobj != lpeobj) {
+                    forked = true;
+                    old_lpeobjs.push_back(lpeobj);
+                    new_lpeobjs.push_back(forked_lpeobj);
+                }
+            }
+        }
+
+        if (forked) {
+            lpeitem->replacePathEffects(old_lpeobjs, new_lpeobjs);
+        }
+    }
+
+    return forked;
 }
 
 // Enable or disable the path effects of the item.
@@ -749,43 +858,6 @@ bool sp_lpe_item_path_effects_enabled(SPLPEItem *lpeitem)
     return lpeitem->path_effects_enabled > 0;
 }
 
-void
-sp_lpe_item_add_temporary_canvasitems(SPLPEItem *lpeitem, SPDesktop *desktop)
-{
-    if (lpeitem->adding_helperpaths) {
-        return;
-    }
-    lpeitem->adding_helperpaths = true;
-    // FIXME: for some reason it seems that we must create the variable lpe AFTER checking
-    //        for adding_helperpaths == true; otherwise we get a crash on startup. why??
-    Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(lpeitem);
-    if (lpe) {
-        // TODO: can we just update the tempitem's SPCurve instead of recreating it each time?
-        lpe->addHelperPaths(lpeitem, desktop);
-    }
-    lpeitem->adding_helperpaths = false;
-}
-
-void
-sp_lpe_item_remove_temporary_canvasitems(SPLPEItem *lpeitem, SPDesktop *desktop)
-{
-    g_return_if_fail(lpeitem);
-    g_return_if_fail(desktop);
-
-    if (lpeitem->removing_helperpaths) {
-        return;
-    }
-    lpeitem->removing_helperpaths = true;
-
-    // destroy all temporary canvasitems created by LPEs
-    std::vector<Inkscape::Display::TemporaryItem*>::iterator i;
-    for (i = lpeitem->lpe_helperpaths.begin(); i != lpeitem->lpe_helperpaths.end(); ++i) {
-        desktop->remove_temporary_canvasitem(*i);
-    }
-
-    lpeitem->removing_helperpaths = false;
-}
-
 /*
   Local Variables:
   mode:c++
@@ -795,4 +867,4 @@ sp_lpe_item_remove_temporary_canvasitems(SPLPEItem *lpeitem, SPDesktop *desktop)
   fill-column:99
   End:
 */
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :