diff --git a/src/knotholder.cpp b/src/knotholder.cpp
index 161e779d1baf97b7134dec2375baeae60c80667b..314ad807c5a1de383472327124a322bff06cfd6a 100644 (file)
--- a/src/knotholder.cpp
+++ b/src/knotholder.cpp
* Authors:
* Mitsuru Oka <oka326@parkcity.ne.jp>
* bulia byak <buliabyak@users.sf.net>
+ * Maximilian Albert <maximilian.albert@gmail.com>
*
- * 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 "document.h"
#include "sp-shape.h"
#include "knot.h"
#include "knotholder.h"
-#include "knot-holder-entity.h"
#include "rect-context.h"
#include "sp-rect.h"
#include "arc-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
-#include <libnr/nr-matrix-div.h>
#include <glibmm/i18n.h>
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);
-static void sp_knot_holder_class_init(SPKnotHolderClass *klass);
-
-void sp_knot_holder_dispose(GObject *object);
-
-#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
-
-static GObjectClass *parent_class;
-
-/**
- * Registers SPKnotHolder class and returns its type number.
- */
-GType sp_knot_holder_get_type()
-{
- static GType type = 0;
- if (!type) {
- GTypeInfo info = {
- sizeof(SPKnotHolderClass),
- NULL, /* base_init */
- NULL, /* base_finalize */
- (GClassInitFunc) sp_knot_holder_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (SPKnotHolder),
- 16, /* n_preallocs */
- NULL,
- NULL
- };
- type = g_type_register_static (G_TYPE_OBJECT, "SPKnotHolder", &info, (GTypeFlags) 0);
- }
- return type;
-}
-
-/**
- * SPKnotHolder vtable initialization.
- */
-static void sp_knot_holder_class_init(SPKnotHolderClass *klass){
- parent_class = (GObjectClass*) g_type_class_peek_parent(klass);
- klass->dispose = sp_knot_holder_dispose;
-}
-
-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);
-
- SPKnotHolder *knot_holder = (SPKnotHolder*)g_object_new (SP_TYPE_KNOT_HOLDER, 0);
- 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_dispose(GObject *object) {
- SPKnotHolder *kh = G_TYPE_CHECK_INSTANCE_CAST((object), SP_TYPE_KNOT_HOLDER, SPKnotHolder);
-
- g_object_unref(G_OBJECT(kh->item));
- while (kh->entity) {
- SPKnotHolderEntity *e = (SPKnotHolderEntity *) kh->entity->data;
- g_signal_handler_disconnect(e->knot, e->_click_handler_id);
- g_signal_handler_disconnect(e->knot, e->_ungrab_handler_id);
- /* unref should call destroy */
- g_object_unref(e->knot);
- g_free(e);
- kh->entity = g_slist_remove(kh->entity, e);
+KnotHolder::~KnotHolder() {
+ g_object_unref(G_OBJECT(item));
+ for(std::list<KnotHolderEntity *>::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);
+ }
+ (*i) = NULL;
}
+ entity.clear(); // is this necessary?
}
-void sp_knot_holder_destroy(SPKnotHolder *kh) {
- g_object_unref(kh);
- }
+/**
+ * \param p In desktop coordinates.
+ */
-void sp_knot_holder_add(
- SPKnotHolder *knot_holder,
- SPKnotHolderSetFunc knot_set,
- SPKnotHolderGetFunc knot_get,
- void (* knot_click) (SPItem *item, guint state),
- const gchar *tip
- )
+void
+KnotHolder::update_knots()
{
- sp_knot_holder_add_full(knot_holder, knot_set, knot_get, knot_click, SP_KNOT_SHAPE_DIAMOND, SP_KNOT_MODE_XOR, tip);
-}
+ Geom::Matrix const i2d(sp_item_i2d_affine(item));
-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
- )
-{
- 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;
+ for(std::list<KnotHolderEntity *>::iterator i = entity.begin(); i != entity.end(); ++i) {
+ KnotHolderEntity *e = *i;
+ e->update_knot();
}
-
- 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(e->knot, "moved", G_CALLBACK(knot_moved_handler), knot_holder);
- e->_click_handler_id = g_signal_connect(e->knot, "clicked", G_CALLBACK(knot_clicked_handler), knot_holder);
- e->_ungrab_handler_id = g_signal_connect(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);
}
/**
- * \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 = 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<KnotHolderEntity *>::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;
- g_object_ref(knot_holder);
- for (GSList *el = knot_holder->entity; el; el = el->next) {
- SPKnotHolderEntity *e = (SPKnotHolderEntity *) el->data;
+ for(std::list<KnotHolderEntity *>::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;
}
}
sp_shape_set_shape(SP_SHAPE(item));
}
- knotholder_update_knots(knot_holder, item);
- g_object_unref(knot_holder);
+ knot_holder->update_knots();
unsigned int object_verb = SP_VERB_NONE;
}
// 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), object_verb,
+ sp_document_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<KnotHolderEntity *>::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 * sp_item_i2d_affine(item).inverse();
+ e->knot_set(q, e->knot->drag_origin * sp_item_i2d_affine(item).inverse(), state);
break;
}
}
@@ -278,16 +173,38 @@ static void knot_moved_handler(SPKnot *knot, NR::Point const *p, guint state, gp
sp_shape_set_shape(SP_SHAPE (item));
}
- 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);
+ 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;
@@ -307,12 +224,43 @@ static void knot_ungrabbed_handler(SPKnot */*knot*/, unsigned int /*state*/, SPK
else
object_verb = SP_VERB_SELECTION_DYNAMIC_OFFSET;
}
-
+
sp_document_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
+ _("<b>Move</b> the pattern fill inside the object"),
+ SP_KNOT_SHAPE_CROSS);
+ entity_scale->create(desktop, item, this,
+ _("<b>Scale</b> the pattern fill; uniformly if with <b>Ctrl</b>"),
+ SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR);
+ entity_angle->create(desktop, item, this,
+ _("<b>Rotate</b> the pattern fill; with <b>Ctrl</b> 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);
+ }
+}
+
/*
Local Variables:
mode:c++