Code

remove the deactivate knotholder parameter
[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     {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     {RECURSIVE_SKELETON,    N_("Recursive skeleton"),      "recursive_skeleton"},
104     {RULER,                 N_("Ruler"),                   "ruler"},
105     {TANGENT_TO_CURVE,      N_("Tangent to curve"),        "tangent_to_curve"},
106     {TEXT_LABEL,            N_("Text label"),              "text_label"},
107 #endif
108 /* 0.46 */
109     {BEND_PATH,             N_("Bend"),                     "bend_path"},
110     {GEARS,                 N_("Gears"),                   "gears"},
111     {PATTERN_ALONG_PATH,    N_("Pattern Along Path"),      "skeletal"},   // for historic reasons, this effect is called skeletal(strokes) in Inkscape:SVG
112     {CURVE_STITCH,          N_("Stitch Sub-Paths"),        "curvestitching"},
113 /* 0.47 */
114     {VONKOCH,               N_("VonKoch"),                 "vonkoch"},
115     {KNOT,                  N_("Knot"),                    "knot"},
116     {CONSTRUCT_GRID,        N_("Construct grid"),          "construct_grid"},
117     {SPIRO,                 N_("Spiro spline"),            "spiro"},
118     {ENVELOPE,              N_("Envelope Deformation"),    "envelope"},
119     {ROUGH_HATCHES,         N_("Hatches (rough)"),         "rough_hatches"},
120     {SKETCH,                N_("Sketch"),                  "sketch"},
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) );
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     // add handles provided by the effect itself
472     addKnotHolderEntities(knotholder, desktop, item);
474     // add handles provided by the effect's parameters (if any)
475     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
476         (*p)->addKnotHolderEntities(knotholder, desktop, item);
477     }
480 void
481 Effect::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
482     // TODO: The entities in kh_entity_vector are already instantiated during the call
483     //       to registerKnotHolderHandle(), but they are recreated here. Also, we must not
484     //       delete them when the knotholder is destroyed, whence the clumsy function
485     //       isDeletable(). If we could create entities of different classes dynamically,
486     //       this would be much nicer. How to do this?
487     std::vector<std::pair<KnotHolderEntity*, const char*> >::iterator i;
488     for (i = kh_entity_vector.begin(); i != kh_entity_vector.end(); ++i) {
489         KnotHolderEntity *entity = i->first;
490         const char *descr = i->second;
492         entity->create(desktop, item, knotholder, descr);
493         knotholder->add(entity);
494     }
497 /**
498  * Return a vector of PathVectors which contain all helperpaths that should be drawn by the effect.
499  * This is the function called by external code like SPLPEItem.
500  */
501 std::vector<Geom::PathVector>
502 Effect::getHelperPaths(SPLPEItem *lpeitem)
504     std::vector<Geom::PathVector> hp_vec;
506     if (!SP_IS_SHAPE(lpeitem)) {
507         g_print ("How to handle helperpaths for non-shapes?\n"); // non-shapes are for example SPGroups.
508         return hp_vec;
509     }
511     // TODO: we can probably optimize this by using a lot more references
512     //       rather than copying PathVectors all over the place
513     if (show_orig_path) {
514         // add original path to helperpaths
515         SPCurve* curve = sp_shape_get_curve (SP_SHAPE(lpeitem));
516         hp_vec.push_back(curve->get_pathvector());
517     }
519     // add other helperpaths provided by the effect itself
520     addCanvasIndicators(lpeitem, hp_vec);
522     // add helperpaths provided by the effect's parameters
523     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
524         (*p)->addCanvasIndicators(lpeitem, hp_vec);
525     }
527     return hp_vec;
530 /**
531  * Add possible canvas indicators (i.e., helperpaths other than the original path) to \a hp_vec
532  * This function should be overwritten by derived effects if they want to provide their own helperpaths.
533  */
534 void
535 Effect::addCanvasIndicators(SPLPEItem */*lpeitem*/, std::vector<Geom::PathVector> &/*hp_vec*/)
540 /**
541  * This *creates* a new widget, management of deletion should be done by the caller
542  */
543 Gtk::Widget *
544 Effect::newWidget(Gtk::Tooltips * tooltips)
546     // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
547     Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox() );
549     vbox->set_border_width(5);
551     std::vector<Parameter *>::iterator it = param_vector.begin();
552     while (it != param_vector.end()) {
553         Parameter * param = *it;
554         Gtk::Widget * widg = param->param_newWidget(tooltips);
555         Glib::ustring * tip = param->param_getTooltip();
556         if (widg) {
557            vbox->pack_start(*widg, true, true, 2);
558             if (tip != NULL) {
559                 tooltips->set_tip(*widg, *tip);
560             }
561         }
563         it++;
564     }
566     return dynamic_cast<Gtk::Widget *>(vbox);
570 Inkscape::XML::Node *
571 Effect::getRepr()
573     return SP_OBJECT_REPR(lpeobj);
576 SPDocument *
577 Effect::getSPDoc()
579     if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
580     return SP_OBJECT_DOCUMENT(lpeobj);
583 Parameter *
584 Effect::getParameter(const char * key)
586     Glib::ustring stringkey(key);
588     std::vector<Parameter *>::iterator it = param_vector.begin();
589     while (it != param_vector.end()) {
590         Parameter * param = *it;
591         if ( param->param_key == key) {
592             return param;
593         }
595         it++;
596     }
598     return NULL;
601 Parameter *
602 Effect::getNextOncanvasEditableParam()
604     if (param_vector.size() == 0) // no parameters
605         return NULL;
607     oncanvasedit_it++;
608     if (oncanvasedit_it >= static_cast<int>(param_vector.size())) {
609         oncanvasedit_it = 0;
610     }
611     int old_it = oncanvasedit_it;
613     do {
614         Parameter * param = param_vector[oncanvasedit_it];
615         if(param && param->oncanvas_editable) {
616             return param;
617         } else {
618             oncanvasedit_it++;
619             if (oncanvasedit_it == static_cast<int>(param_vector.size())) {  // loop round the map
620                 oncanvasedit_it = 0;
621             }
622         }
623     } while (oncanvasedit_it != old_it); // iterate until complete loop through map has been made
625     return NULL;
628 void
629 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
631     if (!desktop) return;
633     Parameter * param = getNextOncanvasEditableParam();
634     if (param) {
635         param->param_editOncanvas(item, desktop);
636         gchar *message = g_strdup_printf(_("Editing parameter <b>%s</b>."), param->param_label.c_str());
637         desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, message);
638         g_free(message);
639     } else {
640         desktop->messageStack()->flash( Inkscape::WARNING_MESSAGE,
641                                         _("None of the applied path effect's parameters can be edited on-canvas.") );
642     }
645 /* This function should reset the defaults and is used for example to initialize an effect right after it has been applied to a path
646 * 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!
647 */
648 void
649 Effect::resetDefaults(SPItem * /*item*/)
651     std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
652     for (p = param_vector.begin(); p != param_vector.end(); ++p) {
653         (*p)->param_set_default();
654         (*p)->write_to_SVG();
655     }
658 void
659 Effect::setup_nodepath(Inkscape::NodePath::Path *np)
661     np->helperpath_rgba = 0xff0000ff;
662     np->helperpath_width = 1.0;
665 void
666 Effect::transform_multiply(Geom::Matrix const& postmul, bool set)
668     // cycle through all parameters. Most parameters will not need transformation, but path and point params do.
669     for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); it++) {
670         Parameter * param = *it;
671         param->param_transform_multiply(postmul, set);
672     }
675 // TODO: take _all_ parameters into account, not only PointParams
676 bool
677 Effect::providesKnotholder()
679     // does the effect actively provide any knotholder entities of its own?
680     if (kh_entity_vector.size() > 0)
681         return true;
683     // otherwise: are there any parameters that have knotholderentities?
684     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
685         if ((*p)->providesKnotHolderEntities()) {
686             return true;
687         }
688     }
690     return false;
693 } /* namespace LivePathEffect */
695 } /* namespace Inkscape */
697 /*
698   Local Variables:
699   mode:c++
700   c-file-style:"stroustrup"
701   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
702   indent-tabs-mode:nil
703   fill-column:99
704   End:
705 */
706 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :