Code

Remove 'Is visible?' checkbox from LPE editor because it duplicates functionality
[inkscape.git] / src / live_effects / effect.cpp
1 #define INKSCAPE_LIVEPATHEFFECT_CPP
3 /*
4  * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
5  *
6  * Released under GNU GPL, read the file 'COPYING' for more information
7  */
9 //#define LPE_ENABLE_TEST_EFFECTS
11 #include "live_effects/effect.h"
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include "xml/node-event-vector.h"
18 #include "sp-object.h"
19 #include "attributes.h"
20 #include "message-stack.h"
21 #include "desktop.h"
22 #include "inkscape.h"
23 #include "document.h"
24 #include "document-private.h"
25 #include "xml/document.h"
26 #include <glibmm/i18n.h>
27 #include "pen-context.h"
28 #include "tools-switch.h"
29 #include "message-stack.h"
30 #include "desktop.h"
31 #include "nodepath.h"
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"
79 namespace Inkscape {
81 namespace LivePathEffect {
83 const Util::EnumData<EffectType> LPETypeData[] = {
84     // {constant defined in effect-enum.h, N_("name of your effect"), "name of your effect in SVG"}
85 #ifdef LPE_ENABLE_TEST_EFFECTS
86     {DOEFFECTSTACK_TEST,    N_("doEffect stack test"),     "doeffectstacktest"},
87     {ANGLE_BISECTOR,        N_("Angle bisector"),          "angle_bisector"},
88     // TRANSLATORS: boolean operations
89     {BOOLOPS,               N_("Boolops"),                 "boolops"},
90     {CIRCLE_WITH_RADIUS,    N_("Circle (by center and radius)"),   "circle_with_radius"},
91     {CIRCLE_3PTS,           N_("Circle by 3 points"),      "circle_3pts"},
92     {DYNASTROKE,            N_("Dynamic stroke"),          "dynastroke"},
93     {LATTICE,               N_("Lattice Deformation"),     "lattice"},
94     {LINE_SEGMENT,          N_("Line Segment"),            "line_segment"},
95     {MIRROR_SYMMETRY,       N_("Mirror symmetry"),         "mirror_symmetry"},
96     {OFFSET,                N_("Offset"),                  "offset"},
97     {PARALLEL,              N_("Parallel"),                "parallel"},
98     {PATH_LENGTH,           N_("Path length"),             "path_length"},
99     {PERP_BISECTOR,         N_("Perpendicular bisector"),  "perp_bisector"},
100     {PERSPECTIVE_PATH,      N_("Perspective path"),        "perspective_path"},
101     {COPY_ROTATE,           N_("Rotate copies"),           "copy_rotate"},
102     {RECURSIVE_SKELETON,    N_("Recursive skeleton"),      "recursive_skeleton"},
103     {TANGENT_TO_CURVE,      N_("Tangent to curve"),        "tangent_to_curve"},
104     {TEXT_LABEL,            N_("Text label"),              "text_label"},
105 #endif
106 /* 0.46 */
107     {BEND_PATH,             N_("Bend"),                     "bend_path"},
108     {GEARS,                 N_("Gears"),                   "gears"},
109     {PATTERN_ALONG_PATH,    N_("Pattern Along Path"),      "skeletal"},   // for historic reasons, this effect is called skeletal(strokes) in Inkscape:SVG
110     {CURVE_STITCH,          N_("Stitch Sub-Paths"),        "curvestitching"},
111 /* 0.47 */
112     {VONKOCH,               N_("VonKoch"),                 "vonkoch"},
113     {KNOT,                  N_("Knot"),                    "knot"},
114     {CONSTRUCT_GRID,        N_("Construct grid"),          "construct_grid"},
115     {SPIRO,                 N_("Spiro spline"),            "spiro"},
116     {ENVELOPE,              N_("Envelope Deformation"),    "envelope"},
117     {INTERPOLATE,           N_("Interpolate Sub-Paths"),   "interpolate"},
118     {ROUGH_HATCHES,         N_("Hatches (rough)"),         "rough_hatches"},
119     {SKETCH,                N_("Sketch"),                  "sketch"},
120     {RULER,                 N_("Ruler"),                   "ruler"},
121 };
122 const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData));
124 int
125 Effect::acceptsNumClicks(EffectType type) {
126     switch (type) {
127         case INVALID_LPE: return -1; // in case we want to distinguish between invalid LPE and valid ones that expect zero clicks
128         case ANGLE_BISECTOR: return 3;
129         case CIRCLE_3PTS: return 3;
130         case CIRCLE_WITH_RADIUS: return 2;
131         case LINE_SEGMENT: return 2;
132         case PERP_BISECTOR: return 2;
133         default: return 0;
134     }
137 Effect*
138 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
140     Effect* neweffect = NULL;
141     switch (lpenr) {
142         case PATTERN_ALONG_PATH:
143             neweffect = static_cast<Effect*> ( new LPEPatternAlongPath(lpeobj) );
144             break;
145         case BEND_PATH:
146             neweffect = static_cast<Effect*> ( new LPEBendPath(lpeobj) );
147             break;
148         case SKETCH:
149             neweffect = static_cast<Effect*> ( new LPESketch(lpeobj) );
150             break;
151         case ROUGH_HATCHES:
152             neweffect = static_cast<Effect*> ( new LPERoughHatches(lpeobj) );
153             break;
154         case VONKOCH:
155             neweffect = static_cast<Effect*> ( new LPEVonKoch(lpeobj) );
156             break;
157         case KNOT:
158             neweffect = static_cast<Effect*> ( new LPEKnot(lpeobj) );
159             break;
160         case GEARS:
161             neweffect = static_cast<Effect*> ( new LPEGears(lpeobj) );
162             break;
163         case CURVE_STITCH:
164             neweffect = static_cast<Effect*> ( new LPECurveStitch(lpeobj) );
165             break;
166         case LATTICE:
167             neweffect = static_cast<Effect*> ( new LPELattice(lpeobj) );
168             break;
169         case ENVELOPE:
170             neweffect = static_cast<Effect*> ( new LPEEnvelope(lpeobj) );
171             break;
172         case CIRCLE_WITH_RADIUS:
173             neweffect = static_cast<Effect*> ( new LPECircleWithRadius(lpeobj) );
174             break;
175         case PERSPECTIVE_PATH:
176             neweffect = static_cast<Effect*> ( new LPEPerspectivePath(lpeobj) );
177             break;
178         case SPIRO:
179             neweffect = static_cast<Effect*> ( new LPESpiro(lpeobj) );
180             break;
181         case CONSTRUCT_GRID:
182             neweffect = static_cast<Effect*> ( new LPEConstructGrid(lpeobj) );
183             break;
184         case PERP_BISECTOR:
185             neweffect = static_cast<Effect*> ( new LPEPerpBisector(lpeobj) );
186             break;
187         case TANGENT_TO_CURVE:
188             neweffect = static_cast<Effect*> ( new LPETangentToCurve(lpeobj) );
189             break;
190         case MIRROR_SYMMETRY:
191             neweffect = static_cast<Effect*> ( new LPEMirrorSymmetry(lpeobj) );
192             break;
193         case CIRCLE_3PTS:
194             neweffect = static_cast<Effect*> ( new LPECircle3Pts(lpeobj) );
195             break;
196         case ANGLE_BISECTOR:
197             neweffect = static_cast<Effect*> ( new LPEAngleBisector(lpeobj) );
198             break;
199         case PARALLEL:
200             neweffect = static_cast<Effect*> ( new LPEParallel(lpeobj) );
201             break;
202         case COPY_ROTATE:
203             neweffect = static_cast<Effect*> ( new LPECopyRotate(lpeobj) );
204             break;
205         case OFFSET:
206             neweffect = static_cast<Effect*> ( new LPEOffset(lpeobj) );
207             break;
208         case RULER:
209             neweffect = static_cast<Effect*> ( new LPERuler(lpeobj) );
210             break;
211         case BOOLOPS:
212             neweffect = static_cast<Effect*> ( new LPEBoolops(lpeobj) );
213             break;
214         case INTERPOLATE:
215             neweffect = static_cast<Effect*> ( new LPEInterpolate(lpeobj) );
216             break;
217         case TEXT_LABEL:
218             neweffect = static_cast<Effect*> ( new LPETextLabel(lpeobj) );
219             break;
220         case PATH_LENGTH:
221             neweffect = static_cast<Effect*> ( new LPEPathLength(lpeobj) );
222             break;
223         case LINE_SEGMENT:
224             neweffect = static_cast<Effect*> ( new LPELineSegment(lpeobj) );
225             break;
226         case DOEFFECTSTACK_TEST:
227             neweffect = static_cast<Effect*> ( new LPEdoEffectStackTest(lpeobj) );
228             break;
229         case DYNASTROKE:
230             neweffect = static_cast<Effect*> ( new LPEDynastroke(lpeobj) );
231             break;
232         case RECURSIVE_SKELETON:
233             neweffect = static_cast<Effect*> ( new LPERecursiveSkeleton(lpeobj) );
234             break;
235         default:
236             g_warning("LivePathEffect::Effect::New   called with invalid patheffect type (%d)", lpenr);
237             neweffect = NULL;
238             break;
239     }
241     if (neweffect) {
242         neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));
243     }
245     return neweffect;
248 void
249 Effect::createAndApply(const char* name, SPDocument *doc, SPItem *item)
251     // Path effect definition
252     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
253     Inkscape::XML::Node *repr = xml_doc->createElement("inkscape:path-effect");
254     repr->setAttribute("effect", name);
256     SP_OBJECT_REPR(SP_DOCUMENT_DEFS(doc))->addChild(repr, NULL); // adds to <defs> and assigns the 'id' attribute
257     const gchar * repr_id = repr->attribute("id");
258     Inkscape::GC::release(repr);
260     gchar *href = g_strdup_printf("#%s", repr_id);
261     sp_lpe_item_add_path_effect(SP_LPE_ITEM(item), href, true);
262     g_free(href);
265 void
266 Effect::createAndApply(EffectType type, SPDocument *doc, SPItem *item)
268     createAndApply(LPETypeConverter.get_key(type).c_str(), doc, item);
271 Effect::Effect(LivePathEffectObject *lpeobject)
272     : oncanvasedit_it(0),
273       is_visible(_("Is visible?"), _("If unchecked, the effect remains applied to the object but is temporarily disabled on canvas"), "is_visible", &wr, this, true),
274       show_orig_path(false),
275       lpeobj(lpeobject),
276       concatenate_before_pwd2(false),
277       provides_own_flash_paths(true), // is automatically set to false if providesOwnFlashPaths() is not overridden
278       is_ready(false) // is automatically set to false if providesOwnFlashPaths() is not overridden
280     registerParameter( dynamic_cast<Parameter *>(&is_visible) );
281     is_visible.widget_is_visible = false;
284 Effect::~Effect()
288 Glib::ustring
289 Effect::getName()
291     if (lpeobj->effecttype_set && LPETypeConverter.is_valid_id(lpeobj->effecttype) )
292         return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );
293     else
294         return Glib::ustring( _("No effect") );
297 EffectType
298 Effect::effectType() {
299     return lpeobj->effecttype;
302 /**
303  * Is performed a single time when the effect is freshly applied to a path
304  */
305 void
306 Effect::doOnApply (SPLPEItem */*lpeitem*/)
310 /**
311  * Is performed each time before the effect is updated.
312  */
313 void
314 Effect::doBeforeEffect (SPLPEItem */*lpeitem*/)
316     //Do nothing for simple effects
319 /**
320  * Effects can have a parameter path set before they are applied by accepting a nonzero number of
321  * mouse clicks. This method activates the pen context, which waits for the specified number of
322  * clicks. Override Effect::acceptsNumClicks() to return the number of expected mouse clicks.
323  */
324 void
325 Effect::doAcceptPathPreparations(SPLPEItem *lpeitem)
327     // switch to pen context
328     SPDesktop *desktop = inkscape_active_desktop(); // TODO: Is there a better method to find the item's desktop?
329     if (!tools_isactive(desktop, TOOLS_FREEHAND_PEN)) {
330         tools_switch(desktop, TOOLS_FREEHAND_PEN);
331     }
333     SPEventContext *ec = desktop->event_context;
334     SPPenContext *pc = SP_PEN_CONTEXT(ec);
335     pc->expecting_clicks_for_LPE = this->acceptsNumClicks();
336     pc->waiting_LPE = this;
337     pc->waiting_item = lpeitem;
338     pc->polylines_only = true;
340     ec->desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE,
341         g_strdup_printf(_("Please specify a parameter path for the LPE '%s' with %d mouse clicks"),
342                         getName().c_str(), acceptsNumClicks()));
345 void
346 Effect::writeParamsToSVG() {
347     std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
348     for (p = param_vector.begin(); p != param_vector.end(); ++p) {
349         (*p)->write_to_SVG();
350     }
353 /**
354  * If the effect expects a path parameter (specified by a number of mouse clicks) before it is
355  * applied, this is the method that processes the resulting path. Override it to customize it for
356  * your LPE. But don't forget to call the parent method so that is_ready is set to true!
357  */
358 void
359 Effect::acceptParamPath (SPPath */*param_path*/) {
360     setReady();
363 /*
364  *  Here be the doEffect function chain:
365  */
366 void
367 Effect::doEffect (SPCurve * curve)
369     std::vector<Geom::Path> orig_pathv = curve->get_pathvector();
371     std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
373     curve->set_pathvector(result_pathv);
376 std::vector<Geom::Path>
377 Effect::doEffect_path (std::vector<Geom::Path> const & path_in)
379     std::vector<Geom::Path> path_out;
381     if ( !concatenate_before_pwd2 ) {
382         // default behavior
383         for (unsigned int i=0; i < path_in.size(); i++) {
384             Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb();
385             Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
386             std::vector<Geom::Path> path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
387             // add the output path vector to the already accumulated vector:
388             for (unsigned int j=0; j < path.size(); j++) {
389                 path_out.push_back(path[j]);
390             }
391         }
392     } else {
393       // concatenate the path into possibly discontinuous pwd2
394         Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
395         for (unsigned int i=0; i < path_in.size(); i++) {
396             pwd2_in.concat( path_in[i].toPwSb() );
397         }
398         Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
399         path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
400     }
402     return path_out;
405 Geom::Piecewise<Geom::D2<Geom::SBasis> >
406 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
408     g_warning("Effect has no doEffect implementation");
409     return pwd2_in;
412 void
413 Effect::readallParameters(Inkscape::XML::Node * repr)
415     std::vector<Parameter *>::iterator it = param_vector.begin();
416     while (it != param_vector.end()) {
417         Parameter * param = *it;
418         const gchar * key = param->param_key.c_str();
419         const gchar * value = repr->attribute(key);
420         if (value) {
421             bool accepted = param->param_readSVGValue(value);
422             if (!accepted) {
423                 g_warning("Effect::readallParameters - '%s' not accepted for %s", value, key);
424             }
425         } else {
426             // set default value
427             param->param_set_default();
428         }
430         it++;
431     }
434 /* This function does not and SHOULD NOT write to XML */
435 void
436 Effect::setParameter(const gchar * key, const gchar * new_value)
438     Parameter * param = getParameter(key);
439     if (param) {
440         if (new_value) {
441             bool accepted = param->param_readSVGValue(new_value);
442             if (!accepted) {
443                 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);
444             }
445         } else {
446             // set default value
447             param->param_set_default();
448         }
449     }
452 void
453 Effect::registerParameter(Parameter * param)
455     param_vector.push_back(param);
458 // TODO: should we provide a way to alter the handle's appearance?
459 void
460 Effect::registerKnotHolderHandle(KnotHolderEntity* entity, const char* descr)
462     kh_entity_vector.push_back(std::make_pair(entity, descr));
465 /**
466  * Add all registered LPE knotholder handles to the knotholder
467  */
468 void
469 Effect::addHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
470     using namespace Inkscape::LivePathEffect;
472     // add handles provided by the effect itself
473     addKnotHolderEntities(knotholder, desktop, item);
475     // add handles provided by the effect's parameters (if any)
476     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
477         (*p)->addKnotHolderEntities(knotholder, desktop, item);
478     }
481 void
482 Effect::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
483     // TODO: The entities in kh_entity_vector are already instantiated during the call
484     //       to registerKnotHolderHandle(), but they are recreated here. Also, we must not
485     //       delete them when the knotholder is destroyed, whence the clumsy function
486     //       isDeletable(). If we could create entities of different classes dynamically,
487     //       this would be much nicer. How to do this?
488     std::vector<std::pair<KnotHolderEntity*, const char*> >::iterator i;
489     for (i = kh_entity_vector.begin(); i != kh_entity_vector.end(); ++i) {
490         KnotHolderEntity *entity = i->first;
491         const char *descr = i->second;
493         entity->create(desktop, item, knotholder, descr);
494         knotholder->add(entity);
495     }
498 /**
499  * Return a vector of PathVectors which contain all helperpaths that should be drawn by the effect.
500  * This is the function called by external code like SPLPEItem.
501  */
502 std::vector<Geom::PathVector>
503 Effect::getHelperPaths(SPLPEItem *lpeitem)
505     std::vector<Geom::PathVector> hp_vec;
507     if (!SP_IS_SHAPE(lpeitem)) {
508         g_print ("How to handle helperpaths for non-shapes?\n"); // non-shapes are for example SPGroups.
509         return hp_vec;
510     }
512     // TODO: we can probably optimize this by using a lot more references
513     //       rather than copying PathVectors all over the place
514     if (show_orig_path) {
515         // add original path to helperpaths
516         SPCurve* curve = sp_shape_get_curve (SP_SHAPE(lpeitem));
517         hp_vec.push_back(curve->get_pathvector());
518     }
520     // add other helperpaths provided by the effect itself
521     addCanvasIndicators(lpeitem, hp_vec);
523     // add helperpaths provided by the effect's parameters
524     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
525         (*p)->addCanvasIndicators(lpeitem, hp_vec);
526     }
528     return hp_vec;
531 /**
532  * Add possible canvas indicators (i.e., helperpaths other than the original path) to \a hp_vec
533  * This function should be overwritten by derived effects if they want to provide their own helperpaths.
534  */
535 void
536 Effect::addCanvasIndicators(SPLPEItem */*lpeitem*/, std::vector<Geom::PathVector> &/*hp_vec*/)
541 /**
542  * This *creates* a new widget, management of deletion should be done by the caller
543  */
544 Gtk::Widget *
545 Effect::newWidget(Gtk::Tooltips * tooltips)
547     // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
548     Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox() );
550     vbox->set_border_width(5);
552     std::vector<Parameter *>::iterator it = param_vector.begin();
553     while (it != param_vector.end()) {
554         if ((*it)->widget_is_visible) {
555             Parameter * param = *it;
556             Gtk::Widget * widg = param->param_newWidget(tooltips);
557             Glib::ustring * tip = param->param_getTooltip();
558             if (widg) {
559                 vbox->pack_start(*widg, true, true, 2);
560                 if (tip != NULL) {
561                     tooltips->set_tip(*widg, *tip);
562                 }
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     std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
655     for (p = param_vector.begin(); p != param_vector.end(); ++p) {
656         (*p)->param_set_default();
657         (*p)->write_to_SVG();
658     }
661 void
662 Effect::setup_nodepath(Inkscape::NodePath::Path *np)
664     np->helperpath_rgba = 0xff0000ff;
665     np->helperpath_width = 1.0;
668 void
669 Effect::transform_multiply(Geom::Matrix const& postmul, bool set)
671     // cycle through all parameters. Most parameters will not need transformation, but path and point params do.
672     for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); it++) {
673         Parameter * param = *it;
674         param->param_transform_multiply(postmul, set);
675     }
678 // TODO: take _all_ parameters into account, not only PointParams
679 bool
680 Effect::providesKnotholder()
682     // does the effect actively provide any knotholder entities of its own?
683     if (kh_entity_vector.size() > 0)
684         return true;
686     // otherwise: are there any parameters that have knotholderentities?
687     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
688         if ((*p)->providesKnotHolderEntities()) {
689             return true;
690         }
691     }
693     return false;
696 } /* namespace LivePathEffect */
698 } /* namespace Inkscape */
700 /*
701   Local Variables:
702   mode:c++
703   c-file-style:"stroustrup"
704   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
705   indent-tabs-mode:nil
706   fill-column:99
707   End:
708 */
709 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :