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/lpeobject.h"
22 #include "live_effects/lpeobject-reference.h"
24 #include "sp-path.h"
25 #include "sp-item-group.h"
26 #include "streq.h"
27 #include "macros.h"
28 #include "attributes.h"
29 #include "sp-lpe-item.h"
30 #include "xml/repr.h"
31 #include "uri.h"
32 #include "message-stack.h"
33 #include "inkscape.h"
34 #include "desktop.h"
36 #include <algorithm>
38 /* LPEItem base class */
40 static void sp_lpe_item_class_init(SPLPEItemClass *klass);
41 static void sp_lpe_item_init(SPLPEItem *lpe_item);
42 static void sp_lpe_item_finalize(GObject *object);
44 static void sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
45 static void sp_lpe_item_release(SPObject *object);
46 static void sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value);
47 static void sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags);
48 static void sp_lpe_item_modified (SPObject *object, unsigned int flags);
49 static Inkscape::XML::Node *sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags);
51 static void sp_lpe_item_child_added (SPObject * object, Inkscape::XML::Node * child, Inkscape::XML::Node * ref);
52 static void sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child);
54 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable);
56 static void lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem);
57 static void lpeobject_ref_modified(SPObject *href, guint flags, SPLPEItem *lpeitem);
59 static void sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem);
60 static void sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem);
61 typedef std::list<std::string> HRefList;
62 static std::string patheffectlist_write_svg(PathEffectList const & list);
63 static std::string hreflist_write_svg(HRefList const & list);
65 static SPItemClass *parent_class;
67 GType
68 sp_lpe_item_get_type()
69 {
70 static GType lpe_item_type = 0;
72 if (!lpe_item_type) {
73 GTypeInfo lpe_item_info = {
74 sizeof(SPLPEItemClass),
75 NULL, NULL,
76 (GClassInitFunc) sp_lpe_item_class_init,
77 NULL, NULL,
78 sizeof(SPLPEItem),
79 16,
80 (GInstanceInitFunc) sp_lpe_item_init,
81 NULL, /* value_table */
82 };
83 lpe_item_type = g_type_register_static(SP_TYPE_ITEM, "SPLPEItem", &lpe_item_info, (GTypeFlags)0);
84 }
85 return lpe_item_type;
86 }
88 static void
89 sp_lpe_item_class_init(SPLPEItemClass *klass)
90 {
91 GObjectClass *gobject_class;
92 SPObjectClass *sp_object_class;
94 gobject_class = (GObjectClass *) klass;
95 sp_object_class = (SPObjectClass *) klass;
96 parent_class = (SPItemClass *)g_type_class_peek_parent (klass);
98 gobject_class->finalize = sp_lpe_item_finalize;
100 sp_object_class->build = sp_lpe_item_build;
101 sp_object_class->release = sp_lpe_item_release;
102 sp_object_class->set = sp_lpe_item_set;
103 sp_object_class->update = sp_lpe_item_update;
104 sp_object_class->modified = sp_lpe_item_modified;
105 sp_object_class->write = sp_lpe_item_write;
106 sp_object_class->child_added = sp_lpe_item_child_added;
107 sp_object_class->remove_child = sp_lpe_item_remove_child;
109 klass->update_patheffect = NULL;
110 }
112 static void
113 sp_lpe_item_init(SPLPEItem *lpeitem)
114 {
115 lpeitem->path_effects_enabled = 1;
117 lpeitem->path_effect_list = new PathEffectList();
118 lpeitem->current_path_effect = NULL;
120 new (&lpeitem->lpe_modified_connection) sigc::connection();
122 lpeitem->adding_helperpaths = false;
123 lpeitem->removing_helperpaths = false;
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 }
133 delete SP_LPE_ITEM(object)->path_effect_list;
134 }
136 /**
137 * Reads the Inkscape::XML::Node, and initializes SPLPEItem variables. For this to get called,
138 * our name must be associated with a repr via "sp_object_type_register". Best done through
139 * sp-object-repr.cpp's repr_name_entries array.
140 */
141 static void
142 sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
143 {
144 sp_object_read_attr(object, "inkscape:path-effect");
146 if (((SPObjectClass *) parent_class)->build) {
147 ((SPObjectClass *) parent_class)->build(object, document, repr);
148 }
149 }
151 /**
152 * Drops any allocated memory.
153 */
154 static void
155 sp_lpe_item_release(SPObject *object)
156 {
157 SPLPEItem *lpeitem;
158 lpeitem = (SPLPEItem *) object;
160 lpeitem->lpe_modified_connection.disconnect();
161 lpeitem->lpe_modified_connection.~connection();
163 if (((SPObjectClass *) parent_class)->release)
164 ((SPObjectClass *) parent_class)->release(object);
165 }
167 /**
168 * Sets a specific value in the SPLPEItem.
169 */
170 static void
171 sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value)
172 {
173 SPLPEItem *lpeitem = (SPLPEItem *) object;
175 switch (key) {
176 case SP_ATTR_INKSCAPE_PATH_EFFECT:
177 {
178 lpeitem->current_path_effect = NULL;
180 // Disable the path effects while populating the LPE list
181 sp_lpe_item_enable_path_effects(lpeitem, false);
183 // Clear the path effect list
184 PathEffectList::iterator it = lpeitem->path_effect_list->begin();
185 while ( it != lpeitem->path_effect_list->end() )
186 {
187 (*it)->unlink();
188 delete *it;
189 it = lpeitem->path_effect_list->erase(it);
190 }
192 // Parse the contents of "value" to rebuild the path effect reference list
193 if ( value ) {
194 std::istringstream iss(value);
195 std::string href;
196 while (std::getline(iss, href, ';'))
197 {
198 Inkscape::LivePathEffect::LPEObjectReference *path_effect_ref;
199 path_effect_ref = new Inkscape::LivePathEffect::LPEObjectReference(SP_OBJECT(lpeitem));
200 path_effect_ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(lpeobject_ref_changed), SP_LPE_ITEM(object)));
201 // Now do the attaching, which emits the changed signal.
202 // Fixme, it should not do this changed signal and updating before all effects are added to the path_effect_list
203 try {
204 path_effect_ref->link(href.c_str());
205 } catch (Inkscape::BadURIException &e) {
206 g_warning("BadURIException: %s", e.what());
207 path_effect_ref->unlink();
208 delete path_effect_ref;
209 path_effect_ref = NULL;
210 }
212 if (path_effect_ref) {
213 lpeitem->path_effect_list->push_back(path_effect_ref);
214 }
215 }
216 }
218 sp_lpe_item_enable_path_effects(lpeitem, true);
219 }
220 break;
221 default:
222 if (((SPObjectClass *) parent_class)->set) {
223 ((SPObjectClass *) parent_class)->set(object, key, value);
224 }
225 break;
226 }
227 }
229 /**
230 * Receives update notifications.
231 */
232 static void
233 sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags)
234 {
235 if (((SPObjectClass *) parent_class)->update) {
236 ((SPObjectClass *) parent_class)->update(object, ctx, flags);
237 }
238 }
240 /**
241 * Sets modified flag for all sub-item views.
242 */
243 static void
244 sp_lpe_item_modified (SPObject *object, unsigned int flags)
245 {
247 if (SP_IS_GROUP(object) && (flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_USER_MODIFIED_FLAG_B)) {
248 sp_lpe_item_update_patheffect(SP_LPE_ITEM(object), true, true);
249 }
251 if (((SPObjectClass *) (parent_class))->modified) {
252 (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
253 }
254 }
256 /**
257 * Writes its settings to an incoming repr object, if any.
258 */
259 static Inkscape::XML::Node *
260 sp_lpe_item_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
261 {
262 SPLPEItem *lpeitem = (SPLPEItem *) object;
264 if ( sp_lpe_item_has_path_effect(lpeitem) ) {
265 std::string href = patheffectlist_write_svg(*lpeitem->path_effect_list);
266 repr->setAttribute("inkscape:path-effect", href.c_str());
267 } else {
268 repr->setAttribute("inkscape:path-effect", NULL);
269 }
271 if (((SPObjectClass *)(parent_class))->write) {
272 ((SPObjectClass *)(parent_class))->write(object, xml_doc, repr, flags);
273 }
275 return repr;
276 }
278 void sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve) {
279 if (!lpeitem) return;
280 if (!curve) return;
282 if (sp_lpe_item_has_path_effect(lpeitem) && sp_lpe_item_path_effects_enabled(lpeitem)) {
283 for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
284 {
285 LivePathEffectObject *lpeobj = (*it)->lpeobject;
286 if (!lpeobj) {
287 g_warning("sp_lpe_item_perform_path_effect - NULL lpeobj in list!");
288 return;
289 }
290 if (!lpeobj->lpe) {
291 g_warning("sp_lpe_item_perform_path_effect - lpeobj without lpe!");
292 return;
293 }
295 Inkscape::LivePathEffect::Effect *lpe = lpeobj->lpe;
296 if (lpe->isVisible()) {
297 if (lpe->acceptsNumParams() > 0 && !lpe->pathParamAccepted()) {
298 // if the effect expects mouse input before being applied and the input is not finished
299 // yet, we don't alter the path
300 return;
301 }
303 // Groups have their doBeforeEffect called elsewhere
304 if (!SP_IS_GROUP(lpeitem)) {
305 lpe->doBeforeEffect(lpeitem);
306 }
308 try {
309 lpe->doEffect(curve);
310 }
311 catch (std::exception & e) {
312 g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what());
313 SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
314 _("An exception occurred during execution of the Path Effect.") );
315 }
316 }
317 }
318 }
319 }
321 /**
322 * Calls any registered handlers for the update_patheffect action
323 */
324 void
325 sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write)
326 {
327 #ifdef SHAPE_VERBOSE
328 g_message("sp_lpe_item_update_patheffect: %p\n", lpeitem);
329 #endif
330 g_return_if_fail (lpeitem != NULL);
331 g_return_if_fail (SP_IS_LPE_ITEM (lpeitem));
333 if (!sp_lpe_item_path_effects_enabled(lpeitem))
334 return;
336 SPLPEItem *top;
338 if (wholetree) {
339 SPObject *prev_parent = lpeitem;
340 SPObject *parent = prev_parent->parent;
341 while (parent && SP_IS_LPE_ITEM(parent) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent))) {
342 prev_parent = parent;
343 parent = prev_parent->parent;
344 }
345 top = SP_LPE_ITEM(prev_parent);
346 }
347 else {
348 top = lpeitem;
349 }
351 // TODO: ditch inkscape_active_desktop()
352 SPDesktop *desktop = inkscape_active_desktop();
353 if (desktop) {
354 //sp_lpe_item_remove_temporary_canvasitems(lpeitem, desktop);
355 //sp_lpe_item_add_temporary_canvasitems(lpeitem, desktop);
356 }
358 if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect) {
359 SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect (top, write);
360 }
361 }
363 /**
364 * Gets called when (re)attached to another lpeobject.
365 */
366 static void
367 lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem)
368 {
369 if (old_ref) {
370 sp_signal_disconnect_by_data(old_ref, lpeitem);
371 }
372 if ( IS_LIVEPATHEFFECT(ref) && ref != lpeitem )
373 {
374 lpeitem->lpe_modified_connection.disconnect();
375 lpeitem->lpe_modified_connection = ref->connectModified(sigc::bind(sigc::ptr_fun(&lpeobject_ref_modified), lpeitem));
376 lpeobject_ref_modified(ref, 0, lpeitem);
377 }
378 }
380 /**
381 * Gets called when lpeobject repr contents change: i.e. parameter change.
382 */
383 static void
384 lpeobject_ref_modified(SPObject */*href*/, guint /*flags*/, SPLPEItem *lpeitem)
385 {
386 sp_lpe_item_update_patheffect (lpeitem, true, true);
387 }
389 static void
390 sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem)
391 {
392 if (SP_IS_GROUP(lpeitem)) {
393 GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
394 for ( GSList const *iter = item_list; iter; iter = iter->next ) {
395 SPObject *subitem = static_cast<SPObject *>(iter->data);
396 if (SP_IS_LPE_ITEM(subitem)) {
397 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(subitem));
398 }
399 }
400 }
401 else if (SP_IS_PATH(lpeitem)) {
402 Inkscape::XML::Node *pathrepr = SP_OBJECT_REPR(lpeitem);
403 if ( !pathrepr->attribute("inkscape:original-d") ) {
404 pathrepr->setAttribute("inkscape:original-d", pathrepr->attribute("d"));
405 }
406 }
407 }
409 static void
410 sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem)
411 {
412 if (SP_IS_GROUP(lpeitem)) {
413 GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
414 for ( GSList const *iter = item_list; iter; iter = iter->next ) {
415 SPObject *subitem = static_cast<SPObject *>(iter->data);
416 if (SP_IS_LPE_ITEM(subitem)) {
417 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(subitem));
418 }
419 }
420 }
421 else if (SP_IS_PATH(lpeitem)) {
422 Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem);
423 if (!sp_lpe_item_has_path_effect_recursive(lpeitem)
424 && repr->attribute("inkscape:original-d")) {
425 repr->setAttribute("d", repr->attribute("inkscape:original-d"));
426 repr->setAttribute("inkscape:original-d", NULL);
427 }
428 else {
429 sp_lpe_item_update_patheffect(lpeitem, true, true);
430 }
431 }
432 }
434 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset)
435 {
436 if (value) {
437 // Apply the path effects here because in the casse of a group, lpe->resetDefaults
438 // needs that all the subitems have their effects applied
439 sp_lpe_item_update_patheffect(lpeitem, false, true);
441 // Disable the path effects while preparing the new lpe
442 sp_lpe_item_enable_path_effects(lpeitem, false);
444 // Add the new reference to the list of LPE references
445 HRefList hreflist;
446 for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
447 {
448 hreflist.push_back( std::string((*it)->lpeobject_href) );
449 }
450 hreflist.push_back( std::string(value) );
451 std::string hrefs = hreflist_write_svg(hreflist);
453 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", hrefs.c_str());
455 // make sure there is an original-d for paths!!!
456 sp_lpe_item_create_original_path_recursive(lpeitem);
458 LivePathEffectObject *lpeobj = lpeitem->path_effect_list->back()->lpeobject;
459 if (lpeobj && lpeobj->lpe) {
460 Inkscape::LivePathEffect::Effect *lpe = lpeobj->lpe;
461 // Ask the path effect to reset itself if it doesn't have parameters yet
462 if (reset) {
463 // has to be called when all the subitems have their lpes applied
464 lpe->resetDefaults(lpeitem);
465 }
467 // perform this once when the effect is applied
468 lpe->doOnApply(SP_LPE_ITEM(lpeitem));
470 // if the effect expects a number of mouse clicks to set a parameter path, perform the
471 // necessary preparations
472 if (lpe->acceptsNumParams() > 0) {
473 lpe->doAcceptPathPreparations(lpeitem);
474 }
475 }
477 //Enable the path effects now that everything is ready to apply the new path effect
478 sp_lpe_item_enable_path_effects(lpeitem, true);
480 // Apply the path effect
481 sp_lpe_item_update_patheffect(lpeitem, true, true);
482 }
483 }
485 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj)
486 {
487 const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
488 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
489 sp_lpe_item_add_path_effect(lpeitem, hrefstr, false);
490 g_free(hrefstr);
491 }
493 void sp_lpe_item_remove_current_path_effect(SPLPEItem *lpeitem, bool keep_paths)
494 {
495 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
496 if (!lperef)
497 return;
499 PathEffectList new_list = *lpeitem->path_effect_list;
500 new_list.remove(lperef); //current lpe ref is always our 'own' pointer from the path_effect_list
501 std::string r = patheffectlist_write_svg(new_list);
503 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
505 if (!keep_paths) {
506 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
507 }
508 }
510 void sp_lpe_item_remove_all_path_effects(SPLPEItem *lpeitem, bool keep_paths)
511 {
512 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
514 if (!keep_paths) {
515 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
516 }
517 }
519 void sp_lpe_item_down_current_path_effect(SPLPEItem *lpeitem)
520 {
521 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
522 if (!lperef)
523 return;
525 PathEffectList new_list = *lpeitem->path_effect_list;
526 PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
527 if (cur_it != new_list.end()) {
528 PathEffectList::iterator down_it = cur_it;
529 down_it++;
530 if (down_it != new_list.end()) { // perhaps current effect is already last effect
531 std::iter_swap(cur_it, down_it);
532 }
533 }
534 std::string r = patheffectlist_write_svg(new_list);
535 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
537 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
538 }
540 void sp_lpe_item_up_current_path_effect(SPLPEItem *lpeitem)
541 {
542 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
543 if (!lperef)
544 return;
546 PathEffectList new_list = *lpeitem->path_effect_list;
547 PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef );
548 if (cur_it != new_list.end() && cur_it != new_list.begin()) {
549 PathEffectList::iterator up_it = cur_it;
550 up_it--;
551 std::iter_swap(cur_it, up_it);
552 }
553 std::string r = patheffectlist_write_svg(new_list);
555 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
557 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
558 }
561 bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem)
562 {
563 return !lpeitem->path_effect_list->empty();
564 }
566 bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem)
567 {
568 SPObject *parent = lpeitem->parent;
569 if (parent && SP_IS_LPE_ITEM(parent)) {
570 return sp_lpe_item_has_path_effect(lpeitem) || sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent));
571 }
572 else {
573 return sp_lpe_item_has_path_effect(lpeitem);
574 }
575 }
577 void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt)
578 {
579 Inkscape::LivePathEffect::LPEObjectReference *lperef = sp_lpe_item_get_current_lpereference(lpeitem);
580 if (lperef && lperef->lpeobject && lperef->lpeobject->lpe) {
581 lperef->lpeobject->lpe->editNextParamOncanvas(SP_ITEM(lpeitem), dt);
582 }
583 }
585 static void
586 sp_lpe_item_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
587 {
588 if (((SPObjectClass *) (parent_class))->child_added)
589 (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref);
591 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
592 SPObject *ochild = sp_object_get_child_by_repr(object, child);
593 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
594 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(ochild));
595 }
596 }
597 }
599 static void
600 sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child)
601 {
602 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
603 SPObject *ochild = sp_object_get_child_by_repr(object, child);
604 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
605 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(ochild));
606 }
607 }
609 if (((SPObjectClass *) (parent_class))->remove_child)
610 (* ((SPObjectClass *) (parent_class))->remove_child) (object, child);
611 }
613 static std::string patheffectlist_write_svg(PathEffectList const & list)
614 {
615 HRefList hreflist;
616 for (PathEffectList::const_iterator it = list.begin(); it != list.end(); ++it)
617 {
618 hreflist.push_back( std::string((*it)->lpeobject_href) );
619 }
620 return hreflist_write_svg(hreflist);
621 }
623 /**
624 * THE function that should be used to generate any patheffectlist string.
625 * one of the methods to change the effect list:
626 * - create temporary href list
627 * - populate the templist with the effects from the old list that you want to have and their order
628 * - call this function with temp list as param
629 */
630 static std::string hreflist_write_svg(HRefList const & list)
631 {
632 std::string r;
633 bool semicolon_first = false;
634 for (HRefList::const_iterator it = list.begin(); it != list.end(); ++it)
635 {
636 if (semicolon_first) {
637 r += ';';
638 }
639 semicolon_first = true;
641 r += (*it);
642 }
643 return r;
644 }
646 // Return a copy of the effect list
647 PathEffectList sp_lpe_item_get_effect_list(SPLPEItem *lpeitem)
648 {
649 return *lpeitem->path_effect_list;
650 }
652 Inkscape::LivePathEffect::LPEObjectReference* sp_lpe_item_get_current_lpereference(SPLPEItem *lpeitem)
653 {
654 if (!lpeitem->current_path_effect && !lpeitem->path_effect_list->empty())
655 sp_lpe_item_set_current_path_effect(lpeitem, lpeitem->path_effect_list->back());
657 return lpeitem->current_path_effect;
658 }
660 Inkscape::LivePathEffect::Effect* sp_lpe_item_get_current_lpe(SPLPEItem *lpeitem)
661 {
662 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
664 if (lperef && lperef->lpeobject)
665 return lperef->lpeobject->lpe;
666 else
667 return NULL;
668 }
670 bool sp_lpe_item_set_current_path_effect(SPLPEItem *lpeitem, Inkscape::LivePathEffect::LPEObjectReference* lperef)
671 {
672 for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); it++) {
673 if ((*it)->lpeobject_repr == lperef->lpeobject_repr) {
674 lpeobject_ref_changed(NULL, (*it)->lpeobject, SP_LPE_ITEM(lpeitem)); // FIXME: explain why this is here?
675 lpeitem->current_path_effect = (*it); // current_path_effect should always be a pointer from the path_effect_list !
676 return true;
677 }
678 }
680 return false;
681 }
683 void sp_lpe_item_replace_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * old_lpeobj,
684 LivePathEffectObject * new_lpeobj)
685 {
686 HRefList hreflist;
687 for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
688 {
689 if ((*it)->lpeobject == old_lpeobj) {
690 const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
691 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
692 hreflist.push_back( std::string(hrefstr) );
693 g_free(hrefstr);
694 }
695 else {
696 hreflist.push_back( std::string((*it)->lpeobject_href) );
697 }
698 }
699 std::string r = hreflist_write_svg(hreflist);
700 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
701 }
703 // Enable or disable the path effects of the item.
704 // The counter allows nested calls
705 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable)
706 {
707 if (enable) {
708 lpeitem->path_effects_enabled++;
709 }
710 else {
711 lpeitem->path_effects_enabled--;
712 }
713 }
715 // Are the path effects enabled on this item ?
716 bool sp_lpe_item_path_effects_enabled(SPLPEItem *lpeitem)
717 {
718 return lpeitem->path_effects_enabled > 0;
719 }
721 void
722 sp_lpe_item_add_temporary_canvasitems(SPLPEItem *lpeitem, SPDesktop *desktop)
723 {
724 if (lpeitem->adding_helperpaths) {
725 return;
726 }
727 lpeitem->adding_helperpaths = true;
728 // FIXME: for some reason it seems that we must create the variable lpe AFTER checking
729 // for adding_helperpaths == true; otherwise we get a crash on startup. why??
730 Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(lpeitem);
731 if (lpe) {
732 // TODO: can we just update the tempitem's SPCurve instead of recreating it each time?
733 lpe->addHelperPaths(lpeitem, desktop);
734 }
735 lpeitem->adding_helperpaths = false;
736 }
738 void
739 sp_lpe_item_remove_temporary_canvasitems(SPLPEItem *lpeitem, SPDesktop *desktop)
740 {
741 g_return_if_fail(lpeitem);
742 g_return_if_fail(desktop);
744 if (lpeitem->removing_helperpaths) {
745 return;
746 }
747 lpeitem->removing_helperpaths = true;
749 // destroy all temporary canvasitems created by LPEs
750 std::vector<Inkscape::Display::TemporaryItem*>::iterator i;
751 for (i = lpeitem->lpe_helperpaths.begin(); i != lpeitem->lpe_helperpaths.end(); ++i) {
752 desktop->remove_temporary_canvasitem(*i);
753 }
755 lpeitem->removing_helperpaths = false;
756 }
758 /*
759 Local Variables:
760 mode:c++
761 c-file-style:"stroustrup"
762 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
763 indent-tabs-mode:nil
764 fill-column:99
765 End:
766 */
767 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :