Code

Merge and cleanup of GSoC C++-ification project.
[inkscape.git] / src / sp-lpe-item.cpp
1 /** \file
2  * Base class for live path effect items
3  */
4 /*
5  * Authors:
6  *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
7  *   Bastien Bouclet <bgkweb@gmail.com>
8  *   Abhishek Sharma
9  *
10  * Copyright (C) 2008 authors
11  *
12  * Released under GNU GPL, read the file 'COPYING' for more information
13  */
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
19 #include "live_effects/effect.h"
20 #include "live_effects/lpe-path_length.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"
35 #include "shape-editor.h"
37 #include <algorithm>
39 /* LPEItem base class */
41 static void sp_lpe_item_class_init(SPLPEItemClass *klass);
42 static void sp_lpe_item_init(SPLPEItem *lpe_item);
43 static void sp_lpe_item_finalize(GObject *object);
45 static void sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
46 static void sp_lpe_item_release(SPObject *object);
47 static void sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value);
48 static void sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags);
49 static void sp_lpe_item_modified (SPObject *object, unsigned int flags);
50 static Inkscape::XML::Node *sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags);
52 static void sp_lpe_item_child_added (SPObject * object, Inkscape::XML::Node * child, Inkscape::XML::Node * ref);
53 static void sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child);
55 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable);
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;
112 static void
113 sp_lpe_item_init(SPLPEItem *lpeitem)
115     lpeitem->path_effects_enabled = 1;
117     lpeitem->path_effect_list = new PathEffectList();
118     lpeitem->current_path_effect = NULL;
120     lpeitem->lpe_modified_connection_list = new std::list<sigc::connection>();
123 static void
124 sp_lpe_item_finalize(GObject *object)
126     if (((GObjectClass *) (parent_class))->finalize) {
127         (* ((GObjectClass *) (parent_class))->finalize)(object);
128     }
131 /**
132  * Reads the Inkscape::XML::Node, and initializes SPLPEItem variables.  For this to get called,
133  * our name must be associated with a repr via "sp_object_type_register".  Best done through
134  * sp-object-repr.cpp's repr_name_entries array.
135  */
136 static void
137 sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
139     object->readAttr( "inkscape:path-effect" );
141     if (((SPObjectClass *) parent_class)->build) {
142         ((SPObjectClass *) parent_class)->build(object, document, repr);
143     }
146 /**
147  * Drops any allocated memory.
148  */
149 static void
150 sp_lpe_item_release(SPObject *object)
152     SPLPEItem *lpeitem = (SPLPEItem *) object;
154     // disconnect all modified listeners:
155     for (std::list<sigc::connection>::iterator mod_it = lpeitem->lpe_modified_connection_list->begin();
156          mod_it != lpeitem->lpe_modified_connection_list->end(); ++mod_it)
157     {
158         mod_it->disconnect();
159     }
160     delete lpeitem->lpe_modified_connection_list;
161     lpeitem->lpe_modified_connection_list = NULL;
163     PathEffectList::iterator it = lpeitem->path_effect_list->begin();
164     while ( it != lpeitem->path_effect_list->end() ) {
165         // unlink and delete all references in the list
166         (*it)->unlink();
167         delete *it;
168         it = lpeitem->path_effect_list->erase(it);
169     }
170     // delete the list itself
171     delete lpeitem->path_effect_list;
172     lpeitem->path_effect_list = NULL;
174     if (((SPObjectClass *) parent_class)->release)
175         ((SPObjectClass *) parent_class)->release(object);
178 /**
179  * Sets a specific value in the SPLPEItem.
180  */
181 static void
182 sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value)
184     SPLPEItem *lpeitem = (SPLPEItem *) object;
186     switch (key) {
187         case SP_ATTR_INKSCAPE_PATH_EFFECT:
188             {
189                 lpeitem->current_path_effect = NULL;
191                 // Disable the path effects while populating the LPE list
192                  sp_lpe_item_enable_path_effects(lpeitem, false);
194                 // disconnect all modified listeners:
195                 for ( std::list<sigc::connection>::iterator mod_it = lpeitem->lpe_modified_connection_list->begin();
196                       mod_it != lpeitem->lpe_modified_connection_list->end();
197                       ++mod_it)
198                 {
199                     mod_it->disconnect();
200                 }
201                 lpeitem->lpe_modified_connection_list->clear();
202                 // Clear the path effect list
203                 PathEffectList::iterator it = lpeitem->path_effect_list->begin();
204                 while ( it != lpeitem->path_effect_list->end() )
205                 {
206                     (*it)->unlink();
207                     delete *it;
208                     it = lpeitem->path_effect_list->erase(it);
209                 }
211                 // Parse the contents of "value" to rebuild the path effect reference list
212                 if ( value ) {
213                     std::istringstream iss(value);
214                     std::string href;
215                     while (std::getline(iss, href, ';'))
216                     {
217                         Inkscape::LivePathEffect::LPEObjectReference *path_effect_ref = new Inkscape::LivePathEffect::LPEObjectReference(object);
218                         try {
219                             path_effect_ref->link(href.c_str());
220                         } catch (Inkscape::BadURIException e) {
221                             g_warning("BadURIException when trying to find LPE: %s", e.what());
222                             path_effect_ref->unlink();
223                             delete path_effect_ref;
224                             path_effect_ref = NULL;
225                         }
227                         lpeitem->path_effect_list->push_back(path_effect_ref);
228                         if ( path_effect_ref->lpeobject && path_effect_ref->lpeobject->get_lpe() ) {
229                             // connect modified-listener
230                             lpeitem->lpe_modified_connection_list->push_back(
231                                                 path_effect_ref->lpeobject->connectModified(sigc::bind(sigc::ptr_fun(&lpeobject_ref_modified), lpeitem)) );
232                         } else {
233                             // something has gone wrong in finding the right patheffect.
234                             g_warning("Unknown LPE type specified, LPE stack effectively disabled");
235                             // keep the effect in the lpestack, so the whole stack is effectively disabled but maintained
236                         }
237                     }
238                 }
240                 sp_lpe_item_enable_path_effects(lpeitem, true);
241             }
242             break;
243         default:
244             if (((SPObjectClass *) parent_class)->set) {
245                 ((SPObjectClass *) parent_class)->set(object, key, value);
246             }
247             break;
248     }
251 /**
252  * Receives update notifications.
253  */
254 static void
255 sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags)
257     if (((SPObjectClass *) parent_class)->update) {
258         ((SPObjectClass *) parent_class)->update(object, ctx, flags);
259     }
261     // update the helperpaths of all LPEs applied to the item
262     // TODO: re-add for the new node tool
265 /**
266  * Sets modified flag for all sub-item views.
267  */
268 static void
269 sp_lpe_item_modified (SPObject *object, unsigned int flags)
271     if (SP_IS_GROUP(object) && (flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_USER_MODIFIED_FLAG_B)) {
272         sp_lpe_item_update_patheffect(SP_LPE_ITEM(object), true, true);
273     }
275     if (((SPObjectClass *) (parent_class))->modified) {
276         (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
277     }
280 /**
281  * Writes its settings to an incoming repr object, if any.
282  */
283 static Inkscape::XML::Node *
284 sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
286     SPLPEItem *lpeitem = (SPLPEItem *) object;
288     if (flags & SP_OBJECT_WRITE_EXT) {
289         if ( sp_lpe_item_has_path_effect(lpeitem) ) {
290             std::string href = patheffectlist_write_svg(*lpeitem->path_effect_list);
291             repr->setAttribute("inkscape:path-effect", href.c_str());
292         } else {
293             repr->setAttribute("inkscape:path-effect", NULL);
294         }
295     }
297     if (((SPObjectClass *)(parent_class))->write) {
298         ((SPObjectClass *)(parent_class))->write(object, xml_doc, repr, flags);
299     }
301     return repr;
304 /**
305  * returns true when LPE was successful.
306  */
307 bool sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve) {
308     if (!lpeitem) return false;
309     if (!curve) return false;
311     if (sp_lpe_item_has_path_effect(lpeitem) && sp_lpe_item_path_effects_enabled(lpeitem)) {
312         for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
313         {
314             LivePathEffectObject *lpeobj = (*it)->lpeobject;
315             if (!lpeobj) {
316                 /** \todo Investigate the cause of this.
317                  * 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.
318                  */
319                 g_warning("sp_lpe_item_perform_path_effect - NULL lpeobj in list!");
320                 return false;
321             }
322             Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
323             if (!lpe) {
324                 /** \todo Investigate the cause of this.
325                  * Not sure, but I think this can happen when an unknown effect type is specified...
326                  */
327                 g_warning("sp_lpe_item_perform_path_effect - lpeobj with invalid lpe in the stack!");
328                 return false;
329             }
331             if (lpe->isVisible()) {
332                 if (lpe->acceptsNumClicks() > 0 && !lpe->isReady()) {
333                     // if the effect expects mouse input before being applied and the input is not finished
334                     // yet, we don't alter the path
335                     return false;
336                 }
338                 // Groups have their doBeforeEffect called elsewhere
339                 if (!SP_IS_GROUP(lpeitem)) {
340                     lpe->doBeforeEffect(lpeitem);
341                 }
343                 try {
344                     lpe->doEffect(curve);
345                 }
346                 catch (std::exception & e) {
347                     g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what());
348                     if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->messageStack()) {
349                         SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
350                                         _("An exception occurred during execution of the Path Effect.") );
351                     }
352                     return false;
353                 }
354             }
355         }
356     }
358     return true;
361 /**
362  * Calls any registered handlers for the update_patheffect action
363  */
364 void
365 sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write)
367 #ifdef SHAPE_VERBOSE
368     g_message("sp_lpe_item_update_patheffect: %p\n", lpeitem);
369 #endif
370     g_return_if_fail (lpeitem != NULL);
371     g_return_if_fail (SP_IS_LPE_ITEM (lpeitem));
373     if (!sp_lpe_item_path_effects_enabled(lpeitem))
374         return;
376     // TODO: hack! this will be removed when path length measuring is reimplemented in a better way
377     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
378     std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
379     for (i = lpelist.begin(); i != lpelist.end(); ++i) {
380         if ((*i)->lpeobject) {
381             Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe();
382             if (dynamic_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)) {
383                 if (!lpe->isVisible()) {
384                     // we manually disable text for LPEPathLength
385                     // use static_cast, because we already checked for the right type above
386                     static_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)->hideCanvasText();
387                 }
388             }
389         }
390     }
392     SPLPEItem *top;
394     if (wholetree) {
395         SPObject *prev_parent = lpeitem;
396         SPObject *parent = prev_parent->parent;
397         while (parent && SP_IS_LPE_ITEM(parent) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent))) {
398             prev_parent = parent;
399             parent = prev_parent->parent;
400         }
401         top = SP_LPE_ITEM(prev_parent);
402     }
403     else {
404         top = lpeitem;
405     }
407     if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect) {
408         SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect (top, write);
409     }
412 /**
413  * Gets called when any of the lpestack's lpeobject repr contents change: i.e. parameter change in any of the stacked LPEs
414  */
415 static void
416 lpeobject_ref_modified(SPObject */*href*/, guint /*flags*/, SPLPEItem *lpeitem)
418 #ifdef SHAPE_VERBOSE
419     g_message("lpeobject_ref_modified");
420 #endif
421     sp_lpe_item_update_patheffect (lpeitem, true, true);
424 static void
425 sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem)
427     if (SP_IS_GROUP(lpeitem)) {
428         GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
429         for ( GSList const *iter = item_list; iter; iter = iter->next ) {
430             SPObject *subitem = static_cast<SPObject *>(iter->data);
431             if (SP_IS_LPE_ITEM(subitem)) {
432                 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(subitem));
433             }
434         }
435     }
436     else if (SP_IS_PATH(lpeitem)) {
437         Inkscape::XML::Node *pathrepr = SP_OBJECT_REPR(lpeitem);
438         if ( !pathrepr->attribute("inkscape:original-d") ) {
439             pathrepr->setAttribute("inkscape:original-d", pathrepr->attribute("d"));
440         }
441     }
444 static void
445 sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem)
447     if (SP_IS_GROUP(lpeitem)) {
448         GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
449         for ( GSList const *iter = item_list; iter; iter = iter->next ) {
450             SPObject *subitem = static_cast<SPObject *>(iter->data);
451             if (SP_IS_LPE_ITEM(subitem)) {
452                 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(subitem));
453             }
454         }
455     }
456     else if (SP_IS_PATH(lpeitem)) {
457         Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem);
458         if (!sp_lpe_item_has_path_effect_recursive(lpeitem)
459                 && repr->attribute("inkscape:original-d")) {
460             repr->setAttribute("d", repr->attribute("inkscape:original-d"));
461             repr->setAttribute("inkscape:original-d", NULL);
462         }
463         else {
464             sp_lpe_item_update_patheffect(lpeitem, true, true);
465         }
466     }
469 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset)
471     if (value) {
472         // Apply the path effects here because in the casse of a group, lpe->resetDefaults
473         // needs that all the subitems have their effects applied
474         sp_lpe_item_update_patheffect(lpeitem, false, true);
476         // Disable the path effects while preparing the new lpe
477         sp_lpe_item_enable_path_effects(lpeitem, false);
479         // Add the new reference to the list of LPE references
480         HRefList hreflist;
481         for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
482         {
483             hreflist.push_back( std::string((*it)->lpeobject_href) );
484         }
485         hreflist.push_back( std::string(value) );
486         std::string hrefs = hreflist_write_svg(hreflist);
488         SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", hrefs.c_str());
490         // make sure there is an original-d for paths!!!
491         sp_lpe_item_create_original_path_recursive(lpeitem);
493         LivePathEffectObject *lpeobj = lpeitem->path_effect_list->back()->lpeobject;
494         if (lpeobj && lpeobj->get_lpe()) {
495             Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
496             // Ask the path effect to reset itself if it doesn't have parameters yet
497             if (reset) {
498                 // has to be called when all the subitems have their lpes applied
499                 lpe->resetDefaults(lpeitem);
500             }
502             // perform this once when the effect is applied
503             lpe->doOnApply(SP_LPE_ITEM(lpeitem));
505             // indicate that all necessary preparations are done and the effect can be performed
506             lpe->setReady();
507         }
509         //Enable the path effects now that everything is ready to apply the new path effect
510         sp_lpe_item_enable_path_effects(lpeitem, true);
512         // Apply the path effect
513         sp_lpe_item_update_patheffect(lpeitem, true, true);
514     }
517 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj)
519     const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
520     gchar *hrefstr = g_strdup_printf("#%s", repr_id);
521     sp_lpe_item_add_path_effect(lpeitem, hrefstr, false);
522     g_free(hrefstr);
525 void sp_lpe_item_remove_current_path_effect(SPLPEItem *lpeitem, bool keep_paths)
527     Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
528     if (!lperef)
529         return;
531     PathEffectList new_list = *lpeitem->path_effect_list;
532     new_list.remove(lperef); //current lpe ref is always our 'own' pointer from the path_effect_list
533     std::string r = patheffectlist_write_svg(new_list);
535     if (!r.empty()) {
536         SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
537     } else {
538         SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
539     }
541     if (!keep_paths) {
542         sp_lpe_item_cleanup_original_path_recursive(lpeitem);
543     }
546 void sp_lpe_item_remove_all_path_effects(SPLPEItem *lpeitem, bool keep_paths)
548     SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
550     if (!keep_paths) {
551         sp_lpe_item_cleanup_original_path_recursive(lpeitem);
552     }
555 void sp_lpe_item_down_current_path_effect(SPLPEItem *lpeitem)
557     Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
558     if (!lperef)
559         return;
561     PathEffectList new_list = *lpeitem->path_effect_list;
562     PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
563     if (cur_it != new_list.end()) {
564         PathEffectList::iterator down_it = cur_it;
565         down_it++;
566         if (down_it != new_list.end()) { // perhaps current effect is already last effect
567             std::iter_swap(cur_it, down_it);
568         }
569     }
570     std::string r = patheffectlist_write_svg(new_list);
571     SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
573     sp_lpe_item_cleanup_original_path_recursive(lpeitem);
576 void sp_lpe_item_up_current_path_effect(SPLPEItem *lpeitem)
578     Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
579     if (!lperef)
580         return;
582     PathEffectList new_list = *lpeitem->path_effect_list;
583     PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
584     if (cur_it != new_list.end() && cur_it != new_list.begin()) {
585         PathEffectList::iterator up_it = cur_it;
586         up_it--;
587         std::iter_swap(cur_it, up_it);
588     }
589     std::string r = patheffectlist_write_svg(new_list);
591     SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
593     sp_lpe_item_cleanup_original_path_recursive(lpeitem);
596 /** used for shapes so they can see if they should also disable shape calculation and read from d= */
597 bool sp_lpe_item_has_broken_path_effect(SPLPEItem *lpeitem)
599     if (lpeitem->path_effect_list->empty())
600         return false;
602     // go through the list; if some are unknown or invalid, return true
603     PathEffectList effect_list =  sp_lpe_item_get_effect_list(lpeitem);
604     for (PathEffectList::iterator it = effect_list.begin(); it != effect_list.end(); it++)
605     {
606         LivePathEffectObject *lpeobj = (*it)->lpeobject;
607         if (!lpeobj || !lpeobj->get_lpe())
608             return true;
609     }
611     return false;
615 bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem)
617     if (lpeitem->path_effect_list->empty())
618         return false;
620     // go through the list; if some are unknown or invalid, we are not an LPE item!
621     PathEffectList effect_list =  sp_lpe_item_get_effect_list(lpeitem);
622     for (PathEffectList::iterator it = effect_list.begin(); it != effect_list.end(); it++)
623     {
624         LivePathEffectObject *lpeobj = (*it)->lpeobject;
625         if (!lpeobj || !lpeobj->get_lpe())
626             return false;
627     }
629     return true;
632 bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem)
634     SPObject *parent = lpeitem->parent;
635     if (parent && SP_IS_LPE_ITEM(parent)) {
636         return sp_lpe_item_has_path_effect(lpeitem) || sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent));
637     }
638     else {
639         return sp_lpe_item_has_path_effect(lpeitem);
640     }
643 Inkscape::LivePathEffect::Effect*
644 sp_lpe_item_has_path_effect_of_type(SPLPEItem *lpeitem, int type)
646     std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
647     for (i = lpeitem->path_effect_list->begin(); i != lpeitem->path_effect_list->end(); ++i) {
648         Inkscape::LivePathEffect::Effect* lpe = (*i)->lpeobject->get_lpe();
649         if (lpe && (lpe->effectType() == type)) {
650             return lpe;
651         }
652     }
653     return NULL;
656 /* Return false if the item is not a path or already has a shape applied */
657 bool sp_lpe_item_can_accept_freehand_shape(SPLPEItem *lpeitem)
659     if (!SP_IS_PATH(lpeitem))
660         return false;
662     if (sp_lpe_item_has_path_effect_of_type(lpeitem, Inkscape::LivePathEffect::FREEHAND_SHAPE))
663         return false;
665     return true;
668 void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt)
670     Inkscape::LivePathEffect::LPEObjectReference *lperef = sp_lpe_item_get_current_lpereference(lpeitem);
671     if (lperef && lperef->lpeobject && lperef->lpeobject->get_lpe()) {
672         lperef->lpeobject->get_lpe()->editNextParamOncanvas(SP_ITEM(lpeitem), dt);
673     }
676 static void
677 sp_lpe_item_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
679     if (((SPObjectClass *) (parent_class))->child_added)
680         (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref);
682     if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
683         SPObject *ochild = object->get_child_by_repr(child);
684         if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
685             sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(ochild));
686         }
687     }
690 static void
691 sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child)
693     if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
694         SPObject *ochild = object->get_child_by_repr(child);
695         if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
696             sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(ochild));
697         }
698     }
700     if (((SPObjectClass *) (parent_class))->remove_child)
701         (* ((SPObjectClass *) (parent_class))->remove_child) (object, child);
704 static std::string patheffectlist_write_svg(PathEffectList const & list)
706     HRefList hreflist;
707     for (PathEffectList::const_iterator it = list.begin(); it != list.end(); ++it)
708     {
709         hreflist.push_back( std::string((*it)->lpeobject_href) );
710     }
711     return hreflist_write_svg(hreflist);
714 /**
715  *  THE function that should be used to generate any patheffectlist string.
716  * one of the methods to change the effect list:
717  *  - create temporary href list
718  *  - populate the templist with the effects from the old list that you want to have and their order
719  *  - call this function with temp list as param
720  */
721 static std::string hreflist_write_svg(HRefList const & list)
723     std::string r;
724     bool semicolon_first = false;
725     for (HRefList::const_iterator it = list.begin(); it != list.end(); ++it)
726     {
727         if (semicolon_first) {
728             r += ';';
729         }
730         semicolon_first = true;
732         r += (*it);
733     }
734     return r;
737 // Return a copy of the effect list
738 PathEffectList sp_lpe_item_get_effect_list(SPLPEItem *lpeitem)
740     return *lpeitem->path_effect_list;
743 Inkscape::LivePathEffect::LPEObjectReference* sp_lpe_item_get_current_lpereference(SPLPEItem *lpeitem)
745     if (!lpeitem->current_path_effect && !lpeitem->path_effect_list->empty()) {
746         sp_lpe_item_set_current_path_effect(lpeitem, lpeitem->path_effect_list->back());
747     }
749     return lpeitem->current_path_effect;
752 Inkscape::LivePathEffect::Effect* sp_lpe_item_get_current_lpe(SPLPEItem *lpeitem)
754     Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
756     if (lperef && lperef->lpeobject)
757         return lperef->lpeobject->get_lpe();
758     else
759         return NULL;
762 bool sp_lpe_item_set_current_path_effect(SPLPEItem *lpeitem, Inkscape::LivePathEffect::LPEObjectReference* lperef)
764     for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); it++) {
765         if ((*it)->lpeobject_repr == lperef->lpeobject_repr) {
766             lpeitem->current_path_effect = (*it);  // current_path_effect should always be a pointer from the path_effect_list !
767             return true;
768         }
769     }
771     return false;
774 /**
775  * Writes a new "inkscape:path-effect" string to xml, where the old_lpeobjects are substituted by the new ones.
776  *  Note that this method messes up the item's \c PathEffectList.
777  */
778 void SPLPEItem::replacePathEffects( std::vector<LivePathEffectObject const *> const old_lpeobjs,
779                                     std::vector<LivePathEffectObject const *> const new_lpeobjs )
781     HRefList hreflist;
782     for (PathEffectList::const_iterator it = this->path_effect_list->begin(); it != this->path_effect_list->end(); ++it)
783     {
784         LivePathEffectObject const * current_lpeobj = (*it)->lpeobject;
785         std::vector<LivePathEffectObject const *>::const_iterator found_it(std::find(old_lpeobjs.begin(), old_lpeobjs.end(), current_lpeobj));
786         if ( found_it != old_lpeobjs.end() ) {
787             std::vector<LivePathEffectObject const *>::difference_type found_index = std::distance (old_lpeobjs.begin(), found_it);
788             const gchar * repr_id = SP_OBJECT_REPR(new_lpeobjs[found_index])->attribute("id");
789             gchar *hrefstr = g_strdup_printf("#%s", repr_id);
790             hreflist.push_back( std::string(hrefstr) );
791             g_free(hrefstr);
792         }
793         else {
794             hreflist.push_back( std::string((*it)->lpeobject_href) );
795         }
796     }
797     std::string r = hreflist_write_svg(hreflist);
798     SP_OBJECT_REPR(this)->setAttribute("inkscape:path-effect", r.c_str());
801 /**
802  *  Check all effects in the stack if they are used by other items, and fork them if so.
803  *  It is not recommended to fork the effects by yourself calling LivePathEffectObject::fork_private_if_necessary,
804  *  use this method instead.
805  *  Returns true if one or more effects were forked; returns false if nothing was done.
806  */
807 bool sp_lpe_item_fork_path_effects_if_necessary(SPLPEItem *lpeitem, unsigned int nr_of_allowed_users)
809     bool forked = false;
811     if ( sp_lpe_item_has_path_effect(lpeitem) ) {
812         // If one of the path effects is used by 2 or more items, fork it
813         // so that each object has its own independent copy of the effect.
814         // Note: replacing path effects messes up the path effect list
816         // Clones of the LPEItem will increase the refcount of the lpeobjects.
817         // Therefore, nr_of_allowed_users should be increased with the number of clones (i.e. refs to the lpeitem)
818         nr_of_allowed_users += SP_OBJECT(lpeitem)->hrefcount;
820         std::vector<LivePathEffectObject const *> old_lpeobjs, new_lpeobjs;
821         PathEffectList effect_list =  sp_lpe_item_get_effect_list(lpeitem);
822         for (PathEffectList::iterator it = effect_list.begin(); it != effect_list.end(); it++)
823         {
824             LivePathEffectObject *lpeobj = (*it)->lpeobject;
825             if (lpeobj) {
826                 LivePathEffectObject *forked_lpeobj = lpeobj->fork_private_if_necessary(nr_of_allowed_users);
827                 if (forked_lpeobj != lpeobj) {
828                     forked = true;
829                     old_lpeobjs.push_back(lpeobj);
830                     new_lpeobjs.push_back(forked_lpeobj);
831                 }
832             }
833         }
835         if (forked) {
836             lpeitem->replacePathEffects(old_lpeobjs, new_lpeobjs);
837         }
838     }
840     return forked;
843 // Enable or disable the path effects of the item.
844 // The counter allows nested calls
845 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable)
847     if (enable) {
848         lpeitem->path_effects_enabled++;
849     }
850     else {
851         lpeitem->path_effects_enabled--;
852     }
855 // Are the path effects enabled on this item ?
856 bool sp_lpe_item_path_effects_enabled(SPLPEItem *lpeitem)
858     return lpeitem->path_effects_enabled > 0;
861 /*
862   Local Variables:
863   mode:c++
864   c-file-style:"stroustrup"
865   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
866   indent-tabs-mode:nil
867   fill-column:99
868   End:
869 */
870 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :