Code

Fix LPEs and break mask transform undo
[inkscape.git] / src / live_effects / effect.cpp
1 #define INKSCAPE_LIVEPATHEFFECT_CPP
3 /*
4  * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
5  *
6  * Released under GNU GPL, read the file 'COPYING' for more information
7  */
9 //#define LPE_ENABLE_TEST_EFFECTS
11 #include "live_effects/effect.h"
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include "xml/node-event-vector.h"
18 #include "sp-object.h"
19 #include "attributes.h"
20 #include "message-stack.h"
21 #include "desktop.h"
22 #include "inkscape.h"
23 #include "document.h"
24 #include "document-private.h"
25 #include "xml/document.h"
26 #include <glibmm/i18n.h>
27 #include "pen-context.h"
28 #include "tools-switch.h"
29 #include "message-stack.h"
30 #include "desktop.h"
31 #include "nodepath.h"
32 #include "knotholder.h"
34 #include "live_effects/lpeobject.h"
35 #include "live_effects/parameter/parameter.h"
36 #include <glibmm/ustring.h>
37 #include "display/curve.h"
38 #include <gtkmm.h>
40 #include <exception>
42 #include <2geom/sbasis-to-bezier.h>
43 #include <2geom/matrix.h>
44 #include <2geom/pathvector.h>
46 // include effects:
47 #include "live_effects/lpe-patternalongpath.h"
48 #include "live_effects/lpe-bendpath.h"
49 #include "live_effects/lpe-sketch.h"
50 #include "live_effects/lpe-vonkoch.h"
51 #include "live_effects/lpe-knot.h"
52 #include "live_effects/lpe-rough-hatches.h"
53 #include "live_effects/lpe-dynastroke.h"
54 #include "live_effects/lpe-test-doEffect-stack.h"
55 #include "live_effects/lpe-gears.h"
56 #include "live_effects/lpe-curvestitch.h"
57 #include "live_effects/lpe-circle_with_radius.h"
58 #include "live_effects/lpe-perspective_path.h"
59 #include "live_effects/lpe-spiro.h"
60 #include "live_effects/lpe-lattice.h"
61 #include "live_effects/lpe-envelope.h"
62 #include "live_effects/lpe-constructgrid.h"
63 #include "live_effects/lpe-perp_bisector.h"
64 #include "live_effects/lpe-tangent_to_curve.h"
65 #include "live_effects/lpe-mirror_symmetry.h"
66 #include "live_effects/lpe-circle_3pts.h"
67 #include "live_effects/lpe-angle_bisector.h"
68 #include "live_effects/lpe-parallel.h"
69 #include "live_effects/lpe-copy_rotate.h"
70 #include "live_effects/lpe-offset.h"
71 #include "live_effects/lpe-ruler.h"
72 #include "live_effects/lpe-boolops.h"
73 #include "live_effects/lpe-interpolate.h"
74 #include "live_effects/lpe-text_label.h"
75 #include "live_effects/lpe-path_length.h"
76 #include "live_effects/lpe-line_segment.h"
77 #include "live_effects/lpe-recursiveskeleton.h"
78 #include "live_effects/lpe-extrude.h"
81 namespace Inkscape {
83 namespace LivePathEffect {
85 const Util::EnumData<EffectType> LPETypeData[] = {
86     // {constant defined in effect-enum.h, N_("name of your effect"), "name of your effect in SVG"}
87 #ifdef LPE_ENABLE_TEST_EFFECTS
88     {DOEFFECTSTACK_TEST,    N_("doEffect stack test"),     "doeffectstacktest"},
89     {ANGLE_BISECTOR,        N_("Angle bisector"),          "angle_bisector"},
90     // TRANSLATORS: boolean operations
91     {BOOLOPS,               N_("Boolops"),                 "boolops"},
92     {CIRCLE_WITH_RADIUS,    N_("Circle (by center and radius)"),   "circle_with_radius"},
93     {CIRCLE_3PTS,           N_("Circle by 3 points"),      "circle_3pts"},
94     {DYNASTROKE,            N_("Dynamic stroke"),          "dynastroke"},
95     {EXTRUDE,               N_("Extrude"),                 "extrude"},
96     {LATTICE,               N_("Lattice Deformation"),     "lattice"},
97     {LINE_SEGMENT,          N_("Line Segment"),            "line_segment"},
98     {MIRROR_SYMMETRY,       N_("Mirror symmetry"),         "mirror_symmetry"},
99     {OFFSET,                N_("Offset"),                  "offset"},
100     {PARALLEL,              N_("Parallel"),                "parallel"},
101     {PATH_LENGTH,           N_("Path length"),             "path_length"},
102     {PERP_BISECTOR,         N_("Perpendicular bisector"),  "perp_bisector"},
103     {PERSPECTIVE_PATH,      N_("Perspective path"),        "perspective_path"},
104     {COPY_ROTATE,           N_("Rotate copies"),           "copy_rotate"},
105     {RECURSIVE_SKELETON,    N_("Recursive skeleton"),      "recursive_skeleton"},
106     {TANGENT_TO_CURVE,      N_("Tangent to curve"),        "tangent_to_curve"},
107     {TEXT_LABEL,            N_("Text label"),              "text_label"},
108 #endif
109 /* 0.46 */
110     {BEND_PATH,             N_("Bend"),                     "bend_path"},
111     {GEARS,                 N_("Gears"),                   "gears"},
112     {PATTERN_ALONG_PATH,    N_("Pattern Along Path"),      "skeletal"},   // for historic reasons, this effect is called skeletal(strokes) in Inkscape:SVG
113     {CURVE_STITCH,          N_("Stitch Sub-Paths"),        "curvestitching"},
114 /* 0.47 */
115     {VONKOCH,               N_("VonKoch"),                 "vonkoch"},
116     {KNOT,                  N_("Knot"),                    "knot"},
117     {CONSTRUCT_GRID,        N_("Construct grid"),          "construct_grid"},
118     {SPIRO,                 N_("Spiro spline"),            "spiro"},
119     {ENVELOPE,              N_("Envelope Deformation"),    "envelope"},
120     {INTERPOLATE,           N_("Interpolate Sub-Paths"),   "interpolate"},
121     {ROUGH_HATCHES,         N_("Hatches (rough)"),         "rough_hatches"},
122     {SKETCH,                N_("Sketch"),                  "sketch"},
123     {RULER,                 N_("Ruler"),                   "ruler"},
124 };
125 const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData));
127 int
128 Effect::acceptsNumClicks(EffectType type) {
129     switch (type) {
130         case INVALID_LPE: return -1; // in case we want to distinguish between invalid LPE and valid ones that expect zero clicks
131         case ANGLE_BISECTOR: return 3;
132         case CIRCLE_3PTS: return 3;
133         case CIRCLE_WITH_RADIUS: return 2;
134         case LINE_SEGMENT: return 2;
135         case PERP_BISECTOR: return 2;
136         default: return 0;
137     }
140 Effect*
141 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
143     Effect* neweffect = NULL;
144     switch (lpenr) {
145         case PATTERN_ALONG_PATH:
146             neweffect = static_cast<Effect*> ( new LPEPatternAlongPath(lpeobj) );
147             break;
148         case BEND_PATH:
149             neweffect = static_cast<Effect*> ( new LPEBendPath(lpeobj) );
150             break;
151         case SKETCH:
152             neweffect = static_cast<Effect*> ( new LPESketch(lpeobj) );
153             break;
154         case ROUGH_HATCHES:
155             neweffect = static_cast<Effect*> ( new LPERoughHatches(lpeobj) );
156             break;
157         case VONKOCH:
158             neweffect = static_cast<Effect*> ( new LPEVonKoch(lpeobj) );
159             break;
160         case KNOT:
161             neweffect = static_cast<Effect*> ( new LPEKnot(lpeobj) );
162             break;
163         case GEARS:
164             neweffect = static_cast<Effect*> ( new LPEGears(lpeobj) );
165             break;
166         case CURVE_STITCH:
167             neweffect = static_cast<Effect*> ( new LPECurveStitch(lpeobj) );
168             break;
169         case LATTICE:
170             neweffect = static_cast<Effect*> ( new LPELattice(lpeobj) );
171             break;
172         case ENVELOPE:
173             neweffect = static_cast<Effect*> ( new LPEEnvelope(lpeobj) );
174             break;
175         case CIRCLE_WITH_RADIUS:
176             neweffect = static_cast<Effect*> ( new LPECircleWithRadius(lpeobj) );
177             break;
178         case PERSPECTIVE_PATH:
179             neweffect = static_cast<Effect*> ( new LPEPerspectivePath(lpeobj) );
180             break;
181         case SPIRO:
182             neweffect = static_cast<Effect*> ( new LPESpiro(lpeobj) );
183             break;
184         case CONSTRUCT_GRID:
185             neweffect = static_cast<Effect*> ( new LPEConstructGrid(lpeobj) );
186             break;
187         case PERP_BISECTOR:
188             neweffect = static_cast<Effect*> ( new LPEPerpBisector(lpeobj) );
189             break;
190         case TANGENT_TO_CURVE:
191             neweffect = static_cast<Effect*> ( new LPETangentToCurve(lpeobj) );
192             break;
193         case MIRROR_SYMMETRY:
194             neweffect = static_cast<Effect*> ( new LPEMirrorSymmetry(lpeobj) );
195             break;
196         case CIRCLE_3PTS:
197             neweffect = static_cast<Effect*> ( new LPECircle3Pts(lpeobj) );
198             break;
199         case ANGLE_BISECTOR:
200             neweffect = static_cast<Effect*> ( new LPEAngleBisector(lpeobj) );
201             break;
202         case PARALLEL:
203             neweffect = static_cast<Effect*> ( new LPEParallel(lpeobj) );
204             break;
205         case COPY_ROTATE:
206             neweffect = static_cast<Effect*> ( new LPECopyRotate(lpeobj) );
207             break;
208         case OFFSET:
209             neweffect = static_cast<Effect*> ( new LPEOffset(lpeobj) );
210             break;
211         case RULER:
212             neweffect = static_cast<Effect*> ( new LPERuler(lpeobj) );
213             break;
214         case BOOLOPS:
215             neweffect = static_cast<Effect*> ( new LPEBoolops(lpeobj) );
216             break;
217         case INTERPOLATE:
218             neweffect = static_cast<Effect*> ( new LPEInterpolate(lpeobj) );
219             break;
220         case TEXT_LABEL:
221             neweffect = static_cast<Effect*> ( new LPETextLabel(lpeobj) );
222             break;
223         case PATH_LENGTH:
224             neweffect = static_cast<Effect*> ( new LPEPathLength(lpeobj) );
225             break;
226         case LINE_SEGMENT:
227             neweffect = static_cast<Effect*> ( new LPELineSegment(lpeobj) );
228             break;
229         case DOEFFECTSTACK_TEST:
230             neweffect = static_cast<Effect*> ( new LPEdoEffectStackTest(lpeobj) );
231             break;
232         case DYNASTROKE:
233             neweffect = static_cast<Effect*> ( new LPEDynastroke(lpeobj) );
234             break;
235         case RECURSIVE_SKELETON:
236             neweffect = static_cast<Effect*> ( new LPERecursiveSkeleton(lpeobj) );
237             break;
238         case EXTRUDE:
239             neweffect = static_cast<Effect*> ( new LPEExtrude(lpeobj) );
240             break;
241         default:
242             g_warning("LivePathEffect::Effect::New   called with invalid patheffect type (%d)", lpenr);
243             neweffect = NULL;
244             break;
245     }
247     if (neweffect) {
248         neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));
249     }
251     return neweffect;
254 void
255 Effect::createAndApply(const char* name, SPDocument *doc, SPItem *item)
257     // Path effect definition
258     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
259     Inkscape::XML::Node *repr = xml_doc->createElement("inkscape:path-effect");
260     repr->setAttribute("effect", name);
262     SP_OBJECT_REPR(SP_DOCUMENT_DEFS(doc))->addChild(repr, NULL); // adds to <defs> and assigns the 'id' attribute
263     const gchar * repr_id = repr->attribute("id");
264     Inkscape::GC::release(repr);
266     gchar *href = g_strdup_printf("#%s", repr_id);
267     sp_lpe_item_add_path_effect(SP_LPE_ITEM(item), href, true);
268     g_free(href);
271 void
272 Effect::createAndApply(EffectType type, SPDocument *doc, SPItem *item)
274     createAndApply(LPETypeConverter.get_key(type).c_str(), doc, item);
277 Effect::Effect(LivePathEffectObject *lpeobject)
278     : oncanvasedit_it(0),
279       is_visible(_("Is visible?"), _("If unchecked, the effect remains applied to the object but is temporarily disabled on canvas"), "is_visible", &wr, this, true),
280       show_orig_path(false),
281       lpeobj(lpeobject),
282       concatenate_before_pwd2(false),
283       provides_own_flash_paths(true), // is automatically set to false if providesOwnFlashPaths() is not overridden
284       is_ready(false) // is automatically set to false if providesOwnFlashPaths() is not overridden
286     registerParameter( dynamic_cast<Parameter *>(&is_visible) );
287     is_visible.widget_is_visible = false;
290 Effect::~Effect()
294 Glib::ustring
295 Effect::getName()
297     if (lpeobj->effecttype_set && LPETypeConverter.is_valid_id(lpeobj->effecttype) )
298         return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );
299     else
300         return Glib::ustring( _("No effect") );
303 EffectType
304 Effect::effectType() {
305     return lpeobj->effecttype;
308 /**
309  * Is performed a single time when the effect is freshly applied to a path
310  */
311 void
312 Effect::doOnApply (SPLPEItem */*lpeitem*/)
316 /**
317  * Is performed each time before the effect is updated.
318  */
319 void
320 Effect::doBeforeEffect (SPLPEItem */*lpeitem*/)
322     //Do nothing for simple effects
325 /**
326  * Effects can have a parameter path set before they are applied by accepting a nonzero number of
327  * mouse clicks. This method activates the pen context, which waits for the specified number of
328  * clicks. Override Effect::acceptsNumClicks() to return the number of expected mouse clicks.
329  */
330 void
331 Effect::doAcceptPathPreparations(SPLPEItem *lpeitem)
333     // switch to pen context
334     SPDesktop *desktop = inkscape_active_desktop(); // TODO: Is there a better method to find the item's desktop?
335     if (!tools_isactive(desktop, TOOLS_FREEHAND_PEN)) {
336         tools_switch(desktop, TOOLS_FREEHAND_PEN);
337     }
339     SPEventContext *ec = desktop->event_context;
340     SPPenContext *pc = SP_PEN_CONTEXT(ec);
341     pc->expecting_clicks_for_LPE = this->acceptsNumClicks();
342     pc->waiting_LPE = this;
343     pc->waiting_item = lpeitem;
344     pc->polylines_only = true;
346     ec->desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE,
347         g_strdup_printf(_("Please specify a parameter path for the LPE '%s' with %d mouse clicks"),
348                         getName().c_str(), acceptsNumClicks()));
351 void
352 Effect::writeParamsToSVG() {
353     std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
354     for (p = param_vector.begin(); p != param_vector.end(); ++p) {
355         (*p)->write_to_SVG();
356     }
359 /**
360  * If the effect expects a path parameter (specified by a number of mouse clicks) before it is
361  * applied, this is the method that processes the resulting path. Override it to customize it for
362  * your LPE. But don't forget to call the parent method so that is_ready is set to true!
363  */
364 void
365 Effect::acceptParamPath (SPPath */*param_path*/) {
366     setReady();
369 /*
370  *  Here be the doEffect function chain:
371  */
372 void
373 Effect::doEffect (SPCurve * curve)
375     std::vector<Geom::Path> orig_pathv = curve->get_pathvector();
377     std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
379     curve->set_pathvector(result_pathv);
382 std::vector<Geom::Path>
383 Effect::doEffect_path (std::vector<Geom::Path> const & path_in)
385     std::vector<Geom::Path> path_out;
387     if ( !concatenate_before_pwd2 ) {
388         // default behavior
389         for (unsigned int i=0; i < path_in.size(); i++) {
390             Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb();
391             Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
392             std::vector<Geom::Path> path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
393             // add the output path vector to the already accumulated vector:
394             for (unsigned int j=0; j < path.size(); j++) {
395                 path_out.push_back(path[j]);
396             }
397         }
398     } else {
399       // concatenate the path into possibly discontinuous pwd2
400         Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
401         for (unsigned int i=0; i < path_in.size(); i++) {
402             pwd2_in.concat( path_in[i].toPwSb() );
403         }
404         Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
405         path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
406     }
408     return path_out;
411 Geom::Piecewise<Geom::D2<Geom::SBasis> >
412 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
414     g_warning("Effect has no doEffect implementation");
415     return pwd2_in;
418 void
419 Effect::readallParameters(Inkscape::XML::Node * repr)
421     std::vector<Parameter *>::iterator it = param_vector.begin();
422     while (it != param_vector.end()) {
423         Parameter * param = *it;
424         const gchar * key = param->param_key.c_str();
425         const gchar * value = repr->attribute(key);
426         if (value) {
427             bool accepted = param->param_readSVGValue(value);
428             if (!accepted) {
429                 g_warning("Effect::readallParameters - '%s' not accepted for %s", value, key);
430             }
431         } else {
432             // set default value
433             param->param_set_default();
434         }
436         it++;
437     }
440 /* This function does not and SHOULD NOT write to XML */
441 void
442 Effect::setParameter(const gchar * key, const gchar * new_value)
444     Parameter * param = getParameter(key);
445     if (param) {
446         if (new_value) {
447             bool accepted = param->param_readSVGValue(new_value);
448             if (!accepted) {
449                 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);
450             }
451         } else {
452             // set default value
453             param->param_set_default();
454         }
455     }
458 void
459 Effect::registerParameter(Parameter * param)
461     param_vector.push_back(param);
464 // TODO: should we provide a way to alter the handle's appearance?
465 void
466 Effect::registerKnotHolderHandle(KnotHolderEntity* entity, const char* descr)
468     kh_entity_vector.push_back(std::make_pair(entity, descr));
471 /**
472  * Add all registered LPE knotholder handles to the knotholder
473  */
474 void
475 Effect::addHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
476     using namespace Inkscape::LivePathEffect;
478     // add handles provided by the effect itself
479     addKnotHolderEntities(knotholder, desktop, item);
481     // add handles provided by the effect's parameters (if any)
482     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
483         (*p)->addKnotHolderEntities(knotholder, desktop, item);
484     }
487 void
488 Effect::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
489     // TODO: The entities in kh_entity_vector are already instantiated during the call
490     //       to registerKnotHolderHandle(), but they are recreated here. Also, we must not
491     //       delete them when the knotholder is destroyed, whence the clumsy function
492     //       isDeletable(). If we could create entities of different classes dynamically,
493     //       this would be much nicer. How to do this?
494     std::vector<std::pair<KnotHolderEntity*, const char*> >::iterator i;
495     for (i = kh_entity_vector.begin(); i != kh_entity_vector.end(); ++i) {
496         KnotHolderEntity *entity = i->first;
497         const char *descr = i->second;
499         entity->create(desktop, item, knotholder, descr);
500         knotholder->add(entity);
501     }
504 /**
505  * Return a vector of PathVectors which contain all helperpaths that should be drawn by the effect.
506  * This is the function called by external code like SPLPEItem.
507  */
508 std::vector<Geom::PathVector>
509 Effect::getHelperPaths(SPLPEItem *lpeitem)
511     std::vector<Geom::PathVector> hp_vec;
513     if (!SP_IS_SHAPE(lpeitem)) {
514 //        g_print ("How to handle helperpaths for non-shapes?\n"); // non-shapes are for example SPGroups.
515         return hp_vec;
516     }
518     // TODO: we can probably optimize this by using a lot more references
519     //       rather than copying PathVectors all over the place
520     if (show_orig_path) {
521         // add original path to helperpaths
522         SPCurve* curve = sp_shape_get_curve (SP_SHAPE(lpeitem));
523         hp_vec.push_back(curve->get_pathvector());
524     }
526     // add other helperpaths provided by the effect itself
527     addCanvasIndicators(lpeitem, hp_vec);
529     // add helperpaths provided by the effect's parameters
530     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
531         (*p)->addCanvasIndicators(lpeitem, hp_vec);
532     }
534     return hp_vec;
537 /**
538  * Add possible canvas indicators (i.e., helperpaths other than the original path) to \a hp_vec
539  * This function should be overwritten by derived effects if they want to provide their own helperpaths.
540  */
541 void
542 Effect::addCanvasIndicators(SPLPEItem */*lpeitem*/, std::vector<Geom::PathVector> &/*hp_vec*/)
547 /**
548  * This *creates* a new widget, management of deletion should be done by the caller
549  */
550 Gtk::Widget *
551 Effect::newWidget(Gtk::Tooltips * tooltips)
553     // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
554     Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox() );
556     vbox->set_border_width(5);
558     std::vector<Parameter *>::iterator it = param_vector.begin();
559     while (it != param_vector.end()) {
560         if ((*it)->widget_is_visible) {
561             Parameter * param = *it;
562             Gtk::Widget * widg = param->param_newWidget(tooltips);
563             Glib::ustring * tip = param->param_getTooltip();
564             if (widg) {
565                 vbox->pack_start(*widg, true, true, 2);
566                 if (tip != NULL) {
567                     tooltips->set_tip(*widg, *tip);
568                 }
569             }
570         }
572         it++;
573     }
575     return dynamic_cast<Gtk::Widget *>(vbox);
579 Inkscape::XML::Node *
580 Effect::getRepr()
582     return SP_OBJECT_REPR(lpeobj);
585 SPDocument *
586 Effect::getSPDoc()
588     if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
589     return SP_OBJECT_DOCUMENT(lpeobj);
592 Parameter *
593 Effect::getParameter(const char * key)
595     Glib::ustring stringkey(key);
597     std::vector<Parameter *>::iterator it = param_vector.begin();
598     while (it != param_vector.end()) {
599         Parameter * param = *it;
600         if ( param->param_key == key) {
601             return param;
602         }
604         it++;
605     }
607     return NULL;
610 Parameter *
611 Effect::getNextOncanvasEditableParam()
613     if (param_vector.size() == 0) // no parameters
614         return NULL;
616     oncanvasedit_it++;
617     if (oncanvasedit_it >= static_cast<int>(param_vector.size())) {
618         oncanvasedit_it = 0;
619     }
620     int old_it = oncanvasedit_it;
622     do {
623         Parameter * param = param_vector[oncanvasedit_it];
624         if(param && param->oncanvas_editable) {
625             return param;
626         } else {
627             oncanvasedit_it++;
628             if (oncanvasedit_it == static_cast<int>(param_vector.size())) {  // loop round the map
629                 oncanvasedit_it = 0;
630             }
631         }
632     } while (oncanvasedit_it != old_it); // iterate until complete loop through map has been made
634     return NULL;
637 void
638 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
640     if (!desktop) return;
642     Parameter * param = getNextOncanvasEditableParam();
643     if (param) {
644         param->param_editOncanvas(item, desktop);
645         gchar *message = g_strdup_printf(_("Editing parameter <b>%s</b>."), param->param_label.c_str());
646         desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, message);
647         g_free(message);
648     } else {
649         desktop->messageStack()->flash( Inkscape::WARNING_MESSAGE,
650                                         _("None of the applied path effect's parameters can be edited on-canvas.") );
651     }
654 /* This function should reset the defaults and is used for example to initialize an effect right after it has been applied to a path
655 * The nice thing about this is that this function can use knowledge of the original path and set things accordingly for example to the size or origin of the original path!
656 */
657 void
658 Effect::resetDefaults(SPItem * /*item*/)
660     std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
661     for (p = param_vector.begin(); p != param_vector.end(); ++p) {
662         (*p)->param_set_default();
663         (*p)->write_to_SVG();
664     }
667 void
668 Effect::transform_multiply(Geom::Matrix const& postmul, bool set)
670     // cycle through all parameters. Most parameters will not need transformation, but path and point params do.
671     for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); it++) {
672         Parameter * param = *it;
673         param->param_transform_multiply(postmul, set);
674     }
677 // TODO: take _all_ parameters into account, not only PointParams
678 bool
679 Effect::providesKnotholder()
681     // does the effect actively provide any knotholder entities of its own?
682     if (kh_entity_vector.size() > 0)
683         return true;
685     // otherwise: are there any parameters that have knotholderentities?
686     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
687         if ((*p)->providesKnotHolderEntities()) {
688             return true;
689         }
690     }
692     return false;
695 } /* namespace LivePathEffect */
697 } /* namespace Inkscape */
699 /*
700   Local Variables:
701   mode:c++
702   c-file-style:"stroustrup"
703   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
704   indent-tabs-mode:nil
705   fill-column:99
706   End:
707 */
708 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :