Code

now that selection description includes style (filtered, clipped), we need to 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 #include "live_effects/effect.h"
11 #ifdef HAVE_CONFIG_H
12 # include "config.h"
13 #endif
15 #include "xml/node-event-vector.h"
16 #include "sp-object.h"
17 #include "attributes.h"
18 #include "message-stack.h"
19 #include "desktop.h"
20 #include "inkscape.h"
21 #include "document.h"
22 #include "document-private.h"
23 #include "xml/document.h"
24 #include <glibmm/i18n.h>
25 #include "pen-context.h"
26 #include "tools-switch.h"
27 #include "message-stack.h"
28 #include "desktop.h"
29 #include "nodepath.h"
31 #include "live_effects/lpeobject.h"
32 #include "live_effects/parameter/parameter.h"
33 #include <glibmm/ustring.h>
34 #include "display/curve.h"
35 #include <gtkmm.h>
37 #include <exception>
39 #include <2geom/sbasis-to-bezier.h>
40 #include <2geom/matrix.h>
41 #include <2geom/pathvector.h>
43 // include effects:
44 #include "live_effects/lpe-patternalongpath.h"
45 #include "live_effects/lpe-bendpath.h"
46 #include "live_effects/lpe-sketch.h"
47 #include "live_effects/lpe-vonkoch.h"
48 #include "live_effects/lpe-knot.h"
49 #include "live_effects/lpe-rough-hatches.h"
50 #include "live_effects/lpe-dynastroke.h"
51 #include "live_effects/lpe-test-doEffect-stack.h"
52 #include "live_effects/lpe-gears.h"
53 #include "live_effects/lpe-curvestitch.h"
54 #include "live_effects/lpe-circle_with_radius.h"
55 #include "live_effects/lpe-perspective_path.h"
56 #include "live_effects/lpe-spiro.h"
57 #include "live_effects/lpe-lattice.h"
58 #include "live_effects/lpe-envelope.h"
59 #include "live_effects/lpe-constructgrid.h"
60 #include "live_effects/lpe-perp_bisector.h"
61 #include "live_effects/lpe-tangent_to_curve.h"
62 #include "live_effects/lpe-mirror_symmetry.h"
63 #include "live_effects/lpe-circle_3pts.h"
64 #include "live_effects/lpe-angle_bisector.h"
65 #include "live_effects/lpe-parallel.h"
66 #include "live_effects/lpe-copy_rotate.h"
67 #include "live_effects/lpe-offset.h"
68 #include "live_effects/lpe-ruler.h"
69 #include "live_effects/lpe-boolops.h"
70 #include "live_effects/lpe-interpolate.h"
71 #include "live_effects/lpe-text_label.h"
72 #include "live_effects/lpe-path_length.h"
73 #include "live_effects/lpe-line_segment.h"
75 //#define LPE_ENABLE_TEST_EFFECTS
78 namespace Inkscape {
80 namespace LivePathEffect {
82 const Util::EnumData<EffectType> LPETypeData[] = {
83     // {constant defined in effect-enum.h, N_("name of your effect"), "name of your effect in SVG"}
84 #ifdef LPE_ENABLE_TEST_EFFECTS
85     {DOEFFECTSTACK_TEST,    N_("doEffect stack test"),     "doeffectstacktest"},
86     {ANGLE_BISECTOR,        N_("Angle bisector"),          "angle_bisector"},
87     {BOOLOPS,               N_("Boolops"),                 "boolops"},
88     {CIRCLE_WITH_RADIUS,    N_("Circle (by center and radius)"),   "circle_with_radius"},
89     {CIRCLE_3PTS,           N_("Circle by 3 points"),      "circle_3pts"},
90     {DYNASTROKE,            N_("Dynamic stroke"),          "dynastroke"},
91     {ENVELOPE,              N_("Envelope Deformation"),    "envelope"},
92     {ROUGH_HATCHES,         N_("Hatches (rough)"),         "rough_hatches"},
93     {INTERPOLATE,           N_("Interpolate Sub-Paths"),   "interpolate"},
94     {LATTICE,               N_("Lattice Deformation"),     "lattice"},
95     {LINE_SEGMENT,          N_("Line Segment"),            "line_segment"},
96     {MIRROR_SYMMETRY,       N_("Mirror symmetry"),         "mirror_symmetry"},
97     {OFFSET,                N_("Offset"),                  "offset"},
98     {PARALLEL,              N_("Parallel"),                "parallel"},
99     {PATH_LENGTH,           N_("Path length"),             "path_length"},
100     {PERP_BISECTOR,         N_("Perpendicular bisector"),  "perp_bisector"},
101     {PERSPECTIVE_PATH,      N_("Perspective path"),        "perspective_path"},
102     {COPY_ROTATE,           N_("Rotate copies"),           "copy_rotate"},
103     {RULER,                 N_("Ruler"),                   "ruler"},
104     {SKETCH,                N_("Sketch"),                  "sketch"},
105     {TANGENT_TO_CURVE,      N_("Tangent to curve"),        "tangent_to_curve"},
106     {TEXT_LABEL,            N_("Text label"),              "text_label"},
107     {VONKOCH,               N_("VonKoch"),                 "vonkoch"},
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     {FREEHAND_SHAPE,        N_("Freehand Shape"),          "freehand_shape"}, // this is actually a special type of PatternAlongPath, used to paste shapes in pen/pencil tool
116     {KNOT,                  N_("Knot"),                    "knot"},
117     {CONSTRUCT_GRID,        N_("Construct grid"),          "construct_grid"},
118     {SPIRO,                 N_("Spiro spline"),            "spiro"},
119 };
120 const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData));
122 int
123 Effect::acceptsNumClicks(EffectType type) {
124     switch (type) {
125         case INVALID_LPE: return -1; // in case we want to distinguish between invalid LPE and valid ones that expect zero clicks
126         case ANGLE_BISECTOR: return 3;
127         case CIRCLE_3PTS: return 3;
128         case CIRCLE_WITH_RADIUS: return 2;
129         case LINE_SEGMENT: return 2;
130         case PERP_BISECTOR: return 2;
131         default: return 0;
132     }
135 Effect*
136 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
138     Effect* neweffect = NULL;
139     switch (lpenr) {
140         case PATTERN_ALONG_PATH:
141             neweffect = static_cast<Effect*> ( new LPEPatternAlongPath(lpeobj) );
142             break;
143         case FREEHAND_SHAPE:
144             neweffect = static_cast<Effect*> ( new LPEFreehandShape(lpeobj) );
145             break;
146         case BEND_PATH:
147             neweffect = static_cast<Effect*> ( new LPEBendPath(lpeobj) );
148             break;
149         case SKETCH:
150             neweffect = static_cast<Effect*> ( new LPESketch(lpeobj) );
151             break;
152         case ROUGH_HATCHES:
153             neweffect = static_cast<Effect*> ( new LPERoughHatches(lpeobj) );
154             break;
155         case VONKOCH:
156             neweffect = static_cast<Effect*> ( new LPEVonKoch(lpeobj) );
157             break;
158         case KNOT:
159             neweffect = static_cast<Effect*> ( new LPEKnot(lpeobj) );
160             break;
161         case GEARS:
162             neweffect = static_cast<Effect*> ( new LPEGears(lpeobj) );
163             break;
164         case CURVE_STITCH:
165             neweffect = static_cast<Effect*> ( new LPECurveStitch(lpeobj) );
166             break;
167         case LATTICE:
168             neweffect = static_cast<Effect*> ( new LPELattice(lpeobj) );
169             break;
170         case ENVELOPE:
171             neweffect = static_cast<Effect*> ( new LPEEnvelope(lpeobj) );
172             break;
173         case CIRCLE_WITH_RADIUS:
174             neweffect = static_cast<Effect*> ( new LPECircleWithRadius(lpeobj) );
175             break;
176         case PERSPECTIVE_PATH:
177             neweffect = static_cast<Effect*> ( new LPEPerspectivePath(lpeobj) );
178             break;
179         case SPIRO:
180             neweffect = static_cast<Effect*> ( new LPESpiro(lpeobj) );
181             break;
182         case CONSTRUCT_GRID:
183             neweffect = static_cast<Effect*> ( new LPEConstructGrid(lpeobj) );
184             break;
185         case PERP_BISECTOR:
186             neweffect = static_cast<Effect*> ( new LPEPerpBisector(lpeobj) );
187             break;
188         case TANGENT_TO_CURVE:
189             neweffect = static_cast<Effect*> ( new LPETangentToCurve(lpeobj) );
190             break;
191         case MIRROR_SYMMETRY:
192             neweffect = static_cast<Effect*> ( new LPEMirrorSymmetry(lpeobj) );
193             break;
194         case CIRCLE_3PTS:
195             neweffect = static_cast<Effect*> ( new LPECircle3Pts(lpeobj) );
196             break;
197         case ANGLE_BISECTOR:
198             neweffect = static_cast<Effect*> ( new LPEAngleBisector(lpeobj) );
199             break;
200         case PARALLEL:
201             neweffect = static_cast<Effect*> ( new LPEParallel(lpeobj) );
202             break;
203         case COPY_ROTATE:
204             neweffect = static_cast<Effect*> ( new LPECopyRotate(lpeobj) );
205             break;
206         case OFFSET:
207             neweffect = static_cast<Effect*> ( new LPEOffset(lpeobj) );
208             break;
209         case RULER:
210             neweffect = static_cast<Effect*> ( new LPERuler(lpeobj) );
211             break;
212         case BOOLOPS:
213             neweffect = static_cast<Effect*> ( new LPEBoolops(lpeobj) );
214             break;
215         case INTERPOLATE:
216             neweffect = static_cast<Effect*> ( new LPEInterpolate(lpeobj) );
217             break;
218         case TEXT_LABEL:
219             neweffect = static_cast<Effect*> ( new LPETextLabel(lpeobj) );
220             break;
221         case PATH_LENGTH:
222             neweffect = static_cast<Effect*> ( new LPEPathLength(lpeobj) );
223             break;
224         case LINE_SEGMENT:
225             neweffect = static_cast<Effect*> ( new LPELineSegment(lpeobj) );
226             break;
227         case DOEFFECTSTACK_TEST:
228             neweffect = static_cast<Effect*> ( new LPEdoEffectStackTest(lpeobj) );
229             break;
230         case DYNASTROKE:
231             neweffect = static_cast<Effect*> ( new LPEDynastroke(lpeobj) );
232             break;
233         default:
234             g_warning("LivePathEffect::Effect::New   called with invalid patheffect type (%d)", lpenr);
235             neweffect = NULL;
236             break;
237     }
239     if (neweffect) {
240         neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));
241     }
243     return neweffect;
246 void
247 Effect::createAndApply(const char* name, SPDocument *doc, SPItem *item)
249     // Path effect definition
250     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
251     Inkscape::XML::Node *repr = xml_doc->createElement("inkscape:path-effect");
252     repr->setAttribute("effect", name);
254     SP_OBJECT_REPR(SP_DOCUMENT_DEFS(doc))->addChild(repr, NULL); // adds to <defs> and assigns the 'id' attribute
255     const gchar * repr_id = repr->attribute("id");
256     Inkscape::GC::release(repr);
258     gchar *href = g_strdup_printf("#%s", repr_id);
259     sp_lpe_item_add_path_effect(SP_LPE_ITEM(item), href, true);
260     g_free(href);
263 void
264 Effect::createAndApply(EffectType type, SPDocument *doc, SPItem *item)
266     createAndApply(LPETypeConverter.get_key(type).c_str(), doc, item);
269 Effect::Effect(LivePathEffectObject *lpeobject)
270     : oncanvasedit_it(0),
271       is_visible(_("Is visible?"), _("If unchecked, the effect remains applied to the object but is temporarily disabled on canvas"), "is_visible", &wr, this, true),
272       deactivate_knotholder(_("Deactivate knotholder?"), _("Check this to deactivate knotholder handles (useful if they interfere with node handles during editing)"), "deactivate_knotholder", &wr, this, false),
273       show_orig_path(false),
274       lpeobj(lpeobject),
275       concatenate_before_pwd2(false),
276       provides_own_flash_paths(true), // is automatically set to false if providesOwnFlashPaths() is not overridden
277       is_ready(false) // is automatically set to false if providesOwnFlashPaths() is not overridden
279     registerParameter( dynamic_cast<Parameter *>(&is_visible) );
280     registerParameter( dynamic_cast<Parameter *>(&deactivate_knotholder) );
283 Effect::~Effect()
287 Glib::ustring
288 Effect::getName()
290     if (lpeobj->effecttype_set && LPETypeConverter.is_valid_id(lpeobj->effecttype) )
291         return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );
292     else
293         return Glib::ustring( _("No effect") );
296 EffectType
297 Effect::effectType() {
298     return lpeobj->effecttype;
301 /**
302  * Is performed a single time when the effect is freshly applied to a path
303  */
304 void
305 Effect::doOnApply (SPLPEItem */*lpeitem*/)
309 /**
310  * Is performed each time before the effect is updated.
311  */
312 void
313 Effect::doBeforeEffect (SPLPEItem */*lpeitem*/)
315     //Do nothing for simple effects
318 /**
319  * Effects can have a parameter path set before they are applied by accepting a nonzero number of
320  * mouse clicks. This method activates the pen context, which waits for the specified number of
321  * clicks. Override Effect::acceptsNumClicks() to return the number of expected mouse clicks.
322  */
323 void
324 Effect::doAcceptPathPreparations(SPLPEItem *lpeitem)
326     // switch to pen context
327     SPDesktop *desktop = inkscape_active_desktop(); // TODO: Is there a better method to find the item's desktop?
328     if (!tools_isactive(desktop, TOOLS_FREEHAND_PEN)) {
329         tools_switch(desktop, TOOLS_FREEHAND_PEN);
330     }
332     SPEventContext *ec = desktop->event_context;
333     SPPenContext *pc = SP_PEN_CONTEXT(ec);
334     pc->expecting_clicks_for_LPE = this->acceptsNumClicks();
335     pc->waiting_LPE = this;
336     pc->waiting_item = lpeitem;
337     pc->polylines_only = true;
339     ec->desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE,
340         g_strdup_printf(_("Please specify a parameter path for the LPE '%s' with %d mouse clicks"),
341                         getName().c_str(), acceptsNumClicks()));
344 void
345 Effect::writeParamsToSVG() {
346     std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
347     for (p = param_vector.begin(); p != param_vector.end(); ++p) {
348         (*p)->write_to_SVG();
349     }
352 /**
353  * If the effect expects a path parameter (specified by a number of mouse clicks) before it is
354  * applied, this is the method that processes the resulting path. Override it to customize it for
355  * your LPE. But don't forget to call the parent method so that is_ready is set to true!
356  */
357 void
358 Effect::acceptParamPath (SPPath */*param_path*/) {
359     setReady();
362 /*
363  *  Here be the doEffect function chain:
364  */
365 void
366 Effect::doEffect (SPCurve * curve)
368     std::vector<Geom::Path> orig_pathv = curve->get_pathvector();
370     std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
372     curve->set_pathvector(result_pathv);
375 std::vector<Geom::Path>
376 Effect::doEffect_path (std::vector<Geom::Path> const & path_in)
378     std::vector<Geom::Path> path_out;
380     if ( !concatenate_before_pwd2 ) {
381         // default behavior
382         for (unsigned int i=0; i < path_in.size(); i++) {
383             Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb();
384             Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
385             std::vector<Geom::Path> path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
386             // add the output path vector to the already accumulated vector:
387             for (unsigned int j=0; j < path.size(); j++) {
388                 path_out.push_back(path[j]);
389             }
390         }
391     } else {
392       // concatenate the path into possibly discontinuous pwd2
393         Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
394         for (unsigned int i=0; i < path_in.size(); i++) {
395             pwd2_in.concat( path_in[i].toPwSb() );
396         }
397         Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
398         path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
399     }
401     return path_out;
404 Geom::Piecewise<Geom::D2<Geom::SBasis> >
405 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
407     g_warning("Effect has no doEffect implementation");
408     return pwd2_in;
411 void
412 Effect::readallParameters(Inkscape::XML::Node * repr)
414     std::vector<Parameter *>::iterator it = param_vector.begin();
415     while (it != param_vector.end()) {
416         Parameter * param = *it;
417         const gchar * key = param->param_key.c_str();
418         const gchar * value = repr->attribute(key);
419         if (value) {
420             bool accepted = param->param_readSVGValue(value);
421             if (!accepted) {
422                 g_warning("Effect::readallParameters - '%s' not accepted for %s", value, key);
423             }
424         } else {
425             // set default value
426             param->param_set_default();
427         }
429         it++;
430     }
433 /* This function does not and SHOULD NOT write to XML */
434 void
435 Effect::setParameter(const gchar * key, const gchar * new_value)
437     Parameter * param = getParameter(key);
438     if (param) {
439         if (new_value) {
440             bool accepted = param->param_readSVGValue(new_value);
441             if (!accepted) {
442                 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);
443             }
444         } else {
445             // set default value
446             param->param_set_default();
447         }
448     }
451 void
452 Effect::registerParameter(Parameter * param)
454     param_vector.push_back(param);
457 // TODO: should we provide a way to alter the handle's appearance?
458 void
459 Effect::registerKnotHolderHandle(KnotHolderEntity* entity, const char* descr)
461     kh_entity_vector.push_back(std::make_pair(entity, descr));
464 /**
465  * Add all registered LPE knotholder handles to the knotholder
466  */
467 void
468 Effect::addHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
469     using namespace Inkscape::LivePathEffect;
471     if (deactivate_knotholder)
472         return;
474     // add handles provided by the effect itself
475     addKnotHolderEntities(knotholder, desktop, item);
477     // add handles provided by the effect's parameters (if any)
478     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
479         (*p)->addKnotHolderEntities(knotholder, desktop, item);
480     }
483 void
484 Effect::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
485     // TODO: The entities in kh_entity_vector are already instantiated during the call
486     //       to registerKnotHolderHandle(), but they are recreated here. Also, we must not
487     //       delete them when the knotholder is destroyed, whence the clumsy function
488     //       isDeletable(). If we could create entities of different classes dynamically,
489     //       this would be much nicer. How to do this?
490     std::vector<std::pair<KnotHolderEntity*, const char*> >::iterator i;
491     for (i = kh_entity_vector.begin(); i != kh_entity_vector.end(); ++i) {
492         KnotHolderEntity *entity = i->first;
493         const char *descr = i->second;
495         entity->create(desktop, item, knotholder, descr);
496         knotholder->add(entity);
497     }
500 /**
501  * Return a vector of PathVectors which contain all helperpaths that should be drawn by the effect.
502  * This is the function called by external code like SPLPEItem.
503  */
504 std::vector<Geom::PathVector>
505 Effect::getHelperPaths(SPLPEItem *lpeitem)
507     std::vector<Geom::PathVector> hp_vec;
509     if (!SP_IS_SHAPE(lpeitem)) {
510         g_print ("How to handle helperpaths for non-shapes?\n"); // non-shapes are for example SPGroups.
511         return hp_vec;
512     }
514     // TODO: we can probably optimize this by using a lot more references
515     //       rather than copying PathVectors all over the place
516     if (show_orig_path) {
517         // add original path to helperpaths
518         SPCurve* curve = sp_shape_get_curve (SP_SHAPE(lpeitem));
519         hp_vec.push_back(curve->get_pathvector());
520     }
522     // add other helperpaths provided by the effect itself
523     addCanvasIndicators(lpeitem, hp_vec);
525     // add helperpaths provided by the effect's parameters
526     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
527         (*p)->addCanvasIndicators(lpeitem, hp_vec);
528     }
530     return hp_vec;
533 /**
534  * Add possible canvas indicators (i.e., helperpaths other than the original path) to \a hp_vec
535  * This function should be overwritten by derived effects if they want to provide their own helperpaths.
536  */
537 void
538 Effect::addCanvasIndicators(SPLPEItem */*lpeitem*/, std::vector<Geom::PathVector> &/*hp_vec*/)
543 /**
544  * This *creates* a new widget, management of deletion should be done by the caller
545  */
546 Gtk::Widget *
547 Effect::newWidget(Gtk::Tooltips * tooltips)
549     // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
550     Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox() );
552     vbox->set_border_width(5);
554     std::vector<Parameter *>::iterator it = param_vector.begin();
555     while (it != param_vector.end()) {
556         Parameter * param = *it;
557         Gtk::Widget * widg = param->param_newWidget(tooltips);
558         Glib::ustring * tip = param->param_getTooltip();
559         if (widg) {
560            vbox->pack_start(*widg, true, true, 2);
561             if (tip != NULL) {
562                 tooltips->set_tip(*widg, *tip);
563             }
564         }
566         it++;
567     }
569     return dynamic_cast<Gtk::Widget *>(vbox);
573 Inkscape::XML::Node *
574 Effect::getRepr()
576     return SP_OBJECT_REPR(lpeobj);
579 SPDocument *
580 Effect::getSPDoc()
582     if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
583     return SP_OBJECT_DOCUMENT(lpeobj);
586 Parameter *
587 Effect::getParameter(const char * key)
589     Glib::ustring stringkey(key);
591     std::vector<Parameter *>::iterator it = param_vector.begin();
592     while (it != param_vector.end()) {
593         Parameter * param = *it;
594         if ( param->param_key == key) {
595             return param;
596         }
598         it++;
599     }
601     return NULL;
604 Parameter *
605 Effect::getNextOncanvasEditableParam()
607     if (param_vector.size() == 0) // no parameters
608         return NULL;
610     oncanvasedit_it++;
611     if (oncanvasedit_it >= static_cast<int>(param_vector.size())) {
612         oncanvasedit_it = 0;
613     }
614     int old_it = oncanvasedit_it;
616     do {
617         Parameter * param = param_vector[oncanvasedit_it];
618         if(param && param->oncanvas_editable) {
619             return param;
620         } else {
621             oncanvasedit_it++;
622             if (oncanvasedit_it == static_cast<int>(param_vector.size())) {  // loop round the map
623                 oncanvasedit_it = 0;
624             }
625         }
626     } while (oncanvasedit_it != old_it); // iterate until complete loop through map has been made
628     return NULL;
631 void
632 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
634     if (!desktop) return;
636     Parameter * param = getNextOncanvasEditableParam();
637     if (param) {
638         param->param_editOncanvas(item, desktop);
639         gchar *message = g_strdup_printf(_("Editing parameter <b>%s</b>."), param->param_label.c_str());
640         desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, message);
641         g_free(message);
642     } else {
643         desktop->messageStack()->flash( Inkscape::WARNING_MESSAGE,
644                                         _("None of the applied path effect's parameters can be edited on-canvas.") );
645     }
648 /* This function should reset the defaults and is used for example to initialize an effect right after it has been applied to a path
649 * 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!
650 */
651 void
652 Effect::resetDefaults(SPItem * /*item*/)
654     // do nothing for simple effects
657 void
658 Effect::setup_nodepath(Inkscape::NodePath::Path *np)
660     np->helperpath_rgba = 0xff0000ff;
661     np->helperpath_width = 1.0;
664 void
665 Effect::transform_multiply(Geom::Matrix const& postmul, bool set)
667     // cycle through all parameters. Most parameters will not need transformation, but path and point params do.
668     for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); it++) {
669         Parameter * param = *it;
670         param->param_transform_multiply(postmul, set);
671     }
674 // TODO: take _all_ parameters into account, not only PointParams
675 bool
676 Effect::providesKnotholder()
678     // does the effect actively provide any knotholder entities of its own?
679     if (kh_entity_vector.size() > 0)
680         return true;
682     // otherwise: are there any parameters that have knotholderentities?
683     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
684         if ((*p)->providesKnotHolderEntities()) {
685             return true;
686         }
687     }
689     return false;
692 } /* namespace LivePathEffect */
694 } /* namespace Inkscape */
696 /*
697   Local Variables:
698   mode:c++
699   c-file-style:"stroustrup"
700   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
701   indent-tabs-mode:nil
702   fill-column:99
703   End:
704 */
705 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :