Code

Merging from trunk
[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 = new Inkscape::LivePathEffect::LPEObjectReference(SP_OBJECT(lpeitem));
206                         path_effect_ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(lpeobject_ref_changed), SP_LPE_ITEM(object)));
207                         // Now do the attaching, which emits the changed signal.
208                         // Fixme, it should not do this changed signal and updating before all effects are added to the path_effect_list
209                         try {
210                             path_effect_ref->link(href.c_str());
211                         } catch (Inkscape::BadURIException e) {
212                             g_warning("BadURIException: %s", e.what());
213                             path_effect_ref->unlink();
214                             delete path_effect_ref;
215                             path_effect_ref = NULL;
216                         }
218                         if (path_effect_ref && path_effect_ref->lpeobject && path_effect_ref->lpeobject->get_lpe()) {
219                             lpeitem->path_effect_list->push_back(path_effect_ref);
220                         } else {
221                             // something has gone wrong in finding the right patheffect. For example when the specified LPE name does not exist.
222                             path_effect_ref->unlink();
223                             delete path_effect_ref;
224                             path_effect_ref = NULL;
225                         }
226                     }
227                 }
229                 sp_lpe_item_enable_path_effects(lpeitem, true);
230             }
231             break;
232         default:
233             if (((SPObjectClass *) parent_class)->set) {
234                 ((SPObjectClass *) parent_class)->set(object, key, value);
235             }
236             break;
237     }
240 /**
241  * Receives update notifications.
242  */
243 static void
244 sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags)
246     if (((SPObjectClass *) parent_class)->update) {
247         ((SPObjectClass *) parent_class)->update(object, ctx, flags);
248     }
250     // update the helperpaths of all LPEs applied to the item
251     // TODO: is there a more canonical place for this, since we don't have instant access to the item's nodepath?
252     // FIXME: this is called multiple (at least 3) times; how can we avoid this?
254     // FIXME: ditch inkscape_active_event_context()
255     SPEventContext *ec = inkscape_active_event_context();
256     if (!SP_IS_NODE_CONTEXT(ec)) return;
257     SPNodeContext *nc = SP_NODE_CONTEXT(ec);
258     ShapeEditor *sh = nc->shape_editor;
259     g_assert(sh);
260     if (!sh->has_nodepath()) return;
262     Inkscape::NodePath::Path *np = sh->get_nodepath();
263     sp_nodepath_update_helperpaths(np);
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 ( 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     }
296     if (((SPObjectClass *)(parent_class))->write) {
297         ((SPObjectClass *)(parent_class))->write(object, xml_doc, repr, flags);
298     }
300     return repr;
303 void sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve) {
304     if (!lpeitem) return;
305     if (!curve) return;
307     if (sp_lpe_item_has_path_effect(lpeitem) && sp_lpe_item_path_effects_enabled(lpeitem)) {
308         for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
309         {
310             LivePathEffectObject *lpeobj = (*it)->lpeobject;
311             if (!lpeobj) {
312                 g_warning("sp_lpe_item_perform_path_effect - NULL lpeobj in list!");
313                 return;
314             }
315             Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
316             if (!lpe) {
317                 g_warning("sp_lpe_item_perform_path_effect - lpeobj without lpe!");
318                 return;
319             }
321             if (lpe->isVisible()) {
322                 if (lpe->acceptsNumClicks() > 0 && !lpe->isReady()) {
323                     // if the effect expects mouse input before being applied and the input is not finished
324                     // yet, we don't alter the path
325                     return;
326                 }
328                 // Groups have their doBeforeEffect called elsewhere
329                 if (!SP_IS_GROUP(lpeitem)) {
330                     lpe->doBeforeEffect(lpeitem);
331                 }
333                 try {
334                     lpe->doEffect(curve);
335                 }
336                 catch (std::exception & e) {
337                     g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what());
338                     SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
339                         _("An exception occurred during execution of the Path Effect.") );
340                 }
341             }
342         }
343     }
346 /**
347  * Calls any registered handlers for the update_patheffect action
348  */
349 void
350 sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write)
352 #ifdef SHAPE_VERBOSE
353     g_message("sp_lpe_item_update_patheffect: %p\n", lpeitem);
354 #endif
355     g_return_if_fail (lpeitem != NULL);
356     g_return_if_fail (SP_IS_LPE_ITEM (lpeitem));
358     if (!sp_lpe_item_path_effects_enabled(lpeitem))
359         return;
361     // TODO: hack! this will be removed when path length measuring is reimplemented in a better way
362     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
363     std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
364     for (i = lpelist.begin(); i != lpelist.end(); ++i) {
365         Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe();
366         if (dynamic_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)) {
367             if (!lpe->isVisible()) {
368                 // we manually disable text for LPEPathLength
369                 dynamic_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)->hideCanvasText();
370             }
371         }
372     }
374     SPLPEItem *top;
376     if (wholetree) {
377         SPObject *prev_parent = lpeitem;
378         SPObject *parent = prev_parent->parent;
379         while (parent && SP_IS_LPE_ITEM(parent) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent))) {
380             prev_parent = parent;
381             parent = prev_parent->parent;
382         }
383         top = SP_LPE_ITEM(prev_parent);
384     }
385     else {
386         top = lpeitem;
387     }
389     if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect) {
390         SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect (top, write);
391     }
394 /**
395  * Gets called when (re)attached to another lpeobject.
396  */
397 static void
398 lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem)
400     if (old_ref) {
401         sp_signal_disconnect_by_data(old_ref, lpeitem);
402     }
403     if ( IS_LIVEPATHEFFECT(ref) && ref != lpeitem )
404     {
405         lpeitem->lpe_modified_connection.disconnect();
406         lpeitem->lpe_modified_connection = ref->connectModified(sigc::bind(sigc::ptr_fun(&lpeobject_ref_modified), lpeitem));
407         lpeobject_ref_modified(ref, 0, lpeitem);
408     }
411 /**
412  * Gets called when lpeobject repr contents change: i.e. parameter change.
413  */
414 static void
415 lpeobject_ref_modified(SPObject */*href*/, guint /*flags*/, SPLPEItem *lpeitem)
417     sp_lpe_item_update_patheffect (lpeitem, true, true);
420 static void
421 sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem)
423     if (SP_IS_GROUP(lpeitem)) {
424         GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
425         for ( GSList const *iter = item_list; iter; iter = iter->next ) {
426             SPObject *subitem = static_cast<SPObject *>(iter->data);
427             if (SP_IS_LPE_ITEM(subitem)) {
428                 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(subitem));
429             }
430         }
431     }
432     else if (SP_IS_PATH(lpeitem)) {
433         Inkscape::XML::Node *pathrepr = SP_OBJECT_REPR(lpeitem);
434         if ( !pathrepr->attribute("inkscape:original-d") ) {
435             pathrepr->setAttribute("inkscape:original-d", pathrepr->attribute("d"));
436         }
437     }
440 static void
441 sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem)
443     if (SP_IS_GROUP(lpeitem)) {
444         GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
445         for ( GSList const *iter = item_list; iter; iter = iter->next ) {
446             SPObject *subitem = static_cast<SPObject *>(iter->data);
447             if (SP_IS_LPE_ITEM(subitem)) {
448                 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(subitem));
449             }
450         }
451     }
452     else if (SP_IS_PATH(lpeitem)) {
453         Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem);
454         if (!sp_lpe_item_has_path_effect_recursive(lpeitem)
455                 && repr->attribute("inkscape:original-d")) {
456             repr->setAttribute("d", repr->attribute("inkscape:original-d"));
457             repr->setAttribute("inkscape:original-d", NULL);
458         }
459         else {
460             sp_lpe_item_update_patheffect(lpeitem, true, true);
461         }
462     }
465 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset)
467     if (value) {
468         // Apply the path effects here because in the casse of a group, lpe->resetDefaults
469         // needs that all the subitems have their effects applied
470         sp_lpe_item_update_patheffect(lpeitem, false, true);
472         // Disable the path effects while preparing the new lpe
473         sp_lpe_item_enable_path_effects(lpeitem, false);
475         // Add the new reference to the list of LPE references
476         HRefList hreflist;
477         for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
478         {
479             hreflist.push_back( std::string((*it)->lpeobject_href) );
480         }
481         hreflist.push_back( std::string(value) );
482         std::string hrefs = hreflist_write_svg(hreflist);
484         SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", hrefs.c_str());
486         // make sure there is an original-d for paths!!!
487         sp_lpe_item_create_original_path_recursive(lpeitem);
489         LivePathEffectObject *lpeobj = lpeitem->path_effect_list->back()->lpeobject;
490         if (lpeobj && lpeobj->get_lpe()) {
491             Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
492             // Ask the path effect to reset itself if it doesn't have parameters yet
493             if (reset) {
494                 // has to be called when all the subitems have their lpes applied
495                 lpe->resetDefaults(lpeitem);
496             }
498             // perform this once when the effect is applied
499             lpe->doOnApply(SP_LPE_ITEM(lpeitem));
501             // indicate that all necessary preparations are done and the effect can be performed
502             lpe->setReady();
503         }
505         //Enable the path effects now that everything is ready to apply the new path effect
506         sp_lpe_item_enable_path_effects(lpeitem, true);
508         // Apply the path effect
509         sp_lpe_item_update_patheffect(lpeitem, true, true);
510     }
513 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj)
515     const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
516     gchar *hrefstr = g_strdup_printf("#%s", repr_id);
517     sp_lpe_item_add_path_effect(lpeitem, hrefstr, false);
518     g_free(hrefstr);
521 void sp_lpe_item_remove_current_path_effect(SPLPEItem *lpeitem, bool keep_paths)
523     Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
524     if (!lperef)
525         return;
527     PathEffectList new_list = *lpeitem->path_effect_list;
528     new_list.remove(lperef); //current lpe ref is always our 'own' pointer from the path_effect_list
529     std::string r = patheffectlist_write_svg(new_list);
531     SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
533     if (!keep_paths) {
534         sp_lpe_item_cleanup_original_path_recursive(lpeitem);
535     }
538 void sp_lpe_item_remove_all_path_effects(SPLPEItem *lpeitem, bool keep_paths)
540     SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
542     if (!keep_paths) {
543         sp_lpe_item_cleanup_original_path_recursive(lpeitem);
544     }
547 void sp_lpe_item_down_current_path_effect(SPLPEItem *lpeitem)
549     Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
550     if (!lperef)
551         return;
553     PathEffectList new_list = *lpeitem->path_effect_list;
554     PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
555     if (cur_it != new_list.end()) {
556         PathEffectList::iterator down_it = cur_it;
557         down_it++;
558         if (down_it != new_list.end()) { // perhaps current effect is already last effect
559             std::iter_swap(cur_it, down_it);
560         }
561     }
562     std::string r = patheffectlist_write_svg(new_list);
563     SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
565     sp_lpe_item_cleanup_original_path_recursive(lpeitem);
568 void sp_lpe_item_up_current_path_effect(SPLPEItem *lpeitem)
570     Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
571     if (!lperef)
572         return;
574     PathEffectList new_list = *lpeitem->path_effect_list;
575     PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
576     if (cur_it != new_list.end() && cur_it != new_list.begin()) {
577         PathEffectList::iterator up_it = cur_it;
578         up_it--;
579         std::iter_swap(cur_it, up_it);
580     }
581     std::string r = patheffectlist_write_svg(new_list);
583     SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
585     sp_lpe_item_cleanup_original_path_recursive(lpeitem);
589 bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem)
591     return !lpeitem->path_effect_list->empty();
594 bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem)
596     SPObject *parent = lpeitem->parent;
597     if (parent && SP_IS_LPE_ITEM(parent)) {
598         return sp_lpe_item_has_path_effect(lpeitem) || sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent));
599     }
600     else {
601         return sp_lpe_item_has_path_effect(lpeitem);
602     }
605 Inkscape::LivePathEffect::Effect*
606 sp_lpe_item_has_path_effect_of_type(SPLPEItem *lpeitem, int type)
608     std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
609     for (i = lpeitem->path_effect_list->begin(); i != lpeitem->path_effect_list->end(); ++i) {
610         Inkscape::LivePathEffect::Effect* lpe = (*i)->lpeobject->get_lpe();
611         if (lpe && (lpe->effectType() == type)) {
612             return lpe;
613         }
614     }
615     return NULL;
618 /* Return false if the item is not a path or already has a shape applied */
619 bool sp_lpe_item_can_accept_freehand_shape(SPLPEItem *lpeitem)
621     if (!SP_IS_PATH(lpeitem))
622         return false;
624     if (sp_lpe_item_has_path_effect_of_type(lpeitem, Inkscape::LivePathEffect::FREEHAND_SHAPE))
625         return false;
627     return true;
630 void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt)
632     Inkscape::LivePathEffect::LPEObjectReference *lperef = sp_lpe_item_get_current_lpereference(lpeitem);
633     if (lperef && lperef->lpeobject && lperef->lpeobject->get_lpe()) {
634         lperef->lpeobject->get_lpe()->editNextParamOncanvas(SP_ITEM(lpeitem), dt);
635     }
638 static void
639 sp_lpe_item_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
641     if (((SPObjectClass *) (parent_class))->child_added)
642         (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref);
644     if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
645         SPObject *ochild = sp_object_get_child_by_repr(object, child);
646         if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
647             sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(ochild));
648         }
649     }
652 static void
653 sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child)
655     if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
656         SPObject *ochild = sp_object_get_child_by_repr(object, child);
657         if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
658             sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(ochild));
659         }
660     }
662     if (((SPObjectClass *) (parent_class))->remove_child)
663         (* ((SPObjectClass *) (parent_class))->remove_child) (object, child);
666 static std::string patheffectlist_write_svg(PathEffectList const & list)
668     HRefList hreflist;
669     for (PathEffectList::const_iterator it = list.begin(); it != list.end(); ++it)
670     {
671         hreflist.push_back( std::string((*it)->lpeobject_href) );
672     }
673     return hreflist_write_svg(hreflist);
676 /**
677  *  THE function that should be used to generate any patheffectlist string.
678  * one of the methods to change the effect list:
679  *  - create temporary href list
680  *  - populate the templist with the effects from the old list that you want to have and their order
681  *  - call this function with temp list as param
682  */
683 static std::string hreflist_write_svg(HRefList const & list)
685     std::string r;
686     bool semicolon_first = false;
687     for (HRefList::const_iterator it = list.begin(); it != list.end(); ++it)
688     {
689         if (semicolon_first) {
690             r += ';';
691         }
692         semicolon_first = true;
694         r += (*it);
695     }
696     return r;
699 // Return a copy of the effect list
700 PathEffectList sp_lpe_item_get_effect_list(SPLPEItem *lpeitem)
702     return *lpeitem->path_effect_list;
705 Inkscape::LivePathEffect::LPEObjectReference* sp_lpe_item_get_current_lpereference(SPLPEItem *lpeitem)
707     if (!lpeitem->current_path_effect && !lpeitem->path_effect_list->empty())
708         sp_lpe_item_set_current_path_effect(lpeitem, lpeitem->path_effect_list->back());
710     return lpeitem->current_path_effect;
713 Inkscape::LivePathEffect::Effect* sp_lpe_item_get_current_lpe(SPLPEItem *lpeitem)
715     Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
717     if (lperef && lperef->lpeobject)
718         return lperef->lpeobject->get_lpe();
719     else
720         return NULL;
723 bool sp_lpe_item_set_current_path_effect(SPLPEItem *lpeitem, Inkscape::LivePathEffect::LPEObjectReference* lperef)
725     for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); it++) {
726         if ((*it)->lpeobject_repr == lperef->lpeobject_repr) {
727             lpeobject_ref_changed(NULL, (*it)->lpeobject, SP_LPE_ITEM(lpeitem)); // FIXME: explain why this is here?
728             lpeitem->current_path_effect = (*it);  // current_path_effect should always be a pointer from the path_effect_list !
729             return true;
730         }
731     }
733     return false;
736 void sp_lpe_item_replace_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * old_lpeobj,
737                                         LivePathEffectObject * new_lpeobj)
739     HRefList hreflist;
740     for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
741     {
742         if ((*it)->lpeobject == old_lpeobj) {
743             const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
744             gchar *hrefstr = g_strdup_printf("#%s", repr_id);
745             hreflist.push_back( std::string(hrefstr) );
746             g_free(hrefstr);
747         }
748         else {
749             hreflist.push_back( std::string((*it)->lpeobject_href) );
750         }
751     }
752     std::string r = hreflist_write_svg(hreflist);
753     SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
756 // Enable or disable the path effects of the item.
757 // The counter allows nested calls
758 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable)
760     if (enable) {
761         lpeitem->path_effects_enabled++;
762     }
763     else {
764         lpeitem->path_effects_enabled--;
765     }
768 // Are the path effects enabled on this item ?
769 bool sp_lpe_item_path_effects_enabled(SPLPEItem *lpeitem)
771     return lpeitem->path_effects_enabled > 0;
774 /*
775   Local Variables:
776   mode:c++
777   c-file-style:"stroustrup"
778   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
779   indent-tabs-mode:nil
780   fill-column:99
781   End:
782 */
783 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :