Code

Merge and cleanup of GSoC C++-ification project.
[inkscape.git] / src / live_effects / effect.cpp
1 /*
2  * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
3  *   Abhishek Sharma
4  *
5  * Released under GNU GPL, read the file 'COPYING' for more information
6  */
8 //#define LPE_ENABLE_TEST_EFFECTS
10 #include "live_effects/effect.h"
12 #ifdef HAVE_CONFIG_H
13 # include "config.h"
14 #endif
16 #include "xml/node-event-vector.h"
17 #include "sp-object.h"
18 #include "attributes.h"
19 #include "message-stack.h"
20 #include "desktop.h"
21 #include "inkscape.h"
22 #include "document.h"
23 #include "document-private.h"
24 #include "xml/document.h"
25 #include <glibmm/i18n.h>
26 #include "pen-context.h"
27 #include "tools-switch.h"
28 #include "message-stack.h"
29 #include "desktop.h"
30 #include "knotholder.h"
31 #include "sp-lpe-item.h"
32 #include "live_effects/lpeobject.h"
33 #include "live_effects/parameter/parameter.h"
34 #include <glibmm/ustring.h>
35 #include "display/curve.h"
36 #include <gtkmm.h>
38 #include <exception>
40 #include <2geom/sbasis-to-bezier.h>
41 #include <2geom/matrix.h>
42 #include <2geom/pathvector.h>
44 // include effects:
45 #include "live_effects/lpe-patternalongpath.h"
46 #include "live_effects/lpe-bendpath.h"
47 #include "live_effects/lpe-sketch.h"
48 #include "live_effects/lpe-vonkoch.h"
49 #include "live_effects/lpe-knot.h"
50 #include "live_effects/lpe-rough-hatches.h"
51 #include "live_effects/lpe-dynastroke.h"
52 #include "live_effects/lpe-test-doEffect-stack.h"
53 #include "live_effects/lpe-gears.h"
54 #include "live_effects/lpe-curvestitch.h"
55 #include "live_effects/lpe-circle_with_radius.h"
56 #include "live_effects/lpe-perspective_path.h"
57 #include "live_effects/lpe-spiro.h"
58 #include "live_effects/lpe-lattice.h"
59 #include "live_effects/lpe-envelope.h"
60 #include "live_effects/lpe-constructgrid.h"
61 #include "live_effects/lpe-perp_bisector.h"
62 #include "live_effects/lpe-tangent_to_curve.h"
63 #include "live_effects/lpe-mirror_symmetry.h"
64 #include "live_effects/lpe-circle_3pts.h"
65 #include "live_effects/lpe-angle_bisector.h"
66 #include "live_effects/lpe-parallel.h"
67 #include "live_effects/lpe-copy_rotate.h"
68 #include "live_effects/lpe-offset.h"
69 #include "live_effects/lpe-ruler.h"
70 #include "live_effects/lpe-boolops.h"
71 #include "live_effects/lpe-interpolate.h"
72 #include "live_effects/lpe-text_label.h"
73 #include "live_effects/lpe-path_length.h"
74 #include "live_effects/lpe-line_segment.h"
75 #include "live_effects/lpe-recursiveskeleton.h"
76 #include "live_effects/lpe-extrude.h"
77 #include "live_effects/lpe-powerstroke.h"
80 namespace Inkscape {
82 namespace LivePathEffect {
84 const Util::EnumData<EffectType> LPETypeData[] = {
85     // {constant defined in effect-enum.h, N_("name of your effect"), "name of your effect in SVG"}
86 #ifdef LPE_ENABLE_TEST_EFFECTS
87     {DOEFFECTSTACK_TEST,    N_("doEffect stack test"),     "doeffectstacktest"},
88     {ANGLE_BISECTOR,        N_("Angle bisector"),          "angle_bisector"},
89     // TRANSLATORS: boolean operations
90     {BOOLOPS,               N_("Boolops"),                 "boolops"},
91     {CIRCLE_WITH_RADIUS,    N_("Circle (by center and radius)"),   "circle_with_radius"},
92     {CIRCLE_3PTS,           N_("Circle by 3 points"),      "circle_3pts"},
93     {DYNASTROKE,            N_("Dynamic stroke"),          "dynastroke"},
94     {EXTRUDE,               N_("Extrude"),                 "extrude"},
95     {LATTICE,               N_("Lattice Deformation"),     "lattice"},
96     {LINE_SEGMENT,          N_("Line Segment"),            "line_segment"},
97     {MIRROR_SYMMETRY,       N_("Mirror symmetry"),         "mirror_symmetry"},
98     {OFFSET,                N_("Offset"),                  "offset"},
99     {PARALLEL,              N_("Parallel"),                "parallel"},
100     {PATH_LENGTH,           N_("Path length"),             "path_length"},
101     {PERP_BISECTOR,         N_("Perpendicular bisector"),  "perp_bisector"},
102     {PERSPECTIVE_PATH,      N_("Perspective path"),        "perspective_path"},
103     {POWERSTROKE,           N_("Power stroke"),            "powerstroke"},
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 /* 0.49 */
125 };
126 const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData));
128 int
129 Effect::acceptsNumClicks(EffectType type) {
130     switch (type) {
131         case INVALID_LPE: return -1; // in case we want to distinguish between invalid LPE and valid ones that expect zero clicks
132         case ANGLE_BISECTOR: return 3;
133         case CIRCLE_3PTS: return 3;
134         case CIRCLE_WITH_RADIUS: return 2;
135         case LINE_SEGMENT: return 2;
136         case PERP_BISECTOR: return 2;
137         default: return 0;
138     }
141 Effect*
142 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
144     Effect* neweffect = NULL;
145     switch (lpenr) {
146         case PATTERN_ALONG_PATH:
147             neweffect = static_cast<Effect*> ( new LPEPatternAlongPath(lpeobj) );
148             break;
149         case BEND_PATH:
150             neweffect = static_cast<Effect*> ( new LPEBendPath(lpeobj) );
151             break;
152         case SKETCH:
153             neweffect = static_cast<Effect*> ( new LPESketch(lpeobj) );
154             break;
155         case ROUGH_HATCHES:
156             neweffect = static_cast<Effect*> ( new LPERoughHatches(lpeobj) );
157             break;
158         case VONKOCH:
159             neweffect = static_cast<Effect*> ( new LPEVonKoch(lpeobj) );
160             break;
161         case KNOT:
162             neweffect = static_cast<Effect*> ( new LPEKnot(lpeobj) );
163             break;
164         case GEARS:
165             neweffect = static_cast<Effect*> ( new LPEGears(lpeobj) );
166             break;
167         case CURVE_STITCH:
168             neweffect = static_cast<Effect*> ( new LPECurveStitch(lpeobj) );
169             break;
170         case LATTICE:
171             neweffect = static_cast<Effect*> ( new LPELattice(lpeobj) );
172             break;
173         case ENVELOPE:
174             neweffect = static_cast<Effect*> ( new LPEEnvelope(lpeobj) );
175             break;
176         case CIRCLE_WITH_RADIUS:
177             neweffect = static_cast<Effect*> ( new LPECircleWithRadius(lpeobj) );
178             break;
179         case PERSPECTIVE_PATH:
180             neweffect = static_cast<Effect*> ( new LPEPerspectivePath(lpeobj) );
181             break;
182         case SPIRO:
183             neweffect = static_cast<Effect*> ( new LPESpiro(lpeobj) );
184             break;
185         case CONSTRUCT_GRID:
186             neweffect = static_cast<Effect*> ( new LPEConstructGrid(lpeobj) );
187             break;
188         case PERP_BISECTOR:
189             neweffect = static_cast<Effect*> ( new LPEPerpBisector(lpeobj) );
190             break;
191         case TANGENT_TO_CURVE:
192             neweffect = static_cast<Effect*> ( new LPETangentToCurve(lpeobj) );
193             break;
194         case MIRROR_SYMMETRY:
195             neweffect = static_cast<Effect*> ( new LPEMirrorSymmetry(lpeobj) );
196             break;
197         case CIRCLE_3PTS:
198             neweffect = static_cast<Effect*> ( new LPECircle3Pts(lpeobj) );
199             break;
200         case ANGLE_BISECTOR:
201             neweffect = static_cast<Effect*> ( new LPEAngleBisector(lpeobj) );
202             break;
203         case PARALLEL:
204             neweffect = static_cast<Effect*> ( new LPEParallel(lpeobj) );
205             break;
206         case COPY_ROTATE:
207             neweffect = static_cast<Effect*> ( new LPECopyRotate(lpeobj) );
208             break;
209         case OFFSET:
210             neweffect = static_cast<Effect*> ( new LPEOffset(lpeobj) );
211             break;
212         case RULER:
213             neweffect = static_cast<Effect*> ( new LPERuler(lpeobj) );
214             break;
215         case BOOLOPS:
216             neweffect = static_cast<Effect*> ( new LPEBoolops(lpeobj) );
217             break;
218         case INTERPOLATE:
219             neweffect = static_cast<Effect*> ( new LPEInterpolate(lpeobj) );
220             break;
221         case TEXT_LABEL:
222             neweffect = static_cast<Effect*> ( new LPETextLabel(lpeobj) );
223             break;
224         case PATH_LENGTH:
225             neweffect = static_cast<Effect*> ( new LPEPathLength(lpeobj) );
226             break;
227         case LINE_SEGMENT:
228             neweffect = static_cast<Effect*> ( new LPELineSegment(lpeobj) );
229             break;
230         case DOEFFECTSTACK_TEST:
231             neweffect = static_cast<Effect*> ( new LPEdoEffectStackTest(lpeobj) );
232             break;
233         case DYNASTROKE:
234             neweffect = static_cast<Effect*> ( new LPEDynastroke(lpeobj) );
235             break;
236         case RECURSIVE_SKELETON:
237             neweffect = static_cast<Effect*> ( new LPERecursiveSkeleton(lpeobj) );
238             break;
239         case EXTRUDE:
240             neweffect = static_cast<Effect*> ( new LPEExtrude(lpeobj) );
241             break;
242         case POWERSTROKE:
243             neweffect = static_cast<Effect*> ( new LPEPowerStroke(lpeobj) );
244             break;
245         default:
246             g_warning("LivePathEffect::Effect::New   called with invalid patheffect type (%d)", lpenr);
247             neweffect = NULL;
248             break;
249     }
251     if (neweffect) {
252         neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));
253     }
255     return neweffect;
258 void Effect::createAndApply(const char* name, SPDocument *doc, SPItem *item)
260     // Path effect definition
261     Inkscape::XML::Document *xml_doc = doc->getReprDoc();
262     Inkscape::XML::Node *repr = xml_doc->createElement("inkscape:path-effect");
263     repr->setAttribute("effect", name);
265     SP_OBJECT_REPR(SP_DOCUMENT_DEFS(doc))->addChild(repr, NULL); // adds to <defs> and assigns the 'id' attribute
266     const gchar * repr_id = repr->attribute("id");
267     Inkscape::GC::release(repr);
269     gchar *href = g_strdup_printf("#%s", repr_id);
270     sp_lpe_item_add_path_effect(SP_LPE_ITEM(item), href, true);
271     g_free(href);
274 void
275 Effect::createAndApply(EffectType type, SPDocument *doc, SPItem *item)
277     createAndApply(LPETypeConverter.get_key(type).c_str(), doc, item);
280 Effect::Effect(LivePathEffectObject *lpeobject)
281     : oncanvasedit_it(0),
282       is_visible(_("Is visible?"), _("If unchecked, the effect remains applied to the object but is temporarily disabled on canvas"), "is_visible", &wr, this, true),
283       show_orig_path(false),
284       lpeobj(lpeobject),
285       concatenate_before_pwd2(false),
286       provides_own_flash_paths(true), // is automatically set to false if providesOwnFlashPaths() is not overridden
287       is_ready(false) // is automatically set to false if providesOwnFlashPaths() is not overridden
289     registerParameter( dynamic_cast<Parameter *>(&is_visible) );
290     is_visible.widget_is_visible = false;
293 Effect::~Effect()
297 Glib::ustring
298 Effect::getName()
300     if (lpeobj->effecttype_set && LPETypeConverter.is_valid_id(lpeobj->effecttype) )
301         return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );
302     else
303         return Glib::ustring( _("No effect") );
306 EffectType
307 Effect::effectType() {
308     return lpeobj->effecttype;
311 /**
312  * Is performed a single time when the effect is freshly applied to a path
313  */
314 void
315 Effect::doOnApply (SPLPEItem */*lpeitem*/)
319 /**
320  * Is performed each time before the effect is updated.
321  */
322 void
323 Effect::doBeforeEffect (SPLPEItem */*lpeitem*/)
325     //Do nothing for simple effects
328 /**
329  * Effects can have a parameter path set before they are applied by accepting a nonzero number of
330  * mouse clicks. This method activates the pen context, which waits for the specified number of
331  * clicks. Override Effect::acceptsNumClicks() to return the number of expected mouse clicks.
332  */
333 void
334 Effect::doAcceptPathPreparations(SPLPEItem *lpeitem)
336     // switch to pen context
337     SPDesktop *desktop = inkscape_active_desktop(); // TODO: Is there a better method to find the item's desktop?
338     if (!tools_isactive(desktop, TOOLS_FREEHAND_PEN)) {
339         tools_switch(desktop, TOOLS_FREEHAND_PEN);
340     }
342     SPEventContext *ec = desktop->event_context;
343     SPPenContext *pc = SP_PEN_CONTEXT(ec);
344     pc->expecting_clicks_for_LPE = this->acceptsNumClicks();
345     pc->waiting_LPE = this;
346     pc->waiting_item = lpeitem;
347     pc->polylines_only = true;
349     ec->desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE,
350         g_strdup_printf(_("Please specify a parameter path for the LPE '%s' with %d mouse clicks"),
351                         getName().c_str(), acceptsNumClicks()));
354 void
355 Effect::writeParamsToSVG() {
356     std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
357     for (p = param_vector.begin(); p != param_vector.end(); ++p) {
358         (*p)->write_to_SVG();
359     }
362 /**
363  * If the effect expects a path parameter (specified by a number of mouse clicks) before it is
364  * applied, this is the method that processes the resulting path. Override it to customize it for
365  * your LPE. But don't forget to call the parent method so that is_ready is set to true!
366  */
367 void
368 Effect::acceptParamPath (SPPath */*param_path*/) {
369     setReady();
372 /*
373  *  Here be the doEffect function chain:
374  */
375 void
376 Effect::doEffect (SPCurve * curve)
378     std::vector<Geom::Path> orig_pathv = curve->get_pathvector();
380     std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
382     curve->set_pathvector(result_pathv);
385 std::vector<Geom::Path>
386 Effect::doEffect_path (std::vector<Geom::Path> const & path_in)
388     std::vector<Geom::Path> path_out;
390     if ( !concatenate_before_pwd2 ) {
391         // default behavior
392         for (unsigned int i=0; i < path_in.size(); i++) {
393             Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb();
394             Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
395             std::vector<Geom::Path> path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
396             // add the output path vector to the already accumulated vector:
397             for (unsigned int j=0; j < path.size(); j++) {
398                 path_out.push_back(path[j]);
399             }
400         }
401     } else {
402       // concatenate the path into possibly discontinuous pwd2
403         Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
404         for (unsigned int i=0; i < path_in.size(); i++) {
405             pwd2_in.concat( path_in[i].toPwSb() );
406         }
407         Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
408         path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
409     }
411     return path_out;
414 Geom::Piecewise<Geom::D2<Geom::SBasis> >
415 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
417     g_warning("Effect has no doEffect implementation");
418     return pwd2_in;
421 void
422 Effect::readallParameters(Inkscape::XML::Node * repr)
424     std::vector<Parameter *>::iterator it = param_vector.begin();
425     while (it != param_vector.end()) {
426         Parameter * param = *it;
427         const gchar * key = param->param_key.c_str();
428         const gchar * value = repr->attribute(key);
429         if (value) {
430             bool accepted = param->param_readSVGValue(value);
431             if (!accepted) {
432                 g_warning("Effect::readallParameters - '%s' not accepted for %s", value, key);
433             }
434         } else {
435             // set default value
436             param->param_set_default();
437         }
439         it++;
440     }
443 /* This function does not and SHOULD NOT write to XML */
444 void
445 Effect::setParameter(const gchar * key, const gchar * new_value)
447     Parameter * param = getParameter(key);
448     if (param) {
449         if (new_value) {
450             bool accepted = param->param_readSVGValue(new_value);
451             if (!accepted) {
452                 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);
453             }
454         } else {
455             // set default value
456             param->param_set_default();
457         }
458     }
461 void
462 Effect::registerParameter(Parameter * param)
464     param_vector.push_back(param);
467 // TODO: should we provide a way to alter the handle's appearance?
468 void
469 Effect::registerKnotHolderHandle(KnotHolderEntity* entity, const char* descr)
471     kh_entity_vector.push_back(std::make_pair(entity, descr));
474 /**
475  * Add all registered LPE knotholder handles to the knotholder
476  */
477 void
478 Effect::addHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
479     using namespace Inkscape::LivePathEffect;
481     // add handles provided by the effect itself
482     addKnotHolderEntities(knotholder, desktop, item);
484     // add handles provided by the effect's parameters (if any)
485     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
486         (*p)->addKnotHolderEntities(knotholder, desktop, item);
487     }
490 void
491 Effect::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
492     // TODO: The entities in kh_entity_vector are already instantiated during the call
493     //       to registerKnotHolderHandle(), but they are recreated here. Also, we must not
494     //       delete them when the knotholder is destroyed, whence the clumsy function
495     //       isDeletable(). If we could create entities of different classes dynamically,
496     //       this would be much nicer. How to do this?
497     std::vector<std::pair<KnotHolderEntity*, const char*> >::iterator i;
498     for (i = kh_entity_vector.begin(); i != kh_entity_vector.end(); ++i) {
499         KnotHolderEntity *entity = i->first;
500         const char *descr = i->second;
502         entity->create(desktop, item, knotholder, descr);
503         knotholder->add(entity);
504     }
507 /**
508  * Return a vector of PathVectors which contain all helperpaths that should be drawn by the effect.
509  * This is the function called by external code like SPLPEItem.
510  */
511 std::vector<Geom::PathVector>
512 Effect::getHelperPaths(SPLPEItem *lpeitem)
514     std::vector<Geom::PathVector> hp_vec;
516     if (!SP_IS_SHAPE(lpeitem)) {
517 //        g_print ("How to handle helperpaths for non-shapes?\n"); // non-shapes are for example SPGroups.
518         return hp_vec;
519     }
521     // TODO: we can probably optimize this by using a lot more references
522     //       rather than copying PathVectors all over the place
523     if (show_orig_path) {
524         // add original path to helperpaths
525         SPCurve* curve = SP_SHAPE(lpeitem)->getCurve ();
526         hp_vec.push_back(curve->get_pathvector());
527     }
529     // add other helperpaths provided by the effect itself
530     addCanvasIndicators(lpeitem, hp_vec);
532     // add helperpaths provided by the effect's parameters
533     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
534         (*p)->addCanvasIndicators(lpeitem, hp_vec);
535     }
537     return hp_vec;
540 /**
541  * Add possible canvas indicators (i.e., helperpaths other than the original path) to \a hp_vec
542  * This function should be overwritten by derived effects if they want to provide their own helperpaths.
543  */
544 void
545 Effect::addCanvasIndicators(SPLPEItem */*lpeitem*/, std::vector<Geom::PathVector> &/*hp_vec*/)
550 /**
551  * This *creates* a new widget, management of deletion should be done by the caller
552  */
553 Gtk::Widget *
554 Effect::newWidget(Gtk::Tooltips * tooltips)
556     // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
557     Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox() );
559     vbox->set_border_width(5);
561     std::vector<Parameter *>::iterator it = param_vector.begin();
562     while (it != param_vector.end()) {
563         if ((*it)->widget_is_visible) {
564             Parameter * param = *it;
565             Gtk::Widget * widg = param->param_newWidget(tooltips);
566             Glib::ustring * tip = param->param_getTooltip();
567             if (widg) {
568                 vbox->pack_start(*widg, true, true, 2);
569                 if (tip != NULL) {
570                     tooltips->set_tip(*widg, *tip);
571                 }
572             }
573         }
575         it++;
576     }
578     return dynamic_cast<Gtk::Widget *>(vbox);
582 Inkscape::XML::Node *
583 Effect::getRepr()
585     return SP_OBJECT_REPR(lpeobj);
588 SPDocument *
589 Effect::getSPDoc()
591     if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
592     return SP_OBJECT_DOCUMENT(lpeobj);
595 Parameter *
596 Effect::getParameter(const char * key)
598     Glib::ustring stringkey(key);
600     std::vector<Parameter *>::iterator it = param_vector.begin();
601     while (it != param_vector.end()) {
602         Parameter * param = *it;
603         if ( param->param_key == key) {
604             return param;
605         }
607         it++;
608     }
610     return NULL;
613 Parameter *
614 Effect::getNextOncanvasEditableParam()
616     if (param_vector.size() == 0) // no parameters
617         return NULL;
619     oncanvasedit_it++;
620     if (oncanvasedit_it >= static_cast<int>(param_vector.size())) {
621         oncanvasedit_it = 0;
622     }
623     int old_it = oncanvasedit_it;
625     do {
626         Parameter * param = param_vector[oncanvasedit_it];
627         if(param && param->oncanvas_editable) {
628             return param;
629         } else {
630             oncanvasedit_it++;
631             if (oncanvasedit_it == static_cast<int>(param_vector.size())) {  // loop round the map
632                 oncanvasedit_it = 0;
633             }
634         }
635     } while (oncanvasedit_it != old_it); // iterate until complete loop through map has been made
637     return NULL;
640 void
641 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
643     if (!desktop) return;
645     Parameter * param = getNextOncanvasEditableParam();
646     if (param) {
647         param->param_editOncanvas(item, desktop);
648         gchar *message = g_strdup_printf(_("Editing parameter <b>%s</b>."), param->param_label.c_str());
649         desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, message);
650         g_free(message);
651     } else {
652         desktop->messageStack()->flash( Inkscape::WARNING_MESSAGE,
653                                         _("None of the applied path effect's parameters can be edited on-canvas.") );
654     }
657 /* This function should reset the defaults and is used for example to initialize an effect right after it has been applied to a path
658 * 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!
659 */
660 void
661 Effect::resetDefaults(SPItem * /*item*/)
663     std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
664     for (p = param_vector.begin(); p != param_vector.end(); ++p) {
665         (*p)->param_set_default();
666         (*p)->write_to_SVG();
667     }
670 void
671 Effect::transform_multiply(Geom::Matrix const& postmul, bool set)
673     // cycle through all parameters. Most parameters will not need transformation, but path and point params do.
674     for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); it++) {
675         Parameter * param = *it;
676         param->param_transform_multiply(postmul, set);
677     }
680 bool
681 Effect::providesKnotholder()
683     // does the effect actively provide any knotholder entities of its own?
684     if (kh_entity_vector.size() > 0)
685         return true;
687     // otherwise: are there any parameters that have knotholderentities?
688     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
689         if ((*p)->providesKnotHolderEntities()) {
690             return true;
691         }
692     }
694     return false;
697 } /* namespace LivePathEffect */
699 } /* namespace Inkscape */
701 /*
702   Local Variables:
703   mode:c++
704   c-file-style:"stroustrup"
705   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
706   indent-tabs-mode:nil
707   fill-column:99
708   End:
709 */
710 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :