Code

OCAL. Fix for Bug #638844 (Errors printed to console if openclipart search fails).
[inkscape.git] / src / knotholder.cpp
index a24bb16259d0bcf2b70d7459e0f9436e67448810..f5e28618eff8fb641fd5f2acfbc4a1cb85cfcde0 100644 (file)
@@ -1,25 +1,23 @@
-#define __KNOT_HOLDER_C__
-
 /*
  * Container for SPKnot visual handles
  *
  * Authors:
  *   Mitsuru Oka <oka326@parkcity.ne.jp>
  *   bulia byak <buliabyak@users.sf.net>
+ *   Maximilian Albert <maximilian.albert@gmail.com>
+ *   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 <glibmm/i18n.h>
 
 #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 <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);
+#include "xml/repr.h" // for debugging only
 
-void sp_knot_holder_dispose(GObject *object);
+using Inkscape::DocumentUndo;
 
-#ifdef KNOT_HOLDER_DEBUG
+class SPDesktop;
 
-static void sp_knot_holder_debug(GtkObject *object, gpointer data)
+KnotHolder::KnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler)
 {
-    g_print("sp-knot-holder-debug: [type=%s] [data=%s]\n", gtk_type_name(GTK_OBJECT_TYPE(object)), (const gchar *) data);
-}
-#endif
+    //XML Tree being used directly here while it shouldn't be...
+    Inkscape::XML::Node *repr = SP_OBJECT(item)->getRepr();
 
-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);
+    if (!desktop || !item || !SP_IS_ITEM(item)) {
+        g_print ("Error! Throw an exception, please!\n");
     }
-    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)
-{
-    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;
+    this->desktop = desktop;
+    this->item = item;
+    g_object_ref(G_OBJECT(item)); // TODO: is this still needed after C++-ification?
 
-    knot_holder->released = relhandler;
+    this->released = relhandler;
 
-    knot_holder->repr = repr;
-    knot_holder->local_change = FALSE;
+    this->repr = repr;
+    this->local_change = FALSE;
 
-#ifdef KNOT_HOLDER_DEBUG
-    g_signal_connect(G_OBJECT(desktop), "destroy", sp_knot_holder_debug, (gpointer) "SPKnotHolder::item");
-#endif
-
-    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(item->i2d_affine());
 
-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;
         }
     }
 
     if (SP_IS_SHAPE(item)) {
-        sp_shape_set_shape(SP_SHAPE(item));
+        SP_SHAPE(item)->setShape();
     }
 
-    knotholder_update_knots(knot_holder, item);
-    g_object_unref(knot_holder);
+    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_3DBOX(item))
+    else if (SP_IS_BOX3D(item))
         object_verb = SP_VERB_CONTEXT_3DBOX;
     else if (SP_IS_GENERICELLIPSE(item))
         object_verb = SP_VERB_CONTEXT_ARC;
@@ -253,46 +148,71 @@ static void knot_clicked_handler(SPKnot *knot, guint state, gpointer data)
     }
 
     // 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, 
-                     _("Change handle"));
+    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<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 * 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);
+        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_3DBOX(object))
+        else if (SP_IS_BOX3D(object))
             object_verb = SP_VERB_CONTEXT_3DBOX;
         else if (SP_IS_GENERICELLIPSE(object))
             object_verb = SP_VERB_CONTEXT_ARC;
@@ -306,9 +226,40 @@ static void knot_ungrabbed_handler(SPKnot *knot, unsigned int state, SPKnotHolde
             else
                 object_verb = SP_VERB_SELECTION_DYNAMIC_OFFSET;
         }
-        
-        sp_document_done(SP_OBJECT_DOCUMENT (object), object_verb,
-                         _("Move handle"));
+
+        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
+                          _("<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);
     }
 }
 
@@ -321,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 :