/* * Inkscape::ShapeEditor * * Authors: * bulia byak * Krzysztof KosiƄski * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "sp-object.h" #include "sp-item.h" #include "sp-lpe-item.h" #include "live_effects/lpeobject.h" #include "selection.h" #include "desktop.h" #include "document.h" #include "desktop-handles.h" #include "knotholder.h" #include "live_effects/parameter/point.h" #include "xml/node-event-vector.h" #include "preferences.h" #include "object-edit.h" #include "style.h" #include "display/curve.h" #include <2geom/pathvector.h> #include "shape-editor.h" ShapeEditor::ShapeEditor(SPDesktop *dt) { this->desktop = dt; this->knotholder = NULL; this->knotholder_listener_attached_for = NULL; } ShapeEditor::~ShapeEditor() { unset_item(SH_KNOTHOLDER); } void ShapeEditor::unset_item(SubType type, bool keep_knotholder) { Inkscape::XML::Node *old_repr = NULL; switch (type) { case SH_NODEPATH: // defunct break; case SH_KNOTHOLDER: if (this->knotholder) { old_repr = this->knotholder->repr; if (old_repr && old_repr == knotholder_listener_attached_for) { sp_repr_remove_listener_by_data(old_repr, this); Inkscape::GC::release(old_repr); knotholder_listener_attached_for = NULL; } if (!keep_knotholder) { delete this->knotholder; this->knotholder = NULL; } } break; } } bool ShapeEditor::has_nodepath () { return false; } bool ShapeEditor::has_knotholder () { return (this->knotholder != NULL); } void ShapeEditor::update_knotholder () { if (this->knotholder) this->knotholder->update_knots(); } bool ShapeEditor::has_local_change (SubType type) { switch (type) { case SH_NODEPATH: // defunct return false; case SH_KNOTHOLDER: return (this->knotholder && this->knotholder->local_change != 0); default: g_assert_not_reached(); } } void ShapeEditor::decrement_local_change (SubType type) { switch (type) { case SH_NODEPATH: // defunct break; case SH_KNOTHOLDER: if (this->knotholder) { this->knotholder->local_change = FALSE; } break; default: g_assert_not_reached(); } } const SPItem *ShapeEditor::get_item (SubType type) { const SPItem *item = NULL; switch (type) { case SH_NODEPATH: // defunct break; case SH_KNOTHOLDER: if (this->has_knotholder()) { item = this->knotholder->getItem(); } break; } return item; } GList *ShapeEditor::save_nodepath_selection () { // defunct stub return NULL; } void ShapeEditor::restore_nodepath_selection (GList */*saved*/) { // defunct stub } void ShapeEditor::shapeeditor_event_attr_changed(gchar const *name) { gboolean changed_kh = FALSE; if (has_knotholder()) { changed_kh = !has_local_change(SH_KNOTHOLDER); decrement_local_change(SH_KNOTHOLDER); if (changed_kh) { // this can happen if an LPEItem's knotholder handle was dragged, in which case we want // to keep the knotholder; in all other cases (e.g., if the LPE itself changes) we delete it reset_item(SH_KNOTHOLDER, !strcmp(name, "d")); } } } static void shapeeditor_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/, bool /*is_interactive*/, gpointer data) { g_assert(data); ShapeEditor *sh = ((ShapeEditor *) data); sh->shapeeditor_event_attr_changed(name); } static Inkscape::XML::NodeEventVector shapeeditor_repr_events = { NULL, /* child_added */ NULL, /* child_removed */ shapeeditor_event_attr_changed, NULL, /* content_changed */ NULL /* order_changed */ }; void ShapeEditor::set_item(SPItem *item, SubType type, bool keep_knotholder) { // this happens (and should only happen) when for an LPEItem having both knotholder and // nodepath the knotholder is adapted; in this case we don't want to delete the knotholder // since this freezes the handles unset_item(type, keep_knotholder); if (item) { Inkscape::XML::Node *repr; switch(type) { case SH_NODEPATH: // defunct break; case SH_KNOTHOLDER: if (!this->knotholder) { // only recreate knotholder if none is present this->knotholder = sp_item_knot_holder(item, desktop); } if (this->knotholder) { this->knotholder->update_knots(); // setting new listener repr = this->knotholder->repr; if (repr != knotholder_listener_attached_for) { Inkscape::GC::anchor(repr); sp_repr_add_listener(repr, &shapeeditor_repr_events, this); knotholder_listener_attached_for = repr; } } break; } } } /** FIXME: This thing is only called when the item needs to be updated in response to repr change. Why not make a reload function in NodePath and in KnotHolder? */ void ShapeEditor::reset_item (SubType type, bool keep_knotholder) { switch (type) { case SH_NODEPATH: // defunct break; case SH_KNOTHOLDER: if ( knotholder ) { SPObject *obj = sp_desktop_document(desktop)->getObjectByRepr(knotholder_listener_attached_for); /// note that it is not certain that this is an SPItem; it could be a LivePathEffectObject. set_item(SP_ITEM(obj), SH_KNOTHOLDER, keep_knotholder); } break; } } void ShapeEditor::nodepath_destroyed () { } bool ShapeEditor::has_selection() { return false; // so far, knotholder cannot have selection } /** * Returns true if this ShapeEditor has a knot above which the mouse currently hovers. */ bool ShapeEditor::knot_mouseover() { if (this->knotholder) { return knotholder->knot_mouseover(); } return false; } /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :