Code

A simple layout document as to what, why and how is cppification.
[inkscape.git] / src / live_effects / effect.cpp
1 #define INKSCAPE_LIVEPATHEFFECT_CPP
3 /*
4  * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
5  *
6  * Released under GNU GPL, read the file 'COPYING' for more information
7  */
9 //#define LPE_ENABLE_TEST_EFFECTS
11 #include "live_effects/effect.h"
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include "xml/node-event-vector.h"
18 #include "sp-object.h"
19 #include "attributes.h"
20 #include "message-stack.h"
21 #include "desktop.h"
22 #include "inkscape.h"
23 #include "document.h"
24 #include "document-private.h"
25 #include "xml/document.h"
26 #include <glibmm/i18n.h>
27 #include "pen-context.h"
28 #include "tools-switch.h"
29 #include "message-stack.h"
30 #include "desktop.h"
31 #include "knotholder.h"
32 #include "sp-lpe-item.h"
33 #include "live_effects/lpeobject.h"
34 #include "live_effects/parameter/parameter.h"
35 #include <glibmm/ustring.h>
36 #include "display/curve.h"
37 #include <gtkmm.h>
39 #include <exception>
41 #include <2geom/sbasis-to-bezier.h>
42 #include <2geom/matrix.h>
43 #include <2geom/pathvector.h>
45 // include effects:
46 #include "live_effects/lpe-patternalongpath.h"
47 #include "live_effects/lpe-bendpath.h"
48 #include "live_effects/lpe-sketch.h"
49 #include "live_effects/lpe-vonkoch.h"
50 #include "live_effects/lpe-knot.h"
51 #include "live_effects/lpe-rough-hatches.h"
52 #include "live_effects/lpe-dynastroke.h"
53 #include "live_effects/lpe-test-doEffect-stack.h"
54 #include "live_effects/lpe-gears.h"
55 #include "live_effects/lpe-curvestitch.h"
56 #include "live_effects/lpe-circle_with_radius.h"
57 #include "live_effects/lpe-perspective_path.h"
58 #include "live_effects/lpe-spiro.h"
59 #include "live_effects/lpe-lattice.h"
60 #include "live_effects/lpe-envelope.h"
61 #include "live_effects/lpe-constructgrid.h"
62 #include "live_effects/lpe-perp_bisector.h"
63 #include "live_effects/lpe-tangent_to_curve.h"
64 #include "live_effects/lpe-mirror_symmetry.h"
65 #include "live_effects/lpe-circle_3pts.h"
66 #include "live_effects/lpe-angle_bisector.h"
67 #include "live_effects/lpe-parallel.h"
68 #include "live_effects/lpe-copy_rotate.h"
69 #include "live_effects/lpe-offset.h"
70 #include "live_effects/lpe-ruler.h"
71 #include "live_effects/lpe-boolops.h"
72 #include "live_effects/lpe-interpolate.h"
73 #include "live_effects/lpe-text_label.h"
74 #include "live_effects/lpe-path_length.h"
75 #include "live_effects/lpe-line_segment.h"
76 #include "live_effects/lpe-recursiveskeleton.h"
77 #include "live_effects/lpe-extrude.h"
80 namespace Inkscape {
82 namespace LivePathEffect {
84 const Util::EnumData<EffectType> LPETypeData[] = {
85     // {constant defined in effect-enum.h, N_("name of your effect"), "name of your effect in SVG"}
86 #ifdef LPE_ENABLE_TEST_EFFECTS
87     {DOEFFECTSTACK_TEST,    N_("doEffect stack test"),     "doeffectstacktest"},
88     {ANGLE_BISECTOR,        N_("Angle bisector"),          "angle_bisector"},
89     // TRANSLATORS: boolean operations
90     {BOOLOPS,               N_("Boolops"),                 "boolops"},
91     {CIRCLE_WITH_RADIUS,    N_("Circle (by center and radius)"),   "circle_with_radius"},
92     {CIRCLE_3PTS,           N_("Circle by 3 points"),      "circle_3pts"},
93     {DYNASTROKE,            N_("Dynamic stroke"),          "dynastroke"},
94     {EXTRUDE,               N_("Extrude"),                 "extrude"},
95     {LATTICE,               N_("Lattice Deformation"),     "lattice"},
96     {LINE_SEGMENT,          N_("Line Segment"),            "line_segment"},
97     {MIRROR_SYMMETRY,       N_("Mirror symmetry"),         "mirror_symmetry"},
98     {OFFSET,                N_("Offset"),                  "offset"},
99     {PARALLEL,              N_("Parallel"),                "parallel"},
100     {PATH_LENGTH,           N_("Path length"),             "path_length"},
101     {PERP_BISECTOR,         N_("Perpendicular bisector"),  "perp_bisector"},
102     {PERSPECTIVE_PATH,      N_("Perspective path"),        "perspective_path"},
103     {COPY_ROTATE,           N_("Rotate copies"),           "copy_rotate"},
104     {RECURSIVE_SKELETON,    N_("Recursive skeleton"),      "recursive_skeleton"},
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     {INTERPOLATE,           N_("Interpolate Sub-Paths"),   "interpolate"},
120     {ROUGH_HATCHES,         N_("Hatches (rough)"),         "rough_hatches"},
121     {SKETCH,                N_("Sketch"),                  "sketch"},
122     {RULER,                 N_("Ruler"),                   "ruler"},
123 };
124 const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData));
126 int
127 Effect::acceptsNumClicks(EffectType type) {
128     switch (type) {
129         case INVALID_LPE: return -1; // in case we want to distinguish between invalid LPE and valid ones that expect zero clicks
130         case ANGLE_BISECTOR: return 3;
131         case CIRCLE_3PTS: return 3;
132         case CIRCLE_WITH_RADIUS: return 2;
133         case LINE_SEGMENT: return 2;
134         case PERP_BISECTOR: return 2;
135         default: return 0;
136     }
139 Effect*
140 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
142     Effect* neweffect = NULL;
143     switch (lpenr) {
144         case PATTERN_ALONG_PATH:
145             neweffect = static_cast<Effect*> ( new LPEPatternAlongPath(lpeobj) );
146             break;
147         case BEND_PATH:
148             neweffect = static_cast<Effect*> ( new LPEBendPath(lpeobj) );
149             break;
150         case SKETCH:
151             neweffect = static_cast<Effect*> ( new LPESketch(lpeobj) );
152             break;
153         case ROUGH_HATCHES:
154             neweffect = static_cast<Effect*> ( new LPERoughHatches(lpeobj) );
155             break;
156         case VONKOCH:
157             neweffect = static_cast<Effect*> ( new LPEVonKoch(lpeobj) );
158             break;
159         case KNOT:
160             neweffect = static_cast<Effect*> ( new LPEKnot(lpeobj) );
161             break;
162         case GEARS:
163             neweffect = static_cast<Effect*> ( new LPEGears(lpeobj) );
164             break;
165         case CURVE_STITCH:
166             neweffect = static_cast<Effect*> ( new LPECurveStitch(lpeobj) );
167             break;
168         case LATTICE:
169             neweffect = static_cast<Effect*> ( new LPELattice(lpeobj) );
170             break;
171         case ENVELOPE:
172             neweffect = static_cast<Effect*> ( new LPEEnvelope(lpeobj) );
173             break;
174         case CIRCLE_WITH_RADIUS:
175             neweffect = static_cast<Effect*> ( new LPECircleWithRadius(lpeobj) );
176             break;
177         case PERSPECTIVE_PATH:
178             neweffect = static_cast<Effect*> ( new LPEPerspectivePath(lpeobj) );
179             break;
180         case SPIRO:
181             neweffect = static_cast<Effect*> ( new LPESpiro(lpeobj) );
182             break;
183         case CONSTRUCT_GRID:
184             neweffect = static_cast<Effect*> ( new LPEConstructGrid(lpeobj) );
185             break;
186         case PERP_BISECTOR:
187             neweffect = static_cast<Effect*> ( new LPEPerpBisector(lpeobj) );
188             break;
189         case TANGENT_TO_CURVE:
190             neweffect = static_cast<Effect*> ( new LPETangentToCurve(lpeobj) );
191             break;
192         case MIRROR_SYMMETRY:
193             neweffect = static_cast<Effect*> ( new LPEMirrorSymmetry(lpeobj) );
194             break;
195         case CIRCLE_3PTS:
196             neweffect = static_cast<Effect*> ( new LPECircle3Pts(lpeobj) );
197             break;
198         case ANGLE_BISECTOR:
199             neweffect = static_cast<Effect*> ( new LPEAngleBisector(lpeobj) );
200             break;
201         case PARALLEL:
202             neweffect = static_cast<Effect*> ( new LPEParallel(lpeobj) );
203             break;
204         case COPY_ROTATE:
205             neweffect = static_cast<Effect*> ( new LPECopyRotate(lpeobj) );
206             break;
207         case OFFSET:
208             neweffect = static_cast<Effect*> ( new LPEOffset(lpeobj) );
209             break;
210         case RULER:
211             neweffect = static_cast<Effect*> ( new LPERuler(lpeobj) );
212             break;
213         case BOOLOPS:
214             neweffect = static_cast<Effect*> ( new LPEBoolops(lpeobj) );
215             break;
216         case INTERPOLATE:
217             neweffect = static_cast<Effect*> ( new LPEInterpolate(lpeobj) );
218             break;
219         case TEXT_LABEL:
220             neweffect = static_cast<Effect*> ( new LPETextLabel(lpeobj) );
221             break;
222         case PATH_LENGTH:
223             neweffect = static_cast<Effect*> ( new LPEPathLength(lpeobj) );
224             break;
225         case LINE_SEGMENT:
226             neweffect = static_cast<Effect*> ( new LPELineSegment(lpeobj) );
227             break;
228         case DOEFFECTSTACK_TEST:
229             neweffect = static_cast<Effect*> ( new LPEdoEffectStackTest(lpeobj) );
230             break;
231         case DYNASTROKE:
232             neweffect = static_cast<Effect*> ( new LPEDynastroke(lpeobj) );
233             break;
234         case RECURSIVE_SKELETON:
235             neweffect = static_cast<Effect*> ( new LPERecursiveSkeleton(lpeobj) );
236             break;
237         case EXTRUDE:
238             neweffect = static_cast<Effect*> ( new LPEExtrude(lpeobj) );
239             break;
240         default:
241             g_warning("LivePathEffect::Effect::New   called with invalid patheffect type (%d)", lpenr);
242             neweffect = NULL;
243             break;
244     }
246     if (neweffect) {
247         neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));
248     }
250     return neweffect;
253 void
254 Effect::createAndApply(const char* name, SPDocument *doc, SPItem *item)
256     // Path effect definition
257     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
258     Inkscape::XML::Node *repr = xml_doc->createElement("inkscape:path-effect");
259     repr->setAttribute("effect", name);
261     SP_OBJECT_REPR(SP_DOCUMENT_DEFS(doc))->addChild(repr, NULL); // adds to <defs> and assigns the 'id' attribute
262     const gchar * repr_id = repr->attribute("id");
263     Inkscape::GC::release(repr);
265     gchar *href = g_strdup_printf("#%s", repr_id);
266     sp_lpe_item_add_path_effect(SP_LPE_ITEM(item), href, true);
267     g_free(href);
270 void
271 Effect::createAndApply(EffectType type, SPDocument *doc, SPItem *item)
273     createAndApply(LPETypeConverter.get_key(type).c_str(), doc, item);
276 Effect::Effect(LivePathEffectObject *lpeobject)
277     : oncanvasedit_it(0),
278       is_visible(_("Is visible?"), _("If unchecked, the effect remains applied to the object but is temporarily disabled on canvas"), "is_visible", &wr, this, true),
279       show_orig_path(false),
280       lpeobj(lpeobject),
281       concatenate_before_pwd2(false),
282       provides_own_flash_paths(true), // is automatically set to false if providesOwnFlashPaths() is not overridden
283       is_ready(false) // is automatically set to false if providesOwnFlashPaths() is not overridden
285     registerParameter( dynamic_cast<Parameter *>(&is_visible) );
286     is_visible.widget_is_visible = false;
289 Effect::~Effect()
293 Glib::ustring
294 Effect::getName()
296     if (lpeobj->effecttype_set && LPETypeConverter.is_valid_id(lpeobj->effecttype) )
297         return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );
298     else
299         return Glib::ustring( _("No effect") );
302 EffectType
303 Effect::effectType() {
304     return lpeobj->effecttype;
307 /**
308  * Is performed a single time when the effect is freshly applied to a path
309  */
310 void
311 Effect::doOnApply (SPLPEItem */*lpeitem*/)
315 /**
316  * Is performed each time before the effect is updated.
317  */
318 void
319 Effect::doBeforeEffect (SPLPEItem */*lpeitem*/)
321     //Do nothing for simple effects
324 /**
325  * Effects can have a parameter path set before they are applied by accepting a nonzero number of
326  * mouse clicks. This method activates the pen context, which waits for the specified number of
327  * clicks. Override Effect::acceptsNumClicks() to return the number of expected mouse clicks.
328  */
329 void
330 Effect::doAcceptPathPreparations(SPLPEItem *lpeitem)
332     // switch to pen context
333     SPDesktop *desktop = inkscape_active_desktop(); // TODO: Is there a better method to find the item's desktop?
334     if (!tools_isactive(desktop, TOOLS_FREEHAND_PEN)) {
335         tools_switch(desktop, TOOLS_FREEHAND_PEN);
336     }
338     SPEventContext *ec = desktop->event_context;
339     SPPenContext *pc = SP_PEN_CONTEXT(ec);
340     pc->expecting_clicks_for_LPE = this->acceptsNumClicks();
341     pc->waiting_LPE = this;
342     pc->waiting_item = lpeitem;
343     pc->polylines_only = true;
345     ec->desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE,
346         g_strdup_printf(_("Please specify a parameter path for the LPE '%s' with %d mouse clicks"),
347                         getName().c_str(), acceptsNumClicks()));
350 void
351 Effect::writeParamsToSVG() {
352     std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
353     for (p = param_vector.begin(); p != param_vector.end(); ++p) {
354         (*p)->write_to_SVG();
355     }
358 /**
359  * If the effect expects a path parameter (specified by a number of mouse clicks) before it is
360  * applied, this is the method that processes the resulting path. Override it to customize it for
361  * your LPE. But don't forget to call the parent method so that is_ready is set to true!
362  */
363 void
364 Effect::acceptParamPath (SPPath */*param_path*/) {
365     setReady();
368 /*
369  *  Here be the doEffect function chain:
370  */
371 void
372 Effect::doEffect (SPCurve * curve)
374     std::vector<Geom::Path> orig_pathv = curve->get_pathvector();
376     std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
378     curve->set_pathvector(result_pathv);
381 std::vector<Geom::Path>
382 Effect::doEffect_path (std::vector<Geom::Path> const & path_in)
384     std::vector<Geom::Path> path_out;
386     if ( !concatenate_before_pwd2 ) {
387         // default behavior
388         for (unsigned int i=0; i < path_in.size(); i++) {
389             Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb();
390             Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
391             std::vector<Geom::Path> path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
392             // add the output path vector to the already accumulated vector:
393             for (unsigned int j=0; j < path.size(); j++) {
394                 path_out.push_back(path[j]);
395             }
396         }
397     } else {
398       // concatenate the path into possibly discontinuous pwd2
399         Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
400         for (unsigned int i=0; i < path_in.size(); i++) {
401             pwd2_in.concat( path_in[i].toPwSb() );
402         }
403         Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
404         path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
405     }
407     return path_out;
410 Geom::Piecewise<Geom::D2<Geom::SBasis> >
411 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
413     g_warning("Effect has no doEffect implementation");
414     return pwd2_in;
417 void
418 Effect::readallParameters(Inkscape::XML::Node * repr)
420     std::vector<Parameter *>::iterator it = param_vector.begin();
421     while (it != param_vector.end()) {
422         Parameter * param = *it;
423         const gchar * key = param->param_key.c_str();
424         const gchar * value = repr->attribute(key);
425         if (value) {
426             bool accepted = param->param_readSVGValue(value);
427             if (!accepted) {
428                 g_warning("Effect::readallParameters - '%s' not accepted for %s", value, key);
429             }
430         } else {
431             // set default value
432             param->param_set_default();
433         }
435         it++;
436     }
439 /* This function does not and SHOULD NOT write to XML */
440 void
441 Effect::setParameter(const gchar * key, const gchar * new_value)
443     Parameter * param = getParameter(key);
444     if (param) {
445         if (new_value) {
446             bool accepted = param->param_readSVGValue(new_value);
447             if (!accepted) {
448                 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);
449             }
450         } else {
451             // set default value
452             param->param_set_default();
453         }
454     }
457 void
458 Effect::registerParameter(Parameter * param)
460     param_vector.push_back(param);
463 // TODO: should we provide a way to alter the handle's appearance?
464 void
465 Effect::registerKnotHolderHandle(KnotHolderEntity* entity, const char* descr)
467     kh_entity_vector.push_back(std::make_pair(entity, descr));
470 /**
471  * Add all registered LPE knotholder handles to the knotholder
472  */
473 void
474 Effect::addHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
475     using namespace Inkscape::LivePathEffect;
477     // add handles provided by the effect itself
478     addKnotHolderEntities(knotholder, desktop, item);
480     // add handles provided by the effect's parameters (if any)
481     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
482         (*p)->addKnotHolderEntities(knotholder, desktop, item);
483     }
486 void
487 Effect::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
488     // TODO: The entities in kh_entity_vector are already instantiated during the call
489     //       to registerKnotHolderHandle(), but they are recreated here. Also, we must not
490     //       delete them when the knotholder is destroyed, whence the clumsy function
491     //       isDeletable(). If we could create entities of different classes dynamically,
492     //       this would be much nicer. How to do this?
493     std::vector<std::pair<KnotHolderEntity*, const char*> >::iterator i;
494     for (i = kh_entity_vector.begin(); i != kh_entity_vector.end(); ++i) {
495         KnotHolderEntity *entity = i->first;
496         const char *descr = i->second;
498         entity->create(desktop, item, knotholder, descr);
499         knotholder->add(entity);
500     }
503 /**
504  * Return a vector of PathVectors which contain all helperpaths that should be drawn by the effect.
505  * This is the function called by external code like SPLPEItem.
506  */
507 std::vector<Geom::PathVector>
508 Effect::getHelperPaths(SPLPEItem *lpeitem)
510     std::vector<Geom::PathVector> hp_vec;
512     if (!SP_IS_SHAPE(lpeitem)) {
513 //        g_print ("How to handle helperpaths for non-shapes?\n"); // non-shapes are for example SPGroups.
514         return hp_vec;
515     }
517     // TODO: we can probably optimize this by using a lot more references
518     //       rather than copying PathVectors all over the place
519     if (show_orig_path) {
520         // add original path to helperpaths
521         SPCurve* curve = SP_SHAPE(lpeitem)->getCurve ();
522         hp_vec.push_back(curve->get_pathvector());
523     }
525     // add other helperpaths provided by the effect itself
526     addCanvasIndicators(lpeitem, hp_vec);
528     // add helperpaths provided by the effect's parameters
529     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
530         (*p)->addCanvasIndicators(lpeitem, hp_vec);
531     }
533     return hp_vec;
536 /**
537  * Add possible canvas indicators (i.e., helperpaths other than the original path) to \a hp_vec
538  * This function should be overwritten by derived effects if they want to provide their own helperpaths.
539  */
540 void
541 Effect::addCanvasIndicators(SPLPEItem */*lpeitem*/, std::vector<Geom::PathVector> &/*hp_vec*/)
546 /**
547  * This *creates* a new widget, management of deletion should be done by the caller
548  */
549 Gtk::Widget *
550 Effect::newWidget(Gtk::Tooltips * tooltips)
552     // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
553     Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox() );
555     vbox->set_border_width(5);
557     std::vector<Parameter *>::iterator it = param_vector.begin();
558     while (it != param_vector.end()) {
559         if ((*it)->widget_is_visible) {
560             Parameter * param = *it;
561             Gtk::Widget * widg = param->param_newWidget(tooltips);
562             Glib::ustring * tip = param->param_getTooltip();
563             if (widg) {
564                 vbox->pack_start(*widg, true, true, 2);
565                 if (tip != NULL) {
566                     tooltips->set_tip(*widg, *tip);
567                 }
568             }
569         }
571         it++;
572     }
574     return dynamic_cast<Gtk::Widget *>(vbox);
578 Inkscape::XML::Node *
579 Effect::getRepr()
581     return SP_OBJECT_REPR(lpeobj);
584 SPDocument *
585 Effect::getSPDoc()
587     if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
588     return SP_OBJECT_DOCUMENT(lpeobj);
591 Parameter *
592 Effect::getParameter(const char * key)
594     Glib::ustring stringkey(key);
596     std::vector<Parameter *>::iterator it = param_vector.begin();
597     while (it != param_vector.end()) {
598         Parameter * param = *it;
599         if ( param->param_key == key) {
600             return param;
601         }
603         it++;
604     }
606     return NULL;
609 Parameter *
610 Effect::getNextOncanvasEditableParam()
612     if (param_vector.size() == 0) // no parameters
613         return NULL;
615     oncanvasedit_it++;
616     if (oncanvasedit_it >= static_cast<int>(param_vector.size())) {
617         oncanvasedit_it = 0;
618     }
619     int old_it = oncanvasedit_it;
621     do {
622         Parameter * param = param_vector[oncanvasedit_it];
623         if(param && param->oncanvas_editable) {
624             return param;
625         } else {
626             oncanvasedit_it++;
627             if (oncanvasedit_it == static_cast<int>(param_vector.size())) {  // loop round the map
628                 oncanvasedit_it = 0;
629             }
630         }
631     } while (oncanvasedit_it != old_it); // iterate until complete loop through map has been made
633     return NULL;
636 void
637 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
639     if (!desktop) return;
641     Parameter * param = getNextOncanvasEditableParam();
642     if (param) {
643         param->param_editOncanvas(item, desktop);
644         gchar *message = g_strdup_printf(_("Editing parameter <b>%s</b>."), param->param_label.c_str());
645         desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, message);
646         g_free(message);
647     } else {
648         desktop->messageStack()->flash( Inkscape::WARNING_MESSAGE,
649                                         _("None of the applied path effect's parameters can be edited on-canvas.") );
650     }
653 /* This function should reset the defaults and is used for example to initialize an effect right after it has been applied to a path
654 * 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!
655 */
656 void
657 Effect::resetDefaults(SPItem * /*item*/)
659     std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
660     for (p = param_vector.begin(); p != param_vector.end(); ++p) {
661         (*p)->param_set_default();
662         (*p)->write_to_SVG();
663     }
666 void
667 Effect::transform_multiply(Geom::Matrix const& postmul, bool set)
669     // cycle through all parameters. Most parameters will not need transformation, but path and point params do.
670     for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); it++) {
671         Parameter * param = *it;
672         param->param_transform_multiply(postmul, set);
673     }
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 :