From: cilix42 Date: Sun, 8 Jun 2008 17:33:58 +0000 (+0000) Subject: Make knotholders for LPE items finally work; each effect can now overload the addKnot... X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=1e944d29efb206f5d0b5d1069cb098e22169d548;p=inkscape.git Make knotholders for LPE items finally work; each effect can now overload the addKnotHolderHandles() method to add handles which control its parameters. There is now also a virtual onKnotUngrabbed() method for each knotholder entity which can be used to do cleanup tasks (for LPE parameters it currently writes the value to SVG, although this should probably happen automatically) --- diff --git a/src/knot-holder-entity.h b/src/knot-holder-entity.h index 5c273fe29..f0862c874 100644 --- a/src/knot-holder-entity.h +++ b/src/knot-holder-entity.h @@ -43,11 +43,17 @@ public: SPKnotModeType mode = SP_KNOT_MODE_XOR, guint32 color = 0xffffff00); + /* derived classes like PointParam for LPEs use this, e.g., to indicate that we must not call + delete on their pointer when a knotholder is destroyed */ + virtual bool isLPEParam() { return false; } + /* the get/set/click handlers are virtual functions; each handler class for a knot should be derived from KnotHolderEntity and override these functions */ virtual void knot_set(NR::Point const &p, NR::Point const &origin, guint state) = 0; virtual NR::Point knot_get() = 0; virtual void knot_click(guint /*state*/) {} + virtual void onKnotUngrabbed() {} // this is called 'manually' from KnotHolder; would it be + // more efficient to establish another signal connection? void update_knot(); diff --git a/src/knot.cpp b/src/knot.cpp index 4e4cf5678..2348f4aea 100644 --- a/src/knot.cpp +++ b/src/knot.cpp @@ -335,7 +335,7 @@ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot g_signal_emit(knot, knot_signals[UNGRABBED], 0, event->button.state); - knot->_ungrabbed_signal.emit(); + knot->_ungrabbed_signal.emit(knot); } else { g_signal_emit(knot, knot_signals[CLICKED], 0, diff --git a/src/knot.h b/src/knot.h index 07f5040fa..b1505daac 100644 --- a/src/knot.h +++ b/src/knot.h @@ -74,7 +74,7 @@ struct SPKnot : GObject { **/ sigc::signal _moved_signal; sigc::signal _click_signal; - sigc::signal _ungrabbed_signal; + sigc::signal _ungrabbed_signal; //TODO: all the members above should eventualle become private, accessible via setters/getters inline void setSize (guint i) {size = i;} diff --git a/src/knotholder.cpp b/src/knotholder.cpp index 0a3cb2957..1ca280d48 100644 --- a/src/knotholder.cpp +++ b/src/knotholder.cpp @@ -58,7 +58,16 @@ KnotHolder::KnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFun KnotHolder::~KnotHolder() { g_object_unref(G_OBJECT(item)); for(std::list::iterator i = entity.begin(); i != entity.end(); ++i) { - delete *i; + KnotHolderEntity* e = (*i); + if (!e->isLPEParam()) { + // knotholder entity may be deleted + delete (*i); + } else { + // we must not delete the entity since it's an LPE parameter, + // but the handle should be destroyed + g_object_unref(e->knot); + } + (*i) = NULL; } entity.clear(); // this shouldn't be necessary, though } @@ -145,7 +154,7 @@ KnotHolder::knot_moved_handler(SPKnot *knot, NR::Point const *p, guint state) } void -KnotHolder::knot_ungrabbed_handler() +KnotHolder::knot_ungrabbed_handler(SPKnot *knot) { if (this->released) { this->released(this->item); @@ -153,6 +162,16 @@ KnotHolder::knot_ungrabbed_handler() SPObject *object = (SPObject *) this->item; object->updateRepr(object->repr, SP_OBJECT_WRITE_EXT); + /* do cleanup tasks (e.g., for LPE items write the parameter values + * that were changed by dragging the handle to SVG) + */ + for(std::list::iterator i = this->entity.begin(); i != this->entity.end(); ++i) { + KnotHolderEntity *e = *i; + if (e->knot == knot) { + e->onKnotUngrabbed(); // for most KnotHolderEntitys this does nothing + } + } + unsigned int object_verb = SP_VERB_NONE; if (SP_IS_RECT(object)) @@ -177,6 +196,12 @@ KnotHolder::knot_ungrabbed_handler() } } +void +KnotHolder::add(KnotHolderEntity *e) +{ + entity.push_back(e); +} + void KnotHolder::add_pattern_knotholder() { diff --git a/src/knotholder.h b/src/knotholder.h index 316db2082..14a139768 100644 --- a/src/knotholder.h +++ b/src/knotholder.h @@ -46,7 +46,9 @@ public: void knot_moved_handler(SPKnot *knot, NR::Point const *p, guint state); void knot_clicked_handler(SPKnot *knot, guint state); - void knot_ungrabbed_handler(); + void knot_ungrabbed_handler(SPKnot *knot); + + void add(KnotHolderEntity *e); void add_pattern_knotholder(); diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index 0749e4e93..db395c81b 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -321,21 +321,39 @@ Effect::registerParameter(Parameter * param) param_vector.push_back(param); } +// TODO: Does it still make sense to use this? E.g., should we keep a list of knotholder entities +// in the effect itself and add them here in a semi-automatic manner (similarly to how it is +// done with parameters) instead of adding each one separately in the overloaded function +// addKnotHolderHandles()? void Effect::registerKnotHolderHandle(SPKnotHolderSetFunc set_func, SPKnotHolderGetFunc get_func) { - knotholder_func_vector.push_back(std::make_pair(set_func, get_func)); + //knotholder_func_vector.push_back(std::make_pair(set_func, get_func)); } -// TODO: allow for adding click_functions and description strings, too void -Effect::addHandles(KnotHolder *knotholder) { - std::vector >::iterator i; - for (i = knotholder_func_vector.begin(); i != knotholder_func_vector.end(); ++i) { - //knotholder->add(i->first, i->second, NULL, ("")); +Effect::addPointParamHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) { + using namespace std; + for (std::vector::iterator p = param_vector.begin(); p != param_vector.end(); ++p) { + if ((*p)->paramType() == Inkscape::LivePathEffect::POINT_PARAM) { + g_print ("Parameter is of type PointParam\n"); + KnotHolderEntity *e = dynamic_cast(*p); + e->create(desktop, item, knotholder); + knotholder->add(e); + } else { + g_print ("Parameter is *not* of type PointParam\n"); + } } } +/** + * Virtual method to add knotholder handles for LPE items + */ +void +Effect::addKnotHolderHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) +{ +} + /** * This *creates* a new widget, management of deletion should be done by the caller */ diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h index a8cb525b8..c46c145b5 100644 --- a/src/live_effects/effect.h +++ b/src/live_effects/effect.h @@ -101,8 +101,9 @@ public: virtual void transform_multiply(Geom::Matrix const& postmul, bool set); - bool providesKnotholder() { return knotholder_func_vector.size() > 0; } - void addHandles(KnotHolder *knotholder); + virtual bool providesKnotholder() { return false; } + void addPointParamHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item); + virtual void addKnotHolderHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item); Glib::ustring getName(); Inkscape::XML::Node * getRepr(); @@ -135,7 +136,6 @@ protected: Parameter * getNextOncanvasEditableParam(); std::vector param_vector; - std::vector > knotholder_func_vector; int oncanvasedit_it; BoolParam is_visible; diff --git a/src/live_effects/lpe-tangent_to_curve.cpp b/src/live_effects/lpe-tangent_to_curve.cpp index 80158e2f4..b61a078a4 100644 --- a/src/live_effects/lpe-tangent_to_curve.cpp +++ b/src/live_effects/lpe-tangent_to_curve.cpp @@ -27,28 +27,110 @@ namespace Inkscape { namespace LivePathEffect { -/* FIXME: We should make these member functions of LPETangentToCurve. - Is there an easy way to register member functions with knotholder? - KNOWN BUG: Because of the above, this effect does not work well when in an LPE stack -*/ -NR::Point attach_pt_get(SPItem *item) { - Inkscape::LivePathEffect::LPETangentToCurve *lpe = - dynamic_cast (sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))); - - if (lpe) - return lpe->ptA; - else - return NR::Point(0,0); +LPETangentToCurve::LPETangentToCurve(LivePathEffectObject *lpeobject) : + Effect(lpeobject), + angle(_("Angle"), _("Additional angle between tangent and curve"), "angle", &wr, this, 0.0), + t_attach(_("Location along curve"), _("Location of the point of attachment along the curve (between 0.0 and number-of-segments)"), "t_attach", &wr, this, 0.5), + length_left(_("Length left"), _("Specifies the left end of the tangent"), "length-left", &wr, this, 150), + length_right(_("Length right"), _("Specifies the right end of the tangent"), "length-right", &wr, this, 150) +{ + registerParameter( dynamic_cast(&angle) ); + registerParameter( dynamic_cast(&t_attach) ); + registerParameter( dynamic_cast(&length_left) ); + registerParameter( dynamic_cast(&length_right) ); } -void attach_pt_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) { - Inkscape::LivePathEffect::LPETangentToCurve *lpe = - dynamic_cast (sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))); - - if (!lpe) - return; +LPETangentToCurve::~LPETangentToCurve() +{ +} +Geom::Piecewise > +LPETangentToCurve::doEffect_pwd2 (Geom::Piecewise > const & pwd2_in) +{ using namespace Geom; + Piecewise > output; + + ptA = pwd2_in.valueAt(t_attach); + derivA = unit_vector(derivative(pwd2_in).valueAt(t_attach)); + + // TODO: Why are positive angles measured clockwise, not counterclockwise? + Geom::Rotate rot(Geom::Rotate::from_degrees(-angle)); + derivA = derivA * rot; + + C = ptA - derivA * length_left; + D = ptA + derivA * length_right; + + output = Piecewise >(D2(Linear(C[X], D[X]), Linear(C[Y], D[Y]))); + + return output; +} + +class KnotHolderEntityAttachPt : public KnotHolderEntity +{ +public: + virtual bool isLPEParam() { return true; } + + virtual void knot_set(NR::Point const &p, NR::Point const &origin, guint state); + virtual NR::Point knot_get(); + virtual void onKnotUngrabbed(); +}; + +class KnotHolderEntityLeftEnd : public KnotHolderEntity +{ +public: + virtual bool isLPEParam() { return true; } + + virtual void knot_set(NR::Point const &p, NR::Point const &origin, guint state); + virtual NR::Point knot_get(); + virtual void onKnotUngrabbed(); +}; + +class KnotHolderEntityRightEnd : public KnotHolderEntity +{ +public: + virtual bool isLPEParam() { return true; } + + virtual void knot_set(NR::Point const &p, NR::Point const &origin, guint state); + virtual NR::Point knot_get(); + virtual void onKnotUngrabbed(); +}; + +void +LPETangentToCurve::addKnotHolderHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) +{ + KnotHolderEntityLeftEnd *entity_left_end = new KnotHolderEntityLeftEnd(); + KnotHolderEntityRightEnd *entity_right_end = new KnotHolderEntityRightEnd(); + KnotHolderEntityAttachPt *entity_attach_pt = new KnotHolderEntityAttachPt(); + + entity_left_end->create(desktop, item, knotholder, + _("Adjust the \"left\" end of the tangent")); + entity_right_end->create(desktop, item, knotholder, + _("Adjust the \"right\" end of the tangent")); + entity_attach_pt->create(desktop, item, knotholder, + _("Adjust the point of attachment of the tangent")); + + knotholder->add(entity_left_end); + knotholder->add(entity_right_end); + knotholder->add(entity_attach_pt); +} + +static LPETangentToCurve * +get_effect(SPItem *item) +{ + Effect *effect = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item)); + if (effect->effectType() != TANGENT_TO_CURVE) { + g_print ("Warning: Effect is not of type LPETangentToCurve!\n"); + return NULL; + } + return static_cast(effect); +} + +void +KnotHolderEntityAttachPt::knot_set(NR::Point const &p, NR::Point const &origin, guint state) +{ + using namespace Geom; + + LPETangentToCurve* lpe = get_effect(item); // FIXME: There must be a better way of converting the path's SPCurve* to pwd2. SPCurve *curve = sp_path_get_curve_for_edit (SP_PATH(item)); @@ -66,94 +148,73 @@ void attach_pt_set(SPItem *item, NR::Point const &p, NR::Point const &origin, gu sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), true, true); } -NR::Point left_end_get(SPItem *item) { - Inkscape::LivePathEffect::LPETangentToCurve *lpe = - dynamic_cast (sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))); - - if (lpe) - return lpe->C; - else - return NR::Point(0,0); -} - -NR::Point right_end_get(SPItem *item) { - Inkscape::LivePathEffect::LPETangentToCurve *lpe = - dynamic_cast (sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))); - - if (lpe) - return lpe->D; - else - return NR::Point(0,0); -} - -void left_end_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) { - Inkscape::LivePathEffect::LPETangentToCurve *lpe = - dynamic_cast (sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))); - - if (!lpe) - return; - +void +KnotHolderEntityLeftEnd::knot_set(NR::Point const &p, NR::Point const &origin, guint state) +{ + LPETangentToCurve *lpe = get_effect(item); + double lambda = Geom::nearest_point(p.to_2geom(), lpe->ptA, lpe->derivA); lpe->length_left.param_set_value(-lambda); - // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating. - sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), true, true); + sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true); } -void right_end_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) { - Inkscape::LivePathEffect::LPETangentToCurve *lpe = - dynamic_cast (sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))); - - if (!lpe) - return; - +void +KnotHolderEntityRightEnd::knot_set(NR::Point const &p, NR::Point const &origin, guint state) +{ + LPETangentToCurve *lpe = get_effect(item); + double lambda = Geom::nearest_point(p.to_2geom(), lpe->ptA, lpe->derivA); lpe->length_right.param_set_value(lambda); - // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating. - sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), true, true); + sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true); } -LPETangentToCurve::LPETangentToCurve(LivePathEffectObject *lpeobject) : - Effect(lpeobject), - t_attach(_("Location along curve"), _("Location of the point of attachment along the curve (between 0.0 and number-of-segments)"), "t_attach", &wr, this, 0.5), - length_left(_("Length left"), _("Specifies the left end of the tangent"), "length-left", &wr, this, 150), - length_right(_("Length right"), _("Specifies the right end of the tangent"), "length-right", &wr, this, 150), - angle(_("Angle"), _("Additional angle between tangent and curve"), "angle", &wr, this, 0.0) +NR::Point +KnotHolderEntityAttachPt::knot_get() { - registerParameter( dynamic_cast(&t_attach) ); - registerParameter( dynamic_cast(&length_left) ); - registerParameter( dynamic_cast(&length_right) ); - registerParameter( dynamic_cast(&angle) ); - registerKnotHolderHandle(attach_pt_set, attach_pt_get); - registerKnotHolderHandle(left_end_set, left_end_get); - registerKnotHolderHandle(right_end_set, right_end_get); + LPETangentToCurve* lpe = get_effect(item); + return lpe->ptA; } -LPETangentToCurve::~LPETangentToCurve() +NR::Point +KnotHolderEntityLeftEnd::knot_get() { + LPETangentToCurve *lpe = get_effect(item); + return lpe->C; } -Geom::Piecewise > -LPETangentToCurve::doEffect_pwd2 (Geom::Piecewise > const & pwd2_in) +NR::Point +KnotHolderEntityRightEnd::knot_get() { - using namespace Geom; - Piecewise > output; + LPETangentToCurve *lpe = get_effect(item); + return lpe->D; +} - ptA = pwd2_in.valueAt(t_attach); - derivA = unit_vector(derivative(pwd2_in).valueAt(t_attach)); +void +KnotHolderEntityAttachPt::onKnotUngrabbed() +{ + LPETangentToCurve *lpe = get_effect(item); + lpe->t_attach.write_to_SVG(); +} - // TODO: Why are positive angles measured clockwise, not counterclockwise? - Geom::Rotate rot(Geom::Rotate::from_degrees(-angle)); - derivA = derivA * rot; +void +KnotHolderEntityLeftEnd::onKnotUngrabbed() +{ + LPETangentToCurve *lpe = get_effect(item); + lpe->length_left.write_to_SVG(); +} - C = ptA - derivA * length_left; - D = ptA + derivA * length_right; +void +KnotHolderEntityRightEnd::onKnotUngrabbed() +{ + LPETangentToCurve *lpe = get_effect(item); + lpe->length_right.write_to_SVG(); +} + + - output = Piecewise >(D2(Linear(C[X], D[X]), Linear(C[Y], D[Y]))); - return output; -} } //namespace LivePathEffect } /* namespace Inkscape */ diff --git a/src/live_effects/lpe-tangent_to_curve.h b/src/live_effects/lpe-tangent_to_curve.h index 3bddc11fb..0de2baf56 100644 --- a/src/live_effects/lpe-tangent_to_curve.h +++ b/src/live_effects/lpe-tangent_to_curve.h @@ -28,25 +28,24 @@ public: LPETangentToCurve(LivePathEffectObject *lpeobject); virtual ~LPETangentToCurve(); + bool providesKnotholder() { return true; } + virtual void addKnotHolderHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item); + virtual Geom::Piecewise > doEffect_pwd2 (Geom::Piecewise > const & pwd2_in); - /* the knotholder functions must be declared friends */ - friend NR::Point attach_pt_get(SPItem *item); - friend NR::Point left_end_get(SPItem *item); - friend NR::Point right_end_get(SPItem *item); - friend void attach_pt_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state); - friend void left_end_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state); - friend void right_end_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state); + /* the knotholder entity classes must be declared friends */ + friend class KnotHolderEntityLeftEnd; + friend class KnotHolderEntityRightEnd; + friend class KnotHolderEntityAttachPt; private: - ScalarParam t_attach; + ScalarParam angle; + ScalarParam t_attach; ScalarParam length_left; ScalarParam length_right; - ScalarParam angle; - Geom::Point ptA; // point of attachment to the curve Geom::Point derivA; diff --git a/src/live_effects/parameter/parameter.h b/src/live_effects/parameter/parameter.h index 511ce46a2..c4e58c8d5 100644 --- a/src/live_effects/parameter/parameter.h +++ b/src/live_effects/parameter/parameter.h @@ -58,7 +58,8 @@ public: virtual bool param_readSVGValue(const gchar * strvalue) = 0; // returns true if new value is valid / accepted. virtual gchar * param_getSVGValue() const = 0; - + void write_to_SVG() { param_write_to_repr(param_getSVGValue()); } + virtual void param_set_default() = 0; void printTypeName(); diff --git a/src/live_effects/parameter/point.cpp b/src/live_effects/parameter/point.cpp index 49a47660f..502af1f23 100644 --- a/src/live_effects/parameter/point.cpp +++ b/src/live_effects/parameter/point.cpp @@ -162,12 +162,33 @@ PointParam::set_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint knot_color = color; } +void +PointParam::knot_set(NR::Point const &p, NR::Point const &origin, guint state) +{ + g_print ("PointParam::knot_set() was called!\n"); + param_setValue(p.to_2geom()); +} + +NR::Point +PointParam::knot_get() +{ + g_print ("PointParam::knot_get() was called.\n"); + g_print ("We return (%f, %f)\n", (*this)[0], (*this)[1]); + return *this; +} + +void +PointParam::knot_click(guint state) +{ + g_print ("PointParam::knot_click() was called!\n"); +} // CALLBACKS: void PointParam::on_button_click() { + g_print ("PointParam::on_button_click()\n"); SPDesktop *desktop = SP_ACTIVE_DESKTOP; SPItem * item = sp_desktop_selection(desktop)->singleItem(); if (item != NULL) { diff --git a/src/live_effects/parameter/point.h b/src/live_effects/parameter/point.h index d963d835a..3e8dc843a 100644 --- a/src/live_effects/parameter/point.h +++ b/src/live_effects/parameter/point.h @@ -16,14 +16,14 @@ #include "live_effects/parameter/parameter.h" -#include "knot-enums.h" +#include "knot-holder-entity.h" namespace Inkscape { namespace LivePathEffect { -class PointParam : public Geom::Point, public Parameter { +class PointParam : public Geom::Point, public Parameter, public KnotHolderEntity { public: PointParam( const Glib::ustring& label, const Glib::ustring& tip, @@ -51,6 +51,13 @@ public: void set_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color); + /* these are overloaded from KnotHolderEntity */ + virtual void knot_set(NR::Point const &p, NR::Point const &origin, guint state); + virtual NR::Point knot_get(); + virtual void knot_click(guint state); + + virtual bool isLPEParam() { return true; } + private: PointParam(const PointParam&); PointParam& operator=(const PointParam&); diff --git a/src/object-edit.cpp b/src/object-edit.cpp index 281b5e89d..8d8be4b85 100644 --- a/src/object-edit.cpp +++ b/src/object-edit.cpp @@ -56,11 +56,8 @@ static KnotHolder *sp_lpe_knot_holder(SPItem *item, SPDesktop *desktop) KnotHolder *knot_holder = new KnotHolder(desktop, item, NULL); Inkscape::LivePathEffect::Effect *effect = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item)); - if (!effect) { - g_error("sp_lpe_knot_holder: logical error, this method cannot be called with item having an LPE"); - } else { - effect->addHandles(knot_holder); - } +// effect->addPointParamHandles(knot_holder, desktop, item); + effect->addKnotHolderHandles(knot_holder, desktop, item); return knot_holder; } diff --git a/src/shape-editor.cpp b/src/shape-editor.cpp index 3b892db03..9cfa83af1 100644 --- a/src/shape-editor.cpp +++ b/src/shape-editor.cpp @@ -192,19 +192,14 @@ void ShapeEditor::set_item(SPItem *item) { if (item) { if (SP_IS_LPE_ITEM(item)) { SPLPEItem *lpeitem = SP_LPE_ITEM(item); - if ( !sp_lpe_item_get_current_lpe(lpeitem) || // if returns NULL, the whole expression evaluates to true and C++ will not call the otherwise crashing 2 functions below - !sp_lpe_item_get_current_lpe(lpeitem)->isVisible() || - !sp_lpe_item_get_current_lpe(lpeitem)->providesKnotholder() ) - // only create nodepath if the item either doesn't have an LPE - // or the LPE is invisible or it doesn't provide a knotholder itself - { - this->nodepath = - sp_nodepath_new(desktop, item, (prefs_get_int_attribute("tools.nodes", "show_handles", 1) != 0)); + if (!sp_lpe_item_get_current_lpe(lpeitem) || // if returns NULL, the whole expression evaluates to true and C++ will not call the otherwise crashing 2 functions below + !sp_lpe_item_get_current_lpe(lpeitem)->isVisible() || + !sp_lpe_item_get_current_lpe(lpeitem)->providesKnotholder()) { + // only create nodepath if the item either doesn't have an LPE + // or the LPE is invisible or it doesn't provide a knotholder itself + this->nodepath = + sp_nodepath_new(desktop, item, (prefs_get_int_attribute("tools.nodes", "show_handles", 1) != 0)); } - } else { - // only create nodepath if the item either doesn't have an LPE: no fear the item cannot have an LPE - this->nodepath = - sp_nodepath_new(desktop, item, (prefs_get_int_attribute("tools.nodes", "show_handles", 1) != 0)); } if (this->nodepath) {