1 /*
2 * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
3 * Abhishek Sharma
4 *
5 * Released under GNU GPL, read the file 'COPYING' for more information
6 */
8 //#define LPE_ENABLE_TEST_EFFECTS
10 #include "live_effects/effect.h"
12 #ifdef HAVE_CONFIG_H
13 # include "config.h"
14 #endif
16 #include "xml/node-event-vector.h"
17 #include "sp-object.h"
18 #include "attributes.h"
19 #include "message-stack.h"
20 #include "desktop.h"
21 #include "inkscape.h"
22 #include "document.h"
23 #include "document-private.h"
24 #include "xml/document.h"
25 #include <glibmm/i18n.h>
26 #include "pen-context.h"
27 #include "tools-switch.h"
28 #include "message-stack.h"
29 #include "desktop.h"
30 #include "knotholder.h"
31 #include "sp-lpe-item.h"
32 #include "live_effects/lpeobject.h"
33 #include "live_effects/parameter/parameter.h"
34 #include <glibmm/ustring.h>
35 #include "display/curve.h"
36 #include <gtkmm.h>
38 #include <exception>
40 #include <2geom/sbasis-to-bezier.h>
41 #include <2geom/matrix.h>
42 #include <2geom/pathvector.h>
44 // include effects:
45 #include "live_effects/lpe-patternalongpath.h"
46 #include "live_effects/lpe-bendpath.h"
47 #include "live_effects/lpe-sketch.h"
48 #include "live_effects/lpe-vonkoch.h"
49 #include "live_effects/lpe-knot.h"
50 #include "live_effects/lpe-rough-hatches.h"
51 #include "live_effects/lpe-dynastroke.h"
52 #include "live_effects/lpe-test-doEffect-stack.h"
53 #include "live_effects/lpe-gears.h"
54 #include "live_effects/lpe-curvestitch.h"
55 #include "live_effects/lpe-circle_with_radius.h"
56 #include "live_effects/lpe-perspective_path.h"
57 #include "live_effects/lpe-spiro.h"
58 #include "live_effects/lpe-lattice.h"
59 #include "live_effects/lpe-envelope.h"
60 #include "live_effects/lpe-constructgrid.h"
61 #include "live_effects/lpe-perp_bisector.h"
62 #include "live_effects/lpe-tangent_to_curve.h"
63 #include "live_effects/lpe-mirror_symmetry.h"
64 #include "live_effects/lpe-circle_3pts.h"
65 #include "live_effects/lpe-angle_bisector.h"
66 #include "live_effects/lpe-parallel.h"
67 #include "live_effects/lpe-copy_rotate.h"
68 #include "live_effects/lpe-offset.h"
69 #include "live_effects/lpe-ruler.h"
70 #include "live_effects/lpe-boolops.h"
71 #include "live_effects/lpe-interpolate.h"
72 #include "live_effects/lpe-text_label.h"
73 #include "live_effects/lpe-path_length.h"
74 #include "live_effects/lpe-line_segment.h"
75 #include "live_effects/lpe-recursiveskeleton.h"
76 #include "live_effects/lpe-extrude.h"
77 #include "live_effects/lpe-powerstroke.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 {POWERSTROKE, N_("Power stroke"), "powerstroke"},
104 {COPY_ROTATE, N_("Rotate copies"), "copy_rotate"},
105 {RECURSIVE_SKELETON, N_("Recursive skeleton"), "recursive_skeleton"},
106 {TANGENT_TO_CURVE, N_("Tangent to curve"), "tangent_to_curve"},
107 {TEXT_LABEL, N_("Text label"), "text_label"},
108 #endif
109 /* 0.46 */
110 {BEND_PATH, N_("Bend"), "bend_path"},
111 {GEARS, N_("Gears"), "gears"},
112 {PATTERN_ALONG_PATH, N_("Pattern Along Path"), "skeletal"}, // for historic reasons, this effect is called skeletal(strokes) in Inkscape:SVG
113 {CURVE_STITCH, N_("Stitch Sub-Paths"), "curvestitching"},
114 /* 0.47 */
115 {VONKOCH, N_("VonKoch"), "vonkoch"},
116 {KNOT, N_("Knot"), "knot"},
117 {CONSTRUCT_GRID, N_("Construct grid"), "construct_grid"},
118 {SPIRO, N_("Spiro spline"), "spiro"},
119 {ENVELOPE, N_("Envelope Deformation"), "envelope"},
120 {INTERPOLATE, N_("Interpolate Sub-Paths"), "interpolate"},
121 {ROUGH_HATCHES, N_("Hatches (rough)"), "rough_hatches"},
122 {SKETCH, N_("Sketch"), "sketch"},
123 {RULER, N_("Ruler"), "ruler"},
124 /* 0.49 */
125 };
126 const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData));
128 int
129 Effect::acceptsNumClicks(EffectType type) {
130 switch (type) {
131 case INVALID_LPE: return -1; // in case we want to distinguish between invalid LPE and valid ones that expect zero clicks
132 case ANGLE_BISECTOR: return 3;
133 case CIRCLE_3PTS: return 3;
134 case CIRCLE_WITH_RADIUS: return 2;
135 case LINE_SEGMENT: return 2;
136 case PERP_BISECTOR: return 2;
137 default: return 0;
138 }
139 }
141 Effect*
142 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
143 {
144 Effect* neweffect = NULL;
145 switch (lpenr) {
146 case PATTERN_ALONG_PATH:
147 neweffect = static_cast<Effect*> ( new LPEPatternAlongPath(lpeobj) );
148 break;
149 case BEND_PATH:
150 neweffect = static_cast<Effect*> ( new LPEBendPath(lpeobj) );
151 break;
152 case SKETCH:
153 neweffect = static_cast<Effect*> ( new LPESketch(lpeobj) );
154 break;
155 case ROUGH_HATCHES:
156 neweffect = static_cast<Effect*> ( new LPERoughHatches(lpeobj) );
157 break;
158 case VONKOCH:
159 neweffect = static_cast<Effect*> ( new LPEVonKoch(lpeobj) );
160 break;
161 case KNOT:
162 neweffect = static_cast<Effect*> ( new LPEKnot(lpeobj) );
163 break;
164 case GEARS:
165 neweffect = static_cast<Effect*> ( new LPEGears(lpeobj) );
166 break;
167 case CURVE_STITCH:
168 neweffect = static_cast<Effect*> ( new LPECurveStitch(lpeobj) );
169 break;
170 case LATTICE:
171 neweffect = static_cast<Effect*> ( new LPELattice(lpeobj) );
172 break;
173 case ENVELOPE:
174 neweffect = static_cast<Effect*> ( new LPEEnvelope(lpeobj) );
175 break;
176 case CIRCLE_WITH_RADIUS:
177 neweffect = static_cast<Effect*> ( new LPECircleWithRadius(lpeobj) );
178 break;
179 case PERSPECTIVE_PATH:
180 neweffect = static_cast<Effect*> ( new LPEPerspectivePath(lpeobj) );
181 break;
182 case SPIRO:
183 neweffect = static_cast<Effect*> ( new LPESpiro(lpeobj) );
184 break;
185 case CONSTRUCT_GRID:
186 neweffect = static_cast<Effect*> ( new LPEConstructGrid(lpeobj) );
187 break;
188 case PERP_BISECTOR:
189 neweffect = static_cast<Effect*> ( new LPEPerpBisector(lpeobj) );
190 break;
191 case TANGENT_TO_CURVE:
192 neweffect = static_cast<Effect*> ( new LPETangentToCurve(lpeobj) );
193 break;
194 case MIRROR_SYMMETRY:
195 neweffect = static_cast<Effect*> ( new LPEMirrorSymmetry(lpeobj) );
196 break;
197 case CIRCLE_3PTS:
198 neweffect = static_cast<Effect*> ( new LPECircle3Pts(lpeobj) );
199 break;
200 case ANGLE_BISECTOR:
201 neweffect = static_cast<Effect*> ( new LPEAngleBisector(lpeobj) );
202 break;
203 case PARALLEL:
204 neweffect = static_cast<Effect*> ( new LPEParallel(lpeobj) );
205 break;
206 case COPY_ROTATE:
207 neweffect = static_cast<Effect*> ( new LPECopyRotate(lpeobj) );
208 break;
209 case OFFSET:
210 neweffect = static_cast<Effect*> ( new LPEOffset(lpeobj) );
211 break;
212 case RULER:
213 neweffect = static_cast<Effect*> ( new LPERuler(lpeobj) );
214 break;
215 case BOOLOPS:
216 neweffect = static_cast<Effect*> ( new LPEBoolops(lpeobj) );
217 break;
218 case INTERPOLATE:
219 neweffect = static_cast<Effect*> ( new LPEInterpolate(lpeobj) );
220 break;
221 case TEXT_LABEL:
222 neweffect = static_cast<Effect*> ( new LPETextLabel(lpeobj) );
223 break;
224 case PATH_LENGTH:
225 neweffect = static_cast<Effect*> ( new LPEPathLength(lpeobj) );
226 break;
227 case LINE_SEGMENT:
228 neweffect = static_cast<Effect*> ( new LPELineSegment(lpeobj) );
229 break;
230 case DOEFFECTSTACK_TEST:
231 neweffect = static_cast<Effect*> ( new LPEdoEffectStackTest(lpeobj) );
232 break;
233 case DYNASTROKE:
234 neweffect = static_cast<Effect*> ( new LPEDynastroke(lpeobj) );
235 break;
236 case RECURSIVE_SKELETON:
237 neweffect = static_cast<Effect*> ( new LPERecursiveSkeleton(lpeobj) );
238 break;
239 case EXTRUDE:
240 neweffect = static_cast<Effect*> ( new LPEExtrude(lpeobj) );
241 break;
242 case POWERSTROKE:
243 neweffect = static_cast<Effect*> ( new LPEPowerStroke(lpeobj) );
244 break;
245 default:
246 g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr);
247 neweffect = NULL;
248 break;
249 }
251 if (neweffect) {
252 neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));
253 }
255 return neweffect;
256 }
258 void Effect::createAndApply(const char* name, SPDocument *doc, SPItem *item)
259 {
260 // Path effect definition
261 Inkscape::XML::Document *xml_doc = doc->getReprDoc();
262 Inkscape::XML::Node *repr = xml_doc->createElement("inkscape:path-effect");
263 repr->setAttribute("effect", name);
265 SP_OBJECT_REPR(SP_DOCUMENT_DEFS(doc))->addChild(repr, NULL); // adds to <defs> and assigns the 'id' attribute
266 const gchar * repr_id = repr->attribute("id");
267 Inkscape::GC::release(repr);
269 gchar *href = g_strdup_printf("#%s", repr_id);
270 sp_lpe_item_add_path_effect(SP_LPE_ITEM(item), href, true);
271 g_free(href);
272 }
274 void
275 Effect::createAndApply(EffectType type, SPDocument *doc, SPItem *item)
276 {
277 createAndApply(LPETypeConverter.get_key(type).c_str(), doc, item);
278 }
280 Effect::Effect(LivePathEffectObject *lpeobject)
281 : oncanvasedit_it(0),
282 is_visible(_("Is visible?"), _("If unchecked, the effect remains applied to the object but is temporarily disabled on canvas"), "is_visible", &wr, this, true),
283 show_orig_path(false),
284 lpeobj(lpeobject),
285 concatenate_before_pwd2(false),
286 provides_own_flash_paths(true), // is automatically set to false if providesOwnFlashPaths() is not overridden
287 is_ready(false) // is automatically set to false if providesOwnFlashPaths() is not overridden
288 {
289 registerParameter( dynamic_cast<Parameter *>(&is_visible) );
290 is_visible.widget_is_visible = false;
291 }
293 Effect::~Effect()
294 {
295 }
297 Glib::ustring
298 Effect::getName()
299 {
300 if (lpeobj->effecttype_set && LPETypeConverter.is_valid_id(lpeobj->effecttype) )
301 return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );
302 else
303 return Glib::ustring( _("No effect") );
304 }
306 EffectType
307 Effect::effectType() {
308 return lpeobj->effecttype;
309 }
311 /**
312 * Is performed a single time when the effect is freshly applied to a path
313 */
314 void
315 Effect::doOnApply (SPLPEItem */*lpeitem*/)
316 {
317 }
319 /**
320 * Is performed each time before the effect is updated.
321 */
322 void
323 Effect::doBeforeEffect (SPLPEItem */*lpeitem*/)
324 {
325 //Do nothing for simple effects
326 }
328 /**
329 * Effects can have a parameter path set before they are applied by accepting a nonzero number of
330 * mouse clicks. This method activates the pen context, which waits for the specified number of
331 * clicks. Override Effect::acceptsNumClicks() to return the number of expected mouse clicks.
332 */
333 void
334 Effect::doAcceptPathPreparations(SPLPEItem *lpeitem)
335 {
336 // switch to pen context
337 SPDesktop *desktop = inkscape_active_desktop(); // TODO: Is there a better method to find the item's desktop?
338 if (!tools_isactive(desktop, TOOLS_FREEHAND_PEN)) {
339 tools_switch(desktop, TOOLS_FREEHAND_PEN);
340 }
342 SPEventContext *ec = desktop->event_context;
343 SPPenContext *pc = SP_PEN_CONTEXT(ec);
344 pc->expecting_clicks_for_LPE = this->acceptsNumClicks();
345 pc->waiting_LPE = this;
346 pc->waiting_item = lpeitem;
347 pc->polylines_only = true;
349 ec->desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE,
350 g_strdup_printf(_("Please specify a parameter path for the LPE '%s' with %d mouse clicks"),
351 getName().c_str(), acceptsNumClicks()));
352 }
354 void
355 Effect::writeParamsToSVG() {
356 std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
357 for (p = param_vector.begin(); p != param_vector.end(); ++p) {
358 (*p)->write_to_SVG();
359 }
360 }
362 /**
363 * If the effect expects a path parameter (specified by a number of mouse clicks) before it is
364 * applied, this is the method that processes the resulting path. Override it to customize it for
365 * your LPE. But don't forget to call the parent method so that is_ready is set to true!
366 */
367 void
368 Effect::acceptParamPath (SPPath */*param_path*/) {
369 setReady();
370 }
372 /*
373 * Here be the doEffect function chain:
374 */
375 void
376 Effect::doEffect (SPCurve * curve)
377 {
378 std::vector<Geom::Path> orig_pathv = curve->get_pathvector();
380 std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
382 curve->set_pathvector(result_pathv);
383 }
385 std::vector<Geom::Path>
386 Effect::doEffect_path (std::vector<Geom::Path> const & path_in)
387 {
388 std::vector<Geom::Path> path_out;
390 if ( !concatenate_before_pwd2 ) {
391 // default behavior
392 for (unsigned int i=0; i < path_in.size(); i++) {
393 Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb();
394 Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
395 std::vector<Geom::Path> path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
396 // add the output path vector to the already accumulated vector:
397 for (unsigned int j=0; j < path.size(); j++) {
398 path_out.push_back(path[j]);
399 }
400 }
401 } else {
402 // concatenate the path into possibly discontinuous pwd2
403 Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
404 for (unsigned int i=0; i < path_in.size(); i++) {
405 pwd2_in.concat( path_in[i].toPwSb() );
406 }
407 Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
408 path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
409 }
411 return path_out;
412 }
414 Geom::Piecewise<Geom::D2<Geom::SBasis> >
415 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
416 {
417 g_warning("Effect has no doEffect implementation");
418 return pwd2_in;
419 }
421 void
422 Effect::readallParameters(Inkscape::XML::Node * repr)
423 {
424 std::vector<Parameter *>::iterator it = param_vector.begin();
425 while (it != param_vector.end()) {
426 Parameter * param = *it;
427 const gchar * key = param->param_key.c_str();
428 const gchar * value = repr->attribute(key);
429 if (value) {
430 bool accepted = param->param_readSVGValue(value);
431 if (!accepted) {
432 g_warning("Effect::readallParameters - '%s' not accepted for %s", value, key);
433 }
434 } else {
435 // set default value
436 param->param_set_default();
437 }
439 it++;
440 }
441 }
443 /* This function does not and SHOULD NOT write to XML */
444 void
445 Effect::setParameter(const gchar * key, const gchar * new_value)
446 {
447 Parameter * param = getParameter(key);
448 if (param) {
449 if (new_value) {
450 bool accepted = param->param_readSVGValue(new_value);
451 if (!accepted) {
452 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);
453 }
454 } else {
455 // set default value
456 param->param_set_default();
457 }
458 }
459 }
461 void
462 Effect::registerParameter(Parameter * param)
463 {
464 param_vector.push_back(param);
465 }
467 // TODO: should we provide a way to alter the handle's appearance?
468 void
469 Effect::registerKnotHolderHandle(KnotHolderEntity* entity, const char* descr)
470 {
471 kh_entity_vector.push_back(std::make_pair(entity, descr));
472 }
474 /**
475 * Add all registered LPE knotholder handles to the knotholder
476 */
477 void
478 Effect::addHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
479 using namespace Inkscape::LivePathEffect;
481 // add handles provided by the effect itself
482 addKnotHolderEntities(knotholder, desktop, item);
484 // add handles provided by the effect's parameters (if any)
485 for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
486 (*p)->addKnotHolderEntities(knotholder, desktop, item);
487 }
488 }
490 void
491 Effect::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
492 // TODO: The entities in kh_entity_vector are already instantiated during the call
493 // to registerKnotHolderHandle(), but they are recreated here. Also, we must not
494 // delete them when the knotholder is destroyed, whence the clumsy function
495 // isDeletable(). If we could create entities of different classes dynamically,
496 // this would be much nicer. How to do this?
497 std::vector<std::pair<KnotHolderEntity*, const char*> >::iterator i;
498 for (i = kh_entity_vector.begin(); i != kh_entity_vector.end(); ++i) {
499 KnotHolderEntity *entity = i->first;
500 const char *descr = i->second;
502 entity->create(desktop, item, knotholder, descr);
503 knotholder->add(entity);
504 }
505 }
507 /**
508 * Return a vector of PathVectors which contain all helperpaths that should be drawn by the effect.
509 * This is the function called by external code like SPLPEItem.
510 */
511 std::vector<Geom::PathVector>
512 Effect::getHelperPaths(SPLPEItem *lpeitem)
513 {
514 std::vector<Geom::PathVector> hp_vec;
516 if (!SP_IS_SHAPE(lpeitem)) {
517 // g_print ("How to handle helperpaths for non-shapes?\n"); // non-shapes are for example SPGroups.
518 return hp_vec;
519 }
521 // TODO: we can probably optimize this by using a lot more references
522 // rather than copying PathVectors all over the place
523 if (show_orig_path) {
524 // add original path to helperpaths
525 SPCurve* curve = SP_SHAPE(lpeitem)->getCurve ();
526 hp_vec.push_back(curve->get_pathvector());
527 }
529 // add other helperpaths provided by the effect itself
530 addCanvasIndicators(lpeitem, hp_vec);
532 // add helperpaths provided by the effect's parameters
533 for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
534 (*p)->addCanvasIndicators(lpeitem, hp_vec);
535 }
537 return hp_vec;
538 }
540 /**
541 * Add possible canvas indicators (i.e., helperpaths other than the original path) to \a hp_vec
542 * This function should be overwritten by derived effects if they want to provide their own helperpaths.
543 */
544 void
545 Effect::addCanvasIndicators(SPLPEItem */*lpeitem*/, std::vector<Geom::PathVector> &/*hp_vec*/)
546 {
547 }
550 /**
551 * This *creates* a new widget, management of deletion should be done by the caller
552 */
553 Gtk::Widget *
554 Effect::newWidget(Gtk::Tooltips * tooltips)
555 {
556 // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
557 Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox() );
559 vbox->set_border_width(5);
561 std::vector<Parameter *>::iterator it = param_vector.begin();
562 while (it != param_vector.end()) {
563 if ((*it)->widget_is_visible) {
564 Parameter * param = *it;
565 Gtk::Widget * widg = param->param_newWidget(tooltips);
566 Glib::ustring * tip = param->param_getTooltip();
567 if (widg) {
568 vbox->pack_start(*widg, true, true, 2);
569 if (tip != NULL) {
570 tooltips->set_tip(*widg, *tip);
571 }
572 }
573 }
575 it++;
576 }
578 return dynamic_cast<Gtk::Widget *>(vbox);
579 }
582 Inkscape::XML::Node *
583 Effect::getRepr()
584 {
585 return SP_OBJECT_REPR(lpeobj);
586 }
588 SPDocument *
589 Effect::getSPDoc()
590 {
591 if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
592 return SP_OBJECT_DOCUMENT(lpeobj);
593 }
595 Parameter *
596 Effect::getParameter(const char * key)
597 {
598 Glib::ustring stringkey(key);
600 std::vector<Parameter *>::iterator it = param_vector.begin();
601 while (it != param_vector.end()) {
602 Parameter * param = *it;
603 if ( param->param_key == key) {
604 return param;
605 }
607 it++;
608 }
610 return NULL;
611 }
613 Parameter *
614 Effect::getNextOncanvasEditableParam()
615 {
616 if (param_vector.size() == 0) // no parameters
617 return NULL;
619 oncanvasedit_it++;
620 if (oncanvasedit_it >= static_cast<int>(param_vector.size())) {
621 oncanvasedit_it = 0;
622 }
623 int old_it = oncanvasedit_it;
625 do {
626 Parameter * param = param_vector[oncanvasedit_it];
627 if(param && param->oncanvas_editable) {
628 return param;
629 } else {
630 oncanvasedit_it++;
631 if (oncanvasedit_it == static_cast<int>(param_vector.size())) { // loop round the map
632 oncanvasedit_it = 0;
633 }
634 }
635 } while (oncanvasedit_it != old_it); // iterate until complete loop through map has been made
637 return NULL;
638 }
640 void
641 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
642 {
643 if (!desktop) return;
645 Parameter * param = getNextOncanvasEditableParam();
646 if (param) {
647 param->param_editOncanvas(item, desktop);
648 gchar *message = g_strdup_printf(_("Editing parameter <b>%s</b>."), param->param_label.c_str());
649 desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, message);
650 g_free(message);
651 } else {
652 desktop->messageStack()->flash( Inkscape::WARNING_MESSAGE,
653 _("None of the applied path effect's parameters can be edited on-canvas.") );
654 }
655 }
657 /* This function should reset the defaults and is used for example to initialize an effect right after it has been applied to a path
658 * 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!
659 */
660 void
661 Effect::resetDefaults(SPItem * /*item*/)
662 {
663 std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
664 for (p = param_vector.begin(); p != param_vector.end(); ++p) {
665 (*p)->param_set_default();
666 (*p)->write_to_SVG();
667 }
668 }
670 void
671 Effect::transform_multiply(Geom::Matrix const& postmul, bool set)
672 {
673 // cycle through all parameters. Most parameters will not need transformation, but path and point params do.
674 for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); it++) {
675 Parameter * param = *it;
676 param->param_transform_multiply(postmul, set);
677 }
678 }
680 bool
681 Effect::providesKnotholder()
682 {
683 // does the effect actively provide any knotholder entities of its own?
684 if (kh_entity_vector.size() > 0)
685 return true;
687 // otherwise: are there any parameters that have knotholderentities?
688 for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
689 if ((*p)->providesKnotHolderEntities()) {
690 return true;
691 }
692 }
694 return false;
695 }
697 } /* namespace LivePathEffect */
699 } /* namespace Inkscape */
701 /*
702 Local Variables:
703 mode:c++
704 c-file-style:"stroustrup"
705 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
706 indent-tabs-mode:nil
707 fill-column:99
708 End:
709 */
710 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :