2b0379cd4b86475106146c93db69035491eef282
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 = 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->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 }
238 }
240 /**
241 * Receives update notifications.
242 */
243 static void
244 sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags)
245 {
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);
264 }
266 /**
267 * Sets modified flag for all sub-item views.
268 */
269 static void
270 sp_lpe_item_modified (SPObject *object, unsigned int flags)
271 {
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 }
279 }
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)
286 {
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;
301 }
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 if (!lpeobj->lpe) {
316 g_warning("sp_lpe_item_perform_path_effect - lpeobj without lpe!");
317 return;
318 }
320 Inkscape::LivePathEffect::Effect *lpe = lpeobj->lpe;
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 }
344 }
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)
351 {
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->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 }
392 }
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)
399 {
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 }
409 }
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)
416 {
417 sp_lpe_item_update_patheffect (lpeitem, true, true);
418 }
420 static void
421 sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem)
422 {
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 }
438 }
440 static void
441 sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem)
442 {
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 }
463 }
465 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset)
466 {
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->lpe) {
491 Inkscape::LivePathEffect::Effect *lpe = lpeobj->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 }
511 }
513 void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj)
514 {
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);
519 }
521 void sp_lpe_item_remove_current_path_effect(SPLPEItem *lpeitem, bool keep_paths)
522 {
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 }
536 }
538 void sp_lpe_item_remove_all_path_effects(SPLPEItem *lpeitem, bool keep_paths)
539 {
540 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL);
542 if (!keep_paths) {
543 sp_lpe_item_cleanup_original_path_recursive(lpeitem);
544 }
545 }
547 void sp_lpe_item_down_current_path_effect(SPLPEItem *lpeitem)
548 {
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);
566 }
568 void sp_lpe_item_up_current_path_effect(SPLPEItem *lpeitem)
569 {
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);
586 }
589 bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem)
590 {
591 return !lpeitem->path_effect_list->empty();
592 }
594 bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem)
595 {
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 }
603 }
605 Inkscape::LivePathEffect::Effect*
606 sp_lpe_item_has_path_effect_of_type(SPLPEItem *lpeitem, int type)
607 {
608 std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i;
609 for (i = lpeitem->path_effect_list->begin(); i != lpeitem->path_effect_list->end(); ++i) {
610 if ((*i)->lpeobject->lpe->effectType() == type) {
611 return (*i)->lpeobject->lpe;
612 }
613 }
614 return NULL;
615 }
617 /* Return false if the item is not a path or already has a shape applied */
618 bool sp_lpe_item_can_accept_freehand_shape(SPLPEItem *lpeitem)
619 {
620 if (!SP_IS_PATH(lpeitem))
621 return false;
623 if (sp_lpe_item_has_path_effect_of_type(lpeitem, Inkscape::LivePathEffect::FREEHAND_SHAPE))
624 return false;
626 return true;
627 }
629 void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt)
630 {
631 Inkscape::LivePathEffect::LPEObjectReference *lperef = sp_lpe_item_get_current_lpereference(lpeitem);
632 if (lperef && lperef->lpeobject && lperef->lpeobject->lpe) {
633 lperef->lpeobject->lpe->editNextParamOncanvas(SP_ITEM(lpeitem), dt);
634 }
635 }
637 static void
638 sp_lpe_item_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
639 {
640 if (((SPObjectClass *) (parent_class))->child_added)
641 (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref);
643 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
644 SPObject *ochild = sp_object_get_child_by_repr(object, child);
645 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
646 sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(ochild));
647 }
648 }
649 }
651 static void
652 sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child)
653 {
654 if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) {
655 SPObject *ochild = sp_object_get_child_by_repr(object, child);
656 if ( ochild && SP_IS_LPE_ITEM(ochild) ) {
657 sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(ochild));
658 }
659 }
661 if (((SPObjectClass *) (parent_class))->remove_child)
662 (* ((SPObjectClass *) (parent_class))->remove_child) (object, child);
663 }
665 static std::string patheffectlist_write_svg(PathEffectList const & list)
666 {
667 HRefList hreflist;
668 for (PathEffectList::const_iterator it = list.begin(); it != list.end(); ++it)
669 {
670 hreflist.push_back( std::string((*it)->lpeobject_href) );
671 }
672 return hreflist_write_svg(hreflist);
673 }
675 /**
676 * THE function that should be used to generate any patheffectlist string.
677 * one of the methods to change the effect list:
678 * - create temporary href list
679 * - populate the templist with the effects from the old list that you want to have and their order
680 * - call this function with temp list as param
681 */
682 static std::string hreflist_write_svg(HRefList const & list)
683 {
684 std::string r;
685 bool semicolon_first = false;
686 for (HRefList::const_iterator it = list.begin(); it != list.end(); ++it)
687 {
688 if (semicolon_first) {
689 r += ';';
690 }
691 semicolon_first = true;
693 r += (*it);
694 }
695 return r;
696 }
698 // Return a copy of the effect list
699 PathEffectList sp_lpe_item_get_effect_list(SPLPEItem *lpeitem)
700 {
701 return *lpeitem->path_effect_list;
702 }
704 Inkscape::LivePathEffect::LPEObjectReference* sp_lpe_item_get_current_lpereference(SPLPEItem *lpeitem)
705 {
706 if (!lpeitem->current_path_effect && !lpeitem->path_effect_list->empty())
707 sp_lpe_item_set_current_path_effect(lpeitem, lpeitem->path_effect_list->back());
709 return lpeitem->current_path_effect;
710 }
712 Inkscape::LivePathEffect::Effect* sp_lpe_item_get_current_lpe(SPLPEItem *lpeitem)
713 {
714 Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem);
716 if (lperef && lperef->lpeobject)
717 return lperef->lpeobject->lpe;
718 else
719 return NULL;
720 }
722 bool sp_lpe_item_set_current_path_effect(SPLPEItem *lpeitem, Inkscape::LivePathEffect::LPEObjectReference* lperef)
723 {
724 for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); it++) {
725 if ((*it)->lpeobject_repr == lperef->lpeobject_repr) {
726 lpeobject_ref_changed(NULL, (*it)->lpeobject, SP_LPE_ITEM(lpeitem)); // FIXME: explain why this is here?
727 lpeitem->current_path_effect = (*it); // current_path_effect should always be a pointer from the path_effect_list !
728 return true;
729 }
730 }
732 return false;
733 }
735 void sp_lpe_item_replace_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * old_lpeobj,
736 LivePathEffectObject * new_lpeobj)
737 {
738 HRefList hreflist;
739 for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
740 {
741 if ((*it)->lpeobject == old_lpeobj) {
742 const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
743 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
744 hreflist.push_back( std::string(hrefstr) );
745 g_free(hrefstr);
746 }
747 else {
748 hreflist.push_back( std::string((*it)->lpeobject_href) );
749 }
750 }
751 std::string r = hreflist_write_svg(hreflist);
752 SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str());
753 }
755 // Enable or disable the path effects of the item.
756 // The counter allows nested calls
757 static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable)
758 {
759 if (enable) {
760 lpeitem->path_effects_enabled++;
761 }
762 else {
763 lpeitem->path_effects_enabled--;
764 }
765 }
767 // Are the path effects enabled on this item ?
768 bool sp_lpe_item_path_effects_enabled(SPLPEItem *lpeitem)
769 {
770 return lpeitem->path_effects_enabled > 0;
771 }
773 /*
774 Local Variables:
775 mode:c++
776 c-file-style:"stroustrup"
777 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
778 indent-tabs-mode:nil
779 fill-column:99
780 End:
781 */
782 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :