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;
113 }
115 static void
116 sp_lpe_item_init(SPLPEItem *lpeitem)
117 {
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();
124 }
126 static void
127 sp_lpe_item_finalize(GObject *object)
128 {
129 if (((GObjectClass *) (parent_class))->finalize) {
130 (* ((GObjectClass *) (parent_class))->finalize)(object);
131 }
132 }
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)
141 {
142 sp_object_read_attr(object, "inkscape:path-effect");
144 if (((SPObjectClass *) parent_class)->build) {
145 ((SPObjectClass *) parent_class)->build(object, document, repr);
146 }
147 }
149 /**
150 * Drops any allocated memory.
151 */
152 static void
153 sp_lpe_item_release(SPObject *object)
154 {
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);
172 }
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)
179 {
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 }
234 }
236 /**
237 * Receives update notifications.
238 */
239 static void
240 sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags)
241 {
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);
260 }
262 /**
263 * Sets modified flag for all sub-item views.
264 */
265 static void
266 sp_lpe_item_modified (SPObject *object, unsigned int flags)
267 {
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 }
275 }
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)
282 {
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;
297 }
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 }
340 }
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)
347 {
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 }
388 }
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)
395 {
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 }
405 }
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)
412 {
413 sp_lpe_item_update_patheffect (lpeitem, true, true);
414 }
416 static void
417 sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem)
418 {
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 }
434 }
436 static void
437 sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem)
438 {
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 }
459 }
461 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset)
462 {
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 }
507 }
509 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj)
510 {
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);
515 }
517 void sp_lpe_item_remove_current_path_effect(SPLPEItem *lpeitem, bool keep_paths)
518 {
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 }
532 }
534 void sp_lpe_item_remove_all_path_effects(SPLPEItem *lpeitem, bool keep_paths)
535 {
536 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
538 if (!keep_paths) {
539 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
540 }
541 }
543 void sp_lpe_item_down_current_path_effect(SPLPEItem *lpeitem)
544 {
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);
562 }
564 void sp_lpe_item_up_current_path_effect(SPLPEItem *lpeitem)
565 {
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);
582 }
585 bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem)
586 {
587 return !lpeitem->path_effect_list->empty();
588 }
590 bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem)
591 {
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 }
599 }
601 Inkscape::LivePathEffect::Effect*
602 sp_lpe_item_has_path_effect_of_type(SPLPEItem *lpeitem, int type)
603 {
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;
611 }
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)
615 {
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;
623 }
625 void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt)
626 {
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 }
631 }
633 static void
634 sp_lpe_item_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
635 {
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 }
645 }
647 static void
648 sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child)
649 {
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);
659 }
661 static std::string patheffectlist_write_svg(PathEffectList const & list)
662 {
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);
669 }
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)
679 {
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;
692 }
694 // Return a copy of the effect list
695 PathEffectList sp_lpe_item_get_effect_list(SPLPEItem *lpeitem)
696 {
697 return *lpeitem->path_effect_list;
698 }
700 Inkscape::LivePathEffect::LPEObjectReference* sp_lpe_item_get_current_lpereference(SPLPEItem *lpeitem)
701 {
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;
706 }
708 Inkscape::LivePathEffect::Effect* sp_lpe_item_get_current_lpe(SPLPEItem *lpeitem)
709 {
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;
716 }
718 bool sp_lpe_item_set_current_path_effect(SPLPEItem *lpeitem, Inkscape::LivePathEffect::LPEObjectReference* lperef)
719 {
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;
729 }
731 void sp_lpe_item_replace_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * old_lpeobj,
732 LivePathEffectObject * new_lpeobj)
733 {
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());
749 }
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)
754 {
755 if (enable) {
756 lpeitem->path_effects_enabled++;
757 }
758 else {
759 lpeitem->path_effects_enabled--;
760 }
761 }
763 // Are the path effects enabled on this item ?
764 bool sp_lpe_item_path_effects_enabled(SPLPEItem *lpeitem)
765 {
766 return lpeitem->path_effects_enabled > 0;
767 }
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 :