Code

Hide canvas text, too, when lpe-path-length is invisible
[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 "node-context.h"
37 #include "shape-editor.h"
39 #include <algorithm>
41 /* LPEItem base class */
43 static void sp_lpe_item_class_init(SPLPEItemClass *klass);
44 static void sp_lpe_item_init(SPLPEItem *lpe_item);
45 static void sp_lpe_item_finalize(GObject *object);
47 static void sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
48 static void sp_lpe_item_release(SPObject *object);
49 static void sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value);
50 static void sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags);
51 static void sp_lpe_item_modified (SPObject *object, unsigned int flags);
52 static Inkscape::XML::Node *sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags);
54 static void sp_lpe_item_child_added (SPObject * object, Inkscape::XML::Node * child, Inkscape::XML::Node * ref);
55 static void sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child);
57 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable);
59 static void lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem);
60 static void lpeobject_ref_modified(SPObject *href, guint flags, SPLPEItem *lpeitem);
62 static void sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem);
63 static void sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem);
64 typedef std::list<std::string> HRefList;
65 static std::string patheffectlist_write_svg(PathEffectList const & list);
66 static std::string hreflist_write_svg(HRefList const & list);
68 static SPItemClass *parent_class;
70 GType
71 sp_lpe_item_get_type()
72 {
73     static GType lpe_item_type = 0;
75     if (!lpe_item_type) {
76         GTypeInfo lpe_item_info = {
77             sizeof(SPLPEItemClass),
78             NULL, NULL,
79             (GClassInitFunc) sp_lpe_item_class_init,
80             NULL, NULL,
81             sizeof(SPLPEItem),
82             16,
83             (GInstanceInitFunc) sp_lpe_item_init,
84             NULL,    /* value_table */
85         };
86         lpe_item_type = g_type_register_static(SP_TYPE_ITEM, "SPLPEItem", &lpe_item_info, (GTypeFlags)0);
87     }
88     return lpe_item_type;
89 }
91 static void
92 sp_lpe_item_class_init(SPLPEItemClass *klass)
93 {
94     GObjectClass *gobject_class;
95     SPObjectClass *sp_object_class;
97     gobject_class = (GObjectClass *) klass;
98     sp_object_class = (SPObjectClass *) klass;
99     parent_class = (SPItemClass *)g_type_class_peek_parent (klass);
101     gobject_class->finalize = sp_lpe_item_finalize;
103     sp_object_class->build = sp_lpe_item_build;
104     sp_object_class->release = sp_lpe_item_release;
105     sp_object_class->set = sp_lpe_item_set;
106     sp_object_class->update = sp_lpe_item_update;
107     sp_object_class->modified = sp_lpe_item_modified;
108     sp_object_class->write = sp_lpe_item_write;
109     sp_object_class->child_added = sp_lpe_item_child_added;
110     sp_object_class->remove_child = sp_lpe_item_remove_child;
112     klass->update_patheffect = NULL;
115 static void
116 sp_lpe_item_init(SPLPEItem *lpeitem)
118     lpeitem->path_effects_enabled = 1;
120     lpeitem->path_effect_list = new PathEffectList();
121     lpeitem->current_path_effect = NULL;
123     new (&lpeitem->lpe_modified_connection) sigc::connection();
126 static void
127 sp_lpe_item_finalize(GObject *object)
129     if (((GObjectClass *) (parent_class))->finalize) {
130         (* ((GObjectClass *) (parent_class))->finalize)(object);
131     }
134 /**
135  * Reads the Inkscape::XML::Node, and initializes SPLPEItem variables.  For this to get called,
136  * our name must be associated with a repr via "sp_object_type_register".  Best done through
137  * sp-object-repr.cpp's repr_name_entries array.
138  */
139 static void
140 sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
142     sp_object_read_attr(object, "inkscape:path-effect");
144     if (((SPObjectClass *) parent_class)->build) {
145         ((SPObjectClass *) parent_class)->build(object, document, repr);
146     }
149 /**
150  * Drops any allocated memory.
151  */
152 static void
153 sp_lpe_item_release(SPObject *object)
155     SPLPEItem *lpeitem = (SPLPEItem *) object;
157     lpeitem->lpe_modified_connection.disconnect();
158     lpeitem->lpe_modified_connection.~connection();
160     PathEffectList::iterator it = lpeitem->path_effect_list->begin();
161     while ( it != lpeitem->path_effect_list->end() ) {
162         // unlink and delete all references in the list
163         (*it)->unlink();
164         delete *it;
165         it = lpeitem->path_effect_list->erase(it);
166     }
167     // delete the list itself
168     delete SP_LPE_ITEM(object)->path_effect_list;
170     if (((SPObjectClass *) parent_class)->release)
171         ((SPObjectClass *) parent_class)->release(object);
174 /**
175  * Sets a specific value in the SPLPEItem.
176  */
177 static void
178 sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value)
180     SPLPEItem *lpeitem = (SPLPEItem *) object;
182     switch (key) {
183         case SP_ATTR_INKSCAPE_PATH_EFFECT:
184             {
185                 lpeitem->current_path_effect = NULL;
187                 // Disable the path effects while populating the LPE list
188                  sp_lpe_item_enable_path_effects(lpeitem, false);
190                 // Clear the path effect list
191                 PathEffectList::iterator it = lpeitem->path_effect_list->begin();
192                 while ( it != lpeitem->path_effect_list->end() )
193                 {
194                     (*it)->unlink();
195                     delete *it;
196                     it = lpeitem->path_effect_list->erase(it);
197                 }
199                 // Parse the contents of "value" to rebuild the path effect reference list
200                 if ( value ) {
201                     std::istringstream iss(value);
202                     std::string href;
203                     while (std::getline(iss, href, ';'))
204                     {
205                         Inkscape::LivePathEffect::LPEObjectReference *path_effect_ref;
206                         path_effect_ref = new Inkscape::LivePathEffect::LPEObjectReference(SP_OBJECT(lpeitem));
207                         path_effect_ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(lpeobject_ref_changed), SP_LPE_ITEM(object)));
208                         // Now do the attaching, which emits the changed signal.
209                         // Fixme, it should not do this changed signal and updating before all effects are added to the path_effect_list
210                         try {
211                             path_effect_ref->link(href.c_str());
212                         } catch (Inkscape::BadURIException &e) {
213                             g_warning("BadURIException: %s", e.what());
214                             path_effect_ref->unlink();
215                             delete path_effect_ref;
216                             path_effect_ref = NULL;
217                         }
219                         if (path_effect_ref) {
220                             lpeitem->path_effect_list->push_back(path_effect_ref);
221                         }
222                     }
223                 }
225                 sp_lpe_item_enable_path_effects(lpeitem, true);
226             }
227             break;
228         default:
229             if (((SPObjectClass *) parent_class)->set) {
230                 ((SPObjectClass *) parent_class)->set(object, key, value);
231             }
232             break;
233     }
236 /**
237  * Receives update notifications.
238  */
239 static void
240 sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags)
242     if (((SPObjectClass *) parent_class)->update) {
243         ((SPObjectClass *) parent_class)->update(object, ctx, flags);
244     }
246     // update the helperpaths of all LPEs applied to the item
247     // TODO: is there a more canonical place for this, since we don't have instant access to the item's nodepath?
248     // FIXME: this is called multiple (at least 3) times; how can we avoid this?
250     // FIXME: ditch inkscape_active_event_context()
251     SPEventContext *ec = inkscape_active_event_context();
252     if (!SP_IS_NODE_CONTEXT(ec)) return;
253     SPNodeContext *nc = SP_NODE_CONTEXT(ec);
254     ShapeEditor *sh = nc->shape_editor;
255     g_assert(sh);
256     if (!sh->has_nodepath()) return;
258     Inkscape::NodePath::Path *np = sh->get_nodepath();
259     sp_nodepath_update_helperpaths(np);
262 /**
263  * Sets modified flag for all sub-item views.
264  */
265 static void
266 sp_lpe_item_modified (SPObject *object, unsigned int flags)
268     if (SP_IS_GROUP(object) && (flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_USER_MODIFIED_FLAG_B)) {
269         sp_lpe_item_update_patheffect(SP_LPE_ITEM(object), true, true);
270     }
272     if (((SPObjectClass *) (parent_class))->modified) {
273         (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
274     }
277 /**
278  * Writes its settings to an incoming repr object, if any.
279  */
280 static Inkscape::XML::Node *
281 sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
283     SPLPEItem *lpeitem = (SPLPEItem *) object;
285     if ( sp_lpe_item_has_path_effect(lpeitem) ) {
286         std::string href = patheffectlist_write_svg(*lpeitem->path_effect_list);
287         repr->setAttribute("inkscape:path-effect", href.c_str());
288     } else {
289         repr->setAttribute("inkscape:path-effect", NULL);
290     }
292     if (((SPObjectClass *)(parent_class))->write) {
293         ((SPObjectClass *)(parent_class))->write(object, xml_doc, repr, flags);
294     }
296     return repr;
299 void sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve) {
300     if (!lpeitem) return;
301     if (!curve) return;
303     if (sp_lpe_item_has_path_effect(lpeitem) && sp_lpe_item_path_effects_enabled(lpeitem)) {
304         for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
305         {
306             LivePathEffectObject *lpeobj = (*it)->lpeobject;
307             if (!lpeobj) {
308                 g_warning("sp_lpe_item_perform_path_effect - NULL lpeobj in list!");
309                 return;
310             }
311             if (!lpeobj->lpe) {
312                 g_warning("sp_lpe_item_perform_path_effect - lpeobj without lpe!");
313                 return;
314             }
316             Inkscape::LivePathEffect::Effect *lpe = lpeobj->lpe;
317             if (lpe->isVisible()) {
318                 if (lpe->acceptsNumClicks() > 0 && !lpe->isReady()) {
319                     // if the effect expects mouse input before being applied and the input is not finished
320                     // yet, we don't alter the path
321                     return;
322                 }
324                 // Groups have their doBeforeEffect called elsewhere
325                 if (!SP_IS_GROUP(lpeitem)) {
326                     lpe->doBeforeEffect(lpeitem);
327                 }
329                 try {
330                     lpe->doEffect(curve);
331                 }
332                 catch (std::exception & e) {
333                     g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what());
334                     SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
335                         _("An exception occurred during execution of the Path Effect.") );
336                 }
337             }
338         }
339     }
342 /**
343  * Calls any registered handlers for the update_patheffect action
344  */
345 void
346 sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write)
348 #ifdef SHAPE_VERBOSE
349     g_message("sp_lpe_item_update_patheffect: %p\n", lpeitem);
350 #endif
351     g_return_if_fail (lpeitem != NULL);
352     g_return_if_fail (SP_IS_LPE_ITEM (lpeitem));
354     if (!sp_lpe_item_path_effects_enabled(lpeitem))
355         return;
357     // TODO: hack! this will be removed when path length measuring is reimplemented in a better way
358     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
359     std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
360     for (i = lpelist.begin(); i != lpelist.end(); ++i) {
361         Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->lpe;
362         if (dynamic_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)) {
363             if (!lpe->isVisible()) {
364                 // we manually disable text for LPEPathLength
365                 dynamic_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)->hideCanvasText();
366             }
367         }
368     }
370     SPLPEItem *top;
372     if (wholetree) {
373         SPObject *prev_parent = lpeitem;
374         SPObject *parent = prev_parent->parent;
375         while (parent && SP_IS_LPE_ITEM(parent) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent))) {
376             prev_parent = parent;
377             parent = prev_parent->parent;
378         }
379         top = SP_LPE_ITEM(prev_parent);
380     }
381     else {
382         top = lpeitem;
383     }
385     if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect) {
386         SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect (top, write);
387     }
390 /**
391  * Gets called when (re)attached to another lpeobject.
392  */
393 static void
394 lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem)
396     if (old_ref) {
397         sp_signal_disconnect_by_data(old_ref, lpeitem);
398     }
399     if ( IS_LIVEPATHEFFECT(ref) && ref != lpeitem )
400     {
401         lpeitem->lpe_modified_connection.disconnect();
402         lpeitem->lpe_modified_connection = ref->connectModified(sigc::bind(sigc::ptr_fun(&lpeobject_ref_modified), lpeitem));
403         lpeobject_ref_modified(ref, 0, lpeitem);
404     }
407 /**
408  * Gets called when lpeobject repr contents change: i.e. parameter change.
409  */
410 static void
411 lpeobject_ref_modified(SPObject */*href*/, guint /*flags*/, SPLPEItem *lpeitem)
413     sp_lpe_item_update_patheffect (lpeitem, true, true);
416 static void
417 sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem)
419     if (SP_IS_GROUP(lpeitem)) {
420         GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
421         for ( GSList const *iter = item_list; iter; iter = iter->next ) {
422             SPObject *subitem = static_cast<SPObject *>(iter->data);
423             if (SP_IS_LPE_ITEM(subitem)) {
424                 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(subitem));
425             }
426         }
427     }
428     else if (SP_IS_PATH(lpeitem)) {
429         Inkscape::XML::Node *pathrepr = SP_OBJECT_REPR(lpeitem);
430         if ( !pathrepr->attribute("inkscape:original-d") ) {
431             pathrepr->setAttribute("inkscape:original-d", pathrepr->attribute("d"));
432         }
433     }
436 static void
437 sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem)
439     if (SP_IS_GROUP(lpeitem)) {
440         GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
441         for ( GSList const *iter = item_list; iter; iter = iter->next ) {
442             SPObject *subitem = static_cast<SPObject *>(iter->data);
443             if (SP_IS_LPE_ITEM(subitem)) {
444                 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(subitem));
445             }
446         }
447     }
448     else if (SP_IS_PATH(lpeitem)) {
449         Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem);
450         if (!sp_lpe_item_has_path_effect_recursive(lpeitem)
451                 && repr->attribute("inkscape:original-d")) {
452             repr->setAttribute("d", repr->attribute("inkscape:original-d"));
453             repr->setAttribute("inkscape:original-d", NULL);
454         }
455         else {
456             sp_lpe_item_update_patheffect(lpeitem, true, true);
457         }
458     }
461 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset)
463     if (value) {
464         // Apply the path effects here because in the casse of a group, lpe->resetDefaults
465         // needs that all the subitems have their effects applied
466         sp_lpe_item_update_patheffect(lpeitem, false, true);
468         // Disable the path effects while preparing the new lpe
469         sp_lpe_item_enable_path_effects(lpeitem, false);
471         // Add the new reference to the list of LPE references
472         HRefList hreflist;
473         for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
474         {
475             hreflist.push_back( std::string((*it)->lpeobject_href) );
476         }
477         hreflist.push_back( std::string(value) );
478         std::string hrefs = hreflist_write_svg(hreflist);
480         SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", hrefs.c_str());
482         // make sure there is an original-d for paths!!!
483         sp_lpe_item_create_original_path_recursive(lpeitem);
485         LivePathEffectObject *lpeobj = lpeitem->path_effect_list->back()->lpeobject;
486         if (lpeobj && lpeobj->lpe) {
487             Inkscape::LivePathEffect::Effect *lpe = lpeobj->lpe;
488             // Ask the path effect to reset itself if it doesn't have parameters yet
489             if (reset) {
490                 // has to be called when all the subitems have their lpes applied
491                 lpe->resetDefaults(lpeitem);
492             }
494             // perform this once when the effect is applied
495             lpe->doOnApply(SP_LPE_ITEM(lpeitem));
497             // indicate that all necessary preparations are done and the effect can be performed
498             lpe->setReady();
499         }
501         //Enable the path effects now that everything is ready to apply the new path effect
502         sp_lpe_item_enable_path_effects(lpeitem, true);
504         // Apply the path effect
505         sp_lpe_item_update_patheffect(lpeitem, true, true);
506     }
509 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj)
511     const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
512     gchar *hrefstr = g_strdup_printf("#%s", repr_id);
513     sp_lpe_item_add_path_effect(lpeitem, hrefstr, false);
514     g_free(hrefstr);
517 void sp_lpe_item_remove_current_path_effect(SPLPEItem *lpeitem, bool keep_paths)
519     Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
520     if (!lperef)
521         return;
523     PathEffectList new_list = *lpeitem->path_effect_list;
524     new_list.remove(lperef); //current lpe ref is always our 'own' pointer from the path_effect_list
525     std::string r = patheffectlist_write_svg(new_list);
527     SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
529     if (!keep_paths) {
530         sp_lpe_item_cleanup_original_path_recursive(lpeitem);
531     }
534 void sp_lpe_item_remove_all_path_effects(SPLPEItem *lpeitem, bool keep_paths)
536     SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
538     if (!keep_paths) {
539         sp_lpe_item_cleanup_original_path_recursive(lpeitem);
540     }
543 void sp_lpe_item_down_current_path_effect(SPLPEItem *lpeitem)
545     Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
546     if (!lperef)
547         return;
549     PathEffectList new_list = *lpeitem->path_effect_list;
550     PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
551     if (cur_it != new_list.end()) {
552         PathEffectList::iterator down_it = cur_it;
553         down_it++;
554         if (down_it != new_list.end()) { // perhaps current effect is already last effect
555             std::iter_swap(cur_it, down_it);
556         }
557     }
558     std::string r = patheffectlist_write_svg(new_list);
559     SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
561     sp_lpe_item_cleanup_original_path_recursive(lpeitem);
564 void sp_lpe_item_up_current_path_effect(SPLPEItem *lpeitem)
566     Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
567     if (!lperef)
568         return;
570     PathEffectList new_list = *lpeitem->path_effect_list;
571     PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
572     if (cur_it != new_list.end() && cur_it != new_list.begin()) {
573         PathEffectList::iterator up_it = cur_it;
574         up_it--;
575         std::iter_swap(cur_it, up_it);
576     }
577     std::string r = patheffectlist_write_svg(new_list);
579     SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
581     sp_lpe_item_cleanup_original_path_recursive(lpeitem);
585 bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem)
587     return !lpeitem->path_effect_list->empty();
590 bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem)
592     SPObject *parent = lpeitem->parent;
593     if (parent && SP_IS_LPE_ITEM(parent)) {
594         return sp_lpe_item_has_path_effect(lpeitem) || sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent));
595     }
596     else {
597         return sp_lpe_item_has_path_effect(lpeitem);
598     }
601 Inkscape::LivePathEffect::Effect*
602 sp_lpe_item_has_path_effect_of_type(SPLPEItem *lpeitem, int type)
604     std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
605     for (i = lpeitem->path_effect_list->begin(); i != lpeitem->path_effect_list->end(); ++i) {
606         if ((*i)->lpeobject->lpe->effectType() == type) {
607             return (*i)->lpeobject->lpe;
608         }
609     }
610     return NULL;
613 /* Return false if the item is not a path or already has a shape applied */
614 bool sp_lpe_item_can_accept_freehand_shape(SPLPEItem *lpeitem)
616     if (!SP_IS_PATH(lpeitem))
617         return false;
619     if (sp_lpe_item_has_path_effect_of_type(lpeitem, Inkscape::LivePathEffect::FREEHAND_SHAPE))
620         return false;
622     return true;
625 void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt)
627     Inkscape::LivePathEffect::LPEObjectReference *lperef = sp_lpe_item_get_current_lpereference(lpeitem);
628     if (lperef && lperef->lpeobject && lperef->lpeobject->lpe) {
629         lperef->lpeobject->lpe->editNextParamOncanvas(SP_ITEM(lpeitem), dt);
630     }
633 static void
634 sp_lpe_item_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
636     if (((SPObjectClass *) (parent_class))->child_added)
637         (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref);
639     if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
640         SPObject *ochild = sp_object_get_child_by_repr(object, child);
641         if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
642             sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(ochild));
643         }
644     }
647 static void
648 sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child)
650     if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
651         SPObject *ochild = sp_object_get_child_by_repr(object, child);
652         if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
653             sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(ochild));
654         }
655     }
657     if (((SPObjectClass *) (parent_class))->remove_child)
658         (* ((SPObjectClass *) (parent_class))->remove_child) (object, child);
661 static std::string patheffectlist_write_svg(PathEffectList const & list)
663     HRefList hreflist;
664     for (PathEffectList::const_iterator it = list.begin(); it != list.end(); ++it)
665     {
666         hreflist.push_back( std::string((*it)->lpeobject_href) );
667     }
668     return hreflist_write_svg(hreflist);
671 /**
672  *  THE function that should be used to generate any patheffectlist string.
673  * one of the methods to change the effect list:
674  *  - create temporary href list
675  *  - populate the templist with the effects from the old list that you want to have and their order
676  *  - call this function with temp list as param
677  */
678 static std::string hreflist_write_svg(HRefList const & list)
680     std::string r;
681     bool semicolon_first = false;
682     for (HRefList::const_iterator it = list.begin(); it != list.end(); ++it)
683     {
684         if (semicolon_first) {
685             r += ';';
686         }
687         semicolon_first = true;
689         r += (*it);
690     }
691     return r;
694 // Return a copy of the effect list
695 PathEffectList sp_lpe_item_get_effect_list(SPLPEItem *lpeitem)
697     return *lpeitem->path_effect_list;
700 Inkscape::LivePathEffect::LPEObjectReference* sp_lpe_item_get_current_lpereference(SPLPEItem *lpeitem)
702     if (!lpeitem->current_path_effect && !lpeitem->path_effect_list->empty())
703         sp_lpe_item_set_current_path_effect(lpeitem, lpeitem->path_effect_list->back());
705     return lpeitem->current_path_effect;
708 Inkscape::LivePathEffect::Effect* sp_lpe_item_get_current_lpe(SPLPEItem *lpeitem)
710     Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
712     if (lperef && lperef->lpeobject)
713         return lperef->lpeobject->lpe;
714     else
715         return NULL;
718 bool sp_lpe_item_set_current_path_effect(SPLPEItem *lpeitem, Inkscape::LivePathEffect::LPEObjectReference* lperef)
720     for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); it++) {
721         if ((*it)->lpeobject_repr == lperef->lpeobject_repr) {
722             lpeobject_ref_changed(NULL, (*it)->lpeobject, SP_LPE_ITEM(lpeitem)); // FIXME: explain why this is here?
723             lpeitem->current_path_effect = (*it);  // current_path_effect should always be a pointer from the path_effect_list !
724             return true;
725         }
726     }
728     return false;
731 void sp_lpe_item_replace_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * old_lpeobj,
732                                         LivePathEffectObject * new_lpeobj)
734     HRefList hreflist;
735     for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
736     {
737         if ((*it)->lpeobject == old_lpeobj) {
738             const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
739             gchar *hrefstr = g_strdup_printf("#%s", repr_id);
740             hreflist.push_back( std::string(hrefstr) );
741             g_free(hrefstr);
742         }
743         else {
744             hreflist.push_back( std::string((*it)->lpeobject_href) );
745         }
746     }
747     std::string r = hreflist_write_svg(hreflist);
748     SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
751 // Enable or disable the path effects of the item.
752 // The counter allows nested calls
753 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable)
755     if (enable) {
756         lpeitem->path_effects_enabled++;
757     }
758     else {
759         lpeitem->path_effects_enabled--;
760     }
763 // Are the path effects enabled on this item ?
764 bool sp_lpe_item_path_effects_enabled(SPLPEItem *lpeitem)
766     return lpeitem->path_effects_enabled > 0;
769 /*
770   Local Variables:
771   mode:c++
772   c-file-style:"stroustrup"
773   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
774   indent-tabs-mode:nil
775   fill-column:99
776   End:
777 */
778 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :