6266fade0297cfe2934de1a0d2df4b8654a6ab18
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 }
137 }
139 Effect*
140 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
141 {
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;
251 }
253 void
254 Effect::createAndApply(const char* name, SPDocument *doc, SPItem *item)
255 {
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);
268 }
270 void
271 Effect::createAndApply(EffectType type, SPDocument *doc, SPItem *item)
272 {
273 createAndApply(LPETypeConverter.get_key(type).c_str(), doc, item);
274 }
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
284 {
285 registerParameter( dynamic_cast<Parameter *>(&is_visible) );
286 is_visible.widget_is_visible = false;
287 }
289 Effect::~Effect()
290 {
291 }
293 Glib::ustring
294 Effect::getName()
295 {
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") );
300 }
302 EffectType
303 Effect::effectType() {
304 return lpeobj->effecttype;
305 }
307 /**
308 * Is performed a single time when the effect is freshly applied to a path
309 */
310 void
311 Effect::doOnApply (SPLPEItem */*lpeitem*/)
312 {
313 }
315 /**
316 * Is performed each time before the effect is updated.
317 */
318 void
319 Effect::doBeforeEffect (SPLPEItem */*lpeitem*/)
320 {
321 //Do nothing for simple effects
322 }
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)
331 {
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()));
348 }
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 }
356 }
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();
366 }
368 /*
369 * Here be the doEffect function chain:
370 */
371 void
372 Effect::doEffect (SPCurve * curve)
373 {
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);
379 }
381 std::vector<Geom::Path>
382 Effect::doEffect_path (std::vector<Geom::Path> const & path_in)
383 {
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;
408 }
410 Geom::Piecewise<Geom::D2<Geom::SBasis> >
411 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
412 {
413 g_warning("Effect has no doEffect implementation");
414 return pwd2_in;
415 }
417 void
418 Effect::readallParameters(Inkscape::XML::Node * repr)
419 {
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 }
437 }
439 /* This function does not and SHOULD NOT write to XML */
440 void
441 Effect::setParameter(const gchar * key, const gchar * new_value)
442 {
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 }
455 }
457 void
458 Effect::registerParameter(Parameter * param)
459 {
460 param_vector.push_back(param);
461 }
463 // TODO: should we provide a way to alter the handle's appearance?
464 void
465 Effect::registerKnotHolderHandle(KnotHolderEntity* entity, const char* descr)
466 {
467 kh_entity_vector.push_back(std::make_pair(entity, descr));
468 }
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 }
484 }
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 }
501 }
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)
509 {
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_get_curve (SP_SHAPE(lpeitem));
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;
534 }
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*/)
542 {
543 }
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)
551 {
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);
575 }
578 Inkscape::XML::Node *
579 Effect::getRepr()
580 {
581 return SP_OBJECT_REPR(lpeobj);
582 }
584 SPDocument *
585 Effect::getSPDoc()
586 {
587 if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
588 return SP_OBJECT_DOCUMENT(lpeobj);
589 }
591 Parameter *
592 Effect::getParameter(const char * key)
593 {
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;
607 }
609 Parameter *
610 Effect::getNextOncanvasEditableParam()
611 {
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;
634 }
636 void
637 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
638 {
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 }
651 }
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*/)
658 {
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 }
664 }
666 void
667 Effect::transform_multiply(Geom::Matrix const& postmul, bool set)
668 {
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 }
674 }
676 bool
677 Effect::providesKnotholder()
678 {
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;
691 }
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 :