Code

apologies for our very fine windows build system
[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 namespace Inkscape {
77 namespace LivePathEffect {
79 const Util::EnumData<EffectType> LPETypeData[] = {
80     // {constant defined in effect-enum.h, N_("name of your effect"), "name of your effect in SVG"}
81 #ifdef LPE_ENABLE_TEST_EFFECTS
82     {DOEFFECTSTACK_TEST,    N_("doEffect stack test"),     "doeffectstacktest"},
83     {DYNASTROKE,            N_("Dynamic stroke"),          "dynastroke"},
84 #endif
85     {ANGLE_BISECTOR,        N_("Angle bisector"),          "angle_bisector"},
86     {BEND_PATH,             N_("Bend"),                     "bend_path"},
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     {CONSTRUCT_GRID,        N_("Construct grid"),          "construct_grid"},
91     {ENVELOPE,              N_("Envelope Deformation"),    "envelope"},
92     {FREEHAND_SHAPE,        N_("Freehand Shape"),          "freehand_shape"}, // this is actually a special type of PatternAlongPath, used to paste shapes in pen/pencil tool
93     {GEARS,                 N_("Gears"),                   "gears"},
94     {ROUGH_HATCHES,         N_("Hatches (rough)"),            "rough_hatches"},
95     {INTERPOLATE,           N_("Interpolate Sub-Paths"),   "interpolate"},
96     {KNOT,                  N_("Knot"),                    "knot"},
97     {LATTICE,               N_("Lattice Deformation"),     "lattice"},
98     {LINE_SEGMENT,          N_("Line Segment"),            "line_segment"},
99     {MIRROR_SYMMETRY,       N_("Mirror symmetry"),         "mirror_symmetry"},
100     {OFFSET,                N_("Offset"),                  "offset"},
101     {PARALLEL,              N_("Parallel"),                "parallel"},
102     {PATH_LENGTH,           N_("Path length"),             "path_length"},
103     {PATTERN_ALONG_PATH,    N_("Pattern Along Path"),      "skeletal"},   // for historic reasons, this effect is called skeletal(strokes) in Inkscape:SVG
104     {PERP_BISECTOR,         N_("Perpendicular bisector"),  "perp_bisector"},
105     {PERSPECTIVE_PATH,      N_("Perspective path"),        "perspective_path"},
106     {COPY_ROTATE,           N_("Rotate copies"),           "copy_rotate"},
107     {RULER,                 N_("Ruler"),                   "ruler"},
108     {SKETCH,                N_("Sketch"),                  "sketch"},
109     {SPIRO,                 N_("Spiro spline"),            "spiro"},
110     {CURVE_STITCH,          N_("Stitch Sub-Paths"),        "curvestitching"},
111     {TANGENT_TO_CURVE,      N_("Tangent to curve"),        "tangent_to_curve"},
112     {TEXT_LABEL,            N_("Text label"),              "text_label"},
113     {VONKOCH,               N_("VonKoch"),                 "vonkoch"},
114 };
115 const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData));
117 int
118 Effect::acceptsNumClicks(EffectType type) {
119     switch (type) {
120         case INVALID_LPE: return -1; // in case we want to distinguish between invalid LPE and valid ones that expect zero clicks
121         case ANGLE_BISECTOR: return 3;
122         case CIRCLE_3PTS: return 3;
123         case CIRCLE_WITH_RADIUS: return 2;
124         case LINE_SEGMENT: return 2;
125         case PERP_BISECTOR: return 2;
126         default: return 0;
127     }
130 Effect*
131 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
133     Effect* neweffect = NULL;
134     switch (lpenr) {
135         case PATTERN_ALONG_PATH:
136             neweffect = static_cast<Effect*> ( new LPEPatternAlongPath(lpeobj) );
137             break;
138         case FREEHAND_SHAPE:
139             neweffect = static_cast<Effect*> ( new LPEFreehandShape(lpeobj) );
140             break;
141         case BEND_PATH:
142             neweffect = static_cast<Effect*> ( new LPEBendPath(lpeobj) );
143             break;
144         case SKETCH:
145             neweffect = static_cast<Effect*> ( new LPESketch(lpeobj) );
146             break;
147         case ROUGH_HATCHES:
148             neweffect = static_cast<Effect*> ( new LPERoughHatches(lpeobj) );
149             break;
150         case VONKOCH:
151             neweffect = static_cast<Effect*> ( new LPEVonKoch(lpeobj) );
152             break;
153         case KNOT:
154             neweffect = static_cast<Effect*> ( new LPEKnot(lpeobj) );
155             break;
156         case GEARS:
157             neweffect = static_cast<Effect*> ( new LPEGears(lpeobj) );
158             break;
159         case CURVE_STITCH:
160             neweffect = static_cast<Effect*> ( new LPECurveStitch(lpeobj) );
161             break;
162         case LATTICE:
163             neweffect = static_cast<Effect*> ( new LPELattice(lpeobj) );
164             break;
165         case ENVELOPE:
166             neweffect = static_cast<Effect*> ( new LPEEnvelope(lpeobj) );
167             break;
168         case CIRCLE_WITH_RADIUS:
169             neweffect = static_cast<Effect*> ( new LPECircleWithRadius(lpeobj) );
170             break;
171         case PERSPECTIVE_PATH:
172             neweffect = static_cast<Effect*> ( new LPEPerspectivePath(lpeobj) );
173             break;
174         case SPIRO:
175             neweffect = static_cast<Effect*> ( new LPESpiro(lpeobj) );
176             break;
177         case CONSTRUCT_GRID:
178             neweffect = static_cast<Effect*> ( new LPEConstructGrid(lpeobj) );
179             break;
180         case PERP_BISECTOR:
181             neweffect = static_cast<Effect*> ( new LPEPerpBisector(lpeobj) );
182             break;
183         case TANGENT_TO_CURVE:
184             neweffect = static_cast<Effect*> ( new LPETangentToCurve(lpeobj) );
185             break;
186         case MIRROR_SYMMETRY:
187             neweffect = static_cast<Effect*> ( new LPEMirrorSymmetry(lpeobj) );
188             break;
189         case CIRCLE_3PTS:
190             neweffect = static_cast<Effect*> ( new LPECircle3Pts(lpeobj) );
191             break;
192         case ANGLE_BISECTOR:
193             neweffect = static_cast<Effect*> ( new LPEAngleBisector(lpeobj) );
194             break;
195         case PARALLEL:
196             neweffect = static_cast<Effect*> ( new LPEParallel(lpeobj) );
197             break;
198         case COPY_ROTATE:
199             neweffect = static_cast<Effect*> ( new LPECopyRotate(lpeobj) );
200             break;
201         case OFFSET:
202             neweffect = static_cast<Effect*> ( new LPEOffset(lpeobj) );
203             break;
204         case RULER:
205             neweffect = static_cast<Effect*> ( new LPERuler(lpeobj) );
206             break;
207         case BOOLOPS:
208             neweffect = static_cast<Effect*> ( new LPEBoolops(lpeobj) );
209             break;
210         case INTERPOLATE:
211             neweffect = static_cast<Effect*> ( new LPEInterpolate(lpeobj) );
212             break;
213         case TEXT_LABEL:
214             neweffect = static_cast<Effect*> ( new LPETextLabel(lpeobj) );
215             break;
216         case PATH_LENGTH:
217             neweffect = static_cast<Effect*> ( new LPEPathLength(lpeobj) );
218             break;
219         case LINE_SEGMENT:
220             neweffect = static_cast<Effect*> ( new LPELineSegment(lpeobj) );
221             break;
222         case DOEFFECTSTACK_TEST:
223             neweffect = static_cast<Effect*> ( new LPEdoEffectStackTest(lpeobj) );
224             break;
225         case DYNASTROKE:
226             neweffect = static_cast<Effect*> ( new LPEDynastroke(lpeobj) );
227             break;
228         default:
229             g_warning("LivePathEffect::Effect::New   called with invalid patheffect type (%d)", lpenr);
230             neweffect = NULL;
231             break;
232     }
234     if (neweffect) {
235         neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));
236     }
238     return neweffect;
241 void
242 Effect::createAndApply(const char* name, SPDocument *doc, SPItem *item)
244     // Path effect definition
245     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
246     Inkscape::XML::Node *repr = xml_doc->createElement("inkscape:path-effect");
247     repr->setAttribute("effect", name);
249     SP_OBJECT_REPR(SP_DOCUMENT_DEFS(doc))->addChild(repr, NULL); // adds to <defs> and assigns the 'id' attribute
250     const gchar * repr_id = repr->attribute("id");
251     Inkscape::GC::release(repr);
253     gchar *href = g_strdup_printf("#%s", repr_id);
254     sp_lpe_item_add_path_effect(SP_LPE_ITEM(item), href, true);
255     g_free(href);
258 void
259 Effect::createAndApply(EffectType type, SPDocument *doc, SPItem *item)
261     createAndApply(LPETypeConverter.get_key(type).c_str(), doc, item);
264 Effect::Effect(LivePathEffectObject *lpeobject)
265     : oncanvasedit_it(0),
266       is_visible(_("Is visible?"), _("If unchecked, the effect remains applied to the object but is temporarily disabled on canvas"), "is_visible", &wr, this, true),
267       deactivate_knotholder(_("Deactivate knotholder?"), _("Check this to deactivate knotholder handles (useful if they interfere with node handles during editing)"), "deactivate_knotholder", &wr, this, false),
268       show_orig_path(false),
269       lpeobj(lpeobject),
270       concatenate_before_pwd2(false),
271       provides_own_flash_paths(true), // is automatically set to false if providesOwnFlashPaths() is not overridden
272       is_ready(false) // is automatically set to false if providesOwnFlashPaths() is not overridden
274     registerParameter( dynamic_cast<Parameter *>(&is_visible) );
275     registerParameter( dynamic_cast<Parameter *>(&deactivate_knotholder) );
278 Effect::~Effect()
282 Glib::ustring
283 Effect::getName()
285     if (lpeobj->effecttype_set && LPETypeConverter.is_valid_id(lpeobj->effecttype) )
286         return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );
287     else
288         return Glib::ustring( _("No effect") );
291 EffectType
292 Effect::effectType() {
293     return lpeobj->effecttype;
296 /**
297  * Is performed a single time when the effect is freshly applied to a path
298  */
299 void
300 Effect::doOnApply (SPLPEItem */*lpeitem*/)
304 /**
305  * Is performed each time before the effect is updated.
306  */
307 void
308 Effect::doBeforeEffect (SPLPEItem */*lpeitem*/)
310     //Do nothing for simple effects
313 /**
314  * Effects can have a parameter path set before they are applied by accepting a nonzero number of
315  * mouse clicks. This method activates the pen context, which waits for the specified number of
316  * clicks. Override Effect::acceptsNumClicks() to return the number of expected mouse clicks.
317  */
318 void
319 Effect::doAcceptPathPreparations(SPLPEItem *lpeitem)
321     // switch to pen context
322     SPDesktop *desktop = inkscape_active_desktop(); // TODO: Is there a better method to find the item's desktop?
323     if (!tools_isactive(desktop, TOOLS_FREEHAND_PEN)) {
324         tools_switch(desktop, TOOLS_FREEHAND_PEN);
325     }
327     SPEventContext *ec = desktop->event_context;
328     SPPenContext *pc = SP_PEN_CONTEXT(ec);
329     pc->expecting_clicks_for_LPE = this->acceptsNumClicks();
330     pc->waiting_LPE = this;
331     pc->waiting_item = lpeitem;
332     pc->polylines_only = true;
334     ec->desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE,
335         g_strdup_printf(_("Please specify a parameter path for the LPE '%s' with %d mouse clicks"),
336                         getName().c_str(), acceptsNumClicks()));
339 void
340 Effect::writeParamsToSVG() {
341     std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
342     for (p = param_vector.begin(); p != param_vector.end(); ++p) {
343         (*p)->write_to_SVG();
344     }
347 /**
348  * If the effect expects a path parameter (specified by a number of mouse clicks) before it is
349  * applied, this is the method that processes the resulting path. Override it to customize it for
350  * your LPE. But don't forget to call the parent method so that is_ready is set to true!
351  */
352 void
353 Effect::acceptParamPath (SPPath */*param_path*/) {
354     setReady();
357 /*
358  *  Here be the doEffect function chain:
359  */
360 void
361 Effect::doEffect (SPCurve * curve)
363     std::vector<Geom::Path> orig_pathv = curve->get_pathvector();
365     std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
367     curve->set_pathvector(result_pathv);
370 std::vector<Geom::Path>
371 Effect::doEffect_path (std::vector<Geom::Path> const & path_in)
373     std::vector<Geom::Path> path_out;
375     if ( !concatenate_before_pwd2 ) {
376         // default behavior
377         for (unsigned int i=0; i < path_in.size(); i++) {
378             Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb();
379             Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
380             std::vector<Geom::Path> path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
381             // add the output path vector to the already accumulated vector:
382             for (unsigned int j=0; j < path.size(); j++) {
383                 path_out.push_back(path[j]);
384             }
385         }
386     } else {
387       // concatenate the path into possibly discontinuous pwd2
388         Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
389         for (unsigned int i=0; i < path_in.size(); i++) {
390             pwd2_in.concat( path_in[i].toPwSb() );
391         }
392         Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
393         path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
394     }
396     return path_out;
399 Geom::Piecewise<Geom::D2<Geom::SBasis> >
400 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
402     g_warning("Effect has no doEffect implementation");
403     return pwd2_in;
406 void
407 Effect::readallParameters(Inkscape::XML::Node * repr)
409     std::vector<Parameter *>::iterator it = param_vector.begin();
410     while (it != param_vector.end()) {
411         Parameter * param = *it;
412         const gchar * key = param->param_key.c_str();
413         const gchar * value = repr->attribute(key);
414         if (value) {
415             bool accepted = param->param_readSVGValue(value);
416             if (!accepted) {
417                 g_warning("Effect::readallParameters - '%s' not accepted for %s", value, key);
418             }
419         } else {
420             // set default value
421             param->param_set_default();
422         }
424         it++;
425     }
428 /* This function does not and SHOULD NOT write to XML */
429 void
430 Effect::setParameter(const gchar * key, const gchar * new_value)
432     Parameter * param = getParameter(key);
433     if (param) {
434         if (new_value) {
435             bool accepted = param->param_readSVGValue(new_value);
436             if (!accepted) {
437                 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);
438             }
439         } else {
440             // set default value
441             param->param_set_default();
442         }
443     }
446 void
447 Effect::registerParameter(Parameter * param)
449     param_vector.push_back(param);
452 // TODO: should we provide a way to alter the handle's appearance?
453 void
454 Effect::registerKnotHolderHandle(KnotHolderEntity* entity, const char* descr)
456     kh_entity_vector.push_back(std::make_pair(entity, descr));
459 /**
460  * Add all registered LPE knotholder handles to the knotholder
461  */
462 void
463 Effect::addHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
464     using namespace Inkscape::LivePathEffect;
466     if (deactivate_knotholder)
467         return;
469     // add handles provided by the effect itself
470     addKnotHolderEntities(knotholder, desktop, item);
472     // add handles provided by the effect's parameters (if any)
473     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
474         (*p)->addKnotHolderEntities(knotholder, desktop, item);
475     }
478 void
479 Effect::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
480     // TODO: The entities in kh_entity_vector are already instantiated during the call
481     //       to registerKnotHolderHandle(), but they are recreated here. Also, we must not
482     //       delete them when the knotholder is destroyed, whence the clumsy function
483     //       isDeletable(). If we could create entities of different classes dynamically,
484     //       this would be much nicer. How to do this?
485     std::vector<std::pair<KnotHolderEntity*, const char*> >::iterator i;
486     for (i = kh_entity_vector.begin(); i != kh_entity_vector.end(); ++i) {
487         KnotHolderEntity *entity = i->first;
488         const char *descr = i->second;
490         entity->create(desktop, item, knotholder, descr);
491         knotholder->add(entity);
492     }
495 /**
496  * Return a vector of PathVectors which contain all helperpaths that should be drawn by the effect.
497  * This is the function called by external code like SPLPEItem.
498  */
499 std::vector<Geom::PathVector>
500 Effect::getHelperPaths(SPLPEItem *lpeitem)
502     std::vector<Geom::PathVector> hp_vec;
504     if (!SP_IS_SHAPE(lpeitem)) {
505         g_print ("How to handle helperpaths for non-shapes?\n"); // non-shapes are for example SPGroups.
506         return hp_vec;
507     }
509     // TODO: we can probably optimize this by using a lot more references
510     //       rather than copying PathVectors all over the place
511     if (show_orig_path) {
512         // add original path to helperpaths
513         SPCurve* curve = sp_shape_get_curve (SP_SHAPE(lpeitem));
514         hp_vec.push_back(curve->get_pathvector());
515     }
517     // add other helperpaths provided by the effect itself
518     addCanvasIndicators(lpeitem, hp_vec);
520     // add helperpaths provided by the effect's parameters
521     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
522         (*p)->addCanvasIndicators(lpeitem, hp_vec);
523     }
525     return hp_vec;
528 /**
529  * Add possible canvas indicators (i.e., helperpaths other than the original path) to \a hp_vec
530  * This function should be overwritten by derived effects if they want to provide their own helperpaths.
531  */
532 void
533 Effect::addCanvasIndicators(SPLPEItem */*lpeitem*/, std::vector<Geom::PathVector> &/*hp_vec*/)
538 /**
539  * This *creates* a new widget, management of deletion should be done by the caller
540  */
541 Gtk::Widget *
542 Effect::newWidget(Gtk::Tooltips * tooltips)
544     // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
545     Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox() );
547     vbox->set_border_width(5);
549     std::vector<Parameter *>::iterator it = param_vector.begin();
550     while (it != param_vector.end()) {
551         Parameter * param = *it;
552         Gtk::Widget * widg = param->param_newWidget(tooltips);
553         Glib::ustring * tip = param->param_getTooltip();
554         if (widg) {
555            vbox->pack_start(*widg, true, true, 2);
556             if (tip != NULL) {
557                 tooltips->set_tip(*widg, *tip);
558             }
559         }
561         it++;
562     }
564     return dynamic_cast<Gtk::Widget *>(vbox);
568 Inkscape::XML::Node *
569 Effect::getRepr()
571     return SP_OBJECT_REPR(lpeobj);
574 SPDocument *
575 Effect::getSPDoc()
577     if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
578     return SP_OBJECT_DOCUMENT(lpeobj);
581 Parameter *
582 Effect::getParameter(const char * key)
584     Glib::ustring stringkey(key);
586     std::vector<Parameter *>::iterator it = param_vector.begin();
587     while (it != param_vector.end()) {
588         Parameter * param = *it;
589         if ( param->param_key == key) {
590             return param;
591         }
593         it++;
594     }
596     return NULL;
599 Parameter *
600 Effect::getNextOncanvasEditableParam()
602     if (param_vector.size() == 0) // no parameters
603         return NULL;
605     oncanvasedit_it++;
606     if (oncanvasedit_it >= static_cast<int>(param_vector.size())) {
607         oncanvasedit_it = 0;
608     }
609     int old_it = oncanvasedit_it;
611     do {
612         Parameter * param = param_vector[oncanvasedit_it];
613         if(param && param->oncanvas_editable) {
614             return param;
615         } else {
616             oncanvasedit_it++;
617             if (oncanvasedit_it == static_cast<int>(param_vector.size())) {  // loop round the map
618                 oncanvasedit_it = 0;
619             }
620         }
621     } while (oncanvasedit_it != old_it); // iterate until complete loop through map has been made
623     return NULL;
626 void
627 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
629     if (!desktop) return;
631     Parameter * param = getNextOncanvasEditableParam();
632     if (param) {
633         param->param_editOncanvas(item, desktop);
634         gchar *message = g_strdup_printf(_("Editing parameter <b>%s</b>."), param->param_label.c_str());
635         desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, message);
636         g_free(message);
637     } else {
638         desktop->messageStack()->flash( Inkscape::WARNING_MESSAGE,
639                                         _("None of the applied path effect's parameters can be edited on-canvas.") );
640     }
643 /* This function should reset the defaults and is used for example to initialize an effect right after it has been applied to a path
644 * 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!
645 */
646 void
647 Effect::resetDefaults(SPItem * /*item*/)
649     // do nothing for simple effects
652 void
653 Effect::setup_nodepath(Inkscape::NodePath::Path *np)
655     np->helperpath_rgba = 0xff0000ff;
656     np->helperpath_width = 1.0;
659 void
660 Effect::transform_multiply(Geom::Matrix const& postmul, bool set)
662     // cycle through all parameters. Most parameters will not need transformation, but path and point params do.
663     for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); it++) {
664         Parameter * param = *it;
665         param->param_transform_multiply(postmul, set);
666     }
669 // TODO: take _all_ parameters into account, not only PointParams
670 bool
671 Effect::providesKnotholder()
673     // does the effect actively provide any knotholder entities of its own?
674     if (kh_entity_vector.size() > 0)
675         return true;
677     // otherwise: are there any parameters that have knotholderentities?
678     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
679         if ((*p)->providesKnotHolderEntities()) {
680             return true;
681         }
682     }
684     return false;
687 } /* namespace LivePathEffect */
689 } /* namespace Inkscape */
691 /*
692   Local Variables:
693   mode:c++
694   c-file-style:"stroustrup"
695   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
696   indent-tabs-mode:nil
697   fill-column:99
698   End:
699 */
700 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :