Code

Pot and Dutch translation update
[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 "knotholder.h"
32 #include "sp-lpe-item.h"
33 #include "live_effects/lpeobject.h"
34 #include "live_effects/parameter/parameter.h"
35 #include <glibmm/ustring.h>
36 #include "display/curve.h"
37 #include <gtkmm.h>
39 #include <exception>
41 #include <2geom/sbasis-to-bezier.h>
42 #include <2geom/matrix.h>
43 #include <2geom/pathvector.h>
45 // include effects:
46 #include "live_effects/lpe-patternalongpath.h"
47 #include "live_effects/lpe-bendpath.h"
48 #include "live_effects/lpe-sketch.h"
49 #include "live_effects/lpe-vonkoch.h"
50 #include "live_effects/lpe-knot.h"
51 #include "live_effects/lpe-rough-hatches.h"
52 #include "live_effects/lpe-dynastroke.h"
53 #include "live_effects/lpe-test-doEffect-stack.h"
54 #include "live_effects/lpe-gears.h"
55 #include "live_effects/lpe-curvestitch.h"
56 #include "live_effects/lpe-circle_with_radius.h"
57 #include "live_effects/lpe-perspective_path.h"
58 #include "live_effects/lpe-spiro.h"
59 #include "live_effects/lpe-lattice.h"
60 #include "live_effects/lpe-envelope.h"
61 #include "live_effects/lpe-constructgrid.h"
62 #include "live_effects/lpe-perp_bisector.h"
63 #include "live_effects/lpe-tangent_to_curve.h"
64 #include "live_effects/lpe-mirror_symmetry.h"
65 #include "live_effects/lpe-circle_3pts.h"
66 #include "live_effects/lpe-angle_bisector.h"
67 #include "live_effects/lpe-parallel.h"
68 #include "live_effects/lpe-copy_rotate.h"
69 #include "live_effects/lpe-offset.h"
70 #include "live_effects/lpe-ruler.h"
71 #include "live_effects/lpe-boolops.h"
72 #include "live_effects/lpe-interpolate.h"
73 #include "live_effects/lpe-text_label.h"
74 #include "live_effects/lpe-path_length.h"
75 #include "live_effects/lpe-line_segment.h"
76 #include "live_effects/lpe-recursiveskeleton.h"
77 #include "live_effects/lpe-extrude.h"
78 #include "live_effects/lpe-powerstroke.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     {POWERSTROKE,           N_("Power stroke"),            "powerstroke"},
105     {COPY_ROTATE,           N_("Rotate copies"),           "copy_rotate"},
106     {RECURSIVE_SKELETON,    N_("Recursive skeleton"),      "recursive_skeleton"},
107     {TANGENT_TO_CURVE,      N_("Tangent to curve"),        "tangent_to_curve"},
108     {TEXT_LABEL,            N_("Text label"),              "text_label"},
109 #endif
110 /* 0.46 */
111     {BEND_PATH,             N_("Bend"),                     "bend_path"},
112     {GEARS,                 N_("Gears"),                   "gears"},
113     {PATTERN_ALONG_PATH,    N_("Pattern Along Path"),      "skeletal"},   // for historic reasons, this effect is called skeletal(strokes) in Inkscape:SVG
114     {CURVE_STITCH,          N_("Stitch Sub-Paths"),        "curvestitching"},
115 /* 0.47 */
116     {VONKOCH,               N_("VonKoch"),                 "vonkoch"},
117     {KNOT,                  N_("Knot"),                    "knot"},
118     {CONSTRUCT_GRID,        N_("Construct grid"),          "construct_grid"},
119     {SPIRO,                 N_("Spiro spline"),            "spiro"},
120     {ENVELOPE,              N_("Envelope Deformation"),    "envelope"},
121     {INTERPOLATE,           N_("Interpolate Sub-Paths"),   "interpolate"},
122     {ROUGH_HATCHES,         N_("Hatches (rough)"),         "rough_hatches"},
123     {SKETCH,                N_("Sketch"),                  "sketch"},
124     {RULER,                 N_("Ruler"),                   "ruler"},
125 /* 0.49 */
126 };
127 const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData));
129 int
130 Effect::acceptsNumClicks(EffectType type) {
131     switch (type) {
132         case INVALID_LPE: return -1; // in case we want to distinguish between invalid LPE and valid ones that expect zero clicks
133         case ANGLE_BISECTOR: return 3;
134         case CIRCLE_3PTS: return 3;
135         case CIRCLE_WITH_RADIUS: return 2;
136         case LINE_SEGMENT: return 2;
137         case PERP_BISECTOR: return 2;
138         default: return 0;
139     }
142 Effect*
143 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
145     Effect* neweffect = NULL;
146     switch (lpenr) {
147         case PATTERN_ALONG_PATH:
148             neweffect = static_cast<Effect*> ( new LPEPatternAlongPath(lpeobj) );
149             break;
150         case BEND_PATH:
151             neweffect = static_cast<Effect*> ( new LPEBendPath(lpeobj) );
152             break;
153         case SKETCH:
154             neweffect = static_cast<Effect*> ( new LPESketch(lpeobj) );
155             break;
156         case ROUGH_HATCHES:
157             neweffect = static_cast<Effect*> ( new LPERoughHatches(lpeobj) );
158             break;
159         case VONKOCH:
160             neweffect = static_cast<Effect*> ( new LPEVonKoch(lpeobj) );
161             break;
162         case KNOT:
163             neweffect = static_cast<Effect*> ( new LPEKnot(lpeobj) );
164             break;
165         case GEARS:
166             neweffect = static_cast<Effect*> ( new LPEGears(lpeobj) );
167             break;
168         case CURVE_STITCH:
169             neweffect = static_cast<Effect*> ( new LPECurveStitch(lpeobj) );
170             break;
171         case LATTICE:
172             neweffect = static_cast<Effect*> ( new LPELattice(lpeobj) );
173             break;
174         case ENVELOPE:
175             neweffect = static_cast<Effect*> ( new LPEEnvelope(lpeobj) );
176             break;
177         case CIRCLE_WITH_RADIUS:
178             neweffect = static_cast<Effect*> ( new LPECircleWithRadius(lpeobj) );
179             break;
180         case PERSPECTIVE_PATH:
181             neweffect = static_cast<Effect*> ( new LPEPerspectivePath(lpeobj) );
182             break;
183         case SPIRO:
184             neweffect = static_cast<Effect*> ( new LPESpiro(lpeobj) );
185             break;
186         case CONSTRUCT_GRID:
187             neweffect = static_cast<Effect*> ( new LPEConstructGrid(lpeobj) );
188             break;
189         case PERP_BISECTOR:
190             neweffect = static_cast<Effect*> ( new LPEPerpBisector(lpeobj) );
191             break;
192         case TANGENT_TO_CURVE:
193             neweffect = static_cast<Effect*> ( new LPETangentToCurve(lpeobj) );
194             break;
195         case MIRROR_SYMMETRY:
196             neweffect = static_cast<Effect*> ( new LPEMirrorSymmetry(lpeobj) );
197             break;
198         case CIRCLE_3PTS:
199             neweffect = static_cast<Effect*> ( new LPECircle3Pts(lpeobj) );
200             break;
201         case ANGLE_BISECTOR:
202             neweffect = static_cast<Effect*> ( new LPEAngleBisector(lpeobj) );
203             break;
204         case PARALLEL:
205             neweffect = static_cast<Effect*> ( new LPEParallel(lpeobj) );
206             break;
207         case COPY_ROTATE:
208             neweffect = static_cast<Effect*> ( new LPECopyRotate(lpeobj) );
209             break;
210         case OFFSET:
211             neweffect = static_cast<Effect*> ( new LPEOffset(lpeobj) );
212             break;
213         case RULER:
214             neweffect = static_cast<Effect*> ( new LPERuler(lpeobj) );
215             break;
216         case BOOLOPS:
217             neweffect = static_cast<Effect*> ( new LPEBoolops(lpeobj) );
218             break;
219         case INTERPOLATE:
220             neweffect = static_cast<Effect*> ( new LPEInterpolate(lpeobj) );
221             break;
222         case TEXT_LABEL:
223             neweffect = static_cast<Effect*> ( new LPETextLabel(lpeobj) );
224             break;
225         case PATH_LENGTH:
226             neweffect = static_cast<Effect*> ( new LPEPathLength(lpeobj) );
227             break;
228         case LINE_SEGMENT:
229             neweffect = static_cast<Effect*> ( new LPELineSegment(lpeobj) );
230             break;
231         case DOEFFECTSTACK_TEST:
232             neweffect = static_cast<Effect*> ( new LPEdoEffectStackTest(lpeobj) );
233             break;
234         case DYNASTROKE:
235             neweffect = static_cast<Effect*> ( new LPEDynastroke(lpeobj) );
236             break;
237         case RECURSIVE_SKELETON:
238             neweffect = static_cast<Effect*> ( new LPERecursiveSkeleton(lpeobj) );
239             break;
240         case EXTRUDE:
241             neweffect = static_cast<Effect*> ( new LPEExtrude(lpeobj) );
242             break;
243         case POWERSTROKE:
244             neweffect = static_cast<Effect*> ( new LPEPowerStroke(lpeobj) );
245             break;
246         default:
247             g_warning("LivePathEffect::Effect::New   called with invalid patheffect type (%d)", lpenr);
248             neweffect = NULL;
249             break;
250     }
252     if (neweffect) {
253         neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));
254     }
256     return neweffect;
259 void
260 Effect::createAndApply(const char* name, SPDocument *doc, SPItem *item)
262     // Path effect definition
263     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
264     Inkscape::XML::Node *repr = xml_doc->createElement("inkscape:path-effect");
265     repr->setAttribute("effect", name);
267     SP_OBJECT_REPR(SP_DOCUMENT_DEFS(doc))->addChild(repr, NULL); // adds to <defs> and assigns the 'id' attribute
268     const gchar * repr_id = repr->attribute("id");
269     Inkscape::GC::release(repr);
271     gchar *href = g_strdup_printf("#%s", repr_id);
272     sp_lpe_item_add_path_effect(SP_LPE_ITEM(item), href, true);
273     g_free(href);
276 void
277 Effect::createAndApply(EffectType type, SPDocument *doc, SPItem *item)
279     createAndApply(LPETypeConverter.get_key(type).c_str(), doc, item);
282 Effect::Effect(LivePathEffectObject *lpeobject)
283     : oncanvasedit_it(0),
284       is_visible(_("Is visible?"), _("If unchecked, the effect remains applied to the object but is temporarily disabled on canvas"), "is_visible", &wr, this, true),
285       show_orig_path(false),
286       lpeobj(lpeobject),
287       concatenate_before_pwd2(false),
288       provides_own_flash_paths(true), // is automatically set to false if providesOwnFlashPaths() is not overridden
289       is_ready(false) // is automatically set to false if providesOwnFlashPaths() is not overridden
291     registerParameter( dynamic_cast<Parameter *>(&is_visible) );
292     is_visible.widget_is_visible = false;
295 Effect::~Effect()
299 Glib::ustring
300 Effect::getName()
302     if (lpeobj->effecttype_set && LPETypeConverter.is_valid_id(lpeobj->effecttype) )
303         return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );
304     else
305         return Glib::ustring( _("No effect") );
308 EffectType
309 Effect::effectType() {
310     return lpeobj->effecttype;
313 /**
314  * Is performed a single time when the effect is freshly applied to a path
315  */
316 void
317 Effect::doOnApply (SPLPEItem */*lpeitem*/)
321 /**
322  * Is performed each time before the effect is updated.
323  */
324 void
325 Effect::doBeforeEffect (SPLPEItem */*lpeitem*/)
327     //Do nothing for simple effects
330 /**
331  * Effects can have a parameter path set before they are applied by accepting a nonzero number of
332  * mouse clicks. This method activates the pen context, which waits for the specified number of
333  * clicks. Override Effect::acceptsNumClicks() to return the number of expected mouse clicks.
334  */
335 void
336 Effect::doAcceptPathPreparations(SPLPEItem *lpeitem)
338     // switch to pen context
339     SPDesktop *desktop = inkscape_active_desktop(); // TODO: Is there a better method to find the item's desktop?
340     if (!tools_isactive(desktop, TOOLS_FREEHAND_PEN)) {
341         tools_switch(desktop, TOOLS_FREEHAND_PEN);
342     }
344     SPEventContext *ec = desktop->event_context;
345     SPPenContext *pc = SP_PEN_CONTEXT(ec);
346     pc->expecting_clicks_for_LPE = this->acceptsNumClicks();
347     pc->waiting_LPE = this;
348     pc->waiting_item = lpeitem;
349     pc->polylines_only = true;
351     ec->desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE,
352         g_strdup_printf(_("Please specify a parameter path for the LPE '%s' with %d mouse clicks"),
353                         getName().c_str(), acceptsNumClicks()));
356 void
357 Effect::writeParamsToSVG() {
358     std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
359     for (p = param_vector.begin(); p != param_vector.end(); ++p) {
360         (*p)->write_to_SVG();
361     }
364 /**
365  * If the effect expects a path parameter (specified by a number of mouse clicks) before it is
366  * applied, this is the method that processes the resulting path. Override it to customize it for
367  * your LPE. But don't forget to call the parent method so that is_ready is set to true!
368  */
369 void
370 Effect::acceptParamPath (SPPath */*param_path*/) {
371     setReady();
374 /*
375  *  Here be the doEffect function chain:
376  */
377 void
378 Effect::doEffect (SPCurve * curve)
380     std::vector<Geom::Path> orig_pathv = curve->get_pathvector();
382     std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
384     curve->set_pathvector(result_pathv);
387 std::vector<Geom::Path>
388 Effect::doEffect_path (std::vector<Geom::Path> const & path_in)
390     std::vector<Geom::Path> path_out;
392     if ( !concatenate_before_pwd2 ) {
393         // default behavior
394         for (unsigned int i=0; i < path_in.size(); i++) {
395             Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb();
396             Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
397             std::vector<Geom::Path> path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
398             // add the output path vector to the already accumulated vector:
399             for (unsigned int j=0; j < path.size(); j++) {
400                 path_out.push_back(path[j]);
401             }
402         }
403     } else {
404       // concatenate the path into possibly discontinuous pwd2
405         Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
406         for (unsigned int i=0; i < path_in.size(); i++) {
407             pwd2_in.concat( path_in[i].toPwSb() );
408         }
409         Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
410         path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
411     }
413     return path_out;
416 Geom::Piecewise<Geom::D2<Geom::SBasis> >
417 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
419     g_warning("Effect has no doEffect implementation");
420     return pwd2_in;
423 void
424 Effect::readallParameters(Inkscape::XML::Node * repr)
426     std::vector<Parameter *>::iterator it = param_vector.begin();
427     while (it != param_vector.end()) {
428         Parameter * param = *it;
429         const gchar * key = param->param_key.c_str();
430         const gchar * value = repr->attribute(key);
431         if (value) {
432             bool accepted = param->param_readSVGValue(value);
433             if (!accepted) {
434                 g_warning("Effect::readallParameters - '%s' not accepted for %s", value, key);
435             }
436         } else {
437             // set default value
438             param->param_set_default();
439         }
441         it++;
442     }
445 /* This function does not and SHOULD NOT write to XML */
446 void
447 Effect::setParameter(const gchar * key, const gchar * new_value)
449     Parameter * param = getParameter(key);
450     if (param) {
451         if (new_value) {
452             bool accepted = param->param_readSVGValue(new_value);
453             if (!accepted) {
454                 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);
455             }
456         } else {
457             // set default value
458             param->param_set_default();
459         }
460     }
463 void
464 Effect::registerParameter(Parameter * param)
466     param_vector.push_back(param);
469 // TODO: should we provide a way to alter the handle's appearance?
470 void
471 Effect::registerKnotHolderHandle(KnotHolderEntity* entity, const char* descr)
473     kh_entity_vector.push_back(std::make_pair(entity, descr));
476 /**
477  * Add all registered LPE knotholder handles to the knotholder
478  */
479 void
480 Effect::addHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
481     using namespace Inkscape::LivePathEffect;
483     // add handles provided by the effect itself
484     addKnotHolderEntities(knotholder, desktop, item);
486     // add handles provided by the effect's parameters (if any)
487     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
488         (*p)->addKnotHolderEntities(knotholder, desktop, item);
489     }
492 void
493 Effect::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
494     // TODO: The entities in kh_entity_vector are already instantiated during the call
495     //       to registerKnotHolderHandle(), but they are recreated here. Also, we must not
496     //       delete them when the knotholder is destroyed, whence the clumsy function
497     //       isDeletable(). If we could create entities of different classes dynamically,
498     //       this would be much nicer. How to do this?
499     std::vector<std::pair<KnotHolderEntity*, const char*> >::iterator i;
500     for (i = kh_entity_vector.begin(); i != kh_entity_vector.end(); ++i) {
501         KnotHolderEntity *entity = i->first;
502         const char *descr = i->second;
504         entity->create(desktop, item, knotholder, descr);
505         knotholder->add(entity);
506     }
509 /**
510  * Return a vector of PathVectors which contain all helperpaths that should be drawn by the effect.
511  * This is the function called by external code like SPLPEItem.
512  */
513 std::vector<Geom::PathVector>
514 Effect::getHelperPaths(SPLPEItem *lpeitem)
516     std::vector<Geom::PathVector> hp_vec;
518     if (!SP_IS_SHAPE(lpeitem)) {
519 //        g_print ("How to handle helperpaths for non-shapes?\n"); // non-shapes are for example SPGroups.
520         return hp_vec;
521     }
523     // TODO: we can probably optimize this by using a lot more references
524     //       rather than copying PathVectors all over the place
525     if (show_orig_path) {
526         // add original path to helperpaths
527         SPCurve* curve = sp_shape_get_curve (SP_SHAPE(lpeitem));
528         hp_vec.push_back(curve->get_pathvector());
529     }
531     // add other helperpaths provided by the effect itself
532     addCanvasIndicators(lpeitem, hp_vec);
534     // add helperpaths provided by the effect's parameters
535     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
536         (*p)->addCanvasIndicators(lpeitem, hp_vec);
537     }
539     return hp_vec;
542 /**
543  * Add possible canvas indicators (i.e., helperpaths other than the original path) to \a hp_vec
544  * This function should be overwritten by derived effects if they want to provide their own helperpaths.
545  */
546 void
547 Effect::addCanvasIndicators(SPLPEItem */*lpeitem*/, std::vector<Geom::PathVector> &/*hp_vec*/)
552 /**
553  * This *creates* a new widget, management of deletion should be done by the caller
554  */
555 Gtk::Widget *
556 Effect::newWidget(Gtk::Tooltips * tooltips)
558     // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
559     Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox() );
561     vbox->set_border_width(5);
563     std::vector<Parameter *>::iterator it = param_vector.begin();
564     while (it != param_vector.end()) {
565         if ((*it)->widget_is_visible) {
566             Parameter * param = *it;
567             Gtk::Widget * widg = param->param_newWidget(tooltips);
568             Glib::ustring * tip = param->param_getTooltip();
569             if (widg) {
570                 vbox->pack_start(*widg, true, true, 2);
571                 if (tip != NULL) {
572                     tooltips->set_tip(*widg, *tip);
573                 }
574             }
575         }
577         it++;
578     }
580     return dynamic_cast<Gtk::Widget *>(vbox);
584 Inkscape::XML::Node *
585 Effect::getRepr()
587     return SP_OBJECT_REPR(lpeobj);
590 SPDocument *
591 Effect::getSPDoc()
593     if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
594     return SP_OBJECT_DOCUMENT(lpeobj);
597 Parameter *
598 Effect::getParameter(const char * key)
600     Glib::ustring stringkey(key);
602     std::vector<Parameter *>::iterator it = param_vector.begin();
603     while (it != param_vector.end()) {
604         Parameter * param = *it;
605         if ( param->param_key == key) {
606             return param;
607         }
609         it++;
610     }
612     return NULL;
615 Parameter *
616 Effect::getNextOncanvasEditableParam()
618     if (param_vector.size() == 0) // no parameters
619         return NULL;
621     oncanvasedit_it++;
622     if (oncanvasedit_it >= static_cast<int>(param_vector.size())) {
623         oncanvasedit_it = 0;
624     }
625     int old_it = oncanvasedit_it;
627     do {
628         Parameter * param = param_vector[oncanvasedit_it];
629         if(param && param->oncanvas_editable) {
630             return param;
631         } else {
632             oncanvasedit_it++;
633             if (oncanvasedit_it == static_cast<int>(param_vector.size())) {  // loop round the map
634                 oncanvasedit_it = 0;
635             }
636         }
637     } while (oncanvasedit_it != old_it); // iterate until complete loop through map has been made
639     return NULL;
642 void
643 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
645     if (!desktop) return;
647     Parameter * param = getNextOncanvasEditableParam();
648     if (param) {
649         param->param_editOncanvas(item, desktop);
650         gchar *message = g_strdup_printf(_("Editing parameter <b>%s</b>."), param->param_label.c_str());
651         desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, message);
652         g_free(message);
653     } else {
654         desktop->messageStack()->flash( Inkscape::WARNING_MESSAGE,
655                                         _("None of the applied path effect's parameters can be edited on-canvas.") );
656     }
659 /* This function should reset the defaults and is used for example to initialize an effect right after it has been applied to a path
660 * 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!
661 */
662 void
663 Effect::resetDefaults(SPItem * /*item*/)
665     std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
666     for (p = param_vector.begin(); p != param_vector.end(); ++p) {
667         (*p)->param_set_default();
668         (*p)->write_to_SVG();
669     }
672 void
673 Effect::transform_multiply(Geom::Matrix const& postmul, bool set)
675     // cycle through all parameters. Most parameters will not need transformation, but path and point params do.
676     for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); it++) {
677         Parameter * param = *it;
678         param->param_transform_multiply(postmul, set);
679     }
682 bool
683 Effect::providesKnotholder()
685     // does the effect actively provide any knotholder entities of its own?
686     if (kh_entity_vector.size() > 0)
687         return true;
689     // otherwise: are there any parameters that have knotholderentities?
690     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
691         if ((*p)->providesKnotHolderEntities()) {
692             return true;
693         }
694     }
696     return false;
699 } /* namespace LivePathEffect */
701 } /* namespace Inkscape */
703 /*
704   Local Variables:
705   mode:c++
706   c-file-style:"stroustrup"
707   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
708   indent-tabs-mode:nil
709   fill-column:99
710   End:
711 */
712 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :