Code

fix crash, allow combine to work transparently on groups
[inkscape.git] / src / sp-item.cpp
index 8868b787519e4d31982cf86fd9edb1cbd5fc5510..b5d9050c69685d26b2ab183b8ab1623b64c56ac7 100644 (file)
@@ -1,5 +1,3 @@
-#define __SP_ITEM_C__
-
 /** \file
  * Base class for visual SVG elements
  */
@@ -35,6 +33,7 @@
 #include "document.h"
 #include "uri.h"
 #include "inkscape.h"
+#include "desktop.h"
 #include "desktop-handles.h"
 
 #include "style.h"
@@ -166,13 +165,13 @@ void SPItem::init() {
     this->display = NULL;
 
     this->clip_ref = new SPClipPathReference(this);
-               sigc::signal<void, SPObject *, SPObject *> cs1=this->clip_ref->changedSignal();
-               sigc::slot2<void,SPObject*, SPObject *> sl1=sigc::bind(sigc::ptr_fun(clip_ref_changed), this);
+    sigc::signal<void, SPObject *, SPObject *> cs1=this->clip_ref->changedSignal();
+    sigc::slot2<void,SPObject*, SPObject *> sl1=sigc::bind(sigc::ptr_fun(clip_ref_changed), this);
     _clip_ref_connection = cs1.connect(sl1);
 
     this->mask_ref = new SPMaskReference(this);
-               sigc::signal<void, SPObject *, SPObject *> cs2=this->mask_ref->changedSignal();
-               sigc::slot2<void,SPObject*, SPObject *> sl2=sigc::bind(sigc::ptr_fun(mask_ref_changed), this);
+    sigc::signal<void, SPObject *, SPObject *> cs2=this->mask_ref->changedSignal();
+    sigc::slot2<void,SPObject*, SPObject *> sl2=sigc::bind(sigc::ptr_fun(mask_ref_changed), this);
     _mask_ref_connection = cs2.connect(sl2);
 
     this->avoidRef = new SPAvoidRef(this);
@@ -271,7 +270,7 @@ bool
 SPItem::isExplicitlyHidden() const
 {
     return (this->style->display.set
-           && this->style->display.value == SP_CSS_DISPLAY_NONE);
+            && this->style->display.value == SP_CSS_DISPLAY_NONE);
 }
 
 /**
@@ -663,7 +662,7 @@ sp_item_write(SPObject *const object, Inkscape::XML::Document *xml_doc, Inkscape
     SPObject *child;
     SPItem *item = SP_ITEM(object);
 
-    // in the case of SP_OBJECT_WRITE_BUILD, the item should always be newly created, 
+    // in the case of SP_OBJECT_WRITE_BUILD, the item should always be newly created,
     // so we need to add any children from the underlying object to the new repr
     if (flags & SP_OBJECT_WRITE_BUILD) {
         Inkscape::XML::Node *crepr;
@@ -749,7 +748,7 @@ sp_item_invoke_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transf
 /** Calls \a item's subclass' bounding box method; clips it by the bbox of clippath, if any; and
  * unions the resulting bbox with \a bbox. If \a clear is true, empties \a bbox first. Passes the
  * transform and the flags to the actual bbox methods. Note that many of subclasses (e.g. groups,
- * clones), in turn, call this function in their bbox methods. 
+ * clones), in turn, call this function in their bbox methods.
  * \retval bbox  Note that there is no guarantee that bbox will contain a rectangle when the
  *               function returns. If this item does not have a boundingbox, this might well be empty.
  */
@@ -792,7 +791,7 @@ sp_item_invoke_bbox_full(SPItem const *item, Geom::OptRect &bbox, Geom::Matrix c
                     y = SP_FILTER(filter)->y.computed;
                 if (SP_FILTER(filter)->width._set)
                     w = SP_FILTER(filter)->width.computed;
-                if (SP_FILTER(filter)->height._set) 
+                if (SP_FILTER(filter)->height._set)
                     h = SP_FILTER(filter)->height.computed;
 
                 double dx0 = 0;
@@ -800,7 +799,7 @@ sp_item_invoke_bbox_full(SPItem const *item, Geom::OptRect &bbox, Geom::Matrix c
                 double dy0 = 0;
                 double dy1 = 0;
                 if (filter_is_single_gaussian_blur(SP_FILTER(filter))) {
-                    // if this is a single blur, use 2.4*radius 
+                    // if this is a single blur, use 2.4*radius
                     // which may be smaller than the default area;
                     // see set_filter_area for why it's 2.4
                     double r = get_single_gaussian_blur_radius (SP_FILTER(filter));
@@ -838,11 +837,11 @@ sp_item_invoke_bbox_full(SPItem const *item, Geom::OptRect &bbox, Geom::Matrix c
     }
 
     if (temp_bbox.x0 > temp_bbox.x1 || temp_bbox.y0 > temp_bbox.y1) {
-        // Either the bbox hasn't been touched by the SPItemClass' bbox method 
+        // Either the bbox hasn't been touched by the SPItemClass' bbox method
         // (it still has its initial values, see above: x0 = y0 = NR_HUGE and x1 = y1 = -NR_HUGE)
         // or it has explicitely been set to be like this (e.g. in sp_shape_bbox)
-        
-        // When x0 > x1 or y0 > y1, the bbox is considered to be "nothing", although it has not been 
+
+        // When x0 > x1 or y0 > y1, the bbox is considered to be "nothing", although it has not been
         // explicitely defined this way for NRRects (as opposed to Geom::OptRect)
         // So union bbox with nothing = do nothing, just return
         return;
@@ -937,15 +936,15 @@ Geom::OptRect sp_item_bbox_desktop(SPItem *item, SPItem::BBoxType type)
     return rect;
 }
 
-static void sp_item_private_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::SnapPreferences const *snapprefs)
+static void sp_item_private_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::SnapPreferences const */*snapprefs*/)
 {
-       /* This will only be called if the derived class doesn't override this.
-        * see for example sp_genericellipse_snappoints in sp-ellipse.cpp        
-        * We don't know what shape we could be dealing with here, so we'll just
-        * return the corners of the bounding box */
+    /* This will only be called if the derived class doesn't override this.
+     * see for example sp_genericellipse_snappoints in sp-ellipse.cpp
+     * We don't know what shape we could be dealing with here, so we'll just
+     * return the corners of the bounding box */
+
+    Geom::OptRect bbox = item->getBounds(sp_item_i2d_affine(item));
 
-       Geom::OptRect bbox = item->getBounds(sp_item_i2d_affine(item));
-    
     if (bbox) {
         Geom::Point p1, p2;
         p1 = bbox->min();
@@ -955,12 +954,12 @@ static void sp_item_private_snappoints(SPItem const *item, SnapPointsIter p, Ink
         *p = p2;
         *p = Geom::Point(p1[Geom::Y], p2[Geom::X]);
     }
-      
+
 }
 
 void sp_item_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::SnapPreferences const *snapprefs)
 {
-       g_assert (item != NULL);
+    g_assert (item != NULL);
     g_assert (SP_IS_ITEM(item));
 
     // Get the snappoints of the item
@@ -971,32 +970,33 @@ void sp_item_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::SnapPref
 
     // Get the snappoints at the item's center
     if (snapprefs != NULL && snapprefs->getIncludeItemCenter()) {
-       *p = item->getCenter();
-    }    
-    
+        *p = item->getCenter();
+    }
+
     // Get the snappoints of clipping paths and mask, if any
     std::list<SPObject const *> clips_and_masks;
-    
+
     clips_and_masks.push_back(SP_OBJECT(item->clip_ref->getObject()));
     clips_and_masks.push_back(SP_OBJECT(item->mask_ref->getObject()));
-    
+
+    SPDesktop *desktop = inkscape_active_desktop();
     for (std::list<SPObject const *>::const_iterator o = clips_and_masks.begin(); o != clips_and_masks.end(); o++) {
-       if (*o) {
-               // obj is a group object, the children are the actual clippers
-               for (SPObject *child = (*o)->children ; child ; child = child->next) {
-                   if (SP_IS_ITEM(child)) {
-                       std::vector<Geom::Point> p_clip_or_mask;                            
-                       // Please note the recursive call here!
-                       sp_item_snappoints(SP_ITEM(child), SnapPointsIter(p_clip_or_mask), snapprefs);
-                       // Take into account the transformation of the item being clipped or masked
-                       for (std::vector<Geom::Point>::const_iterator p_orig = p_clip_or_mask.begin(); p_orig != p_clip_or_mask.end(); p_orig++) {
-                       // All snappoints are in desktop coordinates, but the item's transformation is
-                               // in document coordinates. Hence the awkward construction below
-                               *p = (*p_orig) * matrix_to_desktop (matrix_from_desktop (item->transform, item), item);
-                   }
-                   }
-               }
-       }
+        if (*o) {
+            // obj is a group object, the children are the actual clippers
+            for (SPObject *child = (*o)->children ; child ; child = child->next) {
+                if (SP_IS_ITEM(child)) {
+                    std::vector<Geom::Point> p_clip_or_mask;
+                    // Please note the recursive call here!
+                    sp_item_snappoints(SP_ITEM(child), SnapPointsIter(p_clip_or_mask), snapprefs);
+                    // Take into account the transformation of the item being clipped or masked
+                    for (std::vector<Geom::Point>::const_iterator p_orig = p_clip_or_mask.begin(); p_orig != p_clip_or_mask.end(); p_orig++) {
+                        // All snappoints are in desktop coordinates, but the item's transformation is
+                        // in document coordinates. Hence the awkward construction below
+                        *p = desktop->dt2doc(*p_orig) * sp_item_i2d_affine(item);
+                    }
+                }
+            }
+        }
     }
 }
 
@@ -1368,7 +1368,7 @@ sp_item_adjust_livepatheffect (SPItem *item, Geom::Matrix const &postmul, bool s
                 if (new_lpeobj != lpeobj) {
                     sp_lpe_item_replace_path_effect(lpeitem, lpeobj, new_lpeobj);
                 }
-        
+
                 if (lpeobj->get_lpe()) {
                     Inkscape::LivePathEffect::Effect * effect = lpeobj->get_lpe();
                     effect->transform_multiply(postmul, set);
@@ -1378,19 +1378,6 @@ sp_item_adjust_livepatheffect (SPItem *item, Geom::Matrix const &postmul, bool s
     }
 }
 
-/**
- * A temporary wrapper for the next function accepting the Geom::Matrix
- * instead of Geom::Matrix
- */
-void
-sp_item_write_transform(SPItem *item, Inkscape::XML::Node *repr, Geom::Matrix const *transform, Geom::Matrix const *adv)
-{
-    if (transform == NULL)
-        sp_item_write_transform(item, repr, Geom::identity(), adv);
-    else
-        sp_item_write_transform(item, repr, *transform, adv);
-}
-
 /**
  * Set a new transform on an object.
  *
@@ -1530,8 +1517,9 @@ i2anc_affine(SPObject const *object, SPObject const *const ancestor) {
     while ( object != ancestor && SP_IS_ITEM(object) ) {
         if (SP_IS_ROOT(object)) {
             ret *= SP_ROOT(object)->c2p;
+        } else {
+            ret *= SP_ITEM(object)->transform;
         }
-        ret *= SP_ITEM(object)->transform;
         object = SP_OBJECT_PARENT(object);
     }
     return ret;
@@ -1558,30 +1546,8 @@ Geom::Matrix sp_item_i2doc_affine(SPItem const *item)
 }
 
 /**
- * Returns the accumulated transformation of the item and all its ancestors, but excluding root's viewport.
- * Used in path operations mostly.
- * \pre (item != NULL) and SP_IS_ITEM(item).
+ * Returns the transformation from item to desktop coords
  */
-Geom::Matrix sp_item_i2root_affine(SPItem const *item)
-{
-    g_assert(item != NULL);
-    g_assert(SP_IS_ITEM(item));
-
-    Geom::Matrix ret(Geom::identity());
-    g_assert(ret.isIdentity());
-    while ( NULL != SP_OBJECT_PARENT(item) ) {
-        ret *= item->transform;
-        item = SP_ITEM(SP_OBJECT_PARENT(item));
-    }
-    g_assert(SP_IS_ROOT(item));
-
-    ret *= item->transform;
-
-    return ret;
-}
-
-/* fixme: This is EVIL!!! */
-// fix this note: why/what evil? :)
 Geom::Matrix sp_item_i2d_affine(SPItem const *item)
 {
     g_assert(item != NULL);
@@ -1593,42 +1559,6 @@ Geom::Matrix sp_item_i2d_affine(SPItem const *item)
     return ret;
 }
 
-// same as i2d but with i2root instead of i2doc
-Geom::Matrix sp_item_i2r_affine(SPItem const *item)
-{
-    g_assert(item != NULL);
-    g_assert(SP_IS_ITEM(item));
-
-    Geom::Matrix const ret( sp_item_i2root_affine(item)
-                          * Geom::Scale(1, -1)
-                          * Geom::Translate(0, sp_document_height(SP_OBJECT_DOCUMENT(item))) );
-    return ret;
-}
-
-/**
- * Converts a matrix \a m into the desktop coords of the \a item.
- * Will become a noop when we eliminate the coordinate flipping.
- */
-Geom::Matrix matrix_to_desktop(Geom::Matrix const m, SPItem const *item)
-{
-    Geom::Matrix const ret(m
-                         * Geom::Translate(0, -sp_document_height(SP_OBJECT_DOCUMENT(item)))
-                         * Geom::Scale(1, -1));
-    return ret;
-}
-
-/**
- * Converts a matrix \a m from the desktop coords of the \a item.
- * Will become a noop when we eliminate the coordinate flipping.
- */
-Geom::Matrix matrix_from_desktop(Geom::Matrix const m, SPItem const *item)
-{
-    Geom::Matrix const ret(Geom::Scale(1, -1)
-                         * Geom::Translate(0, sp_document_height(SP_OBJECT_DOCUMENT(item)))
-                         * m);
-    return ret;
-}
-
 void sp_item_set_i2d_affine(SPItem *item, Geom::Matrix const &i2dt)
 {
     g_return_if_fail( item != NULL );
@@ -1647,6 +1577,9 @@ void sp_item_set_i2d_affine(SPItem *item, Geom::Matrix const &i2dt)
 }
 
 
+/**
+ * should rather be named "sp_item_d2i_affine" to match "sp_item_i2d_affine" (or vice versa)
+ */
 Geom::Matrix
 sp_item_dt2i_affine(SPItem const *item)
 {
@@ -1735,7 +1668,7 @@ sp_item_convert_to_guides(SPItem *item) {
 
     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
     int prefs_bbox = prefs->getInt("/tools/bounding_box", 0);
-    SPItem::BBoxType bbox_type = (prefs_bbox ==0)? 
+    SPItem::BBoxType bbox_type = (prefs_bbox ==0)?
         SPItem::APPROXIMATE_BBOX : SPItem::GEOMETRIC_BBOX;
 
     Geom::OptRect bbox = sp_item_bbox_desktop(item, bbox_type);