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