X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fknotholder.cpp;h=f5e28618eff8fb641fd5f2acfbc4a1cb85cfcde0;hb=941ff7c62ac6b94d08e349025428ca1b2e0b6f3a;hp=0ea3ed98ab49ab9c1c377aa7236c81dbc37ee8a6;hpb=f951374eef04129c6a0d213e7ab4d9ed2095ca69;p=inkscape.git diff --git a/src/knotholder.cpp b/src/knotholder.cpp index 0ea3ed98a..f5e28618e 100644 --- a/src/knotholder.cpp +++ b/src/knotholder.cpp @@ -1,219 +1,265 @@ -#define __KNOT_HOLDER_C__ - /* * Container for SPKnot visual handles * * Authors: * Mitsuru Oka * bulia byak + * Maximilian Albert + * Abhishek Sharma * - * Copyright (C) 2001-2005 authors + * Copyright (C) 2001-2008 authors * * Released under GNU GPL, read the file 'COPYING' for more information */ -#define noKNOT_HOLDER_DEBUG - +#include #include "document.h" #include "sp-shape.h" #include "knot.h" #include "knotholder.h" -#include "knot-holder-entity.h" -#include +#include "rect-context.h" +#include "sp-rect.h" +#include "arc-context.h" +#include "sp-ellipse.h" +#include "star-context.h" +#include "sp-star.h" +#include "spiral-context.h" +#include "sp-spiral.h" +#include "sp-offset.h" +#include "box3d.h" +#include "sp-pattern.h" +#include "style.h" +#include "live_effects/lpeobject.h" +#include "live_effects/effect.h" +#include "desktop.h" +#include "display/sp-canvas.h" + +#include "xml/repr.h" // for debugging only + +using Inkscape::DocumentUndo; class SPDesktop; -static void knot_clicked_handler (SPKnot *knot, guint state, gpointer data); -static void knot_moved_handler(SPKnot *knot, NR::Point const *p, guint state, gpointer data); -static void knot_ungrabbed_handler (SPKnot *knot, unsigned int state, SPKnotHolder *kh); - -#ifdef KNOT_HOLDER_DEBUG - -static void sp_knot_holder_debug(GtkObject *object, gpointer data) -{ - g_print("sp-knot-holder-debug: [type=%s] [data=%s]\n", gtk_type_name(GTK_OBJECT_TYPE(object)), (const gchar *) data); -} -#endif - -SPKnotHolder *sp_knot_holder_new(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) +KnotHolder::KnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) { - Inkscape::XML::Node *repr = SP_OBJECT(item)->repr; - - g_return_val_if_fail(desktop != NULL, NULL); - g_return_val_if_fail(item != NULL, NULL); - g_return_val_if_fail(SP_IS_ITEM(item), NULL); + //XML Tree being used directly here while it shouldn't be... + Inkscape::XML::Node *repr = SP_OBJECT(item)->getRepr(); - SPKnotHolder *knot_holder = g_new(SPKnotHolder, 1); - knot_holder->desktop = desktop; - knot_holder->item = item; - g_object_ref(G_OBJECT(item)); - knot_holder->entity = NULL; + if (!desktop || !item || !SP_IS_ITEM(item)) { + g_print ("Error! Throw an exception, please!\n"); + } - knot_holder->released = relhandler; + this->desktop = desktop; + this->item = item; + g_object_ref(G_OBJECT(item)); // TODO: is this still needed after C++-ification? - knot_holder->repr = repr; - knot_holder->local_change = FALSE; + this->released = relhandler; -#ifdef KNOT_HOLDER_DEBUG - g_signal_connect(G_OBJECT(desktop), "destroy", sp_knot_holder_debug, (gpointer) "SPKnotHolder::item"); -#endif + this->repr = repr; + this->local_change = FALSE; - return knot_holder; + this->dragging = false; } -void sp_knot_holder_destroy(SPKnotHolder *kh) -{ - if (kh) { - g_object_unref(G_OBJECT(kh->item)); - while (kh->entity) { - SPKnotHolderEntity *e = (SPKnotHolderEntity *) kh->entity->data; - /* unref should call destroy */ - g_object_unref(G_OBJECT(e->knot)); - g_free(e); - kh->entity = g_slist_remove(kh->entity, e); +KnotHolder::~KnotHolder() { + g_object_unref(G_OBJECT(item)); + for(std::list::iterator i = entity.begin(); i != entity.end(); ++i) { + KnotHolderEntity* e = (*i); + if (e->isDeletable()) { + delete (*i); + } else { + // we must not delete the entity (since it's attached to an LPE parameter), + // but the handle should be destroyed + g_object_unref(e->knot); } - - g_free(kh); + (*i) = NULL; } + entity.clear(); // is this necessary? } -void sp_knot_holder_add( - SPKnotHolder *knot_holder, - SPKnotHolderSetFunc knot_set, - SPKnotHolderGetFunc knot_get, - void (* knot_click) (SPItem *item, guint state), - const gchar *tip - ) -{ - sp_knot_holder_add_full(knot_holder, knot_set, knot_get, knot_click, SP_KNOT_SHAPE_DIAMOND, SP_KNOT_MODE_XOR, tip); -} +/** + * \param p In desktop coordinates. + */ -void sp_knot_holder_add_full( - SPKnotHolder *knot_holder, - SPKnotHolderSetFunc knot_set, - SPKnotHolderGetFunc knot_get, - void (* knot_click) (SPItem *item, guint state), - SPKnotShapeType shape, - SPKnotModeType mode, - const gchar *tip - ) +void +KnotHolder::update_knots() { - g_return_if_fail(knot_holder != NULL); - g_return_if_fail(knot_set != NULL); - g_return_if_fail(knot_get != NULL); - - SPItem *item = SP_ITEM(knot_holder->item); - - /* create new SPKnotHolderEntry */ - SPKnotHolderEntity *e = g_new(SPKnotHolderEntity, 1); - e->knot = sp_knot_new(knot_holder->desktop, tip); - e->knot_set = knot_set; - e->knot_get = knot_get; - if (knot_click) { - e->knot_click = knot_click; - } else { - e->knot_click = NULL; - } + Geom::Matrix const i2d(item->i2d_affine()); - g_object_set(G_OBJECT (e->knot->item), "shape", shape, NULL); - g_object_set(G_OBJECT (e->knot->item), "mode", mode, NULL); - - // TODO: add a color argument - //e->knot->fill [SP_KNOT_STATE_NORMAL] = 0x00ff0000; - //g_object_set (G_OBJECT (e->knot->item), "fill_color", 0x00ff0000, NULL); - - knot_holder->entity = g_slist_append(knot_holder->entity, e); - - /* Move to current point. */ - NR::Point dp = e->knot_get(item) * sp_item_i2d_affine(item); - sp_knot_set_position(e->knot, &dp, SP_KNOT_STATE_NORMAL); - - e->handler_id = g_signal_connect(G_OBJECT(e->knot), "moved", G_CALLBACK(knot_moved_handler), knot_holder); - g_signal_connect(G_OBJECT(e->knot), "clicked", G_CALLBACK(knot_clicked_handler), knot_holder); - g_signal_connect(G_OBJECT(e->knot), "ungrabbed", G_CALLBACK(knot_ungrabbed_handler), knot_holder); - -#ifdef KNOT_HOLDER_DEBUG - g_signal_connect(ob, "destroy", sp_knot_holder_debug, "SPKnotHolder::knot"); -#endif - sp_knot_show(e->knot); + for(std::list::iterator i = entity.begin(); i != entity.end(); ++i) { + KnotHolderEntity *e = *i; + e->update_knot(); + } } /** - * \param p In desktop coordinates. + * \brief Returns true if at least one of the KnotHolderEntities has the mouse hovering above it */ - -static void knotholder_update_knots(SPKnotHolder *knot_holder, SPItem *item) +bool KnotHolder::knot_mouseover() { - NR::Matrix const i2d(sp_item_i2d_affine(item)); - - for (GSList *el = knot_holder->entity; el; el = el->next) { - SPKnotHolderEntity *e = (SPKnotHolderEntity *) el->data; - GObject *kob = G_OBJECT(e->knot); - - NR::Point dp( e->knot_get(item) * i2d ); - g_signal_handler_block(kob, e->handler_id); - sp_knot_set_position(e->knot, &dp, SP_KNOT_STATE_NORMAL); - g_signal_handler_unblock(kob, e->handler_id); + for(std::list::iterator i = entity.begin(); i != entity.end(); ++i) { + SPKnot *knot = (*i)->knot; + if (knot && (knot->flags & SP_KNOT_MOUSEOVER)) { + return true; + } } + + return false; } -static void knot_clicked_handler(SPKnot *knot, guint state, gpointer data) +void +KnotHolder::knot_clicked_handler(SPKnot *knot, guint state) { - SPKnotHolder *knot_holder = (SPKnotHolder *) data; - SPItem *item = SP_ITEM (knot_holder->item); + KnotHolder *knot_holder = this; - for (GSList *el = knot_holder->entity; el; el = el->next) { - SPKnotHolderEntity *e = (SPKnotHolderEntity *) el->data; + for(std::list::iterator i = knot_holder->entity.begin(); i != knot_holder->entity.end(); ++i) { + KnotHolderEntity *e = *i; if (e->knot == knot) { - if (e->knot_click) { - e->knot_click(item, state); - } + // no need to test whether knot_click exists since it's virtual now + e->knot_click(state); break; } } if (SP_IS_SHAPE(item)) { - sp_shape_set_shape(SP_SHAPE(item)); + SP_SHAPE(item)->setShape(); } - knotholder_update_knots(knot_holder, item); + knot_holder->update_knots(); + + unsigned int object_verb = SP_VERB_NONE; + + if (SP_IS_RECT(item)) + object_verb = SP_VERB_CONTEXT_RECT; + else if (SP_IS_BOX3D(item)) + object_verb = SP_VERB_CONTEXT_3DBOX; + else if (SP_IS_GENERICELLIPSE(item)) + object_verb = SP_VERB_CONTEXT_ARC; + else if (SP_IS_STAR(item)) + object_verb = SP_VERB_CONTEXT_STAR; + else if (SP_IS_SPIRAL(item)) + object_verb = SP_VERB_CONTEXT_SPIRAL; + else if (SP_IS_OFFSET(item)) { + if (SP_OFFSET(item)->sourceHref) + object_verb = SP_VERB_SELECTION_LINKED_OFFSET; + else + object_verb = SP_VERB_SELECTION_DYNAMIC_OFFSET; + } // for drag, this is done by ungrabbed_handler, but for click we must do it here - sp_document_done(SP_OBJECT_DOCUMENT(knot_holder->item)); + DocumentUndo::done(SP_OBJECT_DOCUMENT(item), object_verb, + _("Change handle")); } -static void knot_moved_handler(SPKnot *knot, NR::Point const *p, guint state, gpointer data) +void +KnotHolder::knot_moved_handler(SPKnot *knot, Geom::Point const &p, guint state) { - SPKnotHolder *knot_holder = (SPKnotHolder *) data; - SPItem *item = SP_ITEM (knot_holder->item); - // this was a local change and the knotholder does not need to be recreated: - knot_holder->local_change = TRUE; + if (this->dragging == false) { + this->dragging = true; + } - for (GSList *el = knot_holder->entity; el; el = el->next) { - SPKnotHolderEntity *e = (SPKnotHolderEntity *) el->data; + // this was a local change and the knotholder does not need to be recreated: + this->local_change = TRUE; + + for(std::list::iterator i = this->entity.begin(); i != this->entity.end(); ++i) { + KnotHolderEntity *e = *i; if (e->knot == knot) { - NR::Point const q = *p / sp_item_i2d_affine(item); - e->knot_set(item, q, e->knot->drag_origin / sp_item_i2d_affine(item), state); + Geom::Point const q = p * item->i2d_affine().inverse(); + e->knot_set(q, e->knot->drag_origin * item->i2d_affine().inverse(), state); break; } } if (SP_IS_SHAPE (item)) { - sp_shape_set_shape(SP_SHAPE (item)); + SP_SHAPE (item)->setShape(); } - knotholder_update_knots(knot_holder, item); + this->update_knots(); } -static void knot_ungrabbed_handler(SPKnot *knot, unsigned int state, SPKnotHolder *kh) +void +KnotHolder::knot_ungrabbed_handler(SPKnot */*knot*/) { - if (kh->released) { - kh->released(kh->item); + this->dragging = false; + + if (this->released) { + this->released(this->item); } else { - SPObject *object = (SPObject *) kh->item; - object->updateRepr(object->repr, SP_OBJECT_WRITE_EXT); - sp_document_done(SP_OBJECT_DOCUMENT (object)); + SPObject *object = (SPObject *) this->item; + + // Caution: this call involves a screen update, which may process events, and as a + // result the knotholder may be destructed. So, after the updateRepr, we cannot use any + // fields of this knotholder (such as this->item), but only values we have saved beforehand + // (such as object). + object->updateRepr(); + + /* do cleanup tasks (e.g., for LPE items write the parameter values + * that were changed by dragging the handle to SVG) + */ + if (SP_IS_LPE_ITEM(object)) { + // This writes all parameters to SVG. Is this sufficiently efficient or should we only + // write the ones that were changed? + + Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object)); + if (lpe) { + LivePathEffectObject *lpeobj = lpe->getLPEObj(); + SP_OBJECT(lpeobj)->updateRepr(); + } + } + + unsigned int object_verb = SP_VERB_NONE; + + if (SP_IS_RECT(object)) + object_verb = SP_VERB_CONTEXT_RECT; + else if (SP_IS_BOX3D(object)) + object_verb = SP_VERB_CONTEXT_3DBOX; + else if (SP_IS_GENERICELLIPSE(object)) + object_verb = SP_VERB_CONTEXT_ARC; + else if (SP_IS_STAR(object)) + object_verb = SP_VERB_CONTEXT_STAR; + else if (SP_IS_SPIRAL(object)) + object_verb = SP_VERB_CONTEXT_SPIRAL; + else if (SP_IS_OFFSET(object)) { + if (SP_OFFSET(object)->sourceHref) + object_verb = SP_VERB_SELECTION_LINKED_OFFSET; + else + object_verb = SP_VERB_SELECTION_DYNAMIC_OFFSET; + } + + DocumentUndo::done(SP_OBJECT_DOCUMENT (object), object_verb, + _("Move handle")); + } +} + +void +KnotHolder::add(KnotHolderEntity *e) +{ + entity.push_back(e); +} + +void +KnotHolder::add_pattern_knotholder() +{ + if ((SP_OBJECT(item)->style->fill.isPaintserver()) + && SP_IS_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style))) + { + PatternKnotHolderEntityXY *entity_xy = new PatternKnotHolderEntityXY(); + PatternKnotHolderEntityAngle *entity_angle = new PatternKnotHolderEntityAngle(); + PatternKnotHolderEntityScale *entity_scale = new PatternKnotHolderEntityScale(); + entity_xy->create(desktop, item, this, + // TRANSLATORS: This refers to the pattern that's inside the object + _("Move the pattern fill inside the object"), + SP_KNOT_SHAPE_CROSS); + entity_scale->create(desktop, item, this, + _("Scale the pattern fill; uniformly if with Ctrl"), + SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR); + entity_angle->create(desktop, item, this, + _("Rotate the pattern fill; with Ctrl to snap angle"), + SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR); + entity.push_back(entity_xy); + entity.push_back(entity_angle); + entity.push_back(entity_scale); } } @@ -226,4 +272,4 @@ static void knot_ungrabbed_handler(SPKnot *knot, unsigned int state, SPKnotHolde fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :