Code

Pot and Dutch translation update
[inkscape.git] / src / sp-lpe-item.cpp
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 "shape-editor.h"
38 #include <algorithm>
40 /* LPEItem base class */
42 static void sp_lpe_item_class_init(SPLPEItemClass *klass);
43 static void sp_lpe_item_init(SPLPEItem *lpe_item);
44 static void sp_lpe_item_finalize(GObject *object);
46 static void sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
47 static void sp_lpe_item_release(SPObject *object);
48 static void sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value);
49 static void sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags);
50 static void sp_lpe_item_modified (SPObject *object, unsigned int flags);
51 static Inkscape::XML::Node *sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags);
53 static void sp_lpe_item_child_added (SPObject * object, Inkscape::XML::Node * child, Inkscape::XML::Node * ref);
54 static void sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child);
56 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable);
58 static void lpeobject_ref_modified(SPObject *href, guint flags, SPLPEItem *lpeitem);
60 static void sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem);
61 static void sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem);
62 typedef std::list<std::string> HRefList;
63 static std::string patheffectlist_write_svg(PathEffectList const & list);
64 static std::string hreflist_write_svg(HRefList const & list);
66 static SPItemClass *parent_class;
68 GType
69 sp_lpe_item_get_type()
70 {
71     static GType lpe_item_type = 0;
73     if (!lpe_item_type) {
74         GTypeInfo lpe_item_info = {
75             sizeof(SPLPEItemClass),
76             NULL, NULL,
77             (GClassInitFunc) sp_lpe_item_class_init,
78             NULL, NULL,
79             sizeof(SPLPEItem),
80             16,
81             (GInstanceInitFunc) sp_lpe_item_init,
82             NULL,    /* value_table */
83         };
84         lpe_item_type = g_type_register_static(SP_TYPE_ITEM, "SPLPEItem", &lpe_item_info, (GTypeFlags)0);
85     }
86     return lpe_item_type;
87 }
89 static void
90 sp_lpe_item_class_init(SPLPEItemClass *klass)
91 {
92     GObjectClass *gobject_class;
93     SPObjectClass *sp_object_class;
95     gobject_class = (GObjectClass *) klass;
96     sp_object_class = (SPObjectClass *) klass;
97     parent_class = (SPItemClass *)g_type_class_peek_parent (klass);
99     gobject_class->finalize = sp_lpe_item_finalize;
101     sp_object_class->build = sp_lpe_item_build;
102     sp_object_class->release = sp_lpe_item_release;
103     sp_object_class->set = sp_lpe_item_set;
104     sp_object_class->update = sp_lpe_item_update;
105     sp_object_class->modified = sp_lpe_item_modified;
106     sp_object_class->write = sp_lpe_item_write;
107     sp_object_class->child_added = sp_lpe_item_child_added;
108     sp_object_class->remove_child = sp_lpe_item_remove_child;
110     klass->update_patheffect = NULL;
113 static void
114 sp_lpe_item_init(SPLPEItem *lpeitem)
116     lpeitem->path_effects_enabled = 1;
118     lpeitem->path_effect_list = new PathEffectList();
119     lpeitem->current_path_effect = NULL;
121     lpeitem->lpe_modified_connection_list = new std::list<sigc::connection>();
124 static void
125 sp_lpe_item_finalize(GObject *object)
127     if (((GObjectClass *) (parent_class))->finalize) {
128         (* ((GObjectClass *) (parent_class))->finalize)(object);
129     }
132 /**
133  * Reads the Inkscape::XML::Node, and initializes SPLPEItem variables.  For this to get called,
134  * our name must be associated with a repr via "sp_object_type_register".  Best done through
135  * sp-object-repr.cpp's repr_name_entries array.
136  */
137 static void
138 sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
140     sp_object_read_attr(object, "inkscape:path-effect");
142     if (((SPObjectClass *) parent_class)->build) {
143         ((SPObjectClass *) parent_class)->build(object, document, repr);
144     }
147 /**
148  * Drops any allocated memory.
149  */
150 static void
151 sp_lpe_item_release(SPObject *object)
153     SPLPEItem *lpeitem = (SPLPEItem *) object;
155     // disconnect all modified listeners:
156     for (std::list<sigc::connection>::iterator mod_it = lpeitem->lpe_modified_connection_list->begin();
157          mod_it != lpeitem->lpe_modified_connection_list->end(); ++mod_it)
158     {
159         mod_it->disconnect();
160     }
161     delete lpeitem->lpe_modified_connection_list;
162     lpeitem->lpe_modified_connection_list = NULL;
164     PathEffectList::iterator it = lpeitem->path_effect_list->begin();
165     while ( it != lpeitem->path_effect_list->end() ) {
166         // unlink and delete all references in the list
167         (*it)->unlink();
168         delete *it;
169         it = lpeitem->path_effect_list->erase(it);
170     }
171     // delete the list itself
172     delete lpeitem->path_effect_list;
173     lpeitem->path_effect_list = NULL;
175     if (((SPObjectClass *) parent_class)->release)
176         ((SPObjectClass *) parent_class)->release(object);
179 /**
180  * Sets a specific value in the SPLPEItem.
181  */
182 static void
183 sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value)
185     SPLPEItem *lpeitem = (SPLPEItem *) object;
187     switch (key) {
188         case SP_ATTR_INKSCAPE_PATH_EFFECT:
189             {
190                 lpeitem->current_path_effect = NULL;
192                 // Disable the path effects while populating the LPE list
193                  sp_lpe_item_enable_path_effects(lpeitem, false);
195                 // disconnect all modified listeners:
196                 for ( std::list<sigc::connection>::iterator mod_it = lpeitem->lpe_modified_connection_list->begin();
197                       mod_it != lpeitem->lpe_modified_connection_list->end();
198                       ++mod_it)
199                 {
200                     mod_it->disconnect();
201                 }
202                 lpeitem->lpe_modified_connection_list->clear();
203                 // Clear the path effect list
204                 PathEffectList::iterator it = lpeitem->path_effect_list->begin();
205                 while ( it != lpeitem->path_effect_list->end() )
206                 {
207                     (*it)->unlink();
208                     delete *it;
209                     it = lpeitem->path_effect_list->erase(it);
210                 }
212                 // Parse the contents of "value" to rebuild the path effect reference list
213                 if ( value ) {
214                     std::istringstream iss(value);
215                     std::string href;
216                     while (std::getline(iss, href, ';'))
217                     {
218                         Inkscape::LivePathEffect::LPEObjectReference *path_effect_ref = new Inkscape::LivePathEffect::LPEObjectReference(object);
219                         try {
220                             path_effect_ref->link(href.c_str());
221                         } catch (Inkscape::BadURIException e) {
222                             g_warning("BadURIException when trying to find LPE: %s", e.what());
223                             path_effect_ref->unlink();
224                             delete path_effect_ref;
225                             path_effect_ref = NULL;
226                         }
228                         lpeitem->path_effect_list->push_back(path_effect_ref);
229                         if ( path_effect_ref->lpeobject && path_effect_ref->lpeobject->get_lpe() ) {
230                             // connect modified-listener
231                             lpeitem->lpe_modified_connection_list->push_back(
232                                                 path_effect_ref->lpeobject->connectModified(sigc::bind(sigc::ptr_fun(&lpeobject_ref_modified), lpeitem)) );
233                         } else {
234                             // something has gone wrong in finding the right patheffect.
235                             g_warning("Unknown LPE type specified, LPE stack effectively disabled");
236                             // keep the effect in the lpestack, so the whole stack is effectively disabled but maintained
237                         }
238                     }
239                 }
241                 sp_lpe_item_enable_path_effects(lpeitem, true);
242             }
243             break;
244         default:
245             if (((SPObjectClass *) parent_class)->set) {
246                 ((SPObjectClass *) parent_class)->set(object, key, value);
247             }
248             break;
249     }
252 /**
253  * Receives update notifications.
254  */
255 static void
256 sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags)
258     if (((SPObjectClass *) parent_class)->update) {
259         ((SPObjectClass *) parent_class)->update(object, ctx, flags);
260     }
262     // update the helperpaths of all LPEs applied to the item
263     // TODO: re-add for the new node tool
266 /**
267  * Sets modified flag for all sub-item views.
268  */
269 static void
270 sp_lpe_item_modified (SPObject *object, unsigned int flags)
272     if (SP_IS_GROUP(object) && (flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_USER_MODIFIED_FLAG_B)) {
273         sp_lpe_item_update_patheffect(SP_LPE_ITEM(object), true, true);
274     }
276     if (((SPObjectClass *) (parent_class))->modified) {
277         (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
278     }
281 /**
282  * Writes its settings to an incoming repr object, if any.
283  */
284 static Inkscape::XML::Node *
285 sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
287     SPLPEItem *lpeitem = (SPLPEItem *) object;
289     if (flags & SP_OBJECT_WRITE_EXT) {
290         if ( sp_lpe_item_has_path_effect(lpeitem) ) {
291             std::string href = patheffectlist_write_svg(*lpeitem->path_effect_list);
292             repr->setAttribute("inkscape:path-effect", href.c_str());
293         } else {
294             repr->setAttribute("inkscape:path-effect", NULL);
295         }
296     }
298     if (((SPObjectClass *)(parent_class))->write) {
299         ((SPObjectClass *)(parent_class))->write(object, xml_doc, repr, flags);
300     }
302     return repr;
305 /**
306  * returns true when LPE was successful.
307  */
308 bool sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve) {
309     if (!lpeitem) return false;
310     if (!curve) return false;
312     if (sp_lpe_item_has_path_effect(lpeitem) && sp_lpe_item_path_effects_enabled(lpeitem)) {
313         for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
314         {
315             LivePathEffectObject *lpeobj = (*it)->lpeobject;
316             if (!lpeobj) {
317                 /** \todo Investigate the cause of this.
318                  * 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.
319                  */
320                 g_warning("sp_lpe_item_perform_path_effect - NULL lpeobj in list!");
321                 return false;
322             }
323             Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
324             if (!lpe) {
325                 /** \todo Investigate the cause of this.
326                  * Not sure, but I think this can happen when an unknown effect type is specified...
327                  */
328                 g_warning("sp_lpe_item_perform_path_effect - lpeobj with invalid lpe in the stack!");
329                 return false;
330             }
332             if (lpe->isVisible()) {
333                 if (lpe->acceptsNumClicks() > 0 && !lpe->isReady()) {
334                     // if the effect expects mouse input before being applied and the input is not finished
335                     // yet, we don't alter the path
336                     return false;
337                 }
339                 // Groups have their doBeforeEffect called elsewhere
340                 if (!SP_IS_GROUP(lpeitem)) {
341                     lpe->doBeforeEffect(lpeitem);
342                 }
344                 try {
345                     lpe->doEffect(curve);
346                 }
347                 catch (std::exception & e) {
348                     g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what());
349                     if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->messageStack()) {
350                         SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
351                                         _("An exception occurred during execution of the Path Effect.") );
352                     }
353                     return false;
354                 }
355             }
356         }
357     }
359     return true;
362 /**
363  * Calls any registered handlers for the update_patheffect action
364  */
365 void
366 sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write)
368 #ifdef SHAPE_VERBOSE
369     g_message("sp_lpe_item_update_patheffect: %p\n", lpeitem);
370 #endif
371     g_return_if_fail (lpeitem != NULL);
372     g_return_if_fail (SP_IS_LPE_ITEM (lpeitem));
374     if (!sp_lpe_item_path_effects_enabled(lpeitem))
375         return;
377     // TODO: hack! this will be removed when path length measuring is reimplemented in a better way
378     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
379     std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
380     for (i = lpelist.begin(); i != lpelist.end(); ++i) {
381         if ((*i)->lpeobject) {
382             Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe();
383             if (dynamic_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)) {
384                 if (!lpe->isVisible()) {
385                     // we manually disable text for LPEPathLength
386                     // use static_cast, because we already checked for the right type above
387                     static_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)->hideCanvasText();
388                 }
389             }
390         }
391     }
393     SPLPEItem *top;
395     if (wholetree) {
396         SPObject *prev_parent = lpeitem;
397         SPObject *parent = prev_parent->parent;
398         while (parent && SP_IS_LPE_ITEM(parent) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent))) {
399             prev_parent = parent;
400             parent = prev_parent->parent;
401         }
402         top = SP_LPE_ITEM(prev_parent);
403     }
404     else {
405         top = lpeitem;
406     }
408     if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect) {
409         SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect (top, write);
410     }
413 /**
414  * Gets called when any of the lpestack's lpeobject repr contents change: i.e. parameter change in any of the stacked LPEs
415  */
416 static void
417 lpeobject_ref_modified(SPObject */*href*/, guint /*flags*/, SPLPEItem *lpeitem)
419 #ifdef SHAPE_VERBOSE
420     g_message("lpeobject_ref_modified");
421 #endif
422     sp_lpe_item_update_patheffect (lpeitem, true, true);
425 static void
426 sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem)
428     if (SP_IS_GROUP(lpeitem)) {
429         GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
430         for ( GSList const *iter = item_list; iter; iter = iter->next ) {
431             SPObject *subitem = static_cast<SPObject *>(iter->data);
432             if (SP_IS_LPE_ITEM(subitem)) {
433                 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(subitem));
434             }
435         }
436     }
437     else if (SP_IS_PATH(lpeitem)) {
438         Inkscape::XML::Node *pathrepr = SP_OBJECT_REPR(lpeitem);
439         if ( !pathrepr->attribute("inkscape:original-d") ) {
440             pathrepr->setAttribute("inkscape:original-d", pathrepr->attribute("d"));
441         }
442     }
445 static void
446 sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem)
448     if (SP_IS_GROUP(lpeitem)) {
449         GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
450         for ( GSList const *iter = item_list; iter; iter = iter->next ) {
451             SPObject *subitem = static_cast<SPObject *>(iter->data);
452             if (SP_IS_LPE_ITEM(subitem)) {
453                 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(subitem));
454             }
455         }
456     }
457     else if (SP_IS_PATH(lpeitem)) {
458         Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem);
459         if (!sp_lpe_item_has_path_effect_recursive(lpeitem)
460                 && repr->attribute("inkscape:original-d")) {
461             repr->setAttribute("d", repr->attribute("inkscape:original-d"));
462             repr->setAttribute("inkscape:original-d", NULL);
463         }
464         else {
465             sp_lpe_item_update_patheffect(lpeitem, true, true);
466         }
467     }
470 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset)
472     if (value) {
473         // Apply the path effects here because in the casse of a group, lpe->resetDefaults
474         // needs that all the subitems have their effects applied
475         sp_lpe_item_update_patheffect(lpeitem, false, true);
477         // Disable the path effects while preparing the new lpe
478         sp_lpe_item_enable_path_effects(lpeitem, false);
480         // Add the new reference to the list of LPE references
481         HRefList hreflist;
482         for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
483         {
484             hreflist.push_back( std::string((*it)->lpeobject_href) );
485         }
486         hreflist.push_back( std::string(value) );
487         std::string hrefs = hreflist_write_svg(hreflist);
489         SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", hrefs.c_str());
491         // make sure there is an original-d for paths!!!
492         sp_lpe_item_create_original_path_recursive(lpeitem);
494         LivePathEffectObject *lpeobj = lpeitem->path_effect_list->back()->lpeobject;
495         if (lpeobj && lpeobj->get_lpe()) {
496             Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
497             // Ask the path effect to reset itself if it doesn't have parameters yet
498             if (reset) {
499                 // has to be called when all the subitems have their lpes applied
500                 lpe->resetDefaults(lpeitem);
501             }
503             // perform this once when the effect is applied
504             lpe->doOnApply(SP_LPE_ITEM(lpeitem));
506             // indicate that all necessary preparations are done and the effect can be performed
507             lpe->setReady();
508         }
510         //Enable the path effects now that everything is ready to apply the new path effect
511         sp_lpe_item_enable_path_effects(lpeitem, true);
513         // Apply the path effect
514         sp_lpe_item_update_patheffect(lpeitem, true, true);
515     }
518 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj)
520     const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
521     gchar *hrefstr = g_strdup_printf("#%s", repr_id);
522     sp_lpe_item_add_path_effect(lpeitem, hrefstr, false);
523     g_free(hrefstr);
526 void sp_lpe_item_remove_current_path_effect(SPLPEItem *lpeitem, bool keep_paths)
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     new_list.remove(lperef); //current lpe ref is always our 'own' pointer from the path_effect_list
534     std::string r = patheffectlist_write_svg(new_list);
536     if (!r.empty()) {
537         SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
538     } else {
539         SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
540     }
542     if (!keep_paths) {
543         sp_lpe_item_cleanup_original_path_recursive(lpeitem);
544     }
547 void sp_lpe_item_remove_all_path_effects(SPLPEItem *lpeitem, bool keep_paths)
549     SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
551     if (!keep_paths) {
552         sp_lpe_item_cleanup_original_path_recursive(lpeitem);
553     }
556 void sp_lpe_item_down_current_path_effect(SPLPEItem *lpeitem)
558     Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
559     if (!lperef)
560         return;
562     PathEffectList new_list = *lpeitem->path_effect_list;
563     PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
564     if (cur_it != new_list.end()) {
565         PathEffectList::iterator down_it = cur_it;
566         down_it++;
567         if (down_it != new_list.end()) { // perhaps current effect is already last effect
568             std::iter_swap(cur_it, down_it);
569         }
570     }
571     std::string r = patheffectlist_write_svg(new_list);
572     SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
574     sp_lpe_item_cleanup_original_path_recursive(lpeitem);
577 void sp_lpe_item_up_current_path_effect(SPLPEItem *lpeitem)
579     Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
580     if (!lperef)
581         return;
583     PathEffectList new_list = *lpeitem->path_effect_list;
584     PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
585     if (cur_it != new_list.end() && cur_it != new_list.begin()) {
586         PathEffectList::iterator up_it = cur_it;
587         up_it--;
588         std::iter_swap(cur_it, up_it);
589     }
590     std::string r = patheffectlist_write_svg(new_list);
592     SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
594     sp_lpe_item_cleanup_original_path_recursive(lpeitem);
597 /** used for shapes so they can see if they should also disable shape calculation and read from d= */
598 bool sp_lpe_item_has_broken_path_effect(SPLPEItem *lpeitem)
600     if (lpeitem->path_effect_list->empty())
601         return false;
603     // go through the list; if some are unknown or invalid, return true
604     PathEffectList effect_list =  sp_lpe_item_get_effect_list(lpeitem);
605     for (PathEffectList::iterator it = effect_list.begin(); it != effect_list.end(); it++)
606     {
607         LivePathEffectObject *lpeobj = (*it)->lpeobject;
608         if (!lpeobj || !lpeobj->get_lpe())
609             return true;
610     }
612     return false;
616 bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem)
618     if (lpeitem->path_effect_list->empty())
619         return false;
621     // go through the list; if some are unknown or invalid, we are not an LPE item!
622     PathEffectList effect_list =  sp_lpe_item_get_effect_list(lpeitem);
623     for (PathEffectList::iterator it = effect_list.begin(); it != effect_list.end(); it++)
624     {
625         LivePathEffectObject *lpeobj = (*it)->lpeobject;
626         if (!lpeobj || !lpeobj->get_lpe())
627             return false;
628     }
630     return true;
633 bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem)
635     SPObject *parent = lpeitem->parent;
636     if (parent && SP_IS_LPE_ITEM(parent)) {
637         return sp_lpe_item_has_path_effect(lpeitem) || sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent));
638     }
639     else {
640         return sp_lpe_item_has_path_effect(lpeitem);
641     }
644 Inkscape::LivePathEffect::Effect*
645 sp_lpe_item_has_path_effect_of_type(SPLPEItem *lpeitem, int type)
647     std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
648     for (i = lpeitem->path_effect_list->begin(); i != lpeitem->path_effect_list->end(); ++i) {
649         Inkscape::LivePathEffect::Effect* lpe = (*i)->lpeobject->get_lpe();
650         if (lpe && (lpe->effectType() == type)) {
651             return lpe;
652         }
653     }
654     return NULL;
657 /* Return false if the item is not a path or already has a shape applied */
658 bool sp_lpe_item_can_accept_freehand_shape(SPLPEItem *lpeitem)
660     if (!SP_IS_PATH(lpeitem))
661         return false;
663     if (sp_lpe_item_has_path_effect_of_type(lpeitem, Inkscape::LivePathEffect::FREEHAND_SHAPE))
664         return false;
666     return true;
669 void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt)
671     Inkscape::LivePathEffect::LPEObjectReference *lperef = sp_lpe_item_get_current_lpereference(lpeitem);
672     if (lperef && lperef->lpeobject && lperef->lpeobject->get_lpe()) {
673         lperef->lpeobject->get_lpe()->editNextParamOncanvas(SP_ITEM(lpeitem), dt);
674     }
677 static void
678 sp_lpe_item_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
680     if (((SPObjectClass *) (parent_class))->child_added)
681         (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref);
683     if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
684         SPObject *ochild = sp_object_get_child_by_repr(object, child);
685         if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
686             sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(ochild));
687         }
688     }
691 static void
692 sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child)
694     if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
695         SPObject *ochild = sp_object_get_child_by_repr(object, child);
696         if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
697             sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(ochild));
698         }
699     }
701     if (((SPObjectClass *) (parent_class))->remove_child)
702         (* ((SPObjectClass *) (parent_class))->remove_child) (object, child);
705 static std::string patheffectlist_write_svg(PathEffectList const & list)
707     HRefList hreflist;
708     for (PathEffectList::const_iterator it = list.begin(); it != list.end(); ++it)
709     {
710         hreflist.push_back( std::string((*it)->lpeobject_href) );
711     }
712     return hreflist_write_svg(hreflist);
715 /**
716  *  THE function that should be used to generate any patheffectlist string.
717  * one of the methods to change the effect list:
718  *  - create temporary href list
719  *  - populate the templist with the effects from the old list that you want to have and their order
720  *  - call this function with temp list as param
721  */
722 static std::string hreflist_write_svg(HRefList const & list)
724     std::string r;
725     bool semicolon_first = false;
726     for (HRefList::const_iterator it = list.begin(); it != list.end(); ++it)
727     {
728         if (semicolon_first) {
729             r += ';';
730         }
731         semicolon_first = true;
733         r += (*it);
734     }
735     return r;
738 // Return a copy of the effect list
739 PathEffectList sp_lpe_item_get_effect_list(SPLPEItem *lpeitem)
741     return *lpeitem->path_effect_list;
744 Inkscape::LivePathEffect::LPEObjectReference* sp_lpe_item_get_current_lpereference(SPLPEItem *lpeitem)
746     if (!lpeitem->current_path_effect && !lpeitem->path_effect_list->empty()) {
747         sp_lpe_item_set_current_path_effect(lpeitem, lpeitem->path_effect_list->back());
748     }
750     return lpeitem->current_path_effect;
753 Inkscape::LivePathEffect::Effect* sp_lpe_item_get_current_lpe(SPLPEItem *lpeitem)
755     Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
757     if (lperef && lperef->lpeobject)
758         return lperef->lpeobject->get_lpe();
759     else
760         return NULL;
763 bool sp_lpe_item_set_current_path_effect(SPLPEItem *lpeitem, Inkscape::LivePathEffect::LPEObjectReference* lperef)
765     for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); it++) {
766         if ((*it)->lpeobject_repr == lperef->lpeobject_repr) {
767             lpeitem->current_path_effect = (*it);  // current_path_effect should always be a pointer from the path_effect_list !
768             return true;
769         }
770     }
772     return false;
775 /**
776  * Writes a new "inkscape:path-effect" string to xml, where the old_lpeobjects are substituted by the new ones.
777  *  Note that this method messes up the item's \c PathEffectList.
778  */
779 void SPLPEItem::replacePathEffects( std::vector<LivePathEffectObject const *> const old_lpeobjs,
780                                     std::vector<LivePathEffectObject const *> const new_lpeobjs )
782     HRefList hreflist;
783     for (PathEffectList::const_iterator it = this->path_effect_list->begin(); it != this->path_effect_list->end(); ++it)
784     {
785         LivePathEffectObject const * current_lpeobj = (*it)->lpeobject;
786         std::vector<LivePathEffectObject const *>::const_iterator found_it(std::find(old_lpeobjs.begin(), old_lpeobjs.end(), current_lpeobj));
787         if ( found_it != old_lpeobjs.end() ) {
788             std::vector<LivePathEffectObject const *>::difference_type found_index = std::distance (old_lpeobjs.begin(), found_it);
789             const gchar * repr_id = SP_OBJECT_REPR(new_lpeobjs[found_index])->attribute("id");
790             gchar *hrefstr = g_strdup_printf("#%s", repr_id);
791             hreflist.push_back( std::string(hrefstr) );
792             g_free(hrefstr);
793         }
794         else {
795             hreflist.push_back( std::string((*it)->lpeobject_href) );
796         }
797     }
798     std::string r = hreflist_write_svg(hreflist);
799     SP_OBJECT_REPR(this)->setAttribute("inkscape:path-effect", r.c_str());
802 /**
803  *  Check all effects in the stack if they are used by other items, and fork them if so.
804  *  It is not recommended to fork the effects by yourself calling LivePathEffectObject::fork_private_if_necessary,
805  *  use this method instead.
806  *  Returns true if one or more effects were forked; returns false if nothing was done.
807  */
808 bool sp_lpe_item_fork_path_effects_if_necessary(SPLPEItem *lpeitem, unsigned int nr_of_allowed_users)
810     bool forked = false;
812     if ( sp_lpe_item_has_path_effect(lpeitem) ) {
813         // If one of the path effects is used by 2 or more items, fork it
814         // so that each object has its own independent copy of the effect.
815         // Note: replacing path effects messes up the path effect list
817         // Clones of the LPEItem will increase the refcount of the lpeobjects.
818         // Therefore, nr_of_allowed_users should be increased with the number of clones (i.e. refs to the lpeitem)
819         nr_of_allowed_users += SP_OBJECT(lpeitem)->hrefcount;
821         std::vector<LivePathEffectObject const *> old_lpeobjs, new_lpeobjs;
822         PathEffectList effect_list =  sp_lpe_item_get_effect_list(lpeitem);
823         for (PathEffectList::iterator it = effect_list.begin(); it != effect_list.end(); it++)
824         {
825             LivePathEffectObject *lpeobj = (*it)->lpeobject;
826             if (lpeobj) {
827                 LivePathEffectObject *forked_lpeobj = lpeobj->fork_private_if_necessary(nr_of_allowed_users);
828                 if (forked_lpeobj != lpeobj) {
829                     forked = true;
830                     old_lpeobjs.push_back(lpeobj);
831                     new_lpeobjs.push_back(forked_lpeobj);
832                 }
833             }
834         }
836         if (forked) {
837             lpeitem->replacePathEffects(old_lpeobjs, new_lpeobjs);
838         }
839     }
841     return forked;
844 // Enable or disable the path effects of the item.
845 // The counter allows nested calls
846 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable)
848     if (enable) {
849         lpeitem->path_effects_enabled++;
850     }
851     else {
852         lpeitem->path_effects_enabled--;
853     }
856 // Are the path effects enabled on this item ?
857 bool sp_lpe_item_path_effects_enabled(SPLPEItem *lpeitem)
859     return lpeitem->path_effects_enabled > 0;
862 /*
863   Local Variables:
864   mode:c++
865   c-file-style:"stroustrup"
866   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
867   indent-tabs-mode:nil
868   fill-column:99
869   End:
870 */
871 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :