Code

Avoid crash by uninitialized perspectives.
[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_modified(SPObject *href, guint flags, SPLPEItem *lpeitem);
61 static void sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem);
62 static void sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem);
63 typedef std::list<std::string> HRefList;
64 static std::string patheffectlist_write_svg(PathEffectList const & list);
65 static std::string hreflist_write_svg(HRefList const & list);
67 static SPItemClass *parent_class;
69 GType
70 sp_lpe_item_get_type()
71 {
72     static GType lpe_item_type = 0;
74     if (!lpe_item_type) {
75         GTypeInfo lpe_item_info = {
76             sizeof(SPLPEItemClass),
77             NULL, NULL,
78             (GClassInitFunc) sp_lpe_item_class_init,
79             NULL, NULL,
80             sizeof(SPLPEItem),
81             16,
82             (GInstanceInitFunc) sp_lpe_item_init,
83             NULL,    /* value_table */
84         };
85         lpe_item_type = g_type_register_static(SP_TYPE_ITEM, "SPLPEItem", &lpe_item_info, (GTypeFlags)0);
86     }
87     return lpe_item_type;
88 }
90 static void
91 sp_lpe_item_class_init(SPLPEItemClass *klass)
92 {
93     GObjectClass *gobject_class;
94     SPObjectClass *sp_object_class;
96     gobject_class = (GObjectClass *) klass;
97     sp_object_class = (SPObjectClass *) klass;
98     parent_class = (SPItemClass *)g_type_class_peek_parent (klass);
100     gobject_class->finalize = sp_lpe_item_finalize;
102     sp_object_class->build = sp_lpe_item_build;
103     sp_object_class->release = sp_lpe_item_release;
104     sp_object_class->set = sp_lpe_item_set;
105     sp_object_class->update = sp_lpe_item_update;
106     sp_object_class->modified = sp_lpe_item_modified;
107     sp_object_class->write = sp_lpe_item_write;
108     sp_object_class->child_added = sp_lpe_item_child_added;
109     sp_object_class->remove_child = sp_lpe_item_remove_child;
111     klass->update_patheffect = NULL;
114 static void
115 sp_lpe_item_init(SPLPEItem *lpeitem)
117     lpeitem->path_effects_enabled = 1;
119     lpeitem->path_effect_list = new PathEffectList();
120     lpeitem->current_path_effect = NULL;
122     lpeitem->lpe_modified_connection_list = new std::list<sigc::connection>();
125 static void
126 sp_lpe_item_finalize(GObject *object)
128     if (((GObjectClass *) (parent_class))->finalize) {
129         (* ((GObjectClass *) (parent_class))->finalize)(object);
130     }
133 /**
134  * Reads the Inkscape::XML::Node, and initializes SPLPEItem variables.  For this to get called,
135  * our name must be associated with a repr via "sp_object_type_register".  Best done through
136  * sp-object-repr.cpp's repr_name_entries array.
137  */
138 static void
139 sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
141     sp_object_read_attr(object, "inkscape:path-effect");
143     if (((SPObjectClass *) parent_class)->build) {
144         ((SPObjectClass *) parent_class)->build(object, document, repr);
145     }
148 /**
149  * Drops any allocated memory.
150  */
151 static void
152 sp_lpe_item_release(SPObject *object)
154     SPLPEItem *lpeitem = (SPLPEItem *) object;
156     // disconnect all modified listeners:
157     for (std::list<sigc::connection>::iterator mod_it = lpeitem->lpe_modified_connection_list->begin();
158          mod_it != lpeitem->lpe_modified_connection_list->end(); ++mod_it)
159     {
160         mod_it->disconnect();
161     }
162     delete lpeitem->lpe_modified_connection_list;
163     lpeitem->lpe_modified_connection_list = NULL;
165     PathEffectList::iterator it = lpeitem->path_effect_list->begin();
166     while ( it != lpeitem->path_effect_list->end() ) {
167         // unlink and delete all references in the list
168         (*it)->unlink();
169         delete *it;
170         it = lpeitem->path_effect_list->erase(it);
171     }
172     // delete the list itself
173     delete lpeitem->path_effect_list;
174     lpeitem->path_effect_list = NULL;
176     if (((SPObjectClass *) parent_class)->release)
177         ((SPObjectClass *) parent_class)->release(object);
180 /**
181  * Sets a specific value in the SPLPEItem.
182  */
183 static void
184 sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value)
186     SPLPEItem *lpeitem = (SPLPEItem *) object;
188     switch (key) {
189         case SP_ATTR_INKSCAPE_PATH_EFFECT:
190             {
191                 lpeitem->current_path_effect = NULL;
193                 // Disable the path effects while populating the LPE list
194                  sp_lpe_item_enable_path_effects(lpeitem, false);
196                 // disconnect all modified listeners:
197                 for ( std::list<sigc::connection>::iterator mod_it = lpeitem->lpe_modified_connection_list->begin();
198                       mod_it != lpeitem->lpe_modified_connection_list->end();
199                       ++mod_it)
200                 {
201                     mod_it->disconnect();
202                 }
203                 lpeitem->lpe_modified_connection_list->clear();
204                 // Clear the path effect list
205                 PathEffectList::iterator it = lpeitem->path_effect_list->begin();
206                 while ( it != lpeitem->path_effect_list->end() )
207                 {
208                     (*it)->unlink();
209                     delete *it;
210                     it = lpeitem->path_effect_list->erase(it);
211                 }
213                 // Parse the contents of "value" to rebuild the path effect reference list
214                 if ( value ) {
215                     std::istringstream iss(value);
216                     std::string href;
217                     while (std::getline(iss, href, ';'))
218                     {
219                         Inkscape::LivePathEffect::LPEObjectReference *path_effect_ref = new Inkscape::LivePathEffect::LPEObjectReference(object);
220                         try {
221                             path_effect_ref->link(href.c_str());
222                         } catch (Inkscape::BadURIException e) {
223                             g_warning("BadURIException when trying to find LPE: %s", e.what());
224                             path_effect_ref->unlink();
225                             delete path_effect_ref;
226                             path_effect_ref = NULL;
227                         }
229                         lpeitem->path_effect_list->push_back(path_effect_ref);
230                         if ( path_effect_ref->lpeobject && path_effect_ref->lpeobject->get_lpe() ) {
231                             // connect modified-listener
232                             lpeitem->lpe_modified_connection_list->push_back(
233                                                 path_effect_ref->lpeobject->connectModified(sigc::bind(sigc::ptr_fun(&lpeobject_ref_modified), lpeitem)) );
234                         } else {
235                             // something has gone wrong in finding the right patheffect.
236                             g_warning("Unknown LPE type specified, LPE stack effectively disabled");
237                             // keep the effect in the lpestack, so the whole stack is effectively disabled but maintained
238                         }
239                     }
240                 }
242                 sp_lpe_item_enable_path_effects(lpeitem, true);
243             }
244             break;
245         default:
246             if (((SPObjectClass *) parent_class)->set) {
247                 ((SPObjectClass *) parent_class)->set(object, key, value);
248             }
249             break;
250     }
253 /**
254  * Receives update notifications.
255  */
256 static void
257 sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags)
259     if (((SPObjectClass *) parent_class)->update) {
260         ((SPObjectClass *) parent_class)->update(object, ctx, flags);
261     }
263     // update the helperpaths of all LPEs applied to the item
264     // TODO: is there a more canonical place for this, since we don't have instant access to the item's nodepath?
265     // FIXME: this is called multiple (at least 3) times; how can we avoid this?
267     // FIXME: ditch inkscape_active_event_context()
268     SPEventContext *ec = inkscape_active_event_context();
269     if (!SP_IS_NODE_CONTEXT(ec)) return;
270     ShapeEditor *sh = ec->shape_editor;
271     g_assert(sh);
272     if (!sh->has_nodepath()) return;
274     Inkscape::NodePath::Path *np = sh->get_nodepath();
275     sp_nodepath_update_helperpaths(np);
278 /**
279  * Sets modified flag for all sub-item views.
280  */
281 static void
282 sp_lpe_item_modified (SPObject *object, unsigned int flags)
284     if (SP_IS_GROUP(object) && (flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_USER_MODIFIED_FLAG_B)) {
285         sp_lpe_item_update_patheffect(SP_LPE_ITEM(object), true, true);
286     }
288     if (((SPObjectClass *) (parent_class))->modified) {
289         (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
290     }
293 /**
294  * Writes its settings to an incoming repr object, if any.
295  */
296 static Inkscape::XML::Node *
297 sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
299     SPLPEItem *lpeitem = (SPLPEItem *) object;
301     if (flags & SP_OBJECT_WRITE_EXT) {
302         if ( sp_lpe_item_has_path_effect(lpeitem) ) {
303             std::string href = patheffectlist_write_svg(*lpeitem->path_effect_list);
304             repr->setAttribute("inkscape:path-effect", href.c_str());
305         } else {
306             repr->setAttribute("inkscape:path-effect", NULL);
307         }
308     }
310     if (((SPObjectClass *)(parent_class))->write) {
311         ((SPObjectClass *)(parent_class))->write(object, xml_doc, repr, flags);
312     }
314     return repr;
317 /**
318  * returns true when LPE was successful.
319  */
320 bool sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve) {
321     if (!lpeitem) return false;
322     if (!curve) return false;
324     if (sp_lpe_item_has_path_effect(lpeitem) && sp_lpe_item_path_effects_enabled(lpeitem)) {
325         for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
326         {
327             LivePathEffectObject *lpeobj = (*it)->lpeobject;
328             if (!lpeobj) {
329                 /** \todo Investigate the cause of this.
330                  * 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.
331                  */
332                 g_warning("sp_lpe_item_perform_path_effect - NULL lpeobj in list!");
333                 return false;
334             }
335             Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
336             if (!lpe) {
337                 /** \todo Investigate the cause of this.
338                  * Not sure, but I think this can happen when an unknown effect type is specified...
339                  */
340                 g_warning("sp_lpe_item_perform_path_effect - lpeobj with invalid lpe in the stack!");
341                 return false;
342             }
344             if (lpe->isVisible()) {
345                 if (lpe->acceptsNumClicks() > 0 && !lpe->isReady()) {
346                     // if the effect expects mouse input before being applied and the input is not finished
347                     // yet, we don't alter the path
348                     return false;
349                 }
351                 // Groups have their doBeforeEffect called elsewhere
352                 if (!SP_IS_GROUP(lpeitem)) {
353                     lpe->doBeforeEffect(lpeitem);
354                 }
356                 try {
357                     lpe->doEffect(curve);
358                 }
359                 catch (std::exception & e) {
360                     g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what());
361                     if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->messageStack()) {
362                         SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
363                                         _("An exception occurred during execution of the Path Effect.") );
364                     }
365                     return false;
366                 }
367             }
368         }
369     }
371     return true;
374 /**
375  * Calls any registered handlers for the update_patheffect action
376  */
377 void
378 sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write)
380 #ifdef SHAPE_VERBOSE
381     g_message("sp_lpe_item_update_patheffect: %p\n", lpeitem);
382 #endif
383     g_return_if_fail (lpeitem != NULL);
384     g_return_if_fail (SP_IS_LPE_ITEM (lpeitem));
386     if (!sp_lpe_item_path_effects_enabled(lpeitem))
387         return;
389     // TODO: hack! this will be removed when path length measuring is reimplemented in a better way
390     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
391     std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
392     for (i = lpelist.begin(); i != lpelist.end(); ++i) {
393         if ((*i)->lpeobject) {
394             Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe();
395             if (dynamic_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)) {
396                 if (!lpe->isVisible()) {
397                     // we manually disable text for LPEPathLength
398                     dynamic_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)->hideCanvasText();
399                 }
400             }
401         }
402     }
404     SPLPEItem *top;
406     if (wholetree) {
407         SPObject *prev_parent = lpeitem;
408         SPObject *parent = prev_parent->parent;
409         while (parent && SP_IS_LPE_ITEM(parent) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent))) {
410             prev_parent = parent;
411             parent = prev_parent->parent;
412         }
413         top = SP_LPE_ITEM(prev_parent);
414     }
415     else {
416         top = lpeitem;
417     }
419     if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect) {
420         SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect (top, write);
421     }
424 /**
425  * Gets called when any of the lpestack's lpeobject repr contents change: i.e. parameter change in any of the stacked LPEs
426  */
427 static void
428 lpeobject_ref_modified(SPObject */*href*/, guint /*flags*/, SPLPEItem *lpeitem)
430 #ifdef SHAPE_VERBOSE
431     g_message("lpeobject_ref_modified");
432 #endif
433     sp_lpe_item_update_patheffect (lpeitem, true, true);
436 static void
437 sp_lpe_item_create_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_create_original_path_recursive(SP_LPE_ITEM(subitem));
445             }
446         }
447     }
448     else if (SP_IS_PATH(lpeitem)) {
449         Inkscape::XML::Node *pathrepr = SP_OBJECT_REPR(lpeitem);
450         if ( !pathrepr->attribute("inkscape:original-d") ) {
451             pathrepr->setAttribute("inkscape:original-d", pathrepr->attribute("d"));
452         }
453     }
456 static void
457 sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem)
459     if (SP_IS_GROUP(lpeitem)) {
460         GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
461         for ( GSList const *iter = item_list; iter; iter = iter->next ) {
462             SPObject *subitem = static_cast<SPObject *>(iter->data);
463             if (SP_IS_LPE_ITEM(subitem)) {
464                 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(subitem));
465             }
466         }
467     }
468     else if (SP_IS_PATH(lpeitem)) {
469         Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem);
470         if (!sp_lpe_item_has_path_effect_recursive(lpeitem)
471                 && repr->attribute("inkscape:original-d")) {
472             repr->setAttribute("d", repr->attribute("inkscape:original-d"));
473             repr->setAttribute("inkscape:original-d", NULL);
474         }
475         else {
476             sp_lpe_item_update_patheffect(lpeitem, true, true);
477         }
478     }
481 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset)
483     if (value) {
484         // Apply the path effects here because in the casse of a group, lpe->resetDefaults
485         // needs that all the subitems have their effects applied
486         sp_lpe_item_update_patheffect(lpeitem, false, true);
488         // Disable the path effects while preparing the new lpe
489         sp_lpe_item_enable_path_effects(lpeitem, false);
491         // Add the new reference to the list of LPE references
492         HRefList hreflist;
493         for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
494         {
495             hreflist.push_back( std::string((*it)->lpeobject_href) );
496         }
497         hreflist.push_back( std::string(value) );
498         std::string hrefs = hreflist_write_svg(hreflist);
500         SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", hrefs.c_str());
502         // make sure there is an original-d for paths!!!
503         sp_lpe_item_create_original_path_recursive(lpeitem);
505         LivePathEffectObject *lpeobj = lpeitem->path_effect_list->back()->lpeobject;
506         if (lpeobj && lpeobj->get_lpe()) {
507             Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
508             // Ask the path effect to reset itself if it doesn't have parameters yet
509             if (reset) {
510                 // has to be called when all the subitems have their lpes applied
511                 lpe->resetDefaults(lpeitem);
512             }
514             // perform this once when the effect is applied
515             lpe->doOnApply(SP_LPE_ITEM(lpeitem));
517             // indicate that all necessary preparations are done and the effect can be performed
518             lpe->setReady();
519         }
521         //Enable the path effects now that everything is ready to apply the new path effect
522         sp_lpe_item_enable_path_effects(lpeitem, true);
524         // Apply the path effect
525         sp_lpe_item_update_patheffect(lpeitem, true, true);
526     }
529 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj)
531     const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
532     gchar *hrefstr = g_strdup_printf("#%s", repr_id);
533     sp_lpe_item_add_path_effect(lpeitem, hrefstr, false);
534     g_free(hrefstr);
537 void sp_lpe_item_remove_current_path_effect(SPLPEItem *lpeitem, bool keep_paths)
539     Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
540     if (!lperef)
541         return;
543     PathEffectList new_list = *lpeitem->path_effect_list;
544     new_list.remove(lperef); //current lpe ref is always our 'own' pointer from the path_effect_list
545     std::string r = patheffectlist_write_svg(new_list);
547     if (!r.empty()) {
548         SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
549     } else {
550         SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
551     }
553     if (!keep_paths) {
554         sp_lpe_item_cleanup_original_path_recursive(lpeitem);
555     }
558 void sp_lpe_item_remove_all_path_effects(SPLPEItem *lpeitem, bool keep_paths)
560     SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
562     if (!keep_paths) {
563         sp_lpe_item_cleanup_original_path_recursive(lpeitem);
564     }
567 void sp_lpe_item_down_current_path_effect(SPLPEItem *lpeitem)
569     Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
570     if (!lperef)
571         return;
573     PathEffectList new_list = *lpeitem->path_effect_list;
574     PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
575     if (cur_it != new_list.end()) {
576         PathEffectList::iterator down_it = cur_it;
577         down_it++;
578         if (down_it != new_list.end()) { // perhaps current effect is already last effect
579             std::iter_swap(cur_it, down_it);
580         }
581     }
582     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);
588 void sp_lpe_item_up_current_path_effect(SPLPEItem *lpeitem)
590     Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
591     if (!lperef)
592         return;
594     PathEffectList new_list = *lpeitem->path_effect_list;
595     PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
596     if (cur_it != new_list.end() && cur_it != new_list.begin()) {
597         PathEffectList::iterator up_it = cur_it;
598         up_it--;
599         std::iter_swap(cur_it, up_it);
600     }
601     std::string r = patheffectlist_write_svg(new_list);
603     SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
605     sp_lpe_item_cleanup_original_path_recursive(lpeitem);
608 /** used for shapes so they can see if they should also disable shape calculation and read from d= */
609 bool sp_lpe_item_has_broken_path_effect(SPLPEItem *lpeitem)
611     if (lpeitem->path_effect_list->empty())
612         return false;
614     // go through the list; if some are unknown or invalid, return true
615     PathEffectList effect_list =  sp_lpe_item_get_effect_list(lpeitem);
616     for (PathEffectList::iterator it = effect_list.begin(); it != effect_list.end(); it++)
617     {
618         LivePathEffectObject *lpeobj = (*it)->lpeobject;
619         if (!lpeobj || !lpeobj->get_lpe())
620             return true;
621     }
623     return false;
627 bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem)
629     if (lpeitem->path_effect_list->empty())
630         return false;
632     // go through the list; if some are unknown or invalid, we are not an LPE item!
633     PathEffectList effect_list =  sp_lpe_item_get_effect_list(lpeitem);
634     for (PathEffectList::iterator it = effect_list.begin(); it != effect_list.end(); it++)
635     {
636         LivePathEffectObject *lpeobj = (*it)->lpeobject;
637         if (!lpeobj || !lpeobj->get_lpe())
638             return false;
639     }
641     return true;
644 bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem)
646     SPObject *parent = lpeitem->parent;
647     if (parent && SP_IS_LPE_ITEM(parent)) {
648         return sp_lpe_item_has_path_effect(lpeitem) || sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent));
649     }
650     else {
651         return sp_lpe_item_has_path_effect(lpeitem);
652     }
655 Inkscape::LivePathEffect::Effect*
656 sp_lpe_item_has_path_effect_of_type(SPLPEItem *lpeitem, int type)
658     std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
659     for (i = lpeitem->path_effect_list->begin(); i != lpeitem->path_effect_list->end(); ++i) {
660         Inkscape::LivePathEffect::Effect* lpe = (*i)->lpeobject->get_lpe();
661         if (lpe && (lpe->effectType() == type)) {
662             return lpe;
663         }
664     }
665     return NULL;
668 /* Return false if the item is not a path or already has a shape applied */
669 bool sp_lpe_item_can_accept_freehand_shape(SPLPEItem *lpeitem)
671     if (!SP_IS_PATH(lpeitem))
672         return false;
674     if (sp_lpe_item_has_path_effect_of_type(lpeitem, Inkscape::LivePathEffect::FREEHAND_SHAPE))
675         return false;
677     return true;
680 void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt)
682     Inkscape::LivePathEffect::LPEObjectReference *lperef = sp_lpe_item_get_current_lpereference(lpeitem);
683     if (lperef && lperef->lpeobject && lperef->lpeobject->get_lpe()) {
684         lperef->lpeobject->get_lpe()->editNextParamOncanvas(SP_ITEM(lpeitem), dt);
685     }
688 static void
689 sp_lpe_item_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
691     if (((SPObjectClass *) (parent_class))->child_added)
692         (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref);
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_create_original_path_recursive(SP_LPE_ITEM(ochild));
698         }
699     }
702 static void
703 sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child)
705     if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
706         SPObject *ochild = sp_object_get_child_by_repr(object, child);
707         if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
708             sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(ochild));
709         }
710     }
712     if (((SPObjectClass *) (parent_class))->remove_child)
713         (* ((SPObjectClass *) (parent_class))->remove_child) (object, child);
716 static std::string patheffectlist_write_svg(PathEffectList const & list)
718     HRefList hreflist;
719     for (PathEffectList::const_iterator it = list.begin(); it != list.end(); ++it)
720     {
721         hreflist.push_back( std::string((*it)->lpeobject_href) );
722     }
723     return hreflist_write_svg(hreflist);
726 /**
727  *  THE function that should be used to generate any patheffectlist string.
728  * one of the methods to change the effect list:
729  *  - create temporary href list
730  *  - populate the templist with the effects from the old list that you want to have and their order
731  *  - call this function with temp list as param
732  */
733 static std::string hreflist_write_svg(HRefList const & list)
735     std::string r;
736     bool semicolon_first = false;
737     for (HRefList::const_iterator it = list.begin(); it != list.end(); ++it)
738     {
739         if (semicolon_first) {
740             r += ';';
741         }
742         semicolon_first = true;
744         r += (*it);
745     }
746     return r;
749 // Return a copy of the effect list
750 PathEffectList sp_lpe_item_get_effect_list(SPLPEItem *lpeitem)
752     return *lpeitem->path_effect_list;
755 Inkscape::LivePathEffect::LPEObjectReference* sp_lpe_item_get_current_lpereference(SPLPEItem *lpeitem)
757     if (!lpeitem->current_path_effect && !lpeitem->path_effect_list->empty()) {
758         sp_lpe_item_set_current_path_effect(lpeitem, lpeitem->path_effect_list->back());
759     }
761     return lpeitem->current_path_effect;
764 Inkscape::LivePathEffect::Effect* sp_lpe_item_get_current_lpe(SPLPEItem *lpeitem)
766     Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
768     if (lperef && lperef->lpeobject)
769         return lperef->lpeobject->get_lpe();
770     else
771         return NULL;
774 bool sp_lpe_item_set_current_path_effect(SPLPEItem *lpeitem, Inkscape::LivePathEffect::LPEObjectReference* lperef)
776     for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); it++) {
777         if ((*it)->lpeobject_repr == lperef->lpeobject_repr) {
778             lpeitem->current_path_effect = (*it);  // current_path_effect should always be a pointer from the path_effect_list !
779             return true;
780         }
781     }
783     return false;
786 void sp_lpe_item_replace_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * old_lpeobj,
787                                         LivePathEffectObject * new_lpeobj)
789     HRefList hreflist;
790     for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
791     {
792         if ((*it)->lpeobject == old_lpeobj) {
793             const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
794             gchar *hrefstr = g_strdup_printf("#%s", repr_id);
795             hreflist.push_back( std::string(hrefstr) );
796             g_free(hrefstr);
797         }
798         else {
799             hreflist.push_back( std::string((*it)->lpeobject_href) );
800         }
801     }
802     std::string r = hreflist_write_svg(hreflist);
803     SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
806 // Enable or disable the path effects of the item.
807 // The counter allows nested calls
808 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable)
810     if (enable) {
811         lpeitem->path_effects_enabled++;
812     }
813     else {
814         lpeitem->path_effects_enabled--;
815     }
818 // Are the path effects enabled on this item ?
819 bool sp_lpe_item_path_effects_enabled(SPLPEItem *lpeitem)
821     return lpeitem->path_effects_enabled > 0;
824 /*
825   Local Variables:
826   mode:c++
827   c-file-style:"stroustrup"
828   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
829   indent-tabs-mode:nil
830   fill-column:99
831   End:
832 */
833 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :